Path: blob/master/arch/arm/mach-bcmring/csp/dmac/dmacHw.c
10820 views
/*****************************************************************************1* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.2*3* Unless you and Broadcom execute a separate written software license4* agreement governing use of this software, this software is licensed to you5* under the terms of the GNU General Public License version 2, available at6* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").7*8* Notwithstanding the above, under no circumstances may you combine this9* software in any way with any other Broadcom software provided under a10* license other than the GPL, without Broadcom's express prior written11* consent.12*****************************************************************************/1314/****************************************************************************/15/**16* @file dmacHw.c17*18* @brief Low level DMA controller driver routines19*20* @note21*22* These routines provide basic DMA functionality only.23*/24/****************************************************************************/2526/* ---- Include Files ---------------------------------------------------- */27#include <csp/stdint.h>28#include <csp/string.h>29#include <stddef.h>3031#include <csp/dmacHw.h>32#include <mach/csp/dmacHw_reg.h>33#include <mach/csp/dmacHw_priv.h>34#include <mach/csp/chipcHw_inline.h>3536/* ---- External Function Prototypes ------------------------------------- */3738/* Allocate DMA control blocks */39dmacHw_CBLK_t dmacHw_gCblk[dmacHw_MAX_CHANNEL_COUNT];4041uint32_t dmaChannelCount_0 = dmacHw_MAX_CHANNEL_COUNT / 2;42uint32_t dmaChannelCount_1 = dmacHw_MAX_CHANNEL_COUNT / 2;4344/****************************************************************************/45/**46* @brief Get maximum FIFO for a DMA channel47*48* @return Maximum allowable FIFO size49*50*51*/52/****************************************************************************/53static uint32_t GetFifoSize(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */54) {55uint32_t val = 0;56dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);57dmacHw_MISC_t *pMiscReg =58(dmacHw_MISC_t *) dmacHw_REG_MISC_BASE(pCblk->module);5960switch (pCblk->channel) {61case 0:62val = (pMiscReg->CompParm2.lo & 0x70000000) >> 28;63break;64case 1:65val = (pMiscReg->CompParm3.hi & 0x70000000) >> 28;66break;67case 2:68val = (pMiscReg->CompParm3.lo & 0x70000000) >> 28;69break;70case 3:71val = (pMiscReg->CompParm4.hi & 0x70000000) >> 28;72break;73case 4:74val = (pMiscReg->CompParm4.lo & 0x70000000) >> 28;75break;76case 5:77val = (pMiscReg->CompParm5.hi & 0x70000000) >> 28;78break;79case 6:80val = (pMiscReg->CompParm5.lo & 0x70000000) >> 28;81break;82case 7:83val = (pMiscReg->CompParm6.hi & 0x70000000) >> 28;84break;85}8687if (val <= 0x4) {88return 8 << val;89} else {90dmacHw_ASSERT(0);91}92return 0;93}9495/****************************************************************************/96/**97* @brief Program channel register to initiate transfer98*99* @return void100*101*102* @note103* - Descriptor buffer MUST ALWAYS be flushed before calling this function104* - This function should also be called from ISR to program the channel with105* pending descriptors106*/107/****************************************************************************/108void dmacHw_initiateTransfer(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */109dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */110void *pDescriptor /* [ IN ] Descriptor buffer */111) {112dmacHw_DESC_RING_t *pRing;113dmacHw_DESC_t *pProg;114dmacHw_CBLK_t *pCblk;115116pCblk = dmacHw_HANDLE_TO_CBLK(handle);117pRing = dmacHw_GET_DESC_RING(pDescriptor);118119if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) {120/* Not safe yet to program the channel */121return;122}123124if (pCblk->varDataStarted) {125if (pCblk->descUpdated) {126pCblk->descUpdated = 0;127pProg =128(dmacHw_DESC_t *) ((uint32_t)129dmacHw_REG_LLP(pCblk->module,130pCblk->channel) +131pRing->virt2PhyOffset);132133/* Load descriptor if not loaded */134if (!(pProg->ctl.hi & dmacHw_REG_CTL_DONE)) {135dmacHw_SET_SAR(pCblk->module, pCblk->channel,136pProg->sar);137dmacHw_SET_DAR(pCblk->module, pCblk->channel,138pProg->dar);139dmacHw_REG_CTL_LO(pCblk->module,140pCblk->channel) =141pProg->ctl.lo;142dmacHw_REG_CTL_HI(pCblk->module,143pCblk->channel) =144pProg->ctl.hi;145} else if (pProg == (dmacHw_DESC_t *) pRing->pEnd->llp) {146/* Return as end descriptor is processed */147return;148} else {149dmacHw_ASSERT(0);150}151} else {152return;153}154} else {155if (pConfig->transferMode == dmacHw_TRANSFER_MODE_PERIODIC) {156/* Do not make a single chain, rather process one descriptor at a time */157pProg = pRing->pHead;158/* Point to the next descriptor for next iteration */159dmacHw_NEXT_DESC(pRing, pHead);160} else {161/* Return if no more pending descriptor */162if (pRing->pEnd == NULL) {163return;164}165166pProg = pRing->pProg;167if (pConfig->transferMode ==168dmacHw_TRANSFER_MODE_CONTINUOUS) {169/* Make sure a complete ring can be formed */170dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pEnd->171llp == pRing->pProg);172/* Make sure pProg pointing to the pHead */173dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pProg ==174pRing->pHead);175/* Make a complete ring */176do {177pRing->pProg->ctl.lo |=178(dmacHw_REG_CTL_LLP_DST_EN |179dmacHw_REG_CTL_LLP_SRC_EN);180pRing->pProg =181(dmacHw_DESC_t *) pRing->pProg->llp;182} while (pRing->pProg != pRing->pHead);183} else {184/* Make a single long chain */185while (pRing->pProg != pRing->pEnd) {186pRing->pProg->ctl.lo |=187(dmacHw_REG_CTL_LLP_DST_EN |188dmacHw_REG_CTL_LLP_SRC_EN);189pRing->pProg =190(dmacHw_DESC_t *) pRing->pProg->llp;191}192}193}194195/* Program the channel registers */196dmacHw_SET_SAR(pCblk->module, pCblk->channel, pProg->sar);197dmacHw_SET_DAR(pCblk->module, pCblk->channel, pProg->dar);198dmacHw_SET_LLP(pCblk->module, pCblk->channel,199(uint32_t) pProg - pRing->virt2PhyOffset);200dmacHw_REG_CTL_LO(pCblk->module, pCblk->channel) =201pProg->ctl.lo;202dmacHw_REG_CTL_HI(pCblk->module, pCblk->channel) =203pProg->ctl.hi;204if (pRing->pEnd) {205/* Remember the descriptor to use next */206pRing->pProg = (dmacHw_DESC_t *) pRing->pEnd->llp;207}208/* Indicate no more pending descriptor */209pRing->pEnd = (dmacHw_DESC_t *) NULL;210}211/* Start DMA operation */212dmacHw_DMA_START(pCblk->module, pCblk->channel);213}214215/****************************************************************************/216/**217* @brief Initializes DMA218*219* This function initializes DMA CSP driver220*221* @note222* Must be called before using any DMA channel223*/224/****************************************************************************/225void dmacHw_initDma(void)226{227228uint32_t i = 0;229230dmaChannelCount_0 = dmacHw_GET_NUM_CHANNEL(0);231dmaChannelCount_1 = dmacHw_GET_NUM_CHANNEL(1);232233/* Enable access to the DMA block */234chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC0);235chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC1);236237if ((dmaChannelCount_0 + dmaChannelCount_1) > dmacHw_MAX_CHANNEL_COUNT) {238dmacHw_ASSERT(0);239}240241memset((void *)dmacHw_gCblk, 0,242sizeof(dmacHw_CBLK_t) * (dmaChannelCount_0 + dmaChannelCount_1));243for (i = 0; i < dmaChannelCount_0; i++) {244dmacHw_gCblk[i].module = 0;245dmacHw_gCblk[i].channel = i;246}247for (i = 0; i < dmaChannelCount_1; i++) {248dmacHw_gCblk[i + dmaChannelCount_0].module = 1;249dmacHw_gCblk[i + dmaChannelCount_0].channel = i;250}251}252253/****************************************************************************/254/**255* @brief Exit function for DMA256*257* This function isolates DMA from the system258*259*/260/****************************************************************************/261void dmacHw_exitDma(void)262{263/* Disable access to the DMA block */264chipcHw_busInterfaceClockDisable(chipcHw_REG_BUS_CLOCK_DMAC0);265chipcHw_busInterfaceClockDisable(chipcHw_REG_BUS_CLOCK_DMAC1);266}267268/****************************************************************************/269/**270* @brief Gets a handle to a DMA channel271*272* This function returns a handle, representing a control block of a particular DMA channel273*274* @return -1 - On Failure275* handle - On Success, representing a channel control block276*277* @note278* None Channel ID must be created using "dmacHw_MAKE_CHANNEL_ID" macro279*/280/****************************************************************************/281dmacHw_HANDLE_t dmacHw_getChannelHandle(dmacHw_ID_t channelId /* [ IN ] DMA Channel Id */282) {283int idx;284285switch ((channelId >> 8)) {286case 0:287dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_0);288idx = (channelId & 0xff);289break;290case 1:291dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_1);292idx = dmaChannelCount_0 + (channelId & 0xff);293break;294default:295dmacHw_ASSERT(0);296return (dmacHw_HANDLE_t) -1;297}298299return dmacHw_CBLK_TO_HANDLE(&dmacHw_gCblk[idx]);300}301302/****************************************************************************/303/**304* @brief Initializes a DMA channel for use305*306* This function initializes and resets a DMA channel for use307*308* @return -1 - On Failure309* 0 - On Success310*311* @note312* None313*/314/****************************************************************************/315int dmacHw_initChannel(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */316) {317dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);318int module = pCblk->module;319int channel = pCblk->channel;320321/* Reinitialize the control block */322memset((void *)pCblk, 0, sizeof(dmacHw_CBLK_t));323pCblk->module = module;324pCblk->channel = channel;325326/* Enable DMA controller */327dmacHw_DMA_ENABLE(pCblk->module);328/* Reset DMA channel */329dmacHw_RESET_CONTROL_LO(pCblk->module, pCblk->channel);330dmacHw_RESET_CONTROL_HI(pCblk->module, pCblk->channel);331dmacHw_RESET_CONFIG_LO(pCblk->module, pCblk->channel);332dmacHw_RESET_CONFIG_HI(pCblk->module, pCblk->channel);333334/* Clear all raw interrupt status */335dmacHw_TRAN_INT_CLEAR(pCblk->module, pCblk->channel);336dmacHw_BLOCK_INT_CLEAR(pCblk->module, pCblk->channel);337dmacHw_ERROR_INT_CLEAR(pCblk->module, pCblk->channel);338339/* Mask event specific interrupts */340dmacHw_TRAN_INT_DISABLE(pCblk->module, pCblk->channel);341dmacHw_BLOCK_INT_DISABLE(pCblk->module, pCblk->channel);342dmacHw_STRAN_INT_DISABLE(pCblk->module, pCblk->channel);343dmacHw_DTRAN_INT_DISABLE(pCblk->module, pCblk->channel);344dmacHw_ERROR_INT_DISABLE(pCblk->module, pCblk->channel);345346return 0;347}348349/****************************************************************************/350/**351* @brief Finds amount of memory required to form a descriptor ring352*353*354* @return Number of bytes required to form a descriptor ring355*356*357*/358/****************************************************************************/359uint32_t dmacHw_descriptorLen(uint32_t descCnt /* [ IN ] Number of descriptor in the ring */360) {361/* Need extra 4 byte to ensure 32 bit alignment */362return (descCnt * sizeof(dmacHw_DESC_t)) + sizeof(dmacHw_DESC_RING_t) +363sizeof(uint32_t);364}365366/****************************************************************************/367/**368* @brief Initializes descriptor ring369*370* This function will initializes the descriptor ring of a DMA channel371*372*373* @return -1 - On failure374* 0 - On success375* @note376* - "len" parameter should be obtained from "dmacHw_descriptorLen"377* - Descriptor buffer MUST be 32 bit aligned and uncached as it is378* accessed by ARM and DMA379*/380/****************************************************************************/381int dmacHw_initDescriptor(void *pDescriptorVirt, /* [ IN ] Virtual address of uncahced buffer allocated to form descriptor ring */382uint32_t descriptorPhyAddr, /* [ IN ] Physical address of pDescriptorVirt (descriptor buffer) */383uint32_t len, /* [ IN ] Size of the pBuf */384uint32_t num /* [ IN ] Number of descriptor in the ring */385) {386uint32_t i;387dmacHw_DESC_RING_t *pRing;388dmacHw_DESC_t *pDesc;389390/* Check the alignment of the descriptor */391if ((uint32_t) pDescriptorVirt & 0x00000003) {392dmacHw_ASSERT(0);393return -1;394}395396/* Check if enough space has been allocated for descriptor ring */397if (len < dmacHw_descriptorLen(num)) {398return -1;399}400401pRing = dmacHw_GET_DESC_RING(pDescriptorVirt);402pRing->pHead =403(dmacHw_DESC_t *) ((uint32_t) pRing + sizeof(dmacHw_DESC_RING_t));404pRing->pFree = pRing->pTail = pRing->pEnd = pRing->pHead;405pRing->pProg = dmacHw_DESC_INIT;406/* Initialize link item chain, starting from the head */407pDesc = pRing->pHead;408/* Find the offset between virtual to physical address */409pRing->virt2PhyOffset = (uint32_t) pDescriptorVirt - descriptorPhyAddr;410411/* Form the descriptor ring */412for (i = 0; i < num - 1; i++) {413/* Clear link list item */414memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t));415/* Point to the next item in the physical address */416pDesc->llpPhy = (uint32_t) (pDesc + 1) - pRing->virt2PhyOffset;417/* Point to the next item in the virtual address */418pDesc->llp = (uint32_t) (pDesc + 1);419/* Mark descriptor is ready to use */420pDesc->ctl.hi = dmacHw_DESC_FREE;421/* Look into next link list item */422pDesc++;423}424425/* Clear last link list item */426memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t));427/* Last item pointing to the first item in the428physical address to complete the ring */429pDesc->llpPhy = (uint32_t) pRing->pHead - pRing->virt2PhyOffset;430/* Last item pointing to the first item in the431virtual address to complete the ring432*/433pDesc->llp = (uint32_t) pRing->pHead;434/* Mark descriptor is ready to use */435pDesc->ctl.hi = dmacHw_DESC_FREE;436/* Set the number of descriptors in the ring */437pRing->num = num;438return 0;439}440441/****************************************************************************/442/**443* @brief Configure DMA channel444*445* @return 0 : On success446* -1 : On failure447*/448/****************************************************************************/449int dmacHw_configChannel(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */450dmacHw_CONFIG_t *pConfig /* [ IN ] Configuration settings */451) {452dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);453uint32_t cfgHigh = 0;454int srcTrSize;455int dstTrSize;456457pCblk->varDataStarted = 0;458pCblk->userData = NULL;459460/* Configure461- Burst transaction when enough data in available in FIFO462- AHB Access protection 1463- Source and destination peripheral ports464*/465cfgHigh =466dmacHw_REG_CFG_HI_FIFO_ENOUGH | dmacHw_REG_CFG_HI_AHB_HPROT_1 |467dmacHw_SRC_PERI_INTF(pConfig->468srcPeripheralPort) |469dmacHw_DST_PERI_INTF(pConfig->dstPeripheralPort);470/* Set priority */471dmacHw_SET_CHANNEL_PRIORITY(pCblk->module, pCblk->channel,472pConfig->channelPriority);473474if (pConfig->dstStatusRegisterAddress != 0) {475/* Destination status update enable */476cfgHigh |= dmacHw_REG_CFG_HI_UPDATE_DST_STAT;477/* Configure status registers */478dmacHw_SET_DSTATAR(pCblk->module, pCblk->channel,479pConfig->dstStatusRegisterAddress);480}481482if (pConfig->srcStatusRegisterAddress != 0) {483/* Source status update enable */484cfgHigh |= dmacHw_REG_CFG_HI_UPDATE_SRC_STAT;485/* Source status update enable */486dmacHw_SET_SSTATAR(pCblk->module, pCblk->channel,487pConfig->srcStatusRegisterAddress);488}489/* Configure the config high register */490dmacHw_GET_CONFIG_HI(pCblk->module, pCblk->channel) = cfgHigh;491492/* Clear all raw interrupt status */493dmacHw_TRAN_INT_CLEAR(pCblk->module, pCblk->channel);494dmacHw_BLOCK_INT_CLEAR(pCblk->module, pCblk->channel);495dmacHw_ERROR_INT_CLEAR(pCblk->module, pCblk->channel);496497/* Configure block interrupt */498if (pConfig->blockTransferInterrupt == dmacHw_INTERRUPT_ENABLE) {499dmacHw_BLOCK_INT_ENABLE(pCblk->module, pCblk->channel);500} else {501dmacHw_BLOCK_INT_DISABLE(pCblk->module, pCblk->channel);502}503/* Configure complete transfer interrupt */504if (pConfig->completeTransferInterrupt == dmacHw_INTERRUPT_ENABLE) {505dmacHw_TRAN_INT_ENABLE(pCblk->module, pCblk->channel);506} else {507dmacHw_TRAN_INT_DISABLE(pCblk->module, pCblk->channel);508}509/* Configure error interrupt */510if (pConfig->errorInterrupt == dmacHw_INTERRUPT_ENABLE) {511dmacHw_ERROR_INT_ENABLE(pCblk->module, pCblk->channel);512} else {513dmacHw_ERROR_INT_DISABLE(pCblk->module, pCblk->channel);514}515/* Configure gather register */516if (pConfig->srcGatherWidth) {517srcTrSize =518dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);519if (!520((pConfig->srcGatherWidth % srcTrSize)521&& (pConfig->srcGatherJump % srcTrSize))) {522dmacHw_REG_SGR_LO(pCblk->module, pCblk->channel) =523((pConfig->srcGatherWidth /524srcTrSize) << 20) | (pConfig->srcGatherJump /525srcTrSize);526} else {527return -1;528}529}530/* Configure scatter register */531if (pConfig->dstScatterWidth) {532dstTrSize =533dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth);534if (!535((pConfig->dstScatterWidth % dstTrSize)536&& (pConfig->dstScatterJump % dstTrSize))) {537dmacHw_REG_DSR_LO(pCblk->module, pCblk->channel) =538((pConfig->dstScatterWidth /539dstTrSize) << 20) | (pConfig->dstScatterJump /540dstTrSize);541} else {542return -1;543}544}545return 0;546}547548/****************************************************************************/549/**550* @brief Indicates whether DMA transfer is in progress or completed551*552* @return DMA transfer status553* dmacHw_TRANSFER_STATUS_BUSY: DMA Transfer ongoing554* dmacHw_TRANSFER_STATUS_DONE: DMA Transfer completed555* dmacHw_TRANSFER_STATUS_ERROR: DMA Transfer error556*557*/558/****************************************************************************/559dmacHw_TRANSFER_STATUS_e dmacHw_transferCompleted(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */560) {561dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);562563if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) {564return dmacHw_TRANSFER_STATUS_BUSY;565} else if (dmacHw_REG_INT_RAW_ERROR(pCblk->module) &566(0x00000001 << pCblk->channel)) {567return dmacHw_TRANSFER_STATUS_ERROR;568}569570return dmacHw_TRANSFER_STATUS_DONE;571}572573/****************************************************************************/574/**575* @brief Set descriptors for known data length576*577* When DMA has to work as a flow controller, this function prepares the578* descriptor chain to transfer data579*580* from:581* - Memory to memory582* - Peripheral to memory583* - Memory to Peripheral584* - Peripheral to Peripheral585*586* @return -1 - On failure587* 0 - On success588*589*/590/****************************************************************************/591int dmacHw_setDataDescriptor(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */592void *pDescriptor, /* [ IN ] Descriptor buffer */593void *pSrcAddr, /* [ IN ] Source (Peripheral/Memory) address */594void *pDstAddr, /* [ IN ] Destination (Peripheral/Memory) address */595size_t dataLen /* [ IN ] Data length in bytes */596) {597dmacHw_TRANSACTION_WIDTH_e dstTrWidth;598dmacHw_TRANSACTION_WIDTH_e srcTrWidth;599dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);600dmacHw_DESC_t *pStart;601dmacHw_DESC_t *pProg;602int srcTs = 0;603int blkTs = 0;604int oddSize = 0;605int descCount = 0;606int count = 0;607int dstTrSize = 0;608int srcTrSize = 0;609uint32_t maxBlockSize = dmacHw_MAX_BLOCKSIZE;610611dstTrSize = dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth);612srcTrSize = dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);613614/* Skip Tx if buffer is NULL or length is unknown */615if ((pSrcAddr == NULL) || (pDstAddr == NULL) || (dataLen == 0)) {616/* Do not initiate transfer */617return -1;618}619620/* Ensure scatter and gather are transaction aligned */621if ((pConfig->srcGatherWidth % srcTrSize)622|| (pConfig->dstScatterWidth % dstTrSize)) {623return -2;624}625626/*627Background 1: DMAC can not perform DMA if source and destination addresses are628not properly aligned with the channel's transaction width. So, for successful629DMA transfer, transaction width must be set according to the alignment of the630source and destination address.631*/632633/* Adjust destination transaction width if destination address is not aligned properly */634dstTrWidth = pConfig->dstMaxTransactionWidth;635while (dmacHw_ADDRESS_MASK(dstTrSize) & (uint32_t) pDstAddr) {636dstTrWidth = dmacHw_GetNextTrWidth(dstTrWidth);637dstTrSize = dmacHw_GetTrWidthInBytes(dstTrWidth);638}639640/* Adjust source transaction width if source address is not aligned properly */641srcTrWidth = pConfig->srcMaxTransactionWidth;642while (dmacHw_ADDRESS_MASK(srcTrSize) & (uint32_t) pSrcAddr) {643srcTrWidth = dmacHw_GetNextTrWidth(srcTrWidth);644srcTrSize = dmacHw_GetTrWidthInBytes(srcTrWidth);645}646647/* Find the maximum transaction per descriptor */648if (pConfig->maxDataPerBlock649&& ((pConfig->maxDataPerBlock / srcTrSize) <650dmacHw_MAX_BLOCKSIZE)) {651maxBlockSize = pConfig->maxDataPerBlock / srcTrSize;652}653654/* Find number of source transactions needed to complete the DMA transfer */655srcTs = dataLen / srcTrSize;656/* Find the odd number of bytes that need to be transferred as single byte transaction width */657if (srcTs && (dstTrSize > srcTrSize)) {658oddSize = dataLen % dstTrSize;659/* Adjust source transaction count due to "oddSize" */660srcTs = srcTs - (oddSize / srcTrSize);661} else {662oddSize = dataLen % srcTrSize;663}664/* Adjust "descCount" due to "oddSize" */665if (oddSize) {666descCount++;667}668/* Find the number of descriptor needed for total "srcTs" */669if (srcTs) {670descCount += ((srcTs - 1) / maxBlockSize) + 1;671}672673/* Check the availability of "descCount" discriptors in the ring */674pProg = pRing->pHead;675for (count = 0; (descCount <= pRing->num) && (count < descCount);676count++) {677if ((pProg->ctl.hi & dmacHw_DESC_FREE) == 0) {678/* Sufficient descriptors are not available */679return -3;680}681pProg = (dmacHw_DESC_t *) pProg->llp;682}683684/* Remember the link list item to program the channel registers */685pStart = pProg = pRing->pHead;686/* Make a link list with "descCount(=count)" number of descriptors */687while (count) {688/* Reset channel control information */689pProg->ctl.lo = 0;690/* Enable source gather if configured */691if (pConfig->srcGatherWidth) {692pProg->ctl.lo |= dmacHw_REG_CTL_SG_ENABLE;693}694/* Enable destination scatter if configured */695if (pConfig->dstScatterWidth) {696pProg->ctl.lo |= dmacHw_REG_CTL_DS_ENABLE;697}698/* Set source and destination address */699pProg->sar = (uint32_t) pSrcAddr;700pProg->dar = (uint32_t) pDstAddr;701/* Use "devCtl" to mark that user memory need to be freed later if needed */702if (pProg == pRing->pHead) {703pProg->devCtl = dmacHw_FREE_USER_MEMORY;704} else {705pProg->devCtl = 0;706}707708blkTs = srcTs;709710/* Special treatmeant for last descriptor */711if (count == 1) {712/* Mark the last descriptor */713pProg->ctl.lo &=714~(dmacHw_REG_CTL_LLP_DST_EN |715dmacHw_REG_CTL_LLP_SRC_EN);716/* Treatment for odd data bytes */717if (oddSize) {718/* Adjust for single byte transaction width */719switch (pConfig->transferType) {720case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:721dstTrWidth =722dmacHw_DST_TRANSACTION_WIDTH_8;723blkTs =724(oddSize / srcTrSize) +725((oddSize % srcTrSize) ? 1 : 0);726break;727case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:728srcTrWidth =729dmacHw_SRC_TRANSACTION_WIDTH_8;730blkTs = oddSize;731break;732case dmacHw_TRANSFER_TYPE_MEM_TO_MEM:733srcTrWidth =734dmacHw_SRC_TRANSACTION_WIDTH_8;735dstTrWidth =736dmacHw_DST_TRANSACTION_WIDTH_8;737blkTs = oddSize;738break;739case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL:740/* Do not adjust the transaction width */741break;742}743} else {744srcTs -= blkTs;745}746} else {747if (srcTs / maxBlockSize) {748blkTs = maxBlockSize;749}750/* Remaining source transactions for next iteration */751srcTs -= blkTs;752}753/* Must have a valid source transactions */754dmacHw_ASSERT(blkTs > 0);755/* Set control information */756if (pConfig->flowControler == dmacHw_FLOW_CONTROL_DMA) {757pProg->ctl.lo |= pConfig->transferType |758pConfig->srcUpdate |759pConfig->dstUpdate |760srcTrWidth |761dstTrWidth |762pConfig->srcMaxBurstWidth |763pConfig->dstMaxBurstWidth |764pConfig->srcMasterInterface |765pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN;766} else {767uint32_t transferType = 0;768switch (pConfig->transferType) {769case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:770transferType = dmacHw_REG_CTL_TTFC_PM_PERI;771break;772case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:773transferType = dmacHw_REG_CTL_TTFC_MP_PERI;774break;775default:776dmacHw_ASSERT(0);777}778pProg->ctl.lo |= transferType |779pConfig->srcUpdate |780pConfig->dstUpdate |781srcTrWidth |782dstTrWidth |783pConfig->srcMaxBurstWidth |784pConfig->dstMaxBurstWidth |785pConfig->srcMasterInterface |786pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN;787}788789/* Set block transaction size */790pProg->ctl.hi = blkTs & dmacHw_REG_CTL_BLOCK_TS_MASK;791/* Look for next descriptor */792if (count > 1) {793/* Point to the next descriptor */794pProg = (dmacHw_DESC_t *) pProg->llp;795796/* Update source and destination address for next iteration */797switch (pConfig->transferType) {798case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:799if (pConfig->dstScatterWidth) {800pDstAddr =801(char *)pDstAddr +802blkTs * srcTrSize +803(((blkTs * srcTrSize) /804pConfig->dstScatterWidth) *805pConfig->dstScatterJump);806} else {807pDstAddr =808(char *)pDstAddr +809blkTs * srcTrSize;810}811break;812case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:813if (pConfig->srcGatherWidth) {814pSrcAddr =815(char *)pDstAddr +816blkTs * srcTrSize +817(((blkTs * srcTrSize) /818pConfig->srcGatherWidth) *819pConfig->srcGatherJump);820} else {821pSrcAddr =822(char *)pSrcAddr +823blkTs * srcTrSize;824}825break;826case dmacHw_TRANSFER_TYPE_MEM_TO_MEM:827if (pConfig->dstScatterWidth) {828pDstAddr =829(char *)pDstAddr +830blkTs * srcTrSize +831(((blkTs * srcTrSize) /832pConfig->dstScatterWidth) *833pConfig->dstScatterJump);834} else {835pDstAddr =836(char *)pDstAddr +837blkTs * srcTrSize;838}839840if (pConfig->srcGatherWidth) {841pSrcAddr =842(char *)pDstAddr +843blkTs * srcTrSize +844(((blkTs * srcTrSize) /845pConfig->srcGatherWidth) *846pConfig->srcGatherJump);847} else {848pSrcAddr =849(char *)pSrcAddr +850blkTs * srcTrSize;851}852break;853case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL:854/* Do not adjust the address */855break;856default:857dmacHw_ASSERT(0);858}859} else {860/* At the end of transfer "srcTs" must be zero */861dmacHw_ASSERT(srcTs == 0);862}863count--;864}865866/* Remember the descriptor to initialize the registers */867if (pRing->pProg == dmacHw_DESC_INIT) {868pRing->pProg = pStart;869}870/* Indicate that the descriptor is updated */871pRing->pEnd = pProg;872/* Head pointing to the next descriptor */873pRing->pHead = (dmacHw_DESC_t *) pProg->llp;874/* Update Tail pointer if destination is a peripheral,875because no one is going to read from the pTail876*/877if (!dmacHw_DST_IS_MEMORY(pConfig->transferType)) {878pRing->pTail = pRing->pHead;879}880return 0;881}882883/****************************************************************************/884/**885* @brief Provides DMA controller attributes886*887*888* @return DMA controller attributes889*890* @note891* None892*/893/****************************************************************************/894uint32_t dmacHw_getDmaControllerAttribute(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */895dmacHw_CONTROLLER_ATTRIB_e attr /* [ IN ] DMA Controller attribute of type dmacHw_CONTROLLER_ATTRIB_e */896) {897dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);898899switch (attr) {900case dmacHw_CONTROLLER_ATTRIB_CHANNEL_NUM:901return dmacHw_GET_NUM_CHANNEL(pCblk->module);902case dmacHw_CONTROLLER_ATTRIB_CHANNEL_MAX_BLOCK_SIZE:903return (1 <<904(dmacHw_GET_MAX_BLOCK_SIZE905(pCblk->module, pCblk->module) + 2)) - 8;906case dmacHw_CONTROLLER_ATTRIB_MASTER_INTF_NUM:907return dmacHw_GET_NUM_INTERFACE(pCblk->module);908case dmacHw_CONTROLLER_ATTRIB_CHANNEL_BUS_WIDTH:909return 32 << dmacHw_GET_CHANNEL_DATA_WIDTH(pCblk->module,910pCblk->channel);911case dmacHw_CONTROLLER_ATTRIB_CHANNEL_FIFO_SIZE:912return GetFifoSize(handle);913}914dmacHw_ASSERT(0);915return 0;916}917918919