aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/coldfire/files-2.6.31/arch/m68k/coldfire/m547x/MCD_dmaApi.c
blob: 9609d0c6dc585e1b649e19577f6b18d933317f30 (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
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
/*
 * drivers/dma/MCD_dmaApi.c
 *
 * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
 * Kurt Mahan <kmahan@freescale.com>
 * Shrek Wu b16972@freescale.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include "MCD_dma.h"
#include "MCD_tasksInit.h"
#include "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
 */
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 varTabsOffset, funcDescTabsOffset;
		int contextSavesOffset;
		int taskDescTabsOffset;
		int taskTableSize, varTabsSize;
		int 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 save stuff leaves off */
		MCD_modelTaskTable =
			(TaskTableEntry *)contextSavesOffset;

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

		/* Point to local version of model task table */
		entryPtr = MCD_modelTaskTable;
		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;

		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 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;
	MCD_dmaBar->debugComp2 = 0;
	MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
	MCD_dmaBar->debugStatus = DBG_KILL_ALL_STAT;

	/* Enable or disable commbus prefetch */
	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;
}
/*********************** End of MCD_initDma() ***********************/

/********************************************************************/
/* 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;
			/* Determine which initiator
			 * is 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];
}
/******************** End of MCD_dmaStatus() ************************/

/********************************************************************/
/* 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;

#ifndef MCD_INCLUDE_EU
	funcDesc = MCD_FUNC_NOEU1;
#endif

#ifdef MCD_DEBUG
	printf("startDma:Setting up params\n");
#endif

	/* Enable task-wise priority */
	MCD_dmaBar->ptdControl |= (u16) 0x8000;

	/* 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 which variant is running for each channel */
	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;

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

#ifdef MCD_INCLUDE_EU
	realFuncArray = (u32 *)(MCD_taskTable[channel].FDTandFlags
			& 0xffffff00);

	/*
	* Modify the LURC's normal and byte-residue-loop functions
	* according to parameter.
	*/
	switch (xferSize) {
	case 4:
		realFuncArray[(LURC*16)] = funcDesc;
		break;
	case 2:
		realFuncArray[(LURC*16)] = funcDesc & 0xfffff00f;
		break;
	case 1:
	default:
		realFuncArray[(LURC*16)] = funcDesc & 0xffff000f;
		break;
	}

	realFuncArray[(LURC*16 + 1)] = 0
		| (funcDesc & MCD_BYTE_SWAP_KILLER)
		| MCD_NO_BYTE_SWAP_ATALL;
#endif

	/* Write the initiator field in the TCR and
	 * set the initiator-hold bit*/
	MCD_dmaBar->taskControl[channel] = 0
		| (initiator << 8)
		| TASK_CTL_HIPRITSKEN
		| TASK_CTL_HLDINITNUM;

	/*
	* Current versions of the MPC8220 MCD have a hardware quirk that could
	* cause the write to the TCR to collide with an MDE access to the
	* initiator-register file, so we have to verify that the write occurred
	* correctly by reading back the value.  On MCF547x/8x devices and any
	* future revisions of the MPC8220, this loop will not be entered.
	*/
	while (((MCD_dmaBar->taskControl[channel] & 0x1fff) !=
		((initiator << 8) | TASK_CTL_HIPRITSKEN
		 | TASK_CTL_HLDINITNUM)) && (tcrCount < 1000))  {
		tcrCount++;
		MCD_dmaBar->taskControl[channel] = 0
			| (initiator << 8)
			| TASK_CTL_HIPRITSKEN
			| TASK_CTL_HLDINITNUM;
	}

	MCD_dmaBar->priority[channel] = (u8)priority & PRIORITY_PRI_MASK;

	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 */
	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;
		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]);

		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 DMA */ {
		/* 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;
}

/************************ End of MCD_startDma() *********************/

/********************************************************************/
/* 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 {
		/* Take 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 dmaSize and lastDestAddr */
	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 dmaSize. */
		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;
}
/******************* End of MCD_XferProgrQuery() ********************/

/********************************************************************/
/* 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;

	/* Determine which initiators are asserted */
	MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT;

	if ((MCD_dmaBar->ptdDebug >> channel) & 0x1)
		MCD_chStatus[channel] = MCD_RUNNING;
	else
		MCD_chStatus[channel] = MCD_IDLE;
}
/********************* End of MCD_resmActions() *********************/

/********************************************************************/
/* 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)
{
	if ((channel < 0) || (channel >= NCHANNELS))
		return MCD_CHANNEL_INVALID;

	MCD_dmaBar->taskControl[channel] = 0x0;

	/* Clean up after a paused task */
	if (MCD_chStatus[channel] == MCD_PAUSED) {
		MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
		MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus;
	}

	MCD_chStatus[channel] = MCD_HALTED;

	return MCD_OK;
}
/************************ End of MCD_killDma() **********************/

/********************************************************************/
/* 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;
}
/********************** End of MCD_continDma() **********************/

/*********************************************************************
 * 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)
{
	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;
	}

	return MCD_OK;
}
/************************* End of MCD_pauseDma() ********************/

/********************************************************************/
/* 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;
}
/************************ End of MCD_resumeDma() ********************/

/********************************************************************/
/* 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
}
/*********************** End of MCD_resumeDma() *********************/

/********************************************************************/
/* 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 0x2b64;
#else
	return 0x1744;
#endif
}
/********************** End of MCD_getCodeSize() ********************/

/********************************************************************/
/* 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 v1.0";
#define MCD_REV_MAJOR   0x01
#define MCD_REV_MINOR   0x00

int MCD_getVersion(char **longVersion)
{
	int ret = 0;
	*longVersion = MCD_versionString;
	ret = (MCD_REV_MAJOR << 8) | MCD_REV_MINOR;
	return ret;
}
/********************** End of MCD_getVersion() *********************/

/********************************************************************/
/* 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;
}
/********************************************************************/