uboot/drivers/dma/MCD_dmaApi.c
<<
>>
Prefs
   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 */
  33dmaRegs *MCD_dmaBar;
  34
  35/*
  36 * These are the real and model task tables as generated by the
  37 * build process
  38 */
  39extern TaskTableEntry MCD_realTaskTableSrc[NCHANNELS];
  40extern 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 */
  46volatile TaskTableEntry *MCD_taskTable;
  47TaskTableEntry *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 */
  54static 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 */
  62static void MCD_memcpy(int *dest, int *src, u32 size);
  63static 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
  71extern MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
  72#else
  73MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
  74#endif
  75MCD_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 */
 120typedef struct MCD_remVariants_struct MCD_remVariant;
 121struct 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 */
 130MCD_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 */
 144extern u32 MCD_funcDescTab0[];
 145
 146int 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 */
 325int 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
 376int 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(srcAddr, srcAddr, destAddr, MCD_taskTable,
 490                                     channel);
 491        } else if (flags & MCD_FECRX_DMA) {
 492                /* TDTStart and TDTEnd */
 493                MCD_taskTable[channel].TDTstart =
 494                    MCD_modelTaskTable[TASK_FECRX].TDTstart;
 495                MCD_taskTable[channel].TDTend =
 496                    MCD_modelTaskTable[TASK_FECRX].TDTend;
 497                MCD_startDmaENetRcv(srcAddr, srcAddr, destAddr, MCD_taskTable,
 498                                    channel);
 499        } else if (flags & MCD_SINGLE_DMA) {
 500                /* this buffer descriptor is used for storing off initial
 501                   parameters for later progress query calculation and for the
 502                   DMA to write the resulting checksum. The DMA does not use
 503                   this to determine how to operate, that info is passed with
 504                   the init routine */
 505                MCD_relocBuffDesc[channel].srcAddr = srcAddr;
 506                MCD_relocBuffDesc[channel].destAddr = destAddr;
 507
 508                /* definitely not its final value */
 509                MCD_relocBuffDesc[channel].lastDestAddr = destAddr;
 510
 511                MCD_relocBuffDesc[channel].dmaSize = dmaSize;
 512                MCD_relocBuffDesc[channel].flags = 0;   /* not used */
 513                MCD_relocBuffDesc[channel].csumResult = 0;      /* not used */
 514                MCD_relocBuffDesc[channel].next = 0;    /* not used */
 515
 516                /* Initialize the progress-querying stuff to show no
 517                   progress: */
 518                ((volatile int *)MCD_taskTable[channel].
 519                 contextSaveSpace)[SRCPTR + CSAVE_OFFSET] = (int)srcAddr;
 520                ((volatile int *)MCD_taskTable[channel].
 521                 contextSaveSpace)[DESTPTR + CSAVE_OFFSET] = (int)destAddr;
 522                ((volatile int *)MCD_taskTable[channel].
 523                 contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
 524                ((volatile int *)MCD_taskTable[channel].
 525                 contextSaveSpace)[CURRBD + CSAVE_OFFSET] =
 526(u32) & (MCD_relocBuffDesc[channel]);
 527                /* tbd - need to keep the user from trying to call the EU
 528                   routine when MCD_INCLUDE_EU is not defined */
 529                if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) {
 530                        /* TDTStart and TDTEnd */
 531                        MCD_taskTable[channel].TDTstart =
 532                            MCD_modelTaskTable[TASK_SINGLENOEU].TDTstart;
 533                        MCD_taskTable[channel].TDTend =
 534                            MCD_modelTaskTable[TASK_SINGLENOEU].TDTend;
 535                        MCD_startDmaSingleNoEu(srcAddr, srcIncr, destAddr,
 536                                               destIncr, dmaSize, xferSizeIncr,
 537                                               flags, (int *)
 538                                               &(MCD_relocBuffDesc[channel]),
 539                                               cSave, MCD_taskTable, channel);
 540                } else {
 541                        /* TDTStart and TDTEnd */
 542                        MCD_taskTable[channel].TDTstart =
 543                            MCD_modelTaskTable[TASK_SINGLEEU].TDTstart;
 544                        MCD_taskTable[channel].TDTend =
 545                            MCD_modelTaskTable[TASK_SINGLEEU].TDTend;
 546                        MCD_startDmaSingleEu(srcAddr, srcIncr, destAddr,
 547                                             destIncr, dmaSize, xferSizeIncr,
 548                                             flags, (int *)
 549                                             &(MCD_relocBuffDesc[channel]),
 550                                             cSave, MCD_taskTable, channel);
 551                }
 552        } else {                /* chained DMAS */
 553                /* Initialize the progress-querying stuff to show no
 554                   progress: */
 555#if 1
 556                /* (!defined(MCD_NEED_ADDR_TRANS)) */
 557                ((volatile int *)MCD_taskTable[channel].
 558                 contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
 559                    = (int)((MCD_bufDesc *) srcAddr)->srcAddr;
 560                ((volatile int *)MCD_taskTable[channel].
 561                 contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
 562                    = (int)((MCD_bufDesc *) srcAddr)->destAddr;
 563#else
 564                /* if using address translation, need the virtual addr of the
 565                   first buffdesc */
 566                ((volatile int *)MCD_taskTable[channel].
 567                 contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
 568                    = (int)((MCD_bufDesc *) srcAddrVirt)->srcAddr;
 569                ((volatile int *)MCD_taskTable[channel].
 570                 contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
 571                    = (int)((MCD_bufDesc *) srcAddrVirt)->destAddr;
 572#endif
 573                ((volatile int *)MCD_taskTable[channel].
 574                 contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
 575                ((volatile int *)MCD_taskTable[channel].
 576                 contextSaveSpace)[CURRBD + CSAVE_OFFSET] = (u32) srcAddr;
 577
 578                if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) {
 579                        /*TDTStart and TDTEnd */
 580                        MCD_taskTable[channel].TDTstart =
 581                            MCD_modelTaskTable[TASK_CHAINNOEU].TDTstart;
 582                        MCD_taskTable[channel].TDTend =
 583                            MCD_modelTaskTable[TASK_CHAINNOEU].TDTend;
 584                        MCD_startDmaChainNoEu((int *)srcAddr, srcIncr,
 585                                              destIncr, xferSize,
 586                                              xferSizeIncr, cSave,
 587                                              MCD_taskTable, channel);
 588                } else {
 589                        /*TDTStart and TDTEnd */
 590                        MCD_taskTable[channel].TDTstart =
 591                            MCD_modelTaskTable[TASK_CHAINEU].TDTstart;
 592                        MCD_taskTable[channel].TDTend =
 593                            MCD_modelTaskTable[TASK_CHAINEU].TDTend;
 594                        MCD_startDmaChainEu((int *)srcAddr, srcIncr, destIncr,
 595                                            xferSize, xferSizeIncr, cSave,
 596                                            MCD_taskTable, channel);
 597                }
 598        }
 599        MCD_chStatus[channel] = MCD_IDLE;
 600        return (MCD_OK);
 601}
 602
 603/************************ End of MCD_startDma() *********************/
 604
 605/********************************************************************/
 606/* Function:    MCD_XferProgrQuery
 607 * Purpose:     Returns progress of DMA on requested channel
 608 * Arguments:   channel - channel to retrieve progress for
 609 *              progRep - pointer to user supplied MCD_XferProg struct
 610 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 611 *
 612 * Notes:
 613 *  MCD_XferProgrQuery() upon completing or after aborting a DMA, or
 614 *  while the DMA is in progress, this function returns the first
 615 *  DMA-destination address not (or not yet) used in the DMA. When
 616 *  encountering a non-ready buffer descriptor, the information for
 617 *  the last completed descriptor is returned.
 618 *
 619 *  MCD_XferProgQuery() has to avoid the possibility of getting
 620 *  partially-updated information in the event that we should happen
 621 *  to query DMA progress just as the DMA is updating it. It does that
 622 *  by taking advantage of the fact context is not saved frequently for
 623 *  the most part. We therefore read it at least twice until we get the
 624 *  same information twice in a row.
 625 *
 626 *  Because a small, but not insignificant, amount of time is required
 627 *  to write out the progress-query information, especially upon
 628 *  completion of the DMA, it would be wise to guarantee some time lag
 629 *  between successive readings of the progress-query information.
 630 */
 631
 632/* How many iterations of the loop below to execute to stabilize values */
 633#define STABTIME 0
 634
 635int MCD_XferProgrQuery(int channel, MCD_XferProg * progRep)
 636{
 637        MCD_XferProg prevRep;
 638        int again;              /* true if we are to try again to ge
 639                                   consistent results */
 640        int i;                  /* used as a time-waste counter */
 641        int destDiffBytes;      /* Total no of bytes that we think actually
 642                                   got xfered. */
 643        int numIterations;      /* number of iterations */
 644        int bytesNotXfered;     /* bytes that did not get xfered. */
 645        s8 *LWAlignedInitDestAddr, *LWAlignedCurrDestAddr;
 646        int subModVal, addModVal;       /* Mode values to added and subtracted
 647                                           from the final destAddr */
 648
 649        if ((channel < 0) || (channel >= NCHANNELS))
 650                return (MCD_CHANNEL_INVALID);
 651
 652        /* Read a trial value for the progress-reporting values */
 653        prevRep.lastSrcAddr =
 654            (s8 *) ((volatile int *)MCD_taskTable[channel].
 655                    contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
 656        prevRep.lastDestAddr =
 657            (s8 *) ((volatile int *)MCD_taskTable[channel].
 658                    contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
 659        prevRep.dmaSize =
 660            ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DCOUNT +
 661                                                                      CSAVE_OFFSET];
 662        prevRep.currBufDesc =
 663            (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel].
 664                             contextSaveSpace)[CURRBD + CSAVE_OFFSET];
 665        /* Repeatedly reread those values until they match previous values: */
 666        do {
 667                /* Waste a little bit of time to ensure stability: */
 668                for (i = 0; i < STABTIME; i++) {
 669                        /* make sure this loop does something so that it
 670                           doesn't get optimized out */
 671                        i += i >> 2;
 672                }
 673                /* Check them again: */
 674                progRep->lastSrcAddr =
 675                    (s8 *) ((volatile int *)MCD_taskTable[channel].
 676                            contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
 677                progRep->lastDestAddr =
 678                    (s8 *) ((volatile int *)MCD_taskTable[channel].
 679                            contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
 680                progRep->dmaSize =
 681                    ((volatile int *)MCD_taskTable[channel].
 682                     contextSaveSpace)[DCOUNT + CSAVE_OFFSET];
 683                progRep->currBufDesc =
 684                    (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel].
 685                                     contextSaveSpace)[CURRBD + CSAVE_OFFSET];
 686                /* See if they match: */
 687                if (prevRep.lastSrcAddr != progRep->lastSrcAddr
 688                    || prevRep.lastDestAddr != progRep->lastDestAddr
 689                    || prevRep.dmaSize != progRep->dmaSize
 690                    || prevRep.currBufDesc != progRep->currBufDesc) {
 691                        /* If they don't match, remember previous values and
 692                           try again: */
 693                        prevRep.lastSrcAddr = progRep->lastSrcAddr;
 694                        prevRep.lastDestAddr = progRep->lastDestAddr;
 695                        prevRep.dmaSize = progRep->dmaSize;
 696                        prevRep.currBufDesc = progRep->currBufDesc;
 697                        again = MCD_TRUE;
 698                } else
 699                        again = MCD_FALSE;
 700        } while (again == MCD_TRUE);
 701
 702        /* Update the dCount, srcAddr and destAddr */
 703        /* To calculate dmaCount, we consider destination address. C
 704           overs M1,P1,Z for destination */
 705        switch (MCD_remVariants.remDestRsdIncr[channel]) {
 706        case MINUS1:
 707                subModVal =
 708                    ((int)progRep->
 709                     lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) -
 710                                      1);
 711                addModVal =
 712                    ((int)progRep->currBufDesc->
 713                     destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
 714                LWAlignedInitDestAddr =
 715                    (progRep->currBufDesc->destAddr) - addModVal;
 716                LWAlignedCurrDestAddr = (progRep->lastDestAddr) - subModVal;
 717                destDiffBytes = LWAlignedInitDestAddr - LWAlignedCurrDestAddr;
 718                bytesNotXfered =
 719                    (destDiffBytes / MCD_remVariants.remDestIncr[channel]) *
 720                    (MCD_remVariants.remDestIncr[channel]
 721                     + MCD_remVariants.remXferSize[channel]);
 722                progRep->dmaSize =
 723                    destDiffBytes - bytesNotXfered + addModVal - subModVal;
 724                break;
 725        case ZERO:
 726                progRep->lastDestAddr = progRep->currBufDesc->destAddr;
 727                break;
 728        case PLUS1:
 729                /* This value has to be subtracted from the final
 730                   calculated dCount. */
 731                subModVal =
 732                    ((int)progRep->currBufDesc->
 733                     destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
 734                /* These bytes are already in lastDestAddr. */
 735                addModVal =
 736                    ((int)progRep->
 737                     lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) -
 738                                      1);
 739                LWAlignedInitDestAddr =
 740                    (progRep->currBufDesc->destAddr) - subModVal;
 741                LWAlignedCurrDestAddr = (progRep->lastDestAddr) - addModVal;
 742                destDiffBytes = (progRep->lastDestAddr - LWAlignedInitDestAddr);
 743                numIterations =
 744                    (LWAlignedCurrDestAddr -
 745                     LWAlignedInitDestAddr) /
 746                    MCD_remVariants.remDestIncr[channel];
 747                bytesNotXfered =
 748                    numIterations * (MCD_remVariants.remDestIncr[channel]
 749                                     - MCD_remVariants.remXferSize[channel]);
 750                progRep->dmaSize = destDiffBytes - bytesNotXfered - subModVal;
 751                break;
 752        default:
 753                break;
 754        }
 755
 756        /* This covers M1,P1,Z for source */
 757        switch (MCD_remVariants.remSrcRsdIncr[channel]) {
 758        case MINUS1:
 759                progRep->lastSrcAddr =
 760                    progRep->currBufDesc->srcAddr +
 761                    (MCD_remVariants.remSrcIncr[channel] *
 762                     (progRep->dmaSize / MCD_remVariants.remXferSize[channel]));
 763                break;
 764        case ZERO:
 765                progRep->lastSrcAddr = progRep->currBufDesc->srcAddr;
 766                break;
 767        case PLUS1:
 768                progRep->lastSrcAddr =
 769                    progRep->currBufDesc->srcAddr +
 770                    (MCD_remVariants.remSrcIncr[channel] *
 771                     (progRep->dmaSize / MCD_remVariants.remXferSize[channel]));
 772                break;
 773        default:
 774                break;
 775        }
 776
 777        return (MCD_OK);
 778}
 779
 780/******************* End of MCD_XferProgrQuery() ********************/
 781
 782/********************************************************************/
 783/* MCD_resmActions() does the majority of the actions of a DMA resume.
 784 * It is called from MCD_killDma() and MCD_resumeDma().  It has to be
 785 * a separate function because the kill function has to negate the task
 786 * enable before resuming it, but the resume function has to do nothing
 787 * if there is no DMA on that channel (i.e., if the enable bit is 0).
 788 */
 789static void MCD_resmActions(int channel)
 790{
 791        MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
 792        MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus;
 793        /* This register is selected to know which initiator is
 794           actually asserted. */
 795        MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT;
 796
 797        if ((MCD_dmaBar->ptdDebug >> channel) & 0x1)
 798                MCD_chStatus[channel] = MCD_RUNNING;
 799        else
 800                MCD_chStatus[channel] = MCD_IDLE;
 801}
 802
 803/********************* End of MCD_resmActions() *********************/
 804
 805/********************************************************************/
 806/* Function:    MCD_killDma
 807 * Purpose:     Halt the DMA on the requested channel, without any
 808 *              intention of resuming the DMA.
 809 * Arguments:   channel - requested channel
 810 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 811 *
 812 * Notes:
 813 *  A DMA may be killed from any state, including paused state, and it
 814 *  always goes to the MCD_HALTED state even if it is killed while in
 815 *  the MCD_NO_DMA or MCD_IDLE states.
 816 */
 817int MCD_killDma(int channel)
 818{
 819        /* MCD_XferProg progRep; */
 820
 821        if ((channel < 0) || (channel >= NCHANNELS))
 822                return (MCD_CHANNEL_INVALID);
 823
 824        MCD_dmaBar->taskControl[channel] = 0x0;
 825        MCD_resumeDma(channel);
 826        /*
 827         * This must be after the write to the TCR so that the task doesn't
 828         * start up again momentarily, and before the status assignment so
 829         * as to override whatever MCD_resumeDma() may do to the channel
 830         * status.
 831         */
 832        MCD_chStatus[channel] = MCD_HALTED;
 833
 834        /*
 835         * Update the current buffer descriptor's lastDestAddr field
 836         *
 837         * MCD_XferProgrQuery (channel, &progRep);
 838         * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
 839         */
 840        return (MCD_OK);
 841}
 842
 843/************************ End of MCD_killDma() **********************/
 844
 845/********************************************************************/
 846/* Function:    MCD_continDma
 847 * Purpose:     Continue a DMA which as stopped due to encountering an
 848 *              unready buffer descriptor.
 849 * Arguments:   channel - channel to continue the DMA on
 850 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 851 *
 852 * Notes:
 853 *  This routine does not check to see if there is a task which can
 854 *  be continued. Also this routine should not be used with single DMAs.
 855 */
 856int MCD_continDma(int channel)
 857{
 858        if ((channel < 0) || (channel >= NCHANNELS))
 859                return (MCD_CHANNEL_INVALID);
 860
 861        MCD_dmaBar->taskControl[channel] |= TASK_CTL_EN;
 862        MCD_chStatus[channel] = MCD_RUNNING;
 863
 864        return (MCD_OK);
 865}
 866
 867/********************** End of MCD_continDma() **********************/
 868
 869/*********************************************************************
 870 * MCD_pauseDma() and MCD_resumeDma() below use the DMA's debug unit
 871 * to freeze a task and resume it.  We freeze a task by breakpointing
 872 * on the stated task.  That is, not any specific place in the task,
 873 * but any time that task executes.  In particular, when that task
 874 * executes, we want to freeze that task and only that task.
 875 *
 876 * The bits of the debug control register influence interrupts vs.
 877 * breakpoints as follows:
 878 * - Bits 14 and 0 enable or disable debug functions.  If enabled, you
 879 *   will get the interrupt but you may or may not get a breakpoint.
 880 * - Bits 2 and 1 decide whether you also get a breakpoint in addition
 881 *   to an interrupt.
 882 *
 883 * The debug unit can do these actions in response to either internally
 884 * detected breakpoint conditions from the comparators, or in response
 885 * to the external breakpoint pin, or both.
 886 * - Bits 14 and 1 perform the above-described functions for
 887 *   internally-generated conditions, i.e., the debug comparators.
 888 * - Bits 0 and 2 perform the above-described functions for external
 889 *   conditions, i.e., the breakpoint external pin.
 890 *
 891 * Note that, although you "always" get the interrupt when you turn
 892 * the debug functions, the interrupt can nevertheless, if desired, be
 893 * masked by the corresponding bit in the PTD's IMR. Note also that
 894 * this means that bits 14 and 0 must enable debug functions before
 895 * bits 1 and 2, respectively, have any effect.
 896 *
 897 * NOTE: It's extremely important to not pause more than one DMA channel
 898 *  at a time.
 899 ********************************************************************/
 900
 901/********************************************************************/
 902/* Function:    MCD_pauseDma
 903 * Purpose:     Pauses the DMA on a given channel (if any DMA is running
 904 *              on that channel).
 905 * Arguments:   channel
 906 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 907 */
 908int MCD_pauseDma(int channel)
 909{
 910        /* MCD_XferProg progRep; */
 911
 912        if ((channel < 0) || (channel >= NCHANNELS))
 913                return (MCD_CHANNEL_INVALID);
 914
 915        if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) {
 916                MCD_dmaBar->debugComp1 = channel;
 917                MCD_dmaBar->debugControl =
 918                    DBG_CTL_ENABLE | (1 << (channel + 16));
 919                MCD_chStatus[channel] = MCD_PAUSED;
 920
 921                /*
 922                 * Update the current buffer descriptor's lastDestAddr field
 923                 *
 924                 * MCD_XferProgrQuery (channel, &progRep);
 925                 * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
 926                 */
 927        }
 928        return (MCD_OK);
 929}
 930
 931/************************* End of MCD_pauseDma() ********************/
 932
 933/********************************************************************/
 934/* Function:    MCD_resumeDma
 935 * Purpose:     Resumes the DMA on a given channel (if any DMA is
 936 *              running on that channel).
 937 * Arguments:   channel - channel on which to resume DMA
 938 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 939 */
 940int MCD_resumeDma(int channel)
 941{
 942        if ((channel < 0) || (channel >= NCHANNELS))
 943                return (MCD_CHANNEL_INVALID);
 944
 945        if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN)
 946                MCD_resmActions(channel);
 947
 948        return (MCD_OK);
 949}
 950
 951/************************ End of MCD_resumeDma() ********************/
 952
 953/********************************************************************/
 954/* Function:    MCD_csumQuery
 955 * Purpose:     Provide the checksum after performing a non-chained DMA
 956 * Arguments:   channel - channel to report on
 957 *              csum - pointer to where to write the checksum/CRC
 958 * Returns:     MCD_ERROR if the channel is invalid, else MCD_OK
 959 *
 960 * Notes:
 961 *
 962 */
 963int MCD_csumQuery(int channel, u32 * csum)
 964{
 965#ifdef MCD_INCLUDE_EU
 966        if ((channel < 0) || (channel >= NCHANNELS))
 967                return (MCD_CHANNEL_INVALID);
 968
 969        *csum = MCD_relocBuffDesc[channel].csumResult;
 970        return (MCD_OK);
 971#else
 972        return (MCD_ERROR);
 973#endif
 974}
 975
 976/*********************** End of MCD_resumeDma() *********************/
 977
 978/********************************************************************/
 979/* Function:    MCD_getCodeSize
 980 * Purpose:     Provide the size requirements of the microcoded tasks
 981 * Returns:     Size in bytes
 982 */
 983int MCD_getCodeSize(void)
 984{
 985#ifdef MCD_INCLUDE_EU
 986        return (0x2b5c);
 987#else
 988        return (0x173c);
 989#endif
 990}
 991
 992/********************** End of MCD_getCodeSize() ********************/
 993
 994/********************************************************************/
 995/* Function:    MCD_getVersion
 996 * Purpose:     Provide the version string and number
 997 * Arguments:   longVersion - user supplied pointer to a pointer to a char
 998 *                    which points to the version string
 999 * Returns:     Version number and version string (by reference)
1000 */
1001char MCD_versionString[] = "Multi-channel DMA API Alpha v0.3 (2004-04-26)";
1002#define MCD_REV_MAJOR   0x00
1003#define MCD_REV_MINOR   0x03
1004
1005int MCD_getVersion(char **longVersion)
1006{
1007        *longVersion = MCD_versionString;
1008        return ((MCD_REV_MAJOR << 8) | MCD_REV_MINOR);
1009}
1010
1011/********************** End of MCD_getVersion() *********************/
1012
1013/********************************************************************/
1014/* Private version of memcpy()
1015 * Note that everything this is used for is longword-aligned.
1016 */
1017static void MCD_memcpy(int *dest, int *src, u32 size)
1018{
1019        u32 i;
1020
1021        for (i = 0; i < size; i += sizeof(int), dest++, src++)
1022                *dest = *src;
1023}
1024