root/usr/src/uts/intel/io/scsi/adapters/arcmsr/arcmsr.c
/*
 *       O.S   : Solaris
 *  FILE NAME  : arcmsr.c
 *       BY    : Erich Chen, C.L. Huang
 *  Description: SCSI RAID Device Driver for
 *               ARECA RAID Host adapter
 *
 *  Copyright (C) 2002,2010 Areca Technology Corporation All rights reserved.
 *  Copyright (C) 2002,2010 Erich Chen
 *          Web site: www.areca.com.tw
 *            E-mail: erich@areca.com.tw; ching2048@areca.com.tw
 *
 *      Redistribution and use in source and binary forms, with or without
 *      modification, are permitted provided that the following conditions
 *      are met:
 *      1. Redistributions of source code must retain the above copyright
 *         notice, this list of conditions and the following disclaimer.
 *      2. Redistributions in binary form must reproduce the above copyright
 *         notice, this list of conditions and the following disclaimer in the
 *         documentation and/or other materials provided with the distribution.
 *  3. The party using or redistributing the source code and binary forms
 *     agrees to the disclaimer below and the terms and conditions set forth
 *     herein.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 *
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 */
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */
/*
 * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
 * Copyright 2023 Oxide Computer Company
 */
#include <sys/types.h>
#include <sys/ddidmareq.h>
#include <sys/scsi/scsi.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/file.h>
#include <sys/disp.h>
#include <sys/signal.h>
#include <sys/debug.h>
#include <sys/pci.h>
#include <sys/policy.h>
#include <sys/atomic.h>
#include "arcmsr.h"

static int arcmsr_attach(dev_info_t *dev_info, ddi_attach_cmd_t cmd);
static int arcmsr_cb_ioctl(dev_t dev, int ioctl_cmd, intptr_t arg,
    int mode, cred_t *credp, int *rvalp);
static int arcmsr_detach(dev_info_t *dev_info, ddi_detach_cmd_t cmd);
static int arcmsr_reset(dev_info_t *resetdev, ddi_reset_cmd_t cmd);
static int arcmsr_tran_start(struct scsi_address *ap, struct scsi_pkt *pkt);
static int arcmsr_tran_abort(struct scsi_address *ap, struct scsi_pkt *pkt);
static int arcmsr_tran_reset(struct scsi_address *ap, int level);
static int arcmsr_tran_getcap(struct scsi_address *ap, char *cap, int whom);
static int arcmsr_tran_setcap(struct scsi_address *ap, char *cap, int value,
    int whom);
static int arcmsr_tran_tgt_init(dev_info_t *host_dev_info,
    dev_info_t *target_dev_info, scsi_hba_tran_t *hosttran,
    struct scsi_device *sd);
static void arcmsr_tran_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt);
static void arcmsr_tran_destroy_pkt(struct scsi_address *ap,
    struct scsi_pkt *pkt);
static void arcmsr_tran_sync_pkt(struct scsi_address *ap,
    struct scsi_pkt *pkt);
static struct scsi_pkt *arcmsr_tran_init_pkt(struct scsi_address *ap,
    struct scsi_pkt *pkt, struct buf *bp, int cmdlen, int statuslen,
    int tgtlen, int flags, int (*callback)(), caddr_t arg);
static int arcmsr_config_child(struct ACB *acb, struct scsi_device *sd,
    dev_info_t **dipp);

static int arcmsr_config_lun(struct ACB *acb, uint16_t tgt, uint8_t lun,
    dev_info_t **ldip);
static uint8_t arcmsr_abort_host_command(struct ACB *acb);
static uint8_t arcmsr_get_echo_from_iop(struct ACB *acb);
static uint_t arcmsr_intr_handler(caddr_t arg, caddr_t arg2);
static int arcmsr_initialize(struct ACB *acb);
static int arcmsr_dma_alloc(struct ACB *acb,
    struct scsi_pkt *pkt, struct buf *bp, int flags, int (*callback)());
static int arcmsr_dma_move(struct ACB *acb,
    struct scsi_pkt *pkt, struct buf *bp);
static void arcmsr_handle_iop_bus_hold(struct ACB *acb);
static void arcmsr_hbc_message_isr(struct ACB *acb);
static void arcmsr_pcidev_disattach(struct ACB *acb);
static void arcmsr_ccb_complete(struct CCB *ccb, int flag);
static void arcmsr_iop_init(struct ACB *acb);
static void arcmsr_iop_parking(struct ACB *acb);
/*PRINTFLIKE3*/
static void arcmsr_log(struct ACB *acb, int level, char *fmt, ...);
/*PRINTFLIKE2*/
static void arcmsr_warn(struct ACB *acb, char *fmt, ...);
static void arcmsr_mutex_init(struct ACB *acb);
static void arcmsr_remove_intr(struct ACB *acb);
static void arcmsr_ccbs_timeout(void* arg);
static void arcmsr_devMap_monitor(void* arg);
static void arcmsr_pcidev_disattach(struct ACB *acb);
static void arcmsr_iop_message_read(struct ACB *acb);
static void arcmsr_free_ccb(struct CCB *ccb);
static void arcmsr_post_ioctldata2iop(struct ACB *acb);
static void arcmsr_report_sense_info(struct CCB *ccb);
static void arcmsr_init_list_head(struct list_head *list);
static void arcmsr_enable_allintr(struct ACB *acb, uint32_t intmask_org);
static void arcmsr_done4abort_postqueue(struct ACB *acb);
static void arcmsr_list_add_tail(kmutex_t *list_lock,
    struct list_head *new_one, struct list_head *head);
static int arcmsr_name_node(dev_info_t *dip, char *name, int len);
static int arcmsr_seek_cmd2abort(struct ACB *acb, struct scsi_pkt *abortpkt);
static int arcmsr_iop_message_xfer(struct ACB *acb, struct scsi_pkt *pkt);
static int arcmsr_post_ccb(struct ACB *acb, struct CCB *ccb);
static int arcmsr_parse_devname(char *devnm, int *tgt, int *lun);
static int arcmsr_do_ddi_attach(dev_info_t *dev_info, int instance);
static uint8_t arcmsr_iop_reset(struct ACB *acb);
static uint32_t arcmsr_disable_allintr(struct ACB *acb);
static uint32_t arcmsr_iop_confirm(struct ACB *acb);
static struct CCB *arcmsr_get_freeccb(struct ACB *acb);
static void arcmsr_flush_hba_cache(struct ACB *acb);
static void arcmsr_flush_hbb_cache(struct ACB *acb);
static void arcmsr_flush_hbc_cache(struct ACB *acb);
static void arcmsr_stop_hba_bgrb(struct ACB *acb);
static void arcmsr_stop_hbb_bgrb(struct ACB *acb);
static void arcmsr_stop_hbc_bgrb(struct ACB *acb);
static void arcmsr_start_hba_bgrb(struct ACB *acb);
static void arcmsr_start_hbb_bgrb(struct ACB *acb);
static void arcmsr_start_hbc_bgrb(struct ACB *acb);
static void arcmsr_mutex_destroy(struct ACB *acb);
static void arcmsr_polling_hba_ccbdone(struct ACB *acb, struct CCB *poll_ccb);
static void arcmsr_polling_hbb_ccbdone(struct ACB *acb, struct CCB *poll_ccb);
static void arcmsr_polling_hbc_ccbdone(struct ACB *acb, struct CCB *poll_ccb);
static void arcmsr_build_ccb(struct CCB *ccb);
static int arcmsr_tran_bus_config(dev_info_t *parent, uint_t flags,
    ddi_bus_config_op_t op, void *arg, dev_info_t **childp);
static int arcmsr_name_node(dev_info_t *dip, char *name, int len);
static dev_info_t *arcmsr_find_child(struct ACB *acb, uint16_t tgt,
    uint8_t lun);
static struct QBUFFER *arcmsr_get_iop_rqbuffer(struct ACB *acb);

static int arcmsr_add_intr(struct ACB *, int);

static void *arcmsr_soft_state = NULL;

static ddi_dma_attr_t arcmsr_dma_attr = {
        DMA_ATTR_V0,            /* ddi_dma_attr version */
        0,                      /* low DMA address range */
        0xffffffffffffffffull,  /* high DMA address range */
        0x00ffffff,             /* DMA counter counter upper bound */
        1,                      /* DMA address alignment requirements */
        DEFAULT_BURSTSIZE | BURST32 | BURST64,  /* burst sizes */
        1,                      /* minimum effective DMA size */
        ARCMSR_MAX_XFER_LEN,    /* maximum DMA xfer size */
        /*
         * The dma_attr_seg field supplies the limit of each Scatter/Gather
         * list element's "address+length". The Intel IOP331 can not use
         * segments over the 4G boundary due to segment boundary restrictions
         */
        0xffffffff,
        ARCMSR_MAX_SG_ENTRIES,  /* scatter/gather list count */
        1,                      /* device granularity */
        DDI_DMA_FORCE_PHYSICAL  /* Bus specific DMA flags */
};


static ddi_dma_attr_t arcmsr_ccb_attr = {
        DMA_ATTR_V0,    /* ddi_dma_attr version */
        0,              /* low DMA address range */
        0xffffffff,     /* high DMA address range */
        0x00ffffff,     /* DMA counter counter upper bound */
        1,              /* default byte alignment */
        DEFAULT_BURSTSIZE | BURST32 | BURST64,   /* burst sizes */
        1,              /* minimum effective DMA size */
        0xffffffff,     /* maximum DMA xfer size */
        0x00ffffff,     /* max segment size, segment boundary restrictions */
        1,              /* scatter/gather list count */
        1,              /* device granularity */
        DDI_DMA_FORCE_PHYSICAL  /* Bus specific DMA flags */
};


static struct cb_ops arcmsr_cb_ops = {
        scsi_hba_open,          /* open(9E) */
        scsi_hba_close,         /* close(9E) */
        nodev,                  /* strategy(9E), returns ENXIO */
        nodev,                  /* print(9E) */
        nodev,                  /* dump(9E) Cannot be used as a dump device */
        nodev,                  /* read(9E) */
        nodev,                  /* write(9E) */
        arcmsr_cb_ioctl,        /* ioctl(9E) */
        nodev,                  /* devmap(9E) */
        nodev,                  /* mmap(9E) */
        nodev,                  /* segmap(9E) */
        NULL,                   /* chpoll(9E) returns ENXIO */
        nodev,                  /* prop_op(9E) */
        NULL,                   /* streamtab(9S) */
        D_MP,
        CB_REV,
        nodev,                  /* aread(9E) */
        nodev                   /* awrite(9E) */
};

static struct dev_ops arcmsr_ops = {
        DEVO_REV,               /* devo_rev */
        0,                      /* reference count */
        nodev,                  /* getinfo */
        nulldev,                /* identify */
        nulldev,                /* probe */
        arcmsr_attach,          /* attach */
        arcmsr_detach,          /* detach */
        arcmsr_reset,           /* reset, shutdown, reboot notify */
        &arcmsr_cb_ops,         /* driver operations */
        NULL,                   /* bus operations */
        NULL                    /* power */
};

static struct modldrv arcmsr_modldrv = {
        &mod_driverops,                 /* Type of module. This is a driver. */
        "ARECA RAID Controller",        /* module name, from arcmsr.h */
        &arcmsr_ops,                    /* driver ops */
};

static struct modlinkage arcmsr_modlinkage = {
        MODREV_1,
        &arcmsr_modldrv,
        NULL
};


int
_init(void)
{
        int ret;

        ret = ddi_soft_state_init(&arcmsr_soft_state, sizeof (struct ACB), 1);
        if (ret != 0) {
                return (ret);
        }
        if ((ret = scsi_hba_init(&arcmsr_modlinkage)) != 0) {
                ddi_soft_state_fini(&arcmsr_soft_state);
                return (ret);
        }

        if ((ret = mod_install(&arcmsr_modlinkage)) != 0) {
                scsi_hba_fini(&arcmsr_modlinkage);
                if (arcmsr_soft_state != NULL) {
                        ddi_soft_state_fini(&arcmsr_soft_state);
                }
        }
        return (ret);
}


int
_fini(void)
{
        int ret;

        ret = mod_remove(&arcmsr_modlinkage);
        if (ret == 0) {
                /* if ret = 0 , said driver can remove */
                scsi_hba_fini(&arcmsr_modlinkage);
                if (arcmsr_soft_state != NULL) {
                        ddi_soft_state_fini(&arcmsr_soft_state);
                }
        }
        return (ret);
}


int
_info(struct modinfo *modinfop)
{
        return (mod_info(&arcmsr_modlinkage, modinfop));
}


/*
 *      Function: arcmsr_attach(9E)
 *   Description: Set up all device state and allocate data structures,
 *                mutexes, condition variables, etc. for device operation.
 *                Set mt_attr property for driver to indicate MT-safety.
 *                Add interrupts needed.
 *         Input: dev_info_t *dev_info, ddi_attach_cmd_t cmd
 *        Output: Return DDI_SUCCESS if device is ready,
 *                        else return DDI_FAILURE
 */
static int
arcmsr_attach(dev_info_t *dev_info, ddi_attach_cmd_t cmd)
{
        scsi_hba_tran_t *hba_trans;
        struct ACB *acb;

        switch (cmd) {
        case DDI_ATTACH:
                return (arcmsr_do_ddi_attach(dev_info,
                    ddi_get_instance(dev_info)));
        case DDI_RESUME:
                /*
                 * There is no hardware state to restart and no
                 * timeouts to restart since we didn't DDI_SUSPEND with
                 * active cmds or active timeouts We just need to
                 * unblock waiting threads and restart I/O the code
                 */
                hba_trans = ddi_get_driver_private(dev_info);
                if (hba_trans == NULL) {
                        return (DDI_FAILURE);
                }
                acb = hba_trans->tran_hba_private;
                mutex_enter(&acb->acb_mutex);
                arcmsr_iop_init(acb);

                /* restart ccbs "timeout" watchdog */
                acb->timeout_count = 0;
                acb->timeout_id = timeout(arcmsr_ccbs_timeout, (caddr_t)acb,
                    (ARCMSR_TIMEOUT_WATCH * drv_usectohz(1000000)));
                acb->timeout_sc_id = timeout(arcmsr_devMap_monitor,
                    (caddr_t)acb,
                    (ARCMSR_DEV_MAP_WATCH * drv_usectohz(1000000)));
                mutex_exit(&acb->acb_mutex);
                return (DDI_SUCCESS);

        default:
                return (DDI_FAILURE);
        }
}

/*
 *    Function: arcmsr_detach(9E)
 * Description: Remove all device allocation and system resources, disable
 *                      device interrupt.
 *       Input: dev_info_t *dev_info
 *                      ddi_detach_cmd_t cmd
 *      Output: Return DDI_SUCCESS if done,
 *                      else returnDDI_FAILURE
 */
static int
arcmsr_detach(dev_info_t *dev_info, ddi_detach_cmd_t cmd)
{
        int instance;
        struct ACB *acb;

        instance = ddi_get_instance(dev_info);
        acb = ddi_get_soft_state(arcmsr_soft_state, instance);
        if (acb == NULL)
                return (DDI_FAILURE);

        switch (cmd) {
        case DDI_DETACH:
                mutex_enter(&acb->acb_mutex);
                if (acb->timeout_id != 0) {
                        mutex_exit(&acb->acb_mutex);
                        (void) untimeout(acb->timeout_id);
                        mutex_enter(&acb->acb_mutex);
                        acb->timeout_id = 0;
                }
                if (acb->timeout_sc_id != 0) {
                        mutex_exit(&acb->acb_mutex);
                        (void) untimeout(acb->timeout_sc_id);
                        mutex_enter(&acb->acb_mutex);
                        acb->timeout_sc_id = 0;
                }
                arcmsr_pcidev_disattach(acb);
                /* Remove interrupt set up by ddi_add_intr */
                arcmsr_remove_intr(acb);
                /* unbind mapping object to handle */
                (void) ddi_dma_unbind_handle(acb->ccbs_pool_handle);
                /* Free ccb pool memory */
                ddi_dma_mem_free(&acb->ccbs_acc_handle);
                /* Free DMA handle */
                ddi_dma_free_handle(&acb->ccbs_pool_handle);
                ddi_regs_map_free(&acb->reg_mu_acc_handle0);
                if (scsi_hba_detach(dev_info) != DDI_SUCCESS)
                        arcmsr_warn(acb, "Unable to detach instance cleanly "
                            "(should not happen)");
                /* free scsi_hba_transport from scsi_hba_tran_alloc */
                scsi_hba_tran_free(acb->scsi_hba_transport);
                ddi_taskq_destroy(acb->taskq);
                ddi_prop_remove_all(dev_info);
                mutex_exit(&acb->acb_mutex);
                arcmsr_mutex_destroy(acb);
                pci_config_teardown(&acb->pci_acc_handle);
                ddi_set_driver_private(dev_info, NULL);
                ddi_soft_state_free(arcmsr_soft_state, instance);
                return (DDI_SUCCESS);
        case DDI_SUSPEND:
                mutex_enter(&acb->acb_mutex);
                if (acb->timeout_id != 0) {
                        acb->acb_flags |= ACB_F_SCSISTOPADAPTER;
                        mutex_exit(&acb->acb_mutex);
                        (void) untimeout(acb->timeout_id);
                        (void) untimeout(acb->timeout_sc_id);
                        mutex_enter(&acb->acb_mutex);
                        acb->timeout_id = 0;
                }

                if (acb->timeout_sc_id != 0) {
                        acb->acb_flags |= ACB_F_SCSISTOPADAPTER;
                        mutex_exit(&acb->acb_mutex);
                        (void) untimeout(acb->timeout_sc_id);
                        mutex_enter(&acb->acb_mutex);
                        acb->timeout_sc_id = 0;
                }

                /* disable all outbound interrupt */
                (void) arcmsr_disable_allintr(acb);
                /* stop adapter background rebuild */
                switch (acb->adapter_type) {
                case ACB_ADAPTER_TYPE_A:
                        arcmsr_stop_hba_bgrb(acb);
                        arcmsr_flush_hba_cache(acb);
                        break;

                case ACB_ADAPTER_TYPE_B:
                        arcmsr_stop_hbb_bgrb(acb);
                        arcmsr_flush_hbb_cache(acb);
                        break;

                case ACB_ADAPTER_TYPE_C:
                        arcmsr_stop_hbc_bgrb(acb);
                        arcmsr_flush_hbc_cache(acb);
                        break;
                }
                mutex_exit(&acb->acb_mutex);
                return (DDI_SUCCESS);
        default:
                return (DDI_FAILURE);
        }
}

static int
arcmsr_reset(dev_info_t *resetdev, ddi_reset_cmd_t cmd)
{
        struct ACB *acb;
        scsi_hba_tran_t *scsi_hba_transport;
        _NOTE(ARGUNUSED(cmd));

        scsi_hba_transport = ddi_get_driver_private(resetdev);
        if (scsi_hba_transport == NULL)
                return (DDI_FAILURE);

        acb = (struct ACB *)scsi_hba_transport->tran_hba_private;
        if (!acb)
                return (DDI_FAILURE);

        arcmsr_pcidev_disattach(acb);

        return (DDI_SUCCESS);
}

static int
arcmsr_cb_ioctl(dev_t dev, int ioctl_cmd, intptr_t arg, int mode,
    cred_t *credp, int *rvalp)
{
        struct ACB *acb;
        struct CMD_MESSAGE_FIELD *pktioctlfld;
        int retvalue = 0;
        int instance = MINOR2INST(getminor(dev));

        if (instance < 0)
                return (ENXIO);

        if (secpolicy_sys_config(credp, B_FALSE) != 0)
                return (EPERM);

        acb = ddi_get_soft_state(arcmsr_soft_state, instance);
        if (acb == NULL)
                return (ENXIO);

        pktioctlfld = kmem_zalloc(sizeof (struct CMD_MESSAGE_FIELD), KM_SLEEP);

        mutex_enter(&acb->ioctl_mutex);
        if (ddi_copyin((void *)arg, pktioctlfld,
            sizeof (struct CMD_MESSAGE_FIELD), mode) != 0) {
                retvalue = ENXIO;
                goto ioctl_out;
        }

        if (memcmp(pktioctlfld->cmdmessage.Signature, "ARCMSR", 6) != 0) {
                /* validity check */
                retvalue = ENXIO;
                goto ioctl_out;
        }

        switch ((unsigned int)ioctl_cmd) {
        case ARCMSR_MESSAGE_READ_RQBUFFER:
        {
                uint8_t *ver_addr;
                uint8_t *pQbuffer, *ptmpQbuffer;
                int32_t allxfer_len = 0;

                ver_addr = kmem_zalloc(MSGDATABUFLEN, KM_SLEEP);
                ptmpQbuffer = ver_addr;
                while ((acb->rqbuf_firstidx != acb->rqbuf_lastidx) &&
                    (allxfer_len < (MSGDATABUFLEN - 1))) {
                        /* copy READ QBUFFER to srb */
                        pQbuffer = &acb->rqbuffer[acb->rqbuf_firstidx];
                        (void) memcpy(ptmpQbuffer, pQbuffer, 1);
                        acb->rqbuf_firstidx++;
                        /* if last index number set it to 0 */
                        acb->rqbuf_firstidx %= ARCMSR_MAX_QBUFFER;
                        ptmpQbuffer++;
                        allxfer_len++;
                }

                if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
                        struct QBUFFER *prbuffer;
                        uint8_t *pQbuffer;
                        uint8_t *iop_data;
                        int32_t iop_len;

                        acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
                        prbuffer = arcmsr_get_iop_rqbuffer(acb);
                        iop_data = (uint8_t *)prbuffer->data;
                        iop_len = (int32_t)prbuffer->data_len;
                        /*
                         * this iop data does no chance to make me overflow
                         * again here, so just do it
                         */
                        while (iop_len > 0) {
                                pQbuffer = &acb->rqbuffer[acb->rqbuf_lastidx];
                                (void) memcpy(pQbuffer, iop_data, 1);
                                acb->rqbuf_lastidx++;
                                /* if last index number set it to 0 */
                                acb->rqbuf_lastidx %= ARCMSR_MAX_QBUFFER;
                                iop_data++;
                                iop_len--;
                        }
                        /* let IOP know data has been read */
                        arcmsr_iop_message_read(acb);
                }
                (void) memcpy(pktioctlfld->messagedatabuffer,
                    ver_addr, allxfer_len);
                pktioctlfld->cmdmessage.Length = allxfer_len;
                pktioctlfld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_OK;

                if (ddi_copyout(pktioctlfld, (void *)arg,
                    sizeof (struct CMD_MESSAGE_FIELD), mode) != 0)
                        retvalue = ENXIO;

                kmem_free(ver_addr, MSGDATABUFLEN);
                break;
        }

        case ARCMSR_MESSAGE_WRITE_WQBUFFER:
        {
                uint8_t *ver_addr;
                int32_t my_empty_len, user_len;
                int32_t wqbuf_firstidx, wqbuf_lastidx;
                uint8_t *pQbuffer, *ptmpuserbuffer;

                ver_addr = kmem_zalloc(MSGDATABUFLEN, KM_SLEEP);

                ptmpuserbuffer = ver_addr;
                user_len = min(pktioctlfld->cmdmessage.Length,
                    MSGDATABUFLEN);
                (void) memcpy(ptmpuserbuffer,
                    pktioctlfld->messagedatabuffer, user_len);
                /*
                 * check ifdata xfer length of this request will overflow
                 * my array qbuffer
                 */
                wqbuf_lastidx = acb->wqbuf_lastidx;
                wqbuf_firstidx = acb->wqbuf_firstidx;
                if (wqbuf_lastidx != wqbuf_firstidx) {
                        arcmsr_post_ioctldata2iop(acb);
                        pktioctlfld->cmdmessage.ReturnCode =
                            ARCMSR_MESSAGE_RETURNCODE_ERROR;
                } else {
                        my_empty_len = (wqbuf_firstidx - wqbuf_lastidx - 1)
                            & (ARCMSR_MAX_QBUFFER - 1);
                        if (my_empty_len >= user_len) {
                                while (user_len > 0) {
                                        /* copy srb data to wqbuffer */
                                        pQbuffer =
                                            &acb->wqbuffer[acb->wqbuf_lastidx];
                                        (void) memcpy(pQbuffer,
                                            ptmpuserbuffer, 1);
                                        acb->wqbuf_lastidx++;
                                        /* iflast index number set it to 0 */
                                        acb->wqbuf_lastidx %=
                                            ARCMSR_MAX_QBUFFER;
                                        ptmpuserbuffer++;
                                        user_len--;
                                }
                                /* post first Qbuffer */
                                if (acb->acb_flags &
                                    ACB_F_MESSAGE_WQBUFFER_CLEARED) {
                                        acb->acb_flags &=
                                            ~ACB_F_MESSAGE_WQBUFFER_CLEARED;
                                        arcmsr_post_ioctldata2iop(acb);
                                }
                                pktioctlfld->cmdmessage.ReturnCode =
                                    ARCMSR_MESSAGE_RETURNCODE_OK;
                        } else {
                                pktioctlfld->cmdmessage.ReturnCode =
                                    ARCMSR_MESSAGE_RETURNCODE_ERROR;
                        }
                }
                if (ddi_copyout(pktioctlfld, (void *)arg,
                    sizeof (struct CMD_MESSAGE_FIELD), mode) != 0)
                        retvalue = ENXIO;

                kmem_free(ver_addr, MSGDATABUFLEN);
                break;
        }

        case ARCMSR_MESSAGE_CLEAR_RQBUFFER:
        {
                uint8_t *pQbuffer = acb->rqbuffer;

                if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
                        acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
                        arcmsr_iop_message_read(acb);
                }
                acb->acb_flags |= ACB_F_MESSAGE_RQBUFFER_CLEARED;
                acb->rqbuf_firstidx = 0;
                acb->rqbuf_lastidx = 0;
                bzero(pQbuffer, ARCMSR_MAX_QBUFFER);
                /* report success */
                pktioctlfld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_OK;

                if (ddi_copyout(pktioctlfld, (void *)arg,
                    sizeof (struct CMD_MESSAGE_FIELD), mode) != 0)
                        retvalue = ENXIO;
                break;
        }

        case ARCMSR_MESSAGE_CLEAR_WQBUFFER:
        {
                uint8_t *pQbuffer = acb->wqbuffer;

                if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
                        acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
                        arcmsr_iop_message_read(acb);
                }
                acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
                    ACB_F_MESSAGE_WQBUFFER_READ);
                acb->wqbuf_firstidx = 0;
                acb->wqbuf_lastidx = 0;
                bzero(pQbuffer, ARCMSR_MAX_QBUFFER);
                /* report success */
                pktioctlfld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_OK;

                if (ddi_copyout(pktioctlfld, (void *)arg,
                    sizeof (struct CMD_MESSAGE_FIELD), mode) != 0)
                        retvalue = ENXIO;
                break;
        }

        case ARCMSR_MESSAGE_CLEAR_ALLQBUFFER:
        {
                uint8_t *pQbuffer;

                if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
                        acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
                        arcmsr_iop_message_read(acb);
                }
                acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
                    ACB_F_MESSAGE_RQBUFFER_CLEARED |
                    ACB_F_MESSAGE_WQBUFFER_READ);
                acb->rqbuf_firstidx = 0;
                acb->rqbuf_lastidx = 0;
                acb->wqbuf_firstidx = 0;
                acb->wqbuf_lastidx = 0;
                pQbuffer = acb->rqbuffer;
                bzero(pQbuffer, sizeof (struct QBUFFER));
                pQbuffer = acb->wqbuffer;
                bzero(pQbuffer, sizeof (struct QBUFFER));
                /* report success */
                pktioctlfld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_OK;
                if (ddi_copyout(pktioctlfld, (void *)arg,
                    sizeof (struct CMD_MESSAGE_FIELD), mode) != 0)
                        retvalue = ENXIO;
                break;
        }

        case ARCMSR_MESSAGE_REQUEST_RETURN_CODE_3F:
                pktioctlfld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_3F;
                if (ddi_copyout(pktioctlfld, (void *)arg,
                    sizeof (struct CMD_MESSAGE_FIELD), mode) != 0)
                        retvalue = ENXIO;
                break;

        /* Not supported: ARCMSR_MESSAGE_SAY_HELLO */
        case ARCMSR_MESSAGE_SAY_GOODBYE:
                arcmsr_iop_parking(acb);
                break;

        case ARCMSR_MESSAGE_FLUSH_ADAPTER_CACHE:
                switch (acb->adapter_type) {
                case ACB_ADAPTER_TYPE_A:
                        arcmsr_flush_hba_cache(acb);
                        break;
                case ACB_ADAPTER_TYPE_B:
                        arcmsr_flush_hbb_cache(acb);
                        break;
                case ACB_ADAPTER_TYPE_C:
                        arcmsr_flush_hbc_cache(acb);
                        break;
                }
                break;

        default:
                mutex_exit(&acb->ioctl_mutex);
                kmem_free(pktioctlfld, sizeof (struct CMD_MESSAGE_FIELD));
                return (scsi_hba_ioctl(dev, ioctl_cmd, arg, mode, credp,
                    rvalp));
        }

