cvmx-dma-engine.c revision 215990
1210284Sjmallett/***********************license start***************
2215990Sjmallett * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
3215990Sjmallett * reserved.
4210284Sjmallett *
5210284Sjmallett *
6215990Sjmallett * Redistribution and use in source and binary forms, with or without
7215990Sjmallett * modification, are permitted provided that the following conditions are
8215990Sjmallett * met:
9210284Sjmallett *
10215990Sjmallett *   * Redistributions of source code must retain the above copyright
11215990Sjmallett *     notice, this list of conditions and the following disclaimer.
12210284Sjmallett *
13215990Sjmallett *   * Redistributions in binary form must reproduce the above
14215990Sjmallett *     copyright notice, this list of conditions and the following
15215990Sjmallett *     disclaimer in the documentation and/or other materials provided
16215990Sjmallett *     with the distribution.
17215990Sjmallett
18215990Sjmallett *   * Neither the name of Cavium Networks nor the names of
19215990Sjmallett *     its contributors may be used to endorse or promote products
20215990Sjmallett *     derived from this software without specific prior written
21215990Sjmallett *     permission.
22215990Sjmallett
23215990Sjmallett * This Software, including technical data, may be subject to U.S. export  control
24215990Sjmallett * laws, including the U.S. Export Administration Act and its  associated
25215990Sjmallett * regulations, and may be subject to export or import  regulations in other
26215990Sjmallett * countries.
27215990Sjmallett
28215990Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29215990Sjmallett * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
30215990Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31215990Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32215990Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33215990Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34215990Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35215990Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36215990Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37215990Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38210284Sjmallett ***********************license end**************************************/
39210284Sjmallett
40210284Sjmallett
41210284Sjmallett
42210284Sjmallett
43210284Sjmallett
44210284Sjmallett
45215990Sjmallett
46210284Sjmallett/**
47210284Sjmallett * @file
48210284Sjmallett *
49210284Sjmallett * Interface to the PCI / PCIe DMA engines. These are only avialable
50210284Sjmallett * on chips with PCI / PCIe.
51210284Sjmallett *
52215990Sjmallett * <hr>$Revision: 50126 $<hr>
53210284Sjmallett */
54210284Sjmallett#include "executive-config.h"
55210284Sjmallett#include "cvmx-config.h"
56210284Sjmallett#include "cvmx.h"
57210284Sjmallett#include "cvmx-cmd-queue.h"
58210284Sjmallett#include "cvmx-dma-engine.h"
59210284Sjmallett
60210284Sjmallett#ifdef CVMX_ENABLE_PKO_FUNCTIONS
61210284Sjmallett
62210284Sjmallett/**
63210284Sjmallett * Return the number of DMA engimes supported by this chip
64210284Sjmallett *
65210284Sjmallett * @return Number of DMA engines
66210284Sjmallett */
67210284Sjmallettint cvmx_dma_engine_get_num(void)
68210284Sjmallett{
69215990Sjmallett    if (octeon_has_feature(OCTEON_FEATURE_NPEI))
70210284Sjmallett    {
71210284Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
72210284Sjmallett            return 4;
73210284Sjmallett        else
74210284Sjmallett            return 5;
75210284Sjmallett    }
76215990Sjmallett    else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
77215990Sjmallett        return 8;
78210284Sjmallett    else
79210284Sjmallett        return 2;
80210284Sjmallett}
81210284Sjmallett
82210284Sjmallett/**
83210284Sjmallett * Initialize the DMA engines for use
84210284Sjmallett *
85210284Sjmallett * @return Zero on success, negative on failure
86210284Sjmallett */
87210284Sjmallettint cvmx_dma_engine_initialize(void)
88210284Sjmallett{
89210284Sjmallett    int engine;
90210284Sjmallett
91210284Sjmallett    for (engine=0; engine < cvmx_dma_engine_get_num(); engine++)
92210284Sjmallett    {
93210284Sjmallett        cvmx_cmd_queue_result_t result;
94210284Sjmallett        result = cvmx_cmd_queue_initialize(CVMX_CMD_QUEUE_DMA(engine),
95210284Sjmallett                                           0, CVMX_FPA_OUTPUT_BUFFER_POOL,
96210284Sjmallett                                           CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE);
97210284Sjmallett        if (result != CVMX_CMD_QUEUE_SUCCESS)
98210284Sjmallett            return -1;
99215990Sjmallett        if (octeon_has_feature(OCTEON_FEATURE_NPEI))
100215990Sjmallett        {
101215990Sjmallett            cvmx_npei_dmax_ibuff_saddr_t dmax_ibuff_saddr;
102215990Sjmallett            dmax_ibuff_saddr.u64 = 0;
103215990Sjmallett            dmax_ibuff_saddr.s.saddr = cvmx_ptr_to_phys(cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_DMA(engine))) >> 7;
104210284Sjmallett            cvmx_write_csr(CVMX_PEXP_NPEI_DMAX_IBUFF_SADDR(engine), dmax_ibuff_saddr.u64);
105215990Sjmallett        }
106215990Sjmallett        else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
107215990Sjmallett        {
108215990Sjmallett            cvmx_dpi_dmax_ibuff_saddr_t dpi_dmax_ibuff_saddr;
109215990Sjmallett            dpi_dmax_ibuff_saddr.u64 = 0;
110215990Sjmallett            dpi_dmax_ibuff_saddr.s.csize = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/8;
111215990Sjmallett            dpi_dmax_ibuff_saddr.s.saddr = cvmx_ptr_to_phys(cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_DMA(engine))) >> 7;
112215990Sjmallett            cvmx_write_csr(CVMX_DPI_DMAX_IBUFF_SADDR(engine), dpi_dmax_ibuff_saddr.u64);
113215990Sjmallett        }
114210284Sjmallett        else
115210284Sjmallett        {
116215990Sjmallett            uint64_t address = cvmx_ptr_to_phys(cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_DMA(engine)));
117210284Sjmallett            if (engine)
118215990Sjmallett                cvmx_write_csr(CVMX_NPI_HIGHP_IBUFF_SADDR, address);
119210284Sjmallett            else
120215990Sjmallett                cvmx_write_csr(CVMX_NPI_LOWP_IBUFF_SADDR, address);
121210284Sjmallett        }
122210284Sjmallett    }
123210284Sjmallett
124215990Sjmallett    if (octeon_has_feature(OCTEON_FEATURE_NPEI))
125210284Sjmallett    {
126210284Sjmallett        cvmx_npei_dma_control_t dma_control;
127210284Sjmallett        dma_control.u64 = 0;
128210284Sjmallett        if (cvmx_dma_engine_get_num() >= 5)
129210284Sjmallett            dma_control.s.dma4_enb = 1;
130210284Sjmallett        dma_control.s.dma3_enb = 1;
131210284Sjmallett        dma_control.s.dma2_enb = 1;
132210284Sjmallett        dma_control.s.dma1_enb = 1;
133210284Sjmallett        dma_control.s.dma0_enb = 1;
134210284Sjmallett        dma_control.s.o_mode = 1; /* Pull NS and RO from this register, not the pointers */
135210284Sjmallett        //dma_control.s.dwb_denb = 1;
136210284Sjmallett        //dma_control.s.dwb_ichk = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/128;
137210284Sjmallett        dma_control.s.fpa_que = CVMX_FPA_OUTPUT_BUFFER_POOL;
138210284Sjmallett        dma_control.s.csize = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/8;
139210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_DMA_CONTROL, dma_control.u64);
140210284Sjmallett        /* As a workaround for errata PCIE-811 we only allow a single
141210284Sjmallett            outstanding DMA read over PCIe at a time. This limits performance,
142210284Sjmallett            but works in all cases. If you need higher performance, remove
143210284Sjmallett            this code and implement the more complicated workaround documented
144210284Sjmallett            in the errata. This only affects CN56XX pass 2.0 chips */
145210284Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_0))
146210284Sjmallett        {
147210284Sjmallett            cvmx_npei_dma_pcie_req_num_t pcie_req_num;
148210284Sjmallett            pcie_req_num.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DMA_PCIE_REQ_NUM);
149210284Sjmallett            pcie_req_num.s.dma_cnt = 1;
150210284Sjmallett            cvmx_write_csr(CVMX_PEXP_NPEI_DMA_PCIE_REQ_NUM, pcie_req_num.u64);
151210284Sjmallett        }
152210284Sjmallett    }
153215990Sjmallett    else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
154215990Sjmallett    {
155215990Sjmallett        cvmx_dpi_engx_buf_t dpi_engx_buf;
156215990Sjmallett        cvmx_dpi_dma_control_t dma_control;
157215990Sjmallett        cvmx_dpi_ctl_t dpi_ctl;
158215990Sjmallett
159215990Sjmallett        /* Give engine 0-4 1KB, and 5 3KB. This gives the packet engines better
160215990Sjmallett            performance. Total must not exceed 8KB */
161215990Sjmallett        dpi_engx_buf.u64 = 0;
162215990Sjmallett        dpi_engx_buf.s.blks = 2;
163215990Sjmallett        cvmx_write_csr(CVMX_DPI_ENGX_BUF(0), dpi_engx_buf.u64);
164215990Sjmallett        cvmx_write_csr(CVMX_DPI_ENGX_BUF(1), dpi_engx_buf.u64);
165215990Sjmallett        cvmx_write_csr(CVMX_DPI_ENGX_BUF(2), dpi_engx_buf.u64);
166215990Sjmallett        cvmx_write_csr(CVMX_DPI_ENGX_BUF(3), dpi_engx_buf.u64);
167215990Sjmallett        cvmx_write_csr(CVMX_DPI_ENGX_BUF(4), dpi_engx_buf.u64);
168215990Sjmallett        dpi_engx_buf.s.blks = 6;
169215990Sjmallett        cvmx_write_csr(CVMX_DPI_ENGX_BUF(5), dpi_engx_buf.u64);
170215990Sjmallett
171215990Sjmallett        dma_control.u64 = cvmx_read_csr(CVMX_DPI_DMA_CONTROL);
172215990Sjmallett        dma_control.s.pkt_hp = 1;
173215990Sjmallett        dma_control.s.pkt_en = 1;
174215990Sjmallett        dma_control.s.dma_enb = 0x1f;
175215990Sjmallett        dma_control.s.dwb_denb = 1;
176215990Sjmallett        dma_control.s.dwb_ichk = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/128;
177215990Sjmallett        dma_control.s.fpa_que = CVMX_FPA_OUTPUT_BUFFER_POOL;
178215990Sjmallett        dma_control.s.o_mode = 1;
179215990Sjmallett        cvmx_write_csr(CVMX_DPI_DMA_CONTROL, dma_control.u64);
180215990Sjmallett        dpi_ctl.u64 = cvmx_read_csr(CVMX_DPI_CTL);
181215990Sjmallett        dpi_ctl.s.en = 1;
182215990Sjmallett        cvmx_write_csr(CVMX_DPI_CTL, dpi_ctl.u64);
183215990Sjmallett    }
184210284Sjmallett    else
185210284Sjmallett    {
186210284Sjmallett        cvmx_npi_dma_control_t dma_control;
187210284Sjmallett        dma_control.u64 = 0;
188210284Sjmallett        //dma_control.s.dwb_denb = 1;
189210284Sjmallett        //dma_control.s.dwb_ichk = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/128;
190210284Sjmallett        dma_control.s.o_add1 = 1;
191210284Sjmallett        dma_control.s.fpa_que = CVMX_FPA_OUTPUT_BUFFER_POOL;
192210284Sjmallett        dma_control.s.hp_enb = 1;
193210284Sjmallett        dma_control.s.lp_enb = 1;
194210284Sjmallett        dma_control.s.csize = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/8;
195210284Sjmallett        cvmx_write_csr(CVMX_NPI_DMA_CONTROL, dma_control.u64);
196210284Sjmallett    }
197210284Sjmallett
198210284Sjmallett    return 0;
199210284Sjmallett}
200210284Sjmallett
201210284Sjmallett
202210284Sjmallett/**
203210284Sjmallett * Shutdown all DMA engines. The engeines must be idle when this
204210284Sjmallett * function is called.
205210284Sjmallett *
206210284Sjmallett * @return Zero on success, negative on failure
207210284Sjmallett */
208210284Sjmallettint cvmx_dma_engine_shutdown(void)
209210284Sjmallett{
210210284Sjmallett    int engine;
211210284Sjmallett
212210284Sjmallett    for (engine=0; engine < cvmx_dma_engine_get_num(); engine++)
213210284Sjmallett    {
214210284Sjmallett        if (cvmx_cmd_queue_length(CVMX_CMD_QUEUE_DMA(engine)))
215210284Sjmallett        {
216210284Sjmallett            cvmx_dprintf("ERROR: cvmx_dma_engine_shutdown: Engine not idle.\n");
217210284Sjmallett            return -1;
218210284Sjmallett        }
219210284Sjmallett    }
220210284Sjmallett
221215990Sjmallett    if (octeon_has_feature(OCTEON_FEATURE_NPEI))
222210284Sjmallett    {
223210284Sjmallett        cvmx_npei_dma_control_t dma_control;
224210284Sjmallett        dma_control.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DMA_CONTROL);
225210284Sjmallett        if (cvmx_dma_engine_get_num() >= 5)
226210284Sjmallett            dma_control.s.dma4_enb = 0;
227210284Sjmallett        dma_control.s.dma3_enb = 0;
228210284Sjmallett        dma_control.s.dma2_enb = 0;
229210284Sjmallett        dma_control.s.dma1_enb = 0;
230210284Sjmallett        dma_control.s.dma0_enb = 0;
231210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_DMA_CONTROL, dma_control.u64);
232210284Sjmallett        /* Make sure the disable completes */
233210284Sjmallett        cvmx_read_csr(CVMX_PEXP_NPEI_DMA_CONTROL);
234210284Sjmallett    }
235215990Sjmallett    else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
236215990Sjmallett    {
237215990Sjmallett        cvmx_dpi_dma_control_t dma_control;
238215990Sjmallett        dma_control.u64 = cvmx_read_csr(CVMX_DPI_DMA_CONTROL);
239215990Sjmallett        dma_control.s.dma_enb = 0;
240215990Sjmallett        cvmx_write_csr(CVMX_DPI_DMA_CONTROL, dma_control.u64);
241215990Sjmallett        /* Make sure the disable completes */
242215990Sjmallett        cvmx_read_csr(CVMX_DPI_DMA_CONTROL);
243215990Sjmallett    }
244210284Sjmallett    else
245210284Sjmallett    {
246210284Sjmallett        cvmx_npi_dma_control_t dma_control;
247210284Sjmallett        dma_control.u64 = cvmx_read_csr(CVMX_NPI_DMA_CONTROL);
248210284Sjmallett        dma_control.s.hp_enb = 0;
249210284Sjmallett        dma_control.s.lp_enb = 0;
250210284Sjmallett        cvmx_write_csr(CVMX_NPI_DMA_CONTROL, dma_control.u64);
251210284Sjmallett        /* Make sure the disable completes */
252210284Sjmallett        cvmx_read_csr(CVMX_NPI_DMA_CONTROL);
253210284Sjmallett    }
254210284Sjmallett
255210284Sjmallett    for (engine=0; engine < cvmx_dma_engine_get_num(); engine++)
256210284Sjmallett    {
257210284Sjmallett        cvmx_cmd_queue_shutdown(CVMX_CMD_QUEUE_DMA(engine));
258215990Sjmallett        if (octeon_has_feature(OCTEON_FEATURE_NPEI))
259210284Sjmallett            cvmx_write_csr(CVMX_PEXP_NPEI_DMAX_IBUFF_SADDR(engine), 0);
260215990Sjmallett        else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
261215990Sjmallett            cvmx_write_csr(CVMX_DPI_DMAX_IBUFF_SADDR(engine), 0);
262210284Sjmallett        else
263210284Sjmallett        {
264210284Sjmallett            if (engine)
265210284Sjmallett                cvmx_write_csr(CVMX_NPI_HIGHP_IBUFF_SADDR, 0);
266210284Sjmallett            else
267210284Sjmallett                cvmx_write_csr(CVMX_NPI_LOWP_IBUFF_SADDR, 0);
268210284Sjmallett        }
269210284Sjmallett    }
270210284Sjmallett
271210284Sjmallett    return 0;
272210284Sjmallett}
273210284Sjmallett
274210284Sjmallett
275210284Sjmallett/**
276210284Sjmallett * Submit a series of DMA comamnd to the DMA engines.
277210284Sjmallett *
278215990Sjmallett * @param engine  Engine to submit to (0 to cvmx_dma_engine_get_num()-1)
279210284Sjmallett * @param header  Command header
280210284Sjmallett * @param num_buffers
281210284Sjmallett *                The number of data pointers
282210284Sjmallett * @param buffers Comamnd data pointers
283210284Sjmallett *
284210284Sjmallett * @return Zero on success, negative on failure
285210284Sjmallett */
286210284Sjmallettint cvmx_dma_engine_submit(int engine, cvmx_dma_engine_header_t header, int num_buffers, cvmx_dma_engine_buffer_t buffers[])
287210284Sjmallett{
288210284Sjmallett    cvmx_cmd_queue_result_t result;
289210284Sjmallett    int cmd_count = 1;
290210284Sjmallett    uint64_t cmds[num_buffers + 1];
291210284Sjmallett
292210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X))
293210284Sjmallett    {
294210284Sjmallett        /* Check for Errata PCIe-604 */
295210284Sjmallett        if ((header.s.nfst > 11) || (header.s.nlst > 11) || (header.s.nfst + header.s.nlst > 15))
296210284Sjmallett        {
297210284Sjmallett            cvmx_dprintf("DMA engine submit too large\n");
298210284Sjmallett            return -1;
299210284Sjmallett        }
300210284Sjmallett    }
301210284Sjmallett
302210284Sjmallett    cmds[0] = header.u64;
303210284Sjmallett    while (num_buffers--)
304210284Sjmallett    {
305210284Sjmallett        cmds[cmd_count++] = buffers->u64;
306210284Sjmallett        buffers++;
307210284Sjmallett    }
308210284Sjmallett
309210284Sjmallett    /* Due to errata PCIE-13315, it is necessary to have the queue lock while we
310210284Sjmallett        ring the doorbell for the DMA engines. This prevents doorbells from
311210284Sjmallett        possibly arriving out of order with respect to the command queue
312210284Sjmallett        entries */
313210284Sjmallett    __cvmx_cmd_queue_lock(CVMX_CMD_QUEUE_DMA(engine), __cvmx_cmd_queue_get_state(CVMX_CMD_QUEUE_DMA(engine)));
314210284Sjmallett    result = cvmx_cmd_queue_write(CVMX_CMD_QUEUE_DMA(engine), 0, cmd_count, cmds);
315210284Sjmallett    /* This SYNCWS is needed since the command queue didn't do locking, which
316210284Sjmallett        normally implies the SYNCWS. This one makes sure the command queue
317210284Sjmallett        updates make it to L2 before we ring the doorbell */
318210284Sjmallett    CVMX_SYNCWS;
319210284Sjmallett    /* A syncw isn't needed here since the command queue did one as part of the queue unlock */
320210284Sjmallett    if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS))
321210284Sjmallett    {
322215990Sjmallett        if (octeon_has_feature(OCTEON_FEATURE_NPEI))
323210284Sjmallett        {
324210284Sjmallett            /* DMA doorbells are 32bit writes in little endian space. This means we need to xor the address with 4 */
325210284Sjmallett            cvmx_write64_uint32(CVMX_PEXP_NPEI_DMAX_DBELL(engine)^4, cmd_count);
326210284Sjmallett        }
327215990Sjmallett        else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
328215990Sjmallett            cvmx_write_csr(CVMX_DPI_DMAX_DBELL(engine), cmd_count);
329210284Sjmallett        else
330210284Sjmallett        {
331210284Sjmallett            if (engine)
332210284Sjmallett                cvmx_write_csr(CVMX_NPI_HIGHP_DBELL, cmd_count);
333210284Sjmallett            else
334210284Sjmallett                cvmx_write_csr(CVMX_NPI_LOWP_DBELL, cmd_count);
335210284Sjmallett        }
336210284Sjmallett    }
337210284Sjmallett    /* Here is the unlock for the above errata workaround */
338210284Sjmallett    __cvmx_cmd_queue_unlock(__cvmx_cmd_queue_get_state(CVMX_CMD_QUEUE_DMA(engine)));
339210284Sjmallett    return result;
340210284Sjmallett}
341210284Sjmallett
342210284Sjmallett
343210284Sjmallett/**
344210284Sjmallett * @INTERNAL
345210284Sjmallett * Function used by cvmx_dma_engine_transfer() to build the
346210284Sjmallett * internal address list.
347210284Sjmallett *
348210284Sjmallett * @param buffers Location to store the list
349210284Sjmallett * @param address Address to build list for
350210284Sjmallett * @param size    Length of the memory pointed to by address
351210284Sjmallett *
352210284Sjmallett * @return Number of internal pointer chunks created
353210284Sjmallett */
354210284Sjmallettstatic inline int __cvmx_dma_engine_build_internal_pointers(cvmx_dma_engine_buffer_t *buffers, uint64_t address, int size)
355210284Sjmallett{
356210284Sjmallett    int segments = 0;
357210284Sjmallett    while (size)
358210284Sjmallett    {
359210284Sjmallett        /* Each internal chunk can contain a maximum of 8191 bytes */
360210284Sjmallett        int chunk = size;
361210284Sjmallett        if (chunk > 8191)
362210284Sjmallett            chunk = 8191;
363210284Sjmallett        buffers[segments].u64 = 0;
364210284Sjmallett        buffers[segments].internal.size = chunk;
365210284Sjmallett        buffers[segments].internal.addr = address;
366210284Sjmallett        address += chunk;
367210284Sjmallett        size -= chunk;
368210284Sjmallett        segments++;
369210284Sjmallett    }
370210284Sjmallett    return segments;
371210284Sjmallett}
372210284Sjmallett
373210284Sjmallett
374210284Sjmallett/**
375210284Sjmallett * @INTERNAL
376210284Sjmallett * Function used by cvmx_dma_engine_transfer() to build the PCI / PCIe address
377210284Sjmallett * list.
378210284Sjmallett * @param buffers Location to store the list
379210284Sjmallett * @param address Address to build list for
380210284Sjmallett * @param size    Length of the memory pointed to by address
381210284Sjmallett *
382210284Sjmallett * @return Number of PCI / PCIe address chunks created. The number of words used
383210284Sjmallett *         will be segments + (segments-1)/4 + 1.
384210284Sjmallett */
385210284Sjmallettstatic inline int __cvmx_dma_engine_build_external_pointers(cvmx_dma_engine_buffer_t *buffers, uint64_t address, int size)
386210284Sjmallett{
387210284Sjmallett    const int MAX_SIZE = 65535;
388210284Sjmallett    int segments = 0;
389210284Sjmallett    while (size)
390210284Sjmallett    {
391210284Sjmallett        /* Each block of 4 PCI / PCIe pointers uses one dword for lengths followed by
392210284Sjmallett            up to 4 addresses. This then repeats if more data is needed */
393210284Sjmallett        buffers[0].u64 = 0;
394210284Sjmallett        if (size <= MAX_SIZE)
395210284Sjmallett        {
396210284Sjmallett            /* Only one more segment needed */
397210284Sjmallett            buffers[0].pcie_length.len0 = size;
398210284Sjmallett            buffers[1].u64 = address;
399210284Sjmallett            segments++;
400210284Sjmallett            break;
401210284Sjmallett        }
402210284Sjmallett        else if (size <= MAX_SIZE * 2)
403210284Sjmallett        {
404210284Sjmallett            /* Two more segments needed */
405210284Sjmallett            buffers[0].pcie_length.len0 = MAX_SIZE;
406210284Sjmallett            buffers[0].pcie_length.len1 = size - MAX_SIZE;
407210284Sjmallett            buffers[1].u64 = address;
408210284Sjmallett            address += MAX_SIZE;
409210284Sjmallett            buffers[2].u64 = address;
410210284Sjmallett            segments+=2;
411210284Sjmallett            break;
412210284Sjmallett        }
413210284Sjmallett        else if (size <= MAX_SIZE * 3)
414210284Sjmallett        {
415210284Sjmallett            /* Three more segments needed */
416210284Sjmallett            buffers[0].pcie_length.len0 = MAX_SIZE;
417210284Sjmallett            buffers[0].pcie_length.len1 = MAX_SIZE;
418210284Sjmallett            buffers[0].pcie_length.len2 = size - MAX_SIZE * 2;
419210284Sjmallett            buffers[1].u64 = address;
420210284Sjmallett            address += MAX_SIZE;
421210284Sjmallett            buffers[2].u64 = address;
422210284Sjmallett            address += MAX_SIZE;
423210284Sjmallett            buffers[3].u64 = address;
424210284Sjmallett            segments+=3;
425210284Sjmallett            break;
426210284Sjmallett        }
427210284Sjmallett        else if (size <= MAX_SIZE * 4)
428210284Sjmallett        {
429210284Sjmallett            /* Four more segments needed */
430210284Sjmallett            buffers[0].pcie_length.len0 = MAX_SIZE;
431210284Sjmallett            buffers[0].pcie_length.len1 = MAX_SIZE;
432210284Sjmallett            buffers[0].pcie_length.len2 = MAX_SIZE;
433210284Sjmallett            buffers[0].pcie_length.len3 = size - MAX_SIZE * 3;
434210284Sjmallett            buffers[1].u64 = address;
435210284Sjmallett            address += MAX_SIZE;
436210284Sjmallett            buffers[2].u64 = address;
437210284Sjmallett            address += MAX_SIZE;
438210284Sjmallett            buffers[3].u64 = address;
439210284Sjmallett            address += MAX_SIZE;
440210284Sjmallett            buffers[4].u64 = address;
441210284Sjmallett            segments+=4;
442210284Sjmallett            break;
443210284Sjmallett        }
444210284Sjmallett        else
445210284Sjmallett        {
446210284Sjmallett            /* Five or more segments are needed */
447210284Sjmallett            buffers[0].pcie_length.len0 = MAX_SIZE;
448210284Sjmallett            buffers[0].pcie_length.len1 = MAX_SIZE;
449210284Sjmallett            buffers[0].pcie_length.len2 = MAX_SIZE;
450210284Sjmallett            buffers[0].pcie_length.len3 = MAX_SIZE;
451210284Sjmallett            buffers[1].u64 = address;
452210284Sjmallett            address += MAX_SIZE;
453210284Sjmallett            buffers[2].u64 = address;
454210284Sjmallett            address += MAX_SIZE;
455210284Sjmallett            buffers[3].u64 = address;
456210284Sjmallett            address += MAX_SIZE;
457210284Sjmallett            buffers[4].u64 = address;
458210284Sjmallett            address += MAX_SIZE;
459210284Sjmallett            size -= MAX_SIZE*4;
460210284Sjmallett            buffers += 5;
461210284Sjmallett            segments+=4;
462210284Sjmallett        }
463210284Sjmallett    }
464210284Sjmallett    return segments;
465210284Sjmallett}
466210284Sjmallett
467210284Sjmallett
468210284Sjmallett/**
469210284Sjmallett * Build the first and last pointers based on a DMA engine header
470210284Sjmallett * and submit them to the engine. The purpose of this function is
471210284Sjmallett * to simplify the building of DMA engine commands by automatically
472210284Sjmallett * converting a simple address and size into the apropriate internal
473210284Sjmallett * or PCI / PCIe address list. This function does not support gather lists,
474210284Sjmallett * so you will need to build your own lists in that case.
475210284Sjmallett *
476215990Sjmallett * @param engine Engine to submit to (0 to cvmx_dma_engine_get_num()-1)
477210284Sjmallett * @param header DMA Command header. Note that the nfst and nlst fields do not
478210284Sjmallett *               need to be filled in. All other fields must be set properly.
479210284Sjmallett * @param first_address
480210284Sjmallett *               Address to use for the first pointers. In the case of INTERNAL,
481210284Sjmallett *               INBOUND, and OUTBOUND this is an Octeon memory address. In the
482210284Sjmallett *               case of EXTERNAL, this is the source PCI / PCIe address.
483210284Sjmallett * @param last_address
484210284Sjmallett *               Address to use for the last pointers. In the case of EXTERNAL,
485210284Sjmallett *               INBOUND, and OUTBOUND this is a PCI / PCIe address. In the
486210284Sjmallett *               case of INTERNAL, this is the Octeon memory destination address.
487210284Sjmallett * @param size   Size of the transfer to perform.
488210284Sjmallett *
489210284Sjmallett * @return Zero on success, negative on failure
490210284Sjmallett */
491210284Sjmallettint cvmx_dma_engine_transfer(int engine, cvmx_dma_engine_header_t header,
492210284Sjmallett                             uint64_t first_address, uint64_t last_address,
493210284Sjmallett                             int size)
494210284Sjmallett{
495210284Sjmallett    cvmx_dma_engine_buffer_t buffers[32];
496210284Sjmallett    int words = 0;
497210284Sjmallett
498210284Sjmallett    switch (header.s.type)
499210284Sjmallett    {
500210284Sjmallett        case CVMX_DMA_ENGINE_TRANSFER_INTERNAL:
501210284Sjmallett            header.s.nfst = __cvmx_dma_engine_build_internal_pointers(buffers, first_address, size);
502210284Sjmallett            words += header.s.nfst;
503210284Sjmallett            header.s.nlst = __cvmx_dma_engine_build_internal_pointers(buffers + words, last_address, size);
504210284Sjmallett            words += header.s.nlst;
505210284Sjmallett            break;
506210284Sjmallett        case CVMX_DMA_ENGINE_TRANSFER_INBOUND:
507210284Sjmallett        case CVMX_DMA_ENGINE_TRANSFER_OUTBOUND:
508210284Sjmallett            header.s.nfst = __cvmx_dma_engine_build_internal_pointers(buffers, first_address, size);
509210284Sjmallett            words += header.s.nfst;
510210284Sjmallett            header.s.nlst = __cvmx_dma_engine_build_external_pointers(buffers + words, last_address, size);
511210284Sjmallett            words +=  header.s.nlst + ((header.s.nlst-1) >> 2) + 1;
512210284Sjmallett            break;
513210284Sjmallett        case CVMX_DMA_ENGINE_TRANSFER_EXTERNAL:
514210284Sjmallett            header.s.nfst = __cvmx_dma_engine_build_external_pointers(buffers, first_address, size);
515210284Sjmallett            words +=  header.s.nfst + ((header.s.nfst-1) >> 2) + 1;
516210284Sjmallett            header.s.nlst = __cvmx_dma_engine_build_external_pointers(buffers + words, last_address, size);
517210284Sjmallett            words +=  header.s.nlst + ((header.s.nlst-1) >> 2) + 1;
518210284Sjmallett            break;
519210284Sjmallett    }
520210284Sjmallett    return cvmx_dma_engine_submit(engine, header, words, buffers);
521210284Sjmallett}
522210284Sjmallett
523210284Sjmallett#endif
524