Add cq interrupt handler and also slot error handler
function.

Signed-off-by: John Garry <john.ga...@huawei.com>
---
 drivers/scsi/hisi_sas/hisi_sas.h       |   4 +
 drivers/scsi/hisi_sas/hisi_sas_main.c  |  46 +++++
 drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 327 +++++++++++++++++++++++++++++++++
 3 files changed, 377 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index 908baf3..976964f 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -347,6 +347,10 @@ void hisi_sas_slot_index_init(struct hisi_hba *hisi_hba);
 void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int i);
 int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags);
 void hisi_sas_wq_process(struct work_struct *work);
+void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task,
+                       struct hisi_sas_slot *slot);
+
+/* hw specific functions */
 extern void start_delivery_v1_hw(struct hisi_hba *hisi_hba);
 extern int get_free_slot_v1_hw(struct hisi_hba *hisi_hba, int *q, int *s);
 extern int prep_ssp_v1_hw(struct hisi_hba *hisi_hba,
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index 3a82262..74084c2 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -60,6 +60,52 @@ void hisi_sas_slot_index_init(struct hisi_hba *hisi_hba)
                hisi_sas_slot_index_clear(hisi_hba, i);
 }
 
+void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba,
+                       struct sas_task *task,
+                       struct hisi_sas_slot *slot)
+{
+       struct device *dev = &hisi_hba->pdev->dev;
+
+       if (!slot->task)
+               return;
+
+       if (!sas_protocol_ata(task->task_proto))
+               if (slot->n_elem)
+                       dma_unmap_sg(dev, task->scatter, slot->n_elem,
+                                    task->data_dir);
+
+       switch (task->task_proto) {
+       case SAS_PROTOCOL_SMP:
+               break;
+
+       case SAS_PROTOCOL_SATA:
+       case SAS_PROTOCOL_STP:
+       case SAS_PROTOCOL_SSP:
+       default:
+               /* do nothing */
+               break;
+       }
+
+       if (slot->command_table)
+               dma_pool_free(hisi_hba->command_table_pool,
+                             slot->command_table, slot->command_table_dma);
+
+       if (slot->status_buffer)
+               dma_pool_free(hisi_hba->status_buffer_pool,
+                             slot->status_buffer, slot->status_buffer_dma);
+
+       if (slot->sge_page)
+               dma_pool_free(hisi_hba->sge_page_pool, slot->sge_page,
+                       slot->sge_page_dma);
+
+       list_del_init(&slot->entry);
+       task->lldd_task = NULL;
+       slot->task = NULL;
+       slot->port = NULL;
+       hisi_sas_slot_index_free(hisi_hba, slot->idx);
+       memset(slot, 0, sizeof(*slot));
+}
+
 
 static int hisi_sas_task_prep_ssp(struct hisi_hba *hisi_hba,
                                  struct hisi_sas_tei *tei, int is_tmf,
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
index 6e54054..4cb0e1b 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
@@ -957,6 +957,257 @@ int prep_ssp_v1_hw(struct hisi_hba *hisi_hba,
        return 0;
 }
 
+/* by default, task resp is complete */
+static void slot_err_v1_hw(struct hisi_hba *hisi_hba,
+                          struct sas_task *task,
+                          struct hisi_sas_slot *slot)
+{
+       struct task_status_struct *tstat = &task->task_status;
+       struct hisi_sas_err_record *err_record = slot->status_buffer;
+       struct device *dev = &hisi_hba->pdev->dev;
+
+       switch (task->task_proto) {
+       case SAS_PROTOCOL_SSP:
+       {
+               int error = -1;
+               u32 dma_err_type = cpu_to_le32(err_record->dma_err_type);
+               u32 dma_tx_err_type = ((dma_err_type &
+                                       ERR_HDR_DMA_TX_ERR_TYPE_MSK)) >>
+                                       ERR_HDR_DMA_TX_ERR_TYPE_OFF;
+               u32 dma_rx_err_type = ((dma_err_type &
+                                       ERR_HDR_DMA_RX_ERR_TYPE_MSK)) >>
+                                       ERR_HDR_DMA_RX_ERR_TYPE_OFF;
+               u32 trans_tx_fail_type =
+                               cpu_to_le32(err_record->trans_tx_fail_type);
+               u32 trans_rx_fail_type =
+                               cpu_to_le32(err_record->trans_rx_fail_type);
+
+               if (dma_tx_err_type) {
+                       /* dma tx err */
+                       error = ffs(dma_tx_err_type)
+                               - 1 + DMA_TX_ERR_BASE;
+               } else if (dma_rx_err_type) {
+                       /* dma rx err */
+                       error = ffs(dma_rx_err_type)
+                               - 1 + DMA_RX_ERR_BASE;
+               } else if (trans_tx_fail_type) {
+                       /* trans tx err */
+                       error = ffs(trans_tx_fail_type)
+                               - 1 + TRANS_TX_FAIL_BASE;
+               } else if (trans_rx_fail_type) {
+                       /* trans rx err */
+                       error = ffs(trans_rx_fail_type)
+                               - 1 + TRANS_RX_FAIL_BASE;
+               }
+
+               switch (error) {
+               case DMA_TX_DATA_UNDERFLOW_ERR:
+               case DMA_RX_DATA_UNDERFLOW_ERR:
+               {
+                       tstat->residual = 0;
+                       tstat->stat = SAS_DATA_UNDERRUN;
+
+                       break;
+               }
+               case DMA_TX_DATA_SGL_OVERFLOW_ERR:
+               case DMA_TX_DIF_SGL_OVERFLOW_ERR:
+               case DMA_TX_XFER_RDY_LENGTH_OVERFLOW_ERR:
+               case DMA_RX_DATA_OVERFLOW_ERR:
+               case TRANS_RX_FRAME_OVERRUN_ERR:
+               case TRANS_RX_LINK_BUF_OVERRUN_ERR:
+               {
+                       tstat->stat = SAS_DATA_OVERRUN;
+                       tstat->residual = 0;
+                       break;
+               }
+               case TRANS_TX_PHY_NOT_ENABLE_ERR:
+               {
+                       tstat->stat = SAS_PHY_DOWN;
+                       tstat->resp = SAS_TASK_UNDELIVERED;
+                       break;
+               }
+               case TRANS_TX_OPEN_REJCT_WRONG_DEST_ERR:
+               case TRANS_TX_OPEN_REJCT_ZONE_VIOLATION_ERR:
+               case TRANS_TX_OPEN_REJCT_BY_OTHER_ERR:
+               case TRANS_TX_OPEN_REJCT_AIP_TIMEOUT_ERR:
+               case TRANS_TX_OPEN_REJCT_STP_BUSY_ERR:
+               case TRANS_TX_OPEN_REJCT_PROTOCOL_NOT_SUPPORT_ERR:
+               case TRANS_TX_OPEN_REJCT_RATE_NOT_SUPPORT_ERR:
+               case TRANS_TX_OPEN_REJCT_BAD_DEST_ERR:
+               case TRANS_TX_OPEN_BREAK_RECEIVE_ERR:
+               case TRANS_TX_OPEN_REJCT_PATHWAY_BLOCKED_ERR:
+               case TRANS_TX_OPEN_REJCT_NO_DEST_ERR:
+               case TRANS_TX_OPEN_RETRY_ERR:
+               {
+                       tstat->stat = SAS_OPEN_REJECT;
+                       tstat->open_rej_reason = SAS_OREJ_UNKNOWN;
+                       break;
+               }
+               case TRANS_TX_OPEN_TIMEOUT_ERR:
+               {
+                       tstat->stat = SAS_OPEN_TO;
+                       break;
+               }
+               case TRANS_TX_NAK_RECEIVE_ERR:
+               case TRANS_TX_ACK_NAK_TIMEOUT_ERR:
+               {
+                       tstat->stat = SAS_NAK_R_ERR;
+                       break;
+               }
+               default:
+               {
+                       tstat->stat = SAM_STAT_CHECK_CONDITION;
+                       break;
+               }
+               }
+       }
+               break;
+       case SAS_PROTOCOL_SMP:
+       case SAS_PROTOCOL_SATA:
+       case SAS_PROTOCOL_STP:
+       case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+       {
+               dev_err(dev, "slot err: SMP/SATA/STP not supported");
+       }
+               break;
+       default:
+               break;
+       }
+
+}
+
+int slot_complete_v1_hw(struct hisi_hba *hisi_hba,
+                       struct hisi_sas_slot *slot,
+                       int abort)
+{
+       struct sas_task *task = slot->task;
+       struct hisi_sas_device *sas_dev;
+       struct device *dev = &hisi_hba->pdev->dev;
+       struct task_status_struct *tstat;
+       struct domain_device *device;
+       enum exec_status sts;
+       struct hisi_sas_complete_hdr *complete_queue =
+                       hisi_hba->complete_hdr[slot->cmplt_queue];
+       struct hisi_sas_complete_hdr *complete_hdr;
+       u32 cmplt_hdr_data;
+
+       complete_hdr = &complete_queue[slot->cmplt_queue_slot];
+       cmplt_hdr_data = le32_to_cpu(complete_hdr->data);
+
+       if (unlikely(!task || !task->lldd_task || !task->dev))
+               return -1;
+
+       tstat = &task->task_status;
+       device = task->dev;
+       sas_dev = device->lldd_dev;
+
+       task->task_state_flags &=
+               ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
+       task->task_state_flags |= SAS_TASK_STATE_DONE;
+
+       memset(tstat, 0, sizeof(*tstat));
+       tstat->resp = SAS_TASK_COMPLETE;
+
+       if (unlikely(!sas_dev || abort)) {
+               if (!sas_dev)
+                       dev_dbg(dev, "slot complete: port has not device\n");
+               tstat->stat = SAS_PHY_DOWN;
+               goto out;
+       }
+
+       if (cmplt_hdr_data & CMPLT_HDR_IO_CFG_ERR_MSK) {
+               u32 info_reg = hisi_sas_read32(hisi_hba, HGC_INVLD_DQE_INFO);
+
+               if (info_reg & HGC_INVLD_DQE_INFO_DQ_MSK)
+                       dev_err(dev, "slot complete: [%d:%d] has dq IPTT err",
+                               slot->cmplt_queue, slot->cmplt_queue_slot);
+
+               if (info_reg & HGC_INVLD_DQE_INFO_TYPE_MSK)
+                       dev_err(dev, "slot complete: [%d:%d] has dq type err",
+                               slot->cmplt_queue, slot->cmplt_queue_slot);
+
+               if (info_reg & HGC_INVLD_DQE_INFO_FORCE_MSK)
+                       dev_err(dev, "slot complete: [%d:%d] has dq force phy 
err",
+                               slot->cmplt_queue, slot->cmplt_queue_slot);
+
+               if (info_reg & HGC_INVLD_DQE_INFO_PHY_MSK)
+                       dev_err(dev, "slot complete: [%d:%d] has dq phy id err",
+                               slot->cmplt_queue, slot->cmplt_queue_slot);
+
+               if (info_reg & HGC_INVLD_DQE_INFO_ABORT_MSK)
+                       dev_err(dev, "slot complete: [%d:%d] has dq abort flag 
err",
+                               slot->cmplt_queue, slot->cmplt_queue_slot);
+
+               if (info_reg & HGC_INVLD_DQE_INFO_IPTT_OF_MSK)
+                       dev_err(dev, "slot complete: [%d:%d] has dq IPTT or ICT 
err",
+                               slot->cmplt_queue, slot->cmplt_queue_slot);
+
+               if (info_reg & HGC_INVLD_DQE_INFO_SSP_ERR_MSK)
+                       dev_err(dev, "slot complete: [%d:%d] has dq SSP frame 
type err",
+                               slot->cmplt_queue, slot->cmplt_queue_slot);
+
+               if (info_reg & HGC_INVLD_DQE_INFO_OFL_MSK)
+                       dev_err(dev, "slot complete: [%d:%d] has dq order frame 
len err",
+                               slot->cmplt_queue, slot->cmplt_queue_slot);
+
+               tstat->resp = SAS_TASK_UNDELIVERED;
+               tstat->stat = SAS_OPEN_REJECT;
+               tstat->open_rej_reason = SAS_OREJ_UNKNOWN;
+               goto out;
+       }
+
+       if (!(cmplt_hdr_data & CMPLT_HDR_ERR_RCRD_XFRD_MSK)) {
+               if (!(cmplt_hdr_data & CMPLT_HDR_CMD_CMPLT_MSK) ||
+                   !(cmplt_hdr_data & CMPLT_HDR_RSPNS_XFRD_MSK)) {
+                       tstat->stat = SAS_DATA_OVERRUN;
+                       goto out;
+               }
+       } else {
+               slot_err_v1_hw(hisi_hba, task, slot);
+               goto out;
+       }
+
+       switch (task->task_proto) {
+       case SAS_PROTOCOL_SSP:
+       {
+               struct ssp_response_iu *iu = slot->status_buffer +
+                       sizeof(struct hisi_sas_err_record);
+               sas_ssp_task_response(dev, task, iu);
+               break;
+       }
+       case SAS_PROTOCOL_SMP:
+               dev_err(dev, "slot complete: SMP not supported");
+               break;
+       case SAS_PROTOCOL_SATA:
+       case SAS_PROTOCOL_STP:
+       case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+               dev_err(dev, "slot complete: SATA/STP not supported");
+               break;
+
+       default:
+               tstat->stat = SAM_STAT_CHECK_CONDITION;
+               break;
+       }
+
+       if (!slot->port->port_attached) {
+               dev_err(dev, "slot complete: port %d has removed\n",
+                       slot->port->sas_port.id);
+               tstat->stat = SAS_PHY_DOWN;
+       }
+
+out:
+       if (sas_dev && sas_dev->running_req)
+               sas_dev->running_req--;
+
+       hisi_sas_slot_task_free(hisi_hba, task, slot);
+       sts = tstat->stat;
+
+       if (task->task_done)
+               task->task_done(task);
+
+       return sts;
+}
+
 /* Interrupts */
 static irqreturn_t int_phyup_v1_hw(int irq_no, void *p)
 {
@@ -1051,10 +1302,63 @@ end:
 }
 
 
+
+static irqreturn_t cq_interrupt_v1_hw(int irq, void *p)
+{
+       struct hisi_sas_cq *cq = p;
+       struct hisi_hba *hisi_hba = cq->hisi_hba;
+       struct hisi_sas_slot *slot;
+       int queue = cq->id;
+       struct hisi_sas_complete_hdr *complete_queue =
+                       (struct hisi_sas_complete_hdr *)
+                       hisi_hba->complete_hdr[queue];
+       u32 irq_value;
+       u32 rd_point, wr_point;
+
+       irq_value = hisi_sas_read32(hisi_hba, OQ_INT_SRC);
+
+       hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue);
+
+       rd_point = hisi_sas_read32(hisi_hba,
+                       COMPL_Q_0_RD_PTR + (0x14 * queue));
+       wr_point = hisi_sas_read32(hisi_hba,
+                       COMPL_Q_0_WR_PTR + (0x14 * queue));
+
+       while (rd_point != wr_point) {
+               struct hisi_sas_complete_hdr *complete_hdr;
+               int iptt, slot_idx;
+               u32 cmplt_hdr_data;
+
+               complete_hdr = &complete_queue[rd_point];
+               cmplt_hdr_data = cpu_to_le32(complete_hdr->data);
+               iptt = (cmplt_hdr_data & CMPLT_HDR_IPTT_MSK) >>
+                       CMPLT_HDR_IPTT_OFF;
+               slot_idx = iptt;
+               slot = &hisi_hba->slot_info[slot_idx];
+
+               /* The completion queue and queue slot index are not
+                * necessarily the same as the delivery queue and
+                * queue slot index.
+                */
+               slot->cmplt_queue_slot = rd_point;
+               slot->cmplt_queue = queue;
+               slot_complete_v1_hw(hisi_hba, slot, 0);
+
+               if (++rd_point >= HISI_SAS_QUEUE_SLOTS)
+                       rd_point = 0;
+       }
+
+       /* update rd_point */
+       hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point);
+
+       return IRQ_HANDLED;
+}
+
 static const char phy_int_names[HISI_SAS_PHY_INT_NR][32] = {
        {"Phy Up"},
 };
 
