• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/arm/mach-bcmring/csp/dmac/
1/*****************************************************************************
2* Copyright 2003 - 2008 Broadcom Corporation.  All rights reserved.
3*
4* Unless you and Broadcom execute a separate written software license
5* agreement governing use of this software, this software is licensed to you
6* under the terms of the GNU General Public License version 2, available at
7* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
8*
9* Notwithstanding the above, under no circumstances may you combine this
10* software in any way with any other Broadcom software provided under a
11* license other than the GPL, without Broadcom's express prior written
12* consent.
13*****************************************************************************/
14
15/****************************************************************************/
16/**
17*  @file    dmacHw.c
18*
19*  @brief   Low level DMA controller driver routines
20*
21*  @note
22*
23*   These routines provide basic DMA functionality only.
24*/
25/****************************************************************************/
26
27/* ---- Include Files ---------------------------------------------------- */
28#include <csp/stdint.h>
29#include <csp/string.h>
30#include <stddef.h>
31
32#include <csp/dmacHw.h>
33#include <mach/csp/dmacHw_reg.h>
34#include <mach/csp/dmacHw_priv.h>
35#include <mach/csp/chipcHw_inline.h>
36
37/* ---- External Function Prototypes ------------------------------------- */
38
39/* Allocate DMA control blocks */
40dmacHw_CBLK_t dmacHw_gCblk[dmacHw_MAX_CHANNEL_COUNT];
41
42uint32_t dmaChannelCount_0 = dmacHw_MAX_CHANNEL_COUNT / 2;
43uint32_t dmaChannelCount_1 = dmacHw_MAX_CHANNEL_COUNT / 2;
44
45/****************************************************************************/
46/**
47*  @brief   Get maximum FIFO for a DMA channel
48*
49*  @return  Maximum allowable FIFO size
50*
51*
52*/
53/****************************************************************************/
54static uint32_t GetFifoSize(dmacHw_HANDLE_t handle	/*   [ IN ] DMA Channel handle */
55    ) {
56	uint32_t val = 0;
57	dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
58	dmacHw_MISC_t *pMiscReg =
59	    (dmacHw_MISC_t *) dmacHw_REG_MISC_BASE(pCblk->module);
60
61	switch (pCblk->channel) {
62	case 0:
63		val = (pMiscReg->CompParm2.lo & 0x70000000) >> 28;
64		break;
65	case 1:
66		val = (pMiscReg->CompParm3.hi & 0x70000000) >> 28;
67		break;
68	case 2:
69		val = (pMiscReg->CompParm3.lo & 0x70000000) >> 28;
70		break;
71	case 3:
72		val = (pMiscReg->CompParm4.hi & 0x70000000) >> 28;
73		break;
74	case 4:
75		val = (pMiscReg->CompParm4.lo & 0x70000000) >> 28;
76		break;
77	case 5:
78		val = (pMiscReg->CompParm5.hi & 0x70000000) >> 28;
79		break;
80	case 6:
81		val = (pMiscReg->CompParm5.lo & 0x70000000) >> 28;
82		break;
83	case 7:
84		val = (pMiscReg->CompParm6.hi & 0x70000000) >> 28;
85		break;
86	}
87
88	if (val <= 0x4) {
89		return 8 << val;
90	} else {
91		dmacHw_ASSERT(0);
92	}
93	return 0;
94}
95
96/****************************************************************************/
97/**
98*  @brief   Program channel register to initiate transfer
99*
100*  @return  void
101*
102*
103*  @note
104*     - Descriptor buffer MUST ALWAYS be flushed before calling this function
105*     - This function should also be called from ISR to program the channel with
106*       pending descriptors
107*/
108/****************************************************************************/
109void dmacHw_initiateTransfer(dmacHw_HANDLE_t handle,	/*   [ IN ] DMA Channel handle */
110			     dmacHw_CONFIG_t *pConfig,	/*   [ IN ] Configuration settings */
111			     void *pDescriptor	/*   [ IN ] Descriptor buffer */
112    ) {
113	dmacHw_DESC_RING_t *pRing;
114	dmacHw_DESC_t *pProg;
115	dmacHw_CBLK_t *pCblk;
116
117	pCblk = dmacHw_HANDLE_TO_CBLK(handle);
118	pRing = dmacHw_GET_DESC_RING(pDescriptor);
119
120	if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) {
121		/* Not safe yet to program the channel */
122		return;
123	}
124
125	if (pCblk->varDataStarted) {
126		if (pCblk->descUpdated) {
127			pCblk->descUpdated = 0;
128			pProg =
129			    (dmacHw_DESC_t *) ((uint32_t)
130					       dmacHw_REG_LLP(pCblk->module,
131							      pCblk->channel) +
132					       pRing->virt2PhyOffset);
133
134			/* Load descriptor if not loaded */
135			if (!(pProg->ctl.hi & dmacHw_REG_CTL_DONE)) {
136				dmacHw_SET_SAR(pCblk->module, pCblk->channel,
137					       pProg->sar);
138				dmacHw_SET_DAR(pCblk->module, pCblk->channel,
139					       pProg->dar);
140				dmacHw_REG_CTL_LO(pCblk->module,
141						  pCblk->channel) =
142				    pProg->ctl.lo;
143				dmacHw_REG_CTL_HI(pCblk->module,
144						  pCblk->channel) =
145				    pProg->ctl.hi;
146			} else if (pProg == (dmacHw_DESC_t *) pRing->pEnd->llp) {
147				/* Return as end descriptor is processed */
148				return;
149			} else {
150				dmacHw_ASSERT(0);
151			}
152		} else {
153			return;
154		}
155	} else {
156		if (pConfig->transferMode == dmacHw_TRANSFER_MODE_PERIODIC) {
157			/* Do not make a single chain, rather process one descriptor at a time */
158			pProg = pRing->pHead;
159			/* Point to the next descriptor for next iteration */
160			dmacHw_NEXT_DESC(pRing, pHead);
161		} else {
162			/* Return if no more pending descriptor */
163			if (pRing->pEnd == NULL) {
164				return;
165			}
166
167			pProg = pRing->pProg;
168			if (pConfig->transferMode ==
169			    dmacHw_TRANSFER_MODE_CONTINUOUS) {
170				/* Make sure a complete ring can be formed */
171				dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pEnd->
172					      llp == pRing->pProg);
173				/* Make sure pProg pointing to the pHead */
174				dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pProg ==
175					      pRing->pHead);
176				/* Make a complete ring */
177				do {
178					pRing->pProg->ctl.lo |=
179					    (dmacHw_REG_CTL_LLP_DST_EN |
180					     dmacHw_REG_CTL_LLP_SRC_EN);
181					pRing->pProg =
182					    (dmacHw_DESC_t *) pRing->pProg->llp;
183				} while (pRing->pProg != pRing->pHead);
184			} else {
185				/* Make a single long chain */
186				while (pRing->pProg != pRing->pEnd) {
187					pRing->pProg->ctl.lo |=
188					    (dmacHw_REG_CTL_LLP_DST_EN |
189					     dmacHw_REG_CTL_LLP_SRC_EN);
190					pRing->pProg =
191					    (dmacHw_DESC_t *) pRing->pProg->llp;
192				}
193			}
194		}
195
196		/* Program the channel registers */
197		dmacHw_SET_SAR(pCblk->module, pCblk->channel, pProg->sar);
198		dmacHw_SET_DAR(pCblk->module, pCblk->channel, pProg->dar);
199		dmacHw_SET_LLP(pCblk->module, pCblk->channel,
200			       (uint32_t) pProg - pRing->virt2PhyOffset);
201		dmacHw_REG_CTL_LO(pCblk->module, pCblk->channel) =
202		    pProg->ctl.lo;
203		dmacHw_REG_CTL_HI(pCblk->module, pCblk->channel) =
204		    pProg->ctl.hi;
205		if (pRing->pEnd) {
206			/* Remember the descriptor to use next */
207			pRing->pProg = (dmacHw_DESC_t *) pRing->pEnd->llp;
208		}
209		/* Indicate no more pending descriptor  */
210		pRing->pEnd = (dmacHw_DESC_t *) NULL;
211	}
212	/* Start DMA operation */
213	dmacHw_DMA_START(pCblk->module, pCblk->channel);
214}
215
216/****************************************************************************/
217/**
218*  @brief   Initializes DMA
219*
220*  This function initializes DMA CSP driver
221*
222*  @note
223*     Must be called before using any DMA channel
224*/
225/****************************************************************************/
226void dmacHw_initDma(void)
227{
228
229	uint32_t i = 0;
230
231	dmaChannelCount_0 = dmacHw_GET_NUM_CHANNEL(0);
232	dmaChannelCount_1 = dmacHw_GET_NUM_CHANNEL(1);
233
234	/* Enable access to the DMA block */
235	chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC0);
236	chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC1);
237
238	if ((dmaChannelCount_0 + dmaChannelCount_1) > dmacHw_MAX_CHANNEL_COUNT) {
239		dmacHw_ASSERT(0);
240	}
241
242	memset((void *)dmacHw_gCblk, 0,
243	       sizeof(dmacHw_CBLK_t) * (dmaChannelCount_0 + dmaChannelCount_1));
244	for (i = 0; i < dmaChannelCount_0; i++) {
245		dmacHw_gCblk[i].module = 0;
246		dmacHw_gCblk[i].channel = i;
247	}
248	for (i = 0; i < dmaChannelCount_1; i++) {
249		dmacHw_gCblk[i + dmaChannelCount_0].module = 1;
250		dmacHw_gCblk[i + dmaChannelCount_0].channel = i;
251	}
252}
253
254/****************************************************************************/
255/**
256*  @brief   Exit function for  DMA
257*
258*  This function isolates DMA from the system
259*
260*/
261/****************************************************************************/
262void dmacHw_exitDma(void)
263{
264	/* Disable access to the DMA block */
265	chipcHw_busInterfaceClockDisable(chipcHw_REG_BUS_CLOCK_DMAC0);
266	chipcHw_busInterfaceClockDisable(chipcHw_REG_BUS_CLOCK_DMAC1);
267}
268
269/****************************************************************************/
270/**
271*  @brief   Gets a handle to a DMA channel
272*
273*  This function returns a handle, representing a control block of a particular DMA channel
274*
275*  @return  -1       - On Failure
276*            handle  - On Success, representing a channel control block
277*
278*  @note
279*     None  Channel ID must be created using "dmacHw_MAKE_CHANNEL_ID" macro
280*/
281/****************************************************************************/
282dmacHw_HANDLE_t dmacHw_getChannelHandle(dmacHw_ID_t channelId	/* [ IN ] DMA Channel Id */
283    ) {
284	int idx;
285
286	switch ((channelId >> 8)) {
287	case 0:
288		dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_0);
289		idx = (channelId & 0xff);
290		break;
291	case 1:
292		dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_1);
293		idx = dmaChannelCount_0 + (channelId & 0xff);
294		break;
295	default:
296		dmacHw_ASSERT(0);
297		return (dmacHw_HANDLE_t) -1;
298	}
299
300	return dmacHw_CBLK_TO_HANDLE(&dmacHw_gCblk[idx]);
301}
302
303/****************************************************************************/
304/**
305*  @brief   Initializes a DMA channel for use
306*
307*  This function initializes and resets a DMA channel for use
308*
309*  @return  -1     - On Failure
310*            0     - On Success
311*
312*  @note
313*     None
314*/
315/****************************************************************************/
316int dmacHw_initChannel(dmacHw_HANDLE_t handle	/*   [ IN ] DMA Channel handle */
317    ) {
318	dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
319	int module = pCblk->module;
320	int channel = pCblk->channel;
321
322	/* Reinitialize the control block */
323	memset((void *)pCblk, 0, sizeof(dmacHw_CBLK_t));
324	pCblk->module = module;
325	pCblk->channel = channel;
326
327	/* Enable DMA controller */
328	dmacHw_DMA_ENABLE(pCblk->module);
329	/* Reset DMA channel */
330	dmacHw_RESET_CONTROL_LO(pCblk->module, pCblk->channel);
331	dmacHw_RESET_CONTROL_HI(pCblk->module, pCblk->channel);
332	dmacHw_RESET_CONFIG_LO(pCblk->module, pCblk->channel);
333	dmacHw_RESET_CONFIG_HI(pCblk->module, pCblk->channel);
334
335	/* Clear all raw interrupt status */
336	dmacHw_TRAN_INT_CLEAR(pCblk->module, pCblk->channel);
337	dmacHw_BLOCK_INT_CLEAR(pCblk->module, pCblk->channel);
338	dmacHw_ERROR_INT_CLEAR(pCblk->module, pCblk->channel);
339
340	/* Mask event specific interrupts */
341	dmacHw_TRAN_INT_DISABLE(pCblk->module, pCblk->channel);
342	dmacHw_BLOCK_INT_DISABLE(pCblk->module, pCblk->channel);
343	dmacHw_STRAN_INT_DISABLE(pCblk->module, pCblk->channel);
344	dmacHw_DTRAN_INT_DISABLE(pCblk->module, pCblk->channel);
345	dmacHw_ERROR_INT_DISABLE(pCblk->module, pCblk->channel);
346
347	return 0;
348}
349
350/****************************************************************************/
351/**
352*  @brief  Finds amount of memory required to form a descriptor ring
353*
354*
355*  @return   Number of bytes required to form a descriptor ring
356*
357*
358*/
359/****************************************************************************/
360uint32_t dmacHw_descriptorLen(uint32_t descCnt	/* [ IN ] Number of descriptor in the ring */
361    ) {
362	/* Need extra 4 byte to ensure 32 bit alignment  */
363	return (descCnt * sizeof(dmacHw_DESC_t)) + sizeof(dmacHw_DESC_RING_t) +
364		sizeof(uint32_t);
365}
366
367/****************************************************************************/
368/**
369*  @brief   Initializes descriptor ring
370*
371*  This function will initializes the descriptor ring of a DMA channel
372*
373*
374*  @return   -1 - On failure
375*             0 - On success
376*  @note
377*     - "len" parameter should be obtained from "dmacHw_descriptorLen"
378*     - Descriptor buffer MUST be 32 bit aligned and uncached as it is
379*       accessed by ARM and DMA
380*/
381/****************************************************************************/
382int dmacHw_initDescriptor(void *pDescriptorVirt,	/*  [ IN ] Virtual address of uncahced buffer allocated to form descriptor ring */
383			  uint32_t descriptorPhyAddr,	/*  [ IN ] Physical address of pDescriptorVirt (descriptor buffer) */
384			  uint32_t len,	/*  [ IN ] Size of the pBuf */
385			  uint32_t num	/*  [ IN ] Number of descriptor in the ring */
386    ) {
387	uint32_t i;
388	dmacHw_DESC_RING_t *pRing;
389	dmacHw_DESC_t *pDesc;
390
391	/* Check the alignment of the descriptor */
392	if ((uint32_t) pDescriptorVirt & 0x00000003) {
393		dmacHw_ASSERT(0);
394		return -1;
395	}
396
397	/* Check if enough space has been allocated for descriptor ring */
398	if (len < dmacHw_descriptorLen(num)) {
399		return -1;
400	}
401
402	pRing = dmacHw_GET_DESC_RING(pDescriptorVirt);
403	pRing->pHead =
404	    (dmacHw_DESC_t *) ((uint32_t) pRing + sizeof(dmacHw_DESC_RING_t));
405	pRing->pFree = pRing->pTail = pRing->pEnd = pRing->pHead;
406	pRing->pProg = dmacHw_DESC_INIT;
407	/* Initialize link item chain, starting from the head */
408	pDesc = pRing->pHead;
409	/* Find the offset between virtual to physical address */
410	pRing->virt2PhyOffset = (uint32_t) pDescriptorVirt - descriptorPhyAddr;
411
412	/* Form the descriptor ring */
413	for (i = 0; i < num - 1; i++) {
414		/* Clear link list item */
415		memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t));
416		/* Point to the next item in the physical address */
417		pDesc->llpPhy = (uint32_t) (pDesc + 1) - pRing->virt2PhyOffset;
418		/* Point to the next item in the virtual address */
419		pDesc->llp = (uint32_t) (pDesc + 1);
420		/* Mark descriptor is ready to use */
421		pDesc->ctl.hi = dmacHw_DESC_FREE;
422		/* Look into next link list item */
423		pDesc++;
424	}
425
426	/* Clear last link list item */
427	memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t));
428	/* Last item pointing to the first item in the
429	   physical address to complete the ring */
430	pDesc->llpPhy = (uint32_t) pRing->pHead - pRing->virt2PhyOffset;
431	/* Last item pointing to the first item in the
432	   virtual address to complete the ring
433	 */
434	pDesc->llp = (uint32_t) pRing->pHead;
435	/* Mark descriptor is ready to use */
436	pDesc->ctl.hi = dmacHw_DESC_FREE;
437	/* Set the number of descriptors in the ring */
438	pRing->num = num;
439	return 0;
440}
441
442/****************************************************************************/
443/**
444*  @brief   Configure DMA channel
445*
446*  @return  0  : On success
447*           -1 : On failure
448*/
449/****************************************************************************/
450int dmacHw_configChannel(dmacHw_HANDLE_t handle,	/*   [ IN ] DMA Channel handle */
451			 dmacHw_CONFIG_t *pConfig	/*   [ IN ] Configuration settings */
452    ) {
453	dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
454	uint32_t cfgHigh = 0;
455	int srcTrSize;
456	int dstTrSize;
457
458	pCblk->varDataStarted = 0;
459	pCblk->userData = NULL;
460
461	/* Configure
462	   - Burst transaction when enough data in available in FIFO
463	   - AHB Access protection 1
464	   - Source and destination peripheral ports
465	 */
466	cfgHigh =
467	    dmacHw_REG_CFG_HI_FIFO_ENOUGH | dmacHw_REG_CFG_HI_AHB_HPROT_1 |
468	    dmacHw_SRC_PERI_INTF(pConfig->
469				 srcPeripheralPort) |
470	    dmacHw_DST_PERI_INTF(pConfig->dstPeripheralPort);
471	/* Set priority */
472	dmacHw_SET_CHANNEL_PRIORITY(pCblk->module, pCblk->channel,
473				    pConfig->channelPriority);
474
475	if (pConfig->dstStatusRegisterAddress != 0) {
476		/* Destination status update enable */
477		cfgHigh |= dmacHw_REG_CFG_HI_UPDATE_DST_STAT;
478		/* Configure status registers */
479		dmacHw_SET_DSTATAR(pCblk->module, pCblk->channel,
480				   pConfig->dstStatusRegisterAddress);
481	}
482
483	if (pConfig->srcStatusRegisterAddress != 0) {
484		/* Source status update enable */
485		cfgHigh |= dmacHw_REG_CFG_HI_UPDATE_SRC_STAT;
486		/* Source status update enable */
487		dmacHw_SET_SSTATAR(pCblk->module, pCblk->channel,
488				   pConfig->srcStatusRegisterAddress);
489	}
490	/* Configure the config high register */
491	dmacHw_GET_CONFIG_HI(pCblk->module, pCblk->channel) = cfgHigh;
492
493	/* Clear all raw interrupt status */
494	dmacHw_TRAN_INT_CLEAR(pCblk->module, pCblk->channel);
495	dmacHw_BLOCK_INT_CLEAR(pCblk->module, pCblk->channel);
496	dmacHw_ERROR_INT_CLEAR(pCblk->module, pCblk->channel);
497
498	/* Configure block interrupt */
499	if (pConfig->blockTransferInterrupt == dmacHw_INTERRUPT_ENABLE) {
500		dmacHw_BLOCK_INT_ENABLE(pCblk->module, pCblk->channel);
501	} else {
502		dmacHw_BLOCK_INT_DISABLE(pCblk->module, pCblk->channel);
503	}
504	/* Configure complete transfer interrupt */
505	if (pConfig->completeTransferInterrupt == dmacHw_INTERRUPT_ENABLE) {
506		dmacHw_TRAN_INT_ENABLE(pCblk->module, pCblk->channel);
507	} else {
508		dmacHw_TRAN_INT_DISABLE(pCblk->module, pCblk->channel);
509	}
510	/* Configure error interrupt */
511	if (pConfig->errorInterrupt == dmacHw_INTERRUPT_ENABLE) {
512		dmacHw_ERROR_INT_ENABLE(pCblk->module, pCblk->channel);
513	} else {
514		dmacHw_ERROR_INT_DISABLE(pCblk->module, pCblk->channel);
515	}
516	/* Configure gather register */
517	if (pConfig->srcGatherWidth) {
518		srcTrSize =
519		    dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);
520		if (!
521		    ((pConfig->srcGatherWidth % srcTrSize)
522		     && (pConfig->srcGatherJump % srcTrSize))) {
523			dmacHw_REG_SGR_LO(pCblk->module, pCblk->channel) =
524			    ((pConfig->srcGatherWidth /
525			      srcTrSize) << 20) | (pConfig->srcGatherJump /
526						   srcTrSize);
527		} else {
528			return -1;
529		}
530	}
531	/* Configure scatter register */
532	if (pConfig->dstScatterWidth) {
533		dstTrSize =
534		    dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth);
535		if (!
536		    ((pConfig->dstScatterWidth % dstTrSize)
537		     && (pConfig->dstScatterJump % dstTrSize))) {
538			dmacHw_REG_DSR_LO(pCblk->module, pCblk->channel) =
539			    ((pConfig->dstScatterWidth /
540			      dstTrSize) << 20) | (pConfig->dstScatterJump /
541						   dstTrSize);
542		} else {
543			return -1;
544		}
545	}
546	return 0;
547}
548
549/****************************************************************************/
550/**
551*  @brief   Indicates whether DMA transfer is in progress or completed
552*
553*  @return   DMA transfer status
554*          dmacHw_TRANSFER_STATUS_BUSY:         DMA Transfer ongoing
555*          dmacHw_TRANSFER_STATUS_DONE:         DMA Transfer completed
556*          dmacHw_TRANSFER_STATUS_ERROR:        DMA Transfer error
557*
558*/
559/****************************************************************************/
560dmacHw_TRANSFER_STATUS_e dmacHw_transferCompleted(dmacHw_HANDLE_t handle	/*   [ IN ] DMA Channel handle */
561    ) {
562	dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
563
564	if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) {
565		return dmacHw_TRANSFER_STATUS_BUSY;
566	} else if (dmacHw_REG_INT_RAW_ERROR(pCblk->module) &
567		   (0x00000001 << pCblk->channel)) {
568		return dmacHw_TRANSFER_STATUS_ERROR;
569	}
570
571	return dmacHw_TRANSFER_STATUS_DONE;
572}
573
574/****************************************************************************/
575/**
576*  @brief   Set descriptors for known data length
577*
578*  When DMA has to work as a flow controller, this function prepares the
579*  descriptor chain to transfer data
580*
581*  from:
582*          - Memory to memory
583*          - Peripheral to memory
584*          - Memory to Peripheral
585*          - Peripheral to Peripheral
586*
587*  @return   -1 - On failure
588*             0 - On success
589*
590*/
591/****************************************************************************/
592int dmacHw_setDataDescriptor(dmacHw_CONFIG_t *pConfig,	/*   [ IN ] Configuration settings */
593			     void *pDescriptor,	/*   [ IN ] Descriptor buffer */
594			     void *pSrcAddr,	/*   [ IN ] Source (Peripheral/Memory) address */
595			     void *pDstAddr,	/*   [ IN ] Destination (Peripheral/Memory) address */
596			     size_t dataLen	/*   [ IN ] Data length in bytes */
597    ) {
598	dmacHw_TRANSACTION_WIDTH_e dstTrWidth;
599	dmacHw_TRANSACTION_WIDTH_e srcTrWidth;
600	dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);
601	dmacHw_DESC_t *pStart;
602	dmacHw_DESC_t *pProg;
603	int srcTs = 0;
604	int blkTs = 0;
605	int oddSize = 0;
606	int descCount = 0;
607	int count = 0;
608	int dstTrSize = 0;
609	int srcTrSize = 0;
610	uint32_t maxBlockSize = dmacHw_MAX_BLOCKSIZE;
611
612	dstTrSize = dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth);
613	srcTrSize = dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);
614
615	/* Skip Tx if buffer is NULL  or length is unknown */
616	if ((pSrcAddr == NULL) || (pDstAddr == NULL) || (dataLen == 0)) {
617		/* Do not initiate transfer */
618		return -1;
619	}
620
621	/* Ensure scatter and gather are transaction aligned */
622	if ((pConfig->srcGatherWidth % srcTrSize)
623	    || (pConfig->dstScatterWidth % dstTrSize)) {
624		return -2;
625	}
626
627	/*
628	   Background 1: DMAC can not perform DMA if source and destination addresses are
629	   not properly aligned with the channel's transaction width. So, for successful
630	   DMA transfer, transaction width must be set according to the alignment of the
631	   source and destination address.
632	 */
633
634	/* Adjust destination transaction width if destination address is not aligned properly */
635	dstTrWidth = pConfig->dstMaxTransactionWidth;
636	while (dmacHw_ADDRESS_MASK(dstTrSize) & (uint32_t) pDstAddr) {
637		dstTrWidth = dmacHw_GetNextTrWidth(dstTrWidth);
638		dstTrSize = dmacHw_GetTrWidthInBytes(dstTrWidth);
639	}
640
641	/* Adjust source transaction width if source address is not aligned properly */
642	srcTrWidth = pConfig->srcMaxTransactionWidth;
643	while (dmacHw_ADDRESS_MASK(srcTrSize) & (uint32_t) pSrcAddr) {
644		srcTrWidth = dmacHw_GetNextTrWidth(srcTrWidth);
645		srcTrSize = dmacHw_GetTrWidthInBytes(srcTrWidth);
646	}
647
648	/* Find the maximum transaction per descriptor */
649	if (pConfig->maxDataPerBlock
650	    && ((pConfig->maxDataPerBlock / srcTrSize) <
651		dmacHw_MAX_BLOCKSIZE)) {
652		maxBlockSize = pConfig->maxDataPerBlock / srcTrSize;
653	}
654
655	/* Find number of source transactions needed to complete the DMA transfer */
656	srcTs = dataLen / srcTrSize;
657	/* Find the odd number of bytes that need to be transferred as single byte transaction width */
658	if (srcTs && (dstTrSize > srcTrSize)) {
659		oddSize = dataLen % dstTrSize;
660		/* Adjust source transaction count due to "oddSize" */
661		srcTs = srcTs - (oddSize / srcTrSize);
662	} else {
663		oddSize = dataLen % srcTrSize;
664	}
665	/* Adjust "descCount" due to "oddSize" */
666	if (oddSize) {
667		descCount++;
668	}
669	/* Find the number of descriptor needed for total "srcTs" */
670	if (srcTs) {
671		descCount += ((srcTs - 1) / maxBlockSize) + 1;
672	}
673
674	/* Check the availability of "descCount" discriptors in the ring */
675	pProg = pRing->pHead;
676	for (count = 0; (descCount <= pRing->num) && (count < descCount);
677	     count++) {
678		if ((pProg->ctl.hi & dmacHw_DESC_FREE) == 0) {
679			/* Sufficient descriptors are not available */
680			return -3;
681		}
682		pProg = (dmacHw_DESC_t *) pProg->llp;
683	}
684
685	/* Remember the link list item to program the channel registers */
686	pStart = pProg = pRing->pHead;
687	/* Make a link list with "descCount(=count)" number of descriptors */
688	while (count) {
689		/* Reset channel control information */
690		pProg->ctl.lo = 0;
691		/* Enable source gather if configured */
692		if (pConfig->srcGatherWidth) {
693			pProg->ctl.lo |= dmacHw_REG_CTL_SG_ENABLE;
694		}
695		/* Enable destination scatter if configured */
696		if (pConfig->dstScatterWidth) {
697			pProg->ctl.lo |= dmacHw_REG_CTL_DS_ENABLE;
698		}
699		/* Set source and destination address */
700		pProg->sar = (uint32_t) pSrcAddr;
701		pProg->dar = (uint32_t) pDstAddr;
702		/* Use "devCtl" to mark that user memory need to be freed later if needed */
703		if (pProg == pRing->pHead) {
704			pProg->devCtl = dmacHw_FREE_USER_MEMORY;
705		} else {
706			pProg->devCtl = 0;
707		}
708
709		blkTs = srcTs;
710
711		/* Special treatmeant for last descriptor */
712		if (count == 1) {
713			/* Mark the last descriptor */
714			pProg->ctl.lo &=
715			    ~(dmacHw_REG_CTL_LLP_DST_EN |
716			      dmacHw_REG_CTL_LLP_SRC_EN);
717			/* Treatment for odd data bytes */
718			if (oddSize) {
719				/* Adjust for single byte transaction width */
720				switch (pConfig->transferType) {
721				case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
722					dstTrWidth =
723					    dmacHw_DST_TRANSACTION_WIDTH_8;
724					blkTs =
725					    (oddSize / srcTrSize) +
726					    ((oddSize % srcTrSize) ? 1 : 0);
727					break;
728				case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
729					srcTrWidth =
730					    dmacHw_SRC_TRANSACTION_WIDTH_8;
731					blkTs = oddSize;
732					break;
733				case dmacHw_TRANSFER_TYPE_MEM_TO_MEM:
734					srcTrWidth =
735					    dmacHw_SRC_TRANSACTION_WIDTH_8;
736					dstTrWidth =
737					    dmacHw_DST_TRANSACTION_WIDTH_8;
738					blkTs = oddSize;
739					break;
740				case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL:
741					/* Do not adjust the transaction width  */
742					break;
743				}
744			} else {
745				srcTs -= blkTs;
746			}
747		} else {
748			if (srcTs / maxBlockSize) {
749				blkTs = maxBlockSize;
750			}
751			/* Remaining source transactions for next iteration */
752			srcTs -= blkTs;
753		}
754		/* Must have a valid source transactions */
755		dmacHw_ASSERT(blkTs > 0);
756		/* Set control information */
757		if (pConfig->flowControler == dmacHw_FLOW_CONTROL_DMA) {
758			pProg->ctl.lo |= pConfig->transferType |
759			    pConfig->srcUpdate |
760			    pConfig->dstUpdate |
761			    srcTrWidth |
762			    dstTrWidth |
763			    pConfig->srcMaxBurstWidth |
764			    pConfig->dstMaxBurstWidth |
765			    pConfig->srcMasterInterface |
766			    pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN;
767		} else {
768			uint32_t transferType = 0;
769			switch (pConfig->transferType) {
770			case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
771				transferType = dmacHw_REG_CTL_TTFC_PM_PERI;
772				break;
773			case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
774				transferType = dmacHw_REG_CTL_TTFC_MP_PERI;
775				break;
776			default:
777				dmacHw_ASSERT(0);
778			}
779			pProg->ctl.lo |= transferType |
780			    pConfig->srcUpdate |
781			    pConfig->dstUpdate |
782			    srcTrWidth |
783			    dstTrWidth |
784			    pConfig->srcMaxBurstWidth |
785			    pConfig->dstMaxBurstWidth |
786			    pConfig->srcMasterInterface |
787			    pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN;
788		}
789
790		/* Set block transaction size */
791		pProg->ctl.hi = blkTs & dmacHw_REG_CTL_BLOCK_TS_MASK;
792		/* Look for next descriptor */
793		if (count > 1) {
794			/* Point to the next descriptor */
795			pProg = (dmacHw_DESC_t *) pProg->llp;
796
797			/* Update source and destination address for next iteration */
798			switch (pConfig->transferType) {
799			case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
800				if (pConfig->dstScatterWidth) {
801					pDstAddr =
802					    (char *)pDstAddr +
803					    blkTs * srcTrSize +
804					    (((blkTs * srcTrSize) /
805					      pConfig->dstScatterWidth) *
806					     pConfig->dstScatterJump);
807				} else {
808					pDstAddr =
809					    (char *)pDstAddr +
810					    blkTs * srcTrSize;
811				}
812				break;
813			case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
814				if (pConfig->srcGatherWidth) {
815					pSrcAddr =
816					    (char *)pDstAddr +
817					    blkTs * srcTrSize +
818					    (((blkTs * srcTrSize) /
819					      pConfig->srcGatherWidth) *
820					     pConfig->srcGatherJump);
821				} else {
822					pSrcAddr =
823					    (char *)pSrcAddr +
824					    blkTs * srcTrSize;
825				}
826				break;
827			case dmacHw_TRANSFER_TYPE_MEM_TO_MEM:
828				if (pConfig->dstScatterWidth) {
829					pDstAddr =
830					    (char *)pDstAddr +
831					    blkTs * srcTrSize +
832					    (((blkTs * srcTrSize) /
833					      pConfig->dstScatterWidth) *
834					     pConfig->dstScatterJump);
835				} else {
836					pDstAddr =
837					    (char *)pDstAddr +
838					    blkTs * srcTrSize;
839				}
840
841				if (pConfig->srcGatherWidth) {
842					pSrcAddr =
843					    (char *)pDstAddr +
844					    blkTs * srcTrSize +
845					    (((blkTs * srcTrSize) /
846					      pConfig->srcGatherWidth) *
847					     pConfig->srcGatherJump);
848				} else {
849					pSrcAddr =
850					    (char *)pSrcAddr +
851					    blkTs * srcTrSize;
852				}
853				break;
854			case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL:
855				/* Do not adjust the address */
856				break;
857			default:
858				dmacHw_ASSERT(0);
859			}
860		} else {
861			/* At the end of transfer "srcTs" must be zero */
862			dmacHw_ASSERT(srcTs == 0);
863		}
864		count--;
865	}
866
867	/* Remember the descriptor to initialize the registers */
868	if (pRing->pProg == dmacHw_DESC_INIT) {
869		pRing->pProg = pStart;
870	}
871	/* Indicate that the descriptor is updated */
872	pRing->pEnd = pProg;
873	/* Head pointing to the next descriptor */
874	pRing->pHead = (dmacHw_DESC_t *) pProg->llp;
875	/* Update Tail pointer if destination is a peripheral,
876	   because no one is going to read from the pTail
877	 */
878	if (!dmacHw_DST_IS_MEMORY(pConfig->transferType)) {
879		pRing->pTail = pRing->pHead;
880	}
881	return 0;
882}
883
884/****************************************************************************/
885/**
886*  @brief   Provides DMA controller attributes
887*
888*
889*  @return  DMA controller attributes
890*
891*  @note
892*     None
893*/
894/****************************************************************************/
895uint32_t dmacHw_getDmaControllerAttribute(dmacHw_HANDLE_t handle,	/*  [ IN ]  DMA Channel handle */
896					  dmacHw_CONTROLLER_ATTRIB_e attr	/*  [ IN ]  DMA Controler attribute of type  dmacHw_CONTROLLER_ATTRIB_e */
897    ) {
898	dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
899
900	switch (attr) {
901	case dmacHw_CONTROLLER_ATTRIB_CHANNEL_NUM:
902		return dmacHw_GET_NUM_CHANNEL(pCblk->module);
903	case dmacHw_CONTROLLER_ATTRIB_CHANNEL_MAX_BLOCK_SIZE:
904		return (1 <<
905			 (dmacHw_GET_MAX_BLOCK_SIZE
906			  (pCblk->module, pCblk->module) + 2)) - 8;
907	case dmacHw_CONTROLLER_ATTRIB_MASTER_INTF_NUM:
908		return dmacHw_GET_NUM_INTERFACE(pCblk->module);
909	case dmacHw_CONTROLLER_ATTRIB_CHANNEL_BUS_WIDTH:
910		return 32 << dmacHw_GET_CHANNEL_DATA_WIDTH(pCblk->module,
911							   pCblk->channel);
912	case dmacHw_CONTROLLER_ATTRIB_CHANNEL_FIFO_SIZE:
913		return GetFifoSize(handle);
914	}
915	dmacHw_ASSERT(0);
916	return 0;
917}
918