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((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
 639int 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 */
 793static 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 */
 821int 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 */
 860int 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 */
 912int 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 */
 944int 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 */
 967int 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 */
 987int 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 */
1005char 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
1009int 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 */
1021static 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