+static const char cq_int_name[32] = "cq";
 
 static irq_handler_t phy_interrupts[HISI_SAS_PHY_INT_NR] = {
        int_phyup_v1_hw,
@@ -1099,6 +1403,29 @@ int interrupt_init_v1_hw(struct hisi_hba *hisi_hba)
                }
        }
 
+       for (i = 0; i < hisi_hba->queue_count; i++) {
+               int idx = (hisi_hba->n_phy * HISI_SAS_PHY_INT_NR) + i;
+
+               irq = irq_of_parse_and_map(np, idx);
+               if (!irq) {
+                       dev_err(dev, "irq init: [%d] could not map cq interrupt 
%d\n",
+                               hisi_hba->id, idx);
+                       return -ENOENT;
+               }
+               (void)snprintf(&int_names[idx * HISI_SAS_NAME_LEN],
+                               HISI_SAS_NAME_LEN,
+                               DRV_NAME" %s [%d %d]", cq_int_name, id, i);
+               rc = devm_request_irq(dev, irq, cq_interrupt_v1_hw, 0,
+                                     &int_names[idx * HISI_SAS_NAME_LEN],
+                                     &hisi_hba->cq[i]);
+               if (rc) {
+                       dev_err(dev, "irq init: [%d] could not request cq 
interrupt %d, rc=%d\n",
+                               hisi_hba->id, irq, rc);
+                       return -ENOENT;
+               }
+               idx++;
+       }
+
 
        return 0;
 }
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to