arcmsr: add support new adapter ARC12x4 series
authorChing Huang <ching2048@areca.com.tw>
Tue, 19 Aug 2014 07:25:22 +0000 (15:25 +0800)
committerChristoph Hellwig <hch@lst.de>
Tue, 16 Sep 2014 16:40:08 +0000 (09:40 -0700)
Add code to support the new Areca Raid ARC12x4 series adapters.

Signed-off-by: Ching Huang <ching2048@areca.com.tw>
Reviewed-by: Tomas Henzl <thenzl@redhat.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/scsi/arcmsr/arcmsr.h
drivers/scsi/arcmsr/arcmsr_hba.c

index 799393ede1ccf6c8fd8dcdb0033290ddbe5cef4a..d1c78efc202e323bc273e0cb5db2d4360456b80b 100644 (file)
@@ -63,12 +63,17 @@ struct device_attribute;
 #define ARCMSR_MAX_QBUFFER                                                     4096
 #define ARCMSR_DEFAULT_SG_ENTRIES                                              38
 #define ARCMSR_MAX_HBB_POSTQUEUE                                               264
+#define ARCMSR_MAX_ARC1214_POSTQUEUE   256
+#define ARCMSR_MAX_ARC1214_DONEQUEUE   257
 #define ARCMSR_MAX_XFER_LEN                                                    0x26000 /* 152K */
 #define ARCMSR_CDB_SG_PAGE_LENGTH                                              256 
 #define ARCMST_NUM_MSIX_VECTORS                4
 #ifndef PCI_DEVICE_ID_ARECA_1880
 #define PCI_DEVICE_ID_ARECA_1880 0x1880
  #endif
+#ifndef PCI_DEVICE_ID_ARECA_1214
+       #define PCI_DEVICE_ID_ARECA_1214        0x1214
+#endif
 /*
 **********************************************************************************
 **
@@ -339,6 +344,56 @@ struct FIRMWARE_INFO
 #define ARCMSR_HBCMU_MESSAGE_FIRMWARE_OK                       0x80000000
 /*
 *******************************************************************************
+**                SPEC. for Areca Type D adapter
+*******************************************************************************
+*/
+#define ARCMSR_ARC1214_CHIP_ID                         0x00004
+#define ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION                0x00008
+#define ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK          0x00034
+#define ARCMSR_ARC1214_SAMPLE_RESET                    0x00100
+#define ARCMSR_ARC1214_RESET_REQUEST                   0x00108
+#define ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS           0x00200
+#define ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE                0x0020C
+#define ARCMSR_ARC1214_INBOUND_MESSAGE0                        0x00400
+#define ARCMSR_ARC1214_INBOUND_MESSAGE1                        0x00404
+#define ARCMSR_ARC1214_OUTBOUND_MESSAGE0               0x00420
+#define ARCMSR_ARC1214_OUTBOUND_MESSAGE1               0x00424
+#define ARCMSR_ARC1214_INBOUND_DOORBELL                        0x00460
+#define ARCMSR_ARC1214_OUTBOUND_DOORBELL               0x00480
+#define ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE                0x00484
+#define ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW           0x01000
+#define ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH          0x01004
+#define ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER      0x01018
+#define ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW          0x01060
+#define ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH         0x01064
+#define ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER      0x0106C
+#define ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER      0x01070
+#define ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE                0x01088
+#define ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE       0x0108C
+#define ARCMSR_ARC1214_MESSAGE_WBUFFER                 0x02000
+#define ARCMSR_ARC1214_MESSAGE_RBUFFER                 0x02100
+#define ARCMSR_ARC1214_MESSAGE_RWBUFFER                        0x02200
+/* Host Interrupt Mask */
+#define ARCMSR_ARC1214_ALL_INT_ENABLE                  0x00001010
+#define ARCMSR_ARC1214_ALL_INT_DISABLE                 0x00000000
+/* Host Interrupt Status */
+#define ARCMSR_ARC1214_OUTBOUND_DOORBELL_ISR           0x00001000
+#define ARCMSR_ARC1214_OUTBOUND_POSTQUEUE_ISR          0x00000010
+/* DoorBell*/
+#define ARCMSR_ARC1214_DRV2IOP_DATA_IN_READY           0x00000001
+#define ARCMSR_ARC1214_DRV2IOP_DATA_OUT_READ           0x00000002
+/*inbound message 0 ready*/
+#define ARCMSR_ARC1214_IOP2DRV_DATA_WRITE_OK           0x00000001
+/*outbound DATA WRITE isr door bell clear*/
+#define ARCMSR_ARC1214_IOP2DRV_DATA_READ_OK            0x00000002
+/*outbound message 0 ready*/
+#define ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE                0x02000000
+/*outbound message cmd isr door bell clear*/
+/*ARCMSR_HBAMU_MESSAGE_FIRMWARE_OK*/
+#define ARCMSR_ARC1214_MESSAGE_FIRMWARE_OK             0x80000000
+#define ARCMSR_ARC1214_OUTBOUND_LIST_INTERRUPT_CLEAR   0x00000001
+/*
+*******************************************************************************
 **    ARECA SCSI COMMAND DESCRIPTOR BLOCK size 0x1F8 (504)
 *******************************************************************************
 */
