cvmx-dma-engine.c revision 232812
1251881Speter/***********************license start***************
2251881Speter * Copyright (c) 2003-2010  Cavium Inc. (support@cavium.com). All rights
3251881Speter * reserved.
4251881Speter *
5251881Speter *
6251881Speter * Redistribution and use in source and binary forms, with or without
7251881Speter * modification, are permitted provided that the following conditions are
8251881Speter * met:
9251881Speter *
10251881Speter *   * Redistributions of source code must retain the above copyright
11251881Speter *     notice, this list of conditions and the following disclaimer.
12251881Speter *
13251881Speter *   * Redistributions in binary form must reproduce the above
14251881Speter *     copyright notice, this list of conditions and the following
15251881Speter *     disclaimer in the documentation and/or other materials provided
16251881Speter *     with the distribution.
17251881Speter
18251881Speter *   * Neither the name of Cavium Inc. nor the names of
19251881Speter *     its contributors may be used to endorse or promote products
20251881Speter *     derived from this software without specific prior written
21251881Speter *     permission.
22251881Speter
23251881Speter * This Software, including technical data, may be subject to U.S. export  control
24299742Sdim * laws, including the U.S. Export Administration Act and its  associated
25251881Speter * regulations, and may be subject to export or import  regulations in other
26251881Speter * countries.
27251881Speter
28251881Speter * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29251881Speter * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30251881Speter * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31251881Speter * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32251881Speter * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33251881Speter * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34299742Sdim * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35251881Speter * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36299742Sdim * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37299742Sdim * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38251881Speter ***********************license end**************************************/
39251881Speter
40251881Speter
41251881Speter
42251881Speter
43251881Speter
44251881Speter
45299742Sdim
46299742Sdim/**
47299742Sdim * @file
48299742Sdim *
49299742Sdim * Interface to the PCI / PCIe DMA engines. These are only avialable
50299742Sdim * on chips with PCI / PCIe.
51299742Sdim *
52299742Sdim * <hr>$Revision: 70030 $<hr>
53299742Sdim */
54299742Sdim#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
55299742Sdim#include <linux/module.h>
56299742Sdim#include <asm/octeon/cvmx.h>
57299742Sdim#include <asm/octeon/octeon-model.h>
58299742Sdim#include <asm/octeon/cvmx-config.h>
59299742Sdim#include <asm/octeon/cvmx-cmd-queue.h>
60299742Sdim#include <asm/octeon/cvmx-dma-engine.h>
61299742Sdim#include <asm/octeon/octeon-feature.h>
62299742Sdim#include <asm/octeon/cvmx-npi-defs.h>
63299742Sdim#include <asm/octeon/cvmx-npei-defs.h>
64299742Sdim#include <asm/octeon/cvmx-dpi-defs.h>
65299742Sdim#include <asm/octeon/cvmx-pexp-defs.h>
66299742Sdim#include <asm/octeon/cvmx-helper-cfg.h>
67299742Sdim#else
68299742Sdim#include "executive-config.h"
69299742Sdim#include "cvmx-config.h"
70299742Sdim#include "cvmx.h"
71299742Sdim#include "cvmx-cmd-queue.h"
72299742Sdim#include "cvmx-dma-engine.h"
73299742Sdim#include "cvmx-helper-cfg.h"
74299742Sdim#endif
75299742Sdim
76299742Sdim#ifdef CVMX_ENABLE_PKO_FUNCTIONS
77299742Sdim
78299742Sdim/**
79299742Sdim * Return the number of DMA engimes supported by this chip
80299742Sdim *
81299742Sdim * @return Number of DMA engines
82299742Sdim */
83299742Sdimint cvmx_dma_engine_get_num(void)
84299742Sdim{
85299742Sdim    if (octeon_has_feature(OCTEON_FEATURE_NPEI))
86299742Sdim    {
87299742Sdim        if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
88299742Sdim            return 4;
89299742Sdim        else
90299742Sdim            return 5;
91251881Speter    }
92299742Sdim    else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
93299742Sdim        return 8;
94251881Speter    else
95299742Sdim        return 2;
96299742Sdim}
97251881Speter
98299742Sdim/**
99299742Sdim * Initialize the DMA engines for use
100299742Sdim *
101299742Sdim * @return Zero on success, negative on failure
102299742Sdim */
103299742Sdimint cvmx_dma_engine_initialize(void)
104299742Sdim{
105299742Sdim    int engine;
106299742Sdim
107299742Sdim    for (engine=0; engine < cvmx_dma_engine_get_num(); engine++)
108299742Sdim    {
109299742Sdim        cvmx_cmd_queue_result_t result;
110299742Sdim        result = cvmx_cmd_queue_initialize(CVMX_CMD_QUEUE_DMA(engine),
111299742Sdim                                           0, CVMX_FPA_OUTPUT_BUFFER_POOL,
112299742Sdim                                           CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE);
113299742Sdim        if (result != CVMX_CMD_QUEUE_SUCCESS)
114299742Sdim            return -1;
115299742Sdim        if (octeon_has_feature(OCTEON_FEATURE_NPEI))
116299742Sdim        {
117299742Sdim            cvmx_npei_dmax_ibuff_saddr_t dmax_ibuff_saddr;
118299742Sdim            dmax_ibuff_saddr.u64 = 0;
119299742Sdim            dmax_ibuff_saddr.s.saddr = cvmx_ptr_to_phys(cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_DMA(engine))) >> 7;
120299742Sdim            cvmx_write_csr(CVMX_PEXP_NPEI_DMAX_IBUFF_SADDR(engine), dmax_ibuff_saddr.u64);
121299742Sdim        }
122299742Sdim        else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
123299742Sdim        {
124299742Sdim            cvmx_dpi_dmax_ibuff_saddr_t dpi_dmax_ibuff_saddr;
125299742Sdim            dpi_dmax_ibuff_saddr.u64 = 0;
126299742Sdim            dpi_dmax_ibuff_saddr.s.csize = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/8;
127299742Sdim            dpi_dmax_ibuff_saddr.s.saddr = cvmx_ptr_to_phys(cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_DMA(engine))) >> 7;
128299742Sdim            cvmx_write_csr(CVMX_DPI_DMAX_IBUFF_SADDR(engine), dpi_dmax_ibuff_saddr.u64);
129299742Sdim        }
130299742Sdim        else
131299742Sdim        {
132299742Sdim            uint64_t address = cvmx_ptr_to_phys(cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_DMA(engine)));
133299742Sdim            if (engine)
134299742Sdim                cvmx_write_csr(CVMX_NPI_HIGHP_IBUFF_SADDR, address);
135299742Sdim            else
136299742Sdim                cvmx_write_csr(CVMX_NPI_LOWP_IBUFF_SADDR, address);
137299742Sdim        }
138299742Sdim    }
139299742Sdim
140299742Sdim    if (octeon_has_feature(OCTEON_FEATURE_NPEI))
141299742Sdim    {
142299742Sdim        cvmx_npei_dma_control_t dma_control;
143299742Sdim        dma_control.u64 = 0;
144299742Sdim        if (cvmx_dma_engine_get_num() >= 5)
145299742Sdim            dma_control.s.dma4_enb = 1;
146299742Sdim        dma_control.s.dma3_enb = 1;
147299742Sdim        dma_control.s.dma2_enb = 1;
148299742Sdim        dma_control.s.dma1_enb = 1;
149299742Sdim        dma_control.s.dma0_enb = 1;
150251881Speter        dma_control.s.o_mode = 1; /* Pull NS and RO from this register, not the pointers */
151251881Speter        //dma_control.s.dwb_denb = 1;
152251881Speter        //dma_control.s.dwb_ichk = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/128;
153251881Speter        dma_control.s.fpa_que = CVMX_FPA_OUTPUT_BUFFER_POOL;
154251881Speter        dma_control.s.csize = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/8;
155299742Sdim        cvmx_write_csr(CVMX_PEXP_NPEI_DMA_CONTROL, dma_control.u64);
156251881Speter        /* As a workaround for errata PCIE-811 we only allow a single
157251881Speter            outstanding DMA read over PCIe at a time. This limits performance,
158251881Speter            but works in all cases. If you need higher performance, remove
159251881Speter            this code and implement the more complicated workaround documented
160251881Speter            in the errata. This only affects CN56XX pass 2.0 chips */
161251881Speter        if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_0))
162251881Speter        {
163251881Speter            cvmx_npei_dma_pcie_req_num_t pcie_req_num;
164251881Speter            pcie_req_num.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DMA_PCIE_REQ_NUM);
165251881Speter            pcie_req_num.s.dma_cnt = 1;
166251881Speter            cvmx_write_csr(CVMX_PEXP_NPEI_DMA_PCIE_REQ_NUM, pcie_req_num.u64);
167251881Speter        }
168251881Speter    }
169251881Speter    else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
170251881Speter    {
171251881Speter        cvmx_dpi_engx_buf_t dpi_engx_buf;
172251881Speter        cvmx_dpi_dma_engx_en_t dpi_dma_engx_en;
173251881Speter        cvmx_dpi_dma_control_t dma_control;
174251881Speter        cvmx_dpi_ctl_t dpi_ctl;
175251881Speter
176299742Sdim        /* Give engine 0-4 1KB, and 5 3KB. This gives the packet engines better
177299742Sdim            performance. Total must not exceed 8KB */
178299742Sdim        dpi_engx_buf.u64 = 0;
179251881Speter        dpi_engx_buf.s.blks = 2;
180251881Speter        cvmx_write_csr(CVMX_DPI_ENGX_BUF(0), dpi_engx_buf.u64);
181251881Speter        cvmx_write_csr(CVMX_DPI_ENGX_BUF(1), dpi_engx_buf.u64);
182251881Speter        cvmx_write_csr(CVMX_DPI_ENGX_BUF(2), dpi_engx_buf.u64);
183251881Speter        cvmx_write_csr(CVMX_DPI_ENGX_BUF(3), dpi_engx_buf.u64);
184299742Sdim        cvmx_write_csr(CVMX_DPI_ENGX_BUF(4), dpi_engx_buf.u64);
185251881Speter        dpi_engx_buf.s.blks = 6;
186251881Speter        cvmx_write_csr(CVMX_DPI_ENGX_BUF(5), dpi_engx_buf.u64);
187251881Speter
188251881Speter        dma_control.u64 = cvmx_read_csr(CVMX_DPI_DMA_CONTROL);
189251881Speter        dma_control.s.pkt_hp = 1;
190251881Speter        dma_control.s.pkt_en = 1;
191251881Speter        dma_control.s.dma_enb = 0x1f;
192251881Speter        dma_control.s.dwb_denb = cvmx_helper_cfg_opt_get(CVMX_HELPER_CFG_OPT_USE_DWB);
193251881Speter        dma_control.s.dwb_ichk = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/128;
194251881Speter        dma_control.s.fpa_que = CVMX_FPA_OUTPUT_BUFFER_POOL;
195251881Speter        dma_control.s.o_mode = 1;
196251881Speter        cvmx_write_csr(CVMX_DPI_DMA_CONTROL, dma_control.u64);
197251881Speter        /* When dma_control[pkt_en] = 1, engine 5 is used for packets and is not
198251881Speter           available for DMA. */
199251881Speter        dpi_dma_engx_en.u64 = cvmx_read_csr(CVMX_DPI_DMA_ENGX_EN(5));
200251881Speter        dpi_dma_engx_en.s.qen = 0;
201251881Speter        cvmx_write_csr(CVMX_DPI_DMA_ENGX_EN(5), dpi_dma_engx_en.u64);
202299742Sdim        dpi_ctl.u64 = cvmx_read_csr(CVMX_DPI_CTL);
203299742Sdim        dpi_ctl.s.en = 1;
204299742Sdim        cvmx_write_csr(CVMX_DPI_CTL, dpi_ctl.u64);
205251881Speter    }
206299742Sdim    else
207251881Speter    {
208251881Speter        cvmx_npi_dma_control_t dma_control;
209251881Speter        dma_control.u64 = 0;
210251881Speter        //dma_control.s.dwb_denb = 1;
211251881Speter        //dma_control.s.dwb_ichk = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/128;
212251881Speter        dma_control.s.o_add1 = 1;
213251881Speter        dma_control.s.fpa_que = CVMX_FPA_OUTPUT_BUFFER_POOL;
214251881Speter        dma_control.s.hp_enb = 1;
215251881Speter        dma_control.s.lp_enb = 1;
216251881Speter        dma_control.s.csize = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE/8;
217251881Speter        cvmx_write_csr(CVMX_NPI_DMA_CONTROL, dma_control.u64);
218251881Speter    }
219251881Speter
220299742Sdim    return 0;
221251881Speter}
222251881Speter#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
223251881SpeterEXPORT_SYMBOL(cvmx_dma_engine_initialize);
224251881Speter#endif
225251881Speter
226251881Speter/**
227299742Sdim * Shutdown all DMA engines. The engines must be idle when this
228251881Speter * function is called.
229251881Speter *
230299742Sdim * @return Zero on success, negative on failure
231299742Sdim */
232299742Sdimint cvmx_dma_engine_shutdown(void)
233299742Sdim{
234299742Sdim    int engine;
235299742Sdim
236299742Sdim    for (engine=0; engine < cvmx_dma_engine_get_num(); engine++)
237299742Sdim    {
238299742Sdim        if (cvmx_cmd_queue_length(CVMX_CMD_QUEUE_DMA(engine)))
239299742Sdim        {
240299742Sdim            cvmx_dprintf("ERROR: cvmx_dma_engine_shutdown: Engine not idle.\n");
241299742Sdim            return -1;
242299742Sdim        }
243299742Sdim    }
244251881Speter
245251881Speter    if (octeon_has_feature(OCTEON_FEATURE_NPEI))
246251881Speter    {
247251881Speter        cvmx_npei_dma_control_t dma_control;
248251881Speter        dma_control.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DMA_CONTROL);
249251881Speter        if (cvmx_dma_engine_get_num() >= 5)
250251881Speter            dma_control.s.dma4_enb = 0;
251251881Speter        dma_control.s.dma3_enb = 0;
252251881Speter        dma_control.s.dma2_enb = 0;
253251881Speter        dma_control.s.dma1_enb = 0;
254251881Speter        dma_control.s.dma0_enb = 0;
255251881Speter        cvmx_write_csr(CVMX_PEXP_NPEI_DMA_CONTROL, dma_control.u64);
256251881Speter        /* Make sure the disable completes */
257251881Speter        cvmx_read_csr(CVMX_PEXP_NPEI_DMA_CONTROL);
258251881Speter    }
259251881Speter    else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
260251881Speter    {
261251881Speter        cvmx_dpi_dma_control_t dma_control;
262251881Speter        dma_control.u64 = cvmx_read_csr(CVMX_DPI_DMA_CONTROL);
263251881Speter        dma_control.s.dma_enb = 0;
264251881Speter        cvmx_write_csr(CVMX_DPI_DMA_CONTROL, dma_control.u64);
265251881Speter        /* Make sure the disable completes */
266251881Speter        cvmx_read_csr(CVMX_DPI_DMA_CONTROL);
267299742Sdim    }
268299742Sdim    else
269299742Sdim    {
270299742Sdim        cvmx_npi_dma_control_t dma_control;
271299742Sdim        dma_control.u64 = cvmx_read_csr(CVMX_NPI_DMA_CONTROL);
272299742Sdim        dma_control.s.hp_enb = 0;
273251881Speter        dma_control.s.lp_enb = 0;
274251881Speter        cvmx_write_csr(CVMX_NPI_DMA_CONTROL, dma_control.u64);
275251881Speter        /* Make sure the disable completes */
276251881Speter        cvmx_read_csr(CVMX_NPI_DMA_CONTROL);
277251881Speter    }
278251881Speter
279251881Speter    for (engine=0; engine < cvmx_dma_engine_get_num(); engine++)
280251881Speter    {
281251881Speter        cvmx_cmd_queue_shutdown(CVMX_CMD_QUEUE_DMA(engine));
282251881Speter        if (octeon_has_feature(OCTEON_FEATURE_NPEI))
283251881Speter            cvmx_write_csr(CVMX_PEXP_NPEI_DMAX_IBUFF_SADDR(engine), 0);
284251881Speter        else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
285251881Speter            cvmx_write_csr(CVMX_DPI_DMAX_IBUFF_SADDR(engine), 0);
286251881Speter        else
287299742Sdim        {
288299742Sdim            if (engine)
289299742Sdim                cvmx_write_csr(CVMX_NPI_HIGHP_IBUFF_SADDR, 0);
290299742Sdim            else
291299742Sdim                cvmx_write_csr(CVMX_NPI_LOWP_IBUFF_SADDR, 0);
292299742Sdim        }
293251881Speter    }
294251881Speter
295251881Speter    return 0;
296251881Speter}
297251881Speter#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
298251881SpeterEXPORT_SYMBOL(cvmx_dma_engine_shutdown);
299251881Speter#endif
300251881Speter
301251881Speter/**
302251881Speter * Submit a series of DMA command to the DMA engines.
303251881Speter *
304251881Speter * @param engine  Engine to submit to (0 to cvmx_dma_engine_get_num()-1)
305251881Speter * @param header  Command header
306251881Speter * @param num_buffers
307251881Speter *                The number of data pointers
308251881Speter * @param buffers Command data pointers
309251881Speter *
310299742Sdim * @return Zero on success, negative on failure
311299742Sdim */
312299742Sdimint cvmx_dma_engine_submit(int engine, cvmx_dma_engine_header_t header, int num_buffers, cvmx_dma_engine_buffer_t buffers[])
313299742Sdim{
314299742Sdim    cvmx_cmd_queue_result_t result;
315299742Sdim    int cmd_count = 1;
316251881Speter    uint64_t cmds[num_buffers + 1];
317251881Speter
318251881Speter    if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X))
319251881Speter    {
320251881Speter        /* Check for Errata PCIe-604 */
321251881Speter        if ((header.s.nfst > 11) || (header.s.nlst > 11) || (header.s.nfst + header.s.nlst > 15))
322251881Speter        {
323251881Speter            cvmx_dprintf("DMA engine submit too large\n");
324251881Speter            return -1;
325251881Speter        }
326251881Speter    }
327251881Speter
328299742Sdim    cmds[0] = header.u64;
329299742Sdim    while (num_buffers--)
330251881Speter    {
331299742Sdim        cmds[cmd_count++] = buffers->u64;
332251881Speter        buffers++;
333299742Sdim    }
334251881Speter
335251881Speter    /* Due to errata PCIE-13315, it is necessary to have the queue lock while we
336251881Speter        ring the doorbell for the DMA engines. This prevents doorbells from
337251881Speter        possibly arriving out of order with respect to the command queue
338251881Speter        entries */
339251881Speter    __cvmx_cmd_queue_lock(CVMX_CMD_QUEUE_DMA(engine), __cvmx_cmd_queue_get_state(CVMX_CMD_QUEUE_DMA(engine)));
340251881Speter    result = cvmx_cmd_queue_write(CVMX_CMD_QUEUE_DMA(engine), 0, cmd_count, cmds);
341251881Speter    /* This SYNCWS is needed since the command queue didn't do locking, which
342251881Speter        normally implies the SYNCWS. This one makes sure the command queue
343299742Sdim        updates make it to L2 before we ring the doorbell */
344251881Speter    CVMX_SYNCWS;
345251881Speter    /* A syncw isn't needed here since the command queue did one as part of the queue unlock */
346299742Sdim    if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS))
347299742Sdim    {
348251881Speter        if (octeon_has_feature(OCTEON_FEATURE_NPEI))
349299742Sdim        {
350299742Sdim            /* DMA doorbells are 32bit writes in little endian space. This means we need to xor the address with 4 */
351299742Sdim            cvmx_write64_uint32(CVMX_PEXP_NPEI_DMAX_DBELL(engine)^4, cmd_count);
352299742Sdim        }
353299742Sdim        else if (octeon_has_feature(OCTEON_FEATURE_PCIE))
354251881Speter            cvmx_write_csr(CVMX_DPI_DMAX_DBELL(engine), cmd_count);
355299742Sdim        else
356299742Sdim        {
357299742Sdim            if (engine)
358299742Sdim                cvmx_write_csr(CVMX_NPI_HIGHP_DBELL, cmd_count);
359299742Sdim            else
360299742Sdim                cvmx_write_csr(CVMX_NPI_LOWP_DBELL, cmd_count);
361299742Sdim        }
362299742Sdim    }
363299742Sdim    /* Here is the unlock for the above errata workaround */
364299742Sdim    __cvmx_cmd_queue_unlock(__cvmx_cmd_queue_get_state(CVMX_CMD_QUEUE_DMA(engine)));
365299742Sdim    return result;
366251881Speter}
367251881Speter
368251881Speter
369251881Speter/**
370251881Speter * @INTERNAL
371251881Speter * Function used by cvmx_dma_engine_transfer() to build the
372251881Speter * internal address list.
373251881Speter *
374251881Speter * @param buffers Location to store the list
375299742Sdim * @param address Address to build list for
376251881Speter * @param size    Length of the memory pointed to by address
377251881Speter *
378251881Speter * @return Number of internal pointer chunks created
379251881Speter */
380251881Speterstatic inline int __cvmx_dma_engine_build_internal_pointers(cvmx_dma_engine_buffer_t *buffers, uint64_t address, int size)
381251881Speter{
382251881Speter    int segments = 0;
383251881Speter    while (size)
384251881Speter    {
385251881Speter        /* Each internal chunk can contain a maximum of 8191 bytes */
386251881Speter        int chunk = size;
387251881Speter        if (chunk > 8191)
388251881Speter            chunk = 8191;
389251881Speter        buffers[segments].u64 = 0;
390251881Speter        buffers[segments].internal.size = chunk;
391251881Speter        buffers[segments].internal.addr = address;
392251881Speter        address += chunk;
393251881Speter        size -= chunk;
394251881Speter        segments++;
395251881Speter    }
396251881Speter    return segments;
397251881Speter}
398251881Speter
399251881Speter
400251881Speter/**
401251881Speter * @INTERNAL
402251881Speter * Function used by cvmx_dma_engine_transfer() to build the PCI / PCIe address
403251881Speter * list.
404251881Speter * @param buffers Location to store the list
405251881Speter * @param address Address to build list for
406251881Speter * @param size    Length of the memory pointed to by address
407251881Speter *
408251881Speter * @return Number of PCI / PCIe address chunks created. The number of words used
409251881Speter *         will be segments + (segments-1)/4 + 1.
410251881Speter */
411251881Speterstatic inline int __cvmx_dma_engine_build_external_pointers(cvmx_dma_engine_buffer_t *buffers, uint64_t address, int size)
412251881Speter{
413251881Speter    const int MAX_SIZE = 65535;
414251881Speter    int segments = 0;
415251881Speter    while (size)
416251881Speter    {
417251881Speter        /* Each block of 4 PCI / PCIe pointers uses one dword for lengths followed by
418251881Speter            up to 4 addresses. This then repeats if more data is needed */
419251881Speter        buffers[0].u64 = 0;
420251881Speter        if (size <= MAX_SIZE)
421251881Speter        {
422251881Speter            /* Only one more segment needed */
423251881Speter            buffers[0].pcie_length.len0 = size;
424251881Speter            buffers[1].u64 = address;
425251881Speter            segments++;
426251881Speter            break;
427251881Speter        }
428251881Speter        else if (size <= MAX_SIZE * 2)
429251881Speter        {
430251881Speter            /* Two more segments needed */
431251881Speter            buffers[0].pcie_length.len0 = MAX_SIZE;
432251881Speter            buffers[0].pcie_length.len1 = size - MAX_SIZE;
433251881Speter            buffers[1].u64 = address;
434251881Speter            address += MAX_SIZE;
435251881Speter            buffers[2].u64 = address;
436251881Speter            segments+=2;
437251881Speter            break;
438251881Speter        }
439251881Speter        else if (size <= MAX_SIZE * 3)
440251881Speter        {
441251881Speter            /* Three more segments needed */
442251881Speter            buffers[0].pcie_length.len0 = MAX_SIZE;
443251881Speter            buffers[0].pcie_length.len1 = MAX_SIZE;
444299742Sdim            buffers[0].pcie_length.len2 = size - MAX_SIZE * 2;
445299742Sdim            buffers[1].u64 = address;
446299742Sdim            address += MAX_SIZE;
447299742Sdim            buffers[2].u64 = address;
448251881Speter            address += MAX_SIZE;
449251881Speter            buffers[3].u64 = address;
450251881Speter            segments+=3;
451251881Speter            break;
452251881Speter        }
453251881Speter        else if (size <= MAX_SIZE * 4)
454251881Speter        {
455251881Speter            /* Four more segments needed */
456251881Speter            buffers[0].pcie_length.len0 = MAX_SIZE;
457251881Speter            buffers[0].pcie_length.len1 = MAX_SIZE;
458251881Speter            buffers[0].pcie_length.len2 = MAX_SIZE;
459251881Speter            buffers[0].pcie_length.len3 = size - MAX_SIZE * 3;
460251881Speter            buffers[1].u64 = address;
461251881Speter            address += MAX_SIZE;
462251881Speter            buffers[2].u64 = address;
463251881Speter            address += MAX_SIZE;
464251881Speter            buffers[3].u64 = address;
465251881Speter            address += MAX_SIZE;
466251881Speter            buffers[4].u64 = address;
467251881Speter            segments+=4;
468251881Speter            break;
469251881Speter        }
470251881Speter        else
471251881Speter        {
472251881Speter            /* Five or more segments are needed */
473251881Speter            buffers[0].pcie_length.len0 = MAX_SIZE;
474251881Speter            buffers[0].pcie_length.len1 = MAX_SIZE;
475251881Speter            buffers[0].pcie_length.len2 = MAX_SIZE;
476251881Speter            buffers[0].pcie_length.len3 = MAX_SIZE;
477251881Speter            buffers[1].u64 = address;
478299742Sdim            address += MAX_SIZE;
479299742Sdim            buffers[2].u64 = address;
480299742Sdim            address += MAX_SIZE;
481299742Sdim            buffers[3].u64 = address;
482299742Sdim            address += MAX_SIZE;
483299742Sdim            buffers[4].u64 = address;
484299742Sdim            address += MAX_SIZE;
485299742Sdim            size -= MAX_SIZE*4;
486299742Sdim            buffers += 5;
487299742Sdim            segments+=4;
488251881Speter        }
489251881Speter    }
490251881Speter    return segments;
491251881Speter}
492251881Speter
493251881Speter
494251881Speter/**
495251881Speter * Build the first and last pointers based on a DMA engine header
496251881Speter * and submit them to the engine. The purpose of this function is
497251881Speter * to simplify the building of DMA engine commands by automatically
498251881Speter * converting a simple address and size into the apropriate internal
499251881Speter * or PCI / PCIe address list. This function does not support gather lists,
500251881Speter * so you will need to build your own lists in that case.
501251881Speter *
502251881Speter * @param engine Engine to submit to (0 to cvmx_dma_engine_get_num()-1)
503251881Speter * @param header DMA Command header. Note that the nfst and nlst fields do not
504251881Speter *               need to be filled in. All other fields must be set properly.
505299742Sdim * @param first_address
506299742Sdim *               Address to use for the first pointers. In the case of INTERNAL,
507299742Sdim *               INBOUND, and OUTBOUND this is an Octeon memory address. In the
508251881Speter *               case of EXTERNAL, this is the source PCI / PCIe address.
509251881Speter * @param last_address
510251881Speter *               Address to use for the last pointers. In the case of EXTERNAL,
511251881Speter *               INBOUND, and OUTBOUND this is a PCI / PCIe address. In the
512251881Speter *               case of INTERNAL, this is the Octeon memory destination address.
513251881Speter * @param size   Size of the transfer to perform.
514251881Speter *
515251881Speter * @return Zero on success, negative on failure
516251881Speter */
517251881Speterint cvmx_dma_engine_transfer(int engine, cvmx_dma_engine_header_t header,
518251881Speter                             uint64_t first_address, uint64_t last_address,
519251881Speter                             int size)
520251881Speter{
521251881Speter    cvmx_dma_engine_buffer_t buffers[32];
522251881Speter    int words = 0;
523251881Speter
524251881Speter    switch (header.s.type)
525251881Speter    {
526251881Speter        case CVMX_DMA_ENGINE_TRANSFER_INTERNAL:
527251881Speter            header.s.nfst = __cvmx_dma_engine_build_internal_pointers(buffers, first_address, size);
528251881Speter            words += header.s.nfst;
529251881Speter            header.s.nlst = __cvmx_dma_engine_build_internal_pointers(buffers + words, last_address, size);
530251881Speter            words += header.s.nlst;
531251881Speter            break;
532251881Speter        case CVMX_DMA_ENGINE_TRANSFER_INBOUND:
533251881Speter        case CVMX_DMA_ENGINE_TRANSFER_OUTBOUND:
534251881Speter            header.s.nfst = __cvmx_dma_engine_build_internal_pointers(buffers, first_address, size);
535251881Speter            words += header.s.nfst;
536251881Speter            header.s.nlst = __cvmx_dma_engine_build_external_pointers(buffers + words, last_address, size);
537251881Speter            words +=  header.s.nlst + ((header.s.nlst-1) >> 2) + 1;
538251881Speter            break;
539251881Speter        case CVMX_DMA_ENGINE_TRANSFER_EXTERNAL:
540299742Sdim            header.s.nfst = __cvmx_dma_engine_build_external_pointers(buffers, first_address, size);
541299742Sdim            words +=  header.s.nfst + ((header.s.nfst-1) >> 2) + 1;
542299742Sdim            header.s.nlst = __cvmx_dma_engine_build_external_pointers(buffers + words, last_address, size);
543299742Sdim            words +=  header.s.nlst + ((header.s.nlst-1) >> 2) + 1;
544299742Sdim            break;
545299742Sdim    }
546299742Sdim    return cvmx_dma_engine_submit(engine, header, words, buffers);
547299742Sdim}
548251881Speter#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
549251881SpeterEXPORT_SYMBOL(cvmx_dma_engine_transfer);
550251881Speter#endif
551251881Speter#endif
552251881Speter