diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-15 22:06:26 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-15 22:06:26 -0700 |
commit | 72f02ba66bd83b54054da20eae550123de84da6f (patch) | |
tree | 96a8360400e040aa2e38e7352594dbbc485461db /drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | |
parent | db06f826ec12bf0701ea7fc0a3c0aa00b84417c8 (diff) | |
parent | 51372570ac3c919b036e760f4ca449e81cf8e995 (diff) |
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull SCSI updates from James Bottomley:
"This is mostly updates to the usual drivers: mpt3sas, lpfc, qla2xxx,
hisi_sas, smartpqi, megaraid_sas, arcmsr.
In addition, with the continuing absence of Nic we have target updates
for tcmu and target core (all with reviews and acks).
The biggest observable change is going to be that we're (again) trying
to switch to mulitqueue as the default (a user can still override the
setting on the kernel command line).
Other major core stuff is the removal of the remaining Microchannel
drivers, an update of the internal timers and some reworks of
completion and result handling"
* tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (203 commits)
scsi: core: use blk_mq_run_hw_queues in scsi_kick_queue
scsi: ufs: remove unnecessary query(DM) UPIU trace
scsi: qla2xxx: Fix issue reported by static checker for qla2x00_els_dcmd2_sp_done()
scsi: aacraid: Spelling fix in comment
scsi: mpt3sas: Fix calltrace observed while running IO & reset
scsi: aic94xx: fix an error code in aic94xx_init()
scsi: st: remove redundant pointer STbuffer
scsi: qla2xxx: Update driver version to 10.00.00.08-k
scsi: qla2xxx: Migrate NVME N2N handling into state machine
scsi: qla2xxx: Save frame payload size from ICB
scsi: qla2xxx: Fix stalled relogin
scsi: qla2xxx: Fix race between switch cmd completion and timeout
scsi: qla2xxx: Fix Management Server NPort handle reservation logic
scsi: qla2xxx: Flush mailbox commands on chip reset
scsi: qla2xxx: Fix unintended Logout
scsi: qla2xxx: Fix session state stuck in Get Port DB
scsi: qla2xxx: Fix redundant fc_rport registration
scsi: qla2xxx: Silent erroneous message
scsi: qla2xxx: Prevent sysfs access when chip is down
scsi: qla2xxx: Add longer window for chip reset
...
Diffstat (limited to 'drivers/scsi/hisi_sas/hisi_sas_v3_hw.c')
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 290 |
1 files changed, 180 insertions, 110 deletions
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 9f1e2d03f914..08b503e274b8 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -51,7 +51,6 @@ #define CFG_ABT_SET_IPTT_DONE 0xd8 #define CFG_ABT_SET_IPTT_DONE_OFF 0 #define HGC_IOMB_PROC1_STATUS 0x104 -#define CFG_1US_TIMER_TRSH 0xcc #define CHNL_INT_STATUS 0x148 #define HGC_AXI_FIFO_ERR_INFO 0x154 #define AXI_ERR_INFO_OFF 0 @@ -121,6 +120,8 @@ #define PHY_CFG_ENA_MSK (0x1 << PHY_CFG_ENA_OFF) #define PHY_CFG_DC_OPT_OFF 2 #define PHY_CFG_DC_OPT_MSK (0x1 << PHY_CFG_DC_OPT_OFF) +#define PHY_CFG_PHY_RST_OFF 3 +#define PHY_CFG_PHY_RST_MSK (0x1 << PHY_CFG_PHY_RST_OFF) #define PROG_PHY_LINK_RATE (PORT_BASE + 0x8) #define PHY_CTRL (PORT_BASE + 0x14) #define PHY_CTRL_RESET_OFF 0 @@ -131,6 +132,9 @@ #define SL_CONTROL_NOTIFY_EN_MSK (0x1 << SL_CONTROL_NOTIFY_EN_OFF) #define SL_CTA_OFF 17 #define SL_CTA_MSK (0x1 << SL_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 TX_ID_DWORD0 (PORT_BASE + 0x9c) #define TX_ID_DWORD1 (PORT_BASE + 0xa0) #define TX_ID_DWORD2 (PORT_BASE + 0xa4) @@ -206,6 +210,8 @@ #define AXI_MASTER_CFG_BASE (0x5000) #define AM_CTRL_GLOBAL (0x0) +#define AM_CTRL_SHUTDOWN_REQ_OFF 0 +#define AM_CTRL_SHUTDOWN_REQ_MSK (0x1 << AM_CTRL_SHUTDOWN_REQ_OFF) #define AM_CURR_TRANS_RETURN (0x150) #define AM_CFG_MAX_TRANS (0x5010) @@ -425,7 +431,6 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) (u32)((1ULL << hisi_hba->queue_count) - 1)); hisi_sas_write32(hisi_hba, CFG_MAX_TAG, 0xfff0400); hisi_sas_write32(hisi_hba, HGC_SAS_TXFAIL_RETRY_CTRL, 0x108); - hisi_sas_write32(hisi_hba, CFG_1US_TIMER_TRSH, 0xd); hisi_sas_write32(hisi_hba, INT_COAL_EN, 0x1); hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0x1); hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 0x1); @@ -486,6 +491,7 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, SL_RX_BCAST_CHK_MSK, 0x0); hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_OOB_RESTART_MSK, 0x1); hisi_sas_phy_write32(hisi_hba, i, STP_LINK_TIMER, 0x7f7a120); + hisi_sas_phy_write32(hisi_hba, i, CON_CFG_DRIVER, 0x2a0a01); /* used for 12G negotiate */ hisi_sas_phy_write32(hisi_hba, i, COARSETUNE_TIME, 0x1e); @@ -758,15 +764,25 @@ static void enable_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no) u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG); cfg |= PHY_CFG_ENA_MSK; + cfg &= ~PHY_CFG_PHY_RST_MSK; hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg); } static void disable_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no) { u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG); + u32 state; cfg &= ~PHY_CFG_ENA_MSK; hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg); + + mdelay(50); + + state = hisi_sas_read32(hisi_hba, PHY_STATE); + if (state & BIT(phy_no)) { + cfg |= PHY_CFG_PHY_RST_MSK; + hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg); + } } static void start_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no) @@ -866,23 +882,28 @@ get_free_slot_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq) static void start_delivery_v3_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); } @@ -1170,6 +1191,16 @@ static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba) dev_info(dev, "phyup: phy%d link_rate=%d(sata)\n", phy_no, link_rate); initial_fis = &hisi_hba->initial_fis[phy_no]; fis = &initial_fis->fis; + + /* check ERR bit of Status Register */ + if (fis->status & ATA_ERR) { + dev_warn(dev, "sata int: phy%d FIS status: 0x%x\n", + phy_no, fis->status); + hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET); + res = IRQ_NONE; + goto end; + } + sas_phy->oob_mode = SATA_OOB_MODE; attached_sas_addr[0] = 0x50; attached_sas_addr[7] = phy_no; @@ -1256,9 +1287,13 @@ static irqreturn_t phy_bcast_v3_hw(int phy_no, struct hisi_hba *hisi_hba) struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; struct sas_ha_struct *sas_ha = &hisi_hba->sha; + u32 bcast_status; hisi_sas_phy_write32(hisi_hba, phy_no, SL_RX_BCAST_CHK_MSK, 1); - sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD); + bcast_status = hisi_sas_phy_read32(hisi_hba, phy_no, RX_PRIMS_STATUS); + 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); hisi_sas_phy_write32(hisi_hba, phy_no, SL_RX_BCAST_CHK_MSK, 0); @@ -1327,11 +1362,77 @@ static const struct hisi_sas_hw_error port_axi_error[] = { }, }; -static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) +static void handle_chl_int1_v3_hw(struct hisi_hba *hisi_hba, int phy_no) { - struct hisi_hba *hisi_hba = p; + u32 irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT1); + u32 irq_msk = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT1_MSK); struct device *dev = hisi_hba->dev; + int i; + + irq_value &= ~irq_msk; + if (!irq_value) + return; + + for (i = 0; i < ARRAY_SIZE(port_axi_error); i++) { + const struct hisi_sas_hw_error *error = &port_axi_error[i]; + + if (!(irq_value & error->irq_msk)) + continue; + + dev_err(dev, "%s error (phy%d 0x%x) found!\n", + error->msg, phy_no, irq_value); + queue_work(hisi_hba->wq, &hisi_hba->rst_work); + } + + hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT1, irq_value); +} + +static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no) +{ + u32 irq_msk = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2_MSK); + u32 irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2); + struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct pci_dev *pci_dev = hisi_hba->pci_dev; + struct device *dev = hisi_hba->dev; + + irq_value &= ~irq_msk; + if (!irq_value) + return; + + if (irq_value & BIT(CHL_INT2_SL_IDAF_TOUT_CONF_OFF)) { + dev_warn(dev, "phy%d identify timeout\n", phy_no); + hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET); + } + + if (irq_value & BIT(CHL_INT2_STP_LINK_TIMEOUT_OFF)) { + u32 reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, + STP_LINK_TIMEOUT_STATE); + + dev_warn(dev, "phy%d stp link timeout (0x%x)\n", + phy_no, reg_value); + if (reg_value & BIT(4)) + hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET); + } + + if ((irq_value & BIT(CHL_INT2_RX_INVLD_DW_OFF)) && + (pci_dev->revision == 0x20)) { + u32 reg_value; + int rc; + + rc = hisi_sas_read32_poll_timeout_atomic( + HILINK_ERR_DFX, reg_value, + !((reg_value >> 8) & BIT(phy_no)), + 1000, 10000); + if (rc) + hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET); + } + + hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, irq_value); +} + +static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) +{ + struct hisi_hba *hisi_hba = p; u32 irq_msk; int phy_no = 0; @@ -1341,84 +1442,12 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) while (irq_msk) { u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0); - u32 irq_value1 = hisi_sas_phy_read32(hisi_hba, phy_no, - CHL_INT1); - u32 irq_value2 = hisi_sas_phy_read32(hisi_hba, phy_no, - CHL_INT2); - u32 irq_msk1 = hisi_sas_phy_read32(hisi_hba, phy_no, - CHL_INT1_MSK); - u32 irq_msk2 = hisi_sas_phy_read32(hisi_hba, phy_no, - CHL_INT2_MSK); - - irq_value1 &= ~irq_msk1; - irq_value2 &= ~irq_msk2; - - if ((irq_msk & (4 << (phy_no * 4))) && - irq_value1) { - int i; - - for (i = 0; i < ARRAY_SIZE(port_axi_error); i++) { - const struct hisi_sas_hw_error *error = - &port_axi_error[i]; - - if (!(irq_value1 & error->irq_msk)) - continue; - - dev_err(dev, "%s error (phy%d 0x%x) found!\n", - error->msg, phy_no, irq_value1); - queue_work(hisi_hba->wq, &hisi_hba->rst_work); - } - - hisi_sas_phy_write32(hisi_hba, phy_no, - CHL_INT1, irq_value1); - } - if (irq_msk & (8 << (phy_no * 4)) && irq_value2) { - struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; + if (irq_msk & (4 << (phy_no * 4))) + handle_chl_int1_v3_hw(hisi_hba, phy_no); - if (irq_value2 & BIT(CHL_INT2_SL_IDAF_TOUT_CONF_OFF)) { - dev_warn(dev, "phy%d identify timeout\n", - phy_no); - hisi_sas_notify_phy_event(phy, - HISI_PHYE_LINK_RESET); - - } - - if (irq_value2 & BIT(CHL_INT2_STP_LINK_TIMEOUT_OFF)) { - u32 reg_value = hisi_sas_phy_read32(hisi_hba, - phy_no, STP_LINK_TIMEOUT_STATE); - - dev_warn(dev, "phy%d stp link timeout (0x%x)\n", - phy_no, reg_value); - if (reg_value & BIT(4)) - hisi_sas_notify_phy_event(phy, - HISI_PHYE_LINK_RESET); - } - - hisi_sas_phy_write32(hisi_hba, phy_no, - CHL_INT2, irq_value2); - - if ((irq_value2 & BIT(CHL_INT2_RX_INVLD_DW_OFF)) && - (pci_dev->revision == 0x20)) { - u32 reg_value; - int rc; - - rc = hisi_sas_read32_poll_timeout_atomic( - HILINK_ERR_DFX, reg_value, - !((reg_value >> 8) & BIT(phy_no)), - 1000, 10000); - if (rc) { - disable_phy_v3_hw(hisi_hba, phy_no); - hisi_sas_phy_write32(hisi_hba, phy_no, - CHL_INT2, - BIT(CHL_INT2_RX_INVLD_DW_OFF)); - hisi_sas_phy_read32(hisi_hba, phy_no, - ERR_CNT_INVLD_DW); - mdelay(1); - enable_phy_v3_hw(hisi_hba, phy_no); - } - } - } + 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, @@ -1964,11 +1993,11 @@ static void phy_get_events_v3_hw(struct hisi_hba *hisi_hba, int phy_no) } -static int soft_reset_v3_hw(struct hisi_hba *hisi_hba) +static int disable_host_v3_hw(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; + u32 status, reg_val; int rc; - u32 status; interrupt_disable_v3_hw(hisi_hba); hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0x0); @@ -1978,14 +2007,32 @@ static int soft_reset_v3_hw(struct hisi_hba *hisi_hba) mdelay(10); - hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE + AM_CTRL_GLOBAL, 0x1); + 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); /* wait until bus idle */ rc = hisi_sas_read32_poll_timeout(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); + dev_err(dev, "axi bus is not idle, rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int soft_reset_v3_hw(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + int rc; + + rc = disable_host_v3_hw(hisi_hba); + if (rc) { + dev_err(dev, "soft reset: disable host failed rc=%d\n", rc); return rc; } @@ -2433,6 +2480,41 @@ static pci_ers_result_t hisi_sas_slot_reset_v3_hw(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; } +static void hisi_sas_reset_prepare_v3_hw(struct pci_dev *pdev) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct hisi_hba *hisi_hba = sha->lldd_ha; + struct device *dev = hisi_hba->dev; + int rc; + + dev_info(dev, "FLR prepare\n"); + set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags); + hisi_sas_controller_reset_prepare(hisi_hba); + + rc = disable_host_v3_hw(hisi_hba); + if (rc) + dev_err(dev, "FLR: disable host failed rc=%d\n", rc); +} + +static void hisi_sas_reset_done_v3_hw(struct pci_dev *pdev) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct hisi_hba *hisi_hba = sha->lldd_ha; + struct device *dev = hisi_hba->dev; + int rc; + + hisi_sas_init_mem(hisi_hba); + + rc = hw_init_v3_hw(hisi_hba); + if (rc) { + dev_err(dev, "FLR: hw init failed rc=%d\n", rc); + return; + } + + hisi_sas_controller_reset_done(hisi_hba); + dev_info(dev, "FLR done\n"); +} + enum { /* instances of the controller */ hip08, @@ -2444,38 +2526,24 @@ static int hisi_sas_v3_suspend(struct pci_dev *pdev, pm_message_t state) struct hisi_hba *hisi_hba = sha->lldd_ha; struct device *dev = hisi_hba->dev; struct Scsi_Host *shost = hisi_hba->shost; - u32 device_state, status; + u32 device_state; int rc; - u32 reg_val; if (!pdev->pm_cap) { dev_err(dev, "PCI PM not supported\n"); return -ENODEV; } - set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags); + if (test_and_set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)) + return -1; + scsi_block_requests(shost); set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); flush_workqueue(hisi_hba->wq); - /* disable DQ/PHY/bus */ - interrupt_disable_v3_hw(hisi_hba); - hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0x0); - hisi_sas_kill_tasklets(hisi_hba); - - hisi_sas_stop_phys(hisi_hba); - reg_val = hisi_sas_read32(hisi_hba, AXI_MASTER_CFG_BASE + - AM_CTRL_GLOBAL); - reg_val |= 0x1; - hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE + - AM_CTRL_GLOBAL, reg_val); - - /* wait until bus idle */ - rc = hisi_sas_read32_poll_timeout(AXI_MASTER_CFG_BASE + - AM_CURR_TRANS_RETURN, status, - status == 0x3, 10, 100); + rc = disable_host_v3_hw(hisi_hba); if (rc) { - dev_err(dev, "axi bus is not idle, rc = %d\n", rc); + dev_err(dev, "PM suspend: disable host failed rc=%d\n", rc); clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); clear_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags); scsi_unblock_requests(shost); @@ -2538,6 +2606,8 @@ static const struct pci_error_handlers hisi_sas_err_handler = { .error_detected = hisi_sas_error_detected_v3_hw, .mmio_enabled = hisi_sas_mmio_enabled_v3_hw, .slot_reset = hisi_sas_slot_reset_v3_hw, + .reset_prepare = hisi_sas_reset_prepare_v3_hw, + .reset_done = hisi_sas_reset_done_v3_hw, }; static struct pci_driver sas_v3_pci_driver = { |