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