@@ -496,6 +551,56 @@ struct MessageUnit_C{
        uint32_t        msgcode_rwbuffer[256];                  /*2200 23FF*/
 };
 /*
+*********************************************************************
+**     Messaging Unit (MU) of Type D processor
+*********************************************************************
+*/
+struct InBound_SRB {
+       uint32_t addressLow; /* pointer to SRB block */
+       uint32_t addressHigh;
+       uint32_t length; /* in DWORDs */
+       uint32_t reserved0;
+};
+
+struct OutBound_SRB {
+       uint32_t addressLow; /* pointer to SRB block */
+       uint32_t addressHigh;
+};
+
+struct MessageUnit_D {
+       struct InBound_SRB      post_qbuffer[ARCMSR_MAX_ARC1214_POSTQUEUE];
+       volatile struct OutBound_SRB
+                               done_qbuffer[ARCMSR_MAX_ARC1214_DONEQUEUE];
+       u16 postq_index;
+       volatile u16 doneq_index;
+       u32 __iomem *chip_id;                   /* 0x00004 */
+       u32 __iomem *cpu_mem_config;            /* 0x00008 */
+       u32 __iomem *i2o_host_interrupt_mask;   /* 0x00034 */
+       u32 __iomem *sample_at_reset;           /* 0x00100 */
+       u32 __iomem *reset_request;             /* 0x00108 */
+       u32 __iomem *host_int_status;           /* 0x00200 */
+       u32 __iomem *pcief0_int_enable;         /* 0x0020C */
+       u32 __iomem *inbound_msgaddr0;          /* 0x00400 */
+       u32 __iomem *inbound_msgaddr1;          /* 0x00404 */
+       u32 __iomem *outbound_msgaddr0;         /* 0x00420 */
+       u32 __iomem *outbound_msgaddr1;         /* 0x00424 */
+       u32 __iomem *inbound_doorbell;          /* 0x00460 */
+       u32 __iomem *outbound_doorbell;         /* 0x00480 */
+       u32 __iomem *outbound_doorbell_enable;  /* 0x00484 */
+       u32 __iomem *inboundlist_base_low;      /* 0x01000 */
+       u32 __iomem *inboundlist_base_high;     /* 0x01004 */
+       u32 __iomem *inboundlist_write_pointer; /* 0x01018 */
+       u32 __iomem *outboundlist_base_low;     /* 0x01060 */
+       u32 __iomem *outboundlist_base_high;    /* 0x01064 */
+       u32 __iomem *outboundlist_copy_pointer; /* 0x0106C */
+       u32 __iomem *outboundlist_read_pointer; /* 0x01070 0x01072 */
+       u32 __iomem *outboundlist_interrupt_cause;      /* 0x1088 */
+       u32 __iomem *outboundlist_interrupt_enable;     /* 0x108C */
+       u32 __iomem *message_wbuffer;           /* 0x2000 */
+       u32 __iomem *message_rbuffer;           /* 0x2100 */
+       u32 __iomem *msgcode_rwbuffer;          /* 0x2200 */
+};
+/*
 *******************************************************************************
 **                 Adapter Control Block
 *******************************************************************************
@@ -518,12 +623,15 @@ struct AdapterControlBlock
        uint32_t                        reg_mu_acc_handle0;
        spinlock_t                                              eh_lock;
        spinlock_t                                              ccblist_lock;
+       spinlock_t                      postq_lock;
+       spinlock_t                      doneq_lock;
        spinlock_t                      rqbuffer_lock;
        spinlock_t                      wqbuffer_lock;
        union {
                struct MessageUnit_A __iomem *pmuA;
                struct MessageUnit_B    *pmuB;
                struct MessageUnit_C __iomem *pmuC;
+               struct MessageUnit_D    *pmuD;
        };
        /* message unit ATU inbound base address0 */
        void __iomem *mem_base0;
index 26bcdc0fa8bff9fd701654716e31fbbbc6e706e1..b3cb969c72d50c8452ec240983693e56f92e5b4c 100644 (file)
@@ -108,6 +108,7 @@ static void arcmsr_message_isr_bh_fn(struct work_struct *work);
 static bool arcmsr_get_firmware_spec(struct AdapterControlBlock *acb);
 static void arcmsr_start_adapter_bgrb(struct AdapterControlBlock *acb);
 static void arcmsr_hbaC_message_isr(struct AdapterControlBlock *pACB);
+static void arcmsr_hbaD_message_isr(struct AdapterControlBlock *acb);
 static void arcmsr_hardware_reset(struct AdapterControlBlock *acb);
 static const char *arcmsr_info(struct Scsi_Host *);
 static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb);
@@ -161,6 +162,8 @@ static struct pci_device_id arcmsr_device_id_table[] = {
                .driver_data = ACB_ADAPTER_TYPE_B},
        {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1210),
                .driver_data = ACB_ADAPTER_TYPE_A},
+       {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1214),
+               .driver_data = ACB_ADAPTER_TYPE_D},
        {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1220),
                .driver_data = ACB_ADAPTER_TYPE_A},
        {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1230),
@@ -202,7 +205,8 @@ static struct pci_driver arcmsr_pci_driver = {
 static void arcmsr_free_mu(struct AdapterControlBlock *acb)
 {
        switch (acb->adapter_type) {
-       case ACB_ADAPTER_TYPE_B:{
+       case ACB_ADAPTER_TYPE_B:
+       case ACB_ADAPTER_TYPE_D: {
                dma_free_coherent(&acb->pdev->dev, acb->roundup_ccbsize,
                        acb->dma_coherent2, acb->dma_coherent_handle2);
                break;
@@ -251,6 +255,25 @@ static bool arcmsr_remap_pciregion(struct AdapterControlBlock *acb)
                }
                break;
        }
+       case ACB_ADAPTER_TYPE_D: {
+               void __iomem *mem_base0;
+               unsigned long addr, range, flags;
+
+               addr = (unsigned long)pci_resource_start(pdev, 0);
+               range = pci_resource_len(pdev, 0);
+               flags = pci_resource_flags(pdev, 0);
+               if (flags & IORESOURCE_CACHEABLE)
+                       mem_base0 = ioremap(addr, range);
+               else
+                       mem_base0 = ioremap_nocache(addr, range);
+               if (!mem_base0) {
+                       pr_notice("arcmsr%d: memory mapping region fail\n",
+                               acb->host->host_no);
+                       return false;
+               }
+               acb->mem_base0 = mem_base0;
+               break;
+               }
        }
        return true;
 }
@@ -271,6 +294,10 @@ static void arcmsr_unmap_pciregion(struct AdapterControlBlock *acb)
        case ACB_ADAPTER_TYPE_C:{
                iounmap(acb->pmuC);
        }
+       break;
+       case ACB_ADAPTER_TYPE_D:
+               iounmap(acb->mem_base0);
+               break;
        }
 }
 
@@ -367,6 +394,23 @@ static uint8_t arcmsr_hbaC_wait_msgint_ready(struct AdapterControlBlock *pACB)
        return false;
 }
 
+static bool arcmsr_hbaD_wait_msgint_ready(struct AdapterControlBlock *pACB)
+{
+       struct MessageUnit_D *reg = pACB->pmuD;
+       int i;
+
+       for (i = 0; i < 2000; i++) {
+               if (readl(reg->outbound_doorbell)
+                       & ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE) {
+                       writel(ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE,
+                               reg->outbound_doorbell);
+                       return true;
+               }
+               msleep(10);
+       } /* max 20 seconds */
+       return false;
+}
+
 static void arcmsr_hbaA_flush_cache(struct AdapterControlBlock *acb)
 {
        struct MessageUnit_A __iomem *reg = acb->pmuA;
@@ -416,6 +460,24 @@ static void arcmsr_hbaC_flush_cache(struct AdapterControlBlock *pACB)
        } while (retry_count != 0);
        return;
 }
+
+static void arcmsr_hbaD_flush_cache(struct AdapterControlBlock *pACB)
+{
+       int retry_count = 15;
+       struct MessageUnit_D *reg = pACB->pmuD;
+
+       writel(ARCMSR_INBOUND_MESG0_FLUSH_CACHE, reg->inbound_msgaddr0);
+       do {
+               if (arcmsr_hbaD_wait_msgint_ready(pACB))
+                       break;
+
+               retry_count--;
+               pr_notice("arcmsr%d: wait 'flush adapter "
+                       "cache' timeout, retry count down = %d\n",
+                       pACB->host->host_no, retry_count);
+       } while (retry_count != 0);
+}
+
 static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb)
 {
        switch (acb->adapter_type) {
@@ -432,6 +494,10 @@ static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb)
        case ACB_ADAPTER_TYPE_C: {
                arcmsr_hbaC_flush_cache(acb);
                }
+               break;
+       case ACB_ADAPTER_TYPE_D:
+               arcmsr_hbaD_flush_cache(acb);
+               break;
        }
 }
 
