diff options
author | Eric Long <eric.long@spreadtrum.com> | 2018-08-28 07:09:07 -0400 |
---|---|---|
committer | Vinod Koul <vkoul@kernel.org> | 2018-09-03 07:28:50 -0400 |
commit | 4ac695464763ecf696eaba563eff1c2ab994f6d8 (patch) | |
tree | bc0320ce3728e6813d598b65af02aca274ee6a95 | |
parent | 5b394b2ddf0347bef56e50c69a58773c94343ff3 (diff) |
dmaengine: sprd: Support DMA link-list mode
The Spreadtrum DMA can support the link-list transaction mode, which means
DMA controller can do transaction one by one automatically once we linked
these transaction by link-list register.
Signed-off-by: Eric Long <eric.long@spreadtrum.com>
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
-rw-r--r-- | drivers/dma/sprd-dma.c | 81 | ||||
-rw-r--r-- | include/linux/dma/sprd-dma.h | 69 |
2 files changed, 143 insertions, 7 deletions
diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 55df0d41355b..38d4e4f07c66 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c | |||
@@ -68,6 +68,7 @@ | |||
68 | 68 | ||
69 | /* SPRD_DMA_CHN_CFG register definition */ | 69 | /* SPRD_DMA_CHN_CFG register definition */ |
70 | #define SPRD_DMA_CHN_EN BIT(0) | 70 | #define SPRD_DMA_CHN_EN BIT(0) |
71 | #define SPRD_DMA_LINKLIST_EN BIT(4) | ||
71 | #define SPRD_DMA_WAIT_BDONE_OFFSET 24 | 72 | #define SPRD_DMA_WAIT_BDONE_OFFSET 24 |
72 | #define SPRD_DMA_DONOT_WAIT_BDONE 1 | 73 | #define SPRD_DMA_DONOT_WAIT_BDONE 1 |
73 | 74 | ||
@@ -103,7 +104,7 @@ | |||
103 | #define SPRD_DMA_REQ_MODE_MASK GENMASK(1, 0) | 104 | #define SPRD_DMA_REQ_MODE_MASK GENMASK(1, 0) |
104 | #define SPRD_DMA_FIX_SEL_OFFSET 21 | 105 | #define SPRD_DMA_FIX_SEL_OFFSET 21 |
105 | #define SPRD_DMA_FIX_EN_OFFSET 20 | 106 | #define SPRD_DMA_FIX_EN_OFFSET 20 |
106 | #define SPRD_DMA_LLIST_END_OFFSET 19 | 107 | #define SPRD_DMA_LLIST_END BIT(19) |
107 | #define SPRD_DMA_FRG_LEN_MASK GENMASK(16, 0) | 108 | #define SPRD_DMA_FRG_LEN_MASK GENMASK(16, 0) |
108 | 109 | ||
109 | /* SPRD_DMA_CHN_BLK_LEN register definition */ | 110 | /* SPRD_DMA_CHN_BLK_LEN register definition */ |
@@ -164,6 +165,7 @@ struct sprd_dma_desc { | |||
164 | struct sprd_dma_chn { | 165 | struct sprd_dma_chn { |
165 | struct virt_dma_chan vc; | 166 | struct virt_dma_chan vc; |
166 | void __iomem *chn_base; | 167 | void __iomem *chn_base; |
168 | struct sprd_dma_linklist linklist; | ||
167 | struct dma_slave_config slave_cfg; | 169 | struct dma_slave_config slave_cfg; |
168 | u32 chn_num; | 170 | u32 chn_num; |
169 | u32 dev_id; | 171 | u32 dev_id; |
@@ -582,7 +584,8 @@ static int sprd_dma_get_step(enum dma_slave_buswidth buswidth) | |||
582 | } | 584 | } |
583 | 585 | ||
584 | static int sprd_dma_fill_desc(struct dma_chan *chan, | 586 | static int sprd_dma_fill_desc(struct dma_chan *chan, |
585 | struct sprd_dma_desc *sdesc, | 587 | struct sprd_dma_chn_hw *hw, |
588 | unsigned int sglen, int sg_index, | ||
586 | dma_addr_t src, dma_addr_t dst, u32 len, | 589 | dma_addr_t src, dma_addr_t dst, u32 len, |
587 | enum dma_transfer_direction dir, | 590 | enum dma_transfer_direction dir, |
588 | unsigned long flags, | 591 | unsigned long flags, |
@@ -590,7 +593,6 @@ static int sprd_dma_fill_desc(struct dma_chan *chan, | |||
590 | { | 593 | { |
591 | struct sprd_dma_dev *sdev = to_sprd_dma_dev(chan); | 594 | struct sprd_dma_dev *sdev = to_sprd_dma_dev(chan); |
592 | struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); | 595 | struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); |
593 | struct sprd_dma_chn_hw *hw = &sdesc->chn_hw; | ||
594 | u32 req_mode = (flags >> SPRD_DMA_REQ_SHIFT) & SPRD_DMA_REQ_MODE_MASK; | 596 | u32 req_mode = (flags >> SPRD_DMA_REQ_SHIFT) & SPRD_DMA_REQ_MODE_MASK; |
595 | u32 int_mode = flags & SPRD_DMA_INT_MASK; | 597 | u32 int_mode = flags & SPRD_DMA_INT_MASK; |
596 | int src_datawidth, dst_datawidth, src_step, dst_step; | 598 | int src_datawidth, dst_datawidth, src_step, dst_step; |
@@ -670,12 +672,52 @@ static int sprd_dma_fill_desc(struct dma_chan *chan, | |||
670 | temp |= (src_step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_SRC_TRSF_STEP_OFFSET; | 672 | temp |= (src_step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_SRC_TRSF_STEP_OFFSET; |
671 | hw->trsf_step = temp; | 673 | hw->trsf_step = temp; |
672 | 674 | ||
675 | /* link-list configuration */ | ||
676 | if (schan->linklist.phy_addr) { | ||
677 | if (sg_index == sglen - 1) | ||
678 | hw->frg_len |= SPRD_DMA_LLIST_END; | ||
679 | |||
680 | hw->cfg |= SPRD_DMA_LINKLIST_EN; | ||
681 | |||
682 | /* link-list index */ | ||
683 | temp = (sg_index + 1) % sglen; | ||
684 | /* Next link-list configuration's physical address offset */ | ||
685 | temp = temp * sizeof(*hw) + SPRD_DMA_CHN_SRC_ADDR; | ||
686 | /* | ||
687 | * Set the link-list pointer point to next link-list | ||
688 | * configuration's physical address. | ||
689 | */ | ||
690 | hw->llist_ptr = schan->linklist.phy_addr + temp; | ||
691 | } else { | ||
692 | hw->llist_ptr = 0; | ||
693 | } | ||
694 | |||
673 | hw->frg_step = 0; | 695 | hw->frg_step = 0; |
674 | hw->src_blk_step = 0; | 696 | hw->src_blk_step = 0; |
675 | hw->des_blk_step = 0; | 697 | hw->des_blk_step = 0; |
676 | return 0; | 698 | return 0; |
677 | } | 699 | } |
678 | 700 | ||
701 | static int sprd_dma_fill_linklist_desc(struct dma_chan *chan, | ||
702 | unsigned int sglen, int sg_index, | ||
703 | dma_addr_t src, dma_addr_t dst, u32 len, | ||
704 | enum dma_transfer_direction dir, | ||
705 | unsigned long flags, | ||
706 | struct dma_slave_config *slave_cfg) | ||
707 | { | ||
708 | struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); | ||
709 | struct sprd_dma_chn_hw *hw; | ||
710 | |||
711 | if (!schan->linklist.virt_addr) | ||
712 | return -EINVAL; | ||
713 | |||
714 | hw = (struct sprd_dma_chn_hw *)(schan->linklist.virt_addr + | ||
715 | sg_index * sizeof(*hw)); | ||
716 | |||
717 | return sprd_dma_fill_desc(chan, hw, sglen, sg_index, src, dst, len, | ||
718 | dir, flags, slave_cfg); | ||
719 | } | ||
720 | |||
679 | static struct dma_async_tx_descriptor * | 721 | static struct dma_async_tx_descriptor * |
680 | sprd_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, | 722 | sprd_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, |
681 | size_t len, unsigned long flags) | 723 | size_t len, unsigned long flags) |
@@ -744,10 +786,20 @@ sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
744 | u32 len = 0; | 786 | u32 len = 0; |
745 | int ret, i; | 787 | int ret, i; |
746 | 788 | ||
747 | /* TODO: now we only support one sg for each DMA configuration. */ | 789 | if (!is_slave_direction(dir)) |
748 | if (!is_slave_direction(dir) || sglen > 1) | ||
749 | return NULL; | 790 | return NULL; |
750 | 791 | ||
792 | if (context) { | ||
793 | struct sprd_dma_linklist *ll_cfg = | ||
794 | (struct sprd_dma_linklist *)context; | ||
795 | |||
796 | schan->linklist.phy_addr = ll_cfg->phy_addr; | ||
797 | schan->linklist.virt_addr = ll_cfg->virt_addr; | ||
798 | } else { | ||
799 | schan->linklist.phy_addr = 0; | ||
800 | schan->linklist.virt_addr = 0; | ||
801 | } | ||
802 | |||
751 | sdesc = kzalloc(sizeof(*sdesc), GFP_NOWAIT); | 803 | sdesc = kzalloc(sizeof(*sdesc), GFP_NOWAIT); |
752 | if (!sdesc) | 804 | if (!sdesc) |
753 | return NULL; | 805 | return NULL; |
@@ -762,10 +814,25 @@ sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
762 | src = slave_cfg->src_addr; | 814 | src = slave_cfg->src_addr; |
763 | dst = sg_dma_address(sg); | 815 | dst = sg_dma_address(sg); |
764 | } | 816 | } |
817 | |||
818 | /* | ||
819 | * The link-list mode needs at least 2 link-list | ||
820 | * configurations. If there is only one sg, it doesn't | ||
821 | * need to fill the link-list configuration. | ||
822 | */ | ||
823 | if (sglen < 2) | ||
824 | break; | ||
825 | |||
826 | ret = sprd_dma_fill_linklist_desc(chan, sglen, i, src, dst, len, | ||
827 | dir, flags, slave_cfg); | ||
828 | if (ret) { | ||
829 | kfree(sdesc); | ||
830 | return NULL; | ||
831 | } | ||
765 | } | 832 | } |
766 | 833 | ||
767 | ret = sprd_dma_fill_desc(chan, sdesc, src, dst, len, dir, flags, | 834 | ret = sprd_dma_fill_desc(chan, &sdesc->chn_hw, 0, 0, src, dst, len, |
768 | slave_cfg); | 835 | dir, flags, slave_cfg); |
769 | if (ret) { | 836 | if (ret) { |
770 | kfree(sdesc); | 837 | kfree(sdesc); |
771 | return NULL; | 838 | return NULL; |
diff --git a/include/linux/dma/sprd-dma.h b/include/linux/dma/sprd-dma.h index b0115e340fbc..b42b80e52cc2 100644 --- a/include/linux/dma/sprd-dma.h +++ b/include/linux/dma/sprd-dma.h | |||
@@ -58,4 +58,73 @@ enum sprd_dma_int_type { | |||
58 | SPRD_DMA_CFGERR_INT, | 58 | SPRD_DMA_CFGERR_INT, |
59 | }; | 59 | }; |
60 | 60 | ||
61 | /* | ||
62 | * struct sprd_dma_linklist - DMA link-list address structure | ||
63 | * @virt_addr: link-list virtual address to configure link-list node | ||
64 | * @phy_addr: link-list physical address to link DMA transfer | ||
65 | * | ||
66 | * The Spreadtrum DMA controller supports the link-list mode, that means slaves | ||
67 | * can supply several groups configurations (each configuration represents one | ||
68 | * DMA transfer) saved in memory, and DMA controller will link these groups | ||
69 | * configurations by writing the physical address of each configuration into the | ||
70 | * link-list register. | ||
71 | * | ||
72 | * Just as shown below, the link-list pointer register will be pointed to the | ||
73 | * physical address of 'configuration 1', and the 'configuration 1' link-list | ||
74 | * pointer will be pointed to 'configuration 2', and so on. | ||
75 | * Once trigger the DMA transfer, the DMA controller will load 'configuration | ||
76 | * 1' to its registers automatically, after 'configuration 1' transaction is | ||
77 | * done, DMA controller will load 'configuration 2' automatically, until all | ||
78 | * DMA transactions are done. | ||
79 | * | ||
80 | * Note: The last link-list pointer should point to the physical address | ||
81 | * of 'configuration 1', which can avoid DMA controller loads incorrect | ||
82 | * configuration when the last configuration transaction is done. | ||
83 | * | ||
84 | * DMA controller linklist memory | ||
85 | * ====================== ----------------------- | ||
86 | *| | | configuration 1 |<--- | ||
87 | *| DMA controller | ------->| | | | ||
88 | *| | | | | | | ||
89 | *| | | | | | | ||
90 | *| | | | | | | ||
91 | *| linklist pointer reg |---- ----| linklist pointer | | | ||
92 | * ====================== | ----------------------- | | ||
93 | * | | | ||
94 | * | ----------------------- | | ||
95 | * | | configuration 2 | | | ||
96 | * --->| | | | ||
97 | * | | | | ||
98 | * | | | | ||
99 | * | | | | ||
100 | * ----| linklist pointer | | | ||
101 | * | ----------------------- | | ||
102 | * | | | ||
103 | * | ----------------------- | | ||
104 | * | | configuration 3 | | | ||
105 | * --->| | | | ||
106 | * | | | | ||
107 | * | . | | | ||
108 | * . | | ||
109 | * . | | ||
110 | * . | | ||
111 | * | . | | ||
112 | * | ----------------------- | | ||
113 | * | | configuration n | | | ||
114 | * --->| | | | ||
115 | * | | | | ||
116 | * | | | | ||
117 | * | | | | ||
118 | * | linklist pointer |---- | ||
119 | * ----------------------- | ||
120 | * | ||
121 | * To support the link-list mode, DMA slaves should allocate one segment memory | ||
122 | * from always-on IRAM or dma coherent memory to store these groups of DMA | ||
123 | * configuration, and pass the virtual and physical address to DMA controller. | ||
124 | */ | ||
125 | struct sprd_dma_linklist { | ||
126 | unsigned long virt_addr; | ||
127 | phys_addr_t phy_addr; | ||
128 | }; | ||
129 | |||
61 | #endif | 130 | #endif |