diff options
Diffstat (limited to 'drivers/scsi/hisi_sas')
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas.h | 46 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_main.c | 267 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 6 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 194 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 332 |
5 files changed, 699 insertions, 146 deletions
diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 83357b0367d8..e7fd2877c19c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -99,12 +99,43 @@ struct hisi_sas_hw_error { const struct hisi_sas_hw_error *sub; }; +struct hisi_sas_rst { + struct hisi_hba *hisi_hba; + struct completion *completion; + struct work_struct work; + bool done; +}; + +#define HISI_SAS_RST_WORK_INIT(r, c) \ + { .hisi_hba = hisi_hba, \ + .completion = &c, \ + .work = __WORK_INITIALIZER(r.work, \ + hisi_sas_sync_rst_work_handler), \ + .done = false, \ + } + +#define HISI_SAS_DECLARE_RST_WORK_ON_STACK(r) \ + DECLARE_COMPLETION_ONSTACK(c); \ + DECLARE_WORK(w, hisi_sas_sync_rst_work_handler); \ + struct hisi_sas_rst r = HISI_SAS_RST_WORK_INIT(r, c) + +enum hisi_sas_bit_err_type { + HISI_SAS_ERR_SINGLE_BIT_ECC = 0x0, + HISI_SAS_ERR_MULTI_BIT_ECC = 0x1, +}; + +enum hisi_sas_phy_event { + HISI_PHYE_PHY_UP = 0U, + HISI_PHYE_LINK_RESET, + HISI_PHYES_NUM, +}; + struct hisi_sas_phy { + struct work_struct works[HISI_PHYES_NUM]; struct hisi_hba *hisi_hba; struct hisi_sas_port *port; struct asd_sas_phy sas_phy; struct sas_identify identify; - struct work_struct phyup_ws; u64 port_id; /* from hw */ u64 dev_sas_addr; u64 frame_rcvd_size; @@ -205,13 +236,16 @@ struct hisi_sas_hw { void (*phy_set_linkrate)(struct hisi_hba *hisi_hba, int phy_no, struct sas_phy_linkrates *linkrates); enum sas_linkrate (*phy_get_max_linkrate)(void); - void (*free_device)(struct hisi_hba *hisi_hba, + void (*clear_itct)(struct hisi_hba *hisi_hba, struct hisi_sas_device *dev); + void (*free_device)(struct hisi_sas_device *sas_dev); int (*get_wideport_bitmap)(struct hisi_hba *hisi_hba, int port_id); void (*dereg_device)(struct hisi_hba *hisi_hba, struct domain_device *device); int (*soft_reset)(struct hisi_hba *hisi_hba); u32 (*get_phys_state)(struct hisi_hba *hisi_hba); + int (*write_gpio)(struct hisi_hba *hisi_hba, u8 reg_type, + u8 reg_index, u8 reg_count, u8 *write_data); int max_command_entries; int complete_hdr_size; }; @@ -225,6 +259,7 @@ struct hisi_hba { struct device *dev; void __iomem *regs; + void __iomem *sgpio_regs; struct regmap *ctrl; u32 ctrl_reset_reg; u32 ctrl_reset_sts_reg; @@ -409,7 +444,8 @@ 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); -extern u8 hisi_sas_get_ata_protocol(u8 cmd, int direction); +extern u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, + int direction); extern struct hisi_sas_port *to_hisi_sas_port(struct asd_sas_port *sas_port); extern void hisi_sas_sata_done(struct sas_task *task, struct hisi_sas_slot *slot); @@ -425,5 +461,9 @@ extern void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot); extern void hisi_sas_init_mem(struct hisi_hba *hisi_hba); extern void hisi_sas_rst_work_handler(struct work_struct *work); +extern void hisi_sas_sync_rst_work_handler(struct work_struct *work); extern void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba); +extern bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, + enum hisi_sas_phy_event event); +extern void hisi_sas_release_tasks(struct hisi_hba *hisi_hba); #endif diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 5f503cb09508..2d4dbed03ee3 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -22,10 +22,12 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, struct domain_device *device, int abort_flag, int tag); static int hisi_sas_softreset_ata_disk(struct domain_device *device); +static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, + void *funcdata); -u8 hisi_sas_get_ata_protocol(u8 cmd, int direction) +u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction) { - switch (cmd) { + switch (fis->command) { case ATA_CMD_FPDMA_WRITE: case ATA_CMD_FPDMA_READ: case ATA_CMD_FPDMA_RECV: @@ -77,10 +79,26 @@ u8 hisi_sas_get_ata_protocol(u8 cmd, int direction) case ATA_CMD_ZAC_MGMT_OUT: return HISI_SAS_SATA_PROTOCOL_NONDATA; default: + { + if (fis->command == ATA_CMD_SET_MAX) { + switch (fis->features) { + case ATA_SET_MAX_PASSWD: + case ATA_SET_MAX_LOCK: + return HISI_SAS_SATA_PROTOCOL_PIO; + + case ATA_SET_MAX_PASSWD_DMA: + case ATA_SET_MAX_UNLOCK_DMA: + return HISI_SAS_SATA_PROTOCOL_DMA; + + default: + return HISI_SAS_SATA_PROTOCOL_NONDATA; + } + } if (direction == DMA_NONE) return HISI_SAS_SATA_PROTOCOL_NONDATA; return HISI_SAS_SATA_PROTOCOL_PIO; } + } } EXPORT_SYMBOL_GPL(hisi_sas_get_ata_protocol); @@ -192,7 +210,8 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, if (!sas_protocol_ata(task->task_proto)) if (slot->n_elem) - dma_unmap_sg(dev, task->scatter, slot->n_elem, + dma_unmap_sg(dev, task->scatter, + task->num_scatter, task->data_dir); if (sas_dev) @@ -431,7 +450,8 @@ err_out: dev_err(dev, "task prep: failed[%d]!\n", rc); if (!sas_protocol_ata(task->task_proto)) if (n_elem) - dma_unmap_sg(dev, task->scatter, n_elem, + dma_unmap_sg(dev, task->scatter, + task->num_scatter, task->data_dir); prep_out: return rc; @@ -578,6 +598,9 @@ static int hisi_sas_dev_found(struct domain_device *device) } } + dev_info(dev, "dev[%d:%x] found\n", + sas_dev->device_id, sas_dev->dev_type); + return 0; } @@ -617,7 +640,7 @@ static int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time) static void hisi_sas_phyup_work(struct work_struct *work) { struct hisi_sas_phy *phy = - container_of(work, struct hisi_sas_phy, phyup_ws); + container_of(work, typeof(*phy), works[HISI_PHYE_PHY_UP]); struct hisi_hba *hisi_hba = phy->hisi_hba; struct asd_sas_phy *sas_phy = &phy->sas_phy; int phy_no = sas_phy->id; @@ -626,10 +649,37 @@ static void hisi_sas_phyup_work(struct work_struct *work) hisi_sas_bytes_dmaed(hisi_hba, phy_no); } +static void hisi_sas_linkreset_work(struct work_struct *work) +{ + struct hisi_sas_phy *phy = + container_of(work, typeof(*phy), works[HISI_PHYE_LINK_RESET]); + struct asd_sas_phy *sas_phy = &phy->sas_phy; + + hisi_sas_control_phy(sas_phy, PHY_FUNC_LINK_RESET, NULL); +} + +static const work_func_t hisi_sas_phye_fns[HISI_PHYES_NUM] = { + [HISI_PHYE_PHY_UP] = hisi_sas_phyup_work, + [HISI_PHYE_LINK_RESET] = hisi_sas_linkreset_work, +}; + +bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, + enum hisi_sas_phy_event event) +{ + struct hisi_hba *hisi_hba = phy->hisi_hba; + + if (WARN_ON(event >= HISI_PHYES_NUM)) + return false; + + return queue_work(hisi_hba->wq, &phy->works[event]); +} +EXPORT_SYMBOL_GPL(hisi_sas_notify_phy_event); + static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) { struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; + int i; phy->hisi_hba = hisi_hba; phy->port = NULL; @@ -647,7 +697,8 @@ static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) sas_phy->ha = (struct sas_ha_struct *)hisi_hba->shost->hostdata; sas_phy->lldd_phy = phy; - INIT_WORK(&phy->phyup_ws, hisi_sas_phyup_work); + for (i = 0; i < HISI_PHYES_NUM; i++) + INIT_WORK(&phy->works[i], hisi_sas_phye_fns[i]); } static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy) @@ -702,7 +753,7 @@ static void hisi_sas_release_task(struct hisi_hba *hisi_hba, hisi_sas_do_release_task(hisi_hba, slot->task, slot); } -static void hisi_sas_release_tasks(struct hisi_hba *hisi_hba) +void hisi_sas_release_tasks(struct hisi_hba *hisi_hba) { struct hisi_sas_device *sas_dev; struct domain_device *device; @@ -719,6 +770,7 @@ static void hisi_sas_release_tasks(struct hisi_hba *hisi_hba) hisi_sas_release_task(hisi_hba, device); } } +EXPORT_SYMBOL_GPL(hisi_sas_release_tasks); static void hisi_sas_dereg_device(struct hisi_hba *hisi_hba, struct domain_device *device) @@ -733,17 +785,21 @@ static void hisi_sas_dev_gone(struct domain_device *device) struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct device *dev = hisi_hba->dev; - dev_info(dev, "found dev[%d:%x] is gone\n", + dev_info(dev, "dev[%d:%x] is gone\n", sas_dev->device_id, sas_dev->dev_type); - hisi_sas_internal_task_abort(hisi_hba, device, + if (!test_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)) { + hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); - hisi_sas_dereg_device(hisi_hba, device); + hisi_sas_dereg_device(hisi_hba, device); - hisi_hba->hw->free_device(hisi_hba, sas_dev); - device->lldd_dev = NULL; - memset(sas_dev, 0, sizeof(*sas_dev)); + hisi_hba->hw->clear_itct(hisi_hba, sas_dev); + device->lldd_dev = NULL; + } + + if (hisi_hba->hw->free_device) + hisi_hba->hw->free_device(sas_dev); sas_dev->dev_type = SAS_PHY_UNUSED; } @@ -859,12 +915,13 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { struct hisi_sas_slot *slot = task->lldd_task; - dev_err(dev, "abort tmf: TMF task timeout\n"); + dev_err(dev, "abort tmf: TMF task timeout and not done\n"); if (slot) slot->task = NULL; goto ex_err; - } + } else + dev_err(dev, "abort tmf: TMF task timeout\n"); } if (task->task_status.resp == SAS_TASK_COMPLETE && @@ -985,27 +1042,42 @@ static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device, sizeof(ssp_task), tmf); } -static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba, - struct asd_sas_port *sas_port, enum sas_linkrate linkrate) +static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba) { - struct hisi_sas_device *sas_dev; - struct domain_device *device; + u32 state = hisi_hba->hw->get_phys_state(hisi_hba); int i; for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { - sas_dev = &hisi_hba->devices[i]; - device = sas_dev->sas_device; + struct hisi_sas_device *sas_dev = &hisi_hba->devices[i]; + struct domain_device *device = sas_dev->sas_device; + struct asd_sas_port *sas_port; + struct hisi_sas_port *port; + struct hisi_sas_phy *phy = NULL; + struct asd_sas_phy *sas_phy; + if ((sas_dev->dev_type == SAS_PHY_UNUSED) - || !device || (device->port != sas_port)) + || !device || !device->port) continue; - hisi_hba->hw->free_device(hisi_hba, sas_dev); + sas_port = device->port; + port = to_hisi_sas_port(sas_port); + + list_for_each_entry(sas_phy, &sas_port->phy_list, port_phy_el) + if (state & BIT(sas_phy->id)) { + phy = sas_phy->lldd_phy; + break; + } + + if (phy) { + port->id = phy->port_id; - /* Update linkrate of directly attached device. */ - if (!device->parent) - device->linkrate = linkrate; + /* Update linkrate of directly attached device. */ + if (!device->parent) + device->linkrate = phy->sas_phy.linkrate; - hisi_hba->hw->setup_itct(hisi_hba, sas_dev); + hisi_hba->hw->setup_itct(hisi_hba, sas_dev); + } else + port->id = 0xff; } } @@ -1020,21 +1092,17 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state, struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; struct asd_sas_port *sas_port = sas_phy->port; - struct hisi_sas_port *port = to_hisi_sas_port(sas_port); bool do_port_check = !!(_sas_port != sas_port); if (!sas_phy->phy->enabled) continue; /* Report PHY state change to libsas */ - if (state & (1 << phy_no)) { - if (do_port_check && sas_port) { + if (state & BIT(phy_no)) { + if (do_port_check && sas_port && sas_port->port_dev) { struct domain_device *dev = sas_port->port_dev; _sas_port = sas_port; - port->id = phy->port_id; - hisi_sas_refresh_port_id(hisi_hba, - sas_port, sas_phy->linkrate); if (DEV_IS_EXPANDER(dev->dev_type)) sas_ha->notify_port_event(sas_phy, @@ -1045,8 +1113,6 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state, hisi_sas_phy_down(hisi_hba, phy_no, 0); } - - drain_workqueue(hisi_hba->shost->work_q); } static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) @@ -1063,7 +1129,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) if (test_and_set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)) return -1; - dev_dbg(dev, "controller resetting...\n"); + dev_info(dev, "controller resetting...\n"); old_state = hisi_hba->hw->get_phys_state(hisi_hba); scsi_block_requests(shost); @@ -1072,6 +1138,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) if (rc) { dev_warn(dev, "controller reset failed (%d)\n", rc); clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); + scsi_unblock_requests(shost); goto out; } spin_lock_irqsave(&hisi_hba->lock, flags); @@ -1083,15 +1150,14 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) /* Init and wait for PHYs to come up and all libsas event finished. */ hisi_hba->hw->phys_init(hisi_hba); msleep(1000); - drain_workqueue(hisi_hba->wq); - drain_workqueue(shost->work_q); + hisi_sas_refresh_port_id(hisi_hba); + scsi_unblock_requests(shost); state = hisi_hba->hw->get_phys_state(hisi_hba); hisi_sas_rescan_topology(hisi_hba, old_state, state); - dev_dbg(dev, "controller reset complete\n"); + dev_info(dev, "controller reset complete\n"); out: - scsi_unblock_requests(shost); clear_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags); return rc; @@ -1134,6 +1200,11 @@ static int hisi_sas_abort_task(struct sas_task *task) rc2 = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_CMD, tag); + if (rc2 < 0) { + dev_err(dev, "abort task: internal abort (%d)\n", rc2); + return TMF_RESP_FUNC_FAILED; + } + /* * If the TMF finds that the IO is not in the device and also * the internal abort does not succeed, then it is safe to @@ -1151,8 +1222,12 @@ static int hisi_sas_abort_task(struct sas_task *task) } else if (task->task_proto & SAS_PROTOCOL_SATA || task->task_proto & SAS_PROTOCOL_STP) { if (task->dev->dev_type == SAS_SATA_DEV) { - hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "abort task: internal abort failed\n"); + goto out; + } hisi_sas_dereg_device(hisi_hba, device); rc = hisi_sas_softreset_ata_disk(device); } @@ -1163,7 +1238,8 @@ static int hisi_sas_abort_task(struct sas_task *task) rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_CMD, tag); - if (rc == TMF_RESP_FUNC_FAILED && task->lldd_task) { + if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) && + task->lldd_task) { spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_do_release_task(hisi_hba, task, slot); spin_unlock_irqrestore(&hisi_hba->lock, flags); @@ -1178,12 +1254,29 @@ out: static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) { + struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); + struct device *dev = hisi_hba->dev; struct hisi_sas_tmf_task tmf_task; int rc = TMF_RESP_FUNC_FAILED; + unsigned long flags; + + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "abort task set: internal abort rc=%d\n", rc); + return TMF_RESP_FUNC_FAILED; + } + hisi_sas_dereg_device(hisi_hba, device); tmf_task.tmf = TMF_ABORT_TASK_SET; rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); + if (rc == TMF_RESP_FUNC_COMPLETE) { + spin_lock_irqsave(&hisi_hba->lock, flags); + hisi_sas_release_task(hisi_hba, device); + spin_unlock_irqrestore(&hisi_hba->lock, flags); + } + return rc; } @@ -1213,20 +1306,25 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device *device) { struct hisi_sas_device *sas_dev = device->lldd_dev; struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); - unsigned long flags; + struct device *dev = hisi_hba->dev; int rc = TMF_RESP_FUNC_FAILED; + unsigned long flags; if (sas_dev->dev_status != HISI_SAS_DEV_EH) return TMF_RESP_FUNC_FAILED; sas_dev->dev_status = HISI_SAS_DEV_NORMAL; - hisi_sas_internal_task_abort(hisi_hba, device, + rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc); + return TMF_RESP_FUNC_FAILED; + } hisi_sas_dereg_device(hisi_hba, device); rc = hisi_sas_debug_I_T_nexus_reset(device); - if (rc == TMF_RESP_FUNC_COMPLETE) { + if ((rc == TMF_RESP_FUNC_COMPLETE) || (rc == -ENODEV)) { spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_release_task(hisi_hba, device); spin_unlock_irqrestore(&hisi_hba->lock, flags); @@ -1249,8 +1347,10 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) /* Clear internal IO and then hardreset */ rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); - if (rc == TMF_RESP_FUNC_FAILED) + if (rc < 0) { + dev_err(dev, "lu_reset: internal abort failed\n"); goto out; + } hisi_sas_dereg_device(hisi_hba, device); phy = sas_get_local_phy(device); @@ -1266,6 +1366,14 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) } else { struct hisi_sas_tmf_task tmf_task = { .tmf = TMF_LU_RESET }; + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "lu_reset: internal abort failed\n"); + goto out; + } + hisi_sas_dereg_device(hisi_hba, device); + rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); if (rc == TMF_RESP_FUNC_COMPLETE) { spin_lock_irqsave(&hisi_hba->lock, flags); @@ -1283,8 +1391,14 @@ out: static int hisi_sas_clear_nexus_ha(struct sas_ha_struct *sas_ha) { struct hisi_hba *hisi_hba = sas_ha->lldd_ha; + HISI_SAS_DECLARE_RST_WORK_ON_STACK(r); - return hisi_sas_controller_reset(hisi_hba); + queue_work(hisi_hba->wq, &r.work); + wait_for_completion(r.completion); + if (r.done) + return TMF_RESP_FUNC_COMPLETE; + + return TMF_RESP_FUNC_FAILED; } static int hisi_sas_query_task(struct sas_task *task) @@ -1441,8 +1555,14 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, struct device *dev = hisi_hba->dev; int res; + /* + * The interface is not realized means this HW don't support internal + * abort, or don't need to do internal abort. Then here, we return + * TMF_RESP_FUNC_FAILED and let other steps go on, which depends that + * the internal abort has been executed and returned CQ. + */ if (!hisi_hba->hw->prep_abort) - return -EOPNOTSUPP; + return TMF_RESP_FUNC_FAILED; task = sas_alloc_slow_task(GFP_KERNEL); if (!task) @@ -1473,9 +1593,11 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, if (slot) slot->task = NULL; - dev_err(dev, "internal task abort: timeout.\n"); + dev_err(dev, "internal task abort: timeout and not done.\n"); + res = -EIO; goto exit; - } + } else + dev_err(dev, "internal task abort: timeout.\n"); } if (task->task_status.resp == SAS_TASK_COMPLETE && @@ -1507,6 +1629,22 @@ static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy) hisi_sas_port_notify_formed(sas_phy); } +static void hisi_sas_port_deformed(struct asd_sas_phy *sas_phy) +{ +} + +static int hisi_sas_write_gpio(struct sas_ha_struct *sha, u8 reg_type, + u8 reg_index, u8 reg_count, u8 *write_data) +{ + struct hisi_hba *hisi_hba = sha->lldd_ha; + + if (!hisi_hba->hw->write_gpio) + return -EOPNOTSUPP; + + return hisi_hba->hw->write_gpio(hisi_hba, reg_type, + reg_index, reg_count, write_data); +} + static void hisi_sas_phy_disconnected(struct hisi_sas_phy *phy) { phy->phy_attached = 0; @@ -1561,6 +1699,11 @@ EXPORT_SYMBOL_GPL(hisi_sas_kill_tasklets); struct scsi_transport_template *hisi_sas_stt; EXPORT_SYMBOL_GPL(hisi_sas_stt); +static struct device_attribute *host_attrs[] = { + &dev_attr_phy_event_threshold, + NULL, +}; + static struct scsi_host_template _hisi_sas_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -1580,6 +1723,7 @@ static struct scsi_host_template _hisi_sas_sht = { .eh_target_reset_handler = sas_eh_target_reset_handler, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, + .shost_attrs = host_attrs, }; struct scsi_host_template *hisi_sas_sht = &_hisi_sas_sht; EXPORT_SYMBOL_GPL(hisi_sas_sht); @@ -1597,6 +1741,8 @@ static struct sas_domain_function_template hisi_sas_transport_ops = { .lldd_query_task = hisi_sas_query_task, .lldd_clear_nexus_ha = hisi_sas_clear_nexus_ha, .lldd_port_formed = hisi_sas_port_formed, + .lldd_port_deformed = hisi_sas_port_deformed, + .lldd_write_gpio = hisi_sas_write_gpio, }; void hisi_sas_init_mem(struct hisi_hba *hisi_hba) @@ -1657,6 +1803,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) cq->hisi_hba = hisi_hba; /* Delivery queue structure */ + spin_lock_init(&dq->lock); dq->id = i; dq->hisi_hba = hisi_hba; @@ -1803,6 +1950,17 @@ void hisi_sas_rst_work_handler(struct work_struct *work) } EXPORT_SYMBOL_GPL(hisi_sas_rst_work_handler); +void hisi_sas_sync_rst_work_handler(struct work_struct *work) +{ + struct hisi_sas_rst *rst = + container_of(work, struct hisi_sas_rst, work); + + if (!hisi_sas_controller_reset(rst->hisi_hba)) + rst->done = true; + complete(rst->completion); +} +EXPORT_SYMBOL_GPL(hisi_sas_sync_rst_work_handler); + int hisi_sas_get_fw_info(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; @@ -1909,6 +2067,13 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev, if (IS_ERR(hisi_hba->regs)) goto err_out; + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + hisi_hba->sgpio_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(hisi_hba->sgpio_regs)) + goto err_out; + } + if (hisi_sas_alloc(hisi_hba, shost)) { hisi_sas_free(hisi_hba); goto err_out; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index dc6eca8d6afd..679e76f58a0a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -544,7 +544,7 @@ static void setup_itct_v1_hw(struct hisi_hba *hisi_hba, (0xff00ULL << ITCT_HDR_REJ_OPEN_TL_OFF)); } -static void free_device_v1_hw(struct hisi_hba *hisi_hba, +static void clear_itct_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_device *sas_dev) { u64 dev_id = sas_dev->device_id; @@ -1482,7 +1482,7 @@ static irqreturn_t int_phyup_v1_hw(int irq_no, void *p) else if (phy->identify.device_type != SAS_PHY_UNUSED) phy->identify.target_port_protocols = SAS_PROTOCOL_SMP; - queue_work(hisi_hba->wq, &phy->phyup_ws); + hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); end: hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, @@ -1850,7 +1850,7 @@ static const struct hisi_sas_hw hisi_sas_v1_hw = { .hw_init = hisi_sas_v1_init, .setup_itct = setup_itct_v1_hw, .sl_notify = sl_notify_v1_hw, - .free_device = free_device_v1_hw, + .clear_itct = clear_itct_v1_hw, .prep_smp = prep_smp_v1_hw, .prep_ssp = prep_ssp_v1_hw, .get_free_slot = get_free_slot_v1_hw, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 5d3467fd728d..4ccb61e2ae5c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -240,7 +240,12 @@ #define CHL_INT1_DMAC_TX_ECC_ERR_MSK (0x1 << CHL_INT1_DMAC_TX_ECC_ERR_OFF) #define CHL_INT1_DMAC_RX_ECC_ERR_OFF 17 #define CHL_INT1_DMAC_RX_ECC_ERR_MSK (0x1 << CHL_INT1_DMAC_RX_ECC_ERR_OFF) +#define CHL_INT1_DMAC_TX_AXI_WR_ERR_OFF 19 +#define CHL_INT1_DMAC_TX_AXI_RD_ERR_OFF 20 +#define CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF 21 +#define CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF 22 #define CHL_INT2 (PORT_BASE + 0x1bc) +#define CHL_INT2_SL_IDAF_TOUT_CONF_OFF 0 #define CHL_INT0_MSK (PORT_BASE + 0x1c0) #define CHL_INT1_MSK (PORT_BASE + 0x1c4) #define CHL_INT2_MSK (PORT_BASE + 0x1c8) @@ -952,7 +957,7 @@ static void setup_itct_v2_hw(struct hisi_hba *hisi_hba, (0x1ULL << ITCT_HDR_RTOLT_OFF)); } -static void free_device_v2_hw(struct hisi_hba *hisi_hba, +static void clear_itct_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_device *sas_dev) { DECLARE_COMPLETION_ONSTACK(completion); @@ -963,10 +968,6 @@ static void free_device_v2_hw(struct hisi_hba *hisi_hba, sas_dev->completion = &completion; - /* SoC bug workaround */ - if (dev_is_sata(sas_dev->sas_device)) - clear_bit(sas_dev->sata_idx, hisi_hba->sata_dev_bitmap); - /* clear the itct interrupt state */ if (ENT_INT_SRC3_ITC_INT_MSK & reg_val) hisi_sas_write32(hisi_hba, ENT_INT_SRC3, @@ -981,6 +982,15 @@ static void free_device_v2_hw(struct hisi_hba *hisi_hba, } } +static void free_device_v2_hw(struct hisi_sas_device *sas_dev) +{ + struct hisi_hba *hisi_hba = sas_dev->hisi_hba; + + /* SoC bug workaround */ + if (dev_is_sata(sas_dev->sas_device)) + clear_bit(sas_dev->sata_idx, hisi_hba->sata_dev_bitmap); +} + static int reset_hw_v2_hw(struct hisi_hba *hisi_hba) { int i, reset_val; @@ -1177,8 +1187,8 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff); hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, 0xfff87fff); hisi_sas_phy_write32(hisi_hba, i, RXOP_CHECK_CFG_H, 0x1000); - hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xffffffff); - hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x8ffffbff); + hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xff857fff); + hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x8ffffbfe); hisi_sas_phy_write32(hisi_hba, i, SL_CFG, 0x13f801fc); hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL_RDY_MSK, 0x0); hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_NOT_RDY_MSK, 0x0); @@ -2356,6 +2366,7 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) ts->resp = SAS_TASK_COMPLETE; if (unlikely(aborted)) { + dev_dbg(dev, "slot_complete: task(%p) aborted\n", task); ts->stat = SAS_ABORTED_TASK; spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_slot_task_free(hisi_hba, task, slot); @@ -2400,6 +2411,7 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) (!(complete_hdr->dw0 & CMPLT_HDR_RSPNS_XFRD_MSK))) { u32 err_phase = (complete_hdr->dw0 & CMPLT_HDR_ERR_PHASE_MSK) >> CMPLT_HDR_ERR_PHASE_OFF; + u32 *error_info = hisi_sas_status_buf_addr_mem(slot); /* Analyse error happens on which phase TX or RX */ if (ERR_ON_TX_PHASE(err_phase)) @@ -2407,6 +2419,16 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) else if (ERR_ON_RX_PHASE(err_phase)) slot_err_v2_hw(hisi_hba, task, slot, 2); + if (ts->stat != SAS_DATA_UNDERRUN) + dev_info(dev, "erroneous completion iptt=%d task=%p " + "CQ hdr: 0x%x 0x%x 0x%x 0x%x " + "Error info: 0x%x 0x%x 0x%x 0x%x\n", + slot->idx, task, + complete_hdr->dw0, complete_hdr->dw1, + complete_hdr->act, complete_hdr->dw3, + error_info[0], error_info[1], + error_info[2], error_info[3]); + if (unlikely(slot->abort)) return ts->stat; goto out; @@ -2456,7 +2478,7 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) } if (!slot->port->port_attached) { - dev_err(dev, "slot complete: port %d has removed\n", + dev_warn(dev, "slot complete: port %d has removed\n", slot->port->sas_port.id); ts->stat = SAS_PHY_DOWN; } @@ -2517,7 +2539,7 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba, dw1 |= 1 << CMD_HDR_RESET_OFF; dw1 |= (hisi_sas_get_ata_protocol( - task->ata_task.fis.command, task->data_dir)) + &task->ata_task.fis, task->data_dir)) << CMD_HDR_FRAME_TYPE_OFF; dw1 |= sas_dev->device_id << CMD_HDR_DEV_ID_OFF; hdr->dw1 = cpu_to_le32(dw1); @@ -2687,7 +2709,7 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) if (!timer_pending(&hisi_hba->timer)) set_link_timer_quirk(hisi_hba); } - queue_work(hisi_hba->wq, &phy->phyup_ws); + hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); end: hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, @@ -2713,10 +2735,12 @@ static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba) u32 phy_state, sl_ctrl, txid_auto; struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct hisi_sas_port *port = phy->port; + struct device *dev = hisi_hba->dev; hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1); phy_state = hisi_sas_read32(hisi_hba, PHY_STATE); + dev_info(dev, "phydown: phy%d phy_state=0x%x\n", phy_no, phy_state); hisi_sas_phy_down(hisi_hba, phy_no, (phy_state & 1 << phy_no) ? 1 : 0); sl_ctrl = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL); @@ -2813,6 +2837,33 @@ 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, 0); } +static const struct hisi_sas_hw_error port_ecc_axi_error[] = { + { + .irq_msk = BIT(CHL_INT1_DMAC_TX_ECC_ERR_OFF), + .msg = "dmac_tx_ecc_bad_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_RX_ECC_ERR_OFF), + .msg = "dmac_rx_ecc_bad_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_TX_AXI_WR_ERR_OFF), + .msg = "dma_tx_axi_wr_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_TX_AXI_RD_ERR_OFF), + .msg = "dma_tx_axi_rd_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF), + .msg = "dma_rx_axi_wr_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF), + .msg = "dma_rx_axi_rd_err", + }, +}; + static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p) { struct hisi_hba *hisi_hba = p; @@ -2829,40 +2880,55 @@ static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p) HGC_INVLD_DQE_INFO_FB_CH3_OFF) & 0x1ff; while (irq_msk) { - if (irq_msk & (1 << phy_no)) { - 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); - - if (irq_value1) { - if (irq_value1 & (CHL_INT1_DMAC_RX_ECC_ERR_MSK | - CHL_INT1_DMAC_TX_ECC_ERR_MSK)) - panic("%s: DMAC RX/TX ecc bad error!\ - (0x%x)", - dev_name(dev), irq_value1); - - hisi_sas_phy_write32(hisi_hba, phy_no, - CHL_INT1, irq_value1); - } + 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); + + if ((irq_msk & (1 << phy_no)) && irq_value1) { + int i; - if (irq_value2) - hisi_sas_phy_write32(hisi_hba, phy_no, - CHL_INT2, irq_value2); + for (i = 0; i < ARRAY_SIZE(port_ecc_axi_error); i++) { + const struct hisi_sas_hw_error *error = + &port_ecc_axi_error[i]; + + if (!(irq_value1 & error->irq_msk)) + continue; + + dev_warn(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_value0) { - if (irq_value0 & CHL_INT0_SL_RX_BCST_ACK_MSK) - phy_bcast_v2_hw(phy_no, hisi_hba); + if ((irq_msk & (1 << phy_no)) && irq_value2) { + struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; - hisi_sas_phy_write32(hisi_hba, phy_no, - CHL_INT0, irq_value0 - & (~CHL_INT0_HOTPLUG_TOUT_MSK) - & (~CHL_INT0_SL_PHY_ENABLE_MSK) - & (~CHL_INT0_NOT_RDY_MSK)); + 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); } + + hisi_sas_phy_write32(hisi_hba, phy_no, + CHL_INT2, irq_value2); + } + + if ((irq_msk & (1 << phy_no)) && irq_value0) { + if (irq_value0 & CHL_INT0_SL_RX_BCST_ACK_MSK) + phy_bcast_v2_hw(phy_no, hisi_hba); + + hisi_sas_phy_write32(hisi_hba, phy_no, + CHL_INT0, irq_value0 + & (~CHL_INT0_HOTPLUG_TOUT_MSK) + & (~CHL_INT0_SL_PHY_ENABLE_MSK) + & (~CHL_INT0_NOT_RDY_MSK)); } irq_msk &= ~(1 << phy_no); phy_no++; @@ -2906,7 +2972,7 @@ static void multi_bit_ecc_error_process_v2_hw(struct hisi_hba *hisi_hba, val = hisi_sas_read32(hisi_hba, ecc_error->reg); val &= ecc_error->msk; val >>= ecc_error->shift; - dev_warn(dev, ecc_error->msg, irq_value, val); + dev_err(dev, ecc_error->msg, irq_value, val); queue_work(hisi_hba->wq, &hisi_hba->rst_work); } } @@ -3015,12 +3081,12 @@ static irqreturn_t fatal_axi_int_v2_hw(int irq_no, void *p) for (; sub->msk || sub->msg; sub++) { if (!(err_value & sub->msk)) continue; - dev_warn(dev, "%s (0x%x) found!\n", + dev_err(dev, "%s (0x%x) found!\n", sub->msg, irq_value); queue_work(hisi_hba->wq, &hisi_hba->rst_work); } } else { - dev_warn(dev, "%s (0x%x) found!\n", + dev_err(dev, "%s (0x%x) found!\n", axi_error->msg, irq_value); queue_work(hisi_hba->wq, &hisi_hba->rst_work); } @@ -3206,7 +3272,7 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p) phy->identify.device_type = SAS_SATA_DEV; phy->frame_rcvd_size = sizeof(struct dev_to_host_fis); phy->identify.target_port_protocols = SAS_PROTOCOL_SATA; - queue_work(hisi_hba->wq, &phy->phyup_ws); + hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); end: hisi_sas_write32(hisi_hba, ENT_INT_SRC1 + offset, ent_tmp); @@ -3392,7 +3458,7 @@ static int soft_reset_v2_hw(struct hisi_hba *hisi_hba) udelay(10); if (cnt++ > 10) { - dev_info(dev, "wait axi bus state to idle timeout!\n"); + dev_err(dev, "wait axi bus state to idle timeout!\n"); return -1; } } @@ -3408,6 +3474,44 @@ static int soft_reset_v2_hw(struct hisi_hba *hisi_hba) return 0; } +static int write_gpio_v2_hw(struct hisi_hba *hisi_hba, u8 reg_type, + u8 reg_index, u8 reg_count, u8 *write_data) +{ + struct device *dev = hisi_hba->dev; + int phy_no, count; + + if (!hisi_hba->sgpio_regs) + return -EOPNOTSUPP; + + switch (reg_type) { + case SAS_GPIO_REG_TX: + count = reg_count * 4; + count = min(count, hisi_hba->n_phy); + + for (phy_no = 0; phy_no < count; phy_no++) { + /* + * GPIO_TX[n] register has the highest numbered drive + * of the four in the first byte and the lowest + * numbered drive in the fourth byte. + * See SFF-8485 Rev. 0.7 Table 24. + */ + void __iomem *reg_addr = hisi_hba->sgpio_regs + + reg_index * 4 + phy_no; + int data_idx = phy_no + 3 - (phy_no % 4) * 2; + + writeb(write_data[data_idx], reg_addr); + } + + break; + default: + dev_err(dev, "write gpio: unsupported or bad reg type %d\n", + reg_type); + return -EINVAL; + } + + return 0; +} + static const struct hisi_sas_hw hisi_sas_v2_hw = { .hw_init = hisi_sas_v2_init, .setup_itct = setup_itct_v2_hw, @@ -3415,6 +3519,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = { .alloc_dev = alloc_dev_quirk_v2_hw, .sl_notify = sl_notify_v2_hw, .get_wideport_bitmap = get_wideport_bitmap_v2_hw, + .clear_itct = clear_itct_v2_hw, .free_device = free_device_v2_hw, .prep_smp = prep_smp_v2_hw, .prep_ssp = prep_ssp_v2_hw, @@ -3434,6 +3539,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = { .complete_hdr_size = sizeof(struct hisi_sas_complete_v2_hdr), .soft_reset = soft_reset_v2_hw, .get_phys_state = get_phys_state_v2_hw, + .write_gpio = write_gpio_v2_hw, }; static int hisi_sas_v2_probe(struct platform_device *pdev) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 19b1f2ffec17..a1f18689729a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -140,6 +140,7 @@ #define RX_IDAF_DWORD0 (PORT_BASE + 0xc4) #define RXOP_CHECK_CFG_H (PORT_BASE + 0xfc) #define STP_LINK_TIMER (PORT_BASE + 0x120) +#define STP_LINK_TIMEOUT_STATE (PORT_BASE + 0x124) #define CON_CFG_DRIVER (PORT_BASE + 0x130) #define SAS_SSP_CON_TIMER_CFG (PORT_BASE + 0x134) #define SAS_SMP_CON_TIMER_CFG (PORT_BASE + 0x138) @@ -165,6 +166,8 @@ #define CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF 21 #define CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF 22 #define CHL_INT2 (PORT_BASE + 0x1bc) +#define CHL_INT2_SL_IDAF_TOUT_CONF_OFF 0 +#define CHL_INT2_STP_LINK_TIMEOUT_OFF 31 #define CHL_INT0_MSK (PORT_BASE + 0x1c0) #define CHL_INT1_MSK (PORT_BASE + 0x1c4) #define CHL_INT2_MSK (PORT_BASE + 0x1c8) @@ -204,6 +207,13 @@ #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) +/* RAS registers need init */ +#define RAS_BASE (0x6000) +#define SAS_RAS_INTR0 (RAS_BASE) +#define SAS_RAS_INTR1 (RAS_BASE + 0x04) +#define SAS_RAS_INTR0_MASK (RAS_BASE + 0x08) +#define SAS_RAS_INTR1_MASK (RAS_BASE + 0x0c) + /* HW dma structures */ /* Delivery queue header */ /* dw0 */ @@ -422,7 +432,7 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, 0xffffffff); hisi_sas_phy_write32(hisi_hba, i, RXOP_CHECK_CFG_H, 0x1000); hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xff87ffff); - hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x8ffffbff); + hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0xffffbfe); hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL_RDY_MSK, 0x0); hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_NOT_RDY_MSK, 0x0); hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_DWS_RESET_MSK, 0x0); @@ -496,6 +506,10 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_write32(hisi_hba, SATA_INITI_D2H_STORE_ADDR_HI, upper_32_bits(hisi_hba->initial_fis_dma)); + + /* RAS registers init */ + hisi_sas_write32(hisi_hba, SAS_RAS_INTR0_MASK, 0x0); + hisi_sas_write32(hisi_hba, SAS_RAS_INTR1_MASK, 0x0); } static void config_phy_opt_mode_v3_hw(struct hisi_hba *hisi_hba, int phy_no) @@ -588,7 +602,7 @@ static void setup_itct_v3_hw(struct hisi_hba *hisi_hba, (0x1ULL << ITCT_HDR_RTOLT_OFF)); } -static void free_device_v3_hw(struct hisi_hba *hisi_hba, +static void clear_itct_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_device *sas_dev) { DECLARE_COMPLETION_ONSTACK(completion); @@ -1033,7 +1047,7 @@ static int prep_ata_v3_hw(struct hisi_hba *hisi_hba, dw1 |= 1 << CMD_HDR_RESET_OFF; dw1 |= (hisi_sas_get_ata_protocol( - task->ata_task.fis.command, task->data_dir)) + &task->ata_task.fis, task->data_dir)) << CMD_HDR_FRAME_TYPE_OFF; dw1 |= sas_dev->device_id << CMD_HDR_DEV_ID_OFF; @@ -1138,7 +1152,7 @@ static int phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba) struct dev_to_host_fis *fis; u8 attached_sas_addr[SAS_ADDR_SIZE] = {0}; - dev_info(dev, "phyup: phy%d link_rate=%d\n", phy_no, link_rate); + 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; sas_phy->oob_mode = SATA_OOB_MODE; @@ -1181,7 +1195,7 @@ static int phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba) phy->port_id = port_id; phy->phy_attached = 1; - queue_work(hisi_hba->wq, &phy->phyup_ws); + hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); end: hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, @@ -1322,7 +1336,7 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) if (!(irq_value1 & error->irq_msk)) continue; - dev_warn(dev, "%s error (phy%d 0x%x) found!\n", + 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); } @@ -1331,9 +1345,31 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) CHL_INT1, irq_value1); } - if (irq_msk & (8 << (phy_no * 4)) && irq_value2) + if (irq_msk & (8 << (phy_no * 4)) && irq_value2) { + struct hisi_sas_phy *phy = &hisi_hba->phy[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_msk & (2 << (phy_no * 4)) && irq_value0) { @@ -1432,12 +1468,12 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p) if (!(err_value & sub->msk)) continue; - dev_warn(dev, "%s error (0x%x) found!\n", + dev_err(dev, "%s error (0x%x) found!\n", sub->msg, irq_value); queue_work(hisi_hba->wq, &hisi_hba->rst_work); } } else { - dev_warn(dev, "%s error (0x%x) found!\n", + dev_err(dev, "%s error (0x%x) found!\n", error->msg, irq_value); queue_work(hisi_hba->wq, &hisi_hba->rst_work); } @@ -1542,6 +1578,7 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) memset(ts, 0, sizeof(*ts)); ts->resp = SAS_TASK_COMPLETE; if (unlikely(aborted)) { + dev_dbg(dev, "slot complete: task(%p) aborted\n", task); ts->stat = SAS_ABORTED_TASK; spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_slot_task_free(hisi_hba, task, slot); @@ -1583,7 +1620,18 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) /* check for erroneous completion */ if ((complete_hdr->dw0 & CMPLT_HDR_CMPLT_MSK) == 0x3) { + u32 *error_info = hisi_sas_status_buf_addr_mem(slot); + slot_err_v3_hw(hisi_hba, task, slot); + if (ts->stat != SAS_DATA_UNDERRUN) + dev_info(dev, "erroneous completion iptt=%d task=%p " + "CQ hdr: 0x%x 0x%x 0x%x 0x%x " + "Error info: 0x%x 0x%x 0x%x 0x%x\n", + slot->idx, task, + complete_hdr->dw0, complete_hdr->dw1, + complete_hdr->act, complete_hdr->dw3, + error_info[0], error_info[1], + error_info[2], error_info[3]); if (unlikely(slot->abort)) return ts->stat; goto out; @@ -1628,7 +1676,7 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) } if (!slot->port->port_attached) { - dev_err(dev, "slot complete: port %d has removed\n", + dev_warn(dev, "slot complete: port %d has removed\n", slot->port->sas_port.id); ts->stat = SAS_PHY_DOWN; } @@ -1653,9 +1701,8 @@ static void cq_tasklet_v3_hw(unsigned long val) struct hisi_sas_cq *cq = (struct hisi_sas_cq *)val; struct hisi_hba *hisi_hba = cq->hisi_hba; struct hisi_sas_slot *slot; - struct hisi_sas_itct *itct; struct hisi_sas_complete_v3_hdr *complete_queue; - u32 rd_point = cq->rd_point, wr_point, dev_id; + u32 rd_point = cq->rd_point, wr_point; int queue = cq->id; struct hisi_sas_dq *dq = &hisi_hba->dq[queue]; @@ -1671,38 +1718,11 @@ static void cq_tasklet_v3_hw(unsigned long val) complete_hdr = &complete_queue[rd_point]; - /* Check for NCQ completion */ - if (complete_hdr->act) { - u32 act_tmp = complete_hdr->act; - int ncq_tag_count = ffs(act_tmp); - - dev_id = (complete_hdr->dw1 & CMPLT_HDR_DEV_ID_MSK) >> - CMPLT_HDR_DEV_ID_OFF; - itct = &hisi_hba->itct[dev_id]; - - /* The NCQ tags are held in the itct header */ - while (ncq_tag_count) { - __le64 *ncq_tag = &itct->qw4_15[0]; - - ncq_tag_count -= 1; - iptt = (ncq_tag[ncq_tag_count / 5] - >> (ncq_tag_count % 5) * 12) & 0xfff; - - slot = &hisi_hba->slot_info[iptt]; - slot->cmplt_queue_slot = rd_point; - slot->cmplt_queue = queue; - slot_complete_v3_hw(hisi_hba, slot); - - act_tmp &= ~(1 << ncq_tag_count); - ncq_tag_count = ffs(act_tmp); - } - } else { - iptt = (complete_hdr->dw1) & CMPLT_HDR_IPTT_MSK; - slot = &hisi_hba->slot_info[iptt]; - slot->cmplt_queue_slot = rd_point; - slot->cmplt_queue = queue; - slot_complete_v3_hw(hisi_hba, slot); - } + iptt = (complete_hdr->dw1) & CMPLT_HDR_IPTT_MSK; + slot = &hisi_hba->slot_info[iptt]; + slot->cmplt_queue_slot = rd_point; + slot->cmplt_queue = queue; + slot_complete_v3_hw(hisi_hba, slot); if (++rd_point >= HISI_SAS_QUEUE_SLOTS) rd_point = 0; @@ -1951,7 +1971,7 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = { .max_command_entries = HISI_SAS_COMMAND_ENTRIES_V3_HW, .get_wideport_bitmap = get_wideport_bitmap_v3_hw, .complete_hdr_size = sizeof(struct hisi_sas_complete_v3_hdr), - .free_device = free_device_v3_hw, + .clear_itct = clear_itct_v3_hw, .sl_notify = sl_notify_v3_hw, .prep_ssp = prep_ssp_v3_hw, .prep_smp = prep_smp_v3_hw, @@ -2157,21 +2177,243 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev) scsi_host_put(shost); } +static const struct hisi_sas_hw_error sas_ras_intr0_nfe[] = { + { .irq_msk = BIT(19), .msg = "HILINK_INT" }, + { .irq_msk = BIT(20), .msg = "HILINK_PLL0_OUT_OF_LOCK" }, + { .irq_msk = BIT(21), .msg = "HILINK_PLL1_OUT_OF_LOCK" }, + { .irq_msk = BIT(22), .msg = "HILINK_LOSS_OF_REFCLK0" }, + { .irq_msk = BIT(23), .msg = "HILINK_LOSS_OF_REFCLK1" }, + { .irq_msk = BIT(24), .msg = "DMAC0_TX_POISON" }, + { .irq_msk = BIT(25), .msg = "DMAC1_TX_POISON" }, + { .irq_msk = BIT(26), .msg = "DMAC2_TX_POISON" }, + { .irq_msk = BIT(27), .msg = "DMAC3_TX_POISON" }, + { .irq_msk = BIT(28), .msg = "DMAC4_TX_POISON" }, + { .irq_msk = BIT(29), .msg = "DMAC5_TX_POISON" }, + { .irq_msk = BIT(30), .msg = "DMAC6_TX_POISON" }, + { .irq_msk = BIT(31), .msg = "DMAC7_TX_POISON" }, +}; + +static const struct hisi_sas_hw_error sas_ras_intr1_nfe[] = { + { .irq_msk = BIT(0), .msg = "RXM_CFG_MEM3_ECC2B_INTR" }, + { .irq_msk = BIT(1), .msg = "RXM_CFG_MEM2_ECC2B_INTR" }, + { .irq_msk = BIT(2), .msg = "RXM_CFG_MEM1_ECC2B_INTR" }, + { .irq_msk = BIT(3), .msg = "RXM_CFG_MEM0_ECC2B_INTR" }, + { .irq_msk = BIT(4), .msg = "HGC_CQE_ECC2B_INTR" }, + { .irq_msk = BIT(5), .msg = "LM_CFG_IOSTL_ECC2B_INTR" }, + { .irq_msk = BIT(6), .msg = "LM_CFG_ITCTL_ECC2B_INTR" }, + { .irq_msk = BIT(7), .msg = "HGC_ITCT_ECC2B_INTR" }, + { .irq_msk = BIT(8), .msg = "HGC_IOST_ECC2B_INTR" }, + { .irq_msk = BIT(9), .msg = "HGC_DQE_ECC2B_INTR" }, + { .irq_msk = BIT(10), .msg = "DMAC0_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(11), .msg = "DMAC1_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(12), .msg = "DMAC2_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(13), .msg = "DMAC3_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(14), .msg = "DMAC4_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(15), .msg = "DMAC5_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(16), .msg = "DMAC6_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(17), .msg = "DMAC7_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(18), .msg = "OOO_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(20), .msg = "HGC_DQE_POISON_INTR" }, + { .irq_msk = BIT(21), .msg = "HGC_IOST_POISON_INTR" }, + { .irq_msk = BIT(22), .msg = "HGC_ITCT_POISON_INTR" }, + { .irq_msk = BIT(23), .msg = "HGC_ITCT_NCQ_POISON_INTR" }, + { .irq_msk = BIT(24), .msg = "DMAC0_RX_POISON" }, + { .irq_msk = BIT(25), .msg = "DMAC1_RX_POISON" }, + { .irq_msk = BIT(26), .msg = "DMAC2_RX_POISON" }, + { .irq_msk = BIT(27), .msg = "DMAC3_RX_POISON" }, + { .irq_msk = BIT(28), .msg = "DMAC4_RX_POISON" }, + { .irq_msk = BIT(29), .msg = "DMAC5_RX_POISON" }, + { .irq_msk = BIT(30), .msg = "DMAC6_RX_POISON" }, + { .irq_msk = BIT(31), .msg = "DMAC7_RX_POISON" }, +}; + +static bool process_non_fatal_error_v3_hw(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + const struct hisi_sas_hw_error *ras_error; + bool need_reset = false; + u32 irq_value; + int i; + + irq_value = hisi_sas_read32(hisi_hba, SAS_RAS_INTR0); + for (i = 0; i < ARRAY_SIZE(sas_ras_intr0_nfe); i++) { + ras_error = &sas_ras_intr0_nfe[i]; + if (ras_error->irq_msk & irq_value) { + dev_warn(dev, "SAS_RAS_INTR0: %s(irq_value=0x%x) found.\n", + ras_error->msg, irq_value); + need_reset = true; + } + } + hisi_sas_write32(hisi_hba, SAS_RAS_INTR0, irq_value); + + irq_value = hisi_sas_read32(hisi_hba, SAS_RAS_INTR1); + for (i = 0; i < ARRAY_SIZE(sas_ras_intr1_nfe); i++) { + ras_error = &sas_ras_intr1_nfe[i]; + if (ras_error->irq_msk & irq_value) { + dev_warn(dev, "SAS_RAS_INTR1: %s(irq_value=0x%x) found.\n", + ras_error->msg, irq_value); + need_reset = true; + } + } + hisi_sas_write32(hisi_hba, SAS_RAS_INTR1, irq_value); + + return need_reset; +} + +static pci_ers_result_t hisi_sas_error_detected_v3_hw(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct hisi_hba *hisi_hba = sha->lldd_ha; + struct device *dev = hisi_hba->dev; + + dev_info(dev, "PCI error: detected callback, state(%d)!!\n", state); + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + + if (process_non_fatal_error_v3_hw(hisi_hba)) + return PCI_ERS_RESULT_NEED_RESET; + + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static pci_ers_result_t hisi_sas_mmio_enabled_v3_hw(struct pci_dev *pdev) +{ + return PCI_ERS_RESULT_RECOVERED; +} + +static pci_ers_result_t hisi_sas_slot_reset_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; + HISI_SAS_DECLARE_RST_WORK_ON_STACK(r); + + dev_info(dev, "PCI error: slot reset callback!!\n"); + queue_work(hisi_hba->wq, &r.work); + wait_for_completion(r.completion); + if (r.done) + return PCI_ERS_RESULT_RECOVERED; + + return PCI_ERS_RESULT_DISCONNECT; +} + enum { /* instances of the controller */ hip08, }; +static int hisi_sas_v3_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + 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; + int rc; + u32 reg_val; + unsigned long flags; + + if (!pdev->pm_cap) { + dev_err(dev, "PCI PM not supported\n"); + return -ENODEV; + } + + set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags); + 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 = 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); + clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); + clear_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags); + scsi_unblock_requests(shost); + return rc; + } + + hisi_sas_init_mem(hisi_hba); + + device_state = pci_choose_state(pdev, state); + dev_warn(dev, "entering operating state [D%d]\n", + device_state); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, device_state); + + spin_lock_irqsave(&hisi_hba->lock, flags); + hisi_sas_release_tasks(hisi_hba); + spin_unlock_irqrestore(&hisi_hba->lock, flags); + + sas_suspend_ha(sha); + return 0; +} + +static int hisi_sas_v3_resume(struct pci_dev *pdev) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct hisi_hba *hisi_hba = sha->lldd_ha; + struct Scsi_Host *shost = hisi_hba->shost; + struct device *dev = hisi_hba->dev; + unsigned int rc; + u32 device_state = pdev->current_state; + + dev_warn(dev, "resuming from operating state [D%d]\n", + device_state); + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + rc = pci_enable_device(pdev); + if (rc) + dev_err(dev, "enable device failed during resume (%d)\n", rc); + + pci_set_master(pdev); + scsi_unblock_requests(shost); + clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); + + sas_prep_resume_ha(sha); + init_reg_v3_hw(hisi_hba); + hisi_hba->hw->phys_init(hisi_hba); + sas_resume_ha(sha); + clear_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags); + + return 0; +} + static const struct pci_device_id sas_v3_pci_table[] = { { PCI_VDEVICE(HUAWEI, 0xa230), hip08 }, {} }; +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, +}; + static struct pci_driver sas_v3_pci_driver = { .name = DRV_NAME, .id_table = sas_v3_pci_table, .probe = hisi_sas_v3_probe, .remove = hisi_sas_v3_remove, + .suspend = hisi_sas_v3_suspend, + .resume = hisi_sas_v3_resume, + .err_handler = &hisi_sas_err_handler, }; module_pci_driver(sas_v3_pci_driver); |