summaryrefslogtreecommitdiffstats
path: root/arch/m68k/mach-mcfv4e/mcdapi/MCD_dmaApi.c
blob: b4d7f2847006a2e901afb8a2c04a77fca642f086 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
/*
 * File:        MCD_dmaApi.c
 * Purpose:     Main C file for multi-channel DMA API.
 *
 * Notes:
 */

#include <asm/types.h>
#include <asm/proc/mcdapi/MCD_dma.h>
#include <asm/proc/mcdapi/MCD_tasksInit.h>
#include <asm/proc/mcdapi/MCD_progCheck.h>

/*
 * This is an API-internal pointer to the DMA's registers
 */
dmaRegs *MCD_dmaBar;

/*
 * These are the real and model task tables as generated by the
 * build process
 */
extern TaskTableEntry MCD_realTaskTableSrc[NCHANNELS];
extern TaskTableEntry MCD_modelTaskTableSrc[NUMOFVARIANTS];

/*
 * However, this (usually) gets relocated to on-chip SRAM, at which
 * point we access them as these tables
 */
volatile TaskTableEntry *MCD_taskTable;
TaskTableEntry *MCD_modelTaskTable;


/*
 * MCD_chStatus[] is an array of status indicators for remembering
 * whether a DMA has ever been attempted on each channel, pausing
 * status, etc.
 */
static int MCD_chStatus[NCHANNELS] =
{
    MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA,
    MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA,
    MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA,
    MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA
};

/*
 * Prototypes for local functions
 */
static void MCD_memcpy (int *dest, int *src, u32 size);
static void MCD_resmActions (int channel);

/*
 * Buffer descriptors used for storage of progress info for single Dmas
 * Also used as storage for the DMA for CRCs for single DMAs
 * Otherwise, the DMA does not parse these buffer descriptors
 */
#ifdef MCD_INCLUDE_EU
extern MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
#else
MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
#endif
MCD_bufDesc *MCD_relocBuffDesc;


/*
 * Defines for the debug control register's functions
 */
#define DBG_CTL_COMP1_TASK  (0x00002000) /* have comparator 1 look for a task # */
#define DBG_CTL_ENABLE      (DBG_CTL_AUTO_ARM    | \
                             DBG_CTL_BREAK       | \
                             DBG_CTL_INT_BREAK   | \
                             DBG_CTL_COMP1_TASK)
#define DBG_CTL_DISABLE     (DBG_CTL_AUTO_ARM    | \
                             DBG_CTL_INT_BREAK   | \
                             DBG_CTL_COMP1_TASK)
#define DBG_KILL_ALL_STAT   (0xFFFFFFFF)

/*
 * Offset to context save area where progress info is stored
 */
#define CSAVE_OFFSET        10

/*
 * Defines for Byte Swapping
 */
#define MCD_BYTE_SWAP_KILLER    0xFFF8888F
#define MCD_NO_BYTE_SWAP_ATALL  0x00040000

/*
 * Execution Unit Identifiers
 */
#define MAC  0  /* legacy - not used */
#define LUAC 1  /* legacy - not used */
#define CRC  2  /* legacy - not used */
#define LURC 3  /* Logic Unit with CRC */

/*
 * Task Identifiers
 */
#define TASK_CHAINNOEU  0
#define TASK_SINGLENOEU 1
#ifdef MCD_INCLUDE_EU
#define TASK_CHAINEU    2
#define TASK_SINGLEEU   3
#define TASK_FECRX      4
#define TASK_FECTX      5
#else
#define TASK_CHAINEU    0
#define TASK_SINGLEEU   1
#define TASK_FECRX      2
#define TASK_FECTX      3
#endif

/*
 * Structure to remember which variant is on which channel
 * TBD- need this?
 */
typedef struct MCD_remVariants_struct MCD_remVariant;
struct MCD_remVariants_struct
{
   int remDestRsdIncr[NCHANNELS];  /* -1,0,1 */
   int remSrcRsdIncr[NCHANNELS];   /* -1,0,1 */
   s16 remDestIncr[NCHANNELS];     /* DestIncr */
   s16 remSrcIncr[NCHANNELS];      /* srcIncr */
   u32 remXferSize[NCHANNELS];     /* xferSize */
};

/*
 * Structure to remember the startDma parameters for each channel
 */
MCD_remVariant MCD_remVariants;

/*
 * Function: MCD_initDma
 * Purpose:  Initializes the DMA API by setting up a pointer to the DMA
 *           registers, relocating and creating the appropriate task
 *           structures, and setting up some global settings
 * Arguments:
 *  dmaBarAddr    - pointer to the multichannel DMA registers
 *  taskTableDest - location to move DMA task code and structs to
 *  flags         - operational parameters
 * Return Value:
 *  MCD_TABLE_UNALIGNED if taskTableDest is not 512-byte aligned
 *  MCD_OK otherwise
 */
extern u32 MCD_funcDescTab0[];

int MCD_initDma (dmaRegs *dmaBarAddr, void *taskTableDest, u32 flags)
{
    int i;
    TaskTableEntry *entryPtr;

    /* setup the local pointer to register set */
    MCD_dmaBar = dmaBarAddr;

    /* do we need to move/create a task table */
    if ((flags & MCD_RELOC_TASKS) != 0)
    {
        int fixedSize;
        u32 *fixedPtr;
        /*int *tablePtr = taskTableDest;TBD*/
        int varTabsOffset, funcDescTabsOffset, contextSavesOffset;
        int taskDescTabsOffset;
        int taskTableSize, varTabsSize, funcDescTabsSize, contextSavesSize;
        int taskDescTabSize;

        int i;

        /* check if physical address is aligned on 512 byte boundary */
        if (((u32)taskTableDest & 0x000001ff) != 0)
            return(MCD_TABLE_UNALIGNED);

        MCD_taskTable = taskTableDest; /* set up local pointer to task Table */

        /*
         * Create a task table:
         * - compute aligned base offsets for variable tables and
         *   function descriptor tables, then
         * - loop through the task table and setup the pointers
         * - copy over model task table with the the actual task descriptor
         *   tables
         */

        taskTableSize = NCHANNELS * sizeof(TaskTableEntry);
        /* align variable tables to size */
        varTabsOffset = taskTableSize + (u32)taskTableDest;
        if ((varTabsOffset & (VAR_TAB_SIZE - 1)) != 0)
            varTabsOffset = (varTabsOffset + VAR_TAB_SIZE) & (~VAR_TAB_SIZE);
        /* align function descriptor tables */
        varTabsSize = NCHANNELS * VAR_TAB_SIZE;
        funcDescTabsOffset = varTabsOffset + varTabsSize;

        if ((funcDescTabsOffset & (FUNCDESC_TAB_SIZE - 1)) != 0)
            funcDescTabsOffset = (funcDescTabsOffset + FUNCDESC_TAB_SIZE) &
            (~FUNCDESC_TAB_SIZE);

        funcDescTabsSize = FUNCDESC_TAB_NUM * FUNCDESC_TAB_SIZE;
        contextSavesOffset = funcDescTabsOffset + funcDescTabsSize;
        contextSavesSize = (NCHANNELS * CONTEXT_SAVE_SIZE);
        fixedSize = taskTableSize + varTabsSize + funcDescTabsSize +
                    contextSavesSize;

        /* zero the thing out */
        fixedPtr = (u32 *)taskTableDest;
        for (i = 0;i<(fixedSize/4);i++)
            fixedPtr[i] = 0;

        entryPtr = (TaskTableEntry*)MCD_taskTable;
        /* set up fixed pointers */
        for (i = 0; i < NCHANNELS; i++)
        {
            entryPtr[i].varTab = (u32)varTabsOffset; /* update ptr to local value */
            entryPtr[i].FDTandFlags = (u32)funcDescTabsOffset | MCD_TT_FLAGS_DEF;
            entryPtr[i].contextSaveSpace = (u32)contextSavesOffset;
            varTabsOffset += VAR_TAB_SIZE;
#ifdef MCD_INCLUDE_EU /* if not there is only one, just point to the same one */
            funcDescTabsOffset += FUNCDESC_TAB_SIZE;
#endif
            contextSavesOffset += CONTEXT_SAVE_SIZE;
        }
        /* copy over the function descriptor table */
        for ( i = 0; i < FUNCDESC_TAB_NUM; i++)
        {
            MCD_memcpy((void*)(entryPtr[i].FDTandFlags & ~MCD_TT_FLAGS_MASK),
                       (void*)MCD_funcDescTab0, FUNCDESC_TAB_SIZE);
        }

        /* copy model task table to where the context saves stuff leaves off*/
        MCD_modelTaskTable = (TaskTableEntry*)contextSavesOffset;

        MCD_memcpy ((void*)MCD_modelTaskTable, (void*)MCD_modelTaskTableSrc,
                    NUMOFVARIANTS * sizeof(TaskTableEntry));

        entryPtr = MCD_modelTaskTable; /* point to local version of
                                                            model task table */
        taskDescTabsOffset = (u32)MCD_modelTaskTable +
                            (NUMOFVARIANTS * sizeof(TaskTableEntry));

        /* copy actual task code and update TDT ptrs in local model task table */
        for (i = 0; i < NUMOFVARIANTS; i++)
        {
            taskDescTabSize = entryPtr[i].TDTend - entryPtr[i].TDTstart + 4;
            MCD_memcpy ((void*)taskDescTabsOffset, (void*)entryPtr[i].TDTstart, taskDescTabSize);
            entryPtr[i].TDTstart = (u32)taskDescTabsOffset;
            taskDescTabsOffset += taskDescTabSize;
            entryPtr[i].TDTend = (u32)taskDescTabsOffset - 4;
        }
#ifdef MCD_INCLUDE_EU /* Tack single DMA BDs onto end of code so API controls
                         where they are since DMA might write to them */
        MCD_relocBuffDesc = (MCD_bufDesc*)(entryPtr[NUMOFVARIANTS - 1].TDTend + 4);
#else /* DMA does not touch them so they can be wherever and we don't need to
         waste SRAM on them */
        MCD_relocBuffDesc = MCD_singleBufDescs;
#endif
    }
    else
    {
        /* point the would-be relocated task tables and the
        buffer descriptors to the ones the linker generated */

        if (((u32)MCD_realTaskTableSrc & 0x000001ff) != 0)
            return(MCD_TABLE_UNALIGNED);

        /* need to add code to make sure that every thing else is aligned properly TBD*/
        /* this is problematic if we init more than once or after running tasks,
            need to add variable to see if we have aleady init'd */
        entryPtr = MCD_realTaskTableSrc;
        for (i = 0; i < NCHANNELS; i++)
        {
            if (((entryPtr[i].varTab & (VAR_TAB_SIZE - 1)) != 0) ||
                ((entryPtr[i].FDTandFlags & (FUNCDESC_TAB_SIZE - 1)) != 0))
                return(MCD_TABLE_UNALIGNED);
        }

        MCD_taskTable = MCD_realTaskTableSrc;
        MCD_modelTaskTable = MCD_modelTaskTableSrc;
        MCD_relocBuffDesc = MCD_singleBufDescs;
    }


    /* Make all channels as totally inactive, and remember them as such: */

    MCD_dmaBar->taskbar = (u32) MCD_taskTable;
    for (i = 0;  i < NCHANNELS;  i++)
    {
        MCD_dmaBar->taskControl[i] = 0x0;
        MCD_chStatus[i] = MCD_NO_DMA;
    }

   /* Set up pausing mechanism to inactive state: */
    MCD_dmaBar->debugComp1 = 0;  /* no particular values yet for either comparator registers */
    MCD_dmaBar->debugComp2 = 0;
    MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
    MCD_dmaBar->debugStatus = DBG_KILL_ALL_STAT;

    /* enable or disable commbus prefetch, really need an ifdef or
       something to keep from trying to set this in the 8220 */
    if ((flags & MCD_COMM_PREFETCH_EN) != 0)
        MCD_dmaBar->ptdControl &= ~PTD_CTL_COMM_PREFETCH;
    else
        MCD_dmaBar->ptdControl |= PTD_CTL_COMM_PREFETCH;

    return(MCD_OK);
}

/* Function:   MCD_dmaStatus
 * Purpose:    Returns the status of the DMA on the requested channel
 * Arguments:  channel - channel number
 * Returns:    Predefined status indicators
 */
int MCD_dmaStatus (int channel)
{
    u16 tcrValue;

    if((channel < 0) || (channel >= NCHANNELS))
        return(MCD_CHANNEL_INVALID);

    tcrValue = MCD_dmaBar->taskControl[channel];
    if ((tcrValue & TASK_CTL_EN) == 0)
    {  /* nothing running */
        /* if last reported with task enabled */
        if (   MCD_chStatus[channel] == MCD_RUNNING
            || MCD_chStatus[channel] == MCD_IDLE)
            MCD_chStatus[channel] = MCD_DONE;
    }
    else /* something is running */
    {
        /* There are three possibilities: paused, running or idle. */
        if (   MCD_chStatus[channel] == MCD_RUNNING
            || MCD_chStatus[channel] == MCD_IDLE)
        {
            MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT;
            /* This register is selected to know which initiator is
            actually asserted. */
            if ((MCD_dmaBar->ptdDebug >> channel ) & 0x1 )
                MCD_chStatus[channel] = MCD_RUNNING;
            else
                MCD_chStatus[channel] = MCD_IDLE;
        /* do not change the status if it is already paused. */
        }
    }
    return MCD_chStatus[channel];
}

/* Function:    MCD_startDma
 * Ppurpose:    Starts a particular kind of DMA
 * Arguments:   see below
 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 */

int MCD_startDma (
    int  channel,   /* the channel on which to run the DMA */
    s8   *srcAddr,  /* the address to move data from, or physical buffer-descriptor address */
    s16  srcIncr,   /* the amount to increment the source address per transfer */
    s8   *destAddr, /* the address to move data to */
    s16  destIncr,  /* the amount to increment the destination address per transfer */
    u32  dmaSize,   /* the number of bytes to transfer independent of the transfer size */
    u32  xferSize,  /* the number bytes in of each data movement (1, 2, or 4) */
    u32  initiator, /* what device initiates the DMA */
    int  priority,  /* priority of the DMA */
    u32  flags,     /* flags describing the DMA */
    u32  funcDesc   /* a description of byte swapping, bit swapping, and CRC actions */
#ifdef MCD_NEED_ADDR_TRANS
    s8   *srcAddrVirt /* virtual buffer descriptor address TBD*/
#endif
)
{
    int srcRsdIncr, destRsdIncr;
    int *cSave;
    short xferSizeIncr;
    int tcrCount = 0;
#ifdef MCD_INCLUDE_EU
    u32 *realFuncArray;
#endif

    if((channel < 0) || (channel >= NCHANNELS))
        return(MCD_CHANNEL_INVALID);

    /* tbd - need to determine the proper response to a bad funcDesc when not
       including EU functions, for now, assign a benign funcDesc, but maybe
       should return an error */
#ifndef MCD_INCLUDE_EU
    funcDesc = MCD_FUNC_NOEU1;
#endif

#ifdef MCD_DEBUG
printf("startDma:Setting up params\n");
#endif
   /* Set us up for task-wise priority.  We don't technically need to do this on every start, but
      since the register involved is in the same longword as other registers that users are in control
      of, setting it more than once is probably preferable.  That since the documentation doesn't seem
      to be completely consistent about the nature of the PTD control register. */
    MCD_dmaBar->ptdControl |= (u16) 0x8000;
#if 1 /* Not sure what we need to keep here rtm TBD */
    /* Calculate additional parameters to the regular DMA calls. */
    srcRsdIncr = srcIncr < 0 ? -1 : (srcIncr > 0 ? 1 : 0);
    destRsdIncr = destIncr < 0 ? -1 : (destIncr > 0 ? 1 : 0);

    xferSizeIncr = (xferSize & 0xffff) | 0x20000000;

    /* Remember for each channel which variant is running. */
    MCD_remVariants.remSrcRsdIncr[channel] = srcRsdIncr;
    MCD_remVariants.remDestRsdIncr[channel] = destRsdIncr;
    MCD_remVariants.remDestIncr[channel] = destIncr;
    MCD_remVariants.remSrcIncr[channel] = srcIncr;
    MCD_remVariants.remXferSize[channel] = xferSize;
#endif

    cSave = (int*)(MCD_taskTable[channel].contextSaveSpace) + CSAVE_OFFSET + CURRBD;

#ifdef MCD_INCLUDE_EU /* may move this to EU specific calls */
    realFuncArray = (u32 *) (MCD_taskTable[channel].FDTandFlags & 0xffffff00);
    /* Modify the LURC's normal and byte-residue-loop functions according to parameter. */
    realFuncArray[(LURC*16)] = xferSize == 4 ?
                                 funcDesc : xferSize == 2 ?
                                     funcDesc & 0xfffff00f : funcDesc & 0xffff000f;
    realFuncArray[(LURC*16+1)] = (funcDesc & MCD_BYTE_SWAP_KILLER) | MCD_NO_BYTE_SWAP_ATALL;
#endif
   /* Write the initiator field in the TCR, and also set the initiator-hold
      bit.  Note that,due to a hardware quirk, this could collide with an
      MDE access to the initiator-register file, so we have to verify that the write
      reads back correctly. */

    MCD_dmaBar->taskControl[channel] =
        (initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM;

    while(((MCD_dmaBar->taskControl[channel] & 0x1fff) !=
          ((initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM)) &&
            (tcrCount < 1000))
    {
        tcrCount++;
        /*MCD_dmaBar->ptd_tcr[channel] = (initiator << 8) | 0x0020;*/
        MCD_dmaBar->taskControl[channel] =
            (initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM;
    }

    MCD_dmaBar->priority[channel] = (u8)priority & PRIORITY_PRI_MASK;
    /* should be albe to handle this stuff with only one write to ts reg - tbd */
    if (channel < 8 && channel >= 0)
    {
        MCD_dmaBar->taskSize0 &= ~(0xf << (7-channel)*4);
        MCD_dmaBar->taskSize0 |= (xferSize & 3) << (((7 - channel)*4) + 2);
        MCD_dmaBar->taskSize0 |= (xferSize & 3) << ((7 - channel)*4);
    }
    else
    {
        MCD_dmaBar->taskSize1 &= ~(0xf << (15-channel)*4);
        MCD_dmaBar->taskSize1 |= (xferSize & 3) << (((15 - channel)*4) + 2);
        MCD_dmaBar->taskSize1 |= (xferSize & 3) << ((15 - channel)*4);
    }

    /* setup task table flags/options which mostly control the line buffers */
    MCD_taskTable[channel].FDTandFlags &= ~MCD_TT_FLAGS_MASK;
    MCD_taskTable[channel].FDTandFlags |= (MCD_TT_FLAGS_MASK & flags);

    if (flags & MCD_FECTX_DMA)
    {
        /* TDTStart and TDTEnd */
        MCD_taskTable[channel].TDTstart = MCD_modelTaskTable[TASK_FECTX].TDTstart;
        MCD_taskTable[channel].TDTend = MCD_modelTaskTable[TASK_FECTX].TDTend;
        MCD_startDmaENetXmit(srcAddr, srcAddr, destAddr, MCD_taskTable, channel);
    }
    else if (flags & MCD_FECRX_DMA)
    {
        /* TDTStart and TDTEnd */
        MCD_taskTable[channel].TDTstart = MCD_modelTaskTable[TASK_FECRX].TDTstart;
        MCD_taskTable[channel].TDTend = MCD_modelTaskTable[TASK_FECRX].TDTend;
        MCD_startDmaENetRcv(srcAddr, srcAddr, destAddr, MCD_taskTable, channel);
    }
    else if(flags & MCD_SINGLE_DMA)
    {
        /* this buffer descriptor is used for storing off initial parameters for later
           progress query calculation and for the DMA to write the resulting checksum
           The DMA does not use this to determine how to operate, that info is passed
           with the init routine*/
        MCD_relocBuffDesc[channel].srcAddr = srcAddr;
        MCD_relocBuffDesc[channel].destAddr = destAddr;
        MCD_relocBuffDesc[channel].lastDestAddr = destAddr;  /* definitely not its final value */
        MCD_relocBuffDesc[channel].dmaSize = dmaSize;
        MCD_relocBuffDesc[channel].flags = 0;       /* not used */
        MCD_relocBuffDesc[channel].csumResult = 0;  /* not used */
        MCD_relocBuffDesc[channel].next = 0;        /* not used */

        /* Initialize the progress-querying stuff to show no progress:*/
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[SRCPTR + CSAVE_OFFSET] = (int)srcAddr;
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DESTPTR + CSAVE_OFFSET] = (int)destAddr;
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[CURRBD + CSAVE_OFFSET] =
                                             (u32) &(MCD_relocBuffDesc[channel]);
        /* tbd - need to keep the user from trying to call the EU routine
           when MCD_INCLUDE_EU is not defined */
        if( funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2)
        {
           /* TDTStart and TDTEnd */
           MCD_taskTable[channel].TDTstart = MCD_modelTaskTable[TASK_SINGLENOEU].TDTstart;
           MCD_taskTable[channel].TDTend = MCD_modelTaskTable[TASK_SINGLENOEU].TDTend;
           MCD_startDmaSingleNoEu(srcAddr, srcIncr, destAddr, destIncr, dmaSize,
                                xferSizeIncr, flags, (int *)&(MCD_relocBuffDesc[channel]), cSave,
                                MCD_taskTable, channel);
        }
        else
        {
           /* TDTStart and TDTEnd */
           MCD_taskTable[channel].TDTstart = MCD_modelTaskTable[TASK_SINGLEEU].TDTstart;
           MCD_taskTable[channel].TDTend = MCD_modelTaskTable[TASK_SINGLEEU].TDTend;
           MCD_startDmaSingleEu(srcAddr, srcIncr, destAddr, destIncr, dmaSize,
                              xferSizeIncr, flags, (int *)&(MCD_relocBuffDesc[channel]), cSave,
                              MCD_taskTable, channel);
        }
    }
    else
    { /* chained DMAS */
        /* Initialize the progress-querying stuff to show no progress:*/
#if 1 /* (!defined(MCD_NEED_ADDR_TRANS)) */
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
          = (int)((MCD_bufDesc*) srcAddr)->srcAddr;
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
          = (int)((MCD_bufDesc*) srcAddr)->destAddr;
#else /* if using address translation, need the virtual addr of the first buffdesc */
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
          = (int)((MCD_bufDesc*) srcAddrVirt)->srcAddr;
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
          = (int)((MCD_bufDesc*) srcAddrVirt)->destAddr;
#endif
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
        ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[CURRBD + CSAVE_OFFSET] = (u32) srcAddr;

        if( funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2)
        {
          /*TDTStart and TDTEnd*/
          MCD_taskTable[channel].TDTstart = MCD_modelTaskTable[TASK_CHAINNOEU].TDTstart;
          MCD_taskTable[channel].TDTend = MCD_modelTaskTable[TASK_CHAINNOEU].TDTend;
          MCD_startDmaChainNoEu((int *)srcAddr, srcIncr, destIncr, xferSize,
                                    xferSizeIncr, cSave, MCD_taskTable, channel);
        }
        else
        {
          /*TDTStart and TDTEnd*/
          MCD_taskTable[channel].TDTstart = MCD_modelTaskTable[TASK_CHAINEU].TDTstart;
          MCD_taskTable[channel].TDTend = MCD_modelTaskTable[TASK_CHAINEU].TDTend;
          MCD_startDmaChainEu((int *)srcAddr, srcIncr, destIncr, xferSize,
                                 xferSizeIncr, cSave, MCD_taskTable, channel);
        }
    }
    MCD_chStatus[channel] = MCD_IDLE;
    return(MCD_OK);
}

/* Function:    MCD_XferProgrQuery
 * Purpose:     Returns progress of DMA on requested channel
 * Arguments:   channel - channel to retrieve progress for
 *              progRep - pointer to user supplied MCD_XferProg struct
 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 *
 * Notes:
 *  MCD_XferProgrQuery() upon completing or after aborting a DMA, or
 *  while the DMA is in progress, this function returns the first
 *  DMA-destination address not (or not yet) used in the DMA. When
 *  encountering a non-ready buffer descriptor, the information for
 *  the last completed descriptor is returned.
 *
 *  MCD_XferProgQuery() has to avoid the possibility of getting
 *  partially-updated information in the event that we should happen
 *  to query DMA progress just as the DMA is updating it. It does that
 *  by taking advantage of the fact context is not saved frequently for
 *  the most part. We therefore read it at least twice until we get the
 *  same information twice in a row.
 *
 *  Because a small, but not insignificant, amount of time is required
 *  to write out the progress-query information, especially upon
 *  completion of the DMA, it would be wise to guarantee some time lag
 *  between successive readings of the progress-query information.
 */

/*
 * How many iterations of the loop below to execute to stabilize values
 */
#define STABTIME 0

int MCD_XferProgrQuery (int channel, MCD_XferProg *progRep)
{
    MCD_XferProg prevRep;
    int again;  /* true if we are to try again to get consistent results */
    int i;  /* used as a time-waste counter */
    int destDiffBytes; /* Total number of bytes that we think actually got xfered. */
    int numIterations; /* number of iterations */
    int bytesNotXfered; /* bytes that did not get xfered. */
    s8 *LWAlignedInitDestAddr, *LWAlignedCurrDestAddr;
    int subModVal, addModVal; /* Mode values to added and subtracted from the
                                final destAddr */

    if((channel < 0) || (channel >= NCHANNELS))
        return(MCD_CHANNEL_INVALID);

    /* Read a trial value for the progress-reporting values*/
    prevRep.lastSrcAddr =
          (s8 *) ((volatile int*) MCD_taskTable[channel].contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
    prevRep.lastDestAddr =
          (s8 *) ((volatile int*) MCD_taskTable[channel].contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
    prevRep.dmaSize = ((volatile int*) MCD_taskTable[channel].contextSaveSpace)[DCOUNT + CSAVE_OFFSET];
    prevRep.currBufDesc =
          (MCD_bufDesc*) ((volatile int*) MCD_taskTable[channel].contextSaveSpace)[CURRBD + CSAVE_OFFSET];
    /* Repeatedly reread those values until they match previous values: */
    do {
        /* Waste a little bit of time to ensure stability: */
        for (i = 0;  i < STABTIME;  i++)
            i += i >> 2;  /* make sure this loop does something so that it doesn't get optimized out */
        /* Check them again: */
        progRep->lastSrcAddr =
            (s8 *) ((volatile int*) MCD_taskTable[channel].contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
        progRep->lastDestAddr =
            (s8 *) ((volatile int*) MCD_taskTable[channel].contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
        progRep->dmaSize = ((volatile int*) MCD_taskTable[channel].contextSaveSpace)[DCOUNT + CSAVE_OFFSET];
        progRep->currBufDesc =
            (MCD_bufDesc*) ((volatile int*) MCD_taskTable[channel].contextSaveSpace)[CURRBD + CSAVE_OFFSET];
       /* See if they match: */
       if (   prevRep.lastSrcAddr != progRep->lastSrcAddr
           || prevRep.lastDestAddr != progRep->lastDestAddr
           || prevRep.dmaSize != progRep->dmaSize
           || prevRep.currBufDesc != progRep->currBufDesc)
       {
          /* If they don't match, remember previous values and try again:*/
          prevRep.lastSrcAddr = progRep->lastSrcAddr;
          prevRep.lastDestAddr = progRep->lastDestAddr;
          prevRep.dmaSize = progRep->dmaSize;
          prevRep.currBufDesc = progRep->currBufDesc;
          again = MCD_TRUE;
       }
       else
          again = MCD_FALSE;
    } while (again == MCD_TRUE);


    /* Update the dCount, srcAddr and destAddr */
    /* To calculate dmaCount, we consider destination address. C
       overs M1,P1,Z for destination */
    switch(MCD_remVariants.remDestRsdIncr[channel]) {
        case MINUS1:
           subModVal = ((int)progRep->lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
           addModVal = ((int)progRep->currBufDesc->destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
           LWAlignedInitDestAddr = (progRep->currBufDesc->destAddr) - addModVal;
           LWAlignedCurrDestAddr = (progRep->lastDestAddr) - subModVal;
           destDiffBytes = LWAlignedInitDestAddr - LWAlignedCurrDestAddr;
           bytesNotXfered = (destDiffBytes/MCD_remVariants.remDestIncr[channel]) *
                            ( MCD_remVariants.remDestIncr[channel]
                            + MCD_remVariants.remXferSize[channel]);
           progRep->dmaSize = destDiffBytes - bytesNotXfered + addModVal - subModVal;
           break;
        case ZERO:
           progRep->lastDestAddr = progRep->currBufDesc->destAddr;
           break;
        case PLUS1:
           /* This value has to be subtracted from the final calculated dCount. */
           subModVal = ((int)progRep->currBufDesc->destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
           /* These bytes are already in lastDestAddr. */
            addModVal = ((int)progRep->lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
            LWAlignedInitDestAddr = (progRep->currBufDesc->destAddr) - subModVal;
            LWAlignedCurrDestAddr = (progRep->lastDestAddr) - addModVal;
            destDiffBytes = (progRep->lastDestAddr - LWAlignedInitDestAddr);
            numIterations = ( LWAlignedCurrDestAddr - LWAlignedInitDestAddr)/MCD_remVariants.remDestIncr[channel];
            bytesNotXfered =  numIterations *
                ( MCD_remVariants.remDestIncr[channel]
                  - MCD_remVariants.remXferSize[channel]);
           progRep->dmaSize = destDiffBytes - bytesNotXfered - subModVal;
           break;
        default:
           break;
    }

    /* This covers M1,P1,Z for source */
    switch(MCD_remVariants.remSrcRsdIncr[channel]) {
        case MINUS1:
            progRep->lastSrcAddr =
                progRep->currBufDesc->srcAddr +
                 ( MCD_remVariants.remSrcIncr[channel] *
                   (progRep->dmaSize/MCD_remVariants.remXferSize[channel]));
            break;
        case ZERO:
            progRep->lastSrcAddr = progRep->currBufDesc->srcAddr;
            break;
       case PLUS1:
            progRep->lastSrcAddr =
                progRep->currBufDesc->srcAddr +
                 ( MCD_remVariants.remSrcIncr[channel] *
                   (progRep->dmaSize/MCD_remVariants.remXferSize[channel]));
          break;
       default: break;
    }

    return(MCD_OK);
}

/* MCD_resmActions() does the majority of the actions of a DMA resume.
 * It is called from MCD_killDma() and MCD_resumeDma().  It has to be
 * a separate function because the kill function has to negate the task
 * enable before resuming it, but the resume function has to do nothing
 * if there is no DMA on that channel (i.e., if the enable bit is 0).
 */
static void MCD_resmActions (int channel)
{
   MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
   MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus;
   MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT; /* This register is selected to know
                                        which initiator is actually asserted. */
   if((MCD_dmaBar->ptdDebug >> channel ) & 0x1)
       MCD_chStatus[channel] = MCD_RUNNING;
   else
       MCD_chStatus[channel] = MCD_IDLE;
}

/* Function:    MCD_killDma
 * Purpose:     Halt the DMA on the requested channel, without any
 *              intention of resuming the DMA.
 * Arguments:   channel - requested channel
 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 *
 * Notes:
 *  A DMA may be killed from any state, including paused state, and it
 *  always goes to the MCD_HALTED state even if it is killed while in
 *  the MCD_NO_DMA or MCD_IDLE states.
 */
int MCD_killDma (int channel)
{
   /* MCD_XferProg progRep; */

    if((channel < 0) || (channel >= NCHANNELS))
        return(MCD_CHANNEL_INVALID);

    MCD_dmaBar->taskControl[channel] = 0x0;
    MCD_resumeDma (channel);
    /*
     * This must be after the write to the TCR so that the task doesn't
     * start up again momentarily, and before the status assignment so
     * as to override whatever MCD_resumeDma() may do to the channel
     * status.
     */
    MCD_chStatus[channel] = MCD_HALTED;

    /*
     * Update the current buffer descriptor's lastDestAddr field
     *
     * MCD_XferProgrQuery (channel, &progRep);
     * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
     */
    return(MCD_OK);
}

/* Function:    MCD_continDma
 * Purpose:     Continue a DMA which as stopped due to encountering an
 *              unready buffer descriptor.
 * Arguments:   channel - channel to continue the DMA on
 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 *
 * Notes:
 *  This routine does not check to see if there is a task which can
 *  be continued. Also this routine should not be used with single DMAs.
 */
int MCD_continDma (int channel)
{
    if((channel < 0) || (channel >= NCHANNELS))
        return(MCD_CHANNEL_INVALID);

    MCD_dmaBar->taskControl[channel] |= TASK_CTL_EN;
    MCD_chStatus[channel] = MCD_RUNNING;

    return(MCD_OK);
}

/*
 * MCD_pauseDma() and MCD_resumeDma() below use the DMA's debug unit
 * to freeze a task and resume it.  We freeze a task by breakpointing
 * on the stated task.  That is, not any specific place in the task,
 * but any time that task executes.  In particular, when that task
 * executes, we want to freeze that task and only that task.
 *
 * The bits of the debug control register influence interrupts vs.
 * breakpoints as follows:
 * - Bits 14 and 0 enable or disable debug functions.  If enabled, you
 *   will get the interrupt but you may or may not get a breakpoint.
 * - Bits 2 and 1 decide whether you also get a breakpoint in addition
 *   to an interrupt.
 *
 * The debug unit can do these actions in response to either internally
 * detected breakpoint conditions from the comparators, or in response
 * to the external breakpoint pin, or both.
 * - Bits 14 and 1 perform the above-described functions for
 *   internally-generated conditions, i.e., the debug comparators.
 * - Bits 0 and 2 perform the above-described functions for external
 *   conditions, i.e., the breakpoint external pin.
 *
 * Note that, although you "always" get the interrupt when you turn
 * the debug functions, the interrupt can nevertheless, if desired, be
 * masked by the corresponding bit in the PTD's IMR. Note also that
 * this means that bits 14 and 0 must enable debug functions before
 * bits 1 and 2, respectively, have any effect.
 *
 * NOTE: It's extremely important to not pause more than one DMA channel
 *  at a time.
 ********************************************************************/

/* Function:    MCD_pauseDma
 * Purpose:     Pauses the DMA on a given channel (if any DMA is running
 *              on that channel).
 * Arguments:   channel
 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 */
int MCD_pauseDma (int channel)
{
    /* MCD_XferProg progRep; */

    if((channel < 0) || (channel >= NCHANNELS))
        return(MCD_CHANNEL_INVALID);

    if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN)
    {
        MCD_dmaBar->debugComp1 = channel;
        MCD_dmaBar->debugControl = DBG_CTL_ENABLE | (1 << (channel + 16));
        MCD_chStatus[channel] = MCD_PAUSED;

        /*
         * Update the current buffer descriptor's lastDestAddr field
         *
         * MCD_XferProgrQuery (channel, &progRep);
         * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
         */
    }
    return(MCD_OK);
}

/* Function:    MCD_resumeDma
 * Purpose:     Resumes the DMA on a given channel (if any DMA is
 *              running on that channel).
 * Arguments:   channel - channel on which to resume DMA
 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 */
int MCD_resumeDma (int channel)
{
    if((channel < 0) || (channel >= NCHANNELS))
        return(MCD_CHANNEL_INVALID);

    if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN)
        MCD_resmActions (channel);

    return(MCD_OK);
}

/* Function:    MCD_csumQuery
 * Purpose:     Provide the checksum after performing a non-chained DMA
 * Arguments:   channel - channel to report on
 *              csum - pointer to where to write the checksum/CRC
 * Returns:     MCD_ERROR if the channel is invalid, else MCD_OK
 *
 * Notes:
 *
 */
int MCD_csumQuery (int channel, u32 *csum)
{
#ifdef MCD_INCLUDE_EU
    if((channel < 0) || (channel >= NCHANNELS))
        return(MCD_CHANNEL_INVALID);

    *csum = MCD_relocBuffDesc[channel].csumResult;
    return(MCD_OK);
#else
    return(MCD_ERROR);
#endif
}

/* Function:    MCD_getCodeSize
 * Purpose:     Provide the size requirements of the microcoded tasks
 * Returns:     Size in bytes
 */
int MCD_getCodeSize(void)
{
#ifdef MCD_INCLUDE_EU
    return(0x2b5c);
#else
    return(0x173c);
#endif
}

/* Function:    MCD_getVersion
 * Purpose:     Provide the version string and number
 * Arguments:   longVersion - user supplied pointer to a pointer to a char
 *                    which points to the version string
 * Returns:     Version number and version string (by reference)
 */
char MCD_versionString[] = "Multi-channel DMA API Alpha v0.3 (2004-04-26)";
#define MCD_REV_MAJOR   0x00
#define MCD_REV_MINOR   0x03

int MCD_getVersion(char **longVersion)
{
    *longVersion = MCD_versionString;
    return((MCD_REV_MAJOR << 8) | MCD_REV_MINOR);
}

/* Private version of memcpy()
 * Note that everything this is used for is longword-aligned.
 */
static void MCD_memcpy (int *dest, int *src, u32 size)
{
    u32 i;

    for (i = 0;  i < size;  i += sizeof(int), dest++, src++)
        *dest = *src;
}