scsi: hisi_sas: Add support for DIX feature for v3 hw
[linux-2.6-microblaze.git] / drivers / scsi / hisi_sas / hisi_sas_v3_hw.c
index 726ac4b..11fc650 100644 (file)
@@ -399,6 +399,8 @@ struct hisi_sas_err_record_v3 {
 #define USR_DATA_BLOCK_SZ_OFF  20
 #define USR_DATA_BLOCK_SZ_MSK  (0x3 << USR_DATA_BLOCK_SZ_OFF)
 #define T10_CHK_MSK_OFF            16
+#define T10_CHK_REF_TAG_MSK (0xf0 << T10_CHK_MSK_OFF)
+#define T10_CHK_APP_TAG_MSK (0xc << T10_CHK_MSK_OFF)
 
 static bool hisi_sas_intr_conv;
 MODULE_PARM_DESC(intr_conv, "interrupt converge enable (0-1)");
@@ -969,19 +971,44 @@ static void prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba,
 
        hdr->prd_table_addr = cpu_to_le64(hisi_sas_sge_addr_dma(slot));
 
-       hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF);
+       hdr->sg_len |= cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF);
+}
+
+static void prep_prd_sge_dif_v3_hw(struct hisi_hba *hisi_hba,
+                                  struct hisi_sas_slot *slot,
+                                  struct hisi_sas_cmd_hdr *hdr,
+                                  struct scatterlist *scatter,
+                                  int n_elem)
+{
+       struct hisi_sas_sge_dif_page *sge_dif_page;
+       struct scatterlist *sg;
+       int i;
+
+       sge_dif_page = hisi_sas_sge_dif_addr_mem(slot);
+
+       for_each_sg(scatter, sg, n_elem, i) {
+               struct hisi_sas_sge *entry = &sge_dif_page->sge[i];
+
+               entry->addr = cpu_to_le64(sg_dma_address(sg));
+               entry->page_ctrl_0 = 0;
+               entry->page_ctrl_1 = 0;
+               entry->data_len = cpu_to_le32(sg_dma_len(sg));
+               entry->data_off = 0;
+       }
+
+       hdr->dif_prd_table_addr =
+               cpu_to_le64(hisi_sas_sge_dif_addr_dma(slot));
+
+       hdr->sg_len |= cpu_to_le32(n_elem << CMD_HDR_DIF_SGL_LEN_OFF);
 }
 
 static u32 get_prot_chk_msk_v3_hw(struct scsi_cmnd *scsi_cmnd)
 {
        unsigned char prot_flags = scsi_cmnd->prot_flags;
 
-       if (prot_flags & SCSI_PROT_TRANSFER_PI) {
-               if (prot_flags & SCSI_PROT_REF_CHECK)
-                       return 0xc << 16;
-               return 0xfc << 16;
-       }
-       return 0;
+       if (prot_flags & SCSI_PROT_REF_CHECK)
+               return T10_CHK_APP_TAG_MSK;
+       return T10_CHK_REF_TAG_MSK | T10_CHK_APP_TAG_MSK;
 }
 
 static void fill_prot_v3_hw(struct scsi_cmnd *scsi_cmnd,
@@ -992,15 +1019,33 @@ static void fill_prot_v3_hw(struct scsi_cmnd *scsi_cmnd,
        u32 lbrt_chk_val = t10_pi_ref_tag(scsi_cmnd->request);
 
        switch (prot_op) {
+       case SCSI_PROT_READ_INSERT:
+               prot->dw0 |= T10_INSRT_EN_MSK;
+               prot->lbrtgv = lbrt_chk_val;
+               break;
        case SCSI_PROT_READ_STRIP:
                prot->dw0 |= (T10_RMV_EN_MSK | T10_CHK_EN_MSK);
                prot->lbrtcv = lbrt_chk_val;
                prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd);
                break;
+       case SCSI_PROT_READ_PASS:
+               prot->dw0 |= T10_CHK_EN_MSK;
+               prot->lbrtcv = lbrt_chk_val;
+               prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd);
+               break;
        case SCSI_PROT_WRITE_INSERT:
                prot->dw0 |= T10_INSRT_EN_MSK;
                prot->lbrtgv = lbrt_chk_val;
                break;
+       case SCSI_PROT_WRITE_STRIP:
+               prot->dw0 |= (T10_RMV_EN_MSK | T10_CHK_EN_MSK);
+               prot->lbrtcv = lbrt_chk_val;
+               break;
+       case SCSI_PROT_WRITE_PASS:
+               prot->dw0 |= T10_CHK_EN_MSK;
+               prot->lbrtcv = lbrt_chk_val;
+               prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd);
+               break;
        default:
                WARN(1, "prot_op(0x%x) is not valid\n", prot_op);
                break;
