aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorJohn Garry <john.garry@huawei.com>2016-01-25 13:47:17 -0500
committerMartin K. Petersen <martin.petersen@oracle.com>2016-02-23 21:27:02 -0500
commit8c36e31d6eae19c27ab848909479bff6d187fa05 (patch)
treedc42a8a33e36c30854c9d65b82e343dfabaeb1af /drivers/scsi
parent31a9cfa6f739e532d6e73fa446adf470e58033de (diff)
hisi_sas: add v2 path to send ssp frame
Include code to prep ssp frame and deliver to hardware. Signed-off-by: John Garry <john.garry@huawei.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v2_hw.c185
1 files changed, 185 insertions, 0 deletions
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
index 099bc13db741..ab6ea25a1e27 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -264,6 +264,11 @@ enum {
264 264
265#define HISI_SAS_COMMAND_ENTRIES_V2_HW 4096 265#define HISI_SAS_COMMAND_ENTRIES_V2_HW 4096
266 266
267#define DIR_NO_DATA 0
268#define DIR_TO_INI 1
269#define DIR_TO_DEVICE 2
270#define DIR_RESERVED 3
271
267static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off) 272static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
268{ 273{
269 void __iomem *regs = hisi_hba->regs + off; 274 void __iomem *regs = hisi_hba->regs + off;
@@ -271,6 +276,13 @@ static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
271 return readl(regs); 276 return readl(regs);
272} 277}
273 278
279static u32 hisi_sas_read32_relaxed(struct hisi_hba *hisi_hba, u32 off)
280{
281 void __iomem *regs = hisi_hba->regs + off;
282
283 return readl_relaxed(regs);
284}
285
274static void hisi_sas_write32(struct hisi_hba *hisi_hba, u32 off, u32 val) 286static void hisi_sas_write32(struct hisi_hba *hisi_hba, u32 off, u32 val)
275{ 287{
276 void __iomem *regs = hisi_hba->regs + off; 288 void __iomem *regs = hisi_hba->regs + off;
@@ -652,6 +664,176 @@ static int get_wideport_bitmap_v2_hw(struct hisi_hba *hisi_hba, int port_id)
652 return bitmap; 664 return bitmap;
653} 665}
654 666
667/**
668 * This function allocates across all queues to load balance.
669 * Slots are allocated from queues in a round-robin fashion.
670 *
671 * The callpath to this function and upto writing the write
672 * queue pointer should be safe from interruption.
673 */
674static int get_free_slot_v2_hw(struct hisi_hba *hisi_hba, int *q, int *s)
675{
676 struct device *dev = &hisi_hba->pdev->dev;
677 u32 r, w;
678 int queue = hisi_hba->queue;
679
680 while (1) {
681 w = hisi_sas_read32_relaxed(hisi_hba,
682 DLVRY_Q_0_WR_PTR + (queue * 0x14));
683 r = hisi_sas_read32_relaxed(hisi_hba,
684 DLVRY_Q_0_RD_PTR + (queue * 0x14));
685 if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) {
686 queue = (queue + 1) % hisi_hba->queue_count;
687 if (queue == hisi_hba->queue) {
688 dev_warn(dev, "could not find free slot\n");
689 return -EAGAIN;
690 }
691 continue;
692 }
693 break;
694 }
695 hisi_hba->queue = (queue + 1) % hisi_hba->queue_count;
696 *q = queue;
697 *s = w;
698 return 0;
699}
700
701static void start_delivery_v2_hw(struct hisi_hba *hisi_hba)
702{
703 int dlvry_queue = hisi_hba->slot_prep->dlvry_queue;
704 int dlvry_queue_slot = hisi_hba->slot_prep->dlvry_queue_slot;
705
706 hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14),
707 ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS);
708}
709
710static int prep_prd_sge_v2_hw(struct hisi_hba *hisi_hba,
711 struct hisi_sas_slot *slot,
712 struct hisi_sas_cmd_hdr *hdr,
713 struct scatterlist *scatter,
714 int n_elem)
715{
716 struct device *dev = &hisi_hba->pdev->dev;
717 struct scatterlist *sg;
718 int i;
719
720 if (n_elem > HISI_SAS_SGE_PAGE_CNT) {
721 dev_err(dev, "prd err: n_elem(%d) > HISI_SAS_SGE_PAGE_CNT",
722 n_elem);
723 return -EINVAL;
724 }
725
726 slot->sge_page = dma_pool_alloc(hisi_hba->sge_page_pool, GFP_ATOMIC,
727 &slot->sge_page_dma);
728 if (!slot->sge_page)
729 return -ENOMEM;
730
731 for_each_sg(scatter, sg, n_elem, i) {
732 struct hisi_sas_sge *entry = &slot->sge_page->sge[i];
733
734 entry->addr = cpu_to_le64(sg_dma_address(sg));
735 entry->page_ctrl_0 = entry->page_ctrl_1 = 0;
736 entry->data_len = cpu_to_le32(sg_dma_len(sg));
737 entry->data_off = 0;
738 }
739
740 hdr->prd_table_addr = cpu_to_le64(slot->sge_page_dma);
741
742 hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF);
743
744 return 0;
745}
746
747static int prep_ssp_v2_hw(struct hisi_hba *hisi_hba,
748 struct hisi_sas_slot *slot, int is_tmf,
749 struct hisi_sas_tmf_task *tmf)
750{
751 struct sas_task *task = slot->task;
752 struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
753 struct domain_device *device = task->dev;
754 struct hisi_sas_device *sas_dev = device->lldd_dev;
755 struct hisi_sas_port *port = slot->port;
756 struct sas_ssp_task *ssp_task = &task->ssp_task;
757 struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
758 int has_data = 0, rc, priority = is_tmf;
759 u8 *buf_cmd;
760 u32 dw1 = 0, dw2 = 0;
761
762 hdr->dw0 = cpu_to_le32((1 << CMD_HDR_RESP_REPORT_OFF) |
763 (2 << CMD_HDR_TLR_CTRL_OFF) |
764 (port->id << CMD_HDR_PORT_OFF) |
765 (priority << CMD_HDR_PRIORITY_OFF) |
766 (1 << CMD_HDR_CMD_OFF)); /* ssp */
767
768 dw1 = 1 << CMD_HDR_VDTL_OFF;
769 if (is_tmf) {
770 dw1 |= 2 << CMD_HDR_FRAME_TYPE_OFF;
771 dw1 |= DIR_NO_DATA << CMD_HDR_DIR_OFF;
772 } else {
773 dw1 |= 1 << CMD_HDR_FRAME_TYPE_OFF;
774 switch (scsi_cmnd->sc_data_direction) {
775 case DMA_TO_DEVICE:
776 has_data = 1;
777 dw1 |= DIR_TO_DEVICE << CMD_HDR_DIR_OFF;
778 break;
779 case DMA_FROM_DEVICE:
780 has_data = 1;
781 dw1 |= DIR_TO_INI << CMD_HDR_DIR_OFF;
782 break;
783 default:
784 dw1 &= ~CMD_HDR_DIR_MSK;
785 }
786 }
787
788 /* map itct entry */
789 dw1 |= sas_dev->device_id << CMD_HDR_DEV_ID_OFF;
790 hdr->dw1 = cpu_to_le32(dw1);
791
792 dw2 = (((sizeof(struct ssp_command_iu) + sizeof(struct ssp_frame_hdr)
793 + 3) / 4) << CMD_HDR_CFL_OFF) |
794 ((HISI_SAS_MAX_SSP_RESP_SZ / 4) << CMD_HDR_MRFL_OFF) |
795 (2 << CMD_HDR_SG_MOD_OFF);
796 hdr->dw2 = cpu_to_le32(dw2);
797
798 hdr->transfer_tags = cpu_to_le32(slot->idx);
799
800 if (has_data) {
801 rc = prep_prd_sge_v2_hw(hisi_hba, slot, hdr, task->scatter,
802 slot->n_elem);
803 if (rc)
804 return rc;
805 }
806
807 hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len);
808 hdr->cmd_table_addr = cpu_to_le64(slot->command_table_dma);
809 hdr->sts_buffer_addr = cpu_to_le64(slot->status_buffer_dma);
810
811 buf_cmd = slot->command_table + sizeof(struct ssp_frame_hdr);
812
813 memcpy(buf_cmd, &task->ssp_task.LUN, 8);
814 if (!is_tmf) {
815 buf_cmd[9] = task->ssp_task.task_attr |
816 (task->ssp_task.task_prio << 3);
817 memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd,
818 task->ssp_task.cmd->cmd_len);
819 } else {
820 buf_cmd[10] = tmf->tmf;
821 switch (tmf->tmf) {
822 case TMF_ABORT_TASK:
823 case TMF_QUERY_TASK:
824 buf_cmd[12] =
825 (tmf->tag_of_task_to_be_managed >> 8) & 0xff;
826 buf_cmd[13] =
827 tmf->tag_of_task_to_be_managed & 0xff;
828 break;
829 default:
830 break;
831 }
832 }
833
834 return 0;
835}
836
655static int 837static int
656slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, 838slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot,
657 int abort) 839 int abort)
@@ -1213,6 +1395,9 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = {
1213 .hw_init = hisi_sas_v2_init, 1395 .hw_init = hisi_sas_v2_init,
1214 .sl_notify = sl_notify_v2_hw, 1396 .sl_notify = sl_notify_v2_hw,
1215 .get_wideport_bitmap = get_wideport_bitmap_v2_hw, 1397 .get_wideport_bitmap = get_wideport_bitmap_v2_hw,
1398 .prep_ssp = prep_ssp_v2_hw,
1399 .get_free_slot = get_free_slot_v2_hw,
1400 .start_delivery = start_delivery_v2_hw,
1216 .slot_complete = slot_complete_v2_hw, 1401 .slot_complete = slot_complete_v2_hw,
1217 .max_command_entries = HISI_SAS_COMMAND_ENTRIES_V2_HW, 1402 .max_command_entries = HISI_SAS_COMMAND_ENTRIES_V2_HW,
1218 .complete_hdr_size = sizeof(struct hisi_sas_complete_v2_hdr), 1403 .complete_hdr_size = sizeof(struct hisi_sas_complete_v2_hdr),