ioctl_out:
        kmem_free(pktioctlfld, sizeof (struct CMD_MESSAGE_FIELD));
        mutex_exit(&acb->ioctl_mutex);

        return (retvalue);
}


/*
 *    Function: arcmsr_tran_tgt_init
 * Description: Called when initializing a target device instance. If
 *                      no per-target initialization is required, the HBA
 *                      may leave tran_tgt_init to NULL
 *       Input:
 *                      dev_info_t *host_dev_info,
 *                      dev_info_t *target_dev_info,
 *                      scsi_hba_tran_t *tran,
 *                      struct scsi_device *sd
 *
 *      Return: DDI_SUCCESS if success, else return DDI_FAILURE
 *
 *  entry point enables the HBA to allocate and/or initialize any per-
 *  target resources.
 *  It also enables the HBA to qualify the device's address as valid and
 *  supportable for that particular HBA.
 *  By returning DDI_FAILURE, the instance of the target driver for that
 *  device will not be probed or attached.
 *      This entry point is not required, and if none is supplied,
 *  the framework will attempt to probe and attach all possible instances
 *  of the appropriate target drivers.
 */
static int
arcmsr_tran_tgt_init(dev_info_t *host_dev_info, dev_info_t *target_dev_info,
    scsi_hba_tran_t *tran, struct scsi_device *sd)
{
        uint16_t  target;
        uint8_t  lun;
        struct ACB *acb = tran->tran_hba_private;

        _NOTE(ARGUNUSED(tran, target_dev_info, host_dev_info))

        target = sd->sd_address.a_target;
        lun = sd->sd_address.a_lun;
        if ((target >= ARCMSR_MAX_TARGETID) || (lun >= ARCMSR_MAX_TARGETLUN)) {
                return (DDI_FAILURE);
        }


        if (ndi_dev_is_persistent_node(target_dev_info) == 0) {
                /*
                 * If no persistent node exist, we don't allow .conf node
                 * to be created.
                 */
                if (arcmsr_find_child(acb, target, lun) != NULL) {
                        if ((ndi_merge_node(target_dev_info,
                            arcmsr_name_node) != DDI_SUCCESS)) {
                                return (DDI_SUCCESS);
                        }
                }
                return (DDI_FAILURE);
        }

        return (DDI_SUCCESS);
}

/*
 *         Function: arcmsr_tran_getcap(9E)
 *      Description: Get the capability named, and returnits value.
 *    Return Values: current value of capability, ifdefined
 *                           -1 ifcapability is not defined
 * ------------------------------------------------------
 *         Common Capability Strings Array
 * ------------------------------------------------------
 *      #define SCSI_CAP_DMA_MAX                0
 *      #define SCSI_CAP_MSG_OUT                1
 *      #define SCSI_CAP_DISCONNECT             2
 *      #define SCSI_CAP_SYNCHRONOUS            3
 *      #define SCSI_CAP_WIDE_XFER              4
 *      #define SCSI_CAP_PARITY                 5
 *      #define SCSI_CAP_INITIATOR_ID           6
 *      #define SCSI_CAP_UNTAGGED_QING          7
 *      #define SCSI_CAP_TAGGED_QING            8
 *      #define SCSI_CAP_ARQ                    9
 *      #define SCSI_CAP_LINKED_CMDS            10 a
 *      #define SCSI_CAP_SECTOR_SIZE            11 b
 *      #define SCSI_CAP_TOTAL_SECTORS          12 c
 *      #define SCSI_CAP_GEOMETRY               13 d
 *      #define SCSI_CAP_RESET_NOTIFICATION     14 e
 *      #define SCSI_CAP_QFULL_RETRIES          15 f
 *      #define SCSI_CAP_QFULL_RETRY_INTERVAL   16 10
 *      #define SCSI_CAP_SCSI_VERSION           17 11
 *      #define SCSI_CAP_INTERCONNECT_TYPE      18 12
 *      #define SCSI_CAP_LUN_RESET              19 13
 */
static int
arcmsr_tran_getcap(struct scsi_address *ap, char *cap, int whom)
{
        int capability = 0;
        struct ACB *acb = (struct ACB *)ap->a_hba_tran->tran_hba_private;

        if (cap == NULL || whom == 0) {
                return (DDI_FAILURE);
        }

        mutex_enter(&acb->acb_mutex);
        if (acb->devstate[ap->a_target][ap->a_lun] == ARECA_RAID_GONE) {
                mutex_exit(&acb->acb_mutex);
                return (-1);
        }
        switch (scsi_hba_lookup_capstr(cap)) {
        case SCSI_CAP_MSG_OUT:
        case SCSI_CAP_DISCONNECT:
        case SCSI_CAP_WIDE_XFER:
        case SCSI_CAP_TAGGED_QING:
        case SCSI_CAP_UNTAGGED_QING:
        case SCSI_CAP_PARITY:
        case SCSI_CAP_ARQ:
                capability = 1;
                break;
        case SCSI_CAP_SECTOR_SIZE:
                capability = ARCMSR_DEV_SECTOR_SIZE;
                break;
        case SCSI_CAP_DMA_MAX:
                /* Limit to 16MB max transfer */
                capability = ARCMSR_MAX_XFER_LEN;
                break;
        case SCSI_CAP_INITIATOR_ID:
                capability = ARCMSR_SCSI_INITIATOR_ID;
                break;
        case SCSI_CAP_GEOMETRY:
                /* head , track , cylinder */
                capability = (255 << 16) | 63;
                break;
        default:
                capability = -1;
                break;
        }
        mutex_exit(&acb->acb_mutex);
        return (capability);
}

/*
 *      Function: arcmsr_tran_setcap(9E)
 *   Description: Set the specific capability.
 * Return Values: 1 - capability exists and can be set to new value
 *                        0 - capability could not be set to new value
 *                       -1 - no such capability
 */
static int
arcmsr_tran_setcap(struct scsi_address *ap, char *cap, int value, int whom)
{
        _NOTE(ARGUNUSED(value))

        int supported = 0;
        struct ACB *acb = (struct ACB *)ap->a_hba_tran->tran_hba_private;

        if (cap == NULL || whom == 0) {
                return (-1);
        }

        mutex_enter(&acb->acb_mutex);
        if (acb->devstate[ap->a_target][ap->a_lun] == ARECA_RAID_GONE) {
                mutex_exit(&acb->acb_mutex);
                return (-1);
        }
        switch (supported = scsi_hba_lookup_capstr(cap)) {
        case SCSI_CAP_ARQ:                      /* 9 auto request sense */
        case SCSI_CAP_UNTAGGED_QING:            /* 7 */
        case SCSI_CAP_TAGGED_QING:              /* 8 */
                /* these are always on, and cannot be turned off */
                supported = (value == 1) ? 1 : 0;
                break;
        case SCSI_CAP_TOTAL_SECTORS:            /* c */
                supported = 1;
                break;
        case SCSI_CAP_DISCONNECT:               /* 2 */
        case SCSI_CAP_WIDE_XFER:                /* 4 */
        case SCSI_CAP_INITIATOR_ID:             /* 6 */
        case SCSI_CAP_DMA_MAX:                  /* 0 */
        case SCSI_CAP_MSG_OUT:                  /* 1 */
        case SCSI_CAP_PARITY:                   /* 5 */
        case SCSI_CAP_LINKED_CMDS:              /* a */
        case SCSI_CAP_RESET_NOTIFICATION:       /* e */
        case SCSI_CAP_SECTOR_SIZE:              /* b */
                /* these are not settable */
                supported = 0;
                break;
        default:
                supported = -1;
                break;
        }
        mutex_exit(&acb->acb_mutex);
        return (supported);
}


/*
 *      Function: arcmsr_tran_init_pkt
 * Return Values: pointer to scsi_pkt, or NULL
 *   Description: simultaneously allocate both a scsi_pkt(9S) structure and
 *                DMA resources for that pkt.
 *                Called by kernel on behalf of a target driver
 *                        calling scsi_init_pkt(9F).
 *                        Refer to tran_init_pkt(9E) man page
 *       Context: Can be called from different kernel process threads.
 *                        Can be called by interrupt thread.
 * Allocates SCSI packet and DMA resources
 */
static struct
scsi_pkt *arcmsr_tran_init_pkt(struct scsi_address *ap,
    register struct scsi_pkt *pkt, struct buf *bp, int cmdlen, int statuslen,
    int tgtlen, int flags, int (*callback)(), caddr_t arg)
{
        struct CCB *ccb;
        struct ARCMSR_CDB *arcmsr_cdb;
        struct ACB *acb;
        int old_pkt_flag;

        acb = (struct ACB *)ap->a_hba_tran->tran_hba_private;

        if (acb->acb_flags & ACB_F_BUS_RESET) {
                return (NULL);
        }
        if (pkt == NULL) {
                /* get free CCB */
                (void) ddi_dma_sync(acb->ccbs_pool_handle, 0, 0,
                    DDI_DMA_SYNC_FORKERNEL);
                ccb = arcmsr_get_freeccb(acb);
                if (ccb == (struct CCB *)NULL) {
                        return (NULL);
                }

                if (statuslen < sizeof (struct scsi_arq_status)) {
                        statuslen = sizeof (struct scsi_arq_status);
                }
                pkt = scsi_hba_pkt_alloc(acb->dev_info, ap, cmdlen,
                    statuslen, tgtlen, sizeof (void *), callback, arg);
                if (pkt == NULL) {
                        arcmsr_warn(acb, "scsi pkt allocation failed");
                        arcmsr_free_ccb(ccb);
                        return (NULL);
                }
                /* Initialize CCB */
                ccb->pkt = pkt;
                ccb->pkt_dma_handle = NULL;
                /* record how many sg are needed to xfer on this pkt */
                ccb->pkt_ncookies = 0;
                /* record how many sg we got from this window */
                ccb->pkt_cookie = 0;
                /* record how many windows have partial dma map set */
                ccb->pkt_nwin = 0;
                /* record current sg window position */
                ccb->pkt_curwin = 0;
                ccb->pkt_dma_len = 0;
                ccb->pkt_dma_offset = 0;
                ccb->resid_dmacookie.dmac_size = 0;

                /*
                 * we will still use this point for we want to fake some
                 * information in tran_start
                 */
                ccb->bp = bp;

                /* Initialize arcmsr_cdb */
                arcmsr_cdb = &ccb->arcmsr_cdb;
                bzero(arcmsr_cdb, sizeof (struct ARCMSR_CDB));
                arcmsr_cdb->Bus = 0;
                arcmsr_cdb->Function = 1;
                arcmsr_cdb->LUN = ap->a_lun;
                arcmsr_cdb->TargetID = ap->a_target;
                arcmsr_cdb->CdbLength = (uint8_t)cmdlen;
                arcmsr_cdb->Context = (uintptr_t)arcmsr_cdb;

                /* Fill in the rest of the structure */
                pkt->pkt_ha_private = ccb;
                pkt->pkt_address = *ap;
                pkt->pkt_comp = NULL;
                pkt->pkt_flags = 0;
                pkt->pkt_time = 0;
                pkt->pkt_resid = 0;
                pkt->pkt_statistics = 0;
                pkt->pkt_reason = 0;
                old_pkt_flag = 0;
        } else {
                ccb = pkt->pkt_ha_private;
                if (ccb->ccb_state & ARCMSR_ABNORMAL_MASK) {
                        if (!(ccb->ccb_state & ARCMSR_CCB_BACK)) {
                                return (NULL);
                        }
                }

                /*
                 * you cannot update CdbLength with cmdlen here, it would
                 * cause a data compare error
                 */
                ccb->ccb_state = ARCMSR_CCB_UNBUILD;
                old_pkt_flag = 1;
        }

        /* Second step : dma allocation/move */
        if (bp && bp->b_bcount != 0) {
                /*
                 * system had a lot of data trunk need to xfer, from...20 byte
                 * to 819200 byte.
                 * arcmsr_dma_alloc will get pkt_dma_handle (not null) until
                 * this lot of data trunk xfer done this mission will be done
                 * by some of continue READ or WRITE scsi command, till this
                 * lot of data trunk xfer completed.
                 * arcmsr_dma_move do the action repeatedly, and use the same
                 * ccb till this lot of data trunk xfer complete notice.
                 * when after the arcmsr_tran_init_pkt returns the solaris
                 * kernel is by your pkt_resid and its b_bcount to give you
                 * which type of scsi command descriptor to implement the
                 * length of folowing arcmsr_tran_start scsi cdb (data length)
                 *
                 * Each transfer should be aligned on a 512 byte boundary
                 */
                if (ccb->pkt_dma_handle == NULL) {
                        if (arcmsr_dma_alloc(acb, pkt, bp, flags, callback) ==
                            DDI_FAILURE) {
                                /*
                                 * the HBA driver is unable to allocate DMA
                                 * resources, it must free the allocated
                                 * scsi_pkt(9S) before returning
                                 */
                                arcmsr_warn(acb, "dma allocation failure");
                                if (old_pkt_flag == 0) {
                                        arcmsr_warn(acb, "dma "
                                            "allocation failed to free "
                                            "scsi hba pkt");
                                        arcmsr_free_ccb(ccb);
                                        scsi_hba_pkt_free(ap, pkt);
                                }
                                return (NULL);
                        }
                } else {
                        /* DMA resources to next DMA window, for old pkt */
                        if (arcmsr_dma_move(acb, pkt, bp) == DDI_FAILURE) {
                                arcmsr_warn(acb, "dma move failed");
                                return (NULL);
                        }
                }
        } else {
                pkt->pkt_resid = 0;
        }
        return (pkt);
}

/*
 *    Function: arcmsr_tran_start(9E)
 * Description: Transport the command in pktp to the target device.
 *              The command is not finished when this returns, only
 *              sent to the target; arcmsr_intr_handler will call
 *              scsi_hba_pkt_comp(pktp) when the target device has done.
 *
 *       Input: struct scsi_address *ap, struct scsi_pkt *pktp
 *      Output: TRAN_ACCEPT if pkt is OK and not driver not busy
 *              TRAN_BUSY if driver is
 *              TRAN_BADPKT if pkt is invalid
 */
static int
arcmsr_tran_start(struct scsi_address *ap, struct scsi_pkt *pkt)
{
        struct ACB *acb;
        struct CCB *ccb;
        int target = ap->a_target;
        int lun = ap->a_lun;

        acb = (struct ACB *)ap->a_hba_tran->tran_hba_private;
        ccb = pkt->pkt_ha_private;
        *pkt->pkt_scbp = STATUS_GOOD; /* clear arq scsi_status */

        if ((ccb->ccb_flags & CCB_FLAG_DMAVALID) &&
            (ccb->ccb_flags & DDI_DMA_CONSISTENT))
                (void) ddi_dma_sync(ccb->pkt_dma_handle, 0, 0,
                    DDI_DMA_SYNC_FORDEV);

        if (ccb->ccb_state == ARCMSR_CCB_UNBUILD)
                arcmsr_build_ccb(ccb);

        if (acb->acb_flags & ACB_F_BUS_RESET) {
                pkt->pkt_reason = CMD_RESET;
                pkt->pkt_statistics |= STAT_BUS_RESET;
                pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
                    STATE_SENT_CMD | STATE_GOT_STATUS);
                if ((ccb->ccb_flags & CCB_FLAG_DMACONSISTENT) &&
                    (pkt->pkt_state & STATE_XFERRED_DATA))
                        (void) ddi_dma_sync(ccb->pkt_dma_handle,
                            0, 0, DDI_DMA_SYNC_FORCPU);

                scsi_hba_pkt_comp(pkt);
                return (TRAN_ACCEPT);
        }

        /* IMPORTANT: Target 16 is a virtual device for iop message transfer */
        if (target == 16) {

                struct buf *bp = ccb->bp;
                uint8_t scsicmd = pkt->pkt_cdbp[0];

                switch (scsicmd) {
                case SCMD_INQUIRY: {
                        if (lun != 0) {
                                ccb->pkt->pkt_reason = CMD_TIMEOUT;
                                ccb->pkt->pkt_statistics |= STAT_TIMEOUT;
                                arcmsr_ccb_complete(ccb, 0);
                                return (TRAN_ACCEPT);
                        }

                        if (bp && bp->b_un.b_addr && bp->b_bcount) {
                                uint8_t inqdata[36];

                                /* The EVDP and pagecode is not supported */
                                if (pkt->pkt_cdbp[1] || pkt->pkt_cdbp[2]) {
                                        inqdata[1] = 0xFF;
                                        inqdata[2] = 0x00;
                                } else {
                                        /* Periph Qualifier & Periph Dev Type */
                                        inqdata[0] = DTYPE_PROCESSOR;
                                        /* rem media bit & Dev Type Modifier */
                                        inqdata[1] = 0;
                                        /* ISO, ECMA, & ANSI versions */
                                        inqdata[2] = 0;
                                        inqdata[3] = 0;
                                        /* length of additional data */
                                        inqdata[4] = 31;
                                        /* Vendor Identification */
                                        bcopy("Areca   ", &inqdata[8], VIDLEN);
                                        /* Product Identification */
                                        bcopy("RAID controller ", &inqdata[16],
                                            PIDLEN);
                                        /* Product Revision */
                                        bcopy(&inqdata[32], "R001", REVLEN);
                                        if (bp->b_flags & (B_PHYS | B_PAGEIO))
                                                bp_mapin(bp);

                                        (void) memcpy(bp->b_un.b_addr,
                                            inqdata, sizeof (inqdata));
                                }
                                ccb->pkt->pkt_state |= STATE_XFERRED_DATA;
                        }
                        arcmsr_ccb_complete(ccb, 0);
                        return (TRAN_ACCEPT);
                }
                case SCMD_WRITE_BUFFER:
                case SCMD_READ_BUFFER: {
                        if (arcmsr_iop_message_xfer(acb, pkt)) {
                                /* error just for retry */
                                ccb->pkt->pkt_reason = CMD_TRAN_ERR;
                                ccb->pkt->pkt_statistics |= STAT_TERMINATED;
                        }
                        ccb->pkt->pkt_state |= STATE_XFERRED_DATA;
                        arcmsr_ccb_complete(ccb, 0);
                        return (TRAN_ACCEPT);
                }
                default:
                        ccb->pkt->pkt_state |= STATE_XFERRED_DATA;
                        arcmsr_ccb_complete(ccb, 0);
                        return (TRAN_ACCEPT);
                }
        }

        if (acb->devstate[target][lun] == ARECA_RAID_GONE) {
                uint8_t block_cmd;

                block_cmd = pkt->pkt_cdbp[0] & 0x0f;
                if (block_cmd == 0x08 || block_cmd == 0x0a) {
                        pkt->pkt_reason = CMD_TIMEOUT;
                        pkt->pkt_statistics |= STAT_TIMEOUT;
                        pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
                            STATE_SENT_CMD | STATE_GOT_STATUS);
                        if ((ccb->ccb_flags & CCB_FLAG_DMACONSISTENT) &&
                            (pkt->pkt_state & STATE_XFERRED_DATA)) {
                                (void) ddi_dma_sync(ccb->pkt_dma_handle,
                                    ccb->pkt_dma_offset,
                                    ccb->pkt_dma_len, DDI_DMA_SYNC_FORCPU);
                        }
                        scsi_hba_pkt_comp(pkt);
                        return (TRAN_ACCEPT);
                }
        }
        mutex_enter(&acb->postq_mutex);
        if (acb->ccboutstandingcount >= ARCMSR_MAX_OUTSTANDING_CMD) {
                ccb->ccb_state = ARCMSR_CCB_RETRY;
                mutex_exit(&acb->postq_mutex);
                return (TRAN_BUSY);
        } else if (arcmsr_post_ccb(acb, ccb) == DDI_FAILURE) {
                arcmsr_warn(acb, "post ccb failure, ccboutstandingcount = %d",
                    acb->ccboutstandingcount);
                mutex_exit(&acb->postq_mutex);
                return (TRAN_FATAL_ERROR);
        }
        mutex_exit(&acb->postq_mutex);
        return (TRAN_ACCEPT);
}

/*
 * Function name: arcmsr_tran_destroy_pkt
 * Return Values: none
 *   Description: Called by kernel on behalf of a target driver
 *                calling scsi_destroy_pkt(9F).
 *                Refer to tran_destroy_pkt(9E) man page
 *       Context: Can be called from different kernel process threads.
 *                Can be called by interrupt thread.
 */
static void
arcmsr_tran_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
{
        struct CCB *ccb = pkt->pkt_ha_private;
        ddi_dma_handle_t pkt_dma_handle = ccb->pkt_dma_handle;

        if (ccb == NULL) {
                return;
        }
        if (ccb->pkt != pkt) {
                return;
        }
        if (ccb->ccb_flags & CCB_FLAG_DMAVALID) {
                ccb->ccb_flags &= ~CCB_FLAG_DMAVALID;
                if (pkt_dma_handle) {
                        (void) ddi_dma_unbind_handle(ccb->pkt_dma_handle);
                }
        }
        if (pkt_dma_handle) {
                (void) ddi_dma_free_handle(&pkt_dma_handle);
        }
        pkt->pkt_ha_private = NULL;
        if (ccb)        {
                if (ccb->ccb_state & ARCMSR_ABNORMAL_MASK) {
                        if (ccb->ccb_state & ARCMSR_CCB_BACK) {
                                arcmsr_free_ccb(ccb);
                        } else {
                                ccb->ccb_state |= ARCMSR_CCB_WAIT4_FREE;
                        }
                } else {
                        arcmsr_free_ccb(ccb);
                }
        }
        scsi_hba_pkt_free(ap, pkt);
}

/*
 * Function name: arcmsr_tran_dmafree()
 * Return Values: none
 *   Description: free dvma resources
 *       Context: Can be called from different kernel process threads.
 *                Can be called by interrupt thread.
 */
static void
arcmsr_tran_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
{
        struct CCB *ccb = pkt->pkt_ha_private;

        if ((ccb == NULL) || (ccb->pkt != pkt)) {
                return;
        }
        if (ccb->ccb_flags & CCB_FLAG_DMAVALID) {
                ccb->ccb_flags &= ~CCB_FLAG_DMAVALID;
                if (ddi_dma_unbind_handle(ccb->pkt_dma_handle) != DDI_SUCCESS) {
                        arcmsr_warn(ccb->acb, "ddi_dma_unbind_handle() failed "
                            "(target %d lun %d)", ap->a_target, ap->a_lun);
                }
                ddi_dma_free_handle(&ccb->pkt_dma_handle);
                ccb->pkt_dma_handle = NULL;
        }
}

/*
 * Function name: arcmsr_tran_sync_pkt()
 * Return Values: none
 *   Description: sync dma
 *       Context: Can be called from different kernel process threads.
 *                Can be called by interrupt thread.
 */
static void
arcmsr_tran_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
{
        struct CCB *ccb;

        ccb = pkt->pkt_ha_private;
        if ((ccb == NULL) || (ccb->pkt != pkt)) {
                return;
        }
        if (ccb->ccb_flags & CCB_FLAG_DMAVALID) {
                if (ddi_dma_sync(ccb->pkt_dma_handle, 0, 0,
                    (ccb->ccb_flags & CCB_FLAG_DMAWRITE) ?
                    DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU) !=
                    DDI_SUCCESS) {
                        arcmsr_warn(ccb->acb,
                            "sync pkt failed for target %d lun %d",
                            ap->a_target, ap->a_lun);
                }
        }
}


