xref: /OK3568_Linux_fs/kernel/drivers/dma/ste_dma40_ll.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) ST-Ericsson SA 2007-2010
4*4882a593Smuzhiyun  * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson
5*4882a593Smuzhiyun  * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/platform_data/dma-ste-dma40.h>
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include "ste_dma40_ll.h"
12*4882a593Smuzhiyun 
d40_width_to_bits(enum dma_slave_buswidth width)13*4882a593Smuzhiyun static u8 d40_width_to_bits(enum dma_slave_buswidth width)
14*4882a593Smuzhiyun {
15*4882a593Smuzhiyun 	if (width == DMA_SLAVE_BUSWIDTH_1_BYTE)
16*4882a593Smuzhiyun 		return STEDMA40_ESIZE_8_BIT;
17*4882a593Smuzhiyun 	else if (width == DMA_SLAVE_BUSWIDTH_2_BYTES)
18*4882a593Smuzhiyun 		return STEDMA40_ESIZE_16_BIT;
19*4882a593Smuzhiyun 	else if (width == DMA_SLAVE_BUSWIDTH_8_BYTES)
20*4882a593Smuzhiyun 		return STEDMA40_ESIZE_64_BIT;
21*4882a593Smuzhiyun 	else
22*4882a593Smuzhiyun 		return STEDMA40_ESIZE_32_BIT;
23*4882a593Smuzhiyun }
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun /* Sets up proper LCSP1 and LCSP3 register for a logical channel */
d40_log_cfg(struct stedma40_chan_cfg * cfg,u32 * lcsp1,u32 * lcsp3)26*4882a593Smuzhiyun void d40_log_cfg(struct stedma40_chan_cfg *cfg,
27*4882a593Smuzhiyun 		 u32 *lcsp1, u32 *lcsp3)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun 	u32 l3 = 0; /* dst */
30*4882a593Smuzhiyun 	u32 l1 = 0; /* src */
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	/* src is mem? -> increase address pos */
33*4882a593Smuzhiyun 	if (cfg->dir ==  DMA_MEM_TO_DEV ||
34*4882a593Smuzhiyun 	    cfg->dir ==  DMA_MEM_TO_MEM)
35*4882a593Smuzhiyun 		l1 |= BIT(D40_MEM_LCSP1_SCFG_INCR_POS);
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	/* dst is mem? -> increase address pos */
38*4882a593Smuzhiyun 	if (cfg->dir ==  DMA_DEV_TO_MEM ||
39*4882a593Smuzhiyun 	    cfg->dir ==  DMA_MEM_TO_MEM)
40*4882a593Smuzhiyun 		l3 |= BIT(D40_MEM_LCSP3_DCFG_INCR_POS);
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 	/* src is hw? -> master port 1 */
43*4882a593Smuzhiyun 	if (cfg->dir ==  DMA_DEV_TO_MEM ||
44*4882a593Smuzhiyun 	    cfg->dir ==  DMA_DEV_TO_DEV)
45*4882a593Smuzhiyun 		l1 |= BIT(D40_MEM_LCSP1_SCFG_MST_POS);
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	/* dst is hw? -> master port 1 */
48*4882a593Smuzhiyun 	if (cfg->dir ==  DMA_MEM_TO_DEV ||
49*4882a593Smuzhiyun 	    cfg->dir ==  DMA_DEV_TO_DEV)
50*4882a593Smuzhiyun 		l3 |= BIT(D40_MEM_LCSP3_DCFG_MST_POS);
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	l3 |= BIT(D40_MEM_LCSP3_DCFG_EIM_POS);
53*4882a593Smuzhiyun 	l3 |= cfg->dst_info.psize << D40_MEM_LCSP3_DCFG_PSIZE_POS;
54*4882a593Smuzhiyun 	l3 |= d40_width_to_bits(cfg->dst_info.data_width)
55*4882a593Smuzhiyun 		<< D40_MEM_LCSP3_DCFG_ESIZE_POS;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	l1 |= BIT(D40_MEM_LCSP1_SCFG_EIM_POS);
58*4882a593Smuzhiyun 	l1 |= cfg->src_info.psize << D40_MEM_LCSP1_SCFG_PSIZE_POS;
59*4882a593Smuzhiyun 	l1 |= d40_width_to_bits(cfg->src_info.data_width)
60*4882a593Smuzhiyun 		<< D40_MEM_LCSP1_SCFG_ESIZE_POS;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	*lcsp1 = l1;
63*4882a593Smuzhiyun 	*lcsp3 = l3;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
d40_phy_cfg(struct stedma40_chan_cfg * cfg,u32 * src_cfg,u32 * dst_cfg)67*4882a593Smuzhiyun void d40_phy_cfg(struct stedma40_chan_cfg *cfg, u32 *src_cfg, u32 *dst_cfg)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun 	u32 src = 0;
70*4882a593Smuzhiyun 	u32 dst = 0;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	if ((cfg->dir == DMA_DEV_TO_MEM) ||
73*4882a593Smuzhiyun 	    (cfg->dir == DMA_DEV_TO_DEV)) {
74*4882a593Smuzhiyun 		/* Set master port to 1 */
75*4882a593Smuzhiyun 		src |= BIT(D40_SREG_CFG_MST_POS);
76*4882a593Smuzhiyun 		src |= D40_TYPE_TO_EVENT(cfg->dev_type);
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 		if (cfg->src_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL)
79*4882a593Smuzhiyun 			src |= BIT(D40_SREG_CFG_PHY_TM_POS);
80*4882a593Smuzhiyun 		else
81*4882a593Smuzhiyun 			src |= 3 << D40_SREG_CFG_PHY_TM_POS;
82*4882a593Smuzhiyun 	}
83*4882a593Smuzhiyun 	if ((cfg->dir == DMA_MEM_TO_DEV) ||
84*4882a593Smuzhiyun 	    (cfg->dir == DMA_DEV_TO_DEV)) {
85*4882a593Smuzhiyun 		/* Set master port to 1 */
86*4882a593Smuzhiyun 		dst |= BIT(D40_SREG_CFG_MST_POS);
87*4882a593Smuzhiyun 		dst |= D40_TYPE_TO_EVENT(cfg->dev_type);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 		if (cfg->dst_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL)
90*4882a593Smuzhiyun 			dst |= BIT(D40_SREG_CFG_PHY_TM_POS);
91*4882a593Smuzhiyun 		else
92*4882a593Smuzhiyun 			dst |= 3 << D40_SREG_CFG_PHY_TM_POS;
93*4882a593Smuzhiyun 	}
94*4882a593Smuzhiyun 	/* Interrupt on end of transfer for destination */
95*4882a593Smuzhiyun 	dst |= BIT(D40_SREG_CFG_TIM_POS);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	/* Generate interrupt on error */
98*4882a593Smuzhiyun 	src |= BIT(D40_SREG_CFG_EIM_POS);
99*4882a593Smuzhiyun 	dst |= BIT(D40_SREG_CFG_EIM_POS);
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	/* PSIZE */
102*4882a593Smuzhiyun 	if (cfg->src_info.psize != STEDMA40_PSIZE_PHY_1) {
103*4882a593Smuzhiyun 		src |= BIT(D40_SREG_CFG_PHY_PEN_POS);
104*4882a593Smuzhiyun 		src |= cfg->src_info.psize << D40_SREG_CFG_PSIZE_POS;
105*4882a593Smuzhiyun 	}
106*4882a593Smuzhiyun 	if (cfg->dst_info.psize != STEDMA40_PSIZE_PHY_1) {
107*4882a593Smuzhiyun 		dst |= BIT(D40_SREG_CFG_PHY_PEN_POS);
108*4882a593Smuzhiyun 		dst |= cfg->dst_info.psize << D40_SREG_CFG_PSIZE_POS;
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	/* Element size */
112*4882a593Smuzhiyun 	src |= d40_width_to_bits(cfg->src_info.data_width)
113*4882a593Smuzhiyun 		<< D40_SREG_CFG_ESIZE_POS;
114*4882a593Smuzhiyun 	dst |= d40_width_to_bits(cfg->dst_info.data_width)
115*4882a593Smuzhiyun 		<< D40_SREG_CFG_ESIZE_POS;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	/* Set the priority bit to high for the physical channel */
118*4882a593Smuzhiyun 	if (cfg->high_priority) {
119*4882a593Smuzhiyun 		src |= BIT(D40_SREG_CFG_PRI_POS);
120*4882a593Smuzhiyun 		dst |= BIT(D40_SREG_CFG_PRI_POS);
121*4882a593Smuzhiyun 	}
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	if (cfg->src_info.big_endian)
124*4882a593Smuzhiyun 		src |= BIT(D40_SREG_CFG_LBE_POS);
125*4882a593Smuzhiyun 	if (cfg->dst_info.big_endian)
126*4882a593Smuzhiyun 		dst |= BIT(D40_SREG_CFG_LBE_POS);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	*src_cfg = src;
129*4882a593Smuzhiyun 	*dst_cfg = dst;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun 
d40_phy_fill_lli(struct d40_phy_lli * lli,dma_addr_t data,u32 data_size,dma_addr_t next_lli,u32 reg_cfg,struct stedma40_half_channel_info * info,unsigned int flags)132*4882a593Smuzhiyun static int d40_phy_fill_lli(struct d40_phy_lli *lli,
133*4882a593Smuzhiyun 			    dma_addr_t data,
134*4882a593Smuzhiyun 			    u32 data_size,
135*4882a593Smuzhiyun 			    dma_addr_t next_lli,
136*4882a593Smuzhiyun 			    u32 reg_cfg,
137*4882a593Smuzhiyun 			    struct stedma40_half_channel_info *info,
138*4882a593Smuzhiyun 			    unsigned int flags)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	bool addr_inc = flags & LLI_ADDR_INC;
141*4882a593Smuzhiyun 	bool term_int = flags & LLI_TERM_INT;
142*4882a593Smuzhiyun 	unsigned int data_width = info->data_width;
143*4882a593Smuzhiyun 	int psize = info->psize;
144*4882a593Smuzhiyun 	int num_elems;
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	if (psize == STEDMA40_PSIZE_PHY_1)
147*4882a593Smuzhiyun 		num_elems = 1;
148*4882a593Smuzhiyun 	else
149*4882a593Smuzhiyun 		num_elems = 2 << psize;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	/* Must be aligned */
152*4882a593Smuzhiyun 	if (!IS_ALIGNED(data, data_width))
153*4882a593Smuzhiyun 		return -EINVAL;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	/* Transfer size can't be smaller than (num_elms * elem_size) */
156*4882a593Smuzhiyun 	if (data_size < num_elems * data_width)
157*4882a593Smuzhiyun 		return -EINVAL;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	/* The number of elements. IE now many chunks */
160*4882a593Smuzhiyun 	lli->reg_elt = (data_size / data_width) << D40_SREG_ELEM_PHY_ECNT_POS;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	/*
163*4882a593Smuzhiyun 	 * Distance to next element sized entry.
164*4882a593Smuzhiyun 	 * Usually the size of the element unless you want gaps.
165*4882a593Smuzhiyun 	 */
166*4882a593Smuzhiyun 	if (addr_inc)
167*4882a593Smuzhiyun 		lli->reg_elt |= data_width << D40_SREG_ELEM_PHY_EIDX_POS;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	/* Where the data is */
170*4882a593Smuzhiyun 	lli->reg_ptr = data;
171*4882a593Smuzhiyun 	lli->reg_cfg = reg_cfg;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	/* If this scatter list entry is the last one, no next link */
174*4882a593Smuzhiyun 	if (next_lli == 0)
175*4882a593Smuzhiyun 		lli->reg_lnk = BIT(D40_SREG_LNK_PHY_TCP_POS);
176*4882a593Smuzhiyun 	else
177*4882a593Smuzhiyun 		lli->reg_lnk = next_lli;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	/* Set/clear interrupt generation on this link item.*/
180*4882a593Smuzhiyun 	if (term_int)
181*4882a593Smuzhiyun 		lli->reg_cfg |= BIT(D40_SREG_CFG_TIM_POS);
182*4882a593Smuzhiyun 	else
183*4882a593Smuzhiyun 		lli->reg_cfg &= ~BIT(D40_SREG_CFG_TIM_POS);
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	/*
186*4882a593Smuzhiyun 	 * Post link - D40_SREG_LNK_PHY_PRE_POS = 0
187*4882a593Smuzhiyun 	 * Relink happens after transfer completion.
188*4882a593Smuzhiyun 	 */
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	return 0;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun 
d40_seg_size(int size,int data_width1,int data_width2)193*4882a593Smuzhiyun static int d40_seg_size(int size, int data_width1, int data_width2)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun 	u32 max_w = max(data_width1, data_width2);
196*4882a593Smuzhiyun 	u32 min_w = min(data_width1, data_width2);
197*4882a593Smuzhiyun 	u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE * min_w, max_w);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	if (seg_max > STEDMA40_MAX_SEG_SIZE)
200*4882a593Smuzhiyun 		seg_max -= max_w;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	if (size <= seg_max)
203*4882a593Smuzhiyun 		return size;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	if (size <= 2 * seg_max)
206*4882a593Smuzhiyun 		return ALIGN(size / 2, max_w);
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	return seg_max;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun static struct d40_phy_lli *
d40_phy_buf_to_lli(struct d40_phy_lli * lli,dma_addr_t addr,u32 size,dma_addr_t lli_phys,dma_addr_t first_phys,u32 reg_cfg,struct stedma40_half_channel_info * info,struct stedma40_half_channel_info * otherinfo,unsigned long flags)212*4882a593Smuzhiyun d40_phy_buf_to_lli(struct d40_phy_lli *lli, dma_addr_t addr, u32 size,
213*4882a593Smuzhiyun 		   dma_addr_t lli_phys, dma_addr_t first_phys, u32 reg_cfg,
214*4882a593Smuzhiyun 		   struct stedma40_half_channel_info *info,
215*4882a593Smuzhiyun 		   struct stedma40_half_channel_info *otherinfo,
216*4882a593Smuzhiyun 		   unsigned long flags)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun 	bool lastlink = flags & LLI_LAST_LINK;
219*4882a593Smuzhiyun 	bool addr_inc = flags & LLI_ADDR_INC;
220*4882a593Smuzhiyun 	bool term_int = flags & LLI_TERM_INT;
221*4882a593Smuzhiyun 	bool cyclic = flags & LLI_CYCLIC;
222*4882a593Smuzhiyun 	int err;
223*4882a593Smuzhiyun 	dma_addr_t next = lli_phys;
224*4882a593Smuzhiyun 	int size_rest = size;
225*4882a593Smuzhiyun 	int size_seg = 0;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	/*
228*4882a593Smuzhiyun 	 * This piece may be split up based on d40_seg_size(); we only want the
229*4882a593Smuzhiyun 	 * term int on the last part.
230*4882a593Smuzhiyun 	 */
231*4882a593Smuzhiyun 	if (term_int)
232*4882a593Smuzhiyun 		flags &= ~LLI_TERM_INT;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	do {
235*4882a593Smuzhiyun 		size_seg = d40_seg_size(size_rest, info->data_width,
236*4882a593Smuzhiyun 					otherinfo->data_width);
237*4882a593Smuzhiyun 		size_rest -= size_seg;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 		if (size_rest == 0 && term_int)
240*4882a593Smuzhiyun 			flags |= LLI_TERM_INT;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 		if (size_rest == 0 && lastlink)
243*4882a593Smuzhiyun 			next = cyclic ? first_phys : 0;
244*4882a593Smuzhiyun 		else
245*4882a593Smuzhiyun 			next = ALIGN(next + sizeof(struct d40_phy_lli),
246*4882a593Smuzhiyun 				     D40_LLI_ALIGN);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 		err = d40_phy_fill_lli(lli, addr, size_seg, next,
249*4882a593Smuzhiyun 				       reg_cfg, info, flags);
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 		if (err)
252*4882a593Smuzhiyun 			goto err;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 		lli++;
255*4882a593Smuzhiyun 		if (addr_inc)
256*4882a593Smuzhiyun 			addr += size_seg;
257*4882a593Smuzhiyun 	} while (size_rest);
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	return lli;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun err:
262*4882a593Smuzhiyun 	return NULL;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun 
d40_phy_sg_to_lli(struct scatterlist * sg,int sg_len,dma_addr_t target,struct d40_phy_lli * lli_sg,dma_addr_t lli_phys,u32 reg_cfg,struct stedma40_half_channel_info * info,struct stedma40_half_channel_info * otherinfo,unsigned long flags)265*4882a593Smuzhiyun int d40_phy_sg_to_lli(struct scatterlist *sg,
266*4882a593Smuzhiyun 		      int sg_len,
267*4882a593Smuzhiyun 		      dma_addr_t target,
268*4882a593Smuzhiyun 		      struct d40_phy_lli *lli_sg,
269*4882a593Smuzhiyun 		      dma_addr_t lli_phys,
270*4882a593Smuzhiyun 		      u32 reg_cfg,
271*4882a593Smuzhiyun 		      struct stedma40_half_channel_info *info,
272*4882a593Smuzhiyun 		      struct stedma40_half_channel_info *otherinfo,
273*4882a593Smuzhiyun 		      unsigned long flags)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun 	int total_size = 0;
276*4882a593Smuzhiyun 	int i;
277*4882a593Smuzhiyun 	struct scatterlist *current_sg = sg;
278*4882a593Smuzhiyun 	struct d40_phy_lli *lli = lli_sg;
279*4882a593Smuzhiyun 	dma_addr_t l_phys = lli_phys;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	if (!target)
282*4882a593Smuzhiyun 		flags |= LLI_ADDR_INC;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	for_each_sg(sg, current_sg, sg_len, i) {
285*4882a593Smuzhiyun 		dma_addr_t sg_addr = sg_dma_address(current_sg);
286*4882a593Smuzhiyun 		unsigned int len = sg_dma_len(current_sg);
287*4882a593Smuzhiyun 		dma_addr_t dst = target ?: sg_addr;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 		total_size += sg_dma_len(current_sg);
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 		if (i == sg_len - 1)
292*4882a593Smuzhiyun 			flags |= LLI_TERM_INT | LLI_LAST_LINK;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 		l_phys = ALIGN(lli_phys + (lli - lli_sg) *
295*4882a593Smuzhiyun 			       sizeof(struct d40_phy_lli), D40_LLI_ALIGN);
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 		lli = d40_phy_buf_to_lli(lli, dst, len, l_phys, lli_phys,
298*4882a593Smuzhiyun 					 reg_cfg, info, otherinfo, flags);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 		if (lli == NULL)
301*4882a593Smuzhiyun 			return -EINVAL;
302*4882a593Smuzhiyun 	}
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	return total_size;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun /* DMA logical lli operations */
309*4882a593Smuzhiyun 
d40_log_lli_link(struct d40_log_lli * lli_dst,struct d40_log_lli * lli_src,int next,unsigned int flags)310*4882a593Smuzhiyun static void d40_log_lli_link(struct d40_log_lli *lli_dst,
311*4882a593Smuzhiyun 			     struct d40_log_lli *lli_src,
312*4882a593Smuzhiyun 			     int next, unsigned int flags)
313*4882a593Smuzhiyun {
314*4882a593Smuzhiyun 	bool interrupt = flags & LLI_TERM_INT;
315*4882a593Smuzhiyun 	u32 slos = 0;
316*4882a593Smuzhiyun 	u32 dlos = 0;
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	if (next != -EINVAL) {
319*4882a593Smuzhiyun 		slos = next * 2;
320*4882a593Smuzhiyun 		dlos = next * 2 + 1;
321*4882a593Smuzhiyun 	}
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	if (interrupt) {
324*4882a593Smuzhiyun 		lli_dst->lcsp13 |= D40_MEM_LCSP1_SCFG_TIM_MASK;
325*4882a593Smuzhiyun 		lli_dst->lcsp13 |= D40_MEM_LCSP3_DTCP_MASK;
326*4882a593Smuzhiyun 	}
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	lli_src->lcsp13 = (lli_src->lcsp13 & ~D40_MEM_LCSP1_SLOS_MASK) |
329*4882a593Smuzhiyun 		(slos << D40_MEM_LCSP1_SLOS_POS);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	lli_dst->lcsp13 = (lli_dst->lcsp13 & ~D40_MEM_LCSP1_SLOS_MASK) |
332*4882a593Smuzhiyun 		(dlos << D40_MEM_LCSP1_SLOS_POS);
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun 
d40_log_lli_lcpa_write(struct d40_log_lli_full * lcpa,struct d40_log_lli * lli_dst,struct d40_log_lli * lli_src,int next,unsigned int flags)335*4882a593Smuzhiyun void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa,
336*4882a593Smuzhiyun 			   struct d40_log_lli *lli_dst,
337*4882a593Smuzhiyun 			   struct d40_log_lli *lli_src,
338*4882a593Smuzhiyun 			   int next, unsigned int flags)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun 	d40_log_lli_link(lli_dst, lli_src, next, flags);
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	writel_relaxed(lli_src->lcsp02, &lcpa[0].lcsp0);
343*4882a593Smuzhiyun 	writel_relaxed(lli_src->lcsp13, &lcpa[0].lcsp1);
344*4882a593Smuzhiyun 	writel_relaxed(lli_dst->lcsp02, &lcpa[0].lcsp2);
345*4882a593Smuzhiyun 	writel_relaxed(lli_dst->lcsp13, &lcpa[0].lcsp3);
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun 
d40_log_lli_lcla_write(struct d40_log_lli * lcla,struct d40_log_lli * lli_dst,struct d40_log_lli * lli_src,int next,unsigned int flags)348*4882a593Smuzhiyun void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
349*4882a593Smuzhiyun 			   struct d40_log_lli *lli_dst,
350*4882a593Smuzhiyun 			   struct d40_log_lli *lli_src,
351*4882a593Smuzhiyun 			   int next, unsigned int flags)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun 	d40_log_lli_link(lli_dst, lli_src, next, flags);
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	writel_relaxed(lli_src->lcsp02, &lcla[0].lcsp02);
356*4882a593Smuzhiyun 	writel_relaxed(lli_src->lcsp13, &lcla[0].lcsp13);
357*4882a593Smuzhiyun 	writel_relaxed(lli_dst->lcsp02, &lcla[1].lcsp02);
358*4882a593Smuzhiyun 	writel_relaxed(lli_dst->lcsp13, &lcla[1].lcsp13);
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun 
d40_log_fill_lli(struct d40_log_lli * lli,dma_addr_t data,u32 data_size,u32 reg_cfg,u32 data_width,unsigned int flags)361*4882a593Smuzhiyun static void d40_log_fill_lli(struct d40_log_lli *lli,
362*4882a593Smuzhiyun 			     dma_addr_t data, u32 data_size,
363*4882a593Smuzhiyun 			     u32 reg_cfg,
364*4882a593Smuzhiyun 			     u32 data_width,
365*4882a593Smuzhiyun 			     unsigned int flags)
366*4882a593Smuzhiyun {
367*4882a593Smuzhiyun 	bool addr_inc = flags & LLI_ADDR_INC;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	lli->lcsp13 = reg_cfg;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	/* The number of elements to transfer */
372*4882a593Smuzhiyun 	lli->lcsp02 = ((data_size / data_width) <<
373*4882a593Smuzhiyun 		       D40_MEM_LCSP0_ECNT_POS) & D40_MEM_LCSP0_ECNT_MASK;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	BUG_ON((data_size / data_width) > STEDMA40_MAX_SEG_SIZE);
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	/* 16 LSBs address of the current element */
378*4882a593Smuzhiyun 	lli->lcsp02 |= data & D40_MEM_LCSP0_SPTR_MASK;
379*4882a593Smuzhiyun 	/* 16 MSBs address of the current element */
380*4882a593Smuzhiyun 	lli->lcsp13 |= data & D40_MEM_LCSP1_SPTR_MASK;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	if (addr_inc)
383*4882a593Smuzhiyun 		lli->lcsp13 |= D40_MEM_LCSP1_SCFG_INCR_MASK;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun 
d40_log_buf_to_lli(struct d40_log_lli * lli_sg,dma_addr_t addr,int size,u32 lcsp13,u32 data_width1,u32 data_width2,unsigned int flags)387*4882a593Smuzhiyun static struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg,
388*4882a593Smuzhiyun 				       dma_addr_t addr,
389*4882a593Smuzhiyun 				       int size,
390*4882a593Smuzhiyun 				       u32 lcsp13, /* src or dst*/
391*4882a593Smuzhiyun 				       u32 data_width1,
392*4882a593Smuzhiyun 				       u32 data_width2,
393*4882a593Smuzhiyun 				       unsigned int flags)
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun 	bool addr_inc = flags & LLI_ADDR_INC;
396*4882a593Smuzhiyun 	struct d40_log_lli *lli = lli_sg;
397*4882a593Smuzhiyun 	int size_rest = size;
398*4882a593Smuzhiyun 	int size_seg = 0;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	do {
401*4882a593Smuzhiyun 		size_seg = d40_seg_size(size_rest, data_width1, data_width2);
402*4882a593Smuzhiyun 		size_rest -= size_seg;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 		d40_log_fill_lli(lli,
405*4882a593Smuzhiyun 				 addr,
406*4882a593Smuzhiyun 				 size_seg,
407*4882a593Smuzhiyun 				 lcsp13, data_width1,
408*4882a593Smuzhiyun 				 flags);
409*4882a593Smuzhiyun 		if (addr_inc)
410*4882a593Smuzhiyun 			addr += size_seg;
411*4882a593Smuzhiyun 		lli++;
412*4882a593Smuzhiyun 	} while (size_rest);
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	return lli;
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun 
d40_log_sg_to_lli(struct scatterlist * sg,int sg_len,dma_addr_t dev_addr,struct d40_log_lli * lli_sg,u32 lcsp13,u32 data_width1,u32 data_width2)417*4882a593Smuzhiyun int d40_log_sg_to_lli(struct scatterlist *sg,
418*4882a593Smuzhiyun 		      int sg_len,
419*4882a593Smuzhiyun 		      dma_addr_t dev_addr,
420*4882a593Smuzhiyun 		      struct d40_log_lli *lli_sg,
421*4882a593Smuzhiyun 		      u32 lcsp13, /* src or dst*/
422*4882a593Smuzhiyun 		      u32 data_width1, u32 data_width2)
423*4882a593Smuzhiyun {
424*4882a593Smuzhiyun 	int total_size = 0;
425*4882a593Smuzhiyun 	struct scatterlist *current_sg = sg;
426*4882a593Smuzhiyun 	int i;
427*4882a593Smuzhiyun 	struct d40_log_lli *lli = lli_sg;
428*4882a593Smuzhiyun 	unsigned long flags = 0;
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	if (!dev_addr)
431*4882a593Smuzhiyun 		flags |= LLI_ADDR_INC;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	for_each_sg(sg, current_sg, sg_len, i) {
434*4882a593Smuzhiyun 		dma_addr_t sg_addr = sg_dma_address(current_sg);
435*4882a593Smuzhiyun 		unsigned int len = sg_dma_len(current_sg);
436*4882a593Smuzhiyun 		dma_addr_t addr = dev_addr ?: sg_addr;
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 		total_size += sg_dma_len(current_sg);
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 		lli = d40_log_buf_to_lli(lli, addr, len,
441*4882a593Smuzhiyun 					 lcsp13,
442*4882a593Smuzhiyun 					 data_width1,
443*4882a593Smuzhiyun 					 data_width2,
444*4882a593Smuzhiyun 					 flags);
445*4882a593Smuzhiyun 	}
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	return total_size;
448*4882a593Smuzhiyun }
449