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