aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma/ste_dma40_ll.c
diff options
context:
space:
mode:
authorPer Forlin <per.forlin@stericsson.com>2010-12-20 12:31:38 -0500
committerDan Williams <dan.j.williams@intel.com>2011-01-04 20:20:43 -0500
commitd49278e3351b34870cbffffc5067348a318e7b06 (patch)
treea8569e9a24286a0bd885f96e27342e4de2fdecbb /drivers/dma/ste_dma40_ll.c
parente8a7e48bb248a1196484d3f8afa53bded2b24e71 (diff)
dmaengine: dma40: Add support to split up large elements
The maximum transfer size of the stedma40 is (64k-1) x data-width. If the transfer size of one element exceeds this limit the job is split up and sent as linked transfer. Signed-off-by: Per Forlin <per.forlin@linaro.org> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/dma/ste_dma40_ll.c')
-rw-r--r--drivers/dma/ste_dma40_ll.c246
1 files changed, 172 insertions, 74 deletions
diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c
index 8557cb88b255..0b096a38322d 100644
--- a/drivers/dma/ste_dma40_ll.c
+++ b/drivers/dma/ste_dma40_ll.c
@@ -1,6 +1,6 @@
1/* 1/*
2 * Copyright (C) ST-Ericsson SA 2007-2010 2 * Copyright (C) ST-Ericsson SA 2007-2010
3 * Author: Per Friden <per.friden@stericsson.com> for ST-Ericsson 3 * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson
4 * Author: Jonas Aaberg <jonas.aberg@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 5 * License terms: GNU General Public License (GPL) version 2
6 */ 6 */
@@ -122,15 +122,15 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
122 *dst_cfg = dst; 122 *dst_cfg = dst;
123} 123}
124 124
125int d40_phy_fill_lli(struct d40_phy_lli *lli, 125static int d40_phy_fill_lli(struct d40_phy_lli *lli,
126 dma_addr_t data, 126 dma_addr_t data,
127 u32 data_size, 127 u32 data_size,
128 int psize, 128 int psize,
129 dma_addr_t next_lli, 129 dma_addr_t next_lli,
130 u32 reg_cfg, 130 u32 reg_cfg,
131 bool term_int, 131 bool term_int,
132 u32 data_width, 132 u32 data_width,
133 bool is_device) 133 bool is_device)
134{ 134{
135 int num_elems; 135 int num_elems;
136 136
@@ -139,13 +139,6 @@ int d40_phy_fill_lli(struct d40_phy_lli *lli,
139 else 139 else
140 num_elems = 2 << psize; 140 num_elems = 2 << psize;
141 141
142 /*
143 * Size is 16bit. data_width is 8, 16, 32 or 64 bit
144 * Block large than 64 KiB must be split.
145 */
146 if (data_size > (0xffff << data_width))
147 return -EINVAL;
148
149 /* Must be aligned */ 142 /* Must be aligned */
150 if (!IS_ALIGNED(data, 0x1 << data_width)) 143 if (!IS_ALIGNED(data, 0x1 << data_width))
151 return -EINVAL; 144 return -EINVAL;
@@ -187,55 +180,118 @@ int d40_phy_fill_lli(struct d40_phy_lli *lli,
187 return 0; 180 return 0;
188} 181}
189 182
183static int d40_seg_size(int size, int data_width1, int data_width2)
184{
185 u32 max_w = max(data_width1, data_width2);
186 u32 min_w = min(data_width1, data_width2);
187 u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w);
188
189 if (seg_max > STEDMA40_MAX_SEG_SIZE)
190 seg_max -= (1 << max_w);
191
192 if (size <= seg_max)
193 return size;
194
195 if (size <= 2 * seg_max)
196 return ALIGN(size / 2, 1 << max_w);
197
198 return seg_max;
199}
200
201struct d40_phy_lli *d40_phy_buf_to_lli(struct d40_phy_lli *lli,
202 dma_addr_t addr,
203 u32 size,
204 int psize,
205 dma_addr_t lli_phys,
206 u32 reg_cfg,
207 bool term_int,
208 u32 data_width1,
209 u32 data_width2,
210 bool is_device)
211{
212 int err;
213 dma_addr_t next = lli_phys;
214 int size_rest = size;
215 int size_seg = 0;
216
217 do {
218 size_seg = d40_seg_size(size_rest, data_width1, data_width2);
219 size_rest -= size_seg;
220
221 if (term_int && size_rest == 0)
222 next = 0;
223 else
224 next = ALIGN(next + sizeof(struct d40_phy_lli),
225 D40_LLI_ALIGN);
226
227 err = d40_phy_fill_lli(lli,
228 addr,
229 size_seg,
230 psize,
231 next,
232 reg_cfg,
233 !next,
234 data_width1,
235 is_device);
236
237 if (err)
238 goto err;
239
240 lli++;
241 if (!is_device)
242 addr += size_seg;
243 } while (size_rest);
244
245 return lli;
246
247 err:
248 return NULL;
249}
250
190int d40_phy_sg_to_lli(struct scatterlist *sg, 251int d40_phy_sg_to_lli(struct scatterlist *sg,
191 int sg_len, 252 int sg_len,
192 dma_addr_t target, 253 dma_addr_t target,
193 struct d40_phy_lli *lli, 254 struct d40_phy_lli *lli_sg,
194 dma_addr_t lli_phys, 255 dma_addr_t lli_phys,
195 u32 reg_cfg, 256 u32 reg_cfg,
196 u32 data_width, 257 u32 data_width1,
258 u32 data_width2,
197 int psize) 259 int psize)
198{ 260{
199 int total_size = 0; 261 int total_size = 0;
200 int i; 262 int i;
201 struct scatterlist *current_sg = sg; 263 struct scatterlist *current_sg = sg;
202 dma_addr_t next_lli_phys;
203 dma_addr_t dst; 264 dma_addr_t dst;
204 int err = 0; 265 struct d40_phy_lli *lli = lli_sg;
266 dma_addr_t l_phys = lli_phys;
205 267
206 for_each_sg(sg, current_sg, sg_len, i) { 268 for_each_sg(sg, current_sg, sg_len, i) {
207 269
208 total_size += sg_dma_len(current_sg); 270 total_size += sg_dma_len(current_sg);
209 271
210 /* If this scatter list entry is the last one, no next link */
211 if (sg_len - 1 == i)
212 next_lli_phys = 0;
213 else
214 next_lli_phys = ALIGN(lli_phys + (i + 1) *
215 sizeof(struct d40_phy_lli),
216 D40_LLI_ALIGN);
217
218 if (target) 272 if (target)
219 dst = target; 273 dst = target;
220 else 274 else
221 dst = sg_phys(current_sg); 275 dst = sg_phys(current_sg);
222 276
223 err = d40_phy_fill_lli(&lli[i], 277 l_phys = ALIGN(lli_phys + (lli - lli_sg) *
224 dst, 278 sizeof(struct d40_phy_lli), D40_LLI_ALIGN);
225 sg_dma_len(current_sg), 279
226 psize, 280 lli = d40_phy_buf_to_lli(lli,
227 next_lli_phys, 281 dst,
228 reg_cfg, 282 sg_dma_len(current_sg),
229 !next_lli_phys, 283 psize,
230 data_width, 284 l_phys,
231 target == dst); 285 reg_cfg,
232 if (err) 286 sg_len - 1 == i,
233 goto err; 287 data_width1,
288 data_width2,
289 target == dst);
290 if (lli == NULL)
291 return -EINVAL;
234 } 292 }
235 293
236 return total_size; 294 return total_size;
237err:
238 return err;
239} 295}
240 296
241 297
@@ -315,17 +371,20 @@ void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
315 writel(lli_dst->lcsp13, &lcla[1].lcsp13); 371 writel(lli_dst->lcsp13, &lcla[1].lcsp13);
316} 372}
317 373
318void d40_log_fill_lli(struct d40_log_lli *lli, 374static void d40_log_fill_lli(struct d40_log_lli *lli,
319 dma_addr_t data, u32 data_size, 375 dma_addr_t data, u32 data_size,
320 u32 reg_cfg, 376 u32 reg_cfg,
321 u32 data_width, 377 u32 data_width,
322 bool addr_inc) 378 bool addr_inc)
323{ 379{
324 lli->lcsp13 = reg_cfg; 380 lli->lcsp13 = reg_cfg;
325 381
326 /* The number of elements to transfer */ 382 /* The number of elements to transfer */
327 lli->lcsp02 = ((data_size >> data_width) << 383 lli->lcsp02 = ((data_size >> data_width) <<
328 D40_MEM_LCSP0_ECNT_POS) & D40_MEM_LCSP0_ECNT_MASK; 384 D40_MEM_LCSP0_ECNT_POS) & D40_MEM_LCSP0_ECNT_MASK;
385
386 BUG_ON((data_size >> data_width) > STEDMA40_MAX_SEG_SIZE);
387
329 /* 16 LSBs address of the current element */ 388 /* 16 LSBs address of the current element */
330 lli->lcsp02 |= data & D40_MEM_LCSP0_SPTR_MASK; 389 lli->lcsp02 |= data & D40_MEM_LCSP0_SPTR_MASK;
331 /* 16 MSBs address of the current element */ 390 /* 16 MSBs address of the current element */
@@ -348,55 +407,94 @@ int d40_log_sg_to_dev(struct scatterlist *sg,
348 int total_size = 0; 407 int total_size = 0;
349 struct scatterlist *current_sg = sg; 408 struct scatterlist *current_sg = sg;
350 int i; 409 int i;
410 struct d40_log_lli *lli_src = lli->src;
411 struct d40_log_lli *lli_dst = lli->dst;
351 412
352 for_each_sg(sg, current_sg, sg_len, i) { 413 for_each_sg(sg, current_sg, sg_len, i) {
353 total_size += sg_dma_len(current_sg); 414 total_size += sg_dma_len(current_sg);
354 415
355 if (direction == DMA_TO_DEVICE) { 416 if (direction == DMA_TO_DEVICE) {
356 d40_log_fill_lli(&lli->src[i], 417 lli_src =
357 sg_phys(current_sg), 418 d40_log_buf_to_lli(lli_src,
358 sg_dma_len(current_sg), 419 sg_phys(current_sg),
359 lcsp->lcsp1, src_data_width, 420 sg_dma_len(current_sg),
360 true); 421 lcsp->lcsp1, src_data_width,
361 d40_log_fill_lli(&lli->dst[i], 422 dst_data_width,
362 dev_addr, 423 true);
363 sg_dma_len(current_sg), 424 lli_dst =
364 lcsp->lcsp3, dst_data_width, 425 d40_log_buf_to_lli(lli_dst,
365 false); 426 dev_addr,
427 sg_dma_len(current_sg),
428 lcsp->lcsp3, dst_data_width,
429 src_data_width,
430 false);
366 } else { 431 } else {
367 d40_log_fill_lli(&lli->dst[i], 432 lli_dst =
368 sg_phys(current_sg), 433 d40_log_buf_to_lli(lli_dst,
369 sg_dma_len(current_sg), 434 sg_phys(current_sg),
370 lcsp->lcsp3, dst_data_width, 435 sg_dma_len(current_sg),
371 true); 436 lcsp->lcsp3, dst_data_width,
372 d40_log_fill_lli(&lli->src[i], 437 src_data_width,
373 dev_addr, 438 true);
374 sg_dma_len(current_sg), 439 lli_src =
375 lcsp->lcsp1, src_data_width, 440 d40_log_buf_to_lli(lli_src,
376 false); 441 dev_addr,
442 sg_dma_len(current_sg),
443 lcsp->lcsp1, src_data_width,
444 dst_data_width,
445 false);
377 } 446 }
378 } 447 }
379 return total_size; 448 return total_size;
380} 449}
381 450
451struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg,
452 dma_addr_t addr,
453 int size,
454 u32 lcsp13, /* src or dst*/
455 u32 data_width1,
456 u32 data_width2,
457 bool addr_inc)
458{
459 struct d40_log_lli *lli = lli_sg;
460 int size_rest = size;
461 int size_seg = 0;
462
463 do {
464 size_seg = d40_seg_size(size_rest, data_width1, data_width2);
465 size_rest -= size_seg;
466
467 d40_log_fill_lli(lli,
468 addr,
469 size_seg,
470 lcsp13, data_width1,
471 addr_inc);
472 if (addr_inc)
473 addr += size_seg;
474 lli++;
475 } while (size_rest);
476
477 return lli;
478}
479
382int d40_log_sg_to_lli(struct scatterlist *sg, 480int d40_log_sg_to_lli(struct scatterlist *sg,
383 int sg_len, 481 int sg_len,
384 struct d40_log_lli *lli_sg, 482 struct d40_log_lli *lli_sg,
385 u32 lcsp13, /* src or dst*/ 483 u32 lcsp13, /* src or dst*/
386 u32 data_width) 484 u32 data_width1, u32 data_width2)
387{ 485{
388 int total_size = 0; 486 int total_size = 0;
389 struct scatterlist *current_sg = sg; 487 struct scatterlist *current_sg = sg;
390 int i; 488 int i;
489 struct d40_log_lli *lli = lli_sg;
391 490
392 for_each_sg(sg, current_sg, sg_len, i) { 491 for_each_sg(sg, current_sg, sg_len, i) {
393 total_size += sg_dma_len(current_sg); 492 total_size += sg_dma_len(current_sg);
394 493 lli = d40_log_buf_to_lli(lli,
395 d40_log_fill_lli(&lli_sg[i], 494 sg_phys(current_sg),
396 sg_phys(current_sg), 495 sg_dma_len(current_sg),
397 sg_dma_len(current_sg), 496 lcsp13,
398 lcsp13, data_width, 497 data_width1, data_width2, true);
399 true);
400 } 498 }
401 return total_size; 499 return total_size;
402} 500}