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