Skip to content

Commit 4ac6954

Browse files
ericlong123vinodkoul
authored andcommitted
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 <[email protected]> Signed-off-by: Baolin Wang <[email protected]> Signed-off-by: Vinod Koul <[email protected]>
1 parent 5b394b2 commit 4ac6954

File tree

2 files changed

+143
-7
lines changed

2 files changed

+143
-7
lines changed

drivers/dma/sprd-dma.c

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868

6969
/* SPRD_DMA_CHN_CFG register definition */
7070
#define SPRD_DMA_CHN_EN BIT(0)
71+
#define SPRD_DMA_LINKLIST_EN BIT(4)
7172
#define SPRD_DMA_WAIT_BDONE_OFFSET 24
7273
#define SPRD_DMA_DONOT_WAIT_BDONE 1
7374

@@ -103,7 +104,7 @@
103104
#define SPRD_DMA_REQ_MODE_MASK GENMASK(1, 0)
104105
#define SPRD_DMA_FIX_SEL_OFFSET 21
105106
#define SPRD_DMA_FIX_EN_OFFSET 20
106-
#define SPRD_DMA_LLIST_END_OFFSET 19
107+
#define SPRD_DMA_LLIST_END BIT(19)
107108
#define SPRD_DMA_FRG_LEN_MASK GENMASK(16, 0)
108109

109110
/* SPRD_DMA_CHN_BLK_LEN register definition */
@@ -164,6 +165,7 @@ struct sprd_dma_desc {
164165
struct sprd_dma_chn {
165166
struct virt_dma_chan vc;
166167
void __iomem *chn_base;
168+
struct sprd_dma_linklist linklist;
167169
struct dma_slave_config slave_cfg;
168170
u32 chn_num;
169171
u32 dev_id;
@@ -582,15 +584,15 @@ static int sprd_dma_get_step(enum dma_slave_buswidth buswidth)
582584
}
583585

584586
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,
586589
dma_addr_t src, dma_addr_t dst, u32 len,
587590
enum dma_transfer_direction dir,
588591
unsigned long flags,
589592
struct dma_slave_config *slave_cfg)
590593
{
591594
struct sprd_dma_dev *sdev = to_sprd_dma_dev(chan);
592595
struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
593-
struct sprd_dma_chn_hw *hw = &sdesc->chn_hw;
594596
u32 req_mode = (flags >> SPRD_DMA_REQ_SHIFT) & SPRD_DMA_REQ_MODE_MASK;
595597
u32 int_mode = flags & SPRD_DMA_INT_MASK;
596598
int src_datawidth, dst_datawidth, src_step, dst_step;
@@ -670,12 +672,52 @@ static int sprd_dma_fill_desc(struct dma_chan *chan,
670672
temp |= (src_step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_SRC_TRSF_STEP_OFFSET;
671673
hw->trsf_step = temp;
672674

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+
673695
hw->frg_step = 0;
674696
hw->src_blk_step = 0;
675697
hw->des_blk_step = 0;
676698
return 0;
677699
}
678700

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+
679721
static struct dma_async_tx_descriptor *
680722
sprd_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
681723
size_t len, unsigned long flags)
@@ -744,10 +786,20 @@ sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
744786
u32 len = 0;
745787
int ret, i;
746788

747-
/* TODO: now we only support one sg for each DMA configuration. */
748-
if (!is_slave_direction(dir) || sglen > 1)
789+
if (!is_slave_direction(dir))
749790
return NULL;
750791

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+
751803
sdesc = kzalloc(sizeof(*sdesc), GFP_NOWAIT);
752804
if (!sdesc)
753805
return NULL;
@@ -762,10 +814,25 @@ sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
762814
src = slave_cfg->src_addr;
763815
dst = sg_dma_address(sg);
764816
}
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+
}
765832
}
766833

767-
ret = sprd_dma_fill_desc(chan, sdesc, src, dst, len, dir, flags,
768-
slave_cfg);
834+
ret = sprd_dma_fill_desc(chan, &sdesc->chn_hw, 0, 0, src, dst, len,
835+
dir, flags, slave_cfg);
769836
if (ret) {
770837
kfree(sdesc);
771838
return NULL;

include/linux/dma/sprd-dma.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,73 @@ enum sprd_dma_int_type {
5858
SPRD_DMA_CFGERR_INT,
5959
};
6060

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+
61130
#endif

0 commit comments

Comments
 (0)