/*
 * Function: arcmsr_tran_abort(9E)
 *              SCSA interface routine to abort pkt(s) in progress.
 *              Aborts the pkt specified.  If NULL pkt, aborts ALL pkts.
 * Output:      Return 1 if success
 *              Return 0 if failure
 */
static int
arcmsr_tran_abort(struct scsi_address *ap, struct scsi_pkt *abortpkt)
{
        struct ACB *acb;
        int return_code;

        acb = ap->a_hba_tran->tran_hba_private;

        while (acb->ccboutstandingcount != 0) {
                drv_usecwait(10000);
        }

        mutex_enter(&acb->isr_mutex);
        return_code = arcmsr_seek_cmd2abort(acb, abortpkt);
        mutex_exit(&acb->isr_mutex);

        if (return_code != DDI_SUCCESS) {
                arcmsr_warn(acb, "abort command failed for target %d lun %d",
                    ap->a_target, ap->a_lun);
                return (0);
        }
        return (1);
}

/*
 * Function: arcmsr_tran_reset(9E)
 *           SCSA interface routine to perform scsi resets on either
 *           a specified target or the bus (default).
 *   Output: Return 1 if success
 *           Return 0 if failure
 */
static int
arcmsr_tran_reset(struct scsi_address *ap, int level)
{
        struct ACB *acb;
        int return_code = 1;
        int target = ap->a_target;
        int lun = ap->a_lun;

        /* Are we in the middle of dumping core? */
        if (ddi_in_panic())
                return (return_code);

        acb = (struct ACB *)ap->a_hba_tran->tran_hba_private;
        mutex_enter(&acb->isr_mutex);
        switch (level) {
        case RESET_ALL:         /* 0 */
                acb->num_resets++;
                acb->acb_flags |= ACB_F_BUS_RESET;
                if (acb->timeout_count) {
                        if (arcmsr_iop_reset(acb) != 0) {
                                arcmsr_handle_iop_bus_hold(acb);
                                acb->acb_flags &= ~ACB_F_BUS_HANG_ON;
                        }
                }
                acb->acb_flags &= ~ACB_F_BUS_RESET;
                break;
        case RESET_TARGET:      /* 1 */
                if (acb->devstate[target][lun] == ARECA_RAID_GONE)
                        return_code = 0;
                break;
        case RESET_BUS:         /* 2 */
                return_code = 0;
                break;
        case RESET_LUN:         /* 3 */
                return_code = 0;
                break;
        default:
                return_code = 0;
        }
        mutex_exit(&acb->isr_mutex);
        return (return_code);
}

static int
arcmsr_tran_bus_config(dev_info_t *parent, uint_t flags,
    ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
{
        struct ACB *acb;
        int rval;
        int tgt, lun;

        if ((acb = ddi_get_soft_state(arcmsr_soft_state,
            ddi_get_instance(parent))) == NULL)
                return (NDI_FAILURE);

        ndi_devi_enter(parent);
        switch (op) {
        case BUS_CONFIG_ONE:
                if (arcmsr_parse_devname(arg, &tgt, &lun) != 0) {
                        rval = NDI_FAILURE;
                        break;
                }
                if (acb->device_map[tgt] & 1 << lun) {
                        acb->devstate[tgt][lun] = ARECA_RAID_GOOD;
                        rval = arcmsr_config_lun(acb, tgt, lun, childp);
                }
                break;

        case BUS_CONFIG_DRIVER:
        case BUS_CONFIG_ALL:
                for (tgt = 0; tgt < ARCMSR_MAX_TARGETID; tgt++)
                        for (lun = 0; lun < ARCMSR_MAX_TARGETLUN; lun++)
                                if (acb->device_map[tgt] & 1 << lun) {
                                        acb->devstate[tgt][lun] =
                                            ARECA_RAID_GOOD;
                                        (void) arcmsr_config_lun(acb, tgt,
                                            lun, NULL);
                                }

                rval = NDI_SUCCESS;
                break;
        }
        if (rval == NDI_SUCCESS)
                rval = ndi_busop_bus_config(parent, flags, op, arg, childp, 0);
        ndi_devi_exit(parent);
        return (rval);
}

/*
 * Function name: arcmsr_dma_alloc
 * Return Values: 0 if successful, -1 if failure
 *   Description: allocate DMA resources
 *       Context: Can only be called from arcmsr_tran_init_pkt()
 *     register struct scsi_address     *ap = &((pkt)->pkt_address);
 */
static int
arcmsr_dma_alloc(struct ACB *acb, struct scsi_pkt *pkt,
    struct buf *bp, int flags, int (*callback)())
{
        struct CCB *ccb = pkt->pkt_ha_private;
        int alloc_result, map_method, dma_flags;
        int resid = 0;
        int total_ccb_xferlen = 0;
        int (*cb)(caddr_t);
        uint8_t i;

        /*
         * at this point the PKT SCSI CDB is empty, and dma xfer length
         * is bp->b_bcount
         */

        if (bp->b_flags & B_READ) {
                ccb->ccb_flags &= ~CCB_FLAG_DMAWRITE;
                dma_flags = DDI_DMA_READ;
        } else {
                ccb->ccb_flags |= CCB_FLAG_DMAWRITE;
                dma_flags = DDI_DMA_WRITE;
        }

        if (flags & PKT_CONSISTENT) {
                ccb->ccb_flags |= CCB_FLAG_DMACONSISTENT;
                dma_flags |= DDI_DMA_CONSISTENT;
        }
        if (flags & PKT_DMA_PARTIAL) {
                dma_flags |= DDI_DMA_PARTIAL;
        }

        dma_flags |= DDI_DMA_REDZONE;
        cb = (callback == NULL_FUNC) ? DDI_DMA_DONTWAIT : DDI_DMA_SLEEP;

        alloc_result = ddi_dma_alloc_handle(acb->dev_info, &arcmsr_dma_attr,
            cb, 0, &ccb->pkt_dma_handle);
        if (alloc_result != DDI_SUCCESS) {
                arcmsr_warn(acb, "dma allocate failed (%x)", alloc_result);
                return (DDI_FAILURE);
        }

        map_method = ddi_dma_buf_bind_handle(ccb->pkt_dma_handle,
            bp, dma_flags, cb, 0,
            &ccb->pkt_dmacookies[0],    /* SG List pointer */
            &ccb->pkt_ncookies);        /* number of sgl cookies */

        switch (map_method) {
        case DDI_DMA_PARTIAL_MAP:
                /*
                 * When your main memory size larger then 4G
                 * DDI_DMA_PARTIAL_MAP will be touched.
                 *
                 * We've already set DDI_DMA_PARTIAL in dma_flags,
                 * so if it's now missing, there's something screwy
                 * happening. We plow on....
                 */

                if ((dma_flags & DDI_DMA_PARTIAL) == 0) {
                        arcmsr_warn(acb,
                            "dma partial mapping lost ...impossible case!");
                }
                if (ddi_dma_numwin(ccb->pkt_dma_handle, &ccb->pkt_nwin) ==
                    DDI_FAILURE) {
                        arcmsr_warn(acb, "ddi_dma_numwin() failed");
                }

                if (ddi_dma_getwin(ccb->pkt_dma_handle, ccb->pkt_curwin,
                    &ccb->pkt_dma_offset, &ccb->pkt_dma_len,
                    &ccb->pkt_dmacookies[0], &ccb->pkt_ncookies) ==
                    DDI_FAILURE) {
                        arcmsr_warn(acb, "ddi_dma_getwin failed");
                }

                i = 0;
                /* first cookie is accessed from ccb->pkt_dmacookies[0] */
                total_ccb_xferlen = ccb->pkt_dmacookies[0].dmac_size;
                for (;;) {
                        i++;
                        if ((i == ARCMSR_MAX_SG_ENTRIES) ||
                            (i == ccb->pkt_ncookies) ||
                            (total_ccb_xferlen == ARCMSR_MAX_XFER_LEN)) {
                                break;
                        }
                        /*
                         * next cookie will be retrieved from
                         * ccb->pkt_dmacookies[i]
                         */
                        ddi_dma_nextcookie(ccb->pkt_dma_handle,
                            &ccb->pkt_dmacookies[i]);
                        total_ccb_xferlen += ccb->pkt_dmacookies[i].dmac_size;
                }
                ccb->pkt_cookie = i;
                ccb->arcmsr_cdb.sgcount = i;
                if (total_ccb_xferlen > 512) {
                        resid = total_ccb_xferlen % 512;
                        if (resid != 0) {
                                i--;
                                total_ccb_xferlen -= resid;
                                /* modify last sg length */
                                ccb->pkt_dmacookies[i].dmac_size =
                                    ccb->pkt_dmacookies[i].dmac_size - resid;
                                ccb->resid_dmacookie.dmac_size = resid;
                                ccb->resid_dmacookie.dmac_laddress =
                                    ccb->pkt_dmacookies[i].dmac_laddress +
                                    ccb->pkt_dmacookies[i].dmac_size;
                        }
                }
                ccb->total_dmac_size = total_ccb_xferlen;
                ccb->ccb_flags |= CCB_FLAG_DMAVALID;
                pkt->pkt_resid = bp->b_bcount - ccb->total_dmac_size;

                return (DDI_SUCCESS);

        case DDI_DMA_MAPPED:
                ccb->pkt_nwin = 1; /* all mapped, so only one window */
                ccb->pkt_dma_len = 0;
                ccb->pkt_dma_offset = 0;
                i = 0;
                /* first cookie is accessed from ccb->pkt_dmacookies[0] */
                total_ccb_xferlen = ccb->pkt_dmacookies[0].dmac_size;
                for (;;) {
                        i++;
                        if ((i == ARCMSR_MAX_SG_ENTRIES) ||
                            (i == ccb->pkt_ncookies) ||
                            (total_ccb_xferlen == ARCMSR_MAX_XFER_LEN)) {
                                break;
                        }
                        /*
                         * next cookie will be retrieved from
                         * ccb->pkt_dmacookies[i]
                         */
                        ddi_dma_nextcookie(ccb->pkt_dma_handle,
                            &ccb->pkt_dmacookies[i]);
                        total_ccb_xferlen += ccb->pkt_dmacookies[i].dmac_size;
                }
                ccb->pkt_cookie = i;
                ccb->arcmsr_cdb.sgcount = i;
                if (total_ccb_xferlen > 512) {
                        resid = total_ccb_xferlen % 512;
                        if (resid != 0) {
                                i--;
                                total_ccb_xferlen -= resid;
                                /* modify last sg length */
                                ccb->pkt_dmacookies[i].dmac_size =
                                    ccb->pkt_dmacookies[i].dmac_size - resid;
                                ccb->resid_dmacookie.dmac_size = resid;
                                ccb->resid_dmacookie.dmac_laddress =
                                    ccb->pkt_dmacookies[i].dmac_laddress +
                                    ccb->pkt_dmacookies[i].dmac_size;
                        }
                }
                ccb->total_dmac_size = total_ccb_xferlen;
                ccb->ccb_flags |= CCB_FLAG_DMAVALID;
                pkt->pkt_resid = bp->b_bcount - ccb->total_dmac_size;
                return (DDI_SUCCESS);

        case DDI_DMA_NORESOURCES:
                arcmsr_warn(acb, "dma map got 'no resources'");
                bioerror(bp, ENOMEM);
                break;

        case DDI_DMA_NOMAPPING:
                arcmsr_warn(acb, "dma map got 'no mapping'");
                bioerror(bp, EFAULT);
                break;

        case DDI_DMA_TOOBIG:
                arcmsr_warn(acb, "dma map got 'too big'");
                bioerror(bp, EINVAL);
                break;

        case DDI_DMA_INUSE:
                arcmsr_warn(acb, "dma map got 'in use' "
                    "(should not happen)");
                break;
        default:
                arcmsr_warn(acb, "dma map failed (0x%x)", i);
                break;
        }

        ddi_dma_free_handle(&ccb->pkt_dma_handle);
        ccb->pkt_dma_handle = NULL;
        ccb->ccb_flags &= ~CCB_FLAG_DMAVALID;
        return (DDI_FAILURE);
}


/*
 * Function name: arcmsr_dma_move
 * Return Values: 0 if successful, -1 if failure
 *   Description: move DMA resources to next DMA window
 *       Context: Can only be called from arcmsr_tran_init_pkt()
 */
static int
arcmsr_dma_move(struct ACB *acb, struct scsi_pkt *pkt, struct buf *bp)
{
        struct CCB *ccb = pkt->pkt_ha_private;
        uint8_t i = 0;
        int resid = 0;
        int total_ccb_xferlen = 0;

        if (ccb->resid_dmacookie.dmac_size != 0) {
                total_ccb_xferlen += ccb->resid_dmacookie.dmac_size;
                ccb->pkt_dmacookies[i].dmac_size =
                    ccb->resid_dmacookie.dmac_size;
                ccb->pkt_dmacookies[i].dmac_laddress =
                    ccb->resid_dmacookie.dmac_laddress;
                i++;
                ccb->resid_dmacookie.dmac_size = 0;
        }
        /*
         * If there are no more cookies remaining in this window,
         * move to the next window.
         */
        if (ccb->pkt_cookie == ccb->pkt_ncookies) {
                /*
                 * only dma map "partial" arrive here
                 */
                if ((ccb->pkt_curwin == ccb->pkt_nwin) &&
                    (ccb->pkt_nwin == 1)) {
                        return (DDI_SUCCESS);
                }

                /* At last window, cannot move */
                if (++ccb->pkt_curwin >= ccb->pkt_nwin) {
                        arcmsr_warn(acb, "dma partial set, numwin exceeded");
                        return (DDI_FAILURE);
                }
                if (ddi_dma_getwin(ccb->pkt_dma_handle, ccb->pkt_curwin,
                    &ccb->pkt_dma_offset, &ccb->pkt_dma_len,
                    &ccb->pkt_dmacookies[i], &ccb->pkt_ncookies) ==
                    DDI_FAILURE) {
                        arcmsr_warn(acb, "ddi_dma_getwin failed");
                        return (DDI_FAILURE);
                }
                /* reset cookie pointer */
                ccb->pkt_cookie = 0;
        } else {
                /*
                 * only dma map "all" arrive here
                 * We still have more cookies in this window,
                 * get the next one
                 * access the pkt_dma_handle remain cookie record at
                 * ccb->pkt_dmacookies array
                 */
                ddi_dma_nextcookie(ccb->pkt_dma_handle,
                    &ccb->pkt_dmacookies[i]);
        }

        /* Get remaining cookies in this window, up to our maximum */
        total_ccb_xferlen += ccb->pkt_dmacookies[i].dmac_size;

        /* retrieve and store cookies, start at ccb->pkt_dmacookies[0] */
        for (;;) {
                i++;
                /* handled cookies count level indicator */
                ccb->pkt_cookie++;
                if ((i == ARCMSR_MAX_SG_ENTRIES) ||
                    (ccb->pkt_cookie == ccb->pkt_ncookies) ||
                    (total_ccb_xferlen == ARCMSR_MAX_XFER_LEN)) {
                        break;
                }
                ddi_dma_nextcookie(ccb->pkt_dma_handle,
                    &ccb->pkt_dmacookies[i]);
                total_ccb_xferlen += ccb->pkt_dmacookies[i].dmac_size;
        }

        ccb->arcmsr_cdb.sgcount = i;
        if (total_ccb_xferlen > 512) {
                resid = total_ccb_xferlen % 512;
                if (resid != 0) {
                        i--;
                        total_ccb_xferlen -= resid;
                        /* modify last sg length */
                        ccb->pkt_dmacookies[i].dmac_size =
                            ccb->pkt_dmacookies[i].dmac_size - resid;
                        ccb->resid_dmacookie.dmac_size = resid;
                        ccb->resid_dmacookie.dmac_laddress =
                            ccb->pkt_dmacookies[i].dmac_laddress +
                            ccb->pkt_dmacookies[i].dmac_size;
                }
        }
        ccb->total_dmac_size += total_ccb_xferlen;
        pkt->pkt_resid = bp->b_bcount - ccb->total_dmac_size;

        return (DDI_SUCCESS);
}


/*ARGSUSED*/
static void
arcmsr_build_ccb(struct CCB *ccb)
{
        struct scsi_pkt *pkt = ccb->pkt;
        struct ARCMSR_CDB *arcmsr_cdb;
        char *psge;
        uint32_t address_lo, address_hi;
        int arccdbsize = 0x30;
        uint8_t sgcount;

        arcmsr_cdb = (struct ARCMSR_CDB *)&ccb->arcmsr_cdb;
        psge = (char *)&arcmsr_cdb->sgu;

        bcopy((caddr_t)pkt->pkt_cdbp, arcmsr_cdb->Cdb, arcmsr_cdb->CdbLength);
        sgcount = ccb->arcmsr_cdb.sgcount;

        if (sgcount != 0) {
                int length, i;
                int cdb_sgcount = 0;
                int total_xfer_length = 0;

                /* map stor port SG list to our iop SG List. */
                for (i = 0; i < sgcount; i++) {
                        /* Get physaddr of the current data pointer */
                        length = ccb->pkt_dmacookies[i].dmac_size;
                        total_xfer_length += length;
                        address_lo =
                            dma_addr_lo32(ccb->pkt_dmacookies[i].dmac_laddress);
                        address_hi =
                            dma_addr_hi32(ccb->pkt_dmacookies[i].dmac_laddress);

                        if (address_hi == 0) {
                                struct SG32ENTRY *dma_sg;

                                dma_sg = (struct SG32ENTRY *)(intptr_t)psge;
                                dma_sg->address = address_lo;
                                dma_sg->length = length;
                                psge += sizeof (struct SG32ENTRY);
                                arccdbsize += sizeof (struct SG32ENTRY);
                        } else {
                                struct SG64ENTRY *dma_sg;

                                dma_sg = (struct SG64ENTRY *)(intptr_t)psge;
                                dma_sg->addresshigh = address_hi;
                                dma_sg->address = address_lo;
                                dma_sg->length = length | IS_SG64_ADDR;
                                psge += sizeof (struct SG64ENTRY);
                                arccdbsize += sizeof (struct SG64ENTRY);
                        }
                        cdb_sgcount++;
                }
                arcmsr_cdb->sgcount = (uint8_t)cdb_sgcount;
                arcmsr_cdb->DataLength = total_xfer_length;
                if (arccdbsize > 256) {
                        arcmsr_cdb->Flags |= ARCMSR_CDB_FLAG_SGL_BSIZE;
                }
        } else {
                arcmsr_cdb->DataLength = 0;
        }

        if (ccb->ccb_flags & CCB_FLAG_DMAWRITE)
                arcmsr_cdb->Flags |= ARCMSR_CDB_FLAG_WRITE;
        ccb->arc_cdb_size = arccdbsize;
}

/*
 * arcmsr_post_ccb - Send a protocol specific ARC send postcard to a AIOC.
 *
 * handle:              Handle of registered ARC protocol driver
 * adapter_id:          AIOC unique identifier(integer)
 * pPOSTCARD_SEND:      Pointer to ARC send postcard
 *
 * This routine posts a ARC send postcard to the request post FIFO of a
 * specific ARC adapter.
 */
static int
arcmsr_post_ccb(struct ACB *acb, struct CCB *ccb)
{
        uint32_t cdb_phyaddr_pattern = ccb->cdb_phyaddr_pattern;
        struct scsi_pkt *pkt = ccb->pkt;
        struct ARCMSR_CDB *arcmsr_cdb;
        uint_t pkt_flags = pkt->pkt_flags;

        arcmsr_cdb = &ccb->arcmsr_cdb;

        /* TODO: Use correct offset and size for syncing? */
        if (ddi_dma_sync(acb->ccbs_pool_handle, 0, 0, DDI_DMA_SYNC_FORDEV) ==
            DDI_FAILURE)
                return (DDI_FAILURE);

        atomic_inc_32(&acb->ccboutstandingcount);
        ccb->ccb_time = (time_t)(ddi_get_time() + pkt->pkt_time);

        ccb->ccb_state = ARCMSR_CCB_START;
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                if (arcmsr_cdb->Flags & ARCMSR_CDB_FLAG_SGL_BSIZE) {
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbamu->inbound_queueport,
                            cdb_phyaddr_pattern |
                            ARCMSR_CCBPOST_FLAG_SGL_BSIZE);
                } else {
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbamu->inbound_queueport, cdb_phyaddr_pattern);
                }
                if (pkt_flags & FLAG_NOINTR)
                        arcmsr_polling_hba_ccbdone(acb, ccb);
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;
                int ending_index, index;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                index = phbbmu->postq_index;
                ending_index = ((index+1)%ARCMSR_MAX_HBB_POSTQUEUE);
                phbbmu->post_qbuffer[ending_index] = 0;
                if (arcmsr_cdb->Flags & ARCMSR_CDB_FLAG_SGL_BSIZE) {
                        phbbmu->post_qbuffer[index] =
                            (cdb_phyaddr_pattern|ARCMSR_CCBPOST_FLAG_SGL_BSIZE);
                } else {
                        phbbmu->post_qbuffer[index] = cdb_phyaddr_pattern;
                }
                index++;
                /* if last index number set it to 0 */
                index %= ARCMSR_MAX_HBB_POSTQUEUE;
                phbbmu->postq_index = index;
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                    ARCMSR_DRV2IOP_CDB_POSTED);

                if (pkt_flags & FLAG_NOINTR)
                        arcmsr_polling_hbb_ccbdone(acb, ccb);
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;
                uint32_t ccb_post_stamp, arc_cdb_size;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                arc_cdb_size = (ccb->arc_cdb_size > 0x300) ? 0x300 :
                    ccb->arc_cdb_size;
                ccb_post_stamp = (cdb_phyaddr_pattern |
                    ((arc_cdb_size-1) >> 6) |1);
                if (acb->cdb_phyaddr_hi32) {
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbcmu->inbound_queueport_high,
                            acb->cdb_phyaddr_hi32);
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbcmu->inbound_queueport_low, ccb_post_stamp);
                } else {
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbcmu->inbound_queueport_low, ccb_post_stamp);
                }
                if (pkt_flags & FLAG_NOINTR)
                        arcmsr_polling_hbc_ccbdone(acb, ccb);
                break;
        }

        }
        return (DDI_SUCCESS);
}


static void
arcmsr_ccb_complete(struct CCB *ccb, int flag)
{
        struct ACB *acb = ccb->acb;
        struct scsi_pkt *pkt = ccb->pkt;

        if (pkt == NULL) {
                return;
        }
        ccb->ccb_state |= ARCMSR_CCB_DONE;
        pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
            STATE_SENT_CMD | STATE_GOT_STATUS);

        if ((ccb->ccb_flags & CCB_FLAG_DMACONSISTENT) &&
            (pkt->pkt_state & STATE_XFERRED_DATA)) {
                (void) ddi_dma_sync(ccb->pkt_dma_handle, 0, 0,
                    DDI_DMA_SYNC_FORCPU);
        }
        /*
         * TODO: This represents a potential race condition, and is
         * ultimately a poor design decision.  Revisit this code
         * and solve the mutex ownership issue correctly.
         */
        if (mutex_owned(&acb->isr_mutex)) {
                mutex_exit(&acb->isr_mutex);
                scsi_hba_pkt_comp(pkt);
                mutex_enter(&acb->isr_mutex);
        } else {
                scsi_hba_pkt_comp(pkt);
        }
        if (flag == 1) {
                atomic_dec_32(&acb->ccboutstandingcount);
        }
}

static void
arcmsr_report_ccb_state(struct ACB *acb, struct CCB *ccb, boolean_t error)
{
        int id, lun;

        ccb->ccb_state |= ARCMSR_CCB_DONE;
        id = ccb->pkt->pkt_address.a_target;
        lun = ccb->pkt->pkt_address.a_lun;

        if (!error) {
                if (acb->devstate[id][lun] == ARECA_RAID_GONE) {
                        acb->devstate[id][lun] = ARECA_RAID_GOOD;
                }
                ccb->pkt->pkt_reason = CMD_CMPLT;
                ccb->pkt->pkt_state |= STATE_XFERRED_DATA;
                arcmsr_list_add_tail(&acb->ccb_complete_list_mutex,
                    &ccb->complete_queue_pointer, &acb->ccb_complete_list);

        } else {
                switch (ccb->arcmsr_cdb.DeviceStatus) {
                case ARCMSR_DEV_SELECT_TIMEOUT:
                        if (acb->devstate[id][lun] == ARECA_RAID_GOOD) {
                                arcmsr_warn(acb,
                                    "target %d lun %d selection "
                                    "timeout", id, lun);
                        }
                        acb->devstate[id][lun] = ARECA_RAID_GONE;
                        ccb->pkt->pkt_reason = CMD_TIMEOUT; /* CMD_DEV_GONE; */
                        ccb->pkt->pkt_statistics |= STAT_TIMEOUT;
                        arcmsr_list_add_tail(&acb->ccb_complete_list_mutex,
                            &ccb->complete_queue_pointer,
                            &acb->ccb_complete_list);
                        break;
                case ARCMSR_DEV_ABORTED:
                case ARCMSR_DEV_INIT_FAIL:
                        arcmsr_warn(acb, "isr got 'ARCMSR_DEV_ABORTED'"
                            " 'ARCMSR_DEV_INIT_FAIL'");
                        arcmsr_log(acb, CE_NOTE, "raid volume was kicked out");
                        acb->devstate[id][lun] = ARECA_RAID_GONE;
                        ccb->pkt->pkt_reason = CMD_DEV_GONE;
                        ccb->pkt->pkt_statistics |= STAT_TERMINATED;
                        arcmsr_list_add_tail(&acb->ccb_complete_list_mutex,
                            &ccb->complete_queue_pointer,
                            &acb->ccb_complete_list);
                        break;
                case SCSISTAT_CHECK_CONDITION:
                        acb->devstate[id][lun] = ARECA_RAID_GOOD;
                        arcmsr_report_sense_info(ccb);
                        arcmsr_list_add_tail(&acb->ccb_complete_list_mutex,
                            &ccb->complete_queue_pointer,
                            &acb->ccb_complete_list);
                        break;
                default:
                        arcmsr_warn(acb,
                            "target %d lun %d isr received CMD_DONE"
                            " with unknown DeviceStatus (0x%x)",
                            id, lun, ccb->arcmsr_cdb.DeviceStatus);
                        arcmsr_log(acb, CE_NOTE, "raid volume was kicked out");
                        acb->devstate[id][lun] = ARECA_RAID_GONE;
                        /* unknown error or crc error just for retry */
                        ccb->pkt->pkt_reason = CMD_TRAN_ERR;
                        ccb->pkt->pkt_statistics |= STAT_TERMINATED;
                        arcmsr_list_add_tail(&acb->ccb_complete_list_mutex,
                            &ccb->complete_queue_pointer,
                            &acb->ccb_complete_list);
                        break;
                }
        }
}


