1 /* 2 * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. 3 * 4 * See file CREDITS for list of people who contributed to this 5 * project. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 20 * MA 02111-1307 USA 21 */ 22 23 /*Main C file for multi-channel DMA API. */ 24 25 #include <common.h> 26 27 #ifdef CONFIG_FSLDMAFEC 28 29 #include <MCD_dma.h> 30 #include <MCD_tasksInit.h> 31 #include <MCD_progCheck.h> 32 33 /********************************************************************/ 34 /* This is an API-internal pointer to the DMA's registers */ 35 dmaRegs *MCD_dmaBar; 36 37 /* 38 * These are the real and model task tables as generated by the 39 * build process 40 */ 41 extern TaskTableEntry MCD_realTaskTableSrc[NCHANNELS]; 42 extern TaskTableEntry MCD_modelTaskTableSrc[NUMOFVARIANTS]; 43 44 /* 45 * However, this (usually) gets relocated to on-chip SRAM, at which 46 * point we access them as these tables 47 */ 48 volatile TaskTableEntry *MCD_taskTable; 49 TaskTableEntry *MCD_modelTaskTable; 50 51 /* 52 * MCD_chStatus[] is an array of status indicators for remembering 53 * whether a DMA has ever been attempted on each channel, pausing 54 * status, etc. 55 */ 56 static int MCD_chStatus[NCHANNELS] = { 57 MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, 58 MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, 59 MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, 60 MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA 61 }; 62 63 /* Prototypes for local functions */ 64 static void MCD_memcpy(int *dest, int *src, u32 size); 65 static void MCD_resmActions(int channel); 66 67 /* 68 * Buffer descriptors used for storage of progress info for single Dmas 69 * Also used as storage for the DMA for CRCs for single DMAs 70 * Otherwise, the DMA does not parse these buffer descriptors 71 */ 72 #ifdef MCD_INCLUDE_EU 73 extern MCD_bufDesc MCD_singleBufDescs[NCHANNELS]; 74 #else 75 MCD_bufDesc MCD_singleBufDescs[NCHANNELS]; 76 #endif 77 MCD_bufDesc *MCD_relocBuffDesc; 78 79 /* Defines for the debug control register's functions */ 80 #define DBG_CTL_COMP1_TASK (0x00002000) 81 #define DBG_CTL_ENABLE (DBG_CTL_AUTO_ARM | \ 82 DBG_CTL_BREAK | \ 83 DBG_CTL_INT_BREAK | \ 84 DBG_CTL_COMP1_TASK) 85 #define DBG_CTL_DISABLE (DBG_CTL_AUTO_ARM | \ 86 DBG_CTL_INT_BREAK | \ 87 DBG_CTL_COMP1_TASK) 88 #define DBG_KILL_ALL_STAT (0xFFFFFFFF) 89 90 /* Offset to context save area where progress info is stored */ 91 #define CSAVE_OFFSET 10 92 93 /* Defines for Byte Swapping */ 94 #define MCD_BYTE_SWAP_KILLER 0xFFF8888F 95 #define MCD_NO_BYTE_SWAP_ATALL 0x00040000 96 97 /* Execution Unit Identifiers */ 98 #define MAC 0 /* legacy - not used */ 99 #define LUAC 1 /* legacy - not used */ 100 #define CRC 2 /* legacy - not used */ 101 #define LURC 3 /* Logic Unit with CRC */ 102 103 /* Task Identifiers */ 104 #define TASK_CHAINNOEU 0 105 #define TASK_SINGLENOEU 1 106 #ifdef MCD_INCLUDE_EU 107 #define TASK_CHAINEU 2 108 #define TASK_SINGLEEU 3 109 #define TASK_FECRX 4 110 #define TASK_FECTX 5 111 #else 112 #define TASK_CHAINEU 0 113 #define TASK_SINGLEEU 1 114 #define TASK_FECRX 2 115 #define TASK_FECTX 3 116 #endif 117 118 /* 119 * Structure to remember which variant is on which channel 120 * TBD- need this? 121 */ 122 typedef struct MCD_remVariants_struct MCD_remVariant; 123 struct MCD_remVariants_struct { 124 int remDestRsdIncr[NCHANNELS]; /* -1,0,1 */ 125 int remSrcRsdIncr[NCHANNELS]; /* -1,0,1 */ 126 s16 remDestIncr[NCHANNELS]; /* DestIncr */ 127 s16 remSrcIncr[NCHANNELS]; /* srcIncr */ 128 u32 remXferSize[NCHANNELS]; /* xferSize */ 129 }; 130 131 /* Structure to remember the startDma parameters for each channel */ 132 MCD_remVariant MCD_remVariants; 133 /********************************************************************/ 134 /* Function: MCD_initDma 135 * Purpose: Initializes the DMA API by setting up a pointer to the DMA 136 * registers, relocating and creating the appropriate task 137 * structures, and setting up some global settings 138 * Arguments: 139 * dmaBarAddr - pointer to the multichannel DMA registers 140 * taskTableDest - location to move DMA task code and structs to 141 * flags - operational parameters 142 * Return Value: 143 * MCD_TABLE_UNALIGNED if taskTableDest is not 512-byte aligned 144 * MCD_OK otherwise 145 */ 146 extern u32 MCD_funcDescTab0[]; 147 148 int MCD_initDma(dmaRegs * dmaBarAddr, void *taskTableDest, u32 flags) 149 { 150 int i; 151 TaskTableEntry *entryPtr; 152 153 /* setup the local pointer to register set */ 154 MCD_dmaBar = dmaBarAddr; 155 156 /* do we need to move/create a task table */ 157 if ((flags & MCD_RELOC_TASKS) != 0) { 158 int fixedSize; 159 u32 *fixedPtr; 160 /*int *tablePtr = taskTableDest;TBD */ 161 int varTabsOffset, funcDescTabsOffset, contextSavesOffset; 162 int taskDescTabsOffset; 163 int taskTableSize, varTabsSize, funcDescTabsSize, 164 contextSavesSize; 165 int taskDescTabSize; 166 167 int i; 168 169 /* check if physical address is aligned on 512 byte boundary */ 170 if (((u32) taskTableDest & 0x000001ff) != 0) 171 return (MCD_TABLE_UNALIGNED); 172 173 /* set up local pointer to task Table */ 174 MCD_taskTable = taskTableDest; 175 176 /* 177 * Create a task table: 178 * - compute aligned base offsets for variable tables and 179 * function descriptor tables, then 180 * - loop through the task table and setup the pointers 181 * - copy over model task table with the the actual task 182 * descriptor tables 183 */ 184 185 taskTableSize = NCHANNELS * sizeof(TaskTableEntry); 186 /* align variable tables to size */ 187 varTabsOffset = taskTableSize + (u32) taskTableDest; 188 if ((varTabsOffset & (VAR_TAB_SIZE - 1)) != 0) 189 varTabsOffset = 190 (varTabsOffset + VAR_TAB_SIZE) & (~VAR_TAB_SIZE); 191 /* align function descriptor tables */ 192 varTabsSize = NCHANNELS * VAR_TAB_SIZE; 193 funcDescTabsOffset = varTabsOffset + varTabsSize; 194 195 if ((funcDescTabsOffset & (FUNCDESC_TAB_SIZE - 1)) != 0) 196 funcDescTabsOffset = 197 (funcDescTabsOffset + 198 FUNCDESC_TAB_SIZE) & (~FUNCDESC_TAB_SIZE); 199 200 funcDescTabsSize = FUNCDESC_TAB_NUM * FUNCDESC_TAB_SIZE; 201 contextSavesOffset = funcDescTabsOffset + funcDescTabsSize; 202 contextSavesSize = (NCHANNELS * CONTEXT_SAVE_SIZE); 203 fixedSize = 204 taskTableSize + varTabsSize + funcDescTabsSize + 205 contextSavesSize; 206 207 /* zero the thing out */ 208 fixedPtr = (u32 *) taskTableDest; 209 for (i = 0; i < (fixedSize / 4); i++) 210 fixedPtr[i] = 0; 211 212 entryPtr = (TaskTableEntry *) MCD_taskTable; 213 /* set up fixed pointers */ 214 for (i = 0; i < NCHANNELS; i++) { 215 /* update ptr to local value */ 216 entryPtr[i].varTab = (u32) varTabsOffset; 217 entryPtr[i].FDTandFlags = 218 (u32) funcDescTabsOffset | MCD_TT_FLAGS_DEF; 219 entryPtr[i].contextSaveSpace = (u32) contextSavesOffset; 220 varTabsOffset += VAR_TAB_SIZE; 221 #ifdef MCD_INCLUDE_EU 222 /* if not there is only one, just point to the 223 same one */ 224 funcDescTabsOffset += FUNCDESC_TAB_SIZE; 225 #endif 226 contextSavesOffset += CONTEXT_SAVE_SIZE; 227 } 228 /* copy over the function descriptor table */ 229 for (i = 0; i < FUNCDESC_TAB_NUM; i++) { 230 MCD_memcpy((void *)(entryPtr[i]. 231 FDTandFlags & ~MCD_TT_FLAGS_MASK), 232 (void *)MCD_funcDescTab0, FUNCDESC_TAB_SIZE); 233 } 234 235 /* copy model task table to where the context saves stuff 236 leaves off */ 237 MCD_modelTaskTable = (TaskTableEntry *) contextSavesOffset; 238 239 MCD_memcpy((void *)MCD_modelTaskTable, 240 (void *)MCD_modelTaskTableSrc, 241 NUMOFVARIANTS * sizeof(TaskTableEntry)); 242 243 /* point to local version of model task table */ 244 entryPtr = MCD_modelTaskTable; 245 taskDescTabsOffset = (u32) MCD_modelTaskTable + 246 (NUMOFVARIANTS * sizeof(TaskTableEntry)); 247 248 /* copy actual task code and update TDT ptrs in local 249 model task table */ 250 for (i = 0; i < NUMOFVARIANTS; i++) { 251 taskDescTabSize = 252 entryPtr[i].TDTend - entryPtr[i].TDTstart + 4; 253 MCD_memcpy((void *)taskDescTabsOffset, 254 (void *)entryPtr[i].TDTstart, 255 taskDescTabSize); 256 entryPtr[i].TDTstart = (u32) taskDescTabsOffset; 257 taskDescTabsOffset += taskDescTabSize; 258 entryPtr[i].TDTend = (u32) taskDescTabsOffset - 4; 259 } 260 #ifdef MCD_INCLUDE_EU 261 /* Tack single DMA BDs onto end of code so API controls 262 where they are since DMA might write to them */ 263 MCD_relocBuffDesc = 264 (MCD_bufDesc *) (entryPtr[NUMOFVARIANTS - 1].TDTend + 4); 265 #else 266 /* DMA does not touch them so they can be wherever and we 267 don't need to waste SRAM on them */ 268 MCD_relocBuffDesc = MCD_singleBufDescs; 269 #endif 270 } else { 271 /* point the would-be relocated task tables and the 272 buffer descriptors to the ones the linker generated */ 273 274 if (((u32) MCD_realTaskTableSrc & 0x000001ff) != 0) 275 return (MCD_TABLE_UNALIGNED); 276 277 /* need to add code to make sure that every thing else is 278 aligned properly TBD. this is problematic if we init 279 more than once or after running tasks, need to add 280 variable to see if we have aleady init'd */ 281 entryPtr = MCD_realTaskTableSrc; 282 for (i = 0; i < NCHANNELS; i++) { 283 if (((entryPtr[i].varTab & (VAR_TAB_SIZE - 1)) != 0) || 284 ((entryPtr[i]. 285 FDTandFlags & (FUNCDESC_TAB_SIZE - 1)) != 0)) 286 return (MCD_TABLE_UNALIGNED); 287 } 288 289 MCD_taskTable = MCD_realTaskTableSrc; 290 MCD_modelTaskTable = MCD_modelTaskTableSrc; 291 MCD_relocBuffDesc = MCD_singleBufDescs; 292 } 293 294 /* Make all channels as totally inactive, and remember them as such: */ 295 296 MCD_dmaBar->taskbar = (u32) MCD_taskTable; 297 for (i = 0; i < NCHANNELS; i++) { 298 MCD_dmaBar->taskControl[i] = 0x0; 299 MCD_chStatus[i] = MCD_NO_DMA; 300 } 301 302 /* Set up pausing mechanism to inactive state: */ 303 /* no particular values yet for either comparator registers */ 304 MCD_dmaBar->debugComp1 = 0; 305 MCD_dmaBar->debugComp2 = 0; 306 MCD_dmaBar->debugControl = DBG_CTL_DISABLE; 307 MCD_dmaBar->debugStatus = DBG_KILL_ALL_STAT; 308 309 /* enable or disable commbus prefetch, really need an ifdef or 310 something to keep from trying to set this in the 8220 */ 311 if ((flags & MCD_COMM_PREFETCH_EN) != 0) 312 MCD_dmaBar->ptdControl &= ~PTD_CTL_COMM_PREFETCH; 313 else 314 MCD_dmaBar->ptdControl |= PTD_CTL_COMM_PREFETCH; 315 316 return (MCD_OK); 317 } 318 319 /*********************** End of MCD_initDma() ***********************/ 320 321 /********************************************************************/ 322 /* Function: MCD_dmaStatus 323 * Purpose: Returns the status of the DMA on the requested channel 324 * Arguments: channel - channel number 325 * Returns: Predefined status indicators 326 */ 327 int MCD_dmaStatus(int channel) 328 { 329 u16 tcrValue; 330 331 if ((channel < 0) || (channel >= NCHANNELS)) 332 return (MCD_CHANNEL_INVALID); 333 334 tcrValue = MCD_dmaBar->taskControl[channel]; 335 if ((tcrValue & TASK_CTL_EN) == 0) { /* nothing running */ 336 /* if last reported with task enabled */ 337 if (MCD_chStatus[channel] == MCD_RUNNING 338 || MCD_chStatus[channel] == MCD_IDLE) 339 MCD_chStatus[channel] = MCD_DONE; 340 } else { /* something is running */ 341 342 /* There are three possibilities: paused, running or idle. */ 343 if (MCD_chStatus[channel] == MCD_RUNNING 344 || MCD_chStatus[channel] == MCD_IDLE) { 345 MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT; 346 /* This register is selected to know which initiator is 347 actually asserted. */ 348 if ((MCD_dmaBar->ptdDebug >> channel) & 0x1) 349 MCD_chStatus[channel] = MCD_RUNNING; 350 else 351 MCD_chStatus[channel] = MCD_IDLE; 352 /* do not change the status if it is already paused. */ 353 } 354 } 355 return MCD_chStatus[channel]; 356 } 357 358 /******************** End of MCD_dmaStatus() ************************/ 359 360 /********************************************************************/ 361 /* Function: MCD_startDma 362 * Ppurpose: Starts a particular kind of DMA 363 * Arguments: 364 * srcAddr - the channel on which to run the DMA 365 * srcIncr - the address to move data from, or buffer-descriptor address 366 * destAddr - the amount to increment the source address per transfer 367 * destIncr - the address to move data to 368 * dmaSize - the amount to increment the destination address per transfer 369 * xferSize - the number bytes in of each data movement (1, 2, or 4) 370 * initiator - what device initiates the DMA 371 * priority - priority of the DMA 372 * flags - flags describing the DMA 373 * funcDesc - description of byte swapping, bit swapping, and CRC actions 374 * srcAddrVirt - virtual buffer descriptor address TBD 375 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK 376 */ 377 378 int MCD_startDma(int channel, s8 * srcAddr, s16 srcIncr, s8 * destAddr, 379 s16 destIncr, u32 dmaSize, u32 xferSize, u32 initiator, 380 int priority, u32 flags, u32 funcDesc 381 #ifdef MCD_NEED_ADDR_TRANS 382 s8 * srcAddrVirt 383 #endif 384 ) 385 { 386 int srcRsdIncr, destRsdIncr; 387 int *cSave; 388 short xferSizeIncr; 389 int tcrCount = 0; 390 #ifdef MCD_INCLUDE_EU 391 u32 *realFuncArray; 392 #endif 393 394 if ((channel < 0) || (channel >= NCHANNELS)) 395 return (MCD_CHANNEL_INVALID); 396 397 /* tbd - need to determine the proper response to a bad funcDesc when 398 not including EU functions, for now, assign a benign funcDesc, but 399 maybe should return an error */ 400 #ifndef MCD_INCLUDE_EU 401 funcDesc = MCD_FUNC_NOEU1; 402 #endif 403 404 #ifdef MCD_DEBUG 405 printf("startDma:Setting up params\n"); 406 #endif 407 /* Set us up for task-wise priority. We don't technically need to do 408 this on every start, but since the register involved is in the same 409 longword as other registers that users are in control of, setting 410 it more than once is probably preferable. That since the 411 documentation doesn't seem to be completely consistent about the 412 nature of the PTD control register. */ 413 MCD_dmaBar->ptdControl |= (u16) 0x8000; 414 415 /* Not sure what we need to keep here rtm TBD */ 416 #if 1 417 /* Calculate additional parameters to the regular DMA calls. */ 418 srcRsdIncr = srcIncr < 0 ? -1 : (srcIncr > 0 ? 1 : 0); 419 destRsdIncr = destIncr < 0 ? -1 : (destIncr > 0 ? 1 : 0); 420 421 xferSizeIncr = (xferSize & 0xffff) | 0x20000000; 422 423 /* Remember for each channel which variant is running. */ 424 MCD_remVariants.remSrcRsdIncr[channel] = srcRsdIncr; 425 MCD_remVariants.remDestRsdIncr[channel] = destRsdIncr; 426 MCD_remVariants.remDestIncr[channel] = destIncr; 427 MCD_remVariants.remSrcIncr[channel] = srcIncr; 428 MCD_remVariants.remXferSize[channel] = xferSize; 429 #endif 430 431 cSave = 432 (int *)(MCD_taskTable[channel].contextSaveSpace) + CSAVE_OFFSET + 433 CURRBD; 434 435 #ifdef MCD_INCLUDE_EU 436 /* may move this to EU specific calls */ 437 realFuncArray = 438 (u32 *) (MCD_taskTable[channel].FDTandFlags & 0xffffff00); 439 /* Modify the LURC's normal and byte-residue-loop functions according 440 to parameter. */ 441 realFuncArray[(LURC * 16)] = xferSize == 4 ? 442 funcDesc : xferSize == 2 ? 443 funcDesc & 0xfffff00f : funcDesc & 0xffff000f; 444 realFuncArray[(LURC * 16 + 1)] = 445 (funcDesc & MCD_BYTE_SWAP_KILLER) | MCD_NO_BYTE_SWAP_ATALL; 446 #endif 447 /* Write the initiator field in the TCR, and also set the 448 initiator-hold bit. Note that,due to a hardware quirk, this could 449 collide with an MDE access to the initiator-register file, so we 450 have to verify that the write reads back correctly. */ 451 452 MCD_dmaBar->taskControl[channel] = 453 (initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM; 454 455 while (((MCD_dmaBar->taskControl[channel] & 0x1fff) != 456 ((initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM)) 457 && (tcrCount < 1000)) { 458 tcrCount++; 459 /*MCD_dmaBar->ptd_tcr[channel] = (initiator << 8) | 0x0020; */ 460 MCD_dmaBar->taskControl[channel] = 461 (initiator << 8) | TASK_CTL_HIPRITSKEN | 462 TASK_CTL_HLDINITNUM; 463 } 464 465 MCD_dmaBar->priority[channel] = (u8) priority & PRIORITY_PRI_MASK; 466 /* should be albe to handle this stuff with only one write to ts reg 467 - tbd */ 468 if (channel < 8 && channel >= 0) { 469 MCD_dmaBar->taskSize0 &= ~(0xf << (7 - channel) * 4); 470 MCD_dmaBar->taskSize0 |= 471 (xferSize & 3) << (((7 - channel) * 4) + 2); 472 MCD_dmaBar->taskSize0 |= (xferSize & 3) << ((7 - channel) * 4); 473 } else { 474 MCD_dmaBar->taskSize1 &= ~(0xf << (15 - channel) * 4); 475 MCD_dmaBar->taskSize1 |= 476 (xferSize & 3) << (((15 - channel) * 4) + 2); 477 MCD_dmaBar->taskSize1 |= (xferSize & 3) << ((15 - channel) * 4); 478 } 479 480 /* setup task table flags/options which mostly control the line 481 buffers */ 482 MCD_taskTable[channel].FDTandFlags &= ~MCD_TT_FLAGS_MASK; 483 MCD_taskTable[channel].FDTandFlags |= (MCD_TT_FLAGS_MASK & flags); 484 485 if (flags & MCD_FECTX_DMA) { 486 /* TDTStart and TDTEnd */ 487 MCD_taskTable[channel].TDTstart = 488 MCD_modelTaskTable[TASK_FECTX].TDTstart; 489 MCD_taskTable[channel].TDTend = 490 MCD_modelTaskTable[TASK_FECTX].TDTend; 491 MCD_startDmaENetXmit(srcAddr, srcAddr, destAddr, MCD_taskTable, 492 channel); 493 } else if (flags & MCD_FECRX_DMA) { 494 /* TDTStart and TDTEnd */ 495 MCD_taskTable[channel].TDTstart = 496 MCD_modelTaskTable[TASK_FECRX].TDTstart; 497 MCD_taskTable[channel].TDTend = 498 MCD_modelTaskTable[TASK_FECRX].TDTend; 499 MCD_startDmaENetRcv(srcAddr, srcAddr, destAddr, MCD_taskTable, 500 channel); 501 } else if (flags & MCD_SINGLE_DMA) { 502 /* this buffer descriptor is used for storing off initial 503 parameters for later progress query calculation and for the 504 DMA to write the resulting checksum. The DMA does not use 505 this to determine how to operate, that info is passed with 506 the init routine */ 507 MCD_relocBuffDesc[channel].srcAddr = srcAddr; 508 MCD_relocBuffDesc[channel].destAddr = destAddr; 509 510 /* definitely not its final value */ 511 MCD_relocBuffDesc[channel].lastDestAddr = destAddr; 512 513 MCD_relocBuffDesc[channel].dmaSize = dmaSize; 514 MCD_relocBuffDesc[channel].flags = 0; /* not used */ 515 MCD_relocBuffDesc[channel].csumResult = 0; /* not used */ 516 MCD_relocBuffDesc[channel].next = 0; /* not used */ 517 518 /* Initialize the progress-querying stuff to show no 519 progress: */ 520 ((volatile int *)MCD_taskTable[channel]. 521 contextSaveSpace)[SRCPTR + CSAVE_OFFSET] = (int)srcAddr; 522 ((volatile int *)MCD_taskTable[channel]. 523 contextSaveSpace)[DESTPTR + CSAVE_OFFSET] = (int)destAddr; 524 ((volatile int *)MCD_taskTable[channel]. 525 contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0; 526 ((volatile int *)MCD_taskTable[channel]. 527 contextSaveSpace)[CURRBD + CSAVE_OFFSET] = 528 (u32) & (MCD_relocBuffDesc[channel]); 529 /* tbd - need to keep the user from trying to call the EU 530 routine when MCD_INCLUDE_EU is not defined */ 531 if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) { 532 /* TDTStart and TDTEnd */ 533 MCD_taskTable[channel].TDTstart = 534 MCD_modelTaskTable[TASK_SINGLENOEU].TDTstart; 535 MCD_taskTable[channel].TDTend = 536 MCD_modelTaskTable[TASK_SINGLENOEU].TDTend; 537 MCD_startDmaSingleNoEu(srcAddr, srcIncr, destAddr, 538 destIncr, dmaSize, xferSizeIncr, 539 flags, (int *) 540 &(MCD_relocBuffDesc[channel]), 541 cSave, MCD_taskTable, channel); 542 } else { 543 /* TDTStart and TDTEnd */ 544 MCD_taskTable[channel].TDTstart = 545 MCD_modelTaskTable[TASK_SINGLEEU].TDTstart; 546 MCD_taskTable[channel].TDTend = 547 MCD_modelTaskTable[TASK_SINGLEEU].TDTend; 548 MCD_startDmaSingleEu(srcAddr, srcIncr, destAddr, 549 destIncr, dmaSize, xferSizeIncr, 550 flags, (int *) 551 &(MCD_relocBuffDesc[channel]), 552 cSave, MCD_taskTable, channel); 553 } 554 } else { /* chained DMAS */ 555 /* Initialize the progress-querying stuff to show no 556 progress: */ 557 #if 1 558 /* (!defined(MCD_NEED_ADDR_TRANS)) */ 559 ((volatile int *)MCD_taskTable[channel]. 560 contextSaveSpace)[SRCPTR + CSAVE_OFFSET] 561 = (int)((MCD_bufDesc *) srcAddr)->srcAddr; 562 ((volatile int *)MCD_taskTable[channel]. 563 contextSaveSpace)[DESTPTR + CSAVE_OFFSET] 564 = (int)((MCD_bufDesc *) srcAddr)->destAddr; 565 #else 566 /* if using address translation, need the virtual addr of the 567 first buffdesc */ 568 ((volatile int *)MCD_taskTable[channel]. 569 contextSaveSpace)[SRCPTR + CSAVE_OFFSET] 570 = (int)((MCD_bufDesc *) srcAddrVirt)->srcAddr; 571 ((volatile int *)MCD_taskTable[channel]. 572 contextSaveSpace)[DESTPTR + CSAVE_OFFSET] 573 = (int)((MCD_bufDesc *) srcAddrVirt)->destAddr; 574 #endif 575 ((volatile int *)MCD_taskTable[channel]. 576 contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0; 577 ((volatile int *)MCD_taskTable[channel]. 578 contextSaveSpace)[CURRBD + CSAVE_OFFSET] = (u32) srcAddr; 579 580 if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) { 581 /*TDTStart and TDTEnd */ 582 MCD_taskTable[channel].TDTstart = 583 MCD_modelTaskTable[TASK_CHAINNOEU].TDTstart; 584 MCD_taskTable[channel].TDTend = 585 MCD_modelTaskTable[TASK_CHAINNOEU].TDTend; 586 MCD_startDmaChainNoEu((int *)srcAddr, srcIncr, 587 destIncr, xferSize, 588 xferSizeIncr, cSave, 589 MCD_taskTable, channel); 590 } else { 591 /*TDTStart and TDTEnd */ 592 MCD_taskTable[channel].TDTstart = 593 MCD_modelTaskTable[TASK_CHAINEU].TDTstart; 594 MCD_taskTable[channel].TDTend = 595 MCD_modelTaskTable[TASK_CHAINEU].TDTend; 596 MCD_startDmaChainEu((int *)srcAddr, srcIncr, destIncr, 597 xferSize, xferSizeIncr, cSave, 598 MCD_taskTable, channel); 599 } 600 } 601 MCD_chStatus[channel] = MCD_IDLE; 602 return (MCD_OK); 603 } 604 605 /************************ End of MCD_startDma() *********************/ 606 607 /********************************************************************/ 608 /* Function: MCD_XferProgrQuery 609 * Purpose: Returns progress of DMA on requested channel 610 * Arguments: channel - channel to retrieve progress for 611 * progRep - pointer to user supplied MCD_XferProg struct 612 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK 613 * 614 * Notes: 615 * MCD_XferProgrQuery() upon completing or after aborting a DMA, or 616 * while the DMA is in progress, this function returns the first 617 * DMA-destination address not (or not yet) used in the DMA. When 618 * encountering a non-ready buffer descriptor, the information for 619 * the last completed descriptor is returned. 620 * 621 * MCD_XferProgQuery() has to avoid the possibility of getting 622 * partially-updated information in the event that we should happen 623 * to query DMA progress just as the DMA is updating it. It does that 624 * by taking advantage of the fact context is not saved frequently for 625 * the most part. We therefore read it at least twice until we get the 626 * same information twice in a row. 627 * 628 * Because a small, but not insignificant, amount of time is required 629 * to write out the progress-query information, especially upon 630 * completion of the DMA, it would be wise to guarantee some time lag 631 * between successive readings of the progress-query information. 632 */ 633 634 /* How many iterations of the loop below to execute to stabilize values */ 635 #define STABTIME 0 636 637 int MCD_XferProgrQuery(int channel, MCD_XferProg * progRep) 638 { 639 MCD_XferProg prevRep; 640 int again; /* true if we are to try again to ge 641 consistent results */ 642 int i; /* used as a time-waste counter */ 643 int destDiffBytes; /* Total no of bytes that we think actually 644 got xfered. */ 645 int numIterations; /* number of iterations */ 646 int bytesNotXfered; /* bytes that did not get xfered. */ 647 s8 *LWAlignedInitDestAddr, *LWAlignedCurrDestAddr; 648 int subModVal, addModVal; /* Mode values to added and subtracted 649 from the final destAddr */ 650 651 if ((channel < 0) || (channel >= NCHANNELS)) 652 return (MCD_CHANNEL_INVALID); 653 654 /* Read a trial value for the progress-reporting values */ 655 prevRep.lastSrcAddr = 656 (s8 *) ((volatile int *)MCD_taskTable[channel]. 657 contextSaveSpace)[SRCPTR + CSAVE_OFFSET]; 658 prevRep.lastDestAddr = 659 (s8 *) ((volatile int *)MCD_taskTable[channel]. 660 contextSaveSpace)[DESTPTR + CSAVE_OFFSET]; 661 prevRep.dmaSize = 662 ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DCOUNT + 663 CSAVE_OFFSET]; 664 prevRep.currBufDesc = 665 (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel]. 666 contextSaveSpace)[CURRBD + CSAVE_OFFSET]; 667 /* Repeatedly reread those values until they match previous values: */ 668 do { 669 /* Waste a little bit of time to ensure stability: */ 670 for (i = 0; i < STABTIME; i++) { 671 /* make sure this loop does something so that it 672 doesn't get optimized out */ 673 i += i >> 2; 674 } 675 /* Check them again: */ 676 progRep->lastSrcAddr = 677 (s8 *) ((volatile int *)MCD_taskTable[channel]. 678 contextSaveSpace)[SRCPTR + CSAVE_OFFSET]; 679 progRep->lastDestAddr = 680 (s8 *) ((volatile int *)MCD_taskTable[channel]. 681 contextSaveSpace)[DESTPTR + CSAVE_OFFSET]; 682 progRep->dmaSize = 683 ((volatile int *)MCD_taskTable[channel]. 684 contextSaveSpace)[DCOUNT + CSAVE_OFFSET]; 685 progRep->currBufDesc = 686 (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel]. 687 contextSaveSpace)[CURRBD + CSAVE_OFFSET]; 688 /* See if they match: */ 689 if (prevRep.lastSrcAddr != progRep->lastSrcAddr 690 || prevRep.lastDestAddr != progRep->lastDestAddr 691 || prevRep.dmaSize != progRep->dmaSize 692 || prevRep.currBufDesc != progRep->currBufDesc) { 693 /* If they don't match, remember previous values and 694 try again: */ 695 prevRep.lastSrcAddr = progRep->lastSrcAddr; 696 prevRep.lastDestAddr = progRep->lastDestAddr; 697 prevRep.dmaSize = progRep->dmaSize; 698 prevRep.currBufDesc = progRep->currBufDesc; 699 again = MCD_TRUE; 700 } else 701 again = MCD_FALSE; 702 } while (again == MCD_TRUE); 703 704 /* Update the dCount, srcAddr and destAddr */ 705 /* To calculate dmaCount, we consider destination address. C 706 overs M1,P1,Z for destination */ 707 switch (MCD_remVariants.remDestRsdIncr[channel]) { 708 case MINUS1: 709 subModVal = 710 ((int)progRep-> 711 lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) - 712 1); 713 addModVal = 714 ((int)progRep->currBufDesc-> 715 destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1); 716 LWAlignedInitDestAddr = 717 (progRep->currBufDesc->destAddr) - addModVal; 718 LWAlignedCurrDestAddr = (progRep->lastDestAddr) - subModVal; 719 destDiffBytes = LWAlignedInitDestAddr - LWAlignedCurrDestAddr; 720 bytesNotXfered = 721 (destDiffBytes / MCD_remVariants.remDestIncr[channel]) * 722 (MCD_remVariants.remDestIncr[channel] 723 + MCD_remVariants.remXferSize[channel]); 724 progRep->dmaSize = 725 destDiffBytes - bytesNotXfered + addModVal - subModVal; 726 break; 727 case ZERO: 728 progRep->lastDestAddr = progRep->currBufDesc->destAddr; 729 break; 730 case PLUS1: 731 /* This value has to be subtracted from the final 732 calculated dCount. */ 733 subModVal = 734 ((int)progRep->currBufDesc-> 735 destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1); 736 /* These bytes are already in lastDestAddr. */ 737 addModVal = 738 ((int)progRep-> 739 lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) - 740 1); 741 LWAlignedInitDestAddr = 742 (progRep->currBufDesc->destAddr) - subModVal; 743 LWAlignedCurrDestAddr = (progRep->lastDestAddr) - addModVal; 744 destDiffBytes = (progRep->lastDestAddr - LWAlignedInitDestAddr); 745 numIterations = 746 (LWAlignedCurrDestAddr - 747 LWAlignedInitDestAddr) / 748 MCD_remVariants.remDestIncr[channel]; 749 bytesNotXfered = 750 numIterations * (MCD_remVariants.remDestIncr[channel] 751 - MCD_remVariants.remXferSize[channel]); 752 progRep->dmaSize = destDiffBytes - bytesNotXfered - subModVal; 753 break; 754 default: 755 break; 756 } 757 758 /* This covers M1,P1,Z for source */ 759 switch (MCD_remVariants.remSrcRsdIncr[channel]) { 760 case MINUS1: 761 progRep->lastSrcAddr = 762 progRep->currBufDesc->srcAddr + 763 (MCD_remVariants.remSrcIncr[channel] * 764 (progRep->dmaSize / MCD_remVariants.remXferSize[channel])); 765 break; 766 case ZERO: 767 progRep->lastSrcAddr = progRep->currBufDesc->srcAddr; 768 break; 769 case PLUS1: 770 progRep->lastSrcAddr = 771 progRep->currBufDesc->srcAddr + 772 (MCD_remVariants.remSrcIncr[channel] * 773 (progRep->dmaSize / MCD_remVariants.remXferSize[channel])); 774 break; 775 default: 776 break; 777 } 778 779 return (MCD_OK); 780 } 781 782 /******************* End of MCD_XferProgrQuery() ********************/ 783 784 /********************************************************************/ 785 /* MCD_resmActions() does the majority of the actions of a DMA resume. 786 * It is called from MCD_killDma() and MCD_resumeDma(). It has to be 787 * a separate function because the kill function has to negate the task 788 * enable before resuming it, but the resume function has to do nothing 789 * if there is no DMA on that channel (i.e., if the enable bit is 0). 790 */ 791 static void MCD_resmActions(int channel) 792 { 793 MCD_dmaBar->debugControl = DBG_CTL_DISABLE; 794 MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus; 795 /* This register is selected to know which initiator is 796 actually asserted. */ 797 MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT; 798 799 if ((MCD_dmaBar->ptdDebug >> channel) & 0x1) 800 MCD_chStatus[channel] = MCD_RUNNING; 801 else 802 MCD_chStatus[channel] = MCD_IDLE; 803 } 804 805 /********************* End of MCD_resmActions() *********************/ 806 807 /********************************************************************/ 808 /* Function: MCD_killDma 809 * Purpose: Halt the DMA on the requested channel, without any 810 * intention of resuming the DMA. 811 * Arguments: channel - requested channel 812 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK 813 * 814 * Notes: 815 * A DMA may be killed from any state, including paused state, and it 816 * always goes to the MCD_HALTED state even if it is killed while in 817 * the MCD_NO_DMA or MCD_IDLE states. 818 */ 819 int MCD_killDma(int channel) 820 { 821 /* MCD_XferProg progRep; */ 822 823 if ((channel < 0) || (channel >= NCHANNELS)) 824 return (MCD_CHANNEL_INVALID); 825 826 MCD_dmaBar->taskControl[channel] = 0x0; 827 MCD_resumeDma(channel); 828 /* 829 * This must be after the write to the TCR so that the task doesn't 830 * start up again momentarily, and before the status assignment so 831 * as to override whatever MCD_resumeDma() may do to the channel 832 * status. 833 */ 834 MCD_chStatus[channel] = MCD_HALTED; 835 836 /* 837 * Update the current buffer descriptor's lastDestAddr field 838 * 839 * MCD_XferProgrQuery (channel, &progRep); 840 * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr; 841 */ 842 return (MCD_OK); 843 } 844 845 /************************ End of MCD_killDma() **********************/ 846 847 /********************************************************************/ 848 /* Function: MCD_continDma 849 * Purpose: Continue a DMA which as stopped due to encountering an 850 * unready buffer descriptor. 851 * Arguments: channel - channel to continue the DMA on 852 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK 853 * 854 * Notes: 855 * This routine does not check to see if there is a task which can 856 * be continued. Also this routine should not be used with single DMAs. 857 */ 858 int MCD_continDma(int channel) 859 { 860 if ((channel < 0) || (channel >= NCHANNELS)) 861 return (MCD_CHANNEL_INVALID); 862 863 MCD_dmaBar->taskControl[channel] |= TASK_CTL_EN; 864 MCD_chStatus[channel] = MCD_RUNNING; 865 866 return (MCD_OK); 867 } 868 869 /********************** End of MCD_continDma() **********************/ 870 871 /********************************************************************* 872 * MCD_pauseDma() and MCD_resumeDma() below use the DMA's debug unit 873 * to freeze a task and resume it. We freeze a task by breakpointing 874 * on the stated task. That is, not any specific place in the task, 875 * but any time that task executes. In particular, when that task 876 * executes, we want to freeze that task and only that task. 877 * 878 * The bits of the debug control register influence interrupts vs. 879 * breakpoints as follows: 880 * - Bits 14 and 0 enable or disable debug functions. If enabled, you 881 * will get the interrupt but you may or may not get a breakpoint. 882 * - Bits 2 and 1 decide whether you also get a breakpoint in addition 883 * to an interrupt. 884 * 885 * The debug unit can do these actions in response to either internally 886 * detected breakpoint conditions from the comparators, or in response 887 * to the external breakpoint pin, or both. 888 * - Bits 14 and 1 perform the above-described functions for 889 * internally-generated conditions, i.e., the debug comparators. 890 * - Bits 0 and 2 perform the above-described functions for external 891 * conditions, i.e., the breakpoint external pin. 892 * 893 * Note that, although you "always" get the interrupt when you turn 894 * the debug functions, the interrupt can nevertheless, if desired, be 895 * masked by the corresponding bit in the PTD's IMR. Note also that 896 * this means that bits 14 and 0 must enable debug functions before 897 * bits 1 and 2, respectively, have any effect. 898 * 899 * NOTE: It's extremely important to not pause more than one DMA channel 900 * at a time. 901 ********************************************************************/ 902 903 /********************************************************************/ 904 /* Function: MCD_pauseDma 905 * Purpose: Pauses the DMA on a given channel (if any DMA is running 906 * on that channel). 907 * Arguments: channel 908 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK 909 */ 910 int MCD_pauseDma(int channel) 911 { 912 /* MCD_XferProg progRep; */ 913 914 if ((channel < 0) || (channel >= NCHANNELS)) 915 return (MCD_CHANNEL_INVALID); 916 917 if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) { 918 MCD_dmaBar->debugComp1 = channel; 919 MCD_dmaBar->debugControl = 920 DBG_CTL_ENABLE | (1 << (channel + 16)); 921 MCD_chStatus[channel] = MCD_PAUSED; 922 923 /* 924 * Update the current buffer descriptor's lastDestAddr field 925 * 926 * MCD_XferProgrQuery (channel, &progRep); 927 * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr; 928 */ 929 } 930 return (MCD_OK); 931 } 932 933 /************************* End of MCD_pauseDma() ********************/ 934 935 /********************************************************************/ 936 /* Function: MCD_resumeDma 937 * Purpose: Resumes the DMA on a given channel (if any DMA is 938 * running on that channel). 939 * Arguments: channel - channel on which to resume DMA 940 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK 941 */ 942 int MCD_resumeDma(int channel) 943 { 944 if ((channel < 0) || (channel >= NCHANNELS)) 945 return (MCD_CHANNEL_INVALID); 946 947 if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) 948 MCD_resmActions(channel); 949 950 return (MCD_OK); 951 } 952 953 /************************ End of MCD_resumeDma() ********************/ 954 955 /********************************************************************/ 956 /* Function: MCD_csumQuery 957 * Purpose: Provide the checksum after performing a non-chained DMA 958 * Arguments: channel - channel to report on 959 * csum - pointer to where to write the checksum/CRC 960 * Returns: MCD_ERROR if the channel is invalid, else MCD_OK 961 * 962 * Notes: 963 * 964 */ 965 int MCD_csumQuery(int channel, u32 * csum) 966 { 967 #ifdef MCD_INCLUDE_EU 968 if ((channel < 0) || (channel >= NCHANNELS)) 969 return (MCD_CHANNEL_INVALID); 970 971 *csum = MCD_relocBuffDesc[channel].csumResult; 972 return (MCD_OK); 973 #else 974 return (MCD_ERROR); 975 #endif 976 } 977 978 /*********************** End of MCD_resumeDma() *********************/ 979 980 /********************************************************************/ 981 /* Function: MCD_getCodeSize 982 * Purpose: Provide the size requirements of the microcoded tasks 983 * Returns: Size in bytes 984 */ 985 int MCD_getCodeSize(void) 986 { 987 #ifdef MCD_INCLUDE_EU 988 return (0x2b5c); 989 #else 990 return (0x173c); 991 #endif 992 } 993 994 /********************** End of MCD_getCodeSize() ********************/ 995 996 /********************************************************************/ 997 /* Function: MCD_getVersion 998 * Purpose: Provide the version string and number 999 * Arguments: longVersion - user supplied pointer to a pointer to a char 1000 * which points to the version string 1001 * Returns: Version number and version string (by reference) 1002 */ 1003 char MCD_versionString[] = "Multi-channel DMA API Alpha v0.3 (2004-04-26)"; 1004 #define MCD_REV_MAJOR 0x00 1005 #define MCD_REV_MINOR 0x03 1006 1007 int MCD_getVersion(char **longVersion) 1008 { 1009 *longVersion = MCD_versionString; 1010 return ((MCD_REV_MAJOR << 8) | MCD_REV_MINOR); 1011 } 1012 1013 /********************** End of MCD_getVersion() *********************/ 1014 1015 /********************************************************************/ 1016 /* Private version of memcpy() 1017 * Note that everything this is used for is longword-aligned. 1018 */ 1019 static void MCD_memcpy(int *dest, int *src, u32 size) 1020 { 1021 u32 i; 1022 1023 for (i = 0; i < size; i += sizeof(int), dest++, src++) 1024 *dest = *src; 1025 } 1026 #endif /* CONFIG_FSLDMAFEC */ 1027