@@ -475,9 +541,16 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
        acb->vir2phy_offset = (unsigned long)dma_coherent - (unsigned long)dma_coherent_handle;
        for(i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++){
                cdb_phyaddr = dma_coherent_handle + offsetof(struct CommandControlBlock, arcmsr_cdb);
-               ccb_tmp->cdb_phyaddr =
-                       ((acb->adapter_type == ACB_ADAPTER_TYPE_C) ?
-                        cdb_phyaddr : (cdb_phyaddr >> 5));
+               switch (acb->adapter_type) {
+               case ACB_ADAPTER_TYPE_A:
+               case ACB_ADAPTER_TYPE_B:
+                       ccb_tmp->cdb_phyaddr = cdb_phyaddr >> 5;
+                       break;
+               case ACB_ADAPTER_TYPE_C:
+               case ACB_ADAPTER_TYPE_D:
+                       ccb_tmp->cdb_phyaddr = cdb_phyaddr;
+                       break;
+               }
                acb->pccb_pool[i] = ccb_tmp;
                ccb_tmp->acb = acb;
                INIT_LIST_HEAD(&ccb_tmp->list);
@@ -521,6 +594,13 @@ static void arcmsr_message_isr_bh_fn(struct work_struct *work)
                devicemap = (char __iomem *)(&reg->msgcode_rwbuffer[21]);
                break;
        }
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D *reg  = acb->pmuD;
+
+               signature = (uint32_t __iomem *)(&reg->msgcode_rwbuffer[0]);
+               devicemap = (char __iomem *)(&reg->msgcode_rwbuffer[21]);
+               break;
+       }
        }
        atomic_inc(&acb->rq_map_token);
        if (readl(signature) != ARCMSR_SIGNATURE_GET_CONFIG)
@@ -651,6 +731,8 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        }
        spin_lock_init(&acb->eh_lock);
        spin_lock_init(&acb->ccblist_lock);
+       spin_lock_init(&acb->postq_lock);
+       spin_lock_init(&acb->doneq_lock);
        spin_lock_init(&acb->rqbuffer_lock);
        spin_lock_init(&acb->wqbuffer_lock);
        acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
@@ -836,6 +918,20 @@ static uint8_t arcmsr_hbaC_abort_allcmd(struct AdapterControlBlock *pACB)
        }
        return true;
 }
+
+static uint8_t arcmsr_hbaD_abort_allcmd(struct AdapterControlBlock *pACB)
+{
+       struct MessageUnit_D *reg = pACB->pmuD;
+
+       writel(ARCMSR_INBOUND_MESG0_ABORT_CMD, reg->inbound_msgaddr0);
+       if (!arcmsr_hbaD_wait_msgint_ready(pACB)) {
+               pr_notice("arcmsr%d: wait 'abort all outstanding "
+                       "command' timeout\n", pACB->host->host_no);
+               return false;
+       }
+       return true;
+}
+
 static uint8_t arcmsr_abort_allcmd(struct AdapterControlBlock *acb)
 {
        uint8_t rtnval = 0;
@@ -853,6 +949,11 @@ static uint8_t arcmsr_abort_allcmd(struct AdapterControlBlock *acb)
        case ACB_ADAPTER_TYPE_C: {
                rtnval = arcmsr_hbaC_abort_allcmd(acb);
                }
+               break;
+
+       case ACB_ADAPTER_TYPE_D:
+               rtnval = arcmsr_hbaD_abort_allcmd(acb);
+               break;
        }
        return rtnval;
 }
@@ -919,6 +1020,12 @@ static u32 arcmsr_disable_outbound_ints(struct AdapterControlBlock *acb)
                writel(orig_mask|ARCMSR_HBCMU_ALL_INTMASKENABLE, &reg->host_int_mask);
                }
                break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D *reg = acb->pmuD;
+               /* disable all outbound interrupt */
+               writel(ARCMSR_ARC1214_ALL_INT_DISABLE, reg->pcief0_int_enable);
+               }
+               break;
        }
        return orig_mask;
 }
@@ -1066,7 +1173,62 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
                        error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false;
                        arcmsr_drain_donequeue(acb, pCCB, error);
                }
-       }
+               }
+               break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D  *pmu = acb->pmuD;
+               uint32_t ccb_cdb_phy, outbound_write_pointer;
+               uint32_t doneq_index, index_stripped, addressLow, residual;
+               bool error;
+               struct CommandControlBlock *pCCB;
+
+               outbound_write_pointer = pmu->done_qbuffer[0].addressLow + 1;
+               doneq_index = pmu->doneq_index;
+               residual = atomic_read(&acb->ccboutstandingcount);
+               for (i = 0; i < residual; i++) {
+                       while ((doneq_index & 0xFFF) !=
+                               (outbound_write_pointer & 0xFFF)) {
+                               if (doneq_index & 0x4000) {
+                                       index_stripped = doneq_index & 0xFFF;
+                                       index_stripped += 1;
+                                       index_stripped %=
+                                               ARCMSR_MAX_ARC1214_DONEQUEUE;
+                                       pmu->doneq_index = index_stripped ?
+                                               (index_stripped | 0x4000) :
+                                               (index_stripped + 1);
+                               } else {
+                                       index_stripped = doneq_index;
+                                       index_stripped += 1;
+                                       index_stripped %=
+                                               ARCMSR_MAX_ARC1214_DONEQUEUE;
+                                       pmu->doneq_index = index_stripped ?
+                                               index_stripped :
+                                               ((index_stripped | 0x4000) + 1);
+                               }
+                               doneq_index = pmu->doneq_index;
+                               addressLow = pmu->done_qbuffer[doneq_index &
+                                       0xFFF].addressLow;
+                               ccb_cdb_phy = (addressLow & 0xFFFFFFF0);
+                               pARCMSR_CDB = (struct  ARCMSR_CDB *)
+                                       (acb->vir2phy_offset + ccb_cdb_phy);
+                               pCCB = container_of(pARCMSR_CDB,
+                                       struct CommandControlBlock, arcmsr_cdb);
+                               error = (addressLow &
+                                       ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ?
+                                       true : false;
+                               arcmsr_drain_donequeue(acb, pCCB, error);
+                               writel(doneq_index,
+                                       pmu->outboundlist_read_pointer);
+                       }
+                       mdelay(10);
+                       outbound_write_pointer =
+                               pmu->done_qbuffer[0].addressLow + 1;
+                       doneq_index = pmu->doneq_index;
+               }
+               pmu->postq_index = 0;
+               pmu->doneq_index = 0x40FF;
+               }
+               break;
        }
 }
 
@@ -1175,6 +1337,14 @@ static void arcmsr_enable_outbound_ints(struct AdapterControlBlock *acb,
                writel(intmask_org & mask, &reg->host_int_mask);
                acb->outbound_int_enable = ~(intmask_org & mask) & 0x0000000f;
                }
+               break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D *reg = acb->pmuD;
+
+               mask = ARCMSR_ARC1214_ALL_INT_ENABLE;
+               writel(intmask_org | mask, reg->pcief0_int_enable);
+               break;
+               }
        }
 }
 