static void
arcmsr_drain_donequeue(struct ACB *acb, struct CCB *ccb, boolean_t error)
{
        uint16_t        ccb_state;

        if (ccb->acb != acb) {
                return;
        }
        if (ccb->ccb_state != ARCMSR_CCB_START) {
                switch (ccb->ccb_state & ARCMSR_ABNORMAL_MASK) {
                case ARCMSR_CCB_TIMEOUT:
                        ccb_state = ccb->ccb_state;
                        if (ccb_state & ARCMSR_CCB_WAIT4_FREE)
                                arcmsr_free_ccb(ccb);
                        else
                                ccb->ccb_state |= ARCMSR_CCB_BACK;
                        return;

                case ARCMSR_CCB_ABORTED:
                        ccb_state = ccb->ccb_state;
                        if (ccb_state & ARCMSR_CCB_WAIT4_FREE)
                                arcmsr_free_ccb(ccb);
                        else
                                ccb->ccb_state |= ARCMSR_CCB_BACK;
                        return;
                case ARCMSR_CCB_RESET:
                        ccb_state = ccb->ccb_state;
                        if (ccb_state & ARCMSR_CCB_WAIT4_FREE)
                                arcmsr_free_ccb(ccb);
                        else
                                ccb->ccb_state |= ARCMSR_CCB_BACK;
                        return;
                default:
                        return;
                }
        }
        arcmsr_report_ccb_state(acb, ccb, error);
}

static void
arcmsr_report_sense_info(struct CCB *ccb)
{
        struct SENSE_DATA *cdb_sensedata;
        struct scsi_pkt *pkt = ccb->pkt;
        struct scsi_arq_status *arq_status;
        union scsi_cdb *cdbp;
        uint64_t err_blkno;

        cdbp = (void *)pkt->pkt_cdbp;
        err_blkno = ARCMSR_GETGXADDR(ccb->arcmsr_cdb.CdbLength, cdbp);

        arq_status = (struct scsi_arq_status *)(intptr_t)(pkt->pkt_scbp);
        bzero((caddr_t)arq_status, sizeof (struct scsi_arq_status));
        *pkt->pkt_scbp = STATUS_CHECK; /* CHECK CONDITION */
        arq_status->sts_rqpkt_reason = CMD_CMPLT;
        arq_status->sts_rqpkt_state = (STATE_GOT_BUS | STATE_GOT_TARGET |
            STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS);
        arq_status->sts_rqpkt_statistics = 0;
        arq_status->sts_rqpkt_resid = 0;

        pkt->pkt_reason = CMD_CMPLT;
        /* auto rqsense took place */
        pkt->pkt_state |= STATE_ARQ_DONE;

        cdb_sensedata = (struct SENSE_DATA *)ccb->arcmsr_cdb.SenseData;
        if (err_blkno <= 0xfffffffful) {
                struct scsi_extended_sense *sts_sensedata;

                sts_sensedata = &arq_status->sts_sensedata;
                sts_sensedata->es_code = cdb_sensedata->ErrorCode;
                /* must eq CLASS_EXTENDED_SENSE (0x07) */
                sts_sensedata->es_class = cdb_sensedata->ErrorClass;
                sts_sensedata->es_valid = cdb_sensedata->Valid;
                sts_sensedata->es_segnum = cdb_sensedata->SegmentNumber;
                sts_sensedata->es_key = cdb_sensedata->SenseKey;
                sts_sensedata->es_ili = cdb_sensedata->IncorrectLength;
                sts_sensedata->es_eom = cdb_sensedata->EndOfMedia;
                sts_sensedata->es_filmk = cdb_sensedata->FileMark;
                sts_sensedata->es_info_1 = (err_blkno >> 24) & 0xFF;
                sts_sensedata->es_info_2 = (err_blkno >> 16) & 0xFF;
                sts_sensedata->es_info_3 = (err_blkno >>  8) & 0xFF;
                sts_sensedata->es_info_4 = err_blkno & 0xFF;
                sts_sensedata->es_add_len =
                    cdb_sensedata->AdditionalSenseLength;
                sts_sensedata->es_cmd_info[0] =
                    cdb_sensedata->CommandSpecificInformation[0];
                sts_sensedata->es_cmd_info[1] =
                    cdb_sensedata->CommandSpecificInformation[1];
                sts_sensedata->es_cmd_info[2] =
                    cdb_sensedata->CommandSpecificInformation[2];
                sts_sensedata->es_cmd_info[3] =
                    cdb_sensedata->CommandSpecificInformation[3];
                sts_sensedata->es_add_code =
                    cdb_sensedata->AdditionalSenseCode;
                sts_sensedata->es_qual_code =
                    cdb_sensedata->AdditionalSenseCodeQualifier;
                sts_sensedata->es_fru_code =
                    cdb_sensedata->FieldReplaceableUnitCode;
        } else { /* 64-bit LBA */
                struct scsi_descr_sense_hdr *dsp;
                struct scsi_information_sense_descr *isd;

                dsp = (struct scsi_descr_sense_hdr *)
                    &arq_status->sts_sensedata;
                dsp->ds_class = CLASS_EXTENDED_SENSE;
                dsp->ds_code = CODE_FMT_DESCR_CURRENT;
                dsp->ds_key = cdb_sensedata->SenseKey;
                dsp->ds_add_code = cdb_sensedata->AdditionalSenseCode;
                dsp->ds_qual_code =
                    cdb_sensedata->AdditionalSenseCodeQualifier;
                dsp->ds_addl_sense_length =
                    sizeof (struct scsi_information_sense_descr);

                isd = (struct scsi_information_sense_descr *)(dsp+1);
                isd->isd_descr_type = DESCR_INFORMATION;
                isd->isd_valid = 1;
                isd->isd_information[0] = (err_blkno >> 56) & 0xFF;
                isd->isd_information[1] = (err_blkno >> 48) & 0xFF;
                isd->isd_information[2] = (err_blkno >> 40) & 0xFF;
                isd->isd_information[3] = (err_blkno >> 32) & 0xFF;
                isd->isd_information[4] = (err_blkno >> 24) & 0xFF;
                isd->isd_information[5] = (err_blkno >> 16) & 0xFF;
                isd->isd_information[6] = (err_blkno >>  8) & 0xFF;
                isd->isd_information[7] = (err_blkno) & 0xFF;
        }
}


static int
arcmsr_seek_cmd2abort(struct ACB *acb, struct scsi_pkt *abortpkt)
{
        struct CCB *ccb;
        uint32_t intmask_org = 0;
        int i = 0;

        acb->num_aborts++;

        if (abortpkt != NULL) {
                /*
                 * We don't support abort of a single packet.  All
                 * callers in our kernel always do a global abort, so
                 * there is no point in having code to support it
                 * here.
                 */
                return (DDI_FAILURE);
        }

        /*
         * if abortpkt is NULL, the upper layer needs us
         * to abort all commands
         */
        if (acb->ccboutstandingcount != 0) {
                /* disable all outbound interrupt */
                intmask_org = arcmsr_disable_allintr(acb);
                /* clear and abort all outbound posted Q */
                arcmsr_done4abort_postqueue(acb);
                /* talk to iop 331 outstanding command aborted */
                (void) arcmsr_abort_host_command(acb);

                for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
                        ccb = acb->pccb_pool[i];
                        if (ccb->ccb_state == ARCMSR_CCB_START) {
                                /*
                                 * this ccb will complete at
                                 * hwinterrupt
                                 */
                                /* ccb->ccb_state = ARCMSR_CCB_ABORTED; */
                                ccb->pkt->pkt_reason = CMD_ABORTED;
                                ccb->pkt->pkt_statistics |= STAT_ABORTED;
                                arcmsr_ccb_complete(ccb, 1);
                        }
                }
                /*
                 * enable outbound Post Queue, outbound
                 * doorbell Interrupt
                 */
                arcmsr_enable_allintr(acb, intmask_org);
        }
        return (DDI_SUCCESS);
}


/*
 * Autoconfiguration support
 */
static int
arcmsr_parse_devname(char *devnm, int *tgt, int *lun)
{
        char devbuf[SCSI_MAXNAMELEN];
        char *addr;
        char *p,  *tp, *lp;
        long num;

        /* Parse dev name and address */
        (void) strlcpy(devbuf, devnm, sizeof (devbuf));
        addr = "";
        for (p = devbuf; *p != '\0'; p++) {
                if (*p == '@') {
                        addr = p + 1;
                        *p = '\0';
                } else if (*p == ':') {
                        *p = '\0';
                        break;
                }
        }

        /* Parse target and lun */
        for (p = tp = addr, lp = NULL; *p != '\0'; p++) {
                if (*p == ',') {
                        lp = p + 1;
                        *p = '\0';
                        break;
                }
        }
        if ((tgt != NULL) && (tp != NULL)) {
                if (ddi_strtol(tp, NULL, 0x10, &num) != 0)
                        return (-1);
                *tgt = (int)num;
        }
        if ((lun != NULL) && (lp != NULL)) {
                if (ddi_strtol(lp, NULL, 0x10, &num) != 0)
                        return (-1);
                *lun = (int)num;
        }
        return (0);
}

static int
arcmsr_name_node(dev_info_t *dip, char *name, int len)
{
        int tgt, lun;

        tgt = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "target",
            -1);
        if (tgt == -1)
                return (DDI_FAILURE);
        lun = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "lun",
            -1);
        if (lun == -1)
                return (DDI_FAILURE);
        (void) snprintf(name, len, "%x,%x", tgt, lun);
        return (DDI_SUCCESS);
}

static dev_info_t *
arcmsr_find_child(struct ACB *acb, uint16_t tgt, uint8_t lun)
{
        dev_info_t *child = NULL;
        char addr[SCSI_MAXNAMELEN];
        char tmp[SCSI_MAXNAMELEN];

        (void) sprintf(addr, "%x,%x", tgt, lun);

        for (child = ddi_get_child(acb->dev_info);
            child;
            child = ddi_get_next_sibling(child)) {
                /* We don't care about non-persistent node */
                if (ndi_dev_is_persistent_node(child) == 0)
                        continue;
                if (arcmsr_name_node(child, tmp, SCSI_MAXNAMELEN) !=
                    DDI_SUCCESS)
                        continue;
                if (strcmp(addr, tmp) == 0)
                        break;
        }
        return (child);
}

static int
arcmsr_config_child(struct ACB *acb, struct scsi_device *sd, dev_info_t **dipp)
{
        char *nodename = NULL;
        char **compatible = NULL;
        int ncompatible = 0;
        dev_info_t *ldip = NULL;
        int tgt = sd->sd_address.a_target;
        int lun = sd->sd_address.a_lun;
        int dtype = sd->sd_inq->inq_dtype & DTYPE_MASK;
        int rval;

        scsi_hba_nodename_compatible_get(sd->sd_inq, NULL, dtype,
            NULL, &nodename, &compatible, &ncompatible);
        if (nodename == NULL) {
                arcmsr_warn(acb, "found no comptible driver for T%dL%d",
                    tgt, lun);
                rval = NDI_FAILURE;
                goto finish;
        }
        /* Create dev node */
        rval = ndi_devi_alloc(acb->dev_info, nodename, DEVI_SID_NODEID, &ldip);
        if (rval == NDI_SUCCESS) {
                if (ndi_prop_update_int(DDI_DEV_T_NONE, ldip, "target", tgt) !=
                    DDI_PROP_SUCCESS) {
                        arcmsr_warn(acb,
                            "unable to create target property for T%dL%d",
                            tgt, lun);
                        rval = NDI_FAILURE;
                        goto finish;
                }
                if (ndi_prop_update_int(DDI_DEV_T_NONE, ldip, "lun", lun) !=
                    DDI_PROP_SUCCESS) {
                        arcmsr_warn(acb,
                            "unable to create lun property for T%dL%d",
                            tgt, lun);
                        rval = NDI_FAILURE;
                        goto finish;
                }
                if (ndi_prop_update_string_array(DDI_DEV_T_NONE, ldip,
                    "compatible", compatible, ncompatible) !=
                    DDI_PROP_SUCCESS) {
                        arcmsr_warn(acb,
                            "unable to create compatible property for T%dL%d",
                            tgt, lun);
                        rval = NDI_FAILURE;
                        goto finish;
                }
                rval = ndi_devi_online(ldip, NDI_ONLINE_ATTACH);
                if (rval != NDI_SUCCESS) {
                        arcmsr_warn(acb, "unable to online T%dL%d", tgt, lun);
                        ndi_prop_remove_all(ldip);
                        (void) ndi_devi_free(ldip);
                } else {
                        arcmsr_log(acb, CE_NOTE, "T%dL%d onlined", tgt, lun);
                }
        }
finish:
        if (dipp)
                *dipp = ldip;

        scsi_hba_nodename_compatible_free(nodename, compatible);
        return (rval);
}

static int
arcmsr_config_lun(struct ACB *acb, uint16_t tgt, uint8_t lun, dev_info_t **ldip)
{
        struct scsi_device sd;
        dev_info_t *child;
        int rval;

        if ((child = arcmsr_find_child(acb, tgt, lun)) != NULL) {
                if (ldip) {
                        *ldip = child;
                }
                return (NDI_SUCCESS);
        }
        bzero(&sd, sizeof (struct scsi_device));
        sd.sd_address.a_hba_tran = acb->scsi_hba_transport;
        sd.sd_address.a_target = tgt;
        sd.sd_address.a_lun = lun;

        rval = scsi_hba_probe(&sd, NULL);
        if (rval == SCSIPROBE_EXISTS)
                rval = arcmsr_config_child(acb, &sd, ldip);
        scsi_unprobe(&sd);
        return (rval);
}


static int
arcmsr_add_intr(struct ACB *acb, int intr_type)
{
        int     rc, count;
        dev_info_t *dev_info;
        const char *type_str;

        switch (intr_type) {
        case DDI_INTR_TYPE_MSI:
                type_str = "MSI";
                break;
        case DDI_INTR_TYPE_MSIX:
                type_str = "MSIX";
                break;
        case DDI_INTR_TYPE_FIXED:
                type_str = "FIXED";
                break;
        default:
                type_str = "unknown";
                break;
        }

        dev_info = acb->dev_info;
        /* Determine number of supported interrupts */
        rc = ddi_intr_get_nintrs(dev_info, intr_type, &count);
        if ((rc != DDI_SUCCESS) || (count == 0)) {
                arcmsr_warn(acb,
                    "no interrupts of type %s, rc=0x%x, count=%d",
                    type_str, rc, count);
                return (DDI_FAILURE);
        }
        acb->intr_size = sizeof (ddi_intr_handle_t) * count;
        acb->phandle = kmem_zalloc(acb->intr_size, KM_SLEEP);
        rc = ddi_intr_alloc(dev_info, acb->phandle, intr_type, 0,
            count, &acb->intr_count, DDI_INTR_ALLOC_NORMAL);
        if ((rc != DDI_SUCCESS) || (acb->intr_count == 0)) {
                arcmsr_warn(acb, "ddi_intr_alloc(%s) failed 0x%x",
                    type_str, rc);
                return (DDI_FAILURE);
        }
        if (acb->intr_count < count) {
                arcmsr_log(acb, CE_NOTE, "Got %d interrupts, but requested %d",
                    acb->intr_count, count);
        }
        /*
         * Get priority for first msi, assume remaining are all the same
         */
        if (ddi_intr_get_pri(acb->phandle[0], &acb->intr_pri) != DDI_SUCCESS) {
                arcmsr_warn(acb, "ddi_intr_get_pri failed");
                return (DDI_FAILURE);
        }
        if (acb->intr_pri >= ddi_intr_get_hilevel_pri()) {
                arcmsr_warn(acb,  "high level interrupt not supported");
                return (DDI_FAILURE);
        }

        for (int x = 0; x < acb->intr_count; x++) {
                if (ddi_intr_add_handler(acb->phandle[x], arcmsr_intr_handler,
                    (caddr_t)acb, NULL) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "ddi_intr_add_handler(%s) failed",
                            type_str);
                        return (DDI_FAILURE);
                }
        }
        (void) ddi_intr_get_cap(acb->phandle[0], &acb->intr_cap);
        if (acb->intr_cap & DDI_INTR_FLAG_BLOCK) {
                /* Call ddi_intr_block_enable() for MSI */
                (void) ddi_intr_block_enable(acb->phandle, acb->intr_count);
        } else {
                /* Call ddi_intr_enable() for MSI non block enable */
                for (int x = 0; x < acb->intr_count; x++) {
                        (void) ddi_intr_enable(acb->phandle[x]);
                }
        }
        return (DDI_SUCCESS);
}

static void
arcmsr_remove_intr(struct ACB *acb)
{
        int x;

        if (acb->phandle == NULL)
                return;

        /* Disable all interrupts */
        if (acb->intr_cap & DDI_INTR_FLAG_BLOCK) {
                /* Call ddi_intr_block_disable() */
                (void) ddi_intr_block_disable(acb->phandle, acb->intr_count);
        } else {
                for (x = 0; x < acb->intr_count; x++) {
                        (void) ddi_intr_disable(acb->phandle[x]);
                }
        }
        /* Call ddi_intr_remove_handler() */
        for (x = 0; x < acb->intr_count; x++) {
                (void) ddi_intr_remove_handler(acb->phandle[x]);
                (void) ddi_intr_free(acb->phandle[x]);
        }
        kmem_free(acb->phandle, acb->intr_size);
        acb->phandle = NULL;
}

static void
arcmsr_mutex_init(struct ACB *acb)
{
        mutex_init(&acb->isr_mutex, NULL, MUTEX_DRIVER, NULL);
        mutex_init(&acb->acb_mutex, NULL, MUTEX_DRIVER, NULL);
        mutex_init(&acb->postq_mutex, NULL, MUTEX_DRIVER, NULL);
        mutex_init(&acb->workingQ_mutex, NULL, MUTEX_DRIVER, NULL);
        mutex_init(&acb->ioctl_mutex, NULL, MUTEX_DRIVER, NULL);
}

static void
arcmsr_mutex_destroy(struct ACB *acb)
{
        mutex_destroy(&acb->isr_mutex);
        mutex_destroy(&acb->acb_mutex);
        mutex_destroy(&acb->postq_mutex);
        mutex_destroy(&acb->workingQ_mutex);
        mutex_destroy(&acb->ioctl_mutex);
}

static int
arcmsr_initialize(struct ACB *acb)
{
        struct CCB *pccb_tmp;
        size_t allocated_length;
        uint16_t wval;
        uint_t intmask_org, count;
        caddr_t arcmsr_ccbs_area;
        uint32_t wlval, cdb_phyaddr, offset, realccb_size;
        int32_t dma_sync_size;
        int i, id, lun, instance;

        instance = ddi_get_instance(acb->dev_info);
        wlval = pci_config_get32(acb->pci_acc_handle, 0);
        wval = (uint16_t)((wlval >> 16) & 0xffff);
        realccb_size = P2ROUNDUP(sizeof (struct CCB), 32);
        switch (wval) {
        case PCI_DEVICE_ID_ARECA_1880:
        case PCI_DEVICE_ID_ARECA_1882:
        {
                uint32_t *iop_mu_regs_map0;

                acb->adapter_type = ACB_ADAPTER_TYPE_C; /* lsi */
                dma_sync_size = ARCMSR_MAX_FREECCB_NUM * realccb_size + 0x20;
                if (ddi_regs_map_setup(acb->dev_info, 2,
                    (caddr_t *)&iop_mu_regs_map0, 0,
                    sizeof (struct HBC_msgUnit), &acb->dev_acc_attr,
                    &acb->reg_mu_acc_handle0) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "unable to map registers");
                        return (DDI_FAILURE);
                }

                if ((i = ddi_dma_alloc_handle(acb->dev_info, &arcmsr_ccb_attr,
                    DDI_DMA_SLEEP, NULL, &acb->ccbs_pool_handle)) !=
                    DDI_SUCCESS) {
                        ddi_regs_map_free(&acb->reg_mu_acc_handle0);
                        arcmsr_warn(acb, "ddi_dma_alloc_handle failed");
                        return (DDI_FAILURE);
                }

                if (ddi_dma_mem_alloc(acb->ccbs_pool_handle, dma_sync_size,
                    &acb->dev_acc_attr, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
                    DDI_DMA_SLEEP, NULL, (caddr_t *)&arcmsr_ccbs_area,
                    &allocated_length, &acb->ccbs_acc_handle) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "ddi_dma_mem_alloc failed");
                        ddi_dma_free_handle(&acb->ccbs_pool_handle);
                        ddi_regs_map_free(&acb->reg_mu_acc_handle0);
                        return (DDI_FAILURE);
                }

                if (ddi_dma_addr_bind_handle(acb->ccbs_pool_handle, NULL,
                    (caddr_t)arcmsr_ccbs_area, dma_sync_size, DDI_DMA_RDWR |
                    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &acb->ccb_cookie,
                    &count) != DDI_DMA_MAPPED) {
                        arcmsr_warn(acb, "ddi_dma_addr_bind_handle failed");
                        ddi_dma_mem_free(&acb->ccbs_acc_handle);
                        ddi_dma_free_handle(&acb->ccbs_pool_handle);
                        ddi_regs_map_free(&acb->reg_mu_acc_handle0);
                        return (DDI_FAILURE);
                }
                bzero(arcmsr_ccbs_area, dma_sync_size);
                offset = (uint32_t)(P2ROUNDUP(PtrToNum(arcmsr_ccbs_area), 32)
                    - PtrToNum(arcmsr_ccbs_area));
                arcmsr_ccbs_area = arcmsr_ccbs_area + offset;
                /* ioport base */
                acb->pmu = (struct msgUnit *)(intptr_t)iop_mu_regs_map0;
                break;
        }

        case PCI_DEVICE_ID_ARECA_1201:
        {
                uint32_t *iop_mu_regs_map0;
                uint32_t *iop_mu_regs_map1;
                struct HBB_msgUnit *phbbmu;

                acb->adapter_type = ACB_ADAPTER_TYPE_B; /* marvell */
                dma_sync_size =
                    (ARCMSR_MAX_FREECCB_NUM * realccb_size + 0x20) +
                    sizeof (struct HBB_msgUnit);
                /* Allocate memory for the ccb */
                if ((i = ddi_dma_alloc_handle(acb->dev_info, &arcmsr_ccb_attr,
                    DDI_DMA_SLEEP, NULL, &acb->ccbs_pool_handle)) !=
                    DDI_SUCCESS) {
                        arcmsr_warn(acb, "ddi_dma_alloc_handle failed");
                        return (DDI_FAILURE);
                }

                if (ddi_dma_mem_alloc(acb->ccbs_pool_handle, dma_sync_size,
                    &acb->dev_acc_attr, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
                    DDI_DMA_SLEEP, NULL, (caddr_t *)&arcmsr_ccbs_area,
                    &allocated_length, &acb->ccbs_acc_handle) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "ddi_dma_mem_alloc failed");
                        ddi_dma_free_handle(&acb->ccbs_pool_handle);
                        return (DDI_FAILURE);
                }

                if (ddi_dma_addr_bind_handle(acb->ccbs_pool_handle, NULL,
                    (caddr_t)arcmsr_ccbs_area, dma_sync_size,
                    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
                    NULL, &acb->ccb_cookie, &count) != DDI_DMA_MAPPED) {
                        arcmsr_warn(acb, "ddi_dma_addr_bind_handle failed");
                        ddi_dma_mem_free(&acb->ccbs_acc_handle);
                        ddi_dma_free_handle(&acb->ccbs_pool_handle);
                        return (DDI_FAILURE);
                }
                bzero(arcmsr_ccbs_area, dma_sync_size);
                offset = (uint32_t)(P2ROUNDUP(PtrToNum(arcmsr_ccbs_area), 32)
                    - PtrToNum(arcmsr_ccbs_area));
                arcmsr_ccbs_area = arcmsr_ccbs_area + offset;
                acb->pmu = (struct msgUnit *)
                    NumToPtr(PtrToNum(arcmsr_ccbs_area) +
                    (realccb_size*ARCMSR_MAX_FREECCB_NUM));
                phbbmu = (struct HBB_msgUnit *)acb->pmu;

                /* setup device register */
                if (ddi_regs_map_setup(acb->dev_info, 1,
                    (caddr_t *)&iop_mu_regs_map0, 0,
                    sizeof (struct HBB_DOORBELL), &acb->dev_acc_attr,
                    &acb->reg_mu_acc_handle0) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "unable to map base0 registers");
                        (void) ddi_dma_unbind_handle(acb->ccbs_pool_handle);
                        ddi_dma_mem_free(&acb->ccbs_acc_handle);
                        ddi_dma_free_handle(&acb->ccbs_pool_handle);
                        return (DDI_FAILURE);
                }

                /* ARCMSR_DRV2IOP_DOORBELL */
                phbbmu->hbb_doorbell = (struct HBB_DOORBELL *)iop_mu_regs_map0;
                if (ddi_regs_map_setup(acb->dev_info, 2,
                    (caddr_t *)&iop_mu_regs_map1, 0,
                    sizeof (struct HBB_RWBUFFER), &acb->dev_acc_attr,
                    &acb->reg_mu_acc_handle1) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "unable to map base1 registers");
                        ddi_regs_map_free(&acb->reg_mu_acc_handle0);
                        (void) ddi_dma_unbind_handle(acb->ccbs_pool_handle);
                        ddi_dma_mem_free(&acb->ccbs_acc_handle);
                        ddi_dma_free_handle(&acb->ccbs_pool_handle);
                        return (DDI_FAILURE);
                }

                /* ARCMSR_MSGCODE_RWBUFFER */
                phbbmu->hbb_rwbuffer = (struct HBB_RWBUFFER *)iop_mu_regs_map1;
                break;
        }

        case    PCI_DEVICE_ID_ARECA_1110:
        case    PCI_DEVICE_ID_ARECA_1120:
        case    PCI_DEVICE_ID_ARECA_1130:
        case    PCI_DEVICE_ID_ARECA_1160:
        case    PCI_DEVICE_ID_ARECA_1170:
        case    PCI_DEVICE_ID_ARECA_1210:
        case    PCI_DEVICE_ID_ARECA_1220:
        case    PCI_DEVICE_ID_ARECA_1230:
        case    PCI_DEVICE_ID_ARECA_1231:
        case    PCI_DEVICE_ID_ARECA_1260:
        case    PCI_DEVICE_ID_ARECA_1261:
        case    PCI_DEVICE_ID_ARECA_1270:
        case    PCI_DEVICE_ID_ARECA_1280:
        case    PCI_DEVICE_ID_ARECA_1212:
        case    PCI_DEVICE_ID_ARECA_1222:
        case    PCI_DEVICE_ID_ARECA_1380:
        case    PCI_DEVICE_ID_ARECA_1381:
        case    PCI_DEVICE_ID_ARECA_1680:
        case    PCI_DEVICE_ID_ARECA_1681:
        {
                uint32_t *iop_mu_regs_map0;

                acb->adapter_type = ACB_ADAPTER_TYPE_A; /* intel */
                dma_sync_size = ARCMSR_MAX_FREECCB_NUM * realccb_size + 0x20;
                if (ddi_regs_map_setup(acb->dev_info, 1,
                    (caddr_t *)&iop_mu_regs_map0, 0,
                    sizeof (struct HBA_msgUnit), &acb->dev_acc_attr,
                    &acb->reg_mu_acc_handle0) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "unable to map registers");
                        return (DDI_FAILURE);
                }

                if ((i = ddi_dma_alloc_handle(acb->dev_info, &arcmsr_ccb_attr,
                    DDI_DMA_SLEEP, NULL, &acb->ccbs_pool_handle)) !=
                    DDI_SUCCESS) {
                        arcmsr_warn(acb, "ddi_dma_alloc_handle failed");
                        ddi_regs_map_free(&acb->reg_mu_acc_handle0);
                        return (DDI_FAILURE);
                }

                if (ddi_dma_mem_alloc(acb->ccbs_pool_handle, dma_sync_size,
                    &acb->dev_acc_attr, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
                    DDI_DMA_SLEEP, NULL, (caddr_t *)&arcmsr_ccbs_area,
                    &allocated_length, &acb->ccbs_acc_handle) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "ddi_dma_mem_alloc failed", instance);
                        ddi_dma_free_handle(&acb->ccbs_pool_handle);
                        ddi_regs_map_free(&acb->reg_mu_acc_handle0);
                        return (DDI_FAILURE);
                }

                if (ddi_dma_addr_bind_handle(acb->ccbs_pool_handle, NULL,
                    (caddr_t)arcmsr_ccbs_area, dma_sync_size, DDI_DMA_RDWR |
                    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &acb->ccb_cookie,
                    &count) != DDI_DMA_MAPPED) {
                        arcmsr_warn(acb, "ddi_dma_addr_bind_handle failed");
                        ddi_dma_mem_free(&acb->ccbs_acc_handle);
                        ddi_dma_free_handle(&acb->ccbs_pool_handle);
                        ddi_regs_map_free(&acb->reg_mu_acc_handle0);
                        return (DDI_FAILURE);
                }
                bzero(arcmsr_ccbs_area, dma_sync_size);
                offset = (uint32_t)(P2ROUNDUP(PtrToNum(arcmsr_ccbs_area), 32)
                    - PtrToNum(arcmsr_ccbs_area));
                arcmsr_ccbs_area = arcmsr_ccbs_area + offset;
                /* ioport base */
                acb->pmu = (struct msgUnit *)(intptr_t)iop_mu_regs_map0;
                break;
        }

        default:
                arcmsr_warn(acb, "Unknown RAID adapter type!");
                return (DDI_FAILURE);
        }
        arcmsr_init_list_head(&acb->ccb_complete_list);
        /* here we can not access pci configuration again */
        acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
            ACB_F_MESSAGE_RQBUFFER_CLEARED | ACB_F_MESSAGE_WQBUFFER_READ);
        acb->acb_flags &= ~ACB_F_SCSISTOPADAPTER;
        /* physical address of acb->pccb_pool */
        cdb_phyaddr = acb->ccb_cookie.dmac_address + offset;

        pccb_tmp = (struct CCB *)(intptr_t)arcmsr_ccbs_area;

        for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
                pccb_tmp->cdb_phyaddr_pattern =
                    (acb->adapter_type == ACB_ADAPTER_TYPE_C) ?
                    cdb_phyaddr : (cdb_phyaddr >> 5);
                pccb_tmp->acb = acb;
                acb->ccbworkingQ[i] = acb->pccb_pool[i] = pccb_tmp;
                cdb_phyaddr = cdb_phyaddr + realccb_size;
                pccb_tmp = (struct CCB *)NumToPtr(PtrToNum(pccb_tmp) +
                    realccb_size);
        }
        acb->vir2phy_offset = PtrToNum(pccb_tmp) - cdb_phyaddr;

        /* disable all outbound interrupt */
        intmask_org = arcmsr_disable_allintr(acb);

        if (!arcmsr_iop_confirm(acb)) {
                arcmsr_warn(acb, "arcmsr_iop_confirm error", instance);
                ddi_dma_mem_free(&acb->ccbs_acc_handle);
                ddi_dma_free_handle(&acb->ccbs_pool_handle);
                return (DDI_FAILURE);
        }

        for (id = 0; id < ARCMSR_MAX_TARGETID; id++) {
                for (lun = 0; lun < ARCMSR_MAX_TARGETLUN; lun++) {
                        acb->devstate[id][lun] = ARECA_RAID_GONE;
                }
        }

        /* enable outbound Post Queue, outbound doorbell Interrupt */
        arcmsr_enable_allintr(acb, intmask_org);

        return (0);
}

