]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
scsi: hisi_sas: Add SATA FIS check for v3 hw
[mirror_ubuntu-bionic-kernel.git] / drivers / scsi / hisi_sas / hisi_sas_v2_hw.c
index 4c06fccc73d0fa93f20700543c87550aefc6db2f..7b39c4d23328b5d00ac55cf91fead074821d9262 100644 (file)
 #define SAS_ECC_INTR_NCQ_MEM3_ECC_1B_OFF       19
 #define SAS_ECC_INTR_MSK               0x1ec
 #define HGC_ERR_STAT_EN                        0x238
+#define CQE_SEND_CNT                   0x248
 #define DLVRY_Q_0_BASE_ADDR_LO         0x260
 #define DLVRY_Q_0_BASE_ADDR_HI         0x264
 #define DLVRY_Q_0_DEPTH                        0x268
 #define CMD_HDR_RESP_REPORT_MSK                (0x1 << CMD_HDR_RESP_REPORT_OFF)
 #define CMD_HDR_TLR_CTRL_OFF           6
 #define CMD_HDR_TLR_CTRL_MSK           (0x3 << CMD_HDR_TLR_CTRL_OFF)
+#define CMD_HDR_PHY_ID_OFF             8
+#define CMD_HDR_PHY_ID_MSK             (0x1ff << CMD_HDR_PHY_ID_OFF)
+#define CMD_HDR_FORCE_PHY_OFF          17
+#define CMD_HDR_FORCE_PHY_MSK          (0x1 << CMD_HDR_FORCE_PHY_OFF)
 #define CMD_HDR_PORT_OFF               18
 #define CMD_HDR_PORT_MSK               (0xf << CMD_HDR_PORT_OFF)
 #define CMD_HDR_PRIORITY_OFF           27
@@ -1677,23 +1682,28 @@ get_free_slot_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq)
 static void start_delivery_v2_hw(struct hisi_sas_dq *dq)
 {
        struct hisi_hba *hisi_hba = dq->hisi_hba;
-       struct hisi_sas_slot *s, *s1;
+       struct hisi_sas_slot *s, *s1, *s2 = NULL;
        struct list_head *dq_list;
        int dlvry_queue = dq->id;
-       int wp, count = 0;
+       int wp;
 
        dq_list = &dq->list;
        list_for_each_entry_safe(s, s1, &dq->list, delivery) {
                if (!s->ready)
                        break;
-               count++;
-               wp = (s->dlvry_queue_slot + 1) % HISI_SAS_QUEUE_SLOTS;
+               s2 = s;
                list_del(&s->delivery);
        }
 
-       if (!count)
+       if (!s2)
                return;
 
+       /*
+        * Ensure that memories for slots built on other CPUs is observed.
+        */
+       smp_rmb();
+       wp = (s2->dlvry_queue_slot + 1) % HISI_SAS_QUEUE_SLOTS;
+
        hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), wp);
 }
 
@@ -1761,8 +1771,7 @@ static void prep_smp_v2_hw(struct hisi_hba *hisi_hba,
 }
 
 static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba,
-                         struct hisi_sas_slot *slot, int is_tmf,
-                         struct hisi_sas_tmf_task *tmf)
+                         struct hisi_sas_slot *slot)
 {
        struct sas_task *task = slot->task;
        struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
@@ -1771,7 +1780,8 @@ static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba,
        struct hisi_sas_port *port = slot->port;
        struct sas_ssp_task *ssp_task = &task->ssp_task;
        struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
-       int has_data = 0, priority = is_tmf;
+       struct hisi_sas_tmf_task *tmf = slot->tmf;
+       int has_data = 0, priority = !!tmf;
        u8 *buf_cmd;
        u32 dw1 = 0, dw2 = 0;
 
@@ -1782,7 +1792,7 @@ static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba,
                               (1 << CMD_HDR_CMD_OFF)); /* ssp */
 
        dw1 = 1 << CMD_HDR_VDTL_OFF;
-       if (is_tmf) {
+       if (tmf) {
                dw1 |= 2 << CMD_HDR_FRAME_TYPE_OFF;
                dw1 |= DIR_NO_DATA << CMD_HDR_DIR_OFF;
        } else {
@@ -1825,7 +1835,7 @@ static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba,
                sizeof(struct ssp_frame_hdr);
 
        memcpy(buf_cmd, &task->ssp_task.LUN, 8);
-       if (!is_tmf) {
+       if (!tmf) {
                buf_cmd[9] = task->ssp_task.task_attr |
                                (task->ssp_task.task_prio << 3);
                memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd,
@@ -2528,6 +2538,7 @@ static void prep_ata_v2_hw(struct hisi_hba *hisi_hba,
        struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
        struct asd_sas_port *sas_port = device->port;
        struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
+       struct hisi_sas_tmf_task *tmf = slot->tmf;
        u8 *buf_cmd;
        int has_data = 0, hdr_tag = 0;
        u32 dw1 = 0, dw2 = 0;
@@ -2540,6 +2551,12 @@ static void prep_ata_v2_hw(struct hisi_hba *hisi_hba,
        else
                hdr->dw0 |= cpu_to_le32(4 << CMD_HDR_CMD_OFF);
 
+       if (tmf && tmf->force_phy) {
+               hdr->dw0 |= CMD_HDR_FORCE_PHY_MSK;
+               hdr->dw0 |= cpu_to_le32((1 << tmf->phy_id)
+                               << CMD_HDR_PHY_ID_OFF);
+       }
+
        /* dw1 */
        switch (task->data_dir) {
        case DMA_TO_DEVICE:
@@ -2666,6 +2683,7 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
        struct device *dev = hisi_hba->dev;
        u32 *frame_rcvd = (u32 *)sas_phy->frame_rcvd;
        struct sas_identify_frame *id = (struct sas_identify_frame *)frame_rcvd;
+       unsigned long flags;
 
        hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 1);
 
@@ -2718,6 +2736,12 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
                        set_link_timer_quirk(hisi_hba);
        }
        hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP);
+       spin_lock_irqsave(&phy->lock, flags);
+       if (phy->reset_completion) {
+               phy->in_reset = 0;
+               complete(phy->reset_completion);
+       }
+       spin_unlock_irqrestore(&phy->lock, flags);
 
 end:
        hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
@@ -2838,7 +2862,8 @@ static void phy_bcast_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
 
        hisi_sas_phy_write32(hisi_hba, phy_no, SL_RX_BCAST_CHK_MSK, 1);
        bcast_status = hisi_sas_phy_read32(hisi_hba, phy_no, RX_PRIMS_STATUS);
-       if (bcast_status & RX_BCAST_CHG_MSK)
+       if ((bcast_status & RX_BCAST_CHG_MSK) &&
+           !test_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags))
                sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
        hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
                             CHL_INT0_SL_RX_BCST_ACK_MSK);
@@ -3206,6 +3231,7 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p)
        u32 ent_tmp, ent_msk, ent_int, port_id, link_rate, hard_phy_linkrate;
        irqreturn_t res = IRQ_HANDLED;
        u8 attached_sas_addr[SAS_ADDR_SIZE] = {0};
+       unsigned long flags;
        int phy_no, offset;
 
        phy_no = sas_phy->id;
@@ -3231,8 +3257,7 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p)
        if (fis->status & ATA_ERR) {
                dev_warn(dev, "sata int: phy%d FIS status: 0x%x\n", phy_no,
                                fis->status);
-               disable_phy_v2_hw(hisi_hba, phy_no);
-               enable_phy_v2_hw(hisi_hba, phy_no);
+               hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
                res = IRQ_NONE;
                goto end;
        }
@@ -3266,6 +3291,7 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p)
        sas_phy->oob_mode = SATA_OOB_MODE;
        /* Make up some unique SAS address */
        attached_sas_addr[0] = 0x50;
+       attached_sas_addr[6] = hisi_hba->shost->host_no;
        attached_sas_addr[7] = phy_no;
        memcpy(sas_phy->attached_sas_addr, attached_sas_addr, SAS_ADDR_SIZE);
        memcpy(sas_phy->frame_rcvd, fis, sizeof(struct dev_to_host_fis));
@@ -3279,6 +3305,12 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p)
        phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
        hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP);
 
+       spin_lock_irqsave(&phy->lock, flags);
+       if (phy->reset_completion) {
+               phy->in_reset = 0;
+               complete(phy->reset_completion);
+       }
+       spin_unlock_irqrestore(&phy->lock, flags);
 end:
        hisi_sas_write32(hisi_hba, ENT_INT_SRC1 + offset, ent_tmp);
        hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1 + offset, ent_msk);
@@ -3517,6 +3549,23 @@ static int write_gpio_v2_hw(struct hisi_hba *hisi_hba, u8 reg_type,
        return 0;
 }
 
+static void wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba,
+                                            int delay_ms, int timeout_ms)
+{
+       struct device *dev = hisi_hba->dev;
+       int entries, entries_old = 0, time;
+
+       for (time = 0; time < timeout_ms; time += delay_ms) {
+               entries = hisi_sas_read32(hisi_hba, CQE_SEND_CNT);
+               if (entries == entries_old)
+                       break;
+
+               entries_old = entries;
+               msleep(delay_ms);
+       }
+
+       dev_dbg(dev, "wait commands complete %dms\n", time);
+}
 
 static struct scsi_host_template sht_v2_hw = {
        .name                   = DRV_NAME,
@@ -3568,6 +3617,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = {
        .soft_reset = soft_reset_v2_hw,
        .get_phys_state = get_phys_state_v2_hw,
        .write_gpio = write_gpio_v2_hw,
+       .wait_cmds_complete_timeout = wait_cmds_complete_timeout_v2_hw,
        .sht = &sht_v2_hw,
 };