@@ -1282,6 +1452,38 @@ static void arcmsr_post_ccb(struct AdapterControlBlock *acb, struct CommandContr
                        writel(ccb_post_stamp, &phbcmu->inbound_queueport_low);
                }
                }
+               break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D  *pmu = acb->pmuD;
+               u16 index_stripped;
+               u16 postq_index;
+               unsigned long flags;
+               struct InBound_SRB *pinbound_srb;
+
+               spin_lock_irqsave(&acb->postq_lock, flags);
+               postq_index = pmu->postq_index;
+               pinbound_srb = (struct InBound_SRB *)&(pmu->post_qbuffer[postq_index & 0xFF]);
+               pinbound_srb->addressHigh = dma_addr_hi32(cdb_phyaddr);
+               pinbound_srb->addressLow = dma_addr_lo32(cdb_phyaddr);
+               pinbound_srb->length = ccb->arc_cdb_size >> 2;
+               arcmsr_cdb->msgContext = dma_addr_lo32(cdb_phyaddr);
+               if (postq_index & 0x4000) {
+                       index_stripped = postq_index & 0xFF;
+                       index_stripped += 1;
+                       index_stripped %= ARCMSR_MAX_ARC1214_POSTQUEUE;
+                       pmu->postq_index = index_stripped ?
+                               (index_stripped | 0x4000) : index_stripped;
+               } else {
+                       index_stripped = postq_index;
+                       index_stripped += 1;
+                       index_stripped %= ARCMSR_MAX_ARC1214_POSTQUEUE;
+                       pmu->postq_index = index_stripped ? index_stripped :
+                               (index_stripped | 0x4000);
+               }
+               writel(postq_index, pmu->inboundlist_write_pointer);
+               spin_unlock_irqrestore(&acb->postq_lock, flags);
+               break;
+               }
        }
 }
 
@@ -1323,6 +1525,18 @@ static void arcmsr_hbaC_stop_bgrb(struct AdapterControlBlock *pACB)
        }
        return;
 }
+
+static void arcmsr_hbaD_stop_bgrb(struct AdapterControlBlock *pACB)
+{
+       struct MessageUnit_D *reg = pACB->pmuD;
+
+       pACB->acb_flags &= ~ACB_F_MSG_START_BGRB;
+       writel(ARCMSR_INBOUND_MESG0_STOP_BGRB, reg->inbound_msgaddr0);
+       if (!arcmsr_hbaD_wait_msgint_ready(pACB))
+               pr_notice("arcmsr%d: wait 'stop adapter background rebulid' "
+                       "timeout\n", pACB->host->host_no);
+}
+
 static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb)
 {
        switch (acb->adapter_type) {
@@ -1338,6 +1552,10 @@ static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb)
        case ACB_ADAPTER_TYPE_C: {
                arcmsr_hbaC_stop_bgrb(acb);
                }
+               break;
+       case ACB_ADAPTER_TYPE_D:
+               arcmsr_hbaD_stop_bgrb(acb);
+               break;
        }
 }
 
@@ -1362,8 +1580,16 @@ static void arcmsr_iop_message_read(struct AdapterControlBlock *acb)
                break;
        case ACB_ADAPTER_TYPE_C: {
                struct MessageUnit_C __iomem *reg = acb->pmuC;
+
                writel(ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK, &reg->inbound_doorbell);
                }
+               break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D *reg = acb->pmuD;
+               writel(ARCMSR_ARC1214_DRV2IOP_DATA_OUT_READ,
+                       reg->inbound_doorbell);
+               }
+               break;
        }
 }
 
@@ -1398,6 +1624,12 @@ static void arcmsr_iop_message_wrote(struct AdapterControlBlock *acb)
                writel(ARCMSR_HBCMU_DRV2IOP_DATA_WRITE_OK, &reg->inbound_doorbell);
                }
                break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D *reg = acb->pmuD;
+               writel(ARCMSR_ARC1214_DRV2IOP_DATA_IN_READY,
+                       reg->inbound_doorbell);
+               }
+               break;
        }
 }
 
@@ -1421,6 +1653,12 @@ struct QBUFFER __iomem *arcmsr_get_iop_rqbuffer(struct AdapterControlBlock *acb)
                struct MessageUnit_C __iomem *phbcmu = acb->pmuC;
                qbuffer = (struct QBUFFER __iomem *)&phbcmu->message_rbuffer;
                }
+               break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D *reg = acb->pmuD;
+               qbuffer = (struct QBUFFER __iomem *)reg->message_rbuffer;
+               }
+               break;
        }
        return qbuffer;
 }
@@ -1444,8 +1682,13 @@ static struct QBUFFER __iomem *arcmsr_get_iop_wqbuffer(struct AdapterControlBloc
        case ACB_ADAPTER_TYPE_C: {
                struct MessageUnit_C __iomem *reg = acb->pmuC;
                pqbuffer = (struct QBUFFER __iomem *)&reg->message_wbuffer;
-       }
-
+               }
+               break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D *reg = acb->pmuD;
+               pqbuffer = (struct QBUFFER __iomem *)reg->message_wbuffer;
+               }
+               break;
        }
        return pqbuffer;
 }
@@ -1500,7 +1743,7 @@ arcmsr_Read_iop_rqbuffer_data(struct AdapterControlBlock *acb,
        uint8_t __iomem *iop_data;
        uint32_t iop_len;
 
-       if (acb->adapter_type & ACB_ADAPTER_TYPE_C)
+       if (acb->adapter_type & (ACB_ADAPTER_TYPE_C | ACB_ADAPTER_TYPE_D))
                return arcmsr_Read_iop_rqbuffer_in_DWORD(acb, prbuffer);
        iop_data = (uint8_t __iomem *)prbuffer->data;
        iop_len = readl(&prbuffer->data_len);
@@ -1586,7 +1829,7 @@ arcmsr_write_ioctldata2iop(struct AdapterControlBlock *acb)
        uint8_t __iomem *iop_data;
        int32_t allxfer_len = 0;
 
-       if (acb->adapter_type & ACB_ADAPTER_TYPE_C) {
+       if (acb->adapter_type & (ACB_ADAPTER_TYPE_C | ACB_ADAPTER_TYPE_D)) {
                arcmsr_write_ioctldata2iop_in_DWORD(acb);
                return;
        }
@@ -1662,6 +1905,27 @@ static void arcmsr_hbaC_doorbell_isr(struct AdapterControlBlock *pACB)
                | ARCMSR_HBCMU_IOP2DRV_DATA_READ_OK
                | ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE));
 }
