aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
diff options
context:
space:
mode:
authorXiang Chen <chenxiang66@hisilicon.com>2017-03-23 01:25:18 +0800
committerMartin K. Petersen <martin.petersen@oracle.com>2017-03-23 11:12:01 -0400
commit06ec0fb97c028102c95d52422fd6dc5ac701039f (patch)
treec28bb8ca5a68622bb4a8e78348f2ec78eaa10c2c /drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
parentscsi: hisi_sas: add to_hisi_sas_port() (diff)
downloadlinux-dev-06ec0fb97c028102c95d52422fd6dc5ac701039f.tar.xz
linux-dev-06ec0fb97c028102c95d52422fd6dc5ac701039f.zip
scsi: hisi_sas: add controller reset
There are some scenarios that we need to warm-reset to reset registers of SAS controller. During reset we disable interrupts/DQs/PHYs, and after reset we re-init the hardware and rescan the topology to see if anything changed. Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com> Signed-off-by: Xiaofei Tan <tanxiaofei@huawei.com> Signed-off-by: John Garry <john.garry@huawei.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/hisi_sas/hisi_sas_v2_hw.c')
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v2_hw.c94
1 files changed, 94 insertions, 0 deletions
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
index 1590e2f6ac66..53651cfd9da1 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -257,6 +257,10 @@
#define AM_CFG_MAX_TRANS (0x5010)
#define AM_CFG_SINGLE_PORT_MAX_TRANS (0x5014)
+#define AXI_MASTER_CFG_BASE (0x5000)
+#define AM_CTRL_GLOBAL (0x0)
+#define AM_CURR_TRANS_RETURN (0x150)
+
/* HW dma structures */
/* Delivery queue header */
/* dw0 */
@@ -1079,6 +1083,14 @@ static void stop_phy_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
disable_phy_v2_hw(hisi_hba, phy_no);
}
+static void stop_phys_v2_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; i++)
+ stop_phy_v2_hw(hisi_hba, i);
+}
+
static void phy_hard_reset_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
{
struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
@@ -2857,6 +2869,87 @@ static int hisi_sas_v2_init(struct hisi_hba *hisi_hba)
return 0;
}
+static void interrupt_disable_v2_hw(struct hisi_hba *hisi_hba)
+{
+ struct platform_device *pdev = hisi_hba->pdev;
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ hisi_sas_write32(hisi_hba, OQ0_INT_SRC_MSK + 0x4 * i, 0x1);
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK2, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, 0xffffffff);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, 0xffffffff);
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xffffffff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0xffffffff);
+ }
+
+ for (i = 0; i < 128; i++)
+ synchronize_irq(platform_get_irq(pdev, i));
+}
+
+static int soft_reset_v2_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = &hisi_hba->pdev->dev;
+ u32 old_state, state;
+ int rc, cnt;
+ int phy_no;
+
+ old_state = hisi_sas_read32(hisi_hba, PHY_STATE);
+
+ interrupt_disable_v2_hw(hisi_hba);
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0x0);
+
+ stop_phys_v2_hw(hisi_hba);
+
+ mdelay(10);
+
+ hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE + AM_CTRL_GLOBAL, 0x1);
+
+ /* wait until bus idle */
+ cnt = 0;
+ while (1) {
+ u32 status = hisi_sas_read32_relaxed(hisi_hba,
+ AXI_MASTER_CFG_BASE + AM_CURR_TRANS_RETURN);
+
+ if (status == 0x3)
+ break;
+
+ udelay(10);
+ if (cnt++ > 10) {
+ dev_info(dev, "wait axi bus state to idle timeout!\n");
+ return -1;
+ }
+ }
+
+ hisi_sas_init_mem(hisi_hba);
+
+ rc = hw_init_v2_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ /* Re-enable the PHYs */
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ if (sas_phy->enabled)
+ start_phy_v2_hw(hisi_hba, phy_no);
+ }
+
+ /* Wait for the PHYs to come up and read the PHY state */
+ msleep(1000);
+
+ state = hisi_sas_read32(hisi_hba, PHY_STATE);
+
+ hisi_sas_rescan_topology(hisi_hba, old_state, state);
+
+ return 0;
+}
+
static const struct hisi_sas_hw hisi_sas_v2_hw = {
.hw_init = hisi_sas_v2_init,
.setup_itct = setup_itct_v2_hw,
@@ -2879,6 +2972,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = {
.phy_get_max_linkrate = phy_get_max_linkrate_v2_hw,
.max_command_entries = HISI_SAS_COMMAND_ENTRIES_V2_HW,
.complete_hdr_size = sizeof(struct hisi_sas_complete_v2_hdr),
+ .soft_reset = soft_reset_v2_hw,
};
static int hisi_sas_v2_probe(struct platform_device *pdev)