2 * driver/dma/ste_dma40_ll.c
4 * Copyright (C) ST-Ericsson 2007-2010
5 * License terms: GNU General Public License (GPL) version 2
6 * Author: Per Friden <per.friden@stericsson.com>
7 * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
10 #include <linux/kernel.h>
11 #include <plat/ste_dma40.h>
13 #include "ste_dma40_ll.h"
15 /* Sets up proper LCSP1 and LCSP3 register for a logical channel */
16 void d40_log_cfg(struct stedma40_chan_cfg
*cfg
,
17 u32
*lcsp1
, u32
*lcsp3
)
22 /* src is mem? -> increase address pos */
23 if (cfg
->dir
== STEDMA40_MEM_TO_PERIPH
||
24 cfg
->dir
== STEDMA40_MEM_TO_MEM
)
25 l1
|= 1 << D40_MEM_LCSP1_SCFG_INCR_POS
;
27 /* dst is mem? -> increase address pos */
28 if (cfg
->dir
== STEDMA40_PERIPH_TO_MEM
||
29 cfg
->dir
== STEDMA40_MEM_TO_MEM
)
30 l3
|= 1 << D40_MEM_LCSP3_DCFG_INCR_POS
;
32 /* src is hw? -> master port 1 */
33 if (cfg
->dir
== STEDMA40_PERIPH_TO_MEM
||
34 cfg
->dir
== STEDMA40_PERIPH_TO_PERIPH
)
35 l1
|= 1 << D40_MEM_LCSP1_SCFG_MST_POS
;
37 /* dst is hw? -> master port 1 */
38 if (cfg
->dir
== STEDMA40_MEM_TO_PERIPH
||
39 cfg
->dir
== STEDMA40_PERIPH_TO_PERIPH
)
40 l3
|= 1 << D40_MEM_LCSP3_DCFG_MST_POS
;
42 l3
|= 1 << D40_MEM_LCSP3_DCFG_TIM_POS
;
43 l3
|= 1 << D40_MEM_LCSP3_DCFG_EIM_POS
;
44 l3
|= cfg
->dst_info
.psize
<< D40_MEM_LCSP3_DCFG_PSIZE_POS
;
45 l3
|= cfg
->dst_info
.data_width
<< D40_MEM_LCSP3_DCFG_ESIZE_POS
;
46 l3
|= 1 << D40_MEM_LCSP3_DTCP_POS
;
48 l1
|= 1 << D40_MEM_LCSP1_SCFG_EIM_POS
;
49 l1
|= cfg
->src_info
.psize
<< D40_MEM_LCSP1_SCFG_PSIZE_POS
;
50 l1
|= cfg
->src_info
.data_width
<< D40_MEM_LCSP1_SCFG_ESIZE_POS
;
51 l1
|= 1 << D40_MEM_LCSP1_STCP_POS
;
58 /* Sets up SRC and DST CFG register for both logical and physical channels */
59 void d40_phy_cfg(struct stedma40_chan_cfg
*cfg
,
60 u32
*src_cfg
, u32
*dst_cfg
, bool is_log
)
66 /* Physical channel */
67 if ((cfg
->dir
== STEDMA40_PERIPH_TO_MEM
) ||
68 (cfg
->dir
== STEDMA40_PERIPH_TO_PERIPH
)) {
69 /* Set master port to 1 */
70 src
|= 1 << D40_SREG_CFG_MST_POS
;
71 src
|= D40_TYPE_TO_EVENT(cfg
->src_dev_type
);
73 if (cfg
->src_info
.flow_ctrl
== STEDMA40_NO_FLOW_CTRL
)
74 src
|= 1 << D40_SREG_CFG_PHY_TM_POS
;
76 src
|= 3 << D40_SREG_CFG_PHY_TM_POS
;
78 if ((cfg
->dir
== STEDMA40_MEM_TO_PERIPH
) ||
79 (cfg
->dir
== STEDMA40_PERIPH_TO_PERIPH
)) {
80 /* Set master port to 1 */
81 dst
|= 1 << D40_SREG_CFG_MST_POS
;
82 dst
|= D40_TYPE_TO_EVENT(cfg
->dst_dev_type
);
84 if (cfg
->dst_info
.flow_ctrl
== STEDMA40_NO_FLOW_CTRL
)
85 dst
|= 1 << D40_SREG_CFG_PHY_TM_POS
;
87 dst
|= 3 << D40_SREG_CFG_PHY_TM_POS
;
89 /* Interrupt on end of transfer for destination */
90 dst
|= 1 << D40_SREG_CFG_TIM_POS
;
92 /* Generate interrupt on error */
93 src
|= 1 << D40_SREG_CFG_EIM_POS
;
94 dst
|= 1 << D40_SREG_CFG_EIM_POS
;
97 if (cfg
->src_info
.psize
!= STEDMA40_PSIZE_PHY_1
) {
98 src
|= 1 << D40_SREG_CFG_PHY_PEN_POS
;
99 src
|= cfg
->src_info
.psize
<< D40_SREG_CFG_PSIZE_POS
;
101 if (cfg
->dst_info
.psize
!= STEDMA40_PSIZE_PHY_1
) {
102 dst
|= 1 << D40_SREG_CFG_PHY_PEN_POS
;
103 dst
|= cfg
->dst_info
.psize
<< D40_SREG_CFG_PSIZE_POS
;
107 src
|= cfg
->src_info
.data_width
<< D40_SREG_CFG_ESIZE_POS
;
108 dst
|= cfg
->dst_info
.data_width
<< D40_SREG_CFG_ESIZE_POS
;
111 /* Logical channel */
112 dst
|= 1 << D40_SREG_CFG_LOG_GIM_POS
;
113 src
|= 1 << D40_SREG_CFG_LOG_GIM_POS
;
116 if (cfg
->channel_type
& STEDMA40_HIGH_PRIORITY_CHANNEL
) {
117 src
|= 1 << D40_SREG_CFG_PRI_POS
;
118 dst
|= 1 << D40_SREG_CFG_PRI_POS
;
121 src
|= cfg
->src_info
.endianess
<< D40_SREG_CFG_LBE_POS
;
122 dst
|= cfg
->dst_info
.endianess
<< D40_SREG_CFG_LBE_POS
;
128 int d40_phy_fill_lli(struct d40_phy_lli
*lli
,
140 if (psize
== STEDMA40_PSIZE_PHY_1
)
143 num_elems
= 2 << psize
;
146 * Size is 16bit. data_width is 8, 16, 32 or 64 bit
147 * Block large than 64 KiB must be split.
149 if (data_size
> (0xffff << data_width
))
152 /* Must be aligned */
153 if (!IS_ALIGNED(data
, 0x1 << data_width
))
156 /* Transfer size can't be smaller than (num_elms * elem_size) */
157 if (data_size
< num_elems
* (0x1 << data_width
))
160 /* The number of elements. IE now many chunks */
161 lli
->reg_elt
= (data_size
>> data_width
) << D40_SREG_ELEM_PHY_ECNT_POS
;
164 * Distance to next element sized entry.
165 * Usually the size of the element unless you want gaps.
168 lli
->reg_elt
|= (0x1 << data_width
) <<
169 D40_SREG_ELEM_PHY_EIDX_POS
;
171 /* Where the data is */
173 lli
->reg_cfg
= reg_cfg
;
175 /* If this scatter list entry is the last one, no next link */
177 lli
->reg_lnk
= 0x1 << D40_SREG_LNK_PHY_TCP_POS
;
179 lli
->reg_lnk
= next_lli
;
181 /* Set/clear interrupt generation on this link item.*/
183 lli
->reg_cfg
|= 0x1 << D40_SREG_CFG_TIM_POS
;
185 lli
->reg_cfg
&= ~(0x1 << D40_SREG_CFG_TIM_POS
);
188 lli
->reg_lnk
|= 0 << D40_SREG_LNK_PHY_PRE_POS
;
193 int d40_phy_sg_to_lli(struct scatterlist
*sg
,
196 struct d40_phy_lli
*lli
,
205 struct scatterlist
*current_sg
= sg
;
206 dma_addr_t next_lli_phys
;
210 for_each_sg(sg
, current_sg
, sg_len
, i
) {
212 total_size
+= sg_dma_len(current_sg
);
214 /* If this scatter list entry is the last one, no next link */
218 next_lli_phys
= ALIGN(lli_phys
+ (i
+ 1) *
219 sizeof(struct d40_phy_lli
),
225 dst
= sg_phys(current_sg
);
227 err
= d40_phy_fill_lli(&lli
[i
],
229 sg_dma_len(current_sg
),
246 void d40_phy_lli_write(void __iomem
*virtbase
,
248 struct d40_phy_lli
*lli_dst
,
249 struct d40_phy_lli
*lli_src
)
252 writel(lli_src
->reg_cfg
, virtbase
+ D40_DREG_PCBASE
+
253 phy_chan_num
* D40_DREG_PCDELTA
+ D40_CHAN_REG_SSCFG
);
254 writel(lli_src
->reg_elt
, virtbase
+ D40_DREG_PCBASE
+
255 phy_chan_num
* D40_DREG_PCDELTA
+ D40_CHAN_REG_SSELT
);
256 writel(lli_src
->reg_ptr
, virtbase
+ D40_DREG_PCBASE
+
257 phy_chan_num
* D40_DREG_PCDELTA
+ D40_CHAN_REG_SSPTR
);
258 writel(lli_src
->reg_lnk
, virtbase
+ D40_DREG_PCBASE
+
259 phy_chan_num
* D40_DREG_PCDELTA
+ D40_CHAN_REG_SSLNK
);
261 writel(lli_dst
->reg_cfg
, virtbase
+ D40_DREG_PCBASE
+
262 phy_chan_num
* D40_DREG_PCDELTA
+ D40_CHAN_REG_SDCFG
);
263 writel(lli_dst
->reg_elt
, virtbase
+ D40_DREG_PCBASE
+
264 phy_chan_num
* D40_DREG_PCDELTA
+ D40_CHAN_REG_SDELT
);
265 writel(lli_dst
->reg_ptr
, virtbase
+ D40_DREG_PCBASE
+
266 phy_chan_num
* D40_DREG_PCDELTA
+ D40_CHAN_REG_SDPTR
);
267 writel(lli_dst
->reg_lnk
, virtbase
+ D40_DREG_PCBASE
+
268 phy_chan_num
* D40_DREG_PCDELTA
+ D40_CHAN_REG_SDLNK
);
272 /* DMA logical lli operations */
274 void d40_log_fill_lli(struct d40_log_lli
*lli
,
275 dma_addr_t data
, u32 data_size
,
276 u32 lli_next_off
, u32 reg_cfg
,
278 bool term_int
, bool addr_inc
)
280 lli
->lcsp13
= reg_cfg
;
282 /* The number of elements to transfer */
283 lli
->lcsp02
= ((data_size
>> data_width
) <<
284 D40_MEM_LCSP0_ECNT_POS
) & D40_MEM_LCSP0_ECNT_MASK
;
285 /* 16 LSBs address of the current element */
286 lli
->lcsp02
|= data
& D40_MEM_LCSP0_SPTR_MASK
;
287 /* 16 MSBs address of the current element */
288 lli
->lcsp13
|= data
& D40_MEM_LCSP1_SPTR_MASK
;
291 lli
->lcsp13
|= D40_MEM_LCSP1_SCFG_INCR_MASK
;
293 lli
->lcsp13
|= D40_MEM_LCSP3_DTCP_MASK
;
294 /* If this scatter list entry is the last one, no next link */
295 lli
->lcsp13
|= (lli_next_off
<< D40_MEM_LCSP1_SLOS_POS
) &
296 D40_MEM_LCSP1_SLOS_MASK
;
299 lli
->lcsp13
|= D40_MEM_LCSP1_SCFG_TIM_MASK
;
301 lli
->lcsp13
&= ~D40_MEM_LCSP1_SCFG_TIM_MASK
;
304 int d40_log_sg_to_dev(struct d40_lcla_elem
*lcla
,
305 struct scatterlist
*sg
,
307 struct d40_log_lli_bidir
*lli
,
308 struct d40_def_lcsp
*lcsp
,
311 enum dma_data_direction direction
,
312 bool term_int
, dma_addr_t dev_addr
, int max_len
,
316 struct scatterlist
*current_sg
= sg
;
318 u32 next_lli_off_dst
= 0;
319 u32 next_lli_off_src
= 0;
321 for_each_sg(sg
, current_sg
, sg_len
, i
) {
322 total_size
+= sg_dma_len(current_sg
);
325 * If this scatter list entry is the last one or
326 * max length, terminate link.
328 if (sg_len
- 1 == i
|| ((i
+1) % max_len
== 0)) {
329 next_lli_off_src
= 0;
330 next_lli_off_dst
= 0;
332 if (next_lli_off_dst
== 0 &&
333 next_lli_off_src
== 0) {
334 /* The first lli will be at next_lli_off */
335 next_lli_off_dst
= (lcla
->dst_id
*
337 next_lli_off_src
= (lcla
->src_id
*
345 if (direction
== DMA_TO_DEVICE
) {
346 d40_log_fill_lli(&lli
->src
[i
],
348 sg_dma_len(current_sg
),
350 lcsp
->lcsp1
, src_data_width
,
353 d40_log_fill_lli(&lli
->dst
[i
],
355 sg_dma_len(current_sg
),
357 lcsp
->lcsp3
, dst_data_width
,
358 /* No next == terminal interrupt */
359 term_int
&& !next_lli_off_dst
,
362 d40_log_fill_lli(&lli
->dst
[i
],
364 sg_dma_len(current_sg
),
366 lcsp
->lcsp3
, dst_data_width
,
367 /* No next == terminal interrupt */
368 term_int
&& !next_lli_off_dst
,
370 d40_log_fill_lli(&lli
->src
[i
],
372 sg_dma_len(current_sg
),
374 lcsp
->lcsp1
, src_data_width
,
382 int d40_log_sg_to_lli(int lcla_id
,
383 struct scatterlist
*sg
,
385 struct d40_log_lli
*lli_sg
,
386 u32 lcsp13
, /* src or dst*/
388 bool term_int
, int max_len
, int llis_per_log
)
391 struct scatterlist
*current_sg
= sg
;
393 u32 next_lli_off
= 0;
395 for_each_sg(sg
, current_sg
, sg_len
, i
) {
396 total_size
+= sg_dma_len(current_sg
);
399 * If this scatter list entry is the last one or
400 * max length, terminate link.
402 if (sg_len
- 1 == i
|| ((i
+1) % max_len
== 0))
405 if (next_lli_off
== 0)
406 /* The first lli will be at next_lli_off */
407 next_lli_off
= lcla_id
* llis_per_log
+ 1;
412 d40_log_fill_lli(&lli_sg
[i
],
414 sg_dma_len(current_sg
),
417 term_int
&& !next_lli_off
,
423 int d40_log_lli_write(struct d40_log_lli_full
*lcpa
,
424 struct d40_log_lli
*lcla_src
,
425 struct d40_log_lli
*lcla_dst
,
426 struct d40_log_lli
*lli_dst
,
427 struct d40_log_lli
*lli_src
,
434 writel(lli_src
->lcsp02
, &lcpa
->lcsp0
);
435 writel(lli_src
->lcsp13
, &lcpa
->lcsp1
);
436 writel(lli_dst
->lcsp02
, &lcpa
->lcsp2
);
437 writel(lli_dst
->lcsp13
, &lcpa
->lcsp3
);
439 slos
= lli_src
->lcsp13
& D40_MEM_LCSP1_SLOS_MASK
;
440 dlos
= lli_dst
->lcsp13
& D40_MEM_LCSP3_DLOS_MASK
;
442 for (i
= 0; (i
< llis_per_log
) && slos
&& dlos
; i
++) {
443 writel(lli_src
[i
+ 1].lcsp02
, &lcla_src
[i
].lcsp02
);
444 writel(lli_src
[i
+ 1].lcsp13
, &lcla_src
[i
].lcsp13
);
445 writel(lli_dst
[i
+ 1].lcsp02
, &lcla_dst
[i
].lcsp02
);
446 writel(lli_dst
[i
+ 1].lcsp13
, &lcla_dst
[i
].lcsp13
);
448 slos
= lli_src
[i
+ 1].lcsp13
& D40_MEM_LCSP1_SLOS_MASK
;
449 dlos
= lli_dst
[i
+ 1].lcsp13
& D40_MEM_LCSP3_DLOS_MASK
;