+
+static void arcmsr_hbaD_doorbell_isr(struct AdapterControlBlock *pACB)
+{
+       uint32_t outbound_doorbell;
+       struct MessageUnit_D  *pmu = pACB->pmuD;
+
+       outbound_doorbell = readl(pmu->outbound_doorbell);
+       do {
+               writel(outbound_doorbell, pmu->outbound_doorbell);
+               if (outbound_doorbell & ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE)
+                       arcmsr_hbaD_message_isr(pACB);
+               if (outbound_doorbell & ARCMSR_ARC1214_IOP2DRV_DATA_WRITE_OK)
+                       arcmsr_iop2drv_data_wrote_handle(pACB);
+               if (outbound_doorbell & ARCMSR_ARC1214_IOP2DRV_DATA_READ_OK)
+                       arcmsr_iop2drv_data_read_handle(pACB);
+               outbound_doorbell = readl(pmu->outbound_doorbell);
+       } while (outbound_doorbell & (ARCMSR_ARC1214_IOP2DRV_DATA_WRITE_OK
+               | ARCMSR_ARC1214_IOP2DRV_DATA_READ_OK
+               | ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE));
+}
+
 static void arcmsr_hbaA_postqueue_isr(struct AdapterControlBlock *acb)
 {
        uint32_t flag_ccb;
@@ -1728,6 +1992,59 @@ static void arcmsr_hbaC_postqueue_isr(struct AdapterControlBlock *acb)
                }
        }
 }
+
+static void arcmsr_hbaD_postqueue_isr(struct AdapterControlBlock *acb)
+{
+       u32 outbound_write_pointer, doneq_index, index_stripped;
+       uint32_t addressLow, ccb_cdb_phy;
+       int error;
+       struct MessageUnit_D  *pmu;
+       struct ARCMSR_CDB *arcmsr_cdb;
+       struct CommandControlBlock *ccb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&acb->doneq_lock, flags);
+       pmu = acb->pmuD;
+       outbound_write_pointer = pmu->done_qbuffer[0].addressLow + 1;
+       doneq_index = pmu->doneq_index;
+       if ((doneq_index & 0xFFF) != (outbound_write_pointer & 0xFFF)) {
+               do {
+                       if (doneq_index & 0x4000) {
+                               index_stripped = doneq_index & 0xFFF;
+                               index_stripped += 1;
+                               index_stripped %= ARCMSR_MAX_ARC1214_DONEQUEUE;
+                               pmu->doneq_index = index_stripped
+                                       ? (index_stripped | 0x4000) :
+                                       (index_stripped + 1);
+                       } else {
+                               index_stripped = doneq_index;
+                               index_stripped += 1;
+                               index_stripped %= ARCMSR_MAX_ARC1214_DONEQUEUE;
+                               pmu->doneq_index = index_stripped
+                                       ? index_stripped :
+                                       ((index_stripped | 0x4000) + 1);
+                       }
+                       doneq_index = pmu->doneq_index;
+                       addressLow = pmu->done_qbuffer[doneq_index &
+                               0xFFF].addressLow;
+                       ccb_cdb_phy = (addressLow & 0xFFFFFFF0);
+                       arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset
+                               + ccb_cdb_phy);
+                       ccb = container_of(arcmsr_cdb,
+                               struct CommandControlBlock, arcmsr_cdb);
+                       error = (addressLow & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1)
+                               ? true : false;
+                       arcmsr_drain_donequeue(acb, ccb, error);
+                       writel(doneq_index, pmu->outboundlist_read_pointer);
+               } while ((doneq_index & 0xFFF) !=
+                       (outbound_write_pointer & 0xFFF));
+       }
+       writel(ARCMSR_ARC1214_OUTBOUND_LIST_INTERRUPT_CLEAR,
+               pmu->outboundlist_interrupt_cause);
+       readl(pmu->outboundlist_interrupt_cause);
+       spin_unlock_irqrestore(&acb->doneq_lock, flags);
+}
+
 /*
 **********************************************************************************
 ** Handle a message interrupt
@@ -1768,6 +2085,15 @@ static void arcmsr_hbaC_message_isr(struct AdapterControlBlock *acb)
        schedule_work(&acb->arcmsr_do_message_isr_bh);
 }
 
+static void arcmsr_hbaD_message_isr(struct AdapterControlBlock *acb)
+{
+       struct MessageUnit_D *reg  = acb->pmuD;
+
+       writel(ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE, reg->outbound_doorbell);
+       readl(reg->outbound_doorbell);
+       schedule_work(&acb->arcmsr_do_message_isr_bh);
+}
+
 static int arcmsr_hbaA_handle_isr(struct AdapterControlBlock *acb)
 {
        uint32_t outbound_intstatus;
@@ -1845,6 +2171,32 @@ static int arcmsr_hbaC_handle_isr(struct AdapterControlBlock *pACB)
                ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR));
        return IRQ_HANDLED;
 }
+
+static irqreturn_t arcmsr_hbaD_handle_isr(struct AdapterControlBlock *pACB)
+{
+       u32 host_interrupt_status;
+       struct MessageUnit_D  *pmu = pACB->pmuD;
+
+       host_interrupt_status = readl(pmu->host_int_status) &
+               (ARCMSR_ARC1214_OUTBOUND_POSTQUEUE_ISR |
+               ARCMSR_ARC1214_OUTBOUND_DOORBELL_ISR);
+       if (!host_interrupt_status)
+               return IRQ_NONE;
+       do {
+               /* MU post queue interrupts*/
+               if (host_interrupt_status &
+                       ARCMSR_ARC1214_OUTBOUND_POSTQUEUE_ISR)
+                       arcmsr_hbaD_postqueue_isr(pACB);
+               if (host_interrupt_status &
+                       ARCMSR_ARC1214_OUTBOUND_DOORBELL_ISR)
+                       arcmsr_hbaD_doorbell_isr(pACB);
+               host_interrupt_status = readl(pmu->host_int_status);
+       } while (host_interrupt_status &
+               (ARCMSR_ARC1214_OUTBOUND_POSTQUEUE_ISR |
+               ARCMSR_ARC1214_OUTBOUND_DOORBELL_ISR));
+       return IRQ_HANDLED;
+}
+
 static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb)
 {
        switch (acb->adapter_type) {
@@ -1856,6 +2208,8 @@ static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb)
                break;
        case ACB_ADAPTER_TYPE_C:
                return arcmsr_hbaC_handle_isr(acb);
+       case ACB_ADAPTER_TYPE_D:
+               return arcmsr_hbaD_handle_isr(acb);
        default:
                return IRQ_NONE;
        }
@@ -2522,6 +2876,137 @@ static bool arcmsr_hbaC_get_config(struct AdapterControlBlock *pACB)
        /*all interrupt service will be enable at arcmsr_iop_init*/
        return true;
 }
