aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Long <eric.long@spreadtrum.com>2018-08-28 07:09:07 -0400
committerVinod Koul <vkoul@kernel.org>2018-09-03 07:28:50 -0400
commit4ac695464763ecf696eaba563eff1c2ab994f6d8 (patch)
treebc0320ce3728e6813d598b65af02aca274ee6a95
parent5b394b2ddf0347bef56e50c69a58773c94343ff3 (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.c81
-rw-r--r--include/linux/dma/sprd-dma.h69
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 {
164struct sprd_dma_chn { 165struct 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
584static int sprd_dma_fill_desc(struct dma_chan *chan, 586static 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
701static 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
679static struct dma_async_tx_descriptor * 721static struct dma_async_tx_descriptor *
680sprd_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, 722sprd_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 */
125struct sprd_dma_linklist {
126 unsigned long virt_addr;
127 phys_addr_t phy_addr;
128};
129
61#endif 130#endif