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