diff options
author | Xiang Chen <chenxiang66@hisilicon.com> | 2017-08-11 00:09:40 +0800 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2017-08-24 22:28:47 -0400 |
commit | a25d0d3df2a17a245ffa09077d0c4e5fc40088cf (patch) | |
tree | 876dc01d45b5c750d31bca2e5aa5819f4b445227 /drivers/scsi/hisi_sas | |
parent | d499669facfdc9e8a7c479e86fd11f3d49e065ee (diff) | |
download | linux-a25d0d3df2a17a245ffa09077d0c4e5fc40088cf.tar.bz2 |
scsi: hisi_sas: add reset handler for v3 hw
Use ACPI "_RST" method to reset the controller, since FLR is not
supported.
Function hisi_sas_stop_phys() is introduced to remove some code
duplication.
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.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')
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas.h | 2 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_main.c | 9 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 24 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 158 |
4 files changed, 157 insertions, 36 deletions
diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 23a22dcba154..4c393cd8ee81 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -15,6 +15,7 @@ #include <linux/acpi.h> #include <linux/clk.h> #include <linux/dmapool.h> +#include <linux/iopoll.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_address.h> @@ -402,6 +403,7 @@ struct hisi_sas_slot_buf_table { extern struct scsi_transport_template *hisi_sas_stt; extern struct scsi_host_template *hisi_sas_sht; +extern void hisi_sas_stop_phys(struct hisi_hba *hisi_hba); extern void hisi_sas_init_add(struct hisi_hba *hisi_hba); extern int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost); extern void hisi_sas_free(struct hisi_hba *hisi_hba); diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 7e642c8097c7..4112afd357bd 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -127,6 +127,15 @@ struct hisi_sas_port *to_hisi_sas_port(struct asd_sas_port *sas_port) } EXPORT_SYMBOL_GPL(to_hisi_sas_port); +void hisi_sas_stop_phys(struct hisi_hba *hisi_hba) +{ + int phy_no; + + for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) + hisi_hba->hw->phy_disable(hisi_hba, phy_no); +} +EXPORT_SYMBOL_GPL(hisi_sas_stop_phys); + static void hisi_sas_slot_index_clear(struct hisi_hba *hisi_hba, int slot_idx) { void *bitmap = hisi_hba->slot_index_tags; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 0e3634ec1225..779af979b6db 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1486,25 +1486,12 @@ static void start_phy_v2_hw(struct hisi_hba *hisi_hba, int phy_no) enable_phy_v2_hw(hisi_hba, phy_no); } -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]; u32 txid_auto; - stop_phy_v2_hw(hisi_hba, phy_no); + disable_phy_v2_hw(hisi_hba, phy_no); if (phy->identify.device_type == SAS_END_DEVICE) { txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO); hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO, @@ -1533,7 +1520,7 @@ static void phy_get_events_v2_hw(struct hisi_hba *hisi_hba, int phy_no) sphy->running_disparity_error_count += err6_reg_val & 0xFF; } -static void start_phys_v2_hw(struct hisi_hba *hisi_hba) +static void phys_init_v2_hw(struct hisi_hba *hisi_hba) { int i; @@ -1548,11 +1535,6 @@ static void start_phys_v2_hw(struct hisi_hba *hisi_hba) } } -static void phys_init_v2_hw(struct hisi_hba *hisi_hba) -{ - start_phys_v2_hw(hisi_hba); -} - static void sl_notify_v2_hw(struct hisi_hba *hisi_hba, int phy_no) { u32 sl_control; @@ -3429,7 +3411,7 @@ static int soft_reset_v2_hw(struct hisi_hba *hisi_hba) interrupt_disable_v2_hw(hisi_hba); hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0x0); - stop_phys_v2_hw(hisi_hba); + hisi_sas_stop_phys(hisi_hba); mdelay(10); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 992ccc2d4956..a527c3fbb100 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -168,6 +168,31 @@ #define PHYCTRL_PHY_ENA_MSK (PORT_BASE + 0x2bc) #define SL_RX_BCAST_CHK_MSK (PORT_BASE + 0x2c0) #define PHYCTRL_OOB_RESTART_MSK (PORT_BASE + 0x2c4) +#define DMA_TX_STATUS (PORT_BASE + 0x2d0) +#define DMA_TX_STATUS_BUSY_OFF 0 +#define DMA_TX_STATUS_BUSY_MSK (0x1 << DMA_TX_STATUS_BUSY_OFF) +#define DMA_RX_STATUS (PORT_BASE + 0x2e8) +#define DMA_RX_STATUS_BUSY_OFF 0 +#define DMA_RX_STATUS_BUSY_MSK (0x1 << DMA_RX_STATUS_BUSY_OFF) + +#define MAX_ITCT_HW 4096 /* max the hw can support */ +#define DEFAULT_ITCT_HW 2048 /* reset value, not reprogrammed */ +#if (HISI_SAS_MAX_DEVICES > DEFAULT_ITCT_HW) +#error Max ITCT exceeded +#endif + +#define AXI_MASTER_CFG_BASE (0x5000) +#define AM_CTRL_GLOBAL (0x0) +#define AM_CURR_TRANS_RETURN (0x150) + +#define AM_CFG_MAX_TRANS (0x5010) +#define AM_CFG_SINGLE_PORT_MAX_TRANS (0x5014) +#define AXI_CFG (0x5100) +#define AM_ROB_ECC_ERR_ADDR (0x510c) +#define AM_ROB_ECC_ONEBIT_ERR_ADDR_OFF 0 +#define AM_ROB_ECC_ONEBIT_ERR_ADDR_MSK (0xff << AM_ROB_ECC_ONEBIT_ERR_ADDR_OFF) +#define AM_ROB_ECC_MULBIT_ERR_ADDR_OFF 8 +#define AM_ROB_ECC_MULBIT_ERR_ADDR_MSK (0xff << AM_ROB_ECC_MULBIT_ERR_ADDR_OFF) /* HW dma structures */ /* Delivery queue header */ @@ -611,8 +636,52 @@ static void dereg_device_v3_hw(struct hisi_hba *hisi_hba, 1 << CFG_ABT_SET_IPTT_DONE_OFF); } +static int reset_hw_v3_hw(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + int ret; + u32 val; + + hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0); + + /* Disable all of the PHYs */ + hisi_sas_stop_phys(hisi_hba); + udelay(50); + + /* Ensure axi bus idle */ + ret = readl_poll_timeout(hisi_hba->regs + AXI_CFG, val, !val, + 20000, 1000000); + if (ret) { + dev_err(dev, "axi bus is not idle, ret = %d!\n", ret); + return -EIO; + } + + if (ACPI_HANDLE(dev)) { + acpi_status s; + + s = acpi_evaluate_object(ACPI_HANDLE(dev), "_RST", NULL, NULL); + if (ACPI_FAILURE(s)) { + dev_err(dev, "Reset failed\n"); + return -EIO; + } + } else + dev_err(dev, "no reset method!\n"); + + return 0; +} + static int hw_init_v3_hw(struct hisi_hba *hisi_hba) { + struct device *dev = hisi_hba->dev; + int rc; + + rc = reset_hw_v3_hw(hisi_hba); + if (rc) { + dev_err(dev, "hisi_sas_reset_hw failed, rc=%d", rc); + return rc; + } + + msleep(100); init_reg_v3_hw(hisi_hba); return 0; @@ -641,25 +710,12 @@ static void start_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no) enable_phy_v3_hw(hisi_hba, phy_no); } -static void stop_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no) -{ - disable_phy_v3_hw(hisi_hba, phy_no); -} - -static void start_phys_v3_hw(struct hisi_hba *hisi_hba) -{ - int i; - - for (i = 0; i < hisi_hba->n_phy; i++) - start_phy_v3_hw(hisi_hba, i); -} - static void phy_hard_reset_v3_hw(struct hisi_hba *hisi_hba, int phy_no) { struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; u32 txid_auto; - stop_phy_v3_hw(hisi_hba, phy_no); + disable_phy_v3_hw(hisi_hba, phy_no); if (phy->identify.device_type == SAS_END_DEVICE) { txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO); hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO, @@ -676,7 +732,17 @@ enum sas_linkrate phy_get_max_linkrate_v3_hw(void) static void phys_init_v3_hw(struct hisi_hba *hisi_hba) { - start_phys_v3_hw(hisi_hba); + int i; + + for (i = 0; i < hisi_hba->n_phy; i++) { + struct hisi_sas_phy *phy = &hisi_hba->phy[i]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + + if (!sas_phy->phy->enabled) + continue; + + start_phy_v3_hw(hisi_hba, i); + } } static void sl_notify_v3_hw(struct hisi_hba *hisi_hba, int phy_no) @@ -1621,6 +1687,66 @@ static int hisi_sas_v3_init(struct hisi_hba *hisi_hba) return 0; } +static void interrupt_disable_v3_hw(struct hisi_hba *hisi_hba) +{ + struct pci_dev *pdev = hisi_hba->pci_dev; + int i; + + synchronize_irq(pci_irq_vector(pdev, 1)); + synchronize_irq(pci_irq_vector(pdev, 2)); + synchronize_irq(pci_irq_vector(pdev, 11)); + for (i = 0; i < hisi_hba->queue_count; i++) { + hisi_sas_write32(hisi_hba, OQ0_INT_SRC_MSK + 0x4 * i, 0x1); + synchronize_irq(pci_irq_vector(pdev, i + 16)); + } + + 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); + hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_NOT_RDY_MSK, 0x1); + hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_PHY_ENA_MSK, 0x1); + hisi_sas_phy_write32(hisi_hba, i, SL_RX_BCAST_CHK_MSK, 0x1); + } +} + +static u32 get_phys_state_v3_hw(struct hisi_hba *hisi_hba) +{ + return hisi_sas_read32(hisi_hba, PHY_STATE); +} + +static int soft_reset_v3_hw(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + int rc; + u32 status; + + interrupt_disable_v3_hw(hisi_hba); + hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0x0); + + hisi_sas_stop_phys(hisi_hba); + + mdelay(10); + + hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE + AM_CTRL_GLOBAL, 0x1); + + /* wait until bus idle */ + rc = readl_poll_timeout(hisi_hba->regs + AXI_MASTER_CFG_BASE + + AM_CURR_TRANS_RETURN, status, status == 0x3, 10, 100); + if (rc) { + dev_err(dev, "axi bus is not idle, rc = %d\n", rc); + return rc; + } + + hisi_sas_init_mem(hisi_hba); + + return hw_init_v3_hw(hisi_hba); +} + static const struct hisi_sas_hw hisi_sas_v3_hw = { .hw_init = hisi_sas_v3_init, .setup_itct = setup_itct_v3_hw, @@ -1642,6 +1768,8 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = { .phy_hard_reset = phy_hard_reset_v3_hw, .phy_get_max_linkrate = phy_get_max_linkrate_v3_hw, .dereg_device = dereg_device_v3_hw, + .soft_reset = soft_reset_v3_hw, + .get_phys_state = get_phys_state_v3_hw, }; static struct Scsi_Host * |