static int
arcmsr_do_ddi_attach(dev_info_t *dev_info, int instance)
{
        scsi_hba_tran_t *hba_trans;
        ddi_device_acc_attr_t dev_acc_attr;
        struct ACB *acb;
        uint16_t wval;
        int raid6 = 1;
        char *type;
        int intr_types;


        /*
         * Soft State Structure
         * The driver should allocate the per-device-instance
         * soft state structure, being careful to clean up properly if
         * an error occurs. Allocate data structure.
         */
        if (ddi_soft_state_zalloc(arcmsr_soft_state, instance) != DDI_SUCCESS) {
                arcmsr_warn(NULL, "ddi_soft_state_zalloc failed");
                return (DDI_FAILURE);
        }

        acb = ddi_get_soft_state(arcmsr_soft_state, instance);
        ASSERT(acb);

        arcmsr_mutex_init(acb);

        /* acb is already zalloc()d so we don't need to bzero() it */
        dev_acc_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
        dev_acc_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
        dev_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;

        acb->dev_info = dev_info;
        acb->dev_acc_attr = dev_acc_attr;

        /*
         * The driver, if providing DMA, should also check that its hardware is
         * installed in a DMA-capable slot
         */
        if (ddi_slaveonly(dev_info) == DDI_SUCCESS) {
                arcmsr_warn(acb, "hardware is not installed in"
                    " a DMA-capable slot");
                goto error_level_0;
        }
        if (pci_config_setup(dev_info, &acb->pci_acc_handle) != DDI_SUCCESS) {
                arcmsr_warn(acb, "pci_config_setup() failed, attach failed");
                goto error_level_0;
        }

        wval = pci_config_get16(acb->pci_acc_handle, PCI_CONF_VENID);
        if (wval != PCI_VENDOR_ID_ARECA) {
                arcmsr_warn(acb,
                    "'vendorid (0x%04x) does not match 0x%04x "
                    "(PCI_VENDOR_ID_ARECA)",
                    wval, PCI_VENDOR_ID_ARECA);
                goto error_level_0;
        }

        wval = pci_config_get16(acb->pci_acc_handle, PCI_CONF_DEVID);
        switch (wval) {
        case PCI_DEVICE_ID_ARECA_1110:
        case PCI_DEVICE_ID_ARECA_1210:
        case PCI_DEVICE_ID_ARECA_1201:
                raid6 = 0;
                /*FALLTHRU*/
        case PCI_DEVICE_ID_ARECA_1120:
        case PCI_DEVICE_ID_ARECA_1130:
        case PCI_DEVICE_ID_ARECA_1160:
        case PCI_DEVICE_ID_ARECA_1170:
        case PCI_DEVICE_ID_ARECA_1220:
        case PCI_DEVICE_ID_ARECA_1230:
        case PCI_DEVICE_ID_ARECA_1260:
        case PCI_DEVICE_ID_ARECA_1270:
        case PCI_DEVICE_ID_ARECA_1280:
                type = "SATA 3G";
                break;
        case PCI_DEVICE_ID_ARECA_1380:
        case PCI_DEVICE_ID_ARECA_1381:
        case PCI_DEVICE_ID_ARECA_1680:
        case PCI_DEVICE_ID_ARECA_1681:
                type = "SAS 3G";
                break;
        case PCI_DEVICE_ID_ARECA_1880:
                type = "SAS 6G";
                break;
        default:
                type = "X-TYPE";
                arcmsr_warn(acb, "Unknown Host Adapter RAID Controller!");
                goto error_level_0;
        }

        arcmsr_log(acb, CE_CONT, "Areca %s Host Adapter RAID Controller%s\n",
            type, raid6 ? " (RAID6 capable)" : "");

        /* we disable iop interrupt here */
        if (arcmsr_initialize(acb) == DDI_FAILURE) {
                arcmsr_warn(acb, "arcmsr_initialize failed");
                goto error_level_1;
        }

        /* Allocate a transport structure */
        hba_trans = scsi_hba_tran_alloc(dev_info, SCSI_HBA_CANSLEEP);
        if (hba_trans == NULL) {
                arcmsr_warn(acb, "scsi_hba_tran_alloc failed");
                goto error_level_2;
        }
        acb->scsi_hba_transport = hba_trans;
        acb->dev_info = dev_info;
        /* init scsi host adapter transport entry */
        hba_trans->tran_hba_private  = acb;
        hba_trans->tran_tgt_private  = NULL;
        /*
         * If no per-target initialization is required, the HBA can leave
         * tran_tgt_init set to NULL.
         */
        hba_trans->tran_tgt_init = arcmsr_tran_tgt_init;
        hba_trans->tran_tgt_probe = scsi_hba_probe;
        hba_trans->tran_tgt_free = NULL;
        hba_trans->tran_start = arcmsr_tran_start;
        hba_trans->tran_abort = arcmsr_tran_abort;
        hba_trans->tran_reset = arcmsr_tran_reset;
        hba_trans->tran_getcap = arcmsr_tran_getcap;
        hba_trans->tran_setcap = arcmsr_tran_setcap;
        hba_trans->tran_init_pkt = arcmsr_tran_init_pkt;
        hba_trans->tran_destroy_pkt = arcmsr_tran_destroy_pkt;
        hba_trans->tran_dmafree = arcmsr_tran_dmafree;
        hba_trans->tran_sync_pkt = arcmsr_tran_sync_pkt;

        hba_trans->tran_reset_notify = NULL;
        hba_trans->tran_get_bus_addr = NULL;
        hba_trans->tran_get_name = NULL;
        hba_trans->tran_quiesce = NULL;
        hba_trans->tran_unquiesce = NULL;
        hba_trans->tran_bus_reset = NULL;
        hba_trans->tran_bus_config = arcmsr_tran_bus_config;
        hba_trans->tran_add_eventcall = NULL;
        hba_trans->tran_get_eventcookie = NULL;
        hba_trans->tran_post_event = NULL;
        hba_trans->tran_remove_eventcall = NULL;

        /* iop init and enable interrupt here */
        arcmsr_iop_init(acb);

        /* Get supported interrupt types */
        if (ddi_intr_get_supported_types(dev_info, &intr_types) !=
            DDI_SUCCESS) {
                arcmsr_warn(acb, "ddi_intr_get_supported_types failed");
                goto error_level_3;
        }
        if (intr_types & DDI_INTR_TYPE_FIXED) {
                if (arcmsr_add_intr(acb, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS)
                        goto error_level_5;
        } else if (intr_types & DDI_INTR_TYPE_MSI) {
                if (arcmsr_add_intr(acb, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS)
                        goto error_level_5;
        }

        /*
         * The driver should attach this instance of the device, and
         * perform error cleanup if necessary
         */
        if (scsi_hba_attach_setup(dev_info, &arcmsr_dma_attr,
            hba_trans, SCSI_HBA_TRAN_CLONE) != DDI_SUCCESS) {
                arcmsr_warn(acb, "scsi_hba_attach_setup failed");
                goto error_level_5;
        }

        /* Create a taskq for dealing with dr events */
        if ((acb->taskq = ddi_taskq_create(dev_info, "arcmsr_dr_taskq", 1,
            TASKQ_DEFAULTPRI, 0)) == NULL) {
                arcmsr_warn(acb, "ddi_taskq_create failed");
                goto error_level_8;
        }

        acb->timeout_count = 0;
        /* active ccbs "timeout" watchdog */
        acb->timeout_id = timeout(arcmsr_ccbs_timeout, (caddr_t)acb,
            (ARCMSR_TIMEOUT_WATCH * drv_usectohz(1000000)));
        acb->timeout_sc_id = timeout(arcmsr_devMap_monitor, (caddr_t)acb,
            (ARCMSR_DEV_MAP_WATCH * drv_usectohz(1000000)));

        /* report device info */
        ddi_report_dev(dev_info);

        return (DDI_SUCCESS);

error_level_8:
        (void) scsi_hba_detach(dev_info);

error_level_5:
        arcmsr_remove_intr(acb);

error_level_3:
        if (acb->scsi_hba_transport)
                scsi_hba_tran_free(acb->scsi_hba_transport);

error_level_2:
        if (acb->ccbs_acc_handle)
                ddi_dma_mem_free(&acb->ccbs_acc_handle);
        if (acb->ccbs_pool_handle)
                ddi_dma_free_handle(&acb->ccbs_pool_handle);

error_level_1:
        if (acb->pci_acc_handle)
                pci_config_teardown(&acb->pci_acc_handle);
        arcmsr_mutex_destroy(acb);
        ddi_soft_state_free(arcmsr_soft_state, instance);

error_level_0:
        return (DDI_FAILURE);
}


static void
arcmsr_vlog(struct ACB *acb, int level, char *fmt, va_list ap)
{
        char    buf[256];

        if (acb != NULL) {
                (void) snprintf(buf, sizeof (buf), "%s%d: %s",
                    ddi_driver_name(acb->dev_info),
                    ddi_get_instance(acb->dev_info), fmt);
                fmt = buf;
        }
        vcmn_err(level, fmt, ap);
}

static void
arcmsr_log(struct ACB *acb, int level, char *fmt, ...)
{
        va_list ap;

        va_start(ap, fmt);
        arcmsr_vlog(acb, level, fmt, ap);
        va_end(ap);
}

static void
arcmsr_warn(struct ACB *acb, char *fmt, ...)
{
        va_list ap;

        va_start(ap, fmt);
        arcmsr_vlog(acb, CE_WARN, fmt, ap);
        va_end(ap);
}

static void
arcmsr_init_list_head(struct list_head *list)
{
        list->next = list;
        list->prev = list;
}

static void
arcmsr_x_list_del(struct list_head *prev, struct list_head *next)
{
        next->prev = prev;
        prev->next = next;
}

static void
arcmsr_x_list_add(struct list_head *new_one,  struct list_head *prev,
    struct list_head *next)
{
        next->prev = new_one;
        new_one->next = next;
        new_one->prev = prev;
        prev->next = new_one;
}

static void
arcmsr_list_add_tail(kmutex_t *list_lock, struct list_head *new_one,
    struct list_head *head)
{
        mutex_enter(list_lock);
        arcmsr_x_list_add(new_one, head->prev, head);
        mutex_exit(list_lock);
}

static struct list_head *
arcmsr_list_get_first(kmutex_t *list_lock, struct list_head *head)
{
        struct list_head *one = NULL;

        mutex_enter(list_lock);
        if (head->next == head) {
                mutex_exit(list_lock);
                return (NULL);
        }
        one = head->next;
        arcmsr_x_list_del(one->prev, one->next);
        arcmsr_init_list_head(one);
        mutex_exit(list_lock);
        return (one);
}

static struct CCB *
arcmsr_get_complete_ccb_from_list(struct ACB *acb)
{
        struct list_head *first_complete_ccb_list = NULL;
        struct CCB *ccb;

        first_complete_ccb_list =
            arcmsr_list_get_first(&acb->ccb_complete_list_mutex,
            &acb->ccb_complete_list);
        if (first_complete_ccb_list == NULL) {
                return (NULL);
        }
        ccb = (void *)((caddr_t)(first_complete_ccb_list) -
            offsetof(struct CCB, complete_queue_pointer));
        return (ccb);
}

static struct CCB *
arcmsr_get_freeccb(struct ACB *acb)
{
        struct CCB *ccb;
        int ccb_get_index, ccb_put_index;

        mutex_enter(&acb->workingQ_mutex);
        ccb_put_index = acb->ccb_put_index;
        ccb_get_index = acb->ccb_get_index;
        ccb = acb->ccbworkingQ[ccb_get_index];
        ccb_get_index++;
        if (ccb_get_index >= ARCMSR_MAX_FREECCB_NUM)
                ccb_get_index = ccb_get_index - ARCMSR_MAX_FREECCB_NUM;
        if (ccb_put_index != ccb_get_index) {
                acb->ccb_get_index = ccb_get_index;
                arcmsr_init_list_head(&ccb->complete_queue_pointer);
                ccb->ccb_state = ARCMSR_CCB_UNBUILD;
        } else {
                ccb = NULL;
        }
        mutex_exit(&acb->workingQ_mutex);
        return (ccb);
}


static void
arcmsr_free_ccb(struct CCB *ccb)
{
        struct ACB *acb = ccb->acb;

        if (ccb->ccb_state == ARCMSR_CCB_FREE) {
                return;
        }
        mutex_enter(&acb->workingQ_mutex);
        ccb->ccb_state = ARCMSR_CCB_FREE;
        ccb->pkt = NULL;
        ccb->pkt_dma_handle = NULL;
        ccb->ccb_flags = 0;
        acb->ccbworkingQ[acb->ccb_put_index] = ccb;
        acb->ccb_put_index++;
        if (acb->ccb_put_index >= ARCMSR_MAX_FREECCB_NUM)
                acb->ccb_put_index =
                    acb->ccb_put_index - ARCMSR_MAX_FREECCB_NUM;
        mutex_exit(&acb->workingQ_mutex);
}


static void
arcmsr_ccbs_timeout(void* arg)
{
        struct ACB *acb = (struct ACB *)arg;
        struct CCB *ccb;
        int i, instance, timeout_count = 0;
        uint32_t intmask_org;
        time_t current_time = ddi_get_time();

        intmask_org = arcmsr_disable_allintr(acb);
        mutex_enter(&acb->isr_mutex);
        if (acb->ccboutstandingcount != 0) {
                /* check each ccb */
                i = ddi_dma_sync(acb->ccbs_pool_handle, 0, 0,
                    DDI_DMA_SYNC_FORKERNEL);
                if (i != DDI_SUCCESS) {
                        if ((acb->timeout_id != 0) &&
                            ((acb->acb_flags & ACB_F_SCSISTOPADAPTER) == 0)) {
                                /* do pkt timeout check each 60 secs */
                                acb->timeout_id = timeout(arcmsr_ccbs_timeout,
                                    (void*)acb, (ARCMSR_TIMEOUT_WATCH *
                                    drv_usectohz(1000000)));
                        }
                        mutex_exit(&acb->isr_mutex);
                        arcmsr_enable_allintr(acb, intmask_org);
                        return;
                }
                instance = ddi_get_instance(acb->dev_info);
                for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
                        ccb = acb->pccb_pool[i];
                        if (ccb->acb != acb) {
                                break;
                        }
                        if (ccb->ccb_state == ARCMSR_CCB_FREE) {
                                continue;
                        }
                        if (ccb->pkt == NULL) {
                                continue;
                        }
                        if (ccb->pkt->pkt_time == 0) {
                                continue;
                        }
                        if (ccb->ccb_time >= current_time) {
                                continue;
                        }
                        int id = ccb->pkt->pkt_address.a_target;
                        int lun = ccb->pkt->pkt_address.a_lun;
                        if (ccb->ccb_state == ARCMSR_CCB_START) {
                                uint8_t *cdb = (uint8_t *)&ccb->arcmsr_cdb.Cdb;

                                timeout_count++;
                                arcmsr_warn(acb,
                                    "scsi target %d lun %d cmd=0x%x "
                                    "command timeout, ccb=0x%p",
                                    instance, id, lun, *cdb, (void *)ccb);
                                ccb->ccb_state = ARCMSR_CCB_TIMEOUT;
                                ccb->pkt->pkt_reason = CMD_TIMEOUT;
                                ccb->pkt->pkt_statistics = STAT_TIMEOUT;
                                /* acb->devstate[id][lun] = ARECA_RAID_GONE; */
                                arcmsr_ccb_complete(ccb, 1);
                                continue;
                        } else if ((ccb->ccb_state & ARCMSR_CCB_CAN_BE_FREE) ==
                            ARCMSR_CCB_CAN_BE_FREE) {
                                arcmsr_free_ccb(ccb);
                        }
                }
        }
        if ((acb->timeout_id != 0) &&
            ((acb->acb_flags & ACB_F_SCSISTOPADAPTER) == 0)) {
                /* do pkt timeout check each 60 secs */
                acb->timeout_id = timeout(arcmsr_ccbs_timeout,
                    (void*)acb, (ARCMSR_TIMEOUT_WATCH * drv_usectohz(1000000)));
        }
        mutex_exit(&acb->isr_mutex);
        arcmsr_enable_allintr(acb, intmask_org);
}

static void
arcmsr_abort_dr_ccbs(struct ACB *acb, uint16_t target, uint8_t lun)
{
        struct CCB *ccb;
        uint32_t intmask_org;
        int i;

        /* disable all outbound interrupts */
        intmask_org = arcmsr_disable_allintr(acb);
        for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
                ccb = acb->pccb_pool[i];
                if (ccb->ccb_state == ARCMSR_CCB_START) {
                        if ((target == ccb->pkt->pkt_address.a_target) &&
                            (lun == ccb->pkt->pkt_address.a_lun)) {
                                ccb->ccb_state = ARCMSR_CCB_ABORTED;
                                ccb->pkt->pkt_reason = CMD_ABORTED;
                                ccb->pkt->pkt_statistics |= STAT_ABORTED;
                                arcmsr_ccb_complete(ccb, 1);
                                arcmsr_log(acb, CE_NOTE,
                                    "abort T%dL%d ccb", target, lun);
                        }
                }
        }
        /* enable outbound Post Queue, outbound doorbell Interrupt */
        arcmsr_enable_allintr(acb, intmask_org);
}

static int
arcmsr_scsi_device_probe(struct ACB *acb, uint16_t tgt, uint8_t lun)
{
        struct scsi_device sd;
        dev_info_t *child;
        int rval;

        bzero(&sd, sizeof (struct scsi_device));
        sd.sd_address.a_hba_tran = acb->scsi_hba_transport;
        sd.sd_address.a_target = (uint16_t)tgt;
        sd.sd_address.a_lun = (uint8_t)lun;
        if ((child = arcmsr_find_child(acb, tgt, lun)) != NULL) {
                rval = scsi_hba_probe(&sd, NULL);
                if (rval == SCSIPROBE_EXISTS) {
                        rval = ndi_devi_online(child, NDI_ONLINE_ATTACH);
                        if (rval != NDI_SUCCESS) {
                                arcmsr_warn(acb, "unable to online T%dL%d",
                                    tgt, lun);
                        } else {
                                arcmsr_log(acb, CE_NOTE, "T%dL%d onlined",
                                    tgt, lun);
                        }
                }
        } else {
                rval = scsi_hba_probe(&sd, NULL);
                if (rval == SCSIPROBE_EXISTS)
                        rval = arcmsr_config_child(acb, &sd, NULL);
        }
        scsi_unprobe(&sd);
        return (rval);
}