+
+static bool arcmsr_hbaD_get_config(struct AdapterControlBlock *acb)
+{
+       char *acb_firm_model = acb->firm_model;
+       char *acb_firm_version = acb->firm_version;
+       char *acb_device_map = acb->device_map;
+       char __iomem *iop_firm_model;
+       char __iomem *iop_firm_version;
+       char __iomem *iop_device_map;
+       u32 count;
+       struct MessageUnit_D *reg ;
+       void *dma_coherent2;
+       dma_addr_t dma_coherent_handle2;
+       struct pci_dev *pdev = acb->pdev;
+
+       acb->roundup_ccbsize = roundup(sizeof(struct MessageUnit_D), 32);
+       dma_coherent2 = dma_alloc_coherent(&pdev->dev, acb->roundup_ccbsize,
+               &dma_coherent_handle2, GFP_KERNEL);
+       if (!dma_coherent2) {
+               pr_notice("DMA allocation failed...\n");
+               return false;
+       }
+       memset(dma_coherent2, 0, acb->roundup_ccbsize);
+       acb->dma_coherent_handle2 = dma_coherent_handle2;
+       acb->dma_coherent2 = dma_coherent2;
+       reg = (struct MessageUnit_D *)dma_coherent2;
+       acb->pmuD = reg;
+       reg->chip_id = acb->mem_base0 + ARCMSR_ARC1214_CHIP_ID;
+       reg->cpu_mem_config = acb->mem_base0 +
+               ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION;
+       reg->i2o_host_interrupt_mask = acb->mem_base0 +
+               ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK;
+       reg->sample_at_reset = acb->mem_base0 + ARCMSR_ARC1214_SAMPLE_RESET;
+       reg->reset_request = acb->mem_base0 + ARCMSR_ARC1214_RESET_REQUEST;
+       reg->host_int_status = acb->mem_base0 +
+               ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS;
+       reg->pcief0_int_enable = acb->mem_base0 +
+               ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE;
+       reg->inbound_msgaddr0 = acb->mem_base0 +
+               ARCMSR_ARC1214_INBOUND_MESSAGE0;
+       reg->inbound_msgaddr1 = acb->mem_base0 +
+               ARCMSR_ARC1214_INBOUND_MESSAGE1;
+       reg->outbound_msgaddr0 = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_MESSAGE0;
+       reg->outbound_msgaddr1 = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_MESSAGE1;
+       reg->inbound_doorbell = acb->mem_base0 +
+               ARCMSR_ARC1214_INBOUND_DOORBELL;
+       reg->outbound_doorbell = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_DOORBELL;
+       reg->outbound_doorbell_enable = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE;
+       reg->inboundlist_base_low = acb->mem_base0 +
+               ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW;
+       reg->inboundlist_base_high = acb->mem_base0 +
+               ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH;
+       reg->inboundlist_write_pointer = acb->mem_base0 +
+               ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER;
+       reg->outboundlist_base_low = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW;
+       reg->outboundlist_base_high = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH;
+       reg->outboundlist_copy_pointer = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER;
+       reg->outboundlist_read_pointer = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER;
+       reg->outboundlist_interrupt_cause = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE;
+       reg->outboundlist_interrupt_enable = acb->mem_base0 +
+               ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE;
+       reg->message_wbuffer = acb->mem_base0 + ARCMSR_ARC1214_MESSAGE_WBUFFER;
+       reg->message_rbuffer = acb->mem_base0 + ARCMSR_ARC1214_MESSAGE_RBUFFER;
+       reg->msgcode_rwbuffer = acb->mem_base0 +
+               ARCMSR_ARC1214_MESSAGE_RWBUFFER;
+       iop_firm_model = (char __iomem *)(&reg->msgcode_rwbuffer[15]);
+       iop_firm_version = (char __iomem *)(&reg->msgcode_rwbuffer[17]);
+       iop_device_map = (char __iomem *)(&reg->msgcode_rwbuffer[21]);
+       if (readl(acb->pmuD->outbound_doorbell) &
+               ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE) {
+               writel(ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE,
+                       acb->pmuD->outbound_doorbell);/*clear interrupt*/
+       }
+       /* post "get config" instruction */
+       writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, reg->inbound_msgaddr0);
+       /* wait message ready */
+       if (!arcmsr_hbaD_wait_msgint_ready(acb)) {
+               pr_notice("arcmsr%d: wait get adapter firmware "
+                       "miscellaneous data timeout\n", acb->host->host_no);
+               dma_free_coherent(&acb->pdev->dev, acb->roundup_ccbsize,
+                       acb->dma_coherent2, acb->dma_coherent_handle2);
+               return false;
+       }
+       count = 8;
+       while (count) {
+               *acb_firm_model = readb(iop_firm_model);
+               acb_firm_model++;
+               iop_firm_model++;
+               count--;
+       }
+       count = 16;
+       while (count) {
+               *acb_firm_version = readb(iop_firm_version);
+               acb_firm_version++;
+               iop_firm_version++;
+               count--;
+       }
+       count = 16;
+       while (count) {
+               *acb_device_map = readb(iop_device_map);
+               acb_device_map++;
+               iop_device_map++;
+               count--;
+       }
+       acb->signature = readl(&reg->msgcode_rwbuffer[1]);
+       /*firm_signature,1,00-03*/
+       acb->firm_request_len = readl(&reg->msgcode_rwbuffer[2]);
+       /*firm_request_len,1,04-07*/
+       acb->firm_numbers_queue = readl(&reg->msgcode_rwbuffer[3]);
+       /*firm_numbers_queue,2,08-11*/
+       acb->firm_sdram_size = readl(&reg->msgcode_rwbuffer[4]);
+       /*firm_sdram_size,3,12-15*/
+       acb->firm_hd_channels = readl(&reg->msgcode_rwbuffer[5]);
+       /*firm_hd_channels,4,16-19*/
+       acb->firm_cfg_version = readl(&reg->msgcode_rwbuffer[25]);
+       pr_notice("Areca RAID Controller%d: Model %s, F/W %s\n",
+               acb->host->host_no,
+               acb->firm_model,
+               acb->firm_version);
+       return true;
+}
+
 static bool arcmsr_get_firmware_spec(struct AdapterControlBlock *acb)
 {
        bool rtn = false;
@@ -2536,6 +3021,9 @@ static bool arcmsr_get_firmware_spec(struct AdapterControlBlock *acb)
        case ACB_ADAPTER_TYPE_C:
                rtn = arcmsr_hbaC_get_config(acb);
                break;
+       case ACB_ADAPTER_TYPE_D:
+               rtn = arcmsr_hbaD_get_config(acb);
+               break;
        default:
                break;
        }
@@ -2725,6 +3213,89 @@ polling_hbc_ccb_retry:
        }
        return rtn;
 }
