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