added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_K66F/drivers/fsl_edma.c
- Committer:
- JojoS
- Date:
- 2016-09-10
- Revision:
- 147:ba84b7dc41a7
- Parent:
- 144:ef7eb2e8f9f7
File content as of revision 147:ba84b7dc41a7:
/* * Copyright (c) 2015, Freescale Semiconductor, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * o Neither the name of Freescale Semiconductor, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "fsl_edma.h" /******************************************************************************* * Definitions ******************************************************************************/ #define EDMA_TRANSFER_ENABLED_MASK 0x80U /******************************************************************************* * Prototypes ******************************************************************************/ /*! * @brief Get instance number for EDMA. * * @param base EDMA peripheral base address. */ static uint32_t EDMA_GetInstance(DMA_Type *base); /*! * @brief Push content of TCD structure into hardware TCD register. * * @param base EDMA peripheral base address. * @param channel EDMA channel number. * @param tcd Point to TCD structure. */ static void EDMA_InstallTCD(DMA_Type *base, uint32_t channel, edma_tcd_t *tcd); /******************************************************************************* * Variables ******************************************************************************/ /*! @brief Array to map EDMA instance number to base pointer. */ static DMA_Type *const s_edmaBases[] = DMA_BASE_PTRS; /*! @brief Array to map EDMA instance number to clock name. */ static const clock_ip_name_t s_edmaClockName[] = EDMA_CLOCKS; /*! @brief Array to map EDMA instance number to IRQ number. */ static const IRQn_Type s_edmaIRQNumber[] = DMA_CHN_IRQS; /*! @brief Pointers to transfer handle for each EDMA channel. */ static edma_handle_t *s_EDMAHandle[FSL_FEATURE_EDMA_MODULE_CHANNEL * FSL_FEATURE_SOC_EDMA_COUNT]; /******************************************************************************* * Code ******************************************************************************/ static uint32_t EDMA_GetInstance(DMA_Type *base) { uint32_t instance; /* Find the instance index from base address mappings. */ for (instance = 0; instance < FSL_FEATURE_SOC_EDMA_COUNT; instance++) { if (s_edmaBases[instance] == base) { break; } } assert(instance < FSL_FEATURE_SOC_EDMA_COUNT); return instance; } static void EDMA_InstallTCD(DMA_Type *base, uint32_t channel, edma_tcd_t *tcd) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); assert(tcd != NULL); assert(((uint32_t)tcd & 0x1FU) == 0); /* Push tcd into hardware TCD register */ base->TCD[channel].SADDR = tcd->SADDR; base->TCD[channel].SOFF = tcd->SOFF; base->TCD[channel].ATTR = tcd->ATTR; base->TCD[channel].NBYTES_MLNO = tcd->NBYTES; base->TCD[channel].SLAST = tcd->SLAST; base->TCD[channel].DADDR = tcd->DADDR; base->TCD[channel].DOFF = tcd->DOFF; base->TCD[channel].CITER_ELINKNO = tcd->CITER; base->TCD[channel].DLAST_SGA = tcd->DLAST_SGA; /* Clear DONE bit first, otherwise ESG cannot be set */ base->TCD[channel].CSR = 0; base->TCD[channel].CSR = tcd->CSR; base->TCD[channel].BITER_ELINKNO = tcd->BITER; } void EDMA_Init(DMA_Type *base, const edma_config_t *config) { assert(config != NULL); uint32_t tmpreg; /* Ungate EDMA periphral clock */ CLOCK_EnableClock(s_edmaClockName[EDMA_GetInstance(base)]); /* Configure EDMA peripheral according to the configuration structure. */ tmpreg = base->CR; tmpreg &= ~(DMA_CR_ERCA_MASK | DMA_CR_HOE_MASK | DMA_CR_CLM_MASK | DMA_CR_EDBG_MASK); tmpreg |= (DMA_CR_ERCA(config->enableRoundRobinArbitration) | DMA_CR_HOE(config->enableHaltOnError) | DMA_CR_CLM(config->enableContinuousLinkMode) | DMA_CR_EDBG(config->enableDebugMode) | DMA_CR_EMLM(true)); base->CR = tmpreg; } void EDMA_Deinit(DMA_Type *base) { /* Gate EDMA periphral clock */ CLOCK_DisableClock(s_edmaClockName[EDMA_GetInstance(base)]); } void EDMA_GetDefaultConfig(edma_config_t *config) { assert(config != NULL); config->enableRoundRobinArbitration = false; config->enableHaltOnError = true; config->enableContinuousLinkMode = false; config->enableDebugMode = false; } void EDMA_ResetChannel(DMA_Type *base, uint32_t channel) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); EDMA_TcdReset((edma_tcd_t *)&base->TCD[channel]); } void EDMA_SetTransferConfig(DMA_Type *base, uint32_t channel, const edma_transfer_config_t *config, edma_tcd_t *nextTcd) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); assert(config != NULL); assert(((uint32_t)nextTcd & 0x1FU) == 0); EDMA_TcdSetTransferConfig((edma_tcd_t *)&base->TCD[channel], config, nextTcd); } void EDMA_SetMinorOffsetConfig(DMA_Type *base, uint32_t channel, const edma_minor_offset_config_t *config) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); assert(config != NULL); uint32_t tmpreg; tmpreg = base->TCD[channel].NBYTES_MLOFFYES; tmpreg &= ~(DMA_NBYTES_MLOFFYES_SMLOE_MASK | DMA_NBYTES_MLOFFYES_DMLOE_MASK | DMA_NBYTES_MLOFFYES_MLOFF_MASK); tmpreg |= (DMA_NBYTES_MLOFFYES_SMLOE(config->enableSrcMinorOffset) | DMA_NBYTES_MLOFFYES_DMLOE(config->enableDestMinorOffset) | DMA_NBYTES_MLOFFYES_MLOFF(config->minorOffset)); base->TCD[channel].NBYTES_MLOFFYES = tmpreg; } void EDMA_SetChannelLink(DMA_Type *base, uint32_t channel, edma_channel_link_type_t type, uint32_t linkedChannel) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); assert(linkedChannel < FSL_FEATURE_EDMA_MODULE_CHANNEL); EDMA_TcdSetChannelLink((edma_tcd_t *)&base->TCD[channel], type, linkedChannel); } void EDMA_SetBandWidth(DMA_Type *base, uint32_t channel, edma_bandwidth_t bandWidth) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); base->TCD[channel].CSR = (base->TCD[channel].CSR & (~DMA_CSR_BWC_MASK)) | DMA_CSR_BWC(bandWidth); } void EDMA_SetModulo(DMA_Type *base, uint32_t channel, edma_modulo_t srcModulo, edma_modulo_t destModulo) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); uint32_t tmpreg; tmpreg = base->TCD[channel].ATTR & (~(DMA_ATTR_SMOD_MASK | DMA_ATTR_DMOD_MASK)); base->TCD[channel].ATTR = tmpreg | DMA_ATTR_DMOD(destModulo) | DMA_ATTR_SMOD(srcModulo); } void EDMA_EnableChannelInterrupts(DMA_Type *base, uint32_t channel, uint32_t mask) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); /* Enable error interrupt */ if (mask & kEDMA_ErrorInterruptEnable) { base->EEI |= (0x1U << channel); } /* Enable Major interrupt */ if (mask & kEDMA_MajorInterruptEnable) { base->TCD[channel].CSR |= DMA_CSR_INTMAJOR_MASK; } /* Enable Half major interrupt */ if (mask & kEDMA_HalfInterruptEnable) { base->TCD[channel].CSR |= DMA_CSR_INTHALF_MASK; } } void EDMA_DisableChannelInterrupts(DMA_Type *base, uint32_t channel, uint32_t mask) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); /* Disable error interrupt */ if (mask & kEDMA_ErrorInterruptEnable) { base->EEI &= ~(0x1U << channel); } /* Disable Major interrupt */ if (mask & kEDMA_MajorInterruptEnable) { base->TCD[channel].CSR &= ~DMA_CSR_INTMAJOR_MASK; } /* Disable Half major interrupt */ if (mask & kEDMA_HalfInterruptEnable) { base->TCD[channel].CSR &= ~DMA_CSR_INTHALF_MASK; } } void EDMA_TcdReset(edma_tcd_t *tcd) { assert(tcd != NULL); assert(((uint32_t)tcd & 0x1FU) == 0); /* Reset channel TCD */ tcd->SADDR = 0U; tcd->SOFF = 0U; tcd->ATTR = 0U; tcd->NBYTES = 0U; tcd->SLAST = 0U; tcd->DADDR = 0U; tcd->DOFF = 0U; tcd->CITER = 0U; tcd->DLAST_SGA = 0U; /* Enable auto disable request feature */ tcd->CSR = DMA_CSR_DREQ(true); tcd->BITER = 0U; } void EDMA_TcdSetTransferConfig(edma_tcd_t *tcd, const edma_transfer_config_t *config, edma_tcd_t *nextTcd) { assert(tcd != NULL); assert(((uint32_t)tcd & 0x1FU) == 0); assert(config != NULL); assert(((uint32_t)nextTcd & 0x1FU) == 0); /* source address */ tcd->SADDR = config->srcAddr; /* destination address */ tcd->DADDR = config->destAddr; /* Source data and destination data transfer size */ tcd->ATTR = DMA_ATTR_SSIZE(config->srcTransferSize) | DMA_ATTR_DSIZE(config->destTransferSize); /* Source address signed offset */ tcd->SOFF = config->srcOffset; /* Destination address signed offset */ tcd->DOFF = config->destOffset; /* Minor byte transfer count */ tcd->NBYTES = config->minorLoopBytes; /* Current major iteration count */ tcd->CITER = config->majorLoopCounts; /* Starting major iteration count */ tcd->BITER = config->majorLoopCounts; /* Enable scatter/gather processing */ if (nextTcd != NULL) { tcd->DLAST_SGA = (uint32_t)nextTcd; /* Before call EDMA_TcdSetTransferConfig or EDMA_SetTransferConfig, user must call EDMA_TcdReset or EDMA_ResetChannel which will set DREQ, so must use "|" or "&" rather than "=". Clear the DREQ bit because scatter gather has been enabled, so the previous transfer is not the last transfer, and channel request should be enabled at the next transfer(the next TCD). */ tcd->CSR = (tcd->CSR | DMA_CSR_ESG_MASK) & ~DMA_CSR_DREQ_MASK; } } void EDMA_TcdSetMinorOffsetConfig(edma_tcd_t *tcd, const edma_minor_offset_config_t *config) { assert(tcd != NULL); assert(((uint32_t)tcd & 0x1FU) == 0); uint32_t tmpreg; tmpreg = tcd->NBYTES & ~(DMA_NBYTES_MLOFFYES_SMLOE_MASK | DMA_NBYTES_MLOFFYES_DMLOE_MASK | DMA_NBYTES_MLOFFYES_MLOFF_MASK); tmpreg |= (DMA_NBYTES_MLOFFYES_SMLOE(config->enableSrcMinorOffset) | DMA_NBYTES_MLOFFYES_DMLOE(config->enableDestMinorOffset) | DMA_NBYTES_MLOFFYES_MLOFF(config->minorOffset)); tcd->NBYTES = tmpreg; } void EDMA_TcdSetChannelLink(edma_tcd_t *tcd, edma_channel_link_type_t type, uint32_t linkedChannel) { assert(tcd != NULL); assert(((uint32_t)tcd & 0x1FU) == 0); assert(linkedChannel < FSL_FEATURE_EDMA_MODULE_CHANNEL); if (type == kEDMA_MinorLink) /* Minor link config */ { uint32_t tmpreg; /* Enable minor link */ tcd->CITER |= DMA_CITER_ELINKYES_ELINK_MASK; tcd->BITER |= DMA_BITER_ELINKYES_ELINK_MASK; /* Set likned channel */ tmpreg = tcd->CITER & (~DMA_CITER_ELINKYES_LINKCH_MASK); tmpreg |= DMA_CITER_ELINKYES_LINKCH(linkedChannel); tcd->CITER = tmpreg; tmpreg = tcd->BITER & (~DMA_BITER_ELINKYES_LINKCH_MASK); tmpreg |= DMA_BITER_ELINKYES_LINKCH(linkedChannel); tcd->BITER = tmpreg; } else if (type == kEDMA_MajorLink) /* Major link config */ { uint32_t tmpreg; /* Enable major link */ tcd->CSR |= DMA_CSR_MAJORELINK_MASK; /* Set major linked channel */ tmpreg = tcd->CSR & (~DMA_CSR_MAJORLINKCH_MASK); tcd->CSR = tmpreg | DMA_CSR_MAJORLINKCH(linkedChannel); } else /* Link none */ { tcd->CITER &= ~DMA_CITER_ELINKYES_ELINK_MASK; tcd->BITER &= ~DMA_BITER_ELINKYES_ELINK_MASK; tcd->CSR &= ~DMA_CSR_MAJORELINK_MASK; } } void EDMA_TcdSetModulo(edma_tcd_t *tcd, edma_modulo_t srcModulo, edma_modulo_t destModulo) { assert(tcd != NULL); assert(((uint32_t)tcd & 0x1FU) == 0); uint32_t tmpreg; tmpreg = tcd->ATTR & (~(DMA_ATTR_SMOD_MASK | DMA_ATTR_DMOD_MASK)); tcd->ATTR = tmpreg | DMA_ATTR_DMOD(destModulo) | DMA_ATTR_SMOD(srcModulo); } void EDMA_TcdEnableInterrupts(edma_tcd_t *tcd, uint32_t mask) { assert(tcd != NULL); /* Enable Major interrupt */ if (mask & kEDMA_MajorInterruptEnable) { tcd->CSR |= DMA_CSR_INTMAJOR_MASK; } /* Enable Half major interrupt */ if (mask & kEDMA_HalfInterruptEnable) { tcd->CSR |= DMA_CSR_INTHALF_MASK; } } void EDMA_TcdDisableInterrupts(edma_tcd_t *tcd, uint32_t mask) { assert(tcd != NULL); /* Disable Major interrupt */ if (mask & kEDMA_MajorInterruptEnable) { tcd->CSR &= ~DMA_CSR_INTMAJOR_MASK; } /* Disable Half major interrupt */ if (mask & kEDMA_HalfInterruptEnable) { tcd->CSR &= ~DMA_CSR_INTHALF_MASK; } } uint32_t EDMA_GetRemainingBytes(DMA_Type *base, uint32_t channel) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); uint32_t nbytes = 0; uint32_t remainingBytes = 0; if (DMA_CSR_DONE_MASK & base->TCD[channel].CSR) { remainingBytes = 0; } else { /* Calculate the nbytes */ if (base->TCD[channel].NBYTES_MLOFFYES & (DMA_NBYTES_MLOFFYES_SMLOE_MASK | DMA_NBYTES_MLOFFYES_DMLOE_MASK)) { nbytes = (base->TCD[channel].NBYTES_MLOFFYES & DMA_NBYTES_MLOFFYES_NBYTES_MASK) >> DMA_NBYTES_MLOFFYES_NBYTES_SHIFT; } else { nbytes = (base->TCD[channel].NBYTES_MLOFFNO & DMA_NBYTES_MLOFFNO_NBYTES_MASK) >> DMA_NBYTES_MLOFFNO_NBYTES_SHIFT; } /* Calculate the unfinished bytes */ if (base->TCD[channel].CITER_ELINKNO & DMA_CITER_ELINKNO_ELINK_MASK) { remainingBytes = ((base->TCD[channel].CITER_ELINKYES & DMA_CITER_ELINKYES_CITER_MASK) >> DMA_CITER_ELINKYES_CITER_SHIFT) * nbytes; } else { remainingBytes = ((base->TCD[channel].CITER_ELINKNO & DMA_CITER_ELINKNO_CITER_MASK) >> DMA_CITER_ELINKNO_CITER_SHIFT) * nbytes; } } return remainingBytes; } uint32_t EDMA_GetChannelStatusFlags(DMA_Type *base, uint32_t channel) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); uint32_t retval = 0; /* Get DONE bit flag */ retval |= ((base->TCD[channel].CSR & DMA_CSR_DONE_MASK) >> DMA_CSR_DONE_SHIFT); /* Get ERROR bit flag */ retval |= (((base->ERR >> channel) & 0x1U) << 1U); /* Get INT bit flag */ retval |= (((base->INT >> channel) & 0x1U) << 2U); return retval; } void EDMA_ClearChannelStatusFlags(DMA_Type *base, uint32_t channel, uint32_t mask) { assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); /* Clear DONE bit flag */ if (mask & kEDMA_DoneFlag) { base->CDNE = channel; } /* Clear ERROR bit flag */ if (mask & kEDMA_ErrorFlag) { base->CERR = channel; } /* Clear INT bit flag */ if (mask & kEDMA_InterruptFlag) { base->CINT = channel; } } void EDMA_CreateHandle(edma_handle_t *handle, DMA_Type *base, uint32_t channel) { assert(handle != NULL); assert(channel < FSL_FEATURE_EDMA_MODULE_CHANNEL); uint32_t edmaInstance; uint32_t channelIndex; edma_tcd_t *tcdRegs; handle->base = base; handle->channel = channel; /* Get the DMA instance number */ edmaInstance = EDMA_GetInstance(base); channelIndex = (edmaInstance * FSL_FEATURE_EDMA_MODULE_CHANNEL) + channel; s_EDMAHandle[channelIndex] = handle; /* Enable NVIC interrupt */ EnableIRQ(s_edmaIRQNumber[channelIndex]); /* Reset TCD registers to zero. Unlike the EDMA_TcdReset(DREQ will be set), CSR will be 0. Because in order to suit EDMA busy check mechanism in EDMA_SubmitTransfer, CSR must be set 0. */ tcdRegs = (edma_tcd_t *)&handle->base->TCD[handle->channel]; tcdRegs->SADDR = 0; tcdRegs->SOFF = 0; tcdRegs->ATTR = 0; tcdRegs->NBYTES = 0; tcdRegs->SLAST = 0; tcdRegs->DADDR = 0; tcdRegs->DOFF = 0; tcdRegs->CITER = 0; tcdRegs->DLAST_SGA = 0; tcdRegs->CSR = 0; tcdRegs->BITER = 0; } void EDMA_InstallTCDMemory(edma_handle_t *handle, edma_tcd_t *tcdPool, uint32_t tcdSize) { assert(handle != NULL); assert(((uint32_t)tcdPool & 0x1FU) == 0); /* Initialize tcd queue attibute. */ handle->header = 0; handle->tail = 0; handle->tcdUsed = 0; handle->tcdSize = tcdSize; handle->flags = 0; handle->tcdPool = tcdPool; } void EDMA_SetCallback(edma_handle_t *handle, edma_callback callback, void *userData) { assert(handle != NULL); handle->callback = callback; handle->userData = userData; } void EDMA_PrepareTransfer(edma_transfer_config_t *config, void *srcAddr, uint32_t srcWidth, void *destAddr, uint32_t destWidth, uint32_t bytesEachRequest, uint32_t transferBytes, edma_transfer_type_t type) { assert(config != NULL); assert(srcAddr != NULL); assert(destAddr != NULL); assert(srcWidth == 1U || srcWidth == 2U || srcWidth == 4U || srcWidth == 16U || srcWidth == 32U); assert(destWidth == 1U || destWidth == 2U || destWidth == 4U || destWidth == 16U || destWidth == 32U); assert(transferBytes % bytesEachRequest == 0); config->destAddr = (uint32_t)destAddr; config->srcAddr = (uint32_t)srcAddr; config->minorLoopBytes = bytesEachRequest; config->majorLoopCounts = transferBytes / bytesEachRequest; switch (srcWidth) { case 1U: config->srcTransferSize = kEDMA_TransferSize1Bytes; break; case 2U: config->srcTransferSize = kEDMA_TransferSize2Bytes; break; case 4U: config->srcTransferSize = kEDMA_TransferSize4Bytes; break; case 16U: config->srcTransferSize = kEDMA_TransferSize16Bytes; break; case 32U: config->srcTransferSize = kEDMA_TransferSize32Bytes; break; default: break; } switch (destWidth) { case 1U: config->destTransferSize = kEDMA_TransferSize1Bytes; break; case 2U: config->destTransferSize = kEDMA_TransferSize2Bytes; break; case 4U: config->destTransferSize = kEDMA_TransferSize4Bytes; break; case 16U: config->destTransferSize = kEDMA_TransferSize16Bytes; break; case 32U: config->destTransferSize = kEDMA_TransferSize32Bytes; break; default: break; } switch (type) { case kEDMA_MemoryToMemory: config->destOffset = destWidth; config->srcOffset = srcWidth; break; case kEDMA_MemoryToPeripheral: config->destOffset = 0U; config->srcOffset = srcWidth; break; case kEDMA_PeripheralToMemory: config->destOffset = destWidth; config->srcOffset = 0U; break; default: break; } } status_t EDMA_SubmitTransfer(edma_handle_t *handle, const edma_transfer_config_t *config) { assert(handle != NULL); assert(config != NULL); edma_tcd_t *tcdRegs = (edma_tcd_t *)&handle->base->TCD[handle->channel]; if (handle->tcdPool == NULL) { /* Check if EDMA is busy: if the given channel started transfer, CSR will be not zero. Because if it is the last transfer, DREQ will be set. If not, ESG will be set. So in order to suit this check mechanism, EDMA_CreatHandle will clear CSR register. */ if ((tcdRegs->CSR != 0) && ((tcdRegs->CSR & DMA_CSR_DONE_MASK) == 0)) { return kStatus_EDMA_Busy; } else { EDMA_SetTransferConfig(handle->base, handle->channel, config, NULL); /* Enable auto disable request feature */ handle->base->TCD[handle->channel].CSR |= DMA_CSR_DREQ_MASK; /* Enable major interrupt */ handle->base->TCD[handle->channel].CSR |= DMA_CSR_INTMAJOR_MASK; return kStatus_Success; } } else /* Use the TCD queue. */ { uint32_t primask; uint32_t csr; int8_t currentTcd; int8_t previousTcd; int8_t nextTcd; /* Check if tcd pool is full. */ primask = DisableGlobalIRQ(); if (handle->tcdUsed >= handle->tcdSize) { EnableGlobalIRQ(primask); return kStatus_EDMA_QueueFull; } currentTcd = handle->tail; handle->tcdUsed++; /* Calculate index of next TCD */ nextTcd = currentTcd + 1U; if (nextTcd == handle->tcdSize) { nextTcd = 0U; } /* Advance queue tail index */ handle->tail = nextTcd; EnableGlobalIRQ(primask); /* Calculate index of previous TCD */ previousTcd = currentTcd ? currentTcd - 1U : handle->tcdSize - 1U; /* Configure current TCD block. */ EDMA_TcdReset(&handle->tcdPool[currentTcd]); EDMA_TcdSetTransferConfig(&handle->tcdPool[currentTcd], config, NULL); /* Enable major interrupt */ handle->tcdPool[currentTcd].CSR |= DMA_CSR_INTMAJOR_MASK; /* Link current TCD with next TCD for identification of current TCD */ handle->tcdPool[currentTcd].DLAST_SGA = (uint32_t)&handle->tcdPool[nextTcd]; /* Chain from previous descriptor unless tcd pool size is 1(this descriptor is its own predecessor). */ if (currentTcd != previousTcd) { /* Enable scatter/gather feature in the previous TCD block. */ csr = (handle->tcdPool[previousTcd].CSR | DMA_CSR_ESG_MASK) & ~DMA_CSR_DREQ_MASK; handle->tcdPool[previousTcd].CSR = csr; /* Check if the TCD blcok in the registers is the previous one (points to current TCD block). It is used to check if the previous TCD linked has been loaded in TCD register. If so, it need to link the TCD register in case link the current TCD with the dead chain when TCD loading occurs before link the previous TCD block. */ if (tcdRegs->DLAST_SGA == (uint32_t)&handle->tcdPool[currentTcd]) { /* Enable scatter/gather also in the TCD registers. */ csr = (tcdRegs->CSR | DMA_CSR_ESG_MASK) & ~DMA_CSR_DREQ_MASK; /* Must write the CSR register one-time, because the transfer maybe finished anytime. */ tcdRegs->CSR = csr; /* It is very important to check the ESG bit! Because this hardware design: if DONE bit is set, the ESG bit can not be set. So it can be used to check if the dynamic TCD link operation is successful. If ESG bit is not set and the DLAST_SGA is not the next TCD address(it means the dynamic TCD link succeed and the current TCD block has been loaded into TCD registers), it means transfer finished and TCD link operation fail, so must install TCD content into TCD registers and enable transfer again. And if ESG is set, it means transfer has notfinished, so TCD dynamic link succeed. */ if (tcdRegs->CSR & DMA_CSR_ESG_MASK) { return kStatus_Success; } /* Check whether the current TCD block is already loaded in the TCD registers. It is another condition when ESG bit is not set: it means the dynamic TCD link succeed and the current TCD block has been loaded into TCD registers. */ if (tcdRegs->DLAST_SGA == (uint32_t)&handle->tcdPool[nextTcd]) { return kStatus_Success; } /* If go to this, means the previous transfer finished, and the DONE bit is set. So shall configure TCD registers. */ } else if (tcdRegs->DLAST_SGA != 0) { /* The current TCD block has been linked successfully. */ return kStatus_Success; } else { /* DLAST_SGA is 0 and it means the first submit transfer, so shall configure TCD registers. */ } } /* There is no live chain, TCD block need to be installed in TCD registers. */ EDMA_InstallTCD(handle->base, handle->channel, &handle->tcdPool[currentTcd]); /* Enable channel request again. */ if (handle->flags & EDMA_TRANSFER_ENABLED_MASK) { handle->base->SERQ = DMA_SERQ_SERQ(handle->channel); } return kStatus_Success; } } void EDMA_StartTransfer(edma_handle_t *handle) { assert(handle != NULL); if (handle->tcdPool == NULL) { handle->base->SERQ = DMA_SERQ_SERQ(handle->channel); } else /* Use the TCD queue. */ { uint32_t primask; edma_tcd_t *tcdRegs = (edma_tcd_t *)&handle->base->TCD[handle->channel]; handle->flags |= EDMA_TRANSFER_ENABLED_MASK; /* Check if there was at least one descriptor submitted since reset (TCD in registers is valid) */ if (tcdRegs->DLAST_SGA != 0U) { primask = DisableGlobalIRQ(); /* Check if channel request is actually disable. */ if ((handle->base->ERQ & (1U << handle->channel)) == 0U) { /* Check if transfer is paused. */ if ((!(tcdRegs->CSR & DMA_CSR_DONE_MASK)) || (tcdRegs->CSR & DMA_CSR_ESG_MASK)) { /* Re-enable channel request must be as soon as possible, so must put it into critical section to avoid task switching or interrupt service routine. */ handle->base->SERQ = DMA_SERQ_SERQ(handle->channel); } } EnableGlobalIRQ(primask); } } } void EDMA_StopTransfer(edma_handle_t *handle) { assert(handle != NULL); handle->flags &= (~EDMA_TRANSFER_ENABLED_MASK); handle->base->CERQ = DMA_CERQ_CERQ(handle->channel); } void EDMA_AbortTransfer(edma_handle_t *handle) { handle->base->CERQ = DMA_CERQ_CERQ(handle->channel); /* Clear CSR to release channel. Because if the given channel started transfer, CSR will be not zero. Because if it is the last transfer, DREQ will be set. If not, ESG will be set. */ handle->base->TCD[handle->channel].CSR = 0; /* Cancel all next TCD transfer. */ handle->base->TCD[handle->channel].DLAST_SGA = 0; } void EDMA_HandleIRQ(edma_handle_t *handle) { assert(handle != NULL); /* Clear EDMA interrupt flag */ handle->base->CINT = handle->channel; if (handle->tcdPool == NULL) { (handle->callback)(handle, handle->userData, true, 0); } else /* Use the TCD queue. */ { uint32_t sga = handle->base->TCD[handle->channel].DLAST_SGA; uint32_t sga_index; int32_t tcds_done; uint8_t new_header; bool transfer_done; /* Check if transfer is already finished. */ transfer_done = ((handle->base->TCD[handle->channel].CSR & DMA_CSR_DONE_MASK) != 0); /* Get the offset of the current transfer TCD blcoks. */ sga -= (uint32_t)handle->tcdPool; /* Get the index of the current transfer TCD blcoks. */ sga_index = sga / sizeof(edma_tcd_t); /* Adjust header positions. */ if (transfer_done) { /* New header shall point to the next TCD (current one is already finished) */ new_header = sga_index; } else { /* New header shall point to this descriptor (not finished yet) */ new_header = sga_index ? sga_index - 1U : handle->tcdSize - 1U; } /* Calculate the number of finished TCDs */ if (new_header == handle->header) { if (handle->tcdUsed == handle->tcdSize) { tcds_done = handle->tcdUsed; } else { /* Internal error occurs. */ tcds_done = 0; } } else { tcds_done = new_header - handle->header; if (tcds_done < 0) { tcds_done += handle->tcdSize; } } /* Advance header to the point beyond the last finished TCD block. */ handle->header = new_header; /* Release TCD blocks. */ handle->tcdUsed -= tcds_done; /* Invoke callback function. */ if (handle->callback) { (handle->callback)(handle, handle->userData, transfer_done, tcds_done); } } } /* 8 channels (Shared): kl28 */ #if defined(FSL_FEATURE_EDMA_MODULE_CHANNEL) && FSL_FEATURE_EDMA_MODULE_CHANNEL == 8U void DMA0_04_DriverIRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 0U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[0]); } if ((EDMA_GetChannelStatusFlags(DMA0, 4U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[4]); } } void DMA0_15_DriverIRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 1U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[1]); } if ((EDMA_GetChannelStatusFlags(DMA0, 5U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[5]); } } void DMA0_26_DriverIRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 2U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[2]); } if ((EDMA_GetChannelStatusFlags(DMA0, 6U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[6]); } } void DMA0_37_DriverIRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 3U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[3]); } if ((EDMA_GetChannelStatusFlags(DMA0, 7U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[7]); } } #endif /* 8 channels (Shared) */ /* 32 channels (Shared): k80 */ #if defined(FSL_FEATURE_EDMA_MODULE_CHANNEL) && FSL_FEATURE_EDMA_MODULE_CHANNEL == 32U void DMA0_DMA16_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 0U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[0]); } if ((EDMA_GetChannelStatusFlags(DMA0, 16U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[16]); } } void DMA1_DMA17_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 1U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[1]); } if ((EDMA_GetChannelStatusFlags(DMA0, 17U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[17]); } } void DMA2_DMA18_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 2U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[2]); } if ((EDMA_GetChannelStatusFlags(DMA0, 18U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[18]); } } void DMA3_DMA19_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 3U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[3]); } if ((EDMA_GetChannelStatusFlags(DMA0, 19U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[19]); } } void DMA4_DMA20_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 4U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[4]); } if ((EDMA_GetChannelStatusFlags(DMA0, 20U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[20]); } } void DMA5_DMA21_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 5U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[5]); } if ((EDMA_GetChannelStatusFlags(DMA0, 21U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[21]); } } void DMA6_DMA22_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 6U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[6]); } if ((EDMA_GetChannelStatusFlags(DMA0, 22U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[22]); } } void DMA7_DMA23_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 7U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[7]); } if ((EDMA_GetChannelStatusFlags(DMA0, 23U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[23]); } } void DMA8_DMA24_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 8U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[8]); } if ((EDMA_GetChannelStatusFlags(DMA0, 24U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[24]); } } void DMA9_DMA25_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 9U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[9]); } if ((EDMA_GetChannelStatusFlags(DMA0, 25U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[25]); } } void DMA10_DMA26_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 10U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[10]); } if ((EDMA_GetChannelStatusFlags(DMA0, 26U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[26]); } } void DMA11_DMA27_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 11U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[11]); } if ((EDMA_GetChannelStatusFlags(DMA0, 27U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[27]); } } void DMA12_DMA28_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 12U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[12]); } if ((EDMA_GetChannelStatusFlags(DMA0, 28U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[28]); } } void DMA13_DMA29_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 13U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[13]); } if ((EDMA_GetChannelStatusFlags(DMA0, 29U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[29]); } } void DMA14_DMA30_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 14U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[14]); } if ((EDMA_GetChannelStatusFlags(DMA0, 30U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[30]); } } void DMA15_DMA31_IRQHandler(void) { if ((EDMA_GetChannelStatusFlags(DMA0, 15U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[15]); } if ((EDMA_GetChannelStatusFlags(DMA0, 31U) & kEDMA_InterruptFlag) != 0U) { EDMA_HandleIRQ(s_EDMAHandle[31]); } } #endif /* 32 channels (Shared) */ /* 4 channels (No Shared): kv10 */ #if defined(FSL_FEATURE_EDMA_MODULE_CHANNEL) && FSL_FEATURE_EDMA_MODULE_CHANNEL > 0 void DMA0_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[0]); } void DMA1_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[1]); } void DMA2_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[2]); } void DMA3_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[3]); } /* 8 channels (No Shared) */ #if defined(FSL_FEATURE_EDMA_MODULE_CHANNEL) && FSL_FEATURE_EDMA_MODULE_CHANNEL > 4U void DMA4_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[4]); } void DMA5_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[5]); } void DMA6_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[6]); } void DMA7_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[7]); } #endif /* FSL_FEATURE_EDMA_MODULE_CHANNEL == 8 */ /* 16 channels (No Shared) */ #if defined(FSL_FEATURE_EDMA_MODULE_CHANNEL) && FSL_FEATURE_EDMA_MODULE_CHANNEL > 8U void DMA8_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[8]); } void DMA9_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[9]); } void DMA10_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[10]); } void DMA11_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[11]); } void DMA12_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[12]); } void DMA13_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[13]); } void DMA14_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[14]); } void DMA15_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[15]); } #endif /* FSL_FEATURE_EDMA_MODULE_CHANNEL == 16 */ /* 32 channels (No Shared) */ #if defined(FSL_FEATURE_EDMA_MODULE_CHANNEL) && FSL_FEATURE_EDMA_MODULE_CHANNEL > 16U void DMA16_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[16]); } void DMA17_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[17]); } void DMA18_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[18]); } void DMA19_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[19]); } void DMA20_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[20]); } void DMA21_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[21]); } void DMA22_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[22]); } void DMA23_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[23]); } void DMA24_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[24]); } void DMA25_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[25]); } void DMA26_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[26]); } void DMA27_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[27]); } void DMA28_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[28]); } void DMA29_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[29]); } void DMA30_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[30]); } void DMA31_DriverIRQHandler(void) { EDMA_HandleIRQ(s_EDMAHandle[31]); } #endif /* FSL_FEATURE_EDMA_MODULE_CHANNEL == 32 */ #endif /* 4/8/16/32 channels (No Shared) */