+
+static int arcmsr_hbaD_polling_ccbdone(struct AdapterControlBlock *acb,
+                               struct CommandControlBlock *poll_ccb)
+{
+       bool error;
+       uint32_t poll_ccb_done = 0, poll_count = 0, flag_ccb, ccb_cdb_phy;
+       int rtn, doneq_index, index_stripped, outbound_write_pointer;
+       unsigned long flags;
+       struct ARCMSR_CDB *arcmsr_cdb;
+       struct CommandControlBlock *pCCB;
+       struct MessageUnit_D *pmu = acb->pmuD;
+
+polling_hbaD_ccb_retry:
+       poll_count++;
+       while (1) {
+               outbound_write_pointer = pmu->done_qbuffer[0].addressLow + 1;
+               doneq_index = pmu->doneq_index;
+               if ((outbound_write_pointer & 0xFFF) == (doneq_index & 0xFFF)) {
+                       if (poll_ccb_done) {
+                               rtn = SUCCESS;
+                               break;
+                       } else {
+                               msleep(25);
+                               if (poll_count > 40) {
+                                       rtn = FAILED;
+                                       break;
+                               }
+                               goto polling_hbaD_ccb_retry;
+                       }
+               }
+               spin_lock_irqsave(&acb->doneq_lock, flags);
+               if (doneq_index & 0x4000) {
+                       index_stripped = doneq_index & 0xFFF;
+                       index_stripped += 1;
+                       index_stripped %= ARCMSR_MAX_ARC1214_DONEQUEUE;
+                       pmu->doneq_index = index_stripped ?
+                               (index_stripped | 0x4000) :
+                               (index_stripped + 1);
+               } else {
+                       index_stripped = doneq_index;
+                       index_stripped += 1;
+                       index_stripped %= ARCMSR_MAX_ARC1214_DONEQUEUE;
+                       pmu->doneq_index = index_stripped ? index_stripped :
+                               ((index_stripped | 0x4000) + 1);
+               }
+               spin_unlock_irqrestore(&acb->doneq_lock, flags);
+               doneq_index = pmu->doneq_index;
+               flag_ccb = pmu->done_qbuffer[doneq_index & 0xFFF].addressLow;
+               ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
+               arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset +
+                       ccb_cdb_phy);
+               pCCB = container_of(arcmsr_cdb, struct CommandControlBlock,
+                       arcmsr_cdb);
+               poll_ccb_done |= (pCCB == poll_ccb) ? 1 : 0;
+               if ((pCCB->acb != acb) ||
+                       (pCCB->startdone != ARCMSR_CCB_START)) {
+                       if (pCCB->startdone == ARCMSR_CCB_ABORTED) {
+                               pr_notice("arcmsr%d: scsi id = %d "
+                                       "lun = %d ccb = '0x%p' poll command "
+                                       "abort successfully\n"
+                                       , acb->host->host_no
+                                       , pCCB->pcmd->device->id
+                                       , (u32)pCCB->pcmd->device->lun
+                                       , pCCB);
+                               pCCB->pcmd->result = DID_ABORT << 16;
+                               arcmsr_ccb_complete(pCCB);
+                               continue;
+                       }
+                       pr_notice("arcmsr%d: polling an illegal "
+                               "ccb command done ccb = '0x%p' "
+                               "ccboutstandingcount = %d\n"
+                               , acb->host->host_no
+                               , pCCB
+                               , atomic_read(&acb->ccboutstandingcount));
+                       continue;
+               }
+               error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1)
+                       ? true : false;
+               arcmsr_report_ccb_state(acb, pCCB, error);
+       }
+       return rtn;
+}
+
 static int arcmsr_polling_ccbdone(struct AdapterControlBlock *acb,
                                        struct CommandControlBlock *poll_ccb)
 {
@@ -2743,6 +3314,10 @@ static int arcmsr_polling_ccbdone(struct AdapterControlBlock *acb,
        case ACB_ADAPTER_TYPE_C: {
                rtn = arcmsr_hbaC_polling_ccbdone(acb, poll_ccb);
                }
+               break;
+       case ACB_ADAPTER_TYPE_D:
+               rtn = arcmsr_hbaD_polling_ccbdone(acb, poll_ccb);
+               break;
        }
        return rtn;
 }
@@ -2760,6 +3335,7 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
        */
        switch (acb->adapter_type) {
        case ACB_ADAPTER_TYPE_B:
+       case ACB_ADAPTER_TYPE_D:
                dma_coherent_handle = acb->dma_coherent_handle2;
                break;
        default:
@@ -2849,6 +3425,27 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
                        }
                }
                }
+               break;
+       case ACB_ADAPTER_TYPE_D: {
+               uint32_t __iomem *rwbuffer;
+               struct MessageUnit_D *reg = acb->pmuD;
+               reg->postq_index = 0;
+               reg->doneq_index = 0;
+               rwbuffer = reg->msgcode_rwbuffer;
+               writel(ARCMSR_SIGNATURE_SET_CONFIG, rwbuffer++);
+               writel(cdb_phyaddr_hi32, rwbuffer++);
+               writel(cdb_phyaddr, rwbuffer++);
+               writel(cdb_phyaddr + (ARCMSR_MAX_ARC1214_POSTQUEUE *
+                       sizeof(struct InBound_SRB)), rwbuffer++);
+               writel(0x100, rwbuffer);
+               writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, reg->inbound_msgaddr0);
+               if (!arcmsr_hbaD_wait_msgint_ready(acb)) {
+                       pr_notice("arcmsr%d: 'set command Q window' timeout\n",
+                               acb->host->host_no);
+                       return 1;
+               }
+               }
+               break;
        }
        return 0;
 }
@@ -2880,6 +3477,15 @@ static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb)
                        firmware_state = readl(&reg->outbound_msgaddr1);
                } while ((firmware_state & ARCMSR_HBCMU_MESSAGE_FIRMWARE_OK) == 0);
                }
+               break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D *reg = acb->pmuD;
+               do {
+                       firmware_state = readl(reg->outbound_msgaddr1);
+               } while ((firmware_state &
+                       ARCMSR_ARC1214_MESSAGE_FIRMWARE_OK) == 0);
+               }
+               break;
        }
 }
 
@@ -2950,6 +3556,35 @@ static void arcmsr_hbaC_request_device_map(struct AdapterControlBlock *acb)
        return;
 }
 
+static void arcmsr_hbaD_request_device_map(struct AdapterControlBlock *acb)
+{
+       struct MessageUnit_D *reg = acb->pmuD;
+
+       if (unlikely(atomic_read(&acb->rq_map_token) == 0) ||
+               ((acb->acb_flags & ACB_F_BUS_RESET) != 0) ||
+               ((acb->acb_flags & ACB_F_ABORT) != 0)) {
+               mod_timer(&acb->eternal_timer,
+                       jiffies + msecs_to_jiffies(6 * HZ));
+       } else {
+               acb->fw_flag = FW_NORMAL;
+               if (atomic_read(&acb->ante_token_value) ==
+                       atomic_read(&acb->rq_map_token)) {
+                       atomic_set(&acb->rq_map_token, 16);
+               }
+               atomic_set(&acb->ante_token_value,
+                       atomic_read(&acb->rq_map_token));
+               if (atomic_dec_and_test(&acb->rq_map_token)) {
+                       mod_timer(&acb->eternal_timer, jiffies +
+                               msecs_to_jiffies(6 * HZ));
+                       return;
+               }
+               writel(ARCMSR_INBOUND_MESG0_GET_CONFIG,
+                       reg->inbound_msgaddr0);
+               mod_timer(&acb->eternal_timer, jiffies +
+                       msecs_to_jiffies(6 * HZ));
+       }
+}
+
 static void arcmsr_request_device_map(unsigned long pacb)
 {
        struct AdapterControlBlock *acb = (struct AdapterControlBlock *)pacb;
@@ -2965,6 +3600,10 @@ static void arcmsr_request_device_map(unsigned long pacb)
                case ACB_ADAPTER_TYPE_C: {
                        arcmsr_hbaC_request_device_map(acb);
                }
+               break;
+               case ACB_ADAPTER_TYPE_D:
+                       arcmsr_hbaD_request_device_map(acb);
+               break;
        }
 }
 
@@ -3002,6 +3641,19 @@ static void arcmsr_hbaC_start_bgrb(struct AdapterControlBlock *pACB)
        }
        return;
 }
