diff options
author | John Garry <john.garry@huawei.com> | 2016-01-25 13:47:17 -0500 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2016-02-23 21:27:02 -0500 |
commit | 8c36e31d6eae19c27ab848909479bff6d187fa05 (patch) | |
tree | dc42a8a33e36c30854c9d65b82e343dfabaeb1af /drivers/scsi | |
parent | 31a9cfa6f739e532d6e73fa446adf470e58033de (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.c | 185 |
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 | |||
267 | static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off) | 272 | static 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 | ||
279 | static 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 | |||
274 | static void hisi_sas_write32(struct hisi_hba *hisi_hba, u32 off, u32 val) | 286 | static 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 | */ | ||
674 | static 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 | |||
701 | static 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 | |||
710 | static 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 | |||
747 | static 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 | |||
655 | static int | 837 | static int |
656 | slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, | 838 | slot_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), |