static void
arcmsr_dr_handle(struct ACB *acb)
{
        char *acb_dev_map = (char *)acb->device_map;
        char *devicemap;
        char temp;
        uint16_t target;
        uint8_t lun;
        char diff;
        dev_info_t *dip;
        ddi_acc_handle_t reg;

        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                devicemap = (char *)&phbamu->msgcode_rwbuffer[21];
                reg = acb->reg_mu_acc_handle0;
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                devicemap = (char *)
                    &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[21];
                reg = acb->reg_mu_acc_handle1;
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                devicemap = (char *)&phbcmu->msgcode_rwbuffer[21];
                reg = acb->reg_mu_acc_handle0;
                break;
        }

        }

        for (target = 0; target < ARCMSR_MAX_TARGETID - 1; target++) {
                temp = CHIP_REG_READ8(reg, devicemap);
                diff = (*acb_dev_map)^ temp;
                if (diff != 0) {
                        *acb_dev_map = temp;
                        for (lun = 0; lun < ARCMSR_MAX_TARGETLUN; lun++) {
                                if ((temp & 0x01) == 1 && (diff & 0x01) == 1) {
                                        ndi_devi_enter(acb->dev_info);
                                        acb->devstate[target][lun] =
                                            ARECA_RAID_GOOD;
                                        (void) arcmsr_scsi_device_probe(acb,
                                            target, lun);
                                        ndi_devi_exit(acb->dev_info);
                                        arcmsr_log(acb, CE_NOTE,
                                            "T%dL%d on-line", target, lun);
                                } else if ((temp & 0x01) == 0 &&
                                    (diff & 0x01) == 1) {
                                        dip = arcmsr_find_child(acb, target,
                                            lun);
                                        if (dip != NULL) {
                                                acb->devstate[target][lun] =
                                                    ARECA_RAID_GONE;
                                                if (mutex_owned(&acb->
                                                    isr_mutex)) {
                                                        arcmsr_abort_dr_ccbs(
                                                            acb, target, lun);
                                                        (void)
                                                            ndi_devi_offline(
                                                            dip,
                                                            NDI_DEVI_REMOVE |
                                                            NDI_DEVI_OFFLINE);
                                                } else {
                                                        mutex_enter(&acb->
                                                            isr_mutex);
                                                        arcmsr_abort_dr_ccbs(
                                                            acb, target, lun);
                                                        (void)
                                                            ndi_devi_offline(
                                                            dip,
                                                            NDI_DEVI_REMOVE |
                                                            NDI_DEVI_OFFLINE);
                                                        mutex_exit(&acb->
                                                            isr_mutex);
                                                }
                                        }
                                        arcmsr_log(acb, CE_NOTE,
                                            "T%dL%d off-line", target, lun);
                                }
                                temp >>= 1;
                                diff >>= 1;
                        }
                }
                devicemap++;
                acb_dev_map++;
        }
}


static void
arcmsr_devMap_monitor(void* arg)
{

        struct ACB *acb = (struct ACB *)arg;
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->inbound_msgaddr0,
                    ARCMSR_INBOUND_MESG0_GET_CONFIG);
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                    ARCMSR_MESSAGE_GET_CONFIG);
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbcmu->inbound_msgaddr0,
                    ARCMSR_INBOUND_MESG0_GET_CONFIG);
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbcmu->inbound_doorbell,
                    ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE);
                break;
        }

        }

        if ((acb->timeout_id != 0) &&
            ((acb->acb_flags & ACB_F_SCSISTOPADAPTER) == 0)) {
                /* do pkt timeout check each 5 secs */
                acb->timeout_id = timeout(arcmsr_devMap_monitor, (void*)acb,
                    (ARCMSR_DEV_MAP_WATCH * drv_usectohz(1000000)));
        }
}


static uint32_t
arcmsr_disable_allintr(struct ACB *acb)
{
        uint32_t intmask_org;

        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                /* disable all outbound interrupt */
                intmask_org = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_intmask);
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_intmask,
                    intmask_org|ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE);
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                /* disable all outbound interrupt */
                intmask_org = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->iop2drv_doorbell_mask);
                /* disable all interrupts */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->iop2drv_doorbell_mask, 0);
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                /* disable all outbound interrupt */
                intmask_org = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbcmu->host_int_mask); /* disable outbound message0 int */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbcmu->host_int_mask,
                    intmask_org|ARCMSR_HBCMU_ALL_INTMASKENABLE);
                break;
        }

        }
        return (intmask_org);
}


static void
arcmsr_enable_allintr(struct ACB *acb, uint32_t intmask_org)
{
        int mask;

        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                /*
                 * enable outbound Post Queue, outbound doorbell message0
                 * Interrupt
                 */
                mask = ~(ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE |
                    ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE |
                    ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE);
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_intmask, intmask_org & mask);
                acb->outbound_int_enable = ~(intmask_org & mask) & 0x000000ff;
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                mask = (ARCMSR_IOP2DRV_DATA_WRITE_OK |
                    ARCMSR_IOP2DRV_DATA_READ_OK | ARCMSR_IOP2DRV_CDB_DONE |
                    ARCMSR_IOP2DRV_MESSAGE_CMD_DONE);
                /* 1=interrupt enable, 0=interrupt disable */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->iop2drv_doorbell_mask,
                    intmask_org | mask);
                acb->outbound_int_enable = (intmask_org | mask) & 0x0000000f;
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                /* enable outbound Post Queue,outbound doorbell Interrupt */
                mask = ~(ARCMSR_HBCMU_UTILITY_A_ISR_MASK |
                    ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR_MASK |
                    ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR_MASK);
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbcmu->host_int_mask, intmask_org & mask);
                acb->outbound_int_enable = ~(intmask_org & mask) & 0x0000000f;
                break;
        }

        }
}


static void
arcmsr_iop_parking(struct ACB *acb)
{
        /* stop adapter background rebuild */
        if (acb->acb_flags & ACB_F_MSG_START_BGRB) {
                uint32_t intmask_org;

                acb->acb_flags &= ~ACB_F_MSG_START_BGRB;
                /* disable all outbound interrupt */
                intmask_org = arcmsr_disable_allintr(acb);
                switch (acb->adapter_type) {
                case ACB_ADAPTER_TYPE_A:
                        arcmsr_stop_hba_bgrb(acb);
                        arcmsr_flush_hba_cache(acb);
                        break;

                case ACB_ADAPTER_TYPE_B:
                        arcmsr_stop_hbb_bgrb(acb);
                        arcmsr_flush_hbb_cache(acb);
                        break;

                case ACB_ADAPTER_TYPE_C:
                        arcmsr_stop_hbc_bgrb(acb);
                        arcmsr_flush_hbc_cache(acb);
                        break;
                }
                /*
                 * enable outbound Post Queue
                 * enable outbound doorbell Interrupt
                 */
                arcmsr_enable_allintr(acb, intmask_org);
        }
}


static uint8_t
arcmsr_hba_wait_msgint_ready(struct ACB *acb)
{
        uint32_t i;
        uint8_t retries = 0x00;
        struct HBA_msgUnit *phbamu;


        phbamu = (struct HBA_msgUnit *)acb->pmu;

        do {
                for (i = 0; i < 100; i++) {
                        if (CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                            &phbamu->outbound_intstatus) &
                            ARCMSR_MU_OUTBOUND_MESSAGE0_INT) {
                                /* clear interrupt */
                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbamu->outbound_intstatus,
                                    ARCMSR_MU_OUTBOUND_MESSAGE0_INT);
                                return (TRUE);
                        }
                        drv_usecwait(10000);
                        if (ddi_in_panic()) {
                                /* clear interrupts */
                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbamu->outbound_intstatus,
                                    ARCMSR_MU_OUTBOUND_MESSAGE0_INT);
                                return (TRUE);
                        }
                } /* max 1 second */
        } while (retries++ < 20); /* max 20 seconds */
        return (FALSE);
}


static uint8_t
arcmsr_hbb_wait_msgint_ready(struct ACB *acb)
{
        struct HBB_msgUnit *phbbmu;
        uint32_t i;
        uint8_t retries = 0x00;

        phbbmu = (struct HBB_msgUnit *)acb->pmu;

        do {
                for (i = 0; i < 100; i++) {
                        if (CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                            &phbbmu->hbb_doorbell->iop2drv_doorbell) &
                            ARCMSR_IOP2DRV_MESSAGE_CMD_DONE) {
                                /* clear interrupt */
                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbbmu->hbb_doorbell->iop2drv_doorbell,
                                    ARCMSR_MESSAGE_INT_CLEAR_PATTERN);
                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                                    ARCMSR_DRV2IOP_END_OF_INTERRUPT);
                                return (TRUE);
                        }
                        drv_usecwait(10000);
                        if (ddi_in_panic()) {
                                /* clear interrupts */
                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbbmu->hbb_doorbell->iop2drv_doorbell,
                                    ARCMSR_MESSAGE_INT_CLEAR_PATTERN);
                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                                    ARCMSR_DRV2IOP_END_OF_INTERRUPT);
                                return (TRUE);
                        }
                } /* max 1 second */
        } while (retries++ < 20); /* max 20 seconds */

        return (FALSE);
}


static uint8_t
arcmsr_hbc_wait_msgint_ready(struct ACB *acb)
{
        uint32_t i;
        uint8_t retries = 0x00;
        struct HBC_msgUnit *phbcmu;
        uint32_t c = ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE_DOORBELL_CLEAR;


        phbcmu = (struct HBC_msgUnit *)acb->pmu;

        do {
                for (i = 0; i < 100; i++) {
                        if (CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                            &phbcmu->outbound_doorbell) &
                            ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE) {
                                /* clear interrupt */
                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbcmu->outbound_doorbell_clear, c);
                                return (TRUE);
                        }
                        drv_usecwait(10000);
                        if (ddi_in_panic()) {
                                /* clear interrupts */
                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbcmu->outbound_doorbell_clear, c);
                                return (TRUE);
                        }
                } /* max 1 second */
        } while (retries++ < 20); /* max 20 seconds */
        return (FALSE);
}


static void
arcmsr_flush_hba_cache(struct ACB *acb)
{
        struct HBA_msgUnit *phbamu;
        int retry_count = 30;

        /* enlarge wait flush adapter cache time: 10 minutes */

        phbamu = (struct HBA_msgUnit *)acb->pmu;

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0, &phbamu->inbound_msgaddr0,
            ARCMSR_INBOUND_MESG0_FLUSH_CACHE);
        do {
                if (arcmsr_hba_wait_msgint_ready(acb)) {
                        break;
                } else {
                        retry_count--;
                }
        } while (retry_count != 0);
}

static void
arcmsr_flush_hbb_cache(struct ACB *acb)
{
        struct HBB_msgUnit *phbbmu;
        int retry_count = 30;

        /* enlarge wait flush adapter cache time: 10 minutes */

        phbbmu = (struct HBB_msgUnit *)acb->pmu;
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->drv2iop_doorbell,
            ARCMSR_MESSAGE_FLUSH_CACHE);
        do {
                if (arcmsr_hbb_wait_msgint_ready(acb)) {
                        break;
                } else {
                        retry_count--;
                }
        } while (retry_count != 0);
}


static void
arcmsr_flush_hbc_cache(struct ACB *acb)
{
        struct HBC_msgUnit *phbcmu;
        int retry_count = 30;

        /* enlarge wait flush adapter cache time: 10 minutes */

        phbcmu = (struct HBC_msgUnit *)acb->pmu;

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0, &phbcmu->inbound_msgaddr0,
            ARCMSR_INBOUND_MESG0_FLUSH_CACHE);
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0, &phbcmu->inbound_doorbell,
            ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE);
        do {
                if (arcmsr_hbc_wait_msgint_ready(acb)) {
                        break;
                } else {
                        retry_count--;
                }
        } while (retry_count != 0);
}



static uint8_t
arcmsr_abort_hba_allcmd(struct ACB *acb)
{
        struct HBA_msgUnit *phbamu = (struct HBA_msgUnit *)acb->pmu;

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0, &phbamu->inbound_msgaddr0,
            ARCMSR_INBOUND_MESG0_ABORT_CMD);

        if (!arcmsr_hba_wait_msgint_ready(acb)) {
                arcmsr_warn(acb,
                    "timeout while waiting for 'abort all "
                    "outstanding commands'");
                return (0xff);
        }
        return (0x00);
}



static uint8_t
arcmsr_abort_hbb_allcmd(struct ACB *acb)
{
        struct HBB_msgUnit *phbbmu = (struct HBB_msgUnit *)acb->pmu;

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->drv2iop_doorbell, ARCMSR_MESSAGE_ABORT_CMD);

        if (!arcmsr_hbb_wait_msgint_ready(acb)) {
                arcmsr_warn(acb,
                    "timeout while waiting for 'abort all "
                    "outstanding commands'");
                return (0x00);
        }
        return (0x00);
}


static uint8_t
arcmsr_abort_hbc_allcmd(struct ACB *acb)
{
        struct HBC_msgUnit *phbcmu = (struct HBC_msgUnit *)acb->pmu;

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0, &phbcmu->inbound_msgaddr0,
            ARCMSR_INBOUND_MESG0_ABORT_CMD);
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0, &phbcmu->inbound_doorbell,
            ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE);

        if (!arcmsr_hbc_wait_msgint_ready(acb)) {
                arcmsr_warn(acb,
                    "timeout while waiting for 'abort all "
                    "outstanding commands'");
                return (0xff);
        }
        return (0x00);
}


static void
arcmsr_done4abort_postqueue(struct ACB *acb)
{

        struct CCB *ccb;
        uint32_t flag_ccb;
        int i = 0;
        boolean_t error;

        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;
                uint32_t outbound_intstatus;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                /* clear and abort all outbound posted Q */
                outbound_intstatus = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_intstatus) & acb->outbound_int_enable;
                /* clear interrupt */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_intstatus, outbound_intstatus);
                while (((flag_ccb = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_queueport)) != 0xFFFFFFFF) &&
                    (i++ < ARCMSR_MAX_OUTSTANDING_CMD)) {
                        /* frame must be 32 bytes aligned */
                        /* the CDB is the first field of the CCB */
                        ccb = NumToPtr((acb->vir2phy_offset + (flag_ccb << 5)));
                        /* check if command done with no error */
                        error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ?
                            B_TRUE : B_FALSE;
                        arcmsr_drain_donequeue(acb, ccb, error);
                }
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                /* clear all outbound posted Q */
                /* clear doorbell interrupt */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->iop2drv_doorbell,
                    ARCMSR_DOORBELL_INT_CLEAR_PATTERN);
                for (i = 0; i < ARCMSR_MAX_HBB_POSTQUEUE; i++) {
                        if ((flag_ccb = phbbmu->done_qbuffer[i]) != 0) {
                                phbbmu->done_qbuffer[i] = 0;
                                /* frame must be 32 bytes aligned */
                                ccb = NumToPtr((acb->vir2phy_offset +
                                    (flag_ccb << 5)));
                                /* check if command done with no error */
                                error =
                                    (flag_ccb &
                                    ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ?
                                    B_TRUE : B_FALSE;
                                arcmsr_drain_donequeue(acb, ccb, error);
                        }
                        phbbmu->post_qbuffer[i] = 0;
                }       /* drain reply FIFO */
                phbbmu->doneq_index = 0;
                phbbmu->postq_index = 0;
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;
                uint32_t ccb_cdb_phy;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                while ((CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbcmu->host_int_status) &
                    ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) &&
                    (i++ < ARCMSR_MAX_OUTSTANDING_CMD)) {
                        /* need to do */
                        flag_ccb = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                            &phbcmu->outbound_queueport_low);
                        /* frame must be 32 bytes aligned */
                        ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
                        ccb = NumToPtr((acb->vir2phy_offset + ccb_cdb_phy));
                        error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1)?
                            B_TRUE : B_FALSE;
                        arcmsr_drain_donequeue(acb, ccb, error);
                }
                break;
        }

        }
}
/*
 * Routine Description: try to get echo from iop.
 *           Arguments:
 *        Return Value: Nothing.
 */
static uint8_t
arcmsr_get_echo_from_iop(struct ACB *acb)
{
        uint32_t intmask_org;
        uint8_t rtnval = 0;

        if (acb->adapter_type == ACB_ADAPTER_TYPE_A) {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                intmask_org = arcmsr_disable_allintr(acb);
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->inbound_msgaddr0,
                    ARCMSR_INBOUND_MESG0_GET_CONFIG);
                if (!arcmsr_hba_wait_msgint_ready(acb)) {
                        arcmsr_warn(acb, "try to get echo from iop,"
                            "... timeout ...");
                        acb->acb_flags |= ACB_F_BUS_HANG_ON;
                        rtnval = 0xFF;
                }
                /* enable all outbound interrupt */
                arcmsr_enable_allintr(acb, intmask_org);
        }
        return (rtnval);
}

/*
 * Routine Description: Reset 80331 iop.
 *           Arguments:
 *        Return Value: Nothing.
 */
static uint8_t
arcmsr_iop_reset(struct ACB *acb)
{
        struct CCB *ccb;
        uint32_t intmask_org;
        uint8_t rtnval = 0;
        int i = 0;

        if (acb->ccboutstandingcount > 0) {
                /* disable all outbound interrupt */
                intmask_org = arcmsr_disable_allintr(acb);
                /* clear and abort all outbound posted Q */
                arcmsr_done4abort_postqueue(acb);
                /* talk to iop 331 outstanding command aborted */
                rtnval = (acb->acb_flags & ACB_F_BUS_HANG_ON) ?
                    0xFF : arcmsr_abort_host_command(acb);

                for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
                        ccb = acb->pccb_pool[i];
                        if (ccb->ccb_state == ARCMSR_CCB_START) {
                                /* ccb->ccb_state = ARCMSR_CCB_RESET; */
                                ccb->pkt->pkt_reason = CMD_RESET;
                                ccb->pkt->pkt_statistics |= STAT_BUS_RESET;
                                arcmsr_ccb_complete(ccb, 1);
                        }
                }
                atomic_and_32(&acb->ccboutstandingcount, 0);
                /* enable all outbound interrupt */
                arcmsr_enable_allintr(acb, intmask_org);
        } else {
                rtnval = arcmsr_get_echo_from_iop(acb);
        }
        return (rtnval);
}


static struct QBUFFER *
arcmsr_get_iop_rqbuffer(struct ACB *acb)
{
        struct QBUFFER *qb;

        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                qb = (struct QBUFFER *)&phbamu->message_rbuffer;
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                qb = (struct QBUFFER *)&phbbmu->hbb_rwbuffer->message_rbuffer;
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                qb = (struct QBUFFER *)&phbcmu->message_rbuffer;
                break;
        }

        }
        return (qb);
}


static struct QBUFFER *
arcmsr_get_iop_wqbuffer(struct ACB *acb)
{
        struct QBUFFER *qbuffer = NULL;

        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                qbuffer = (struct QBUFFER *)&phbamu->message_wbuffer;
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                qbuffer = (struct QBUFFER *)
                    &phbbmu->hbb_rwbuffer->message_wbuffer;
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                qbuffer = (struct QBUFFER *)&phbcmu->message_wbuffer;
                break;
        }

        }
        return (qbuffer);
}



static void
arcmsr_iop_message_read(struct ACB *acb)
{
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                /* let IOP know the data has been read */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->inbound_doorbell,
                    ARCMSR_INBOUND_DRIVER_DATA_READ_OK);
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                /* let IOP know the data has been read */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                    ARCMSR_DRV2IOP_DATA_READ_OK);
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                /* let IOP know data has been read */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbcmu->inbound_doorbell,
                    ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK);
                break;
        }

        }
}



static void
arcmsr_iop_message_wrote(struct ACB *acb)
{
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A: {
                struct HBA_msgUnit *phbamu;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                /*
                 * push inbound doorbell tell iop, driver data write ok
                 * and wait reply on next hwinterrupt for next Qbuffer post
                 */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->inbound_doorbell,
                    ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK);
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                /*
                 * push inbound doorbell tell iop, driver data was writen
                 * successfully, then await reply on next hwinterrupt for
                 * next Qbuffer post
                 */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                    ARCMSR_DRV2IOP_DATA_WRITE_OK);
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                /*
                 * push inbound doorbell tell iop, driver data write ok
                 * and wait reply on next hwinterrupt for next Qbuffer post
                 */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbcmu->inbound_doorbell,
                    ARCMSR_HBCMU_DRV2IOP_DATA_WRITE_OK);
                break;
        }

        }
}



static void
arcmsr_post_ioctldata2iop(struct ACB *acb)
{
        uint8_t *pQbuffer;
        struct QBUFFER *pwbuffer;
        uint8_t *iop_data;
        int32_t allxfer_len = 0;

        pwbuffer = arcmsr_get_iop_wqbuffer(acb);
        iop_data = (uint8_t *)pwbuffer->data;
        if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_READ) {
                acb->acb_flags &= (~ACB_F_MESSAGE_WQBUFFER_READ);
                while ((acb->wqbuf_firstidx != acb->wqbuf_lastidx) &&
                    (allxfer_len < 124)) {
                        pQbuffer = &acb->wqbuffer[acb->wqbuf_firstidx];
                        (void) memcpy(iop_data, pQbuffer, 1);
                        acb->wqbuf_firstidx++;
                        /* if last index number set it to 0 */
                        acb->wqbuf_firstidx %= ARCMSR_MAX_QBUFFER;
                        iop_data++;
                        allxfer_len++;
                }
                pwbuffer->data_len = allxfer_len;
                /*
                 * push inbound doorbell and wait reply at hwinterrupt
                 * routine for next Qbuffer post
                 */
                arcmsr_iop_message_wrote(acb);
        }
}



static void
arcmsr_stop_hba_bgrb(struct ACB *acb)
{
        struct HBA_msgUnit *phbamu;

        phbamu = (struct HBA_msgUnit *)acb->pmu;

        acb->acb_flags &= ~ACB_F_MSG_START_BGRB;
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbamu->inbound_msgaddr0, ARCMSR_INBOUND_MESG0_STOP_BGRB);
        if (!arcmsr_hba_wait_msgint_ready(acb))
                arcmsr_warn(acb,
                    "timeout while waiting for background rebuild completion");
}


static void
arcmsr_stop_hbb_bgrb(struct ACB *acb)
{
        struct HBB_msgUnit *phbbmu;

        phbbmu = (struct HBB_msgUnit *)acb->pmu;

        acb->acb_flags &= ~ACB_F_MSG_START_BGRB;
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->drv2iop_doorbell, ARCMSR_MESSAGE_STOP_BGRB);

        if (!arcmsr_hbb_wait_msgint_ready(acb))
                arcmsr_warn(acb,
                    "timeout while waiting for background rebuild completion");
}


static void
arcmsr_stop_hbc_bgrb(struct ACB *acb)
{
        struct HBC_msgUnit *phbcmu;

        phbcmu = (struct HBC_msgUnit *)acb->pmu;

        acb->acb_flags &= ~ACB_F_MSG_START_BGRB;
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbcmu->inbound_msgaddr0, ARCMSR_INBOUND_MESG0_STOP_BGRB);
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbcmu->inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE);
        if (!arcmsr_hbc_wait_msgint_ready(acb))
                arcmsr_warn(acb,
                    "timeout while waiting for background rebuild completion");
}


