1 /***********************license start***************
2 * Copyright (c) 2003-2010 Cavium Inc. ([email protected]). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17
18 * * Neither the name of Cavium Inc. nor the names of
19 * its contributors may be used to endorse or promote products
20 * derived from this software without specific prior written
21 * permission.
22
23 * This Software, including technical data, may be subject to U.S. export control
24 * laws, including the U.S. Export Administration Act and its associated
25 * regulations, and may be subject to export or import regulations in other
26 * countries.
27
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39
40
41
42
43
44
45
46 /**
47 * @file
48 *
49 * Interface to the PCI / PCIe DMA engines. These are only avialable
50 * on chips with PCI / PCIe.
51 *
52 * <hr>$Revision: 70030 $<hr>
53 */
54 #ifdef CVMX_BUILD_FOR_LINUX_KERNEL
55 #include <linux/module.h>
56 #include <asm/octeon/cvmx.h>
57 #include <asm/octeon/octeon-model.h>
58 #include <asm/octeon/cvmx-config.h>
59 #include <asm/octeon/cvmx-cmd-queue.h>
60 #include <asm/octeon/cvmx-dma-engine.h>
61 #include <asm/octeon/octeon-feature.h>
62 #include <asm/octeon/cvmx-npi-defs.h>
63 #include <asm/octeon/cvmx-npei-defs.h>
64 #include <asm/octeon/cvmx-dpi-defs.h>
65 #include <asm/octeon/cvmx-pexp-defs.h>
66 #include <asm/octeon/cvmx-helper-cfg.h>
67 #else
68 #if !defined(__FreeBSD__) || !defined(_KERNEL)
69 #include "executive-config.h"
70 #include "cvmx-config.h"
71 #endif
72 #include "cvmx.h"
73 #include "cvmx-cmd-queue.h"
74 #include "cvmx-dma-engine.h"
75 #include "cvmx-helper-cfg.h"
76 #endif
77
78 #ifdef CVMX_ENABLE_PKO_FUNCTIONS
79
80 /**
81 * Return the number of DMA engimes supported by this chip
82 *
83 * @return Number of DMA engines
84 */
cvmx_dma_engine_get_num(void)85 int cvmx_dma_engine_get_num(void)
86 {
87 if (octeon_has_feature(OCTEON_FEATURE_NPEI))
88 {
89 if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
90 return 4;
91 else
92 return 5;
93 }
94 else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
95 return 8;
96 else
97 return 2;
98 }
99
100 /**
101 * Initialize the DMA engines for use
102 *
103 * @return Zero on success, negative on failure
104 */
cvmx_dma_engine_initialize(void)105 int cvmx_dma_engine_initialize(void)
106 {
107 int engine;
108
109 for (engine=0; engine < cvmx_dma_engine_get_num(); engine++)
110 {
111 cvmx_cmd_queue_result_t result;
112 result = cvmx_cmd_queue_initialize(CVMX_CMD_QUEUE_DMA(engine),
113 0, CVMX_FPA_OUTPUT_BUFFER_POOL,
114 CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE);
115 if (result != CVMX_CMD_QUEUE_SUCCESS)
116 return -1;
117 if (octeon_has_feature(OCTEON_FEATURE_NPEI))
118 {
119 cvmx_npei_dmax_ibuff_saddr_t dmax_ibuff_saddr;
120 dmax_ibuff_saddr.u64 = 0;
121 dmax_ibuff_saddr.s.saddr = cvmx_ptr_to_phys(cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_DMA(engine))) >> 7;
122 cvmx_write_csr(CVMX_PEXP_NPEI_DMAX_IBUFF_SADDR(engine), dmax_ibuff_saddr.u64);
123 }
124 else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
125 {
126 cvmx_dpi_dmax_ibuff_saddr_t dpi_dmax_ibuff_saddr;
127 dpi_dmax_ibuff_saddr.u64 = 0;
128 dpi_dmax_ibuff_saddr.s.csize = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/8;
129 dpi_dmax_ibuff_saddr.s.saddr = cvmx_ptr_to_phys(cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_DMA(engine))) >> 7;
130 cvmx_write_csr(CVMX_DPI_DMAX_IBUFF_SADDR(engine), dpi_dmax_ibuff_saddr.u64);
131 }
132 else
133 {
134 uint64_t address = cvmx_ptr_to_phys(cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_DMA(engine)));
135 if (engine)
136 cvmx_write_csr(CVMX_NPI_HIGHP_IBUFF_SADDR, address);
137 else
138 cvmx_write_csr(CVMX_NPI_LOWP_IBUFF_SADDR, address);
139 }
140 }
141
142 if (octeon_has_feature(OCTEON_FEATURE_NPEI))
143 {
144 cvmx_npei_dma_control_t dma_control;
145 dma_control.u64 = 0;
146 if (cvmx_dma_engine_get_num() >= 5)
147 dma_control.s.dma4_enb = 1;
148 dma_control.s.dma3_enb = 1;
149 dma_control.s.dma2_enb = 1;
150 dma_control.s.dma1_enb = 1;
151 dma_control.s.dma0_enb = 1;
152 dma_control.s.o_mode = 1; /* Pull NS and RO from this register, not the pointers */
153 //dma_control.s.dwb_denb = 1;
154 //dma_control.s.dwb_ichk = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/128;
155 dma_control.s.fpa_que = CVMX_FPA_OUTPUT_BUFFER_POOL;
156 dma_control.s.csize = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/8;
157 cvmx_write_csr(CVMX_PEXP_NPEI_DMA_CONTROL, dma_control.u64);
158 /* As a workaround for errata PCIE-811 we only allow a single
159 outstanding DMA read over PCIe at a time. This limits performance,
160 but works in all cases. If you need higher performance, remove
161 this code and implement the more complicated workaround documented
162 in the errata. This only affects CN56XX pass 2.0 chips */
163 if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_0))
164 {
165 cvmx_npei_dma_pcie_req_num_t pcie_req_num;
166 pcie_req_num.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DMA_PCIE_REQ_NUM);
167 pcie_req_num.s.dma_cnt = 1;
168 cvmx_write_csr(CVMX_PEXP_NPEI_DMA_PCIE_REQ_NUM, pcie_req_num.u64);
169 }
170 }
171 else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
172 {
173 cvmx_dpi_engx_buf_t dpi_engx_buf;
174 cvmx_dpi_dma_engx_en_t dpi_dma_engx_en;
175 cvmx_dpi_dma_control_t dma_control;
176 cvmx_dpi_ctl_t dpi_ctl;
177
178 /* Give engine 0-4 1KB, and 5 3KB. This gives the packet engines better
179 performance. Total must not exceed 8KB */
180 dpi_engx_buf.u64 = 0;
181 dpi_engx_buf.s.blks = 2;
182 cvmx_write_csr(CVMX_DPI_ENGX_BUF(0), dpi_engx_buf.u64);
183 cvmx_write_csr(CVMX_DPI_ENGX_BUF(1), dpi_engx_buf.u64);
184 cvmx_write_csr(CVMX_DPI_ENGX_BUF(2), dpi_engx_buf.u64);
185 cvmx_write_csr(CVMX_DPI_ENGX_BUF(3), dpi_engx_buf.u64);
186 cvmx_write_csr(CVMX_DPI_ENGX_BUF(4), dpi_engx_buf.u64);
187 dpi_engx_buf.s.blks = 6;
188 cvmx_write_csr(CVMX_DPI_ENGX_BUF(5), dpi_engx_buf.u64);
189
190 dma_control.u64 = cvmx_read_csr(CVMX_DPI_DMA_CONTROL);
191 dma_control.s.pkt_hp = 1;
192 dma_control.s.pkt_en = 1;
193 dma_control.s.dma_enb = 0x1f;
194 dma_control.s.dwb_denb = cvmx_helper_cfg_opt_get(CVMX_HELPER_CFG_OPT_USE_DWB);
195 dma_control.s.dwb_ichk = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/128;
196 dma_control.s.fpa_que = CVMX_FPA_OUTPUT_BUFFER_POOL;
197 dma_control.s.o_mode = 1;
198 cvmx_write_csr(CVMX_DPI_DMA_CONTROL, dma_control.u64);
199 /* When dma_control[pkt_en] = 1, engine 5 is used for packets and is not
200 available for DMA. */
201 dpi_dma_engx_en.u64 = cvmx_read_csr(CVMX_DPI_DMA_ENGX_EN(5));
202 dpi_dma_engx_en.s.qen = 0;
203 cvmx_write_csr(CVMX_DPI_DMA_ENGX_EN(5), dpi_dma_engx_en.u64);
204 dpi_ctl.u64 = cvmx_read_csr(CVMX_DPI_CTL);
205 dpi_ctl.s.en = 1;
206 cvmx_write_csr(CVMX_DPI_CTL, dpi_ctl.u64);
207 }
208 else
209 {
210 cvmx_npi_dma_control_t dma_control;
211 dma_control.u64 = 0;
212 //dma_control.s.dwb_denb = 1;
213 //dma_control.s.dwb_ichk = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/128;
214 dma_control.s.o_add1 = 1;
215 dma_control.s.fpa_que = CVMX_FPA_OUTPUT_BUFFER_POOL;
216 dma_control.s.hp_enb = 1;
217 dma_control.s.lp_enb = 1;
218 dma_control.s.csize = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/8;
219 cvmx_write_csr(CVMX_NPI_DMA_CONTROL, dma_control.u64);
220 }
221
222 return 0;
223 }
224 #ifdef CVMX_BUILD_FOR_LINUX_KERNEL
225 EXPORT_SYMBOL(cvmx_dma_engine_initialize);
226 #endif
227
228 /**
229 * Shutdown all DMA engines. The engines must be idle when this
230 * function is called.
231 *
232 * @return Zero on success, negative on failure
233 */
cvmx_dma_engine_shutdown(void)234 int cvmx_dma_engine_shutdown(void)
235 {
236 int engine;
237
238 for (engine=0; engine < cvmx_dma_engine_get_num(); engine++)
239 {
240 if (cvmx_cmd_queue_length(CVMX_CMD_QUEUE_DMA(engine)))
241 {
242 cvmx_dprintf("ERROR: cvmx_dma_engine_shutdown: Engine not idle.\n");
243 return -1;
244 }
245 }
246
247 if (octeon_has_feature(OCTEON_FEATURE_NPEI))
248 {
249 cvmx_npei_dma_control_t dma_control;
250 dma_control.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DMA_CONTROL);
251 if (cvmx_dma_engine_get_num() >= 5)
252 dma_control.s.dma4_enb = 0;
253 dma_control.s.dma3_enb = 0;
254 dma_control.s.dma2_enb = 0;
255 dma_control.s.dma1_enb = 0;
256 dma_control.s.dma0_enb = 0;
257 cvmx_write_csr(CVMX_PEXP_NPEI_DMA_CONTROL, dma_control.u64);
258 /* Make sure the disable completes */
259 cvmx_read_csr(CVMX_PEXP_NPEI_DMA_CONTROL);
260 }
261 else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
262 {
263 cvmx_dpi_dma_control_t dma_control;
264 dma_control.u64 = cvmx_read_csr(CVMX_DPI_DMA_CONTROL);
265 dma_control.s.dma_enb = 0;
266 cvmx_write_csr(CVMX_DPI_DMA_CONTROL, dma_control.u64);
267 /* Make sure the disable completes */
268 cvmx_read_csr(CVMX_DPI_DMA_CONTROL);
269 }
270 else
271 {
272 cvmx_npi_dma_control_t dma_control;
273 dma_control.u64 = cvmx_read_csr(CVMX_NPI_DMA_CONTROL);
274 dma_control.s.hp_enb = 0;
275 dma_control.s.lp_enb = 0;
276 cvmx_write_csr(CVMX_NPI_DMA_CONTROL, dma_control.u64);
277 /* Make sure the disable completes */
278 cvmx_read_csr(CVMX_NPI_DMA_CONTROL);
279 }
280
281 for (engine=0; engine < cvmx_dma_engine_get_num(); engine++)
282 {
283 cvmx_cmd_queue_shutdown(CVMX_CMD_QUEUE_DMA(engine));
284 if (octeon_has_feature(OCTEON_FEATURE_NPEI))
285 cvmx_write_csr(CVMX_PEXP_NPEI_DMAX_IBUFF_SADDR(engine), 0);
286 else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
287 cvmx_write_csr(CVMX_DPI_DMAX_IBUFF_SADDR(engine), 0);
288 else
289 {
290 if (engine)
291 cvmx_write_csr(CVMX_NPI_HIGHP_IBUFF_SADDR, 0);
292 else
293 cvmx_write_csr(CVMX_NPI_LOWP_IBUFF_SADDR, 0);
294 }
295 }
296
297 return 0;
298 }
299 #ifdef CVMX_BUILD_FOR_LINUX_KERNEL
300 EXPORT_SYMBOL(cvmx_dma_engine_shutdown);
301 #endif
302
303 /**
304 * Submit a series of DMA command to the DMA engines.
305 *
306 * @param engine Engine to submit to (0 to cvmx_dma_engine_get_num()-1)
307 * @param header Command header
308 * @param num_buffers
309 * The number of data pointers
310 * @param buffers Command data pointers
311 *
312 * @return Zero on success, negative on failure
313 */
cvmx_dma_engine_submit(int engine,cvmx_dma_engine_header_t header,int num_buffers,cvmx_dma_engine_buffer_t buffers[])314 int cvmx_dma_engine_submit(int engine, cvmx_dma_engine_header_t header, int num_buffers, cvmx_dma_engine_buffer_t buffers[])
315 {
316 cvmx_cmd_queue_result_t result;
317 int cmd_count = 1;
318 uint64_t cmds[num_buffers + 1];
319
320 if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X))
321 {
322 /* Check for Errata PCIe-604 */
323 if ((header.s.nfst > 11) || (header.s.nlst > 11) || (header.s.nfst + header.s.nlst > 15))
324 {
325 cvmx_dprintf("DMA engine submit too large\n");
326 return -1;
327 }
328 }
329
330 cmds[0] = header.u64;
331 while (num_buffers--)
332 {
333 cmds[cmd_count++] = buffers->u64;
334 buffers++;
335 }
336
337 /* Due to errata PCIE-13315, it is necessary to have the queue lock while we
338 ring the doorbell for the DMA engines. This prevents doorbells from
339 possibly arriving out of order with respect to the command queue
340 entries */
341 __cvmx_cmd_queue_lock(CVMX_CMD_QUEUE_DMA(engine), __cvmx_cmd_queue_get_state(CVMX_CMD_QUEUE_DMA(engine)));
342 result = cvmx_cmd_queue_write(CVMX_CMD_QUEUE_DMA(engine), 0, cmd_count, cmds);
343 /* This SYNCWS is needed since the command queue didn't do locking, which
344 normally implies the SYNCWS. This one makes sure the command queue
345 updates make it to L2 before we ring the doorbell */
346 CVMX_SYNCWS;
347 /* A syncw isn't needed here since the command queue did one as part of the queue unlock */
348 if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS))
349 {
350 if (octeon_has_feature(OCTEON_FEATURE_NPEI))
351 {
352 /* DMA doorbells are 32bit writes in little endian space. This means we need to xor the address with 4 */
353 cvmx_write64_uint32(CVMX_PEXP_NPEI_DMAX_DBELL(engine)^4, cmd_count);
354 }
355 else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
356 cvmx_write_csr(CVMX_DPI_DMAX_DBELL(engine), cmd_count);
357 else
358 {
359 if (engine)
360 cvmx_write_csr(CVMX_NPI_HIGHP_DBELL, cmd_count);
361 else
362 cvmx_write_csr(CVMX_NPI_LOWP_DBELL, cmd_count);
363 }
364 }
365 /* Here is the unlock for the above errata workaround */
366 __cvmx_cmd_queue_unlock(__cvmx_cmd_queue_get_state(CVMX_CMD_QUEUE_DMA(engine)));
367 return result;
368 }
369
370
371 /**
372 * @INTERNAL
373 * Function used by cvmx_dma_engine_transfer() to build the
374 * internal address list.
375 *
376 * @param buffers Location to store the list
377 * @param address Address to build list for
378 * @param size Length of the memory pointed to by address
379 *
380 * @return Number of internal pointer chunks created
381 */
__cvmx_dma_engine_build_internal_pointers(cvmx_dma_engine_buffer_t * buffers,uint64_t address,int size)382 static inline int __cvmx_dma_engine_build_internal_pointers(cvmx_dma_engine_buffer_t *buffers, uint64_t address, int size)
383 {
384 int segments = 0;
385 while (size)
386 {
387 /* Each internal chunk can contain a maximum of 8191 bytes */
388 int chunk = size;
389 if (chunk > 8191)
390 chunk = 8191;
391 buffers[segments].u64 = 0;
392 buffers[segments].internal.size = chunk;
393 buffers[segments].internal.addr = address;
394 address += chunk;
395 size -= chunk;
396 segments++;
397 }
398 return segments;
399 }
400
401
402 /**
403 * @INTERNAL
404 * Function used by cvmx_dma_engine_transfer() to build the PCI / PCIe address
405 * list.
406 * @param buffers Location to store the list
407 * @param address Address to build list for
408 * @param size Length of the memory pointed to by address
409 *
410 * @return Number of PCI / PCIe address chunks created. The number of words used
411 * will be segments + (segments-1)/4 + 1.
412 */
__cvmx_dma_engine_build_external_pointers(cvmx_dma_engine_buffer_t * buffers,uint64_t address,int size)413 static inline int __cvmx_dma_engine_build_external_pointers(cvmx_dma_engine_buffer_t *buffers, uint64_t address, int size)
414 {
415 const int MAX_SIZE = 65535;
416 int segments = 0;
417 while (size)
418 {
419 /* Each block of 4 PCI / PCIe pointers uses one dword for lengths followed by
420 up to 4 addresses. This then repeats if more data is needed */
421 buffers[0].u64 = 0;
422 if (size <= MAX_SIZE)
423 {
424 /* Only one more segment needed */
425 buffers[0].pcie_length.len0 = size;
426 buffers[1].u64 = address;
427 segments++;
428 break;
429 }
430 else if (size <= MAX_SIZE * 2)
431 {
432 /* Two more segments needed */
433 buffers[0].pcie_length.len0 = MAX_SIZE;
434 buffers[0].pcie_length.len1 = size - MAX_SIZE;
435 buffers[1].u64 = address;
436 address += MAX_SIZE;
437 buffers[2].u64 = address;
438 segments+=2;
439 break;
440 }
441 else if (size <= MAX_SIZE * 3)
442 {
443 /* Three more segments needed */
444 buffers[0].pcie_length.len0 = MAX_SIZE;
445 buffers[0].pcie_length.len1 = MAX_SIZE;
446 buffers[0].pcie_length.len2 = size - MAX_SIZE * 2;
447 buffers[1].u64 = address;
448 address += MAX_SIZE;
449 buffers[2].u64 = address;
450 address += MAX_SIZE;
451 buffers[3].u64 = address;
452 segments+=3;
453 break;
454 }
455 else if (size <= MAX_SIZE * 4)
456 {
457 /* Four more segments needed */
458 buffers[0].pcie_length.len0 = MAX_SIZE;
459 buffers[0].pcie_length.len1 = MAX_SIZE;
460 buffers[0].pcie_length.len2 = MAX_SIZE;
461 buffers[0].pcie_length.len3 = size - MAX_SIZE * 3;
462 buffers[1].u64 = address;
463 address += MAX_SIZE;
464 buffers[2].u64 = address;
465 address += MAX_SIZE;
466 buffers[3].u64 = address;
467 address += MAX_SIZE;
468 buffers[4].u64 = address;
469 segments+=4;
470 break;
471 }
472 else
473 {
474 /* Five or more segments are needed */
475 buffers[0].pcie_length.len0 = MAX_SIZE;
476 buffers[0].pcie_length.len1 = MAX_SIZE;
477 buffers[0].pcie_length.len2 = MAX_SIZE;
478 buffers[0].pcie_length.len3 = MAX_SIZE;
479 buffers[1].u64 = address;
480 address += MAX_SIZE;
481 buffers[2].u64 = address;
482 address += MAX_SIZE;
483 buffers[3].u64 = address;
484 address += MAX_SIZE;
485 buffers[4].u64 = address;
486 address += MAX_SIZE;
487 size -= MAX_SIZE*4;
488 buffers += 5;
489 segments+=4;
490 }
491 }
492 return segments;
493 }
494
495
496 /**
497 * Build the first and last pointers based on a DMA engine header
498 * and submit them to the engine. The purpose of this function is
499 * to simplify the building of DMA engine commands by automatically
500 * converting a simple address and size into the apropriate internal
501 * or PCI / PCIe address list. This function does not support gather lists,
502 * so you will need to build your own lists in that case.
503 *
504 * @param engine Engine to submit to (0 to cvmx_dma_engine_get_num()-1)
505 * @param header DMA Command header. Note that the nfst and nlst fields do not
506 * need to be filled in. All other fields must be set properly.
507 * @param first_address
508 * Address to use for the first pointers. In the case of INTERNAL,
509 * INBOUND, and OUTBOUND this is an Octeon memory address. In the
510 * case of EXTERNAL, this is the source PCI / PCIe address.
511 * @param last_address
512 * Address to use for the last pointers. In the case of EXTERNAL,
513 * INBOUND, and OUTBOUND this is a PCI / PCIe address. In the
514 * case of INTERNAL, this is the Octeon memory destination address.
515 * @param size Size of the transfer to perform.
516 *
517 * @return Zero on success, negative on failure
518 */
cvmx_dma_engine_transfer(int engine,cvmx_dma_engine_header_t header,uint64_t first_address,uint64_t last_address,int size)519 int cvmx_dma_engine_transfer(int engine, cvmx_dma_engine_header_t header,
520 uint64_t first_address, uint64_t last_address,
521 int size)
522 {
523 cvmx_dma_engine_buffer_t buffers[32];
524 int words = 0;
525
526 switch (header.s.type)
527 {
528 case CVMX_DMA_ENGINE_TRANSFER_INTERNAL:
529 header.s.nfst = __cvmx_dma_engine_build_internal_pointers(buffers, first_address, size);
530 words += header.s.nfst;
531 header.s.nlst = __cvmx_dma_engine_build_internal_pointers(buffers + words, last_address, size);
532 words += header.s.nlst;
533 break;
534 case CVMX_DMA_ENGINE_TRANSFER_INBOUND:
535 case CVMX_DMA_ENGINE_TRANSFER_OUTBOUND:
536 header.s.nfst = __cvmx_dma_engine_build_internal_pointers(buffers, first_address, size);
537 words += header.s.nfst;
538 header.s.nlst = __cvmx_dma_engine_build_external_pointers(buffers + words, last_address, size);
539 words += header.s.nlst + ((header.s.nlst-1) >> 2) + 1;
540 break;
541 case CVMX_DMA_ENGINE_TRANSFER_EXTERNAL:
542 header.s.nfst = __cvmx_dma_engine_build_external_pointers(buffers, first_address, size);
543 words += header.s.nfst + ((header.s.nfst-1) >> 2) + 1;
544 header.s.nlst = __cvmx_dma_engine_build_external_pointers(buffers + words, last_address, size);
545 words += header.s.nlst + ((header.s.nlst-1) >> 2) + 1;
546 break;
547 }
548 return cvmx_dma_engine_submit(engine, header, words, buffers);
549 }
550 #ifdef CVMX_BUILD_FOR_LINUX_KERNEL
551 EXPORT_SYMBOL(cvmx_dma_engine_transfer);
552 #endif
553 #endif
554