@@ -1035,8 +1080,8 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
        struct sas_ssp_task *ssp_task = &task->ssp_task;
        struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
        struct hisi_sas_tmf_task *tmf = slot->tmf;
-       unsigned char prot_op = scsi_get_prot_op(scsi_cmnd);
        int has_data = 0, priority = !!tmf;
+       unsigned char prot_op;
        u8 *buf_cmd;
        u32 dw1 = 0, dw2 = 0, len = 0;
 
@@ -1051,6 +1096,7 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
                dw1 |= 2 << CMD_HDR_FRAME_TYPE_OFF;
                dw1 |= DIR_NO_DATA << CMD_HDR_DIR_OFF;
        } else {
+               prot_op = scsi_get_prot_op(scsi_cmnd);
                dw1 |= 1 << CMD_HDR_FRAME_TYPE_OFF;
                switch (scsi_cmnd->sc_data_direction) {
                case DMA_TO_DEVICE:
@@ -1076,9 +1122,15 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
        hdr->dw2 = cpu_to_le32(dw2);
        hdr->transfer_tags = cpu_to_le32(slot->idx);
 
-       if (has_data)
+       if (has_data) {
                prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter,
-                                       slot->n_elem);
+                                  slot->n_elem);
+
+               if (scsi_prot_sg_count(scsi_cmnd))
+                       prep_prd_sge_dif_v3_hw(hisi_hba, slot, hdr,
+                                              scsi_prot_sglist(scsi_cmnd),
+                                              slot->n_elem_dif);
+       }
 
        hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot));
        hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
@@ -1119,18 +1171,19 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
                fill_prot_v3_hw(scsi_cmnd, &prot);
                memcpy(buf_cmd_prot, &prot,
                       sizeof(struct hisi_sas_protect_iu_v3_hw));
-
                /*
                 * For READ, we need length of info read to memory, while for
                 * WRITE we need length of data written to the disk.
                 */
-               if (prot_op == SCSI_PROT_WRITE_INSERT) {
+               if (prot_op == SCSI_PROT_WRITE_INSERT ||
+                   prot_op == SCSI_PROT_READ_INSERT ||
+                   prot_op == SCSI_PROT_WRITE_PASS ||
+                   prot_op == SCSI_PROT_READ_PASS) {
                        unsigned int interval = scsi_prot_interval(scsi_cmnd);
                        unsigned int ilog2_interval = ilog2(interval);
 
                        len = (task->total_xfer_len >> ilog2_interval) * 8;
                }
-
        }
 
        hdr->dw1 = cpu_to_le32(dw1);
@@ -1290,6 +1343,7 @@ static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
        struct device *dev = hisi_hba->dev;
        unsigned long flags;
 
+       del_timer(&phy->timer);
        hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 1);
 
        port_id = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
@@ -1383,9 +1437,11 @@ end:
 
 static irqreturn_t phy_down_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
 {
+       struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
        u32 phy_state, sl_ctrl, txid_auto;
        struct device *dev = hisi_hba->dev;
 
+       del_timer(&phy->timer);
        hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1);
 
        phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
@@ -1554,6 +1610,19 @@ static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
        hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, irq_value);
 }
 