static int
arcmsr_iop_message_xfer(struct ACB *acb, struct scsi_pkt *pkt)
{
        struct CMD_MESSAGE_FIELD *pcmdmessagefld;
        struct CCB *ccb = pkt->pkt_ha_private;
        struct buf *bp = ccb->bp;
        uint8_t *pQbuffer;
        int retvalue = 0, transfer_len = 0;
        char *buffer;
        uint32_t controlcode;


        /* 4 bytes: Areca io control code */
        controlcode =
            (uint32_t)pkt->pkt_cdbp[5] << 24 |
            (uint32_t)pkt->pkt_cdbp[6] << 16 |
            (uint32_t)pkt->pkt_cdbp[7] << 8 |
            (uint32_t)pkt->pkt_cdbp[8];

        if (bp->b_flags & (B_PHYS | B_PAGEIO))
                bp_mapin(bp);

        buffer = bp->b_un.b_addr;
        transfer_len = bp->b_bcount;
        if (transfer_len > sizeof (struct CMD_MESSAGE_FIELD)) {
                retvalue = ARCMSR_MESSAGE_FAIL;
                goto message_out;
        }

        pcmdmessagefld = (struct CMD_MESSAGE_FIELD *)(intptr_t)buffer;
        switch (controlcode) {
        case ARCMSR_MESSAGE_READ_RQBUFFER:
        {
                unsigned long *ver_addr;
                uint8_t *ptmpQbuffer;
                int32_t allxfer_len = 0;

                ver_addr = kmem_zalloc(MSGDATABUFLEN, KM_SLEEP);

                ptmpQbuffer = (uint8_t *)ver_addr;
                while ((acb->rqbuf_firstidx != acb->rqbuf_lastidx) &&
                    (allxfer_len < (MSGDATABUFLEN - 1))) {
                        pQbuffer = &acb->rqbuffer[acb->rqbuf_firstidx];
                        (void) memcpy(ptmpQbuffer, pQbuffer, 1);
                        acb->rqbuf_firstidx++;
                        acb->rqbuf_firstidx %= ARCMSR_MAX_QBUFFER;
                        ptmpQbuffer++;
                        allxfer_len++;
                }

                if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
                        struct QBUFFER *prbuffer;
                        uint8_t  *iop_data;
                        int32_t iop_len;

                        acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
                        prbuffer = arcmsr_get_iop_rqbuffer(acb);
                        iop_data = (uint8_t *)prbuffer->data;
                        iop_len = (int32_t)prbuffer->data_len;

                        while (iop_len > 0) {
                                pQbuffer = &acb->rqbuffer[acb->rqbuf_lastidx];
                                (void) memcpy(pQbuffer, iop_data, 1);
                                acb->rqbuf_lastidx++;
                                acb->rqbuf_lastidx %= ARCMSR_MAX_QBUFFER;
                                iop_data++;
                                iop_len--;
                        }
                        arcmsr_iop_message_read(acb);
                }

                (void) memcpy(pcmdmessagefld->messagedatabuffer,
                    (uint8_t *)ver_addr, allxfer_len);
                pcmdmessagefld->cmdmessage.Length = allxfer_len;
                pcmdmessagefld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_OK;
                kmem_free(ver_addr, MSGDATABUFLEN);
                break;
        }

        case ARCMSR_MESSAGE_WRITE_WQBUFFER:
        {
                uint8_t *ver_addr;
                int32_t my_empty_len, user_len, wqbuf_firstidx,
                    wqbuf_lastidx;
                uint8_t *ptmpuserbuffer;

                ver_addr = kmem_zalloc(MSGDATABUFLEN, KM_SLEEP);

                ptmpuserbuffer = ver_addr;
                user_len = min(pcmdmessagefld->cmdmessage.Length,
                    MSGDATABUFLEN);
                (void) memcpy(ptmpuserbuffer,
                    pcmdmessagefld->messagedatabuffer, user_len);
                wqbuf_lastidx = acb->wqbuf_lastidx;
                wqbuf_firstidx = acb->wqbuf_firstidx;
                if (wqbuf_lastidx != wqbuf_firstidx) {
                        struct scsi_arq_status *arq_status;

                        arcmsr_post_ioctldata2iop(acb);
                        arq_status = (struct scsi_arq_status *)
                            (intptr_t)(pkt->pkt_scbp);
                        bzero((caddr_t)arq_status,
                            sizeof (struct scsi_arq_status));
                        arq_status->sts_rqpkt_reason = CMD_CMPLT;
                        arq_status->sts_rqpkt_state = (STATE_GOT_BUS |
                            STATE_GOT_TARGET | STATE_SENT_CMD |
                            STATE_XFERRED_DATA | STATE_GOT_STATUS);

                        arq_status->sts_rqpkt_statistics =
                            pkt->pkt_statistics;
                        arq_status->sts_rqpkt_resid = 0;

                        struct scsi_extended_sense *sts_sensedata;

                        sts_sensedata = &arq_status->sts_sensedata;

                        /* has error report sensedata */
                        sts_sensedata->es_code = 0x0;
                        sts_sensedata->es_valid = 0x01;
                        sts_sensedata->es_key = KEY_ILLEGAL_REQUEST;
                        /* AdditionalSenseLength */
                        sts_sensedata->es_add_len = 0x0A;
                        /* AdditionalSenseCode */
                        sts_sensedata->es_add_code = 0x20;
                        retvalue = ARCMSR_MESSAGE_FAIL;
                } else {
                        my_empty_len = (wqbuf_firstidx-wqbuf_lastidx - 1) &
                            (ARCMSR_MAX_QBUFFER - 1);
                        if (my_empty_len >= user_len) {
                                while (user_len > 0) {
                                        pQbuffer = &acb->wqbuffer[
                                            acb->wqbuf_lastidx];
                                        (void) memcpy(pQbuffer,
                                            ptmpuserbuffer, 1);
                                        acb->wqbuf_lastidx++;
                                        acb->wqbuf_lastidx %=
                                            ARCMSR_MAX_QBUFFER;
                                        ptmpuserbuffer++;
                                        user_len--;
                                }
                                if (acb->acb_flags &
                                    ACB_F_MESSAGE_WQBUFFER_CLEARED) {
                                        acb->acb_flags &=
                                            ~ACB_F_MESSAGE_WQBUFFER_CLEARED;
                                        arcmsr_post_ioctldata2iop(acb);
                                }
                        } else {
                                struct scsi_arq_status *arq_status;

                                /* has error report sensedata */
                                arq_status = (struct scsi_arq_status *)
                                    (intptr_t)(pkt->pkt_scbp);
                                bzero((caddr_t)arq_status,
                                    sizeof (struct scsi_arq_status));
                                arq_status->sts_rqpkt_reason = CMD_CMPLT;
                                arq_status->sts_rqpkt_state =
                                    (STATE_GOT_BUS |
                                    STATE_GOT_TARGET |STATE_SENT_CMD |
                                    STATE_XFERRED_DATA | STATE_GOT_STATUS);
                                arq_status->sts_rqpkt_statistics =
                                    pkt->pkt_statistics;
                                arq_status->sts_rqpkt_resid = 0;

                                struct scsi_extended_sense *sts_sensedata;
                                sts_sensedata = &arq_status->sts_sensedata;

                                /* has error report sensedata */
                                sts_sensedata->es_code  = 0x0;
                                sts_sensedata->es_valid = 0x01;
                                sts_sensedata->es_key = KEY_ILLEGAL_REQUEST;
                                /* AdditionalSenseLength */
                                sts_sensedata->es_add_len = 0x0A;
                                /* AdditionalSenseCode */
                                sts_sensedata->es_add_code = 0x20;
                                retvalue = ARCMSR_MESSAGE_FAIL;
                        }
                }
                kmem_free(ver_addr, MSGDATABUFLEN);
                break;
        }

        case ARCMSR_MESSAGE_CLEAR_RQBUFFER:
                pQbuffer = acb->rqbuffer;

                if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
                        acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
                        arcmsr_iop_message_read(acb);
                }
                acb->acb_flags |= ACB_F_MESSAGE_RQBUFFER_CLEARED;
                acb->rqbuf_firstidx = 0;
                acb->rqbuf_lastidx = 0;
                (void) memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER);
                pcmdmessagefld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_OK;
                break;
        case ARCMSR_MESSAGE_CLEAR_WQBUFFER:
                pQbuffer = acb->wqbuffer;

                if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
                        acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
                        arcmsr_iop_message_read(acb);
                }
                acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
                    ACB_F_MESSAGE_WQBUFFER_READ);
                acb->wqbuf_firstidx = 0;
                acb->wqbuf_lastidx = 0;
                (void) memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER);
                pcmdmessagefld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_OK;
                break;
        case ARCMSR_MESSAGE_CLEAR_ALLQBUFFER:

                if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
                        acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
                        arcmsr_iop_message_read(acb);
                }
                acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
                    ACB_F_MESSAGE_RQBUFFER_CLEARED |
                    ACB_F_MESSAGE_WQBUFFER_READ);
                acb->rqbuf_firstidx = 0;
                acb->rqbuf_lastidx = 0;
                acb->wqbuf_firstidx = 0;
                acb->wqbuf_lastidx = 0;
                pQbuffer = acb->rqbuffer;
                (void) memset(pQbuffer, 0, sizeof (struct QBUFFER));
                pQbuffer = acb->wqbuffer;
                (void) memset(pQbuffer, 0, sizeof (struct QBUFFER));
                pcmdmessagefld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_OK;
                break;

        case ARCMSR_MESSAGE_REQUEST_RETURN_CODE_3F:
                pcmdmessagefld->cmdmessage.ReturnCode =
                    ARCMSR_MESSAGE_RETURNCODE_3F;
                break;
        /*
         * Not supported - ARCMSR_MESSAGE_SAY_HELLO
         */
        case ARCMSR_MESSAGE_SAY_GOODBYE:
                arcmsr_iop_parking(acb);
                break;
        case ARCMSR_MESSAGE_FLUSH_ADAPTER_CACHE:
                switch (acb->adapter_type) {
                case ACB_ADAPTER_TYPE_A:
                        arcmsr_flush_hba_cache(acb);
                        break;
                case ACB_ADAPTER_TYPE_B:
                        arcmsr_flush_hbb_cache(acb);
                        break;
                case ACB_ADAPTER_TYPE_C:
                        arcmsr_flush_hbc_cache(acb);
                        break;
                }
                break;
        default:
                retvalue = ARCMSR_MESSAGE_FAIL;
        }

message_out:

        return (retvalue);
}




static void
arcmsr_pcidev_disattach(struct ACB *acb)
{
        struct CCB *ccb;
        int i = 0;

        /* disable all outbound interrupts */
        (void) arcmsr_disable_allintr(acb);
        /* stop adapter background rebuild */
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
                arcmsr_stop_hba_bgrb(acb);
                arcmsr_flush_hba_cache(acb);
                break;
        case ACB_ADAPTER_TYPE_B:
                arcmsr_stop_hbb_bgrb(acb);
                arcmsr_flush_hbb_cache(acb);
                break;
        case ACB_ADAPTER_TYPE_C:
                arcmsr_stop_hbc_bgrb(acb);
                arcmsr_flush_hbc_cache(acb);
                break;
        }
        /* abort all outstanding commands */
        acb->acb_flags |= ACB_F_SCSISTOPADAPTER;
        acb->acb_flags &= ~ACB_F_IOP_INITED;

        if (acb->ccboutstandingcount != 0) {
                /* clear and abort all outbound posted Q */
                arcmsr_done4abort_postqueue(acb);
                /* talk to iop outstanding command aborted */
                (void) arcmsr_abort_host_command(acb);

                for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
                        ccb = acb->pccb_pool[i];
                        if (ccb->ccb_state == ARCMSR_CCB_START) {
                                /* ccb->ccb_state = ARCMSR_CCB_ABORTED; */
                                ccb->pkt->pkt_reason = CMD_ABORTED;
                                ccb->pkt->pkt_statistics |= STAT_ABORTED;
                                arcmsr_ccb_complete(ccb, 1);
                        }
                }
        }
}

/* get firmware miscellaneous data */
static void
arcmsr_get_hba_config(struct ACB *acb)
{
        struct HBA_msgUnit *phbamu;

        char *acb_firm_model;
        char *acb_firm_version;
        char *acb_device_map;
        char *iop_firm_model;
        char *iop_firm_version;
        char *iop_device_map;
        int count;

        phbamu = (struct HBA_msgUnit *)acb->pmu;
        acb_firm_model = acb->firm_model;
        acb_firm_version = acb->firm_version;
        acb_device_map = acb->device_map;
        /* firm_model, 15 */
        iop_firm_model =
            (char *)(&phbamu->msgcode_rwbuffer[ARCMSR_FW_MODEL_OFFSET]);
        /* firm_version, 17 */
        iop_firm_version =
            (char *)(&phbamu->msgcode_rwbuffer[ARCMSR_FW_VERS_OFFSET]);

        /* device_map, 21 */
        iop_device_map =
            (char *)(&phbamu->msgcode_rwbuffer[ARCMSR_FW_MAP_OFFSET]);

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbamu->inbound_msgaddr0, ARCMSR_INBOUND_MESG0_GET_CONFIG);

        if (!arcmsr_hba_wait_msgint_ready(acb))
                arcmsr_warn(acb,
                    "timeout while waiting for adapter firmware "
                    "miscellaneous data");

        count = 8;
        while (count) {
                *acb_firm_model = CHIP_REG_READ8(acb->reg_mu_acc_handle0,
                    iop_firm_model);
                acb_firm_model++;
                iop_firm_model++;
                count--;
        }

        count = 16;
        while (count) {
                *acb_firm_version =
                    CHIP_REG_READ8(acb->reg_mu_acc_handle0, iop_firm_version);
                acb_firm_version++;
                iop_firm_version++;
                count--;
        }

        count = 16;
        while (count) {
                *acb_device_map =
                    CHIP_REG_READ8(acb->reg_mu_acc_handle0, iop_device_map);
                acb_device_map++;
                iop_device_map++;
                count--;
        }

        arcmsr_log(acb, CE_CONT, "ARECA RAID FIRMWARE VERSION %s\n",
            acb->firm_version);

        /* firm_request_len, 1 */
        acb->firm_request_len = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbamu->msgcode_rwbuffer[1]);
        /* firm_numbers_queue, 2 */
        acb->firm_numbers_queue = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbamu->msgcode_rwbuffer[2]);
        /* firm_sdram_size, 3 */
        acb->firm_sdram_size = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbamu->msgcode_rwbuffer[3]);
        /* firm_ide_channels, 4 */
        acb->firm_ide_channels = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbamu->msgcode_rwbuffer[4]);
}

/* get firmware miscellaneous data */
static void
arcmsr_get_hbb_config(struct ACB *acb)
{
        struct HBB_msgUnit *phbbmu;
        char *acb_firm_model;
        char *acb_firm_version;
        char *acb_device_map;
        char *iop_firm_model;
        char *iop_firm_version;
        char *iop_device_map;
        int count;

        phbbmu = (struct HBB_msgUnit *)acb->pmu;
        acb_firm_model = acb->firm_model;
        acb_firm_version = acb->firm_version;
        acb_device_map = acb->device_map;
        /* firm_model, 15 */
        iop_firm_model = (char *)
            (&phbbmu->hbb_rwbuffer->msgcode_rwbuffer[ARCMSR_FW_MODEL_OFFSET]);
        /* firm_version, 17 */
        iop_firm_version = (char *)
            (&phbbmu->hbb_rwbuffer->msgcode_rwbuffer[ARCMSR_FW_VERS_OFFSET]);
        /* device_map, 21 */
        iop_device_map = (char *)
            (&phbbmu->hbb_rwbuffer->msgcode_rwbuffer[ARCMSR_FW_MAP_OFFSET]);

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->drv2iop_doorbell, ARCMSR_MESSAGE_GET_CONFIG);

        if (!arcmsr_hbb_wait_msgint_ready(acb))
                arcmsr_warn(acb,
                    "timeout while waiting for adapter firmware "
                    "miscellaneous data");

        count = 8;
        while (count) {
                *acb_firm_model =
                    CHIP_REG_READ8(acb->reg_mu_acc_handle1, iop_firm_model);
                acb_firm_model++;
                iop_firm_model++;
                count--;
        }
        count = 16;
        while (count) {
                *acb_firm_version =
                    CHIP_REG_READ8(acb->reg_mu_acc_handle1, iop_firm_version);
                acb_firm_version++;
                iop_firm_version++;
                count--;
        }
        count = 16;
        while (count) {
                *acb_device_map =
                    CHIP_REG_READ8(acb->reg_mu_acc_handle1, iop_device_map);
                acb_device_map++;
                iop_device_map++;
                count--;
        }

        arcmsr_log(acb, CE_CONT, "ARECA RAID FIRMWARE VERSION %s\n",
            acb->firm_version);

        /* firm_request_len, 1 */
        acb->firm_request_len = CHIP_REG_READ32(acb->reg_mu_acc_handle1,
            &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[1]);
        /* firm_numbers_queue, 2 */
        acb->firm_numbers_queue = CHIP_REG_READ32(acb->reg_mu_acc_handle1,
            &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[2]);
        /* firm_sdram_size, 3 */
        acb->firm_sdram_size = CHIP_REG_READ32(acb->reg_mu_acc_handle1,
            &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[3]);
        /* firm_ide_channels, 4 */
        acb->firm_ide_channels = CHIP_REG_READ32(acb->reg_mu_acc_handle1,
            &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[4]);
}


/* get firmware miscellaneous data */
static void
arcmsr_get_hbc_config(struct ACB *acb)
{
        struct HBC_msgUnit *phbcmu;

        char *acb_firm_model;
        char *acb_firm_version;
        char *acb_device_map;
        char *iop_firm_model;
        char *iop_firm_version;
        char *iop_device_map;
        int count;

        phbcmu = (struct HBC_msgUnit *)acb->pmu;
        acb_firm_model = acb->firm_model;
        acb_firm_version = acb->firm_version;
        acb_device_map = acb->device_map;
        /* firm_model, 15 */
        iop_firm_model =
            (char *)(&phbcmu->msgcode_rwbuffer[ARCMSR_FW_MODEL_OFFSET]);
        /* firm_version, 17 */
        iop_firm_version =
            (char *)(&phbcmu->msgcode_rwbuffer[ARCMSR_FW_VERS_OFFSET]);
        /* device_map, 21 */
        iop_device_map =
            (char *)(&phbcmu->msgcode_rwbuffer[ARCMSR_FW_MAP_OFFSET]);
        /* post "get config" instruction */
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbcmu->inbound_msgaddr0, ARCMSR_INBOUND_MESG0_GET_CONFIG);
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbcmu->inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE);
        if (!arcmsr_hbc_wait_msgint_ready(acb))
                arcmsr_warn(acb,
                    "timeout while waiting for adapter firmware "
                    "miscellaneous data");
        count = 8;
        while (count) {
                *acb_firm_model =
                    CHIP_REG_READ8(acb->reg_mu_acc_handle0, iop_firm_model);
                acb_firm_model++;
                iop_firm_model++;
                count--;
        }

        count = 16;
        while (count) {
                *acb_firm_version =
                    CHIP_REG_READ8(acb->reg_mu_acc_handle0, iop_firm_version);
                acb_firm_version++;
                iop_firm_version++;
                count--;
        }

        count = 16;
        while (count) {
                *acb_device_map =
                    CHIP_REG_READ8(acb->reg_mu_acc_handle0, iop_device_map);
                acb_device_map++;
                iop_device_map++;
                count--;
        }

        arcmsr_log(acb, CE_CONT, "ARECA RAID FIRMWARE VERSION %s\n",
            acb->firm_version);

        /* firm_request_len, 1, 04-07 */
        acb->firm_request_len = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbcmu->msgcode_rwbuffer[1]);
        /* firm_numbers_queue, 2, 08-11 */
        acb->firm_numbers_queue = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbcmu->msgcode_rwbuffer[2]);
        /* firm_sdram_size, 3, 12-15 */
        acb->firm_sdram_size = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbcmu->msgcode_rwbuffer[3]);
        /* firm_ide_channels, 4, 16-19 */
        acb->firm_ide_channels = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbcmu->msgcode_rwbuffer[4]);
        /* firm_cfg_version, 25, 100-103 */
        acb->firm_cfg_version = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbcmu->msgcode_rwbuffer[25]);
}


/* start background rebuild */
static void
arcmsr_start_hba_bgrb(struct ACB *acb)
{
        struct HBA_msgUnit *phbamu;

        phbamu = (struct HBA_msgUnit *)acb->pmu;

        acb->acb_flags |= ACB_F_MSG_START_BGRB;
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbamu->inbound_msgaddr0, ARCMSR_INBOUND_MESG0_START_BGRB);

        if (!arcmsr_hba_wait_msgint_ready(acb))
                arcmsr_warn(acb,
                    "timeout while waiting for background rebuild to start");
}


static void
arcmsr_start_hbb_bgrb(struct ACB *acb)
{
        struct HBB_msgUnit *phbbmu;

        phbbmu = (struct HBB_msgUnit *)acb->pmu;

        acb->acb_flags |= ACB_F_MSG_START_BGRB;
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->drv2iop_doorbell,
            ARCMSR_MESSAGE_START_BGRB);

        if (!arcmsr_hbb_wait_msgint_ready(acb))
                arcmsr_warn(acb,
                    "timeout while waiting for background rebuild to start");
}


static void
arcmsr_start_hbc_bgrb(struct ACB *acb)
{
        struct HBC_msgUnit *phbcmu;

        phbcmu = (struct HBC_msgUnit *)acb->pmu;

        acb->acb_flags |= ACB_F_MSG_START_BGRB;
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbcmu->inbound_msgaddr0, ARCMSR_INBOUND_MESG0_START_BGRB);
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbcmu->inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE);
        if (!arcmsr_hbc_wait_msgint_ready(acb))
                arcmsr_warn(acb,
                    "timeout while waiting for background rebuild to start");
}

static void
arcmsr_polling_hba_ccbdone(struct ACB *acb, struct CCB *poll_ccb)
{
        struct HBA_msgUnit *phbamu;
        struct CCB *ccb;
        boolean_t error;
        uint32_t flag_ccb, outbound_intstatus, intmask_org;
        boolean_t poll_ccb_done = B_FALSE;
        uint32_t poll_count = 0;


        phbamu = (struct HBA_msgUnit *)acb->pmu;

        /* TODO: Use correct offset and size for syncing? */
        if (ddi_dma_sync(acb->ccbs_pool_handle, 0, 0,
            DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS)
                return;
        intmask_org = arcmsr_disable_allintr(acb);

        for (;;) {
                if ((flag_ccb = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_queueport)) == 0xFFFFFFFF) {
                        if (poll_ccb_done) {
                                /* chip FIFO no ccb for completion already */
                                break;
                        } else {
                                drv_usecwait(25000);
                                if ((poll_count > 100) && (poll_ccb != NULL)) {
                                        break;
                                }
                                if (acb->ccboutstandingcount == 0) {
                                        break;
                                }
                                poll_count++;
                                outbound_intstatus =
                                    CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                                    &phbamu->outbound_intstatus) &
                                    acb->outbound_int_enable;

                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbamu->outbound_intstatus,
                                    outbound_intstatus); /* clear interrupt */
                        }
                }

                /* frame must be 32 bytes aligned */
                ccb = NumToPtr((acb->vir2phy_offset + (flag_ccb << 5)));

                /* check if command done with no error */
                error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ?
                    B_TRUE : B_FALSE;
                if (poll_ccb != NULL)
                        poll_ccb_done = (ccb == poll_ccb) ? B_TRUE : B_FALSE;

                if (ccb->acb != acb) {
                        arcmsr_warn(acb, "ccb got a wrong acb!");
                        continue;
                }
                if (ccb->ccb_state != ARCMSR_CCB_START) {
                        if (ccb->ccb_state & ARCMSR_ABNORMAL_MASK) {
                                ccb->ccb_state |= ARCMSR_CCB_BACK;
                                ccb->pkt->pkt_reason = CMD_ABORTED;
                                ccb->pkt->pkt_statistics |= STAT_ABORTED;
                                arcmsr_ccb_complete(ccb, 1);
                                continue;
                        }
                        arcmsr_report_ccb_state(acb, ccb, error);
                        arcmsr_warn(acb,
                            "polling op got unexpected ccb command done");
                        continue;
                }
                arcmsr_report_ccb_state(acb, ccb, error);
        }       /* drain reply FIFO */
        arcmsr_enable_allintr(acb, intmask_org);
}


static void
arcmsr_polling_hbb_ccbdone(struct ACB *acb, struct CCB *poll_ccb)
{
        struct HBB_msgUnit *phbbmu;
        struct CCB *ccb;
        uint32_t flag_ccb, intmask_org;
        boolean_t error;
        uint32_t poll_count = 0;
        int index;
        boolean_t poll_ccb_done = B_FALSE;


        phbbmu = (struct HBB_msgUnit *)acb->pmu;

        /* Use correct offset and size for syncing */
        if (ddi_dma_sync(acb->ccbs_pool_handle, 0, 0,
            DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS)
                return;

        intmask_org = arcmsr_disable_allintr(acb);

        for (;;) {
                index = phbbmu->doneq_index;
                if ((flag_ccb = phbbmu->done_qbuffer[index]) == 0) {
                        if (poll_ccb_done) {
                                /* chip FIFO no ccb for completion already */
                                break;
                        } else {
                                drv_usecwait(25000);
                                if ((poll_count > 100) && (poll_ccb != NULL))
                                        break;
                                if (acb->ccboutstandingcount == 0)
                                        break;
                                poll_count++;
                                /* clear doorbell interrupt */
                                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                                    &phbbmu->hbb_doorbell->iop2drv_doorbell,
                                    ARCMSR_DOORBELL_INT_CLEAR_PATTERN);
                        }
                }

                phbbmu->done_qbuffer[index] = 0;
                index++;
                /* if last index number set it to 0 */
                index %= ARCMSR_MAX_HBB_POSTQUEUE;
                phbbmu->doneq_index = index;
                /* check if command done with no error */
                /* frame must be 32 bytes aligned */
                ccb = NumToPtr((acb->vir2phy_offset + (flag_ccb << 5)));

                /* check if command done with no error */
                error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ?
                    B_TRUE : B_FALSE;

                if (poll_ccb != NULL)
                        poll_ccb_done = (ccb == poll_ccb) ? B_TRUE : B_FALSE;
                if (ccb->acb != acb) {
                        arcmsr_warn(acb, "ccb got a wrong acb!");
                        continue;
                }
                if (ccb->ccb_state != ARCMSR_CCB_START) {
                        if (ccb->ccb_state & ARCMSR_ABNORMAL_MASK) {
                                ccb->ccb_state |= ARCMSR_CCB_BACK;
                                ccb->pkt->pkt_reason = CMD_ABORTED;
                                ccb->pkt->pkt_statistics |= STAT_ABORTED;
                                arcmsr_ccb_complete(ccb, 1);
                                continue;
                        }
                        arcmsr_report_ccb_state(acb, ccb, error);
                        arcmsr_warn(acb,
                            "polling op got unexpect ccb command done");
                        continue;
                }
                arcmsr_report_ccb_state(acb, ccb, error);
        }       /* drain reply FIFO */
        arcmsr_enable_allintr(acb, intmask_org);
}


static void
arcmsr_polling_hbc_ccbdone(struct ACB *acb, struct CCB *poll_ccb)
{

        struct HBC_msgUnit *phbcmu;
        struct CCB *ccb;
        boolean_t error;
        uint32_t ccb_cdb_phy;
        uint32_t flag_ccb, intmask_org;
        boolean_t poll_ccb_done = B_FALSE;
        uint32_t poll_count = 0;


        phbcmu = (struct HBC_msgUnit *)acb->pmu;

        /* Use correct offset and size for syncing */
        if (ddi_dma_sync(acb->ccbs_pool_handle, 0, 0,
            DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS)
                return;

        intmask_org = arcmsr_disable_allintr(acb);

        for (;;) {
                if (!(CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbcmu->host_int_status) &
                    ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR)) {

                        if (poll_ccb_done) {
                                /* chip FIFO no ccb for completion already */
                                break;
                        } else {
                                drv_usecwait(25000);
                                if ((poll_count > 100) && (poll_ccb != NULL)) {
                                        break;
                                }
                                if (acb->ccboutstandingcount == 0) {
                                        break;
                                }
                                poll_count++;
                        }
                }
                flag_ccb = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbcmu->outbound_queueport_low);
                /* frame must be 32 bytes aligned */
                ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
                /* the CDB is the first field of the CCB */
                ccb = NumToPtr((acb->vir2phy_offset + ccb_cdb_phy));

                /* check if command done with no error */
                error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ?
                    B_TRUE : B_FALSE;
                if (poll_ccb != NULL)
                        poll_ccb_done = (ccb == poll_ccb) ? B_TRUE : B_FALSE;

                if (ccb->acb != acb) {
                        arcmsr_warn(acb, "ccb got a wrong acb!");
                        continue;
                }
                if (ccb->ccb_state != ARCMSR_CCB_START) {
                        if (ccb->ccb_state & ARCMSR_ABNORMAL_MASK) {
                                ccb->ccb_state |= ARCMSR_CCB_BACK;
                                ccb->pkt->pkt_reason = CMD_ABORTED;
                                ccb->pkt->pkt_statistics |= STAT_ABORTED;
                                arcmsr_ccb_complete(ccb, 1);
                                continue;
                        }
                        arcmsr_report_ccb_state(acb, ccb, error);
                        arcmsr_warn(acb,
                            "polling op got unexpected ccb command done");
                        continue;
                }
                arcmsr_report_ccb_state(acb, ccb, error);
        }       /* drain reply FIFO */
        arcmsr_enable_allintr(acb, intmask_org);
}


/*
 * Function: arcmsr_hba_hardware_reset()
 *           Bug Fix for Intel IOP cause firmware hang on.
 *           and kernel panic
 */