+
+static void arcmsr_hbaD_start_bgrb(struct AdapterControlBlock *pACB)
+{
+       struct MessageUnit_D *pmu = pACB->pmuD;
+
+       pACB->acb_flags |= ACB_F_MSG_START_BGRB;
+       writel(ARCMSR_INBOUND_MESG0_START_BGRB, pmu->inbound_msgaddr0);
+       if (!arcmsr_hbaD_wait_msgint_ready(pACB)) {
+               pr_notice("arcmsr%d: wait 'start adapter "
+                       "background rebulid' timeout\n", pACB->host->host_no);
+       }
+}
+
 static void arcmsr_start_adapter_bgrb(struct AdapterControlBlock *acb)
 {
        switch (acb->adapter_type) {
@@ -3013,6 +3665,10 @@ static void arcmsr_start_adapter_bgrb(struct AdapterControlBlock *acb)
                break;
        case ACB_ADAPTER_TYPE_C:
                arcmsr_hbaC_start_bgrb(acb);
+               break;
+       case ACB_ADAPTER_TYPE_D:
+               arcmsr_hbaD_start_bgrb(acb);
+               break;
        }
 }
 
@@ -3058,6 +3714,29 @@ static void arcmsr_clear_doorbell_queue_buffer(struct AdapterControlBlock *acb)
                                break;
                }
                }
+               break;
+       case ACB_ADAPTER_TYPE_D: {
+               struct MessageUnit_D *reg = acb->pmuD;
+               uint32_t outbound_doorbell, i;
+               /* empty doorbell Qbuffer if door bell ringed */
+               outbound_doorbell = readl(reg->outbound_doorbell);
+               writel(outbound_doorbell, reg->outbound_doorbell);
+               writel(ARCMSR_ARC1214_DRV2IOP_DATA_OUT_READ,
+                       reg->inbound_doorbell);
+               for (i = 0; i < 200; i++) {
+                       msleep(20);
+                       outbound_doorbell = readl(reg->outbound_doorbell);
+                       if (outbound_doorbell &
+                               ARCMSR_ARC1214_IOP2DRV_DATA_WRITE_OK) {
+                               writel(outbound_doorbell,
+                                       reg->outbound_doorbell);
+                               writel(ARCMSR_ARC1214_DRV2IOP_DATA_OUT_READ,
+                                       reg->inbound_doorbell);
+                       } else
+                               break;
+               }
+               }
+               break;
        }
 }
 
@@ -3088,6 +3767,7 @@ static void arcmsr_hardware_reset(struct AdapterControlBlock *acb)
        int i, count = 0;
        struct MessageUnit_A __iomem *pmuA = acb->pmuA;
        struct MessageUnit_C __iomem *pmuC = acb->pmuC;
+       struct MessageUnit_D *pmuD = acb->pmuD;
 
        /* backup pci config data */
        printk(KERN_NOTICE "arcmsr%d: executing hw bus reset .....\n", acb->host->host_no);
@@ -3108,6 +3788,8 @@ static void arcmsr_hardware_reset(struct AdapterControlBlock *acb)
                        writel(0xD, &pmuC->write_sequence);
                } while (((readl(&pmuC->host_diagnostic) & ARCMSR_ARC1880_DiagWrite_ENABLE) == 0) && (count < 5));
                writel(ARCMSR_ARC1880_RESET_ADAPTER, &pmuC->host_diagnostic);
+       } else if ((acb->dev_id == 0x1214)) {
+               writel(0x20, pmuD->reset_request);
        } else {
                pci_write_config_byte(acb->pdev, 0x84, 0x20);
        }
@@ -3304,6 +3986,66 @@ sleep:
                        }
                        break;
                }
+               case ACB_ADAPTER_TYPE_D: {
+                       if (acb->acb_flags & ACB_F_BUS_RESET) {
+                               long timeout;
+                               pr_notice("arcmsr: there is an bus reset"
+                                       " eh proceeding.......\n");
+                               timeout = wait_event_timeout(wait_q, (acb->acb_flags
+                                       & ACB_F_BUS_RESET) == 0, 220 * HZ);
+                               if (timeout)
+                                       return SUCCESS;
+                       }
+                       acb->acb_flags |= ACB_F_BUS_RESET;
+                       if (!arcmsr_iop_reset(acb)) {
+                               struct MessageUnit_D *reg;
+                               reg = acb->pmuD;
+                               arcmsr_hardware_reset(acb);
+                               acb->acb_flags &= ~ACB_F_IOP_INITED;
+                       nap:
+                               ssleep(ARCMSR_SLEEPTIME);
+                               if ((readl(reg->sample_at_reset) & 0x80) != 0) {
+                                       pr_err("arcmsr%d: waiting for "
+                                               "hw bus reset return, retry=%d\n",
+                                               acb->host->host_no, retry_count);
+                                       if (retry_count > ARCMSR_RETRYCOUNT) {
+                                               acb->fw_flag = FW_DEADLOCK;
+                                               pr_err("arcmsr%d: waiting for hw bus"
+                                                       " reset return, "
+                                                       "RETRY TERMINATED!!\n",
+                                                       acb->host->host_no);
+                                               return FAILED;
+                                       }
+                                       retry_count++;
+                                       goto nap;
+                               }
+                               acb->acb_flags |= ACB_F_IOP_INITED;
+                               /* disable all outbound interrupt */
+                               intmask_org = arcmsr_disable_outbound_ints(acb);
+                               arcmsr_get_firmware_spec(acb);
+                               arcmsr_start_adapter_bgrb(acb);
+                               arcmsr_clear_doorbell_queue_buffer(acb);
+                               arcmsr_enable_outbound_ints(acb, intmask_org);
+                               atomic_set(&acb->rq_map_token, 16);
+                               atomic_set(&acb->ante_token_value, 16);
+                               acb->fw_flag = FW_NORMAL;
+                               mod_timer(&acb->eternal_timer,
+                                       jiffies + msecs_to_jiffies(6 * HZ));
+                               acb->acb_flags &= ~ACB_F_BUS_RESET;
+                               rtn = SUCCESS;
+                               pr_err("arcmsr: scsi bus reset "
+                                       "eh returns with success\n");
+                       } else {
+                               acb->acb_flags &= ~ACB_F_BUS_RESET;
+                               atomic_set(&acb->rq_map_token, 16);
+                               atomic_set(&acb->ante_token_value, 16);
+                               acb->fw_flag = FW_NORMAL;
+                               mod_timer(&acb->eternal_timer,
+                                       jiffies + msecs_to_jiffies(6 * HZ));
+                               rtn = SUCCESS;
+                       }
+                       break;
+               }
        }
        return rtn;
 }
@@ -3380,6 +4122,7 @@ static const char *arcmsr_info(struct Scsi_Host *host)
        case PCI_DEVICE_ID_ARECA_1280:
                type = "SATA";
                break;
+       case PCI_DEVICE_ID_ARECA_1214:
        case PCI_DEVICE_ID_ARECA_1380:
        case PCI_DEVICE_ID_ARECA_1381:
        case PCI_DEVICE_ID_ARECA_1680: