MCD_dmaApi.c 34.6 KB
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
/*
 * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

/*Main C file for multi-channel DMA API. */

#include <common.h>

#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)
#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);

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

		/*
		 * 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++) {
			/* update ptr to local value */
			entryPtr[i].varTab = (u32) varTabsOffset;
			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));

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

		/* 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: */
	/* no particular values yet for either comparator registers */
	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, 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);
}

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

/******************** End of MCD_dmaStatus() ************************/

/********************************************************************/
/* Function:    MCD_startDma
 * Ppurpose:    Starts a particular kind of DMA
 * Arguments:
 * srcAddr	- the channel on which to run the DMA
 * srcIncr	- the address to move data from, or buffer-descriptor address
 * destAddr	- the amount to increment the source address per transfer
 * destIncr	- the address to move data to
 * dmaSize	- the amount to increment the destination address per transfer
 * xferSize	- the number bytes in of each data movement (1, 2, or 4)
 * initiator	- what device initiates the DMA
 * priority	- priority of the DMA
 * flags	- flags describing the DMA
 * funcDesc	- description of byte swapping, bit swapping, and CRC actions
 * srcAddrVirt	- virtual buffer descriptor address TBD
 * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
 */

int MCD_startDma(int channel, s8 * srcAddr, s16 srcIncr, s8 * destAddr,
		 s16 destIncr, u32 dmaSize, u32 xferSize, u32 initiator,
		 int priority, u32 flags, u32 funcDesc
#ifdef MCD_NEED_ADDR_TRANS
		 s8 * srcAddrVirt
#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;

	/* Not sure what we need to keep here rtm TBD */
#if 1
	/* 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((char *)srcAddr, (char *)srcAddr,
				     (char *)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((char *)srcAddr, (char *)srcAddr,
				    (char *)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;

		/* definitely not its final value */
		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]);
		/* 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((char *)srcAddr, srcIncr,
					       (char *)destAddr, destIncr,
					       (int)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((char *)srcAddr, srcIncr,
					     (char *)destAddr, destIncr,
					     (int)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);
}

/************************ 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 ge
				   consistent results */
	int i;			/* used as a time-waste counter */
	int destDiffBytes;	/* Total no 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++) {
			/* make sure this loop does something so that it
			   doesn't get optimized out */
			i += i >> 2;
		}
		/* 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);
}

/******************* 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;
	/* This register is selected to know which initiator is
	   actually 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)
{
	/* 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);
}

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

/************************* 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 (0x2b5c);
#else
	return (0x173c);
#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 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);
}

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