static void
arcmsr_hba_hardware_reset(struct ACB *acb)
{
        struct HBA_msgUnit *phbamu;
        uint8_t value[64];
        int i;

        phbamu = (struct HBA_msgUnit *)acb->pmu;
        /* backup pci config data */
        for (i = 0; i < 64; i++) {
                value[i] = pci_config_get8(acb->pci_acc_handle, i);
        }
        /* hardware reset signal */
        if ((PCI_DEVICE_ID_ARECA_1680 ==
            pci_config_get16(acb->pci_acc_handle, PCI_CONF_DEVID))) {
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->reserved1[0], 0x00000003);
        } else {
                pci_config_put8(acb->pci_acc_handle, 0x84, 0x20);
        }
        drv_usecwait(1000000);
        /* write back pci config data */
        for (i = 0; i < 64; i++) {
                pci_config_put8(acb->pci_acc_handle, i, value[i]);
        }
        drv_usecwait(1000000);
}

/*
 * Function: arcmsr_abort_host_command
 */
static uint8_t
arcmsr_abort_host_command(struct ACB *acb)
{
        uint8_t rtnval = 0;

        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
                rtnval = arcmsr_abort_hba_allcmd(acb);
                break;
        case ACB_ADAPTER_TYPE_B:
                rtnval = arcmsr_abort_hbb_allcmd(acb);
                break;
        case ACB_ADAPTER_TYPE_C:
                rtnval = arcmsr_abort_hbc_allcmd(acb);
                break;
        }
        return (rtnval);
}

/*
 * Function: arcmsr_handle_iop_bus_hold
 */
static void
arcmsr_handle_iop_bus_hold(struct ACB *acb)
{

        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;
                int retry_count = 0;

                acb->timeout_count = 0;
                phbamu = (struct HBA_msgUnit *)acb->pmu;
                arcmsr_hba_hardware_reset(acb);
                acb->acb_flags &= ~ACB_F_IOP_INITED;
        sleep_again:
                drv_usecwait(1000000);
                if ((CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_msgaddr1) &
                    ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK) == 0) {
                        if (retry_count > 60) {
                                arcmsr_warn(acb,
                                    "waiting for hardware"
                                    "bus reset return, RETRY TERMINATED!!");
                                return;
                        }
                        retry_count++;
                        goto sleep_again;
                }
                arcmsr_iop_init(acb);
                break;
        }

        }
}

static void
arcmsr_iop2drv_data_wrote_handle(struct ACB *acb)
{
        struct QBUFFER *prbuffer;
        uint8_t *pQbuffer;
        uint8_t *iop_data;
        int my_empty_len, iop_len;
        int rqbuf_firstidx, rqbuf_lastidx;

        /* check this iop data if overflow my rqbuffer */
        rqbuf_lastidx = acb->rqbuf_lastidx;
        rqbuf_firstidx = acb->rqbuf_firstidx;
        prbuffer = arcmsr_get_iop_rqbuffer(acb);
        iop_data = (uint8_t *)prbuffer->data;
        iop_len = prbuffer->data_len;
        my_empty_len = (rqbuf_firstidx-rqbuf_lastidx - 1) &
            (ARCMSR_MAX_QBUFFER - 1);

        if (my_empty_len >= iop_len) {
                while (iop_len > 0) {
                        pQbuffer = &acb->rqbuffer[rqbuf_lastidx];
                        (void) memcpy(pQbuffer, iop_data, 1);
                        rqbuf_lastidx++;
                        /* if last index number set it to 0 */
                        rqbuf_lastidx %= ARCMSR_MAX_QBUFFER;
                        iop_data++;
                        iop_len--;
                }
                acb->rqbuf_lastidx = rqbuf_lastidx;
                arcmsr_iop_message_read(acb);
                /* signature, let IOP know data has been read */
        } else {
                acb->acb_flags |= ACB_F_IOPDATA_OVERFLOW;
        }
}



static void
arcmsr_iop2drv_data_read_handle(struct ACB *acb)
{
        acb->acb_flags |= ACB_F_MESSAGE_WQBUFFER_READ;
        /*
         * check if there are any mail packages from user space program
         * in my post bag, now is the time to send them into Areca's firmware
         */

        if (acb->wqbuf_firstidx != acb->wqbuf_lastidx) {

                uint8_t *pQbuffer;
                struct QBUFFER *pwbuffer;
                uint8_t *iop_data;
                int allxfer_len = 0;

                acb->acb_flags &= (~ACB_F_MESSAGE_WQBUFFER_READ);
                pwbuffer = arcmsr_get_iop_wqbuffer(acb);
                iop_data = (uint8_t *)pwbuffer->data;

                while ((acb->wqbuf_firstidx != acb->wqbuf_lastidx) &&
                    (allxfer_len < 124)) {
                        pQbuffer = &acb->wqbuffer[acb->wqbuf_firstidx];
                        (void) memcpy(iop_data, pQbuffer, 1);
                        acb->wqbuf_firstidx++;
                        /* if last index number set it to 0 */
                        acb->wqbuf_firstidx %= ARCMSR_MAX_QBUFFER;
                        iop_data++;
                        allxfer_len++;
                }
                pwbuffer->data_len = allxfer_len;
                /*
                 * push inbound doorbell, tell iop driver data write ok
                 * await reply on next hwinterrupt for next Qbuffer post
                 */
                arcmsr_iop_message_wrote(acb);
        }

        if (acb->wqbuf_firstidx == acb->wqbuf_lastidx)
                acb->acb_flags |= ACB_F_MESSAGE_WQBUFFER_CLEARED;
}


static void
arcmsr_hba_doorbell_isr(struct ACB *acb)
{
        uint32_t outbound_doorbell;
        struct HBA_msgUnit *phbamu;

        phbamu = (struct HBA_msgUnit *)acb->pmu;

        /*
         *  Maybe here we need to check wrqbuffer_lock is locked or not
         *  DOORBELL: ding! dong!
         *  check if there are any mail need to pack from firmware
         */

        outbound_doorbell = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbamu->outbound_doorbell);
        /* clear doorbell interrupt */
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbamu->outbound_doorbell, outbound_doorbell);

        if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK)
                arcmsr_iop2drv_data_wrote_handle(acb);


        if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_READ_OK)
                arcmsr_iop2drv_data_read_handle(acb);
}



static void
arcmsr_hbc_doorbell_isr(struct ACB *acb)
{
        uint32_t outbound_doorbell;
        struct HBC_msgUnit *phbcmu;

        phbcmu = (struct HBC_msgUnit *)acb->pmu;

        /*
         *  Maybe here we need to check wrqbuffer_lock is locked or not
         *  DOORBELL: ding! dong!
         *  check if there are any mail need to pick from firmware
         */

        outbound_doorbell = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbcmu->outbound_doorbell);
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbcmu->outbound_doorbell_clear,
            outbound_doorbell); /* clear interrupt */
        if (outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_DATA_WRITE_OK) {
                arcmsr_iop2drv_data_wrote_handle(acb);
        }
        if (outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_DATA_READ_OK) {
                arcmsr_iop2drv_data_read_handle(acb);
        }
        if (outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE) {
                /* messenger of "driver to iop commands" */
                arcmsr_hbc_message_isr(acb);
        }
}


static void
arcmsr_hba_message_isr(struct ACB *acb)
{
        struct HBA_msgUnit *phbamu = (struct HBA_msgUnit *)acb->pmu;
        uint32_t  *signature = (&phbamu->msgcode_rwbuffer[0]);
        uint32_t outbound_message;

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbamu->outbound_intstatus, ARCMSR_MU_OUTBOUND_MESSAGE0_INT);

        outbound_message = CHIP_REG_READ32(acb->reg_mu_acc_handle0, signature);
        if (outbound_message == ARCMSR_SIGNATURE_GET_CONFIG)
                if ((ddi_taskq_dispatch(acb->taskq,
                    (void (*)(void *))arcmsr_dr_handle,
                    acb, DDI_NOSLEEP)) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "DR task start failed");
                }
}

static void
arcmsr_hbb_message_isr(struct ACB *acb)
{
        struct HBB_msgUnit *phbbmu = (struct HBB_msgUnit *)acb->pmu;
        uint32_t  *signature = (&phbbmu->hbb_rwbuffer->msgcode_rwbuffer[0]);
        uint32_t outbound_message;

        /* clear interrupts */
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->iop2drv_doorbell,
            ARCMSR_MESSAGE_INT_CLEAR_PATTERN);
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->drv2iop_doorbell,
            ARCMSR_DRV2IOP_END_OF_INTERRUPT);

        outbound_message = CHIP_REG_READ32(acb->reg_mu_acc_handle0, signature);
        if (outbound_message == ARCMSR_SIGNATURE_GET_CONFIG)
                if ((ddi_taskq_dispatch(acb->taskq,
                    (void (*)(void *))arcmsr_dr_handle,
                    acb, DDI_NOSLEEP)) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "DR task start failed");
                }
}

static void
arcmsr_hbc_message_isr(struct ACB *acb)
{
        struct HBC_msgUnit *phbcmu = (struct HBC_msgUnit *)acb->pmu;
        uint32_t  *signature = (&phbcmu->msgcode_rwbuffer[0]);
        uint32_t outbound_message;

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbcmu->outbound_doorbell_clear,
            ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE_DOORBELL_CLEAR);

        outbound_message = CHIP_REG_READ32(acb->reg_mu_acc_handle0, signature);
        if (outbound_message == ARCMSR_SIGNATURE_GET_CONFIG)
                if ((ddi_taskq_dispatch(acb->taskq,
                    (void (*)(void *))arcmsr_dr_handle,
                    acb, DDI_NOSLEEP)) != DDI_SUCCESS) {
                        arcmsr_warn(acb, "DR task start failed");
                }
}


static void
arcmsr_hba_postqueue_isr(struct ACB *acb)
{

        struct HBA_msgUnit *phbamu;
        struct CCB *ccb;
        uint32_t flag_ccb;
        boolean_t error;

        phbamu = (struct HBA_msgUnit *)acb->pmu;

        /* areca cdb command done */
        /* Use correct offset and size for syncing */
        (void) ddi_dma_sync(acb->ccbs_pool_handle, 0, 0,
            DDI_DMA_SYNC_FORKERNEL);

        while ((flag_ccb = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbamu->outbound_queueport)) != 0xFFFFFFFF) {
                /* frame must be 32 bytes aligned */
                ccb = NumToPtr((acb->vir2phy_offset+(flag_ccb << 5)));
                /* check if command done with no error */
                error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ?
                    B_TRUE : B_FALSE;
                arcmsr_drain_donequeue(acb, ccb, error);
        }       /* drain reply FIFO */
}


static void
arcmsr_hbb_postqueue_isr(struct ACB *acb)
{
        struct HBB_msgUnit *phbbmu;
        struct CCB *ccb;
        uint32_t flag_ccb;
        boolean_t error;
        int index;

        phbbmu = (struct HBB_msgUnit *)acb->pmu;

        /* areca cdb command done */
        index = phbbmu->doneq_index;
        if (ddi_dma_sync(acb->ccbs_pool_handle, 0, 0,
            DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS)
                return;
        while ((flag_ccb = phbbmu->done_qbuffer[index]) != 0) {
                phbbmu->done_qbuffer[index] = 0;
                /* frame must be 32 bytes aligned */

                /* the CDB is the first field of the CCB */
                ccb = NumToPtr((acb->vir2phy_offset + (flag_ccb << 5)));

                /* check if command done with no error */
                error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ?
                    B_TRUE : B_FALSE;
                arcmsr_drain_donequeue(acb, ccb, error);
                index++;
                /* if last index number set it to 0 */
                index %= ARCMSR_MAX_HBB_POSTQUEUE;
                phbbmu->doneq_index = index;
        }       /* drain reply FIFO */
}


static void
arcmsr_hbc_postqueue_isr(struct ACB *acb)
{

        struct HBC_msgUnit *phbcmu;
        struct CCB *ccb;
        uint32_t flag_ccb, ccb_cdb_phy, throttling = 0;
        boolean_t error;

        phbcmu = (struct HBC_msgUnit *)acb->pmu;
        /* areca cdb command done */
        /* Use correct offset and size for syncing */
        (void) ddi_dma_sync(acb->ccbs_pool_handle, 0, 0,
            DDI_DMA_SYNC_FORKERNEL);

        while (CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbcmu->host_int_status) &
            ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) {
                /* check if command done with no error */
                flag_ccb = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbcmu->outbound_queueport_low);
                /* frame must be 32 bytes aligned */
                ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);

                /* the CDB is the first field of the CCB */
                ccb = NumToPtr((acb->vir2phy_offset + ccb_cdb_phy));

                error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ?
                    B_TRUE : B_FALSE;
                /* check if command done with no error */
                arcmsr_drain_donequeue(acb, ccb, error);
                if (throttling == ARCMSR_HBC_ISR_THROTTLING_LEVEL) {
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbcmu->inbound_doorbell,
                            ARCMSR_HBCMU_DRV2IOP_POSTQUEUE_THROTTLING);
                        break;
                }
                throttling++;
        }       /* drain reply FIFO */
}


static uint_t
arcmsr_handle_hba_isr(struct ACB *acb)
{
        uint32_t outbound_intstatus;
        struct HBA_msgUnit *phbamu;

        phbamu = (struct HBA_msgUnit *)acb->pmu;

        outbound_intstatus = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbamu->outbound_intstatus) & acb->outbound_int_enable;

        if (outbound_intstatus == 0)    /* it must be a shared irq */
                return (DDI_INTR_UNCLAIMED);

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0, &phbamu->outbound_intstatus,
            outbound_intstatus); /* clear interrupt */

        /* MU doorbell interrupts */

        if (outbound_intstatus & ARCMSR_MU_OUTBOUND_DOORBELL_INT)
                arcmsr_hba_doorbell_isr(acb);

        /* MU post queue interrupts */
        if (outbound_intstatus & ARCMSR_MU_OUTBOUND_POSTQUEUE_INT)
                arcmsr_hba_postqueue_isr(acb);

        if (outbound_intstatus & ARCMSR_MU_OUTBOUND_MESSAGE0_INT) {
                arcmsr_hba_message_isr(acb);
        }

        return (DDI_INTR_CLAIMED);
}


static uint_t
arcmsr_handle_hbb_isr(struct ACB *acb)
{
        uint32_t outbound_doorbell;
        struct HBB_msgUnit *phbbmu;


        phbbmu = (struct HBB_msgUnit *)acb->pmu;

        outbound_doorbell = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->iop2drv_doorbell) & acb->outbound_int_enable;

        if (outbound_doorbell == 0)             /* it must be a shared irq */
                return (DDI_INTR_UNCLAIMED);

        /* clear doorbell interrupt */
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->iop2drv_doorbell, ~outbound_doorbell);
        /* wait a cycle */
        (void) CHIP_REG_READ32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->iop2drv_doorbell);
        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->drv2iop_doorbell,
            ARCMSR_DRV2IOP_END_OF_INTERRUPT);

        /* MU ioctl transfer doorbell interrupts */
        if (outbound_doorbell & ARCMSR_IOP2DRV_DATA_WRITE_OK)
                arcmsr_iop2drv_data_wrote_handle(acb);

        if (outbound_doorbell & ARCMSR_IOP2DRV_DATA_READ_OK)
                arcmsr_iop2drv_data_read_handle(acb);

        /* MU post queue interrupts */
        if (outbound_doorbell & ARCMSR_IOP2DRV_CDB_DONE)
                arcmsr_hbb_postqueue_isr(acb);

        /* MU message interrupt */

        if (outbound_doorbell & ARCMSR_IOP2DRV_MESSAGE_CMD_DONE) {
                arcmsr_hbb_message_isr(acb);
        }

        return (DDI_INTR_CLAIMED);
}

static uint_t
arcmsr_handle_hbc_isr(struct ACB *acb)
{
        uint32_t host_interrupt_status;
        struct HBC_msgUnit *phbcmu;

        phbcmu = (struct HBC_msgUnit *)acb->pmu;
        /*  check outbound intstatus */
        host_interrupt_status=
            CHIP_REG_READ32(acb->reg_mu_acc_handle0, &phbcmu->host_int_status);
        if (host_interrupt_status == 0) /* it must be share irq */
                return (DDI_INTR_UNCLAIMED);
        /* MU ioctl transfer doorbell interrupts */
        if (host_interrupt_status & ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR) {
                /* messenger of "ioctl message read write" */
                arcmsr_hbc_doorbell_isr(acb);
        }
        /* MU post queue interrupts */
        if (host_interrupt_status & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) {
                /* messenger of "scsi commands" */
                arcmsr_hbc_postqueue_isr(acb);
        }
        return (DDI_INTR_CLAIMED);
}

static uint_t
arcmsr_intr_handler(caddr_t arg, caddr_t arg2)
{
        struct ACB *acb = (void *)arg;
        struct CCB *ccb;
        uint_t retrn = DDI_INTR_UNCLAIMED;
        _NOTE(ARGUNUSED(arg2))

        mutex_enter(&acb->isr_mutex);
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
                retrn = arcmsr_handle_hba_isr(acb);
                break;

        case ACB_ADAPTER_TYPE_B:
                retrn = arcmsr_handle_hbb_isr(acb);
                break;

        case ACB_ADAPTER_TYPE_C:
                retrn = arcmsr_handle_hbc_isr(acb);
                break;

        default:
                /* We should never be here */
                ASSERT(0);
                break;
        }
        mutex_exit(&acb->isr_mutex);
        while ((ccb = arcmsr_get_complete_ccb_from_list(acb)) != NULL) {
                arcmsr_ccb_complete(ccb, 1);
        }
        return (retrn);
}


static void
arcmsr_wait_firmware_ready(struct ACB *acb)
{
        uint32_t firmware_state;

        firmware_state = 0;

        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
        {
                struct HBA_msgUnit *phbamu;
                phbamu = (struct HBA_msgUnit *)acb->pmu;
                do {
                        firmware_state =
                            CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                            &phbamu->outbound_msgaddr1);
                } while ((firmware_state & ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK)
                    == 0);
                break;
        }

        case ACB_ADAPTER_TYPE_B:
        {
                struct HBB_msgUnit *phbbmu;
                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                do {
                        firmware_state =
                            CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                            &phbbmu->hbb_doorbell->iop2drv_doorbell);
                } while ((firmware_state & ARCMSR_MESSAGE_FIRMWARE_OK) == 0);
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                    ARCMSR_DRV2IOP_END_OF_INTERRUPT);
                break;
        }

        case ACB_ADAPTER_TYPE_C:
        {
                struct HBC_msgUnit *phbcmu;
                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                do {
                        firmware_state =
                            CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                            &phbcmu->outbound_msgaddr1);
                } while ((firmware_state & ARCMSR_HBCMU_MESSAGE_FIRMWARE_OK)
                    == 0);
                break;
        }

        }
}

static void
arcmsr_clear_doorbell_queue_buffer(struct ACB *acb)
{
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A: {
                struct HBA_msgUnit *phbamu;
                uint32_t outbound_doorbell;

                phbamu = (struct HBA_msgUnit *)acb->pmu;
                /* empty doorbell Qbuffer if door bell rung */
                outbound_doorbell = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_doorbell);
                /* clear doorbell interrupt */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->outbound_doorbell, outbound_doorbell);
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbamu->inbound_doorbell,
                    ARCMSR_INBOUND_DRIVER_DATA_READ_OK);
                break;
        }

        case ACB_ADAPTER_TYPE_B: {
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                /* clear interrupt and message state */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->iop2drv_doorbell,
                    ARCMSR_MESSAGE_INT_CLEAR_PATTERN);
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                    ARCMSR_DRV2IOP_DATA_READ_OK);
                /* let IOP know data has been read */
                break;
        }

        case ACB_ADAPTER_TYPE_C: {
                struct HBC_msgUnit *phbcmu;
                uint32_t outbound_doorbell;

                phbcmu = (struct HBC_msgUnit *)acb->pmu;
                /* empty doorbell Qbuffer if door bell ringed */
                outbound_doorbell = CHIP_REG_READ32(acb->reg_mu_acc_handle0,
                    &phbcmu->outbound_doorbell);
                /* clear outbound doobell isr */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbcmu->outbound_doorbell_clear, outbound_doorbell);
                /* let IOP know data has been read */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbcmu->inbound_doorbell,
                    ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK);
                break;
        }

        }
}


static uint32_t
arcmsr_iop_confirm(struct ACB *acb)
{
        uint64_t cdb_phyaddr;
        uint32_t cdb_phyaddr_hi32;

        /*
         * here we need to tell iop 331 about our freeccb.HighPart
         * if freeccb.HighPart is non-zero
         */
        cdb_phyaddr = acb->ccb_cookie.dmac_laddress;
        cdb_phyaddr_hi32 = (uint32_t)((cdb_phyaddr >> 16) >> 16);
        acb->cdb_phyaddr_hi32 = cdb_phyaddr_hi32;
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
                if (cdb_phyaddr_hi32 != 0) {
                        struct HBA_msgUnit *phbamu;

                        phbamu = (struct HBA_msgUnit *)acb->pmu;
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbamu->msgcode_rwbuffer[0],
                            ARCMSR_SIGNATURE_SET_CONFIG);
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbamu->msgcode_rwbuffer[1], cdb_phyaddr_hi32);
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbamu->inbound_msgaddr0,
                            ARCMSR_INBOUND_MESG0_SET_CONFIG);
                        if (!arcmsr_hba_wait_msgint_ready(acb)) {
                                arcmsr_warn(acb,
                                    "timeout setting ccb "
                                    "high physical address");
                                return (FALSE);
                        }
                }
                break;

        /* if adapter is type B, set window of "post command queue" */
        case ACB_ADAPTER_TYPE_B: {
                uint32_t post_queue_phyaddr;
                struct HBB_msgUnit *phbbmu;

                phbbmu = (struct HBB_msgUnit *)acb->pmu;
                phbbmu->postq_index = 0;
                phbbmu->doneq_index = 0;
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                    ARCMSR_MESSAGE_SET_POST_WINDOW);

                if (!arcmsr_hbb_wait_msgint_ready(acb)) {
                        arcmsr_warn(acb, "timeout setting post command "
                            "queue window");
                        return (FALSE);
                }

                post_queue_phyaddr = (uint32_t)cdb_phyaddr +
                    ARCMSR_MAX_FREECCB_NUM * P2ROUNDUP(sizeof (struct CCB), 32)
                    + offsetof(struct HBB_msgUnit, post_qbuffer);
                /* driver "set config" signature */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle1,
                    &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[0],
                    ARCMSR_SIGNATURE_SET_CONFIG);
                /* normal should be zero */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle1,
                    &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[1],
                    cdb_phyaddr_hi32);
                /* postQ size (256+8)*4 */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle1,
                    &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[2],
                    post_queue_phyaddr);
                /* doneQ size (256+8)*4 */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle1,
                    &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[3],
                    post_queue_phyaddr+1056);
                /* ccb maxQ size must be --> [(256+8)*4] */
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle1,
                    &phbbmu->hbb_rwbuffer->msgcode_rwbuffer[4], 1056);
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                    ARCMSR_MESSAGE_SET_CONFIG);

                if (!arcmsr_hbb_wait_msgint_ready(acb)) {
                        arcmsr_warn(acb,
                            "timeout setting command queue window");
                        return (FALSE);
                }
                CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                    &phbbmu->hbb_doorbell->drv2iop_doorbell,
                    ARCMSR_MESSAGE_START_DRIVER_MODE);

                if (!arcmsr_hbb_wait_msgint_ready(acb)) {
                        arcmsr_warn(acb, "timeout in 'start driver mode'");
                        return (FALSE);
                }
                break;
        }

        case ACB_ADAPTER_TYPE_C:
                if (cdb_phyaddr_hi32 != 0) {
                        struct HBC_msgUnit *phbcmu;

                        phbcmu = (struct HBC_msgUnit *)acb->pmu;
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbcmu->msgcode_rwbuffer[0],
                            ARCMSR_SIGNATURE_SET_CONFIG);
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbcmu->msgcode_rwbuffer[1], cdb_phyaddr_hi32);
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbcmu->inbound_msgaddr0,
                            ARCMSR_INBOUND_MESG0_SET_CONFIG);
                        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
                            &phbcmu->inbound_doorbell,
                            ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE);
                        if (!arcmsr_hbc_wait_msgint_ready(acb)) {
                                arcmsr_warn(acb, "'set ccb "
                                    "high part physical address' timeout");
                                return (FALSE);
                        }
                }
                break;
        }
        return (TRUE);
}


/*
 * ONLY used for Adapter type B
 */
static void
arcmsr_enable_eoi_mode(struct ACB *acb)
{
        struct HBB_msgUnit *phbbmu;

        phbbmu = (struct HBB_msgUnit *)acb->pmu;

        CHIP_REG_WRITE32(acb->reg_mu_acc_handle0,
            &phbbmu->hbb_doorbell->drv2iop_doorbell,
            ARCMSR_MESSAGE_ACTIVE_EOI_MODE);

        if (!arcmsr_hbb_wait_msgint_ready(acb))
                arcmsr_warn(acb, "'iop enable eoi mode' timeout");
}

/* start background rebuild */
static void
arcmsr_iop_init(struct ACB *acb)
{
        uint32_t intmask_org;

        /* disable all outbound interrupt */
        intmask_org = arcmsr_disable_allintr(acb);
        arcmsr_wait_firmware_ready(acb);
        (void) arcmsr_iop_confirm(acb);

        /* start background rebuild */
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_A:
                arcmsr_get_hba_config(acb);
                arcmsr_start_hba_bgrb(acb);
                break;
        case ACB_ADAPTER_TYPE_B:
                arcmsr_get_hbb_config(acb);
                arcmsr_start_hbb_bgrb(acb);
                break;
        case ACB_ADAPTER_TYPE_C:
                arcmsr_get_hbc_config(acb);
                arcmsr_start_hbc_bgrb(acb);
                break;
        }
        /* empty doorbell Qbuffer if door bell rang */
        arcmsr_clear_doorbell_queue_buffer(acb);

        if (acb->adapter_type == ACB_ADAPTER_TYPE_B)
                arcmsr_enable_eoi_mode(acb);

        /* enable outbound Post Queue, outbound doorbell Interrupt */
        arcmsr_enable_allintr(acb, intmask_org);
        acb->acb_flags |= ACB_F_IOP_INITED;
}