diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-09 16:53:47 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-09 16:53:47 -0800 |
commit | 92fff53b7191cae566be9ca6752069426c7f8241 (patch) | |
tree | 019396be4719ad3969d0395cfa0a90860be75f4a /drivers/scsi/hisi_sas | |
parent | a50243b1ddcdd766d0d17fbfeeb1a22e62fdc461 (diff) | |
parent | 26af1a368e40618d67956b1f883fbcfec292c5d8 (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 update of the usual drivers: arcmsr, qla2xxx, lpfc,
hisi_sas, target/iscsi and target/core.
Additionally Christoph refactored gdth as part of the dma changes. The
major mid-layer change this time is the removal of bidi commands and
with them the whole of the osd/exofs driver and filesystem. This is a
major simplification for block and mq in particular"
* tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (240 commits)
scsi: cxgb4i: validate tcp sequence number only if chip version <= T5
scsi: cxgb4i: get pf number from lldi->pf
scsi: core: replace GFP_ATOMIC with GFP_KERNEL in scsi_scan.c
scsi: mpt3sas: Add missing breaks in switch statements
scsi: aacraid: Fix missing break in switch statement
scsi: kill command serial number
scsi: csiostor: drop serial_number usage
scsi: mvumi: use request tag instead of serial_number
scsi: dpt_i2o: remove serial number usage
scsi: st: osst: Remove negative constant left-shifts
scsi: ufs-bsg: Allow reading descriptors
scsi: ufs: Allow reading descriptor via raw upiu
scsi: ufs-bsg: Change the calling convention for write descriptor
scsi: ufs: Remove unused device quirks
Revert "scsi: ufs: disable vccq if it's not needed by UFS device"
scsi: megaraid_sas: Remove a bunch of set but not used variables
scsi: clean obsolete return values of eh_timed_out
scsi: sd: Optimal I/O size should be a multiple of physical block size
scsi: MAINTAINERS: SCSI initiator and target tweaks
scsi: fcoe: make use of fip_mode enum complete
...
Diffstat (limited to 'drivers/scsi/hisi_sas')
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas.h | 110 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_main.c | 838 | ||||
-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 | 25 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 379 |
5 files changed, 1233 insertions, 125 deletions
diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index af291947a54d..6c87bd34509a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -14,6 +14,7 @@ #include <linux/acpi.h> #include <linux/clk.h> +#include <linux/debugfs.h> #include <linux/dmapool.h> #include <linux/iopoll.h> #include <linux/lcm.h> @@ -29,7 +30,7 @@ #define HISI_SAS_MAX_PHYS 9 #define HISI_SAS_MAX_QUEUES 32 -#define HISI_SAS_QUEUE_SLOTS 512 +#define HISI_SAS_QUEUE_SLOTS 4096 #define HISI_SAS_MAX_ITCT_ENTRIES 1024 #define HISI_SAS_MAX_DEVICES HISI_SAS_MAX_ITCT_ENTRIES #define HISI_SAS_RESET_BIT 0 @@ -40,20 +41,25 @@ #define HISI_SAS_COMMAND_TABLE_SZ (sizeof(union hisi_sas_command_table)) #define hisi_sas_status_buf_addr(buf) \ - (buf + offsetof(struct hisi_sas_slot_buf_table, status_buffer)) -#define hisi_sas_status_buf_addr_mem(slot) hisi_sas_status_buf_addr(slot->buf) + ((buf) + offsetof(struct hisi_sas_slot_buf_table, status_buffer)) +#define hisi_sas_status_buf_addr_mem(slot) hisi_sas_status_buf_addr((slot)->buf) #define hisi_sas_status_buf_addr_dma(slot) \ - hisi_sas_status_buf_addr(slot->buf_dma) + hisi_sas_status_buf_addr((slot)->buf_dma) #define hisi_sas_cmd_hdr_addr(buf) \ - (buf + offsetof(struct hisi_sas_slot_buf_table, command_header)) -#define hisi_sas_cmd_hdr_addr_mem(slot) hisi_sas_cmd_hdr_addr(slot->buf) -#define hisi_sas_cmd_hdr_addr_dma(slot) hisi_sas_cmd_hdr_addr(slot->buf_dma) + ((buf) + offsetof(struct hisi_sas_slot_buf_table, command_header)) +#define hisi_sas_cmd_hdr_addr_mem(slot) hisi_sas_cmd_hdr_addr((slot)->buf) +#define hisi_sas_cmd_hdr_addr_dma(slot) hisi_sas_cmd_hdr_addr((slot)->buf_dma) #define hisi_sas_sge_addr(buf) \ - (buf + offsetof(struct hisi_sas_slot_buf_table, sge_page)) -#define hisi_sas_sge_addr_mem(slot) hisi_sas_sge_addr(slot->buf) -#define hisi_sas_sge_addr_dma(slot) hisi_sas_sge_addr(slot->buf_dma) + ((buf) + offsetof(struct hisi_sas_slot_buf_table, sge_page)) +#define hisi_sas_sge_addr_mem(slot) hisi_sas_sge_addr((slot)->buf) +#define hisi_sas_sge_addr_dma(slot) hisi_sas_sge_addr((slot)->buf_dma) + +#define hisi_sas_sge_dif_addr(buf) \ + ((buf) + offsetof(struct hisi_sas_slot_dif_buf_table, sge_dif_page)) +#define hisi_sas_sge_dif_addr_mem(slot) hisi_sas_sge_dif_addr((slot)->buf) +#define hisi_sas_sge_dif_addr_dma(slot) hisi_sas_sge_dif_addr((slot)->buf_dma) #define HISI_SAS_MAX_SSP_RESP_SZ (sizeof(struct ssp_frame_hdr) + 1024) #define HISI_SAS_MAX_SMP_RESP_SZ 1028 @@ -73,7 +79,13 @@ SHOST_DIF_TYPE2_PROTECTION | \ SHOST_DIF_TYPE3_PROTECTION) -#define HISI_SAS_PROT_MASK (HISI_SAS_DIF_PROT_MASK) +#define HISI_SAS_DIX_PROT_MASK (SHOST_DIX_TYPE1_PROTECTION | \ + SHOST_DIX_TYPE2_PROTECTION | \ + SHOST_DIX_TYPE3_PROTECTION) + +#define HISI_SAS_PROT_MASK (HISI_SAS_DIF_PROT_MASK | HISI_SAS_DIX_PROT_MASK) + +#define HISI_SAS_WAIT_PHYUP_TIMEOUT 20 struct hisi_hba; @@ -82,11 +94,6 @@ enum { PORT_TYPE_SATA = (1U << 0), }; -enum dev_status { - HISI_SAS_DEV_NORMAL, - HISI_SAS_DEV_EH, -}; - enum { HISI_SAS_INT_ABT_CMD = 0, HISI_SAS_INT_ABT_DEV = 1, @@ -145,6 +152,7 @@ struct hisi_sas_phy { struct asd_sas_phy sas_phy; struct sas_identify identify; struct completion *reset_completion; + struct timer_list timer; spinlock_t lock; u64 port_id; /* from hw */ u64 frame_rcvd_size; @@ -165,6 +173,7 @@ struct hisi_sas_port { struct hisi_sas_cq { struct hisi_hba *hisi_hba; + const struct cpumask *pci_irq_mask; struct tasklet_struct tasklet; int rd_point; int id; @@ -187,7 +196,7 @@ struct hisi_sas_device { enum sas_device_type dev_type; int device_id; int sata_idx; - u8 dev_status; + spinlock_t lock; /* For protecting slots */ }; struct hisi_sas_tmf_task { @@ -203,12 +212,14 @@ struct hisi_sas_slot { struct sas_task *task; struct hisi_sas_port *port; u64 n_elem; + u64 n_elem_dif; int dlvry_queue; int dlvry_queue_slot; int cmplt_queue; int cmplt_queue_slot; int abort; int ready; + int device_id; void *cmd_hdr; dma_addr_t cmd_hdr_dma; struct timer_list internal_abort_timer; @@ -220,6 +231,24 @@ struct hisi_sas_slot { u16 idx; }; +#define HISI_SAS_DEBUGFS_REG(x) {#x, x} + +struct hisi_sas_debugfs_reg_lu { + char *name; + int off; +}; + +struct hisi_sas_debugfs_reg { + const struct hisi_sas_debugfs_reg_lu *lu; + int count; + int base_off; + union { + u32 (*read_global_reg)(struct hisi_hba *hisi_hba, u32 off); + u32 (*read_port_reg)(struct hisi_hba *hisi_hba, int port, + u32 off); + }; +}; + struct hisi_sas_hw { int (*hw_init)(struct hisi_hba *hisi_hba); void (*setup_itct)(struct hisi_hba *hisi_hba, @@ -227,7 +256,7 @@ struct hisi_sas_hw { int (*slot_index_alloc)(struct hisi_hba *hisi_hba, struct domain_device *device); struct hisi_sas_device *(*alloc_dev)(struct domain_device *device); - void (*sl_notify)(struct hisi_hba *hisi_hba, int phy_no); + void (*sl_notify_ssp)(struct hisi_hba *hisi_hba, int phy_no); int (*get_free_slot)(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq); void (*start_delivery)(struct hisi_sas_dq *dq); void (*prep_ssp)(struct hisi_hba *hisi_hba, @@ -259,11 +288,16 @@ struct hisi_sas_hw { 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); - void (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba, - int delay_ms, int timeout_ms); + int (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba, + int delay_ms, int timeout_ms); + void (*snapshot_prepare)(struct hisi_hba *hisi_hba); + void (*snapshot_restore)(struct hisi_hba *hisi_hba); int max_command_entries; int complete_hdr_size; struct scsi_host_template *sht; + + const struct hisi_sas_debugfs_reg *debugfs_reg_global; + const struct hisi_sas_debugfs_reg *debugfs_reg_port; }; struct hisi_hba { @@ -329,9 +363,25 @@ struct hisi_hba { const struct hisi_sas_hw *hw; /* Low level hw interface */ unsigned long sata_dev_bitmap[BITS_TO_LONGS(HISI_SAS_MAX_DEVICES)]; struct work_struct rst_work; + struct work_struct debugfs_work; u32 phy_state; u32 intr_coal_ticks; /* Time of interrupt coalesce in us */ u32 intr_coal_count; /* Interrupt count to coalesce */ + + int cq_nvecs; + unsigned int *reply_map; + + /* debugfs memories */ + u32 *debugfs_global_reg; + u32 *debugfs_port_reg[HISI_SAS_MAX_PHYS]; + void *debugfs_complete_hdr[HISI_SAS_MAX_QUEUES]; + struct hisi_sas_cmd_hdr *debugfs_cmd_hdr[HISI_SAS_MAX_QUEUES]; + struct hisi_sas_iost *debugfs_iost; + struct hisi_sas_itct *debugfs_itct; + + struct dentry *debugfs_dir; + struct dentry *debugfs_dump_dentry; + bool debugfs_snapshot; }; /* Generic HW DMA host memory structures */ @@ -430,6 +480,11 @@ struct hisi_sas_sge_page { struct hisi_sas_sge sge[HISI_SAS_SGE_PAGE_CNT]; } __aligned(16); +#define HISI_SAS_SGE_DIF_PAGE_CNT SG_CHUNK_SIZE +struct hisi_sas_sge_dif_page { + struct hisi_sas_sge sge[HISI_SAS_SGE_DIF_PAGE_CNT]; +} __aligned(16); + struct hisi_sas_command_table_ssp { struct ssp_frame_hdr hdr; union { @@ -460,9 +515,18 @@ struct hisi_sas_slot_buf_table { struct hisi_sas_sge_page sge_page; }; +struct hisi_sas_slot_dif_buf_table { + struct hisi_sas_slot_buf_table slot_buf; + struct hisi_sas_sge_dif_page sge_dif_page; +}; + extern struct scsi_transport_template *hisi_sas_stt; + +extern bool hisi_sas_debugfs_enable; +extern struct dentry *hisi_sas_debugfs_dir; + extern void hisi_sas_stop_phys(struct hisi_hba *hisi_hba); -extern int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost); +extern int hisi_sas_alloc(struct hisi_hba *hisi_hba); extern void hisi_sas_free(struct hisi_hba *hisi_hba); extern u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction); @@ -487,10 +551,14 @@ 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 void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no); 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); extern u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max); extern void hisi_sas_controller_reset_prepare(struct hisi_hba *hisi_hba); extern void hisi_sas_controller_reset_done(struct hisi_hba *hisi_hba); +extern void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba); +extern void hisi_sas_debugfs_exit(struct hisi_hba *hisi_hba); +extern void hisi_sas_debugfs_work_handler(struct work_struct *work); #endif diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index bc17fa0d8375..13ca5a0bdf6b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -144,7 +144,7 @@ EXPORT_SYMBOL_GPL(hisi_sas_get_ncq_tag); */ u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max) { - u16 rate = 0; + u8 rate = 0; int i; max -= SAS_LINK_RATE_1_5_GBPS; @@ -241,8 +241,9 @@ static void hisi_sas_slot_index_init(struct hisi_hba *hisi_hba) void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, struct hisi_sas_slot *slot) { - struct hisi_sas_dq *dq = &hisi_hba->dq[slot->dlvry_queue]; unsigned long flags; + int device_id = slot->device_id; + struct hisi_sas_device *sas_dev = &hisi_hba->devices[device_id]; if (task) { struct device *dev = hisi_hba->dev; @@ -252,17 +253,24 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, task->lldd_task = NULL; - if (!sas_protocol_ata(task->task_proto)) + if (!sas_protocol_ata(task->task_proto)) { + struct sas_ssp_task *ssp_task = &task->ssp_task; + struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; + if (slot->n_elem) dma_unmap_sg(dev, task->scatter, task->num_scatter, task->data_dir); + if (slot->n_elem_dif) + dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd), + scsi_prot_sg_count(scsi_cmnd), + task->data_dir); + } } - - spin_lock_irqsave(&dq->lock, flags); + spin_lock_irqsave(&sas_dev->lock, flags); list_del_init(&slot->entry); - spin_unlock_irqrestore(&dq->lock, flags); + spin_unlock_irqrestore(&sas_dev->lock, flags); memset(slot, 0, offsetof(struct hisi_sas_slot, buf)); @@ -380,6 +388,59 @@ prep_out: return rc; } +static void hisi_sas_dif_dma_unmap(struct hisi_hba *hisi_hba, + struct sas_task *task, int n_elem_dif) +{ + struct device *dev = hisi_hba->dev; + + if (n_elem_dif) { + struct sas_ssp_task *ssp_task = &task->ssp_task; + struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; + + dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd), + scsi_prot_sg_count(scsi_cmnd), + task->data_dir); + } +} + +static int hisi_sas_dif_dma_map(struct hisi_hba *hisi_hba, + int *n_elem_dif, struct sas_task *task) +{ + struct device *dev = hisi_hba->dev; + struct sas_ssp_task *ssp_task; + struct scsi_cmnd *scsi_cmnd; + int rc; + + if (task->num_scatter) { + ssp_task = &task->ssp_task; + scsi_cmnd = ssp_task->cmd; + + if (scsi_prot_sg_count(scsi_cmnd)) { + *n_elem_dif = dma_map_sg(dev, + scsi_prot_sglist(scsi_cmnd), + scsi_prot_sg_count(scsi_cmnd), + task->data_dir); + + if (!*n_elem_dif) + return -ENOMEM; + + if (*n_elem_dif > HISI_SAS_SGE_DIF_PAGE_CNT) { + dev_err(dev, "task prep: n_elem_dif(%d) too large\n", + *n_elem_dif); + rc = -EINVAL; + goto err_out_dif_dma_unmap; + } + } + } + + return 0; + +err_out_dif_dma_unmap: + dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd), + scsi_prot_sg_count(scsi_cmnd), task->data_dir); + return rc; +} + static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq **dq_pointer, bool is_tmf, struct hisi_sas_tmf_task *tmf, @@ -394,7 +455,7 @@ static int hisi_sas_task_prep(struct sas_task *task, struct asd_sas_port *sas_port = device->port; struct device *dev = hisi_hba->dev; int dlvry_queue_slot, dlvry_queue, rc, slot_idx; - int n_elem = 0, n_elem_req = 0, n_elem_resp = 0; + int n_elem = 0, n_elem_dif = 0, n_elem_req = 0, n_elem_resp = 0; struct hisi_sas_dq *dq; unsigned long flags; int wr_q_index; @@ -410,7 +471,14 @@ static int hisi_sas_task_prep(struct sas_task *task, return -ECOMM; } - *dq_pointer = dq = sas_dev->dq; + if (hisi_hba->reply_map) { + int cpu = raw_smp_processor_id(); + unsigned int dq_index = hisi_hba->reply_map[cpu]; + + *dq_pointer = dq = &hisi_hba->dq[dq_index]; + } else { + *dq_pointer = dq = sas_dev->dq; + } port = to_hisi_sas_port(sas_port); if (port && !port->port_attached) { @@ -427,6 +495,12 @@ static int hisi_sas_task_prep(struct sas_task *task, if (rc < 0) goto prep_out; + if (!sas_protocol_ata(task->task_proto)) { + rc = hisi_sas_dif_dma_map(hisi_hba, &n_elem_dif, task); + if (rc < 0) + goto err_out_dma_unmap; + } + if (hisi_hba->hw->slot_index_alloc) rc = hisi_hba->hw->slot_index_alloc(hisi_hba, device); else { @@ -445,7 +519,7 @@ static int hisi_sas_task_prep(struct sas_task *task, rc = hisi_sas_slot_index_alloc(hisi_hba, scsi_cmnd); } if (rc < 0) - goto err_out_dma_unmap; + goto err_out_dif_dma_unmap; slot_idx = rc; slot = &hisi_hba->slot_info[slot_idx]; @@ -459,13 +533,17 @@ static int hisi_sas_task_prep(struct sas_task *task, } list_add_tail(&slot->delivery, &dq->list); - list_add_tail(&slot->entry, &sas_dev->list); spin_unlock_irqrestore(&dq->lock, flags); + spin_lock_irqsave(&sas_dev->lock, flags); + list_add_tail(&slot->entry, &sas_dev->list); + spin_unlock_irqrestore(&sas_dev->lock, flags); dlvry_queue = dq->id; dlvry_queue_slot = wr_q_index; + slot->device_id = sas_dev->device_id; slot->n_elem = n_elem; + slot->n_elem_dif = n_elem_dif; slot->dlvry_queue = dlvry_queue; slot->dlvry_queue_slot = dlvry_queue_slot; cmd_hdr_base = hisi_hba->cmd_hdr[dlvry_queue]; @@ -509,6 +587,9 @@ static int hisi_sas_task_prep(struct sas_task *task, err_out_tag: hisi_sas_slot_index_free(hisi_hba, slot_idx); +err_out_dif_dma_unmap: + if (!sas_protocol_ata(task->task_proto)) + hisi_sas_dif_dma_unmap(hisi_hba, task, n_elem_dif); err_out_dma_unmap: hisi_sas_dma_unmap(hisi_hba, task, n_elem, n_elem_req, n_elem_resp); @@ -626,11 +707,11 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device) hisi_hba->devices[i].device_id = i; sas_dev = &hisi_hba->devices[i]; - sas_dev->dev_status = HISI_SAS_DEV_NORMAL; sas_dev->dev_type = device->dev_type; sas_dev->hisi_hba = hisi_hba; sas_dev->sas_device = device; sas_dev->dq = dq; + spin_lock_init(&sas_dev->lock); INIT_LIST_HEAD(&hisi_hba->devices[i].list); break; } @@ -778,7 +859,8 @@ static void hisi_sas_phyup_work(struct work_struct *work) struct asd_sas_phy *sas_phy = &phy->sas_phy; int phy_no = sas_phy->id; - hisi_hba->hw->sl_notify(hisi_hba, phy_no); /* This requires a sleep */ + if (phy->identify.target_port_protocols == SAS_PROTOCOL_SSP) + hisi_hba->hw->sl_notify_ssp(hisi_hba, phy_no); hisi_sas_bytes_dmaed(hisi_hba, phy_no); } @@ -808,6 +890,30 @@ bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, } EXPORT_SYMBOL_GPL(hisi_sas_notify_phy_event); +static void hisi_sas_wait_phyup_timedout(struct timer_list *t) +{ + struct hisi_sas_phy *phy = from_timer(phy, t, timer); + struct hisi_hba *hisi_hba = phy->hisi_hba; + struct device *dev = hisi_hba->dev; + int phy_no = phy->sas_phy.id; + + dev_warn(dev, "phy%d wait phyup timeout, issuing link reset\n", phy_no); + hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET); +} + +void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no) +{ + struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; + struct device *dev = hisi_hba->dev; + + if (!timer_pending(&phy->timer)) { + dev_dbg(dev, "phy%d OOB ready\n", phy_no); + phy->timer.expires = jiffies + HISI_SAS_WAIT_PHYUP_TIMEOUT * HZ; + add_timer(&phy->timer); + } +} +EXPORT_SYMBOL_GPL(hisi_sas_phy_oob_ready); + static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) { struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; @@ -836,6 +942,8 @@ static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) INIT_WORK(&phy->works[i], hisi_sas_phye_fns[i]); spin_lock_init(&phy->lock); + + timer_setup(&phy->timer, hisi_sas_wait_phyup_timedout, 0); } static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy) @@ -926,7 +1034,7 @@ static void hisi_sas_dev_gone(struct domain_device *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_INT_ABT_DEV, 0); hisi_sas_dereg_device(hisi_hba, device); @@ -946,7 +1054,7 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags) return hisi_sas_task_exec(task, gfp_flags, 0, NULL); } -static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, +static int hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, struct sas_phy_linkrates *r) { struct sas_phy_linkrates _r; @@ -955,6 +1063,9 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, struct asd_sas_phy *sas_phy = &phy->sas_phy; enum sas_linkrate min, max; + if (r->minimum_linkrate > SAS_LINK_RATE_1_5_GBPS) + return -EINVAL; + if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) { max = sas_phy->phy->maximum_linkrate; min = r->minimum_linkrate; @@ -962,7 +1073,7 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, max = r->maximum_linkrate; min = sas_phy->phy->minimum_linkrate; } else - return; + return -EINVAL; _r.maximum_linkrate = max; _r.minimum_linkrate = min; @@ -974,6 +1085,8 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, msleep(100); hisi_hba->hw->phy_set_linkrate(hisi_hba, phy_no, &_r); hisi_hba->hw->phy_start(hisi_hba, phy_no); + + return 0; } static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, @@ -999,8 +1112,7 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, break; case PHY_FUNC_SET_LINK_RATE: - hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata); - break; + return hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata); case PHY_FUNC_GET_EVENTS: if (hisi_hba->hw->get_events) { hisi_hba->hw->get_events(hisi_hba, phy_no); @@ -1068,7 +1180,7 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, task->task_done = hisi_sas_task_done; task->slow_task->timer.function = hisi_sas_tmf_timedout; - task->slow_task->timer.expires = jiffies + TASK_TIMEOUT*HZ; + task->slow_task->timer.expires = jiffies + TASK_TIMEOUT * HZ; add_timer(&task->slow_task->timer); res = hisi_sas_task_exec(task, GFP_KERNEL, 1, tmf); @@ -1429,6 +1541,9 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) struct Scsi_Host *shost = hisi_hba->shost; int rc; + if (hisi_sas_debugfs_enable && hisi_hba->debugfs_itct) + queue_work(hisi_hba->wq, &hisi_hba->debugfs_work); + if (!hisi_hba->hw->soft_reset) return -1; @@ -1491,7 +1606,6 @@ static int hisi_sas_abort_task(struct sas_task *task) task->task_state_flags |= SAS_TASK_STATE_ABORTED; spin_unlock_irqrestore(&task->task_state_lock, flags); - sas_dev->dev_status = HISI_SAS_DEV_EH; if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) { struct scsi_cmnd *cmnd = task->uldd_task; struct hisi_sas_slot *slot = task->lldd_task; @@ -1527,7 +1641,8 @@ static int hisi_sas_abort_task(struct sas_task *task) task->task_proto & SAS_PROTOCOL_STP) { if (task->dev->dev_type == SAS_SATA_DEV) { rc = hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + HISI_SAS_INT_ABT_DEV, + 0); if (rc < 0) { dev_err(dev, "abort task: internal abort failed\n"); goto out; @@ -1542,7 +1657,7 @@ static int hisi_sas_abort_task(struct sas_task *task) struct hisi_sas_cq *cq = &hisi_hba->cq[slot->dlvry_queue]; rc = hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_CMD, tag); + HISI_SAS_INT_ABT_CMD, tag); if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) && task->lldd_task) { /* @@ -1568,7 +1683,7 @@ static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) int rc = TMF_RESP_FUNC_FAILED; rc = hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + 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; @@ -1586,8 +1701,8 @@ static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) static int hisi_sas_clear_aca(struct domain_device *device, u8 *lun) { - int rc = TMF_RESP_FUNC_FAILED; struct hisi_sas_tmf_task tmf_task; + int rc; tmf_task.tmf = TMF_CLEAR_ACA; rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); @@ -1635,17 +1750,12 @@ static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device) 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); struct device *dev = hisi_hba->dev; - int rc = TMF_RESP_FUNC_FAILED; - - if (sas_dev->dev_status != HISI_SAS_DEV_EH) - return TMF_RESP_FUNC_FAILED; - sas_dev->dev_status = HISI_SAS_DEV_NORMAL; + int rc; rc = hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + 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; @@ -1667,7 +1777,6 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) struct device *dev = hisi_hba->dev; int rc = TMF_RESP_FUNC_FAILED; - sas_dev->dev_status = HISI_SAS_DEV_EH; if (dev_is_sata(device)) { struct sas_phy *phy; @@ -1691,7 +1800,7 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) 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); + HISI_SAS_INT_ABT_DEV, 0); if (rc < 0) { dev_err(dev, "lu_reset: internal abort failed\n"); goto out; @@ -1777,7 +1886,7 @@ static int hisi_sas_query_task(struct sas_task *task) static int hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, struct sas_task *task, int abort_flag, - int task_tag) + int task_tag, struct hisi_sas_dq *dq) { struct domain_device *device = task->dev; struct hisi_sas_device *sas_dev = device->lldd_dev; @@ -1786,7 +1895,6 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, struct hisi_sas_slot *slot; struct asd_sas_port *sas_port = device->port; struct hisi_sas_cmd_hdr *cmd_hdr_base; - struct hisi_sas_dq *dq = sas_dev->dq; int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx; unsigned long flags, flags_dq = 0; int wr_q_index; @@ -1816,10 +1924,14 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, } list_add_tail(&slot->delivery, &dq->list); spin_unlock_irqrestore(&dq->lock, flags_dq); + spin_lock_irqsave(&sas_dev->lock, flags); + list_add_tail(&slot->entry, &sas_dev->list); + spin_unlock_irqrestore(&sas_dev->lock, flags); dlvry_queue = dq->id; dlvry_queue_slot = wr_q_index; + slot->device_id = sas_dev->device_id; slot->n_elem = n_elem; slot->dlvry_queue = dlvry_queue; slot->dlvry_queue_slot = dlvry_queue_slot; @@ -1843,7 +1955,6 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, WRITE_ONCE(slot->ready, 1); /* send abort command to the chip */ spin_lock_irqsave(&dq->lock, flags); - list_add_tail(&slot->entry, &sas_dev->list); hisi_hba->hw->start_delivery(dq); spin_unlock_irqrestore(&dq->lock, flags); @@ -1858,18 +1969,19 @@ err_out: } /** - * hisi_sas_internal_task_abort -- execute an internal + * _hisi_sas_internal_task_abort -- execute an internal * abort command for single IO command or a device * @hisi_hba: host controller struct * @device: domain device * @abort_flag: mode of operation, device or single IO * @tag: tag of IO to be aborted (only relevant to single * IO mode) + * @dq: delivery queue for this internal abort command */ static int -hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, - struct domain_device *device, - int abort_flag, int tag) +_hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, + struct domain_device *device, int abort_flag, + int tag, struct hisi_sas_dq *dq) { struct sas_task *task; struct hisi_sas_device *sas_dev = device->lldd_dev; @@ -1893,11 +2005,11 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, task->task_proto = device->tproto; task->task_done = hisi_sas_task_done; task->slow_task->timer.function = hisi_sas_tmf_timedout; - task->slow_task->timer.expires = jiffies + INTERNAL_ABORT_TIMEOUT*HZ; + task->slow_task->timer.expires = jiffies + INTERNAL_ABORT_TIMEOUT * HZ; add_timer(&task->slow_task->timer); res = hisi_sas_internal_abort_task_exec(hisi_hba, sas_dev->device_id, - task, abort_flag, tag); + task, abort_flag, tag, dq); if (res) { del_timer(&task->slow_task->timer); dev_err(dev, "internal task abort: executing internal task failed: %d\n", @@ -1923,6 +2035,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, slot->task = NULL; } dev_err(dev, "internal task abort: timeout and not done.\n"); + res = -EIO; goto exit; } else @@ -1953,6 +2066,46 @@ exit: return res; } +static int +hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, + struct domain_device *device, + int abort_flag, int tag) +{ + struct hisi_sas_slot *slot; + struct device *dev = hisi_hba->dev; + struct hisi_sas_dq *dq; + int i, rc; + + switch (abort_flag) { + case HISI_SAS_INT_ABT_CMD: + slot = &hisi_hba->slot_info[tag]; + dq = &hisi_hba->dq[slot->dlvry_queue]; + return _hisi_sas_internal_task_abort(hisi_hba, device, + abort_flag, tag, dq); + case HISI_SAS_INT_ABT_DEV: + for (i = 0; i < hisi_hba->cq_nvecs; i++) { + struct hisi_sas_cq *cq = &hisi_hba->cq[i]; + const struct cpumask *mask = cq->pci_irq_mask; + + if (mask && !cpumask_intersects(cpu_online_mask, mask)) + continue; + dq = &hisi_hba->dq[i]; + rc = _hisi_sas_internal_task_abort(hisi_hba, device, + abort_flag, tag, + dq); + if (rc) + return rc; + } + break; + default: + dev_err(dev, "Unrecognised internal abort flag (%d)\n", + abort_flag); + return -EINVAL; + } + + return 0; +} + static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy) { hisi_sas_port_notify_formed(sas_phy); @@ -2019,7 +2172,7 @@ void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba) { int i; - for (i = 0; i < hisi_hba->queue_count; i++) { + for (i = 0; i < hisi_hba->cq_nvecs; i++) { struct hisi_sas_cq *cq = &hisi_hba->cq[i]; tasklet_kill(&cq->tasklet); @@ -2048,14 +2201,18 @@ static struct sas_domain_function_template hisi_sas_transport_ops = { void hisi_sas_init_mem(struct hisi_hba *hisi_hba) { - int i, s, max_command_entries = hisi_hba->hw->max_command_entries; + int i, s, j, max_command_entries = hisi_hba->hw->max_command_entries; + struct hisi_sas_breakpoint *sata_breakpoint = hisi_hba->sata_breakpoint; for (i = 0; i < hisi_hba->queue_count; i++) { struct hisi_sas_cq *cq = &hisi_hba->cq[i]; struct hisi_sas_dq *dq = &hisi_hba->dq[i]; + struct hisi_sas_cmd_hdr *cmd_hdr = hisi_hba->cmd_hdr[i]; + + s = sizeof(struct hisi_sas_cmd_hdr); + for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++) + memset(&cmd_hdr[j], 0, s); - s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS; - memset(hisi_hba->cmd_hdr[i], 0, s); dq->wr_point = 0; s = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS; @@ -2072,12 +2229,13 @@ void hisi_sas_init_mem(struct hisi_hba *hisi_hba) s = max_command_entries * sizeof(struct hisi_sas_breakpoint); memset(hisi_hba->breakpoint, 0, s); - s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_sata_breakpoint); - memset(hisi_hba->sata_breakpoint, 0, s); + s = sizeof(struct hisi_sas_sata_breakpoint); + for (j = 0; j < HISI_SAS_MAX_ITCT_ENTRIES; j++) + memset(&sata_breakpoint[j], 0, s); } EXPORT_SYMBOL_GPL(hisi_sas_init_mem); -int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) +int hisi_sas_alloc(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; int i, j, s, max_command_entries = hisi_hba->hw->max_command_entries; @@ -2095,7 +2253,6 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { hisi_hba->devices[i].dev_type = SAS_PHY_UNUSED; hisi_hba->devices[i].device_id = i; - hisi_hba->devices[i].dev_status = HISI_SAS_DEV_NORMAL; } for (i = 0; i < hisi_hba->queue_count; i++) { @@ -2131,10 +2288,9 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct); hisi_hba->itct = dmam_alloc_coherent(dev, s, &hisi_hba->itct_dma, - GFP_KERNEL); + GFP_KERNEL | __GFP_ZERO); if (!hisi_hba->itct) goto err_out; - memset(hisi_hba->itct, 0, s); hisi_hba->slot_info = devm_kcalloc(dev, max_command_entries, sizeof(struct hisi_sas_slot), @@ -2144,19 +2300,24 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) /* roundup to avoid overly large block size */ max_command_entries_ru = roundup(max_command_entries, 64); - sz_slot_buf_ru = roundup(sizeof(struct hisi_sas_slot_buf_table), 64); + if (hisi_hba->prot_mask & HISI_SAS_DIX_PROT_MASK) + sz_slot_buf_ru = sizeof(struct hisi_sas_slot_dif_buf_table); + else + sz_slot_buf_ru = sizeof(struct hisi_sas_slot_buf_table); + sz_slot_buf_ru = roundup(sz_slot_buf_ru, 64); s = lcm(max_command_entries_ru, sz_slot_buf_ru); blk_cnt = (max_command_entries_ru * sz_slot_buf_ru) / s; slots_per_blk = s / sz_slot_buf_ru; + for (i = 0; i < blk_cnt; i++) { - struct hisi_sas_slot_buf_table *buf; - dma_addr_t buf_dma; int slot_index = i * slots_per_blk; + dma_addr_t buf_dma; + void *buf; - buf = dmam_alloc_coherent(dev, s, &buf_dma, GFP_KERNEL); + buf = dmam_alloc_coherent(dev, s, &buf_dma, + GFP_KERNEL | __GFP_ZERO); if (!buf) goto err_out; - memset(buf, 0, s); for (j = 0; j < slots_per_blk; j++, slot_index++) { struct hisi_sas_slot *slot; @@ -2166,8 +2327,8 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) slot->buf_dma = buf_dma; slot->idx = slot_index; - buf++; - buf_dma += sizeof(*buf); + buf += sz_slot_buf_ru; + buf_dma += sz_slot_buf_ru; } } @@ -2365,7 +2526,7 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev, goto err_out; } - if (hisi_sas_alloc(hisi_hba, shost)) { + if (hisi_sas_alloc(hisi_hba)) { hisi_sas_free(hisi_hba); goto err_out; } @@ -2461,6 +2622,555 @@ err_out_ha: } EXPORT_SYMBOL_GPL(hisi_sas_probe); +struct dentry *hisi_sas_debugfs_dir; + +static void hisi_sas_debugfs_snapshot_cq_reg(struct hisi_hba *hisi_hba) +{ + int queue_entry_size = hisi_hba->hw->complete_hdr_size; + int i; + + for (i = 0; i < hisi_hba->queue_count; i++) + memcpy(hisi_hba->debugfs_complete_hdr[i], + hisi_hba->complete_hdr[i], + HISI_SAS_QUEUE_SLOTS * queue_entry_size); +} + +static void hisi_sas_debugfs_snapshot_dq_reg(struct hisi_hba *hisi_hba) +{ + int queue_entry_size = sizeof(struct hisi_sas_cmd_hdr); + int i; + + for (i = 0; i < hisi_hba->queue_count; i++) { + struct hisi_sas_cmd_hdr *debugfs_cmd_hdr, *cmd_hdr; + int j; + + debugfs_cmd_hdr = hisi_hba->debugfs_cmd_hdr[i]; + cmd_hdr = hisi_hba->cmd_hdr[i]; + + for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++) + memcpy(&debugfs_cmd_hdr[j], &cmd_hdr[j], + queue_entry_size); + } +} + +static void hisi_sas_debugfs_snapshot_port_reg(struct hisi_hba *hisi_hba) +{ + const struct hisi_sas_debugfs_reg *port = + hisi_hba->hw->debugfs_reg_port; + int i, phy_cnt; + u32 offset; + u32 *databuf; + + for (phy_cnt = 0; phy_cnt < hisi_hba->n_phy; phy_cnt++) { + databuf = (u32 *)hisi_hba->debugfs_p |