+static void handle_chl_int0_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+       u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0);
+
+       if (irq_value0 & CHL_INT0_PHY_RDY_MSK)
+               hisi_sas_phy_oob_ready(hisi_hba, phy_no);
+
+       hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
+                            irq_value0 & (~CHL_INT0_SL_RX_BCST_ACK_MSK)
+                            & (~CHL_INT0_SL_PHY_ENABLE_MSK)
+                            & (~CHL_INT0_NOT_RDY_MSK));
+}
+
 static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p)
 {
        struct hisi_hba *hisi_hba = p;
@@ -1564,8 +1633,8 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p)
                                & 0xeeeeeeee;
 
        while (irq_msk) {
-               u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no,
-                                                    CHL_INT0);
+               if (irq_msk & (2 << (phy_no * 4)))
+                       handle_chl_int0_v3_hw(hisi_hba, phy_no);
 
                if (irq_msk & (4 << (phy_no * 4)))
                        handle_chl_int1_v3_hw(hisi_hba, phy_no);
@@ -1573,13 +1642,6 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p)
                if (irq_msk & (8 << (phy_no * 4)))
                        handle_chl_int2_v3_hw(hisi_hba, phy_no);
 
-               if (irq_msk & (2 << (phy_no * 4)) && irq_value0) {
-                       hisi_sas_phy_write32(hisi_hba, phy_no,
-                                       CHL_INT0, irq_value0
-                                       & (~CHL_INT0_SL_RX_BCST_ACK_MSK)
-                                       & (~CHL_INT0_SL_PHY_ENABLE_MSK)
-                                       & (~CHL_INT0_NOT_RDY_MSK));
-               }
                irq_msk &= ~(0xe << (phy_no * 4));
                phy_no++;
        }
@@ -1646,6 +1708,7 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p)
        u32 irq_value, irq_msk;
        struct hisi_hba *hisi_hba = p;
        struct device *dev = hisi_hba->dev;
+       struct pci_dev *pdev = hisi_hba->pci_dev;
        int i;
 
        irq_msk = hisi_sas_read32(hisi_hba, ENT_INT_SRC_MSK3);
@@ -1677,6 +1740,17 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p)
                                error->msg, irq_value);
                        queue_work(hisi_hba->wq, &hisi_hba->rst_work);
                }
+
+               if (pdev->revision < 0x21) {
+                       u32 reg_val;
+
+                       reg_val = hisi_sas_read32(hisi_hba,
+                                                 AXI_MASTER_CFG_BASE +
+                                                 AM_CTRL_GLOBAL);
+                       reg_val |= AM_CTRL_SHUTDOWN_REQ_MSK;
+                       hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE +
+                                        AM_CTRL_GLOBAL, reg_val);
+               }
        }
 
        if (irq_value & BIT(ENT_INT_SRC3_ITC_INT_OFF)) {
@@ -2504,6 +2578,7 @@ static struct scsi_host_template sht_v3_hw = {
        .bios_param             = sas_bios_param,
        .this_id                = -1,
        .sg_tablesize           = HISI_SAS_SGE_PAGE_CNT,
+       .sg_prot_tablesize      = HISI_SAS_SGE_PAGE_CNT,
        .max_sectors            = SCSI_DEFAULT_MAX_SECTORS,
        .eh_device_reset_handler = sas_eh_device_reset_handler,
        .eh_target_reset_handler = sas_eh_target_reset_handler,
@@ -2579,7 +2654,7 @@ hisi_sas_shost_alloc_pci(struct pci_dev *pdev)
        if (hisi_sas_get_fw_info(hisi_hba) < 0)
                goto err_out;
 
-       if (hisi_sas_alloc(hisi_hba, shost)) {
+       if (hisi_sas_alloc(hisi_hba)) {
                hisi_sas_free(hisi_hba);
                goto err_out;
        }
@@ -2691,6 +2766,9 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                dev_info(dev, "Registering for DIF/DIX prot_mask=0x%x\n",
                         prot_mask);
                scsi_host_set_prot(hisi_hba->shost, prot_mask);
+               if (hisi_hba->prot_mask & HISI_SAS_DIX_PROT_MASK)
+                       scsi_host_set_guard(hisi_hba->shost,
+                                           SHOST_DIX_GUARD_CRC);
        }
 
        scsi_scan_host(shost);