]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
scsi: hisi_sas: add v2 hw internal abort timeout workaround
[mirror_ubuntu-bionic-kernel.git] / drivers / scsi / hisi_sas / hisi_sas_v2_hw.c
index 9b80b2ab69851bad91991b758cd876cc507c47a0..af19613d791ef0683dd809b2756d47043807c4bc 100644 (file)
 #define SL_CONTROL_NOTIFY_EN_MSK       (0x1 << SL_CONTROL_NOTIFY_EN_OFF)
 #define SL_CONTROL_CTA_OFF             17
 #define SL_CONTROL_CTA_MSK             (0x1 << SL_CONTROL_CTA_OFF)
-#define RX_PRIMS_STATUS         (PORT_BASE + 0x98)
-#define RX_BCAST_CHG_OFF        1
-#define RX_BCAST_CHG_MSK        (0x1 << RX_BCAST_CHG_OFF)
+#define RX_PRIMS_STATUS                        (PORT_BASE + 0x98)
+#define RX_BCAST_CHG_OFF               1
+#define RX_BCAST_CHG_MSK               (0x1 << RX_BCAST_CHG_OFF)
 #define TX_ID_DWORD0                   (PORT_BASE + 0x9c)
 #define TX_ID_DWORD1                   (PORT_BASE + 0xa0)
 #define TX_ID_DWORD2                   (PORT_BASE + 0xa4)
 #define TXID_AUTO_CT3_MSK              (0x1 << TXID_AUTO_CT3_OFF)
 #define TXID_AUTO_CTB_OFF              11
 #define TXID_AUTO_CTB_MSK              (0x1 << TXID_AUTO_CTB_OFF)
-#define TX_HARDRST_OFF          2
-#define TX_HARDRST_MSK          (0x1 << TX_HARDRST_OFF)
+#define TX_HARDRST_OFF                 2
+#define TX_HARDRST_MSK                 (0x1 << TX_HARDRST_OFF)
 #define RX_IDAF_DWORD0                 (PORT_BASE + 0xc4)
 #define RX_IDAF_DWORD1                 (PORT_BASE + 0xc8)
 #define RX_IDAF_DWORD2                 (PORT_BASE + 0xcc)
 #define CHL_INT1_MSK                   (PORT_BASE + 0x1c4)
 #define CHL_INT2_MSK                   (PORT_BASE + 0x1c8)
 #define CHL_INT_COAL_EN                        (PORT_BASE + 0x1d0)
-#define DMA_TX_DFX0                    (PORT_BASE + 0x200)
-#define DMA_TX_DFX1                    (PORT_BASE + 0x204)
+#define DMA_TX_DFX0                            (PORT_BASE + 0x200)
+#define DMA_TX_DFX1                            (PORT_BASE + 0x204)
 #define DMA_TX_DFX1_IPTT_OFF           0
 #define DMA_TX_DFX1_IPTT_MSK           (0xffff << DMA_TX_DFX1_IPTT_OFF)
 #define DMA_TX_FIFO_DFX0               (PORT_BASE + 0x240)
-#define PORT_DFX0                      (PORT_BASE + 0x258)
-#define LINK_DFX2                      (PORT_BASE + 0X264)
+#define PORT_DFX0                              (PORT_BASE + 0x258)
+#define LINK_DFX2                                      (PORT_BASE + 0X264)
 #define LINK_DFX2_RCVR_HOLD_STS_OFF    9
 #define LINK_DFX2_RCVR_HOLD_STS_MSK    (0x1 << LINK_DFX2_RCVR_HOLD_STS_OFF)
 #define LINK_DFX2_SEND_HOLD_STS_OFF    10
@@ -2260,15 +2260,18 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
        case STAT_IO_COMPLETE:
                /* internal abort command complete */
                ts->stat = TMF_RESP_FUNC_SUCC;
+               del_timer(&slot->internal_abort_timer);
                goto out;
        case STAT_IO_NO_DEVICE:
                ts->stat = TMF_RESP_FUNC_COMPLETE;
+               del_timer(&slot->internal_abort_timer);
                goto out;
        case STAT_IO_NOT_VALID:
                /* abort single io, controller don't find
                 * the io need to abort
                 */
                ts->stat = TMF_RESP_FUNC_FAILED;
+               del_timer(&slot->internal_abort_timer);
                goto out;
        default:
                break;
@@ -2502,6 +2505,40 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba,
        return 0;
 }
 
+static void hisi_sas_internal_abort_quirk_timeout(unsigned long data)
+{
+       struct hisi_sas_slot *slot = (struct hisi_sas_slot *)data;
+       struct hisi_sas_port *port = slot->port;
+       struct asd_sas_port *asd_sas_port;
+       struct asd_sas_phy *sas_phy;
+
+       if (!port)
+               return;
+
+       asd_sas_port = &port->sas_port;
+
+       /* Kick the hardware - send break command */
+       list_for_each_entry(sas_phy, &asd_sas_port->phy_list, port_phy_el) {
+               struct hisi_sas_phy *phy = sas_phy->lldd_phy;
+               struct hisi_hba *hisi_hba = phy->hisi_hba;
+               int phy_no = sas_phy->id;
+               u32 link_dfx2;
+
+               link_dfx2 = hisi_sas_phy_read32(hisi_hba, phy_no, LINK_DFX2);
+               if ((link_dfx2 == LINK_DFX2_RCVR_HOLD_STS_MSK) ||
+                   (link_dfx2 & LINK_DFX2_SEND_HOLD_STS_MSK)) {
+                       u32 txid_auto;
+
+                       txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no,
+                                                       TXID_AUTO);
+                       txid_auto |= TXID_AUTO_CTB_MSK;
+                       hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
+                                            txid_auto);
+                       return;
+               }
+       }
+}
+
 static int prep_abort_v2_hw(struct hisi_hba *hisi_hba,
                struct hisi_sas_slot *slot,
                int device_id, int abort_flag, int tag_to_abort)
@@ -2510,6 +2547,13 @@ static int prep_abort_v2_hw(struct hisi_hba *hisi_hba,
        struct domain_device *dev = task->dev;
        struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
        struct hisi_sas_port *port = slot->port;
+       struct timer_list *timer = &slot->internal_abort_timer;
+
+       /* setup the quirk timer */
+       setup_timer(timer, hisi_sas_internal_abort_quirk_timeout,
+                   (unsigned long)slot);
+       /* Set the timeout to 10ms less than internal abort timeout */
+       mod_timer(timer, jiffies + msecs_to_jiffies(100));
 
        /* dw0 */
        hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/