void dmacHw_initDma(void) { uint32_t i = 0; dmaChannelCount_0 = dmacHw_GET_NUM_CHANNEL(0); dmaChannelCount_1 = dmacHw_GET_NUM_CHANNEL(1); chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC0); chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC1); if ((dmaChannelCount_0 + dmaChannelCount_1) > dmacHw_MAX_CHANNEL_COUNT) { dmacHw_ASSERT(0); } memset((void *)dmacHw_gCblk, 0, sizeof(dmacHw_CBLK_t) * (dmaChannelCount_0 + dmaChannelCount_1)); for (i = 0; i < dmaChannelCount_0; i++) { dmacHw_gCblk[i].module = 0; dmacHw_gCblk[i].channel = i; } for (i = 0; i < dmaChannelCount_1; i++) { dmacHw_gCblk[i + dmaChannelCount_0].module = 1; dmacHw_gCblk[i + dmaChannelCount_0].channel = i; } }
int dmacHw_readTransferredData(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */ dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */ void *pDescriptor, /* [ IN ] Descriptor buffer */ void **ppBbuf, /* [ OUT ] Data received */ size_t *pLlen /* [ OUT ] Length of the data received */ ) { dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor); (void)handle; if (pConfig->transferMode != dmacHw_TRANSFER_MODE_CONTINUOUS) { if (((pRing->pTail->ctl.hi & dmacHw_DESC_FREE) == 0) || (pRing->pTail == pRing->pHead) ) { /* No receive data available */ *ppBbuf = (char *)NULL; *pLlen = 0; return 0; } } /* Return read buffer and length */ *ppBbuf = (char *)pRing->pTail->dar; /* Extract length of the received data */ if (DmaIsFlowController(pDescriptor)) { uint32_t srcTrSize = 0; switch (pRing->pTail->ctl.lo & dmacHw_REG_CTL_SRC_TR_WIDTH_MASK) { case dmacHw_REG_CTL_SRC_TR_WIDTH_8: srcTrSize = 1; break; case dmacHw_REG_CTL_SRC_TR_WIDTH_16: srcTrSize = 2; break; case dmacHw_REG_CTL_SRC_TR_WIDTH_32: srcTrSize = 4; break; case dmacHw_REG_CTL_SRC_TR_WIDTH_64: srcTrSize = 8; break; default: dmacHw_ASSERT(0); } /* Calculate length from the block size */ *pLlen = (pRing->pTail->ctl.hi & dmacHw_REG_CTL_BLOCK_TS_MASK) * srcTrSize; } else { /* Extract length from the source peripheral */ *pLlen = pRing->pTail->sstat; } /* Advance tail to next descriptor */ dmacHw_NEXT_DESC(pRing, pTail); return 1; }
int dmacHw_initDescriptor(void *pDescriptorVirt, /* [ IN ] Virtual address of uncahced buffer allocated to form descriptor ring */ uint32_t descriptorPhyAddr, /* [ IN ] Physical address of pDescriptorVirt (descriptor buffer) */ uint32_t len, /* [ IN ] Size of the pBuf */ uint32_t num /* [ IN ] Number of descriptor in the ring */ ) { uint32_t i; dmacHw_DESC_RING_t *pRing; dmacHw_DESC_t *pDesc; /* Check the alignment of the descriptor */ if ((uint32_t) pDescriptorVirt & 0x00000003) { dmacHw_ASSERT(0); return -1; } /* Check if enough space has been allocated for descriptor ring */ if (len < dmacHw_descriptorLen(num)) { return -1; } pRing = dmacHw_GET_DESC_RING(pDescriptorVirt); pRing->pHead = (dmacHw_DESC_t *) ((uint32_t) pRing + sizeof(dmacHw_DESC_RING_t)); pRing->pFree = pRing->pTail = pRing->pEnd = pRing->pHead; pRing->pProg = dmacHw_DESC_INIT; /* Initialize link item chain, starting from the head */ pDesc = pRing->pHead; /* Find the offset between virtual to physical address */ pRing->virt2PhyOffset = (uint32_t) pDescriptorVirt - descriptorPhyAddr; /* Form the descriptor ring */ for (i = 0; i < num - 1; i++) { /* Clear link list item */ memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t)); /* Point to the next item in the physical address */ pDesc->llpPhy = (uint32_t) (pDesc + 1) - pRing->virt2PhyOffset; /* Point to the next item in the virtual address */ pDesc->llp = (uint32_t) (pDesc + 1); /* Mark descriptor is ready to use */ pDesc->ctl.hi = dmacHw_DESC_FREE; /* Look into next link list item */ pDesc++; } /* Clear last link list item */ memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t)); /* Last item pointing to the first item in the physical address to complete the ring */ pDesc->llpPhy = (uint32_t) pRing->pHead - pRing->virt2PhyOffset; /* Last item pointing to the first item in the virtual address to complete the ring */ pDesc->llp = (uint32_t) pRing->pHead; /* Mark descriptor is ready to use */ pDesc->ctl.hi = dmacHw_DESC_FREE; /* Set the number of descriptors in the ring */ pRing->num = num; return 0; }
int dmacHw_readTransferredData(dmacHw_HANDLE_t handle, dmacHw_CONFIG_t *pConfig, void *pDescriptor, void **ppBbuf, size_t *pLlen ) { dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor); (void)handle; if (pConfig->transferMode != dmacHw_TRANSFER_MODE_CONTINUOUS) { if (((pRing->pTail->ctl.hi & dmacHw_DESC_FREE) == 0) || (pRing->pTail == pRing->pHead) ) { *ppBbuf = (char *)NULL; *pLlen = 0; return 0; } } *ppBbuf = (char *)pRing->pTail->dar; if (DmaIsFlowController(pDescriptor)) { uint32_t srcTrSize = 0; switch (pRing->pTail->ctl.lo & dmacHw_REG_CTL_SRC_TR_WIDTH_MASK) { case dmacHw_REG_CTL_SRC_TR_WIDTH_8: srcTrSize = 1; break; case dmacHw_REG_CTL_SRC_TR_WIDTH_16: srcTrSize = 2; break; case dmacHw_REG_CTL_SRC_TR_WIDTH_32: srcTrSize = 4; break; case dmacHw_REG_CTL_SRC_TR_WIDTH_64: srcTrSize = 8; break; default: dmacHw_ASSERT(0); } *pLlen = (pRing->pTail->ctl.hi & dmacHw_REG_CTL_BLOCK_TS_MASK) * srcTrSize; } else { *pLlen = pRing->pTail->sstat; } dmacHw_NEXT_DESC(pRing, pTail); return 1; }
int dmacHw_initDescriptor(void *pDescriptorVirt, uint32_t descriptorPhyAddr, uint32_t len, uint32_t num ) { uint32_t i; dmacHw_DESC_RING_t *pRing; dmacHw_DESC_t *pDesc; if ((uint32_t) pDescriptorVirt & 0x00000003) { dmacHw_ASSERT(0); return -1; } if (len < dmacHw_descriptorLen(num)) { return -1; } pRing = dmacHw_GET_DESC_RING(pDescriptorVirt); pRing->pHead = (dmacHw_DESC_t *) ((uint32_t) pRing + sizeof(dmacHw_DESC_RING_t)); pRing->pFree = pRing->pTail = pRing->pEnd = pRing->pHead; pRing->pProg = dmacHw_DESC_INIT; pDesc = pRing->pHead; pRing->virt2PhyOffset = (uint32_t) pDescriptorVirt - descriptorPhyAddr; for (i = 0; i < num - 1; i++) { memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t)); pDesc->llpPhy = (uint32_t) (pDesc + 1) - pRing->virt2PhyOffset; pDesc->llp = (uint32_t) (pDesc + 1); pDesc->ctl.hi = dmacHw_DESC_FREE; pDesc++; } memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t)); pDesc->llpPhy = (uint32_t) pRing->pHead - pRing->virt2PhyOffset; pDesc->llp = (uint32_t) pRing->pHead; pDesc->ctl.hi = dmacHw_DESC_FREE; pRing->num = num; return 0; }
dmacHw_HANDLE_t dmacHw_getChannelHandle(dmacHw_ID_t channelId ) { int idx; switch ((channelId >> 8)) { case 0: dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_0); idx = (channelId & 0xff); break; case 1: dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_1); idx = dmaChannelCount_0 + (channelId & 0xff); break; default: dmacHw_ASSERT(0); return (dmacHw_HANDLE_t) -1; } return dmacHw_CBLK_TO_HANDLE(&dmacHw_gCblk[idx]); }
static uint32_t GetFifoSize(dmacHw_HANDLE_t handle ) { uint32_t val = 0; dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle); dmacHw_MISC_t *pMiscReg = (dmacHw_MISC_t *) dmacHw_REG_MISC_BASE(pCblk->module); switch (pCblk->channel) { case 0: val = (pMiscReg->CompParm2.lo & 0x70000000) >> 28; break; case 1: val = (pMiscReg->CompParm3.hi & 0x70000000) >> 28; break; case 2: val = (pMiscReg->CompParm3.lo & 0x70000000) >> 28; break; case 3: val = (pMiscReg->CompParm4.hi & 0x70000000) >> 28; break; case 4: val = (pMiscReg->CompParm4.lo & 0x70000000) >> 28; break; case 5: val = (pMiscReg->CompParm5.hi & 0x70000000) >> 28; break; case 6: val = (pMiscReg->CompParm5.lo & 0x70000000) >> 28; break; case 7: val = (pMiscReg->CompParm6.hi & 0x70000000) >> 28; break; } if (val <= 0x4) { return 8 << val; } else { dmacHw_ASSERT(0); } return 0; }
uint32_t dmacHw_getDmaControllerAttribute(dmacHw_HANDLE_t handle, dmacHw_CONTROLLER_ATTRIB_e attr ) { dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle); switch (attr) { case dmacHw_CONTROLLER_ATTRIB_CHANNEL_NUM: return dmacHw_GET_NUM_CHANNEL(pCblk->module); case dmacHw_CONTROLLER_ATTRIB_CHANNEL_MAX_BLOCK_SIZE: return (1 << (dmacHw_GET_MAX_BLOCK_SIZE (pCblk->module, pCblk->module) + 2)) - 8; case dmacHw_CONTROLLER_ATTRIB_MASTER_INTF_NUM: return dmacHw_GET_NUM_INTERFACE(pCblk->module); case dmacHw_CONTROLLER_ATTRIB_CHANNEL_BUS_WIDTH: return 32 << dmacHw_GET_CHANNEL_DATA_WIDTH(pCblk->module, pCblk->channel); case dmacHw_CONTROLLER_ATTRIB_CHANNEL_FIFO_SIZE: return GetFifoSize(handle); } dmacHw_ASSERT(0); return 0; }
int dmacHw_setControlDescriptor(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */ void *pDescriptor, /* [ IN ] Descriptor buffer */ uint32_t ctlAddress, /* [ IN ] Address of the device control register */ uint32_t control /* [ IN ] Device control information */ ) { dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor); if (ctlAddress == 0) { return -1; } /* Check the availability of descriptors in the ring */ if ((pRing->pHead->ctl.hi & dmacHw_DESC_FREE) == 0) { return -1; } /* Set control information */ pRing->pHead->devCtl = control; /* Set source and destination address */ pRing->pHead->sar = (uint32_t) &pRing->pHead->devCtl; pRing->pHead->dar = ctlAddress; /* Set control parameters */ if (pConfig->flowControler == dmacHw_FLOW_CONTROL_DMA) { pRing->pHead->ctl.lo = pConfig->transferType | dmacHw_SRC_ADDRESS_UPDATE_MODE_INC | dmacHw_DST_ADDRESS_UPDATE_MODE_INC | dmacHw_SRC_TRANSACTION_WIDTH_32 | pConfig->dstMaxTransactionWidth | dmacHw_SRC_BURST_WIDTH_0 | dmacHw_DST_BURST_WIDTH_0 | pConfig->srcMasterInterface | pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN; } else { uint32_t transferType = 0; switch (pConfig->transferType) { case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM: transferType = dmacHw_REG_CTL_TTFC_PM_PERI; break; case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL: transferType = dmacHw_REG_CTL_TTFC_MP_PERI; break; default: dmacHw_ASSERT(0); } pRing->pHead->ctl.lo = transferType | dmacHw_SRC_ADDRESS_UPDATE_MODE_INC | dmacHw_DST_ADDRESS_UPDATE_MODE_INC | dmacHw_SRC_TRANSACTION_WIDTH_32 | pConfig->dstMaxTransactionWidth | dmacHw_SRC_BURST_WIDTH_0 | dmacHw_DST_BURST_WIDTH_0 | pConfig->srcMasterInterface | pConfig->dstMasterInterface | pConfig->flowControler | dmacHw_REG_CTL_INT_EN; } /* Set block transaction size to one 32 bit transaction */ pRing->pHead->ctl.hi = dmacHw_REG_CTL_BLOCK_TS_MASK & 1; /* Remember the descriptor to initialize the registers */ if (pRing->pProg == dmacHw_DESC_INIT) { pRing->pProg = pRing->pHead; } pRing->pEnd = pRing->pHead; /* Advance the descriptor */ dmacHw_NEXT_DESC(pRing, pHead); /* Update Tail pointer if destination is a peripheral */ if (!dmacHw_DST_IS_MEMORY(pConfig->transferType)) { pRing->pTail = pRing->pHead; } return 0; }
int dmacHw_setVariableDataDescriptor(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */ dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */ void *pDescriptor, /* [ IN ] Descriptor buffer */ uint32_t srcAddr, /* [ IN ] Source peripheral address */ void *(*fpAlloc) (int len), /* [ IN ] Function pointer that provides destination memory */ int len, /* [ IN ] Number of bytes "fpAlloc" will allocate for destination */ int num /* [ IN ] Number of descriptor to set */ ) { dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle); dmacHw_DESC_t *pProg = NULL; dmacHw_DESC_t *pLast = NULL; dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor); uint32_t dstAddr; uint32_t controlParam; int i; dmacHw_ASSERT(pConfig->transferType == dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM); if (num > pRing->num) { return -1; } pLast = pRing->pEnd; /* Last descriptor updated */ pProg = pRing->pHead; /* First descriptor in the new list */ controlParam = pConfig->srcUpdate | pConfig->dstUpdate | pConfig->srcMaxTransactionWidth | pConfig->dstMaxTransactionWidth | pConfig->srcMasterInterface | pConfig->dstMasterInterface | pConfig->srcMaxBurstWidth | pConfig->dstMaxBurstWidth | dmacHw_REG_CTL_TTFC_PM_PERI | dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN | dmacHw_REG_CTL_INT_EN; for (i = 0; i < num; i++) { /* Allocate Rx buffer only for idle descriptor */ if (((pRing->pHead->ctl.hi & dmacHw_DESC_FREE) == 0) || ((dmacHw_DESC_t *) pRing->pHead->llp == pRing->pTail) ) { /* Rx descriptor is not idle */ break; } /* Set source address */ pRing->pHead->sar = srcAddr; if (fpAlloc) { /* Allocate memory for buffer in descriptor */ dstAddr = (uint32_t) (*fpAlloc) (len); /* Check the destination address */ if (dstAddr == 0) { if (i == 0) { /* Not a single descriptor is available */ return -1; } break; } /* Set destination address */ pRing->pHead->dar = dstAddr; } /* Set control information */ pRing->pHead->ctl.lo = controlParam; /* Use "devCtl" to mark the memory that need to be freed later */ pRing->pHead->devCtl = dmacHw_FREE_USER_MEMORY; /* Descriptor is now owned by the channel */ pRing->pHead->ctl.hi = 0; /* Remember the descriptor last updated */ pRing->pEnd = pRing->pHead; /* Update next descriptor */ dmacHw_NEXT_DESC(pRing, pHead); } /* Mark the end of the list */ pRing->pEnd->ctl.lo &= ~(dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN); /* Connect the list */ if (pLast != pProg) { pLast->ctl.lo |= dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN; } /* Mark the descriptors are updated */ pCblk->descUpdated = 1; if (!pCblk->varDataStarted) { /* LLP must be pointing to the first descriptor */ dmacHw_SET_LLP(pCblk->module, pCblk->channel, (uint32_t) pProg - pRing->virt2PhyOffset); /* Channel, handling variable data started */ pCblk->varDataStarted = 1; } return i; }
void dmacHw_initiateTransfer(dmacHw_HANDLE_t handle, dmacHw_CONFIG_t *pConfig, void *pDescriptor ) { dmacHw_DESC_RING_t *pRing; dmacHw_DESC_t *pProg; dmacHw_CBLK_t *pCblk; pCblk = dmacHw_HANDLE_TO_CBLK(handle); pRing = dmacHw_GET_DESC_RING(pDescriptor); if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) { return; } if (pCblk->varDataStarted) { if (pCblk->descUpdated) { pCblk->descUpdated = 0; pProg = (dmacHw_DESC_t *) ((uint32_t) dmacHw_REG_LLP(pCblk->module, pCblk->channel) + pRing->virt2PhyOffset); if (!(pProg->ctl.hi & dmacHw_REG_CTL_DONE)) { dmacHw_SET_SAR(pCblk->module, pCblk->channel, pProg->sar); dmacHw_SET_DAR(pCblk->module, pCblk->channel, pProg->dar); dmacHw_REG_CTL_LO(pCblk->module, pCblk->channel) = pProg->ctl.lo; dmacHw_REG_CTL_HI(pCblk->module, pCblk->channel) = pProg->ctl.hi; } else if (pProg == (dmacHw_DESC_t *) pRing->pEnd->llp) { return; } else { dmacHw_ASSERT(0); } } else { return; } } else { if (pConfig->transferMode == dmacHw_TRANSFER_MODE_PERIODIC) { pProg = pRing->pHead; dmacHw_NEXT_DESC(pRing, pHead); } else { if (pRing->pEnd == NULL) { return; } pProg = pRing->pProg; if (pConfig->transferMode == dmacHw_TRANSFER_MODE_CONTINUOUS) { dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pEnd-> llp == pRing->pProg); dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pProg == pRing->pHead); do { pRing->pProg->ctl.lo |= (dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN); pRing->pProg = (dmacHw_DESC_t *) pRing->pProg->llp; } while (pRing->pProg != pRing->pHead); } else { while (pRing->pProg != pRing->pEnd) { pRing->pProg->ctl.lo |= (dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN); pRing->pProg = (dmacHw_DESC_t *) pRing->pProg->llp; } } } dmacHw_SET_SAR(pCblk->module, pCblk->channel, pProg->sar); dmacHw_SET_DAR(pCblk->module, pCblk->channel, pProg->dar); dmacHw_SET_LLP(pCblk->module, pCblk->channel, (uint32_t) pProg - pRing->virt2PhyOffset); dmacHw_REG_CTL_LO(pCblk->module, pCblk->channel) = pProg->ctl.lo; dmacHw_REG_CTL_HI(pCblk->module, pCblk->channel) = pProg->ctl.hi; if (pRing->pEnd) { pRing->pProg = (dmacHw_DESC_t *) pRing->pEnd->llp; } pRing->pEnd = (dmacHw_DESC_t *) NULL; } dmacHw_DMA_START(pCblk->module, pCblk->channel); }
int dmacHw_setDataDescriptor(dmacHw_CONFIG_t *pConfig, void *pDescriptor, void *pSrcAddr, void *pDstAddr, size_t dataLen ) { dmacHw_TRANSACTION_WIDTH_e dstTrWidth; dmacHw_TRANSACTION_WIDTH_e srcTrWidth; dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor); dmacHw_DESC_t *pStart; dmacHw_DESC_t *pProg; int srcTs = 0; int blkTs = 0; int oddSize = 0; int descCount = 0; int count = 0; int dstTrSize = 0; int srcTrSize = 0; uint32_t maxBlockSize = dmacHw_MAX_BLOCKSIZE; dstTrSize = dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth); srcTrSize = dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth); if ((pSrcAddr == NULL) || (pDstAddr == NULL) || (dataLen == 0)) { return -1; } if ((pConfig->srcGatherWidth % srcTrSize) || (pConfig->dstScatterWidth % dstTrSize)) { return -2; } dstTrWidth = pConfig->dstMaxTransactionWidth; while (dmacHw_ADDRESS_MASK(dstTrSize) & (uint32_t) pDstAddr) { dstTrWidth = dmacHw_GetNextTrWidth(dstTrWidth); dstTrSize = dmacHw_GetTrWidthInBytes(dstTrWidth); } srcTrWidth = pConfig->srcMaxTransactionWidth; while (dmacHw_ADDRESS_MASK(srcTrSize) & (uint32_t) pSrcAddr) { srcTrWidth = dmacHw_GetNextTrWidth(srcTrWidth); srcTrSize = dmacHw_GetTrWidthInBytes(srcTrWidth); } if (pConfig->maxDataPerBlock && ((pConfig->maxDataPerBlock / srcTrSize) < dmacHw_MAX_BLOCKSIZE)) { maxBlockSize = pConfig->maxDataPerBlock / srcTrSize; } srcTs = dataLen / srcTrSize; if (srcTs && (dstTrSize > srcTrSize)) { oddSize = dataLen % dstTrSize; srcTs = srcTs - (oddSize / srcTrSize); } else { oddSize = dataLen % srcTrSize; } if (oddSize) { descCount++; } if (srcTs) { descCount += ((srcTs - 1) / maxBlockSize) + 1; } pProg = pRing->pHead; for (count = 0; (descCount <= pRing->num) && (count < descCount); count++) { if ((pProg->ctl.hi & dmacHw_DESC_FREE) == 0) { return -3; } pProg = (dmacHw_DESC_t *) pProg->llp; } pStart = pProg = pRing->pHead; while (count) { pProg->ctl.lo = 0; if (pConfig->srcGatherWidth) { pProg->ctl.lo |= dmacHw_REG_CTL_SG_ENABLE; } if (pConfig->dstScatterWidth) { pProg->ctl.lo |= dmacHw_REG_CTL_DS_ENABLE; } pProg->sar = (uint32_t) pSrcAddr; pProg->dar = (uint32_t) pDstAddr; if (pProg == pRing->pHead) { pProg->devCtl = dmacHw_FREE_USER_MEMORY; } else { pProg->devCtl = 0; } blkTs = srcTs; if (count == 1) { pProg->ctl.lo &= ~(dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN); if (oddSize) { switch (pConfig->transferType) { case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM: dstTrWidth = dmacHw_DST_TRANSACTION_WIDTH_8; blkTs = (oddSize / srcTrSize) + ((oddSize % srcTrSize) ? 1 : 0); break; case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL: srcTrWidth = dmacHw_SRC_TRANSACTION_WIDTH_8; blkTs = oddSize; break; case dmacHw_TRANSFER_TYPE_MEM_TO_MEM: srcTrWidth = dmacHw_SRC_TRANSACTION_WIDTH_8; dstTrWidth = dmacHw_DST_TRANSACTION_WIDTH_8; blkTs = oddSize; break; case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL: break; } } else { srcTs -= blkTs; } } else { if (srcTs / maxBlockSize) { blkTs = maxBlockSize; } srcTs -= blkTs; } dmacHw_ASSERT(blkTs > 0); if (pConfig->flowControler == dmacHw_FLOW_CONTROL_DMA) { pProg->ctl.lo |= pConfig->transferType | pConfig->srcUpdate | pConfig->dstUpdate | srcTrWidth | dstTrWidth | pConfig->srcMaxBurstWidth | pConfig->dstMaxBurstWidth | pConfig->srcMasterInterface | pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN; } else { uint32_t transferType = 0; switch (pConfig->transferType) { case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM: transferType = dmacHw_REG_CTL_TTFC_PM_PERI; break; case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL: transferType = dmacHw_REG_CTL_TTFC_MP_PERI; break; default: dmacHw_ASSERT(0); } pProg->ctl.lo |= transferType | pConfig->srcUpdate | pConfig->dstUpdate | srcTrWidth | dstTrWidth | pConfig->srcMaxBurstWidth | pConfig->dstMaxBurstWidth | pConfig->srcMasterInterface | pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN; } pProg->ctl.hi = blkTs & dmacHw_REG_CTL_BLOCK_TS_MASK; if (count > 1) { pProg = (dmacHw_DESC_t *) pProg->llp; switch (pConfig->transferType) { case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM: if (pConfig->dstScatterWidth) { pDstAddr = (char *)pDstAddr + blkTs * srcTrSize + (((blkTs * srcTrSize) / pConfig->dstScatterWidth) * pConfig->dstScatterJump); } else { pDstAddr = (char *)pDstAddr + blkTs * srcTrSize; } break; case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL: if (pConfig->srcGatherWidth) { pSrcAddr = (char *)pDstAddr + blkTs * srcTrSize + (((blkTs * srcTrSize) / pConfig->srcGatherWidth) * pConfig->srcGatherJump); } else { pSrcAddr = (char *)pSrcAddr + blkTs * srcTrSize; } break; case dmacHw_TRANSFER_TYPE_MEM_TO_MEM: if (pConfig->dstScatterWidth) { pDstAddr = (char *)pDstAddr + blkTs * srcTrSize + (((blkTs * srcTrSize) / pConfig->dstScatterWidth) * pConfig->dstScatterJump); } else { pDstAddr = (char *)pDstAddr + blkTs * srcTrSize; } if (pConfig->srcGatherWidth) { pSrcAddr = (char *)pDstAddr + blkTs * srcTrSize + (((blkTs * srcTrSize) / pConfig->srcGatherWidth) * pConfig->srcGatherJump); } else { pSrcAddr = (char *)pSrcAddr + blkTs * srcTrSize; } break; case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL: break; default: dmacHw_ASSERT(0); } } else { dmacHw_ASSERT(srcTs == 0); } count--; } if (pRing->pProg == dmacHw_DESC_INIT) { pRing->pProg = pStart; } pRing->pEnd = pProg; pRing->pHead = (dmacHw_DESC_t *) pProg->llp; if (!dmacHw_DST_IS_MEMORY(pConfig->transferType)) { pRing->pTail = pRing->pHead; } return 0; }
int dmacHw_setDataDescriptor(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */ void *pDescriptor, /* [ IN ] Descriptor buffer */ void *pSrcAddr, /* [ IN ] Source (Peripheral/Memory) address */ void *pDstAddr, /* [ IN ] Destination (Peripheral/Memory) address */ size_t dataLen /* [ IN ] Data length in bytes */ ) { dmacHw_TRANSACTION_WIDTH_e dstTrWidth; dmacHw_TRANSACTION_WIDTH_e srcTrWidth; dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor); dmacHw_DESC_t *pStart; dmacHw_DESC_t *pProg; int srcTs = 0; int blkTs = 0; int oddSize = 0; int descCount = 0; int count = 0; int dstTrSize = 0; int srcTrSize = 0; uint32_t maxBlockSize = dmacHw_MAX_BLOCKSIZE; dstTrSize = dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth); srcTrSize = dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth); /* Skip Tx if buffer is NULL or length is unknown */ if ((pSrcAddr == NULL) || (pDstAddr == NULL) || (dataLen == 0)) { /* Do not initiate transfer */ return -1; } /* Ensure scatter and gather are transaction aligned */ if ((pConfig->srcGatherWidth % srcTrSize) || (pConfig->dstScatterWidth % dstTrSize)) { return -2; } /* Background 1: DMAC can not perform DMA if source and destination addresses are not properly aligned with the channel's transaction width. So, for successful DMA transfer, transaction width must be set according to the alignment of the source and destination address. */ /* Adjust destination transaction width if destination address is not aligned properly */ dstTrWidth = pConfig->dstMaxTransactionWidth; while (dmacHw_ADDRESS_MASK(dstTrSize) & (uint32_t) pDstAddr) { dstTrWidth = dmacHw_GetNextTrWidth(dstTrWidth); dstTrSize = dmacHw_GetTrWidthInBytes(dstTrWidth); } /* Adjust source transaction width if source address is not aligned properly */ srcTrWidth = pConfig->srcMaxTransactionWidth; while (dmacHw_ADDRESS_MASK(srcTrSize) & (uint32_t) pSrcAddr) { srcTrWidth = dmacHw_GetNextTrWidth(srcTrWidth); srcTrSize = dmacHw_GetTrWidthInBytes(srcTrWidth); } /* Find the maximum transaction per descriptor */ if (pConfig->maxDataPerBlock && ((pConfig->maxDataPerBlock / srcTrSize) < dmacHw_MAX_BLOCKSIZE)) { maxBlockSize = pConfig->maxDataPerBlock / srcTrSize; } /* Find number of source transactions needed to complete the DMA transfer */ srcTs = dataLen / srcTrSize; /* Find the odd number of bytes that need to be transferred as single byte transaction width */ if (srcTs && (dstTrSize > srcTrSize)) { oddSize = dataLen % dstTrSize; /* Adjust source transaction count due to "oddSize" */ srcTs = srcTs - (oddSize / srcTrSize); } else { oddSize = dataLen % srcTrSize; } /* Adjust "descCount" due to "oddSize" */ if (oddSize) { descCount++; } /* Find the number of descriptor needed for total "srcTs" */ if (srcTs) { descCount += ((srcTs - 1) / maxBlockSize) + 1; } /* Check the availability of "descCount" discriptors in the ring */ pProg = pRing->pHead; for (count = 0; (descCount <= pRing->num) && (count < descCount); count++) { if ((pProg->ctl.hi & dmacHw_DESC_FREE) == 0) { /* Sufficient descriptors are not available */ return -3; } pProg = (dmacHw_DESC_t *) pProg->llp; } /* Remember the link list item to program the channel registers */ pStart = pProg = pRing->pHead; /* Make a link list with "descCount(=count)" number of descriptors */ while (count) { /* Reset channel control information */ pProg->ctl.lo = 0; /* Enable source gather if configured */ if (pConfig->srcGatherWidth) { pProg->ctl.lo |= dmacHw_REG_CTL_SG_ENABLE; } /* Enable destination scatter if configured */ if (pConfig->dstScatterWidth) { pProg->ctl.lo |= dmacHw_REG_CTL_DS_ENABLE; } /* Set source and destination address */ pProg->sar = (uint32_t) pSrcAddr; pProg->dar = (uint32_t) pDstAddr; /* Use "devCtl" to mark that user memory need to be freed later if needed */ if (pProg == pRing->pHead) { pProg->devCtl = dmacHw_FREE_USER_MEMORY; } else { pProg->devCtl = 0; } blkTs = srcTs; /* Special treatmeant for last descriptor */ if (count == 1) { /* Mark the last descriptor */ pProg->ctl.lo &= ~(dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN); /* Treatment for odd data bytes */ if (oddSize) { /* Adjust for single byte transaction width */ switch (pConfig->transferType) { case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM: dstTrWidth = dmacHw_DST_TRANSACTION_WIDTH_8; blkTs = (oddSize / srcTrSize) + ((oddSize % srcTrSize) ? 1 : 0); break; case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL: srcTrWidth = dmacHw_SRC_TRANSACTION_WIDTH_8; blkTs = oddSize; break; case dmacHw_TRANSFER_TYPE_MEM_TO_MEM: srcTrWidth = dmacHw_SRC_TRANSACTION_WIDTH_8; dstTrWidth = dmacHw_DST_TRANSACTION_WIDTH_8; blkTs = oddSize; break; case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL: /* Do not adjust the transaction width */ break; } } else { srcTs -= blkTs; } } else { if (srcTs / maxBlockSize) { blkTs = maxBlockSize; } /* Remaining source transactions for next iteration */ srcTs -= blkTs; } /* Must have a valid source transactions */ dmacHw_ASSERT(blkTs > 0); /* Set control information */ if (pConfig->flowControler == dmacHw_FLOW_CONTROL_DMA) { pProg->ctl.lo |= pConfig->transferType | pConfig->srcUpdate | pConfig->dstUpdate | srcTrWidth | dstTrWidth | pConfig->srcMaxBurstWidth | pConfig->dstMaxBurstWidth | pConfig->srcMasterInterface | pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN; } else { uint32_t transferType = 0; switch (pConfig->transferType) { case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM: transferType = dmacHw_REG_CTL_TTFC_PM_PERI; break; case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL: transferType = dmacHw_REG_CTL_TTFC_MP_PERI; break; default: dmacHw_ASSERT(0); } pProg->ctl.lo |= transferType | pConfig->srcUpdate | pConfig->dstUpdate | srcTrWidth | dstTrWidth | pConfig->srcMaxBurstWidth | pConfig->dstMaxBurstWidth | pConfig->srcMasterInterface | pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN; } /* Set block transaction size */ pProg->ctl.hi = blkTs & dmacHw_REG_CTL_BLOCK_TS_MASK; /* Look for next descriptor */ if (count > 1) { /* Point to the next descriptor */ pProg = (dmacHw_DESC_t *) pProg->llp; /* Update source and destination address for next iteration */ switch (pConfig->transferType) { case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM: if (pConfig->dstScatterWidth) { pDstAddr = (char *)pDstAddr + blkTs * srcTrSize + (((blkTs * srcTrSize) / pConfig->dstScatterWidth) * pConfig->dstScatterJump); } else { pDstAddr = (char *)pDstAddr + blkTs * srcTrSize; } break; case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL: if (pConfig->srcGatherWidth) { pSrcAddr = (char *)pDstAddr + blkTs * srcTrSize + (((blkTs * srcTrSize) / pConfig->srcGatherWidth) * pConfig->srcGatherJump); } else { pSrcAddr = (char *)pSrcAddr + blkTs * srcTrSize; } break; case dmacHw_TRANSFER_TYPE_MEM_TO_MEM: if (pConfig->dstScatterWidth) { pDstAddr = (char *)pDstAddr + blkTs * srcTrSize + (((blkTs * srcTrSize) / pConfig->dstScatterWidth) * pConfig->dstScatterJump); } else { pDstAddr = (char *)pDstAddr + blkTs * srcTrSize; } if (pConfig->srcGatherWidth) { pSrcAddr = (char *)pDstAddr + blkTs * srcTrSize + (((blkTs * srcTrSize) / pConfig->srcGatherWidth) * pConfig->srcGatherJump); } else { pSrcAddr = (char *)pSrcAddr + blkTs * srcTrSize; } break; case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL: /* Do not adjust the address */ break; default: dmacHw_ASSERT(0); } } else { /* At the end of transfer "srcTs" must be zero */ dmacHw_ASSERT(srcTs == 0); } count--; } /* Remember the descriptor to initialize the registers */ if (pRing->pProg == dmacHw_DESC_INIT) { pRing->pProg = pStart; } /* Indicate that the descriptor is updated */ pRing->pEnd = pProg; /* Head pointing to the next descriptor */ pRing->pHead = (dmacHw_DESC_t *) pProg->llp; /* Update Tail pointer if destination is a peripheral, because no one is going to read from the pTail */ if (!dmacHw_DST_IS_MEMORY(pConfig->transferType)) { pRing->pTail = pRing->pHead; } return 0; }
void dmacHw_initiateTransfer(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */ dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */ void *pDescriptor /* [ IN ] Descriptor buffer */ ) { dmacHw_DESC_RING_t *pRing; dmacHw_DESC_t *pProg; dmacHw_CBLK_t *pCblk; pCblk = dmacHw_HANDLE_TO_CBLK(handle); pRing = dmacHw_GET_DESC_RING(pDescriptor); if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) { /* Not safe yet to program the channel */ return; } if (pCblk->varDataStarted) { if (pCblk->descUpdated) { pCblk->descUpdated = 0; pProg = (dmacHw_DESC_t *) ((uint32_t) dmacHw_REG_LLP(pCblk->module, pCblk->channel) + pRing->virt2PhyOffset); /* Load descriptor if not loaded */ if (!(pProg->ctl.hi & dmacHw_REG_CTL_DONE)) { dmacHw_SET_SAR(pCblk->module, pCblk->channel, pProg->sar); dmacHw_SET_DAR(pCblk->module, pCblk->channel, pProg->dar); dmacHw_REG_CTL_LO(pCblk->module, pCblk->channel) = pProg->ctl.lo; dmacHw_REG_CTL_HI(pCblk->module, pCblk->channel) = pProg->ctl.hi; } else if (pProg == (dmacHw_DESC_t *) pRing->pEnd->llp) { /* Return as end descriptor is processed */ return; } else { dmacHw_ASSERT(0); } } else { return; } } else { if (pConfig->transferMode == dmacHw_TRANSFER_MODE_PERIODIC) { /* Do not make a single chain, rather process one descriptor at a time */ pProg = pRing->pHead; /* Point to the next descriptor for next iteration */ dmacHw_NEXT_DESC(pRing, pHead); } else { /* Return if no more pending descriptor */ if (pRing->pEnd == NULL) { return; } pProg = pRing->pProg; if (pConfig->transferMode == dmacHw_TRANSFER_MODE_CONTINUOUS) { /* Make sure a complete ring can be formed */ dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pEnd-> llp == pRing->pProg); /* Make sure pProg pointing to the pHead */ dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pProg == pRing->pHead); /* Make a complete ring */ do { pRing->pProg->ctl.lo |= (dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN); pRing->pProg = (dmacHw_DESC_t *) pRing->pProg->llp; } while (pRing->pProg != pRing->pHead); } else { /* Make a single long chain */ while (pRing->pProg != pRing->pEnd) { pRing->pProg->ctl.lo |= (dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN); pRing->pProg = (dmacHw_DESC_t *) pRing->pProg->llp; } } } /* Program the channel registers */ dmacHw_SET_SAR(pCblk->module, pCblk->channel, pProg->sar); dmacHw_SET_DAR(pCblk->module, pCblk->channel, pProg->dar); dmacHw_SET_LLP(pCblk->module, pCblk->channel, (uint32_t) pProg - pRing->virt2PhyOffset); dmacHw_REG_CTL_LO(pCblk->module, pCblk->channel) = pProg->ctl.lo; dmacHw_REG_CTL_HI(pCblk->module, pCblk->channel) = pProg->ctl.hi; if (pRing->pEnd) { /* Remember the descriptor to use next */ pRing->pProg = (dmacHw_DESC_t *) pRing->pEnd->llp; } /* Indicate no more pending descriptor */ pRing->pEnd = (dmacHw_DESC_t *) NULL; } /* Start DMA operation */ dmacHw_DMA_START(pCblk->module, pCblk->channel); }
int dmacHw_setControlDescriptor(dmacHw_CONFIG_t *pConfig, void *pDescriptor, uint32_t ctlAddress, uint32_t control ) { dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor); if (ctlAddress == 0) { return -1; } if ((pRing->pHead->ctl.hi & dmacHw_DESC_FREE) == 0) { return -1; } pRing->pHead->devCtl = control; pRing->pHead->sar = (uint32_t) &pRing->pHead->devCtl; pRing->pHead->dar = ctlAddress; if (pConfig->flowControler == dmacHw_FLOW_CONTROL_DMA) { pRing->pHead->ctl.lo = pConfig->transferType | dmacHw_SRC_ADDRESS_UPDATE_MODE_INC | dmacHw_DST_ADDRESS_UPDATE_MODE_INC | dmacHw_SRC_TRANSACTION_WIDTH_32 | pConfig->dstMaxTransactionWidth | dmacHw_SRC_BURST_WIDTH_0 | dmacHw_DST_BURST_WIDTH_0 | pConfig->srcMasterInterface | pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN; } else { uint32_t transferType = 0; switch (pConfig->transferType) { case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM: transferType = dmacHw_REG_CTL_TTFC_PM_PERI; break; case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL: transferType = dmacHw_REG_CTL_TTFC_MP_PERI; break; default: dmacHw_ASSERT(0); } pRing->pHead->ctl.lo = transferType | dmacHw_SRC_ADDRESS_UPDATE_MODE_INC | dmacHw_DST_ADDRESS_UPDATE_MODE_INC | dmacHw_SRC_TRANSACTION_WIDTH_32 | pConfig->dstMaxTransactionWidth | dmacHw_SRC_BURST_WIDTH_0 | dmacHw_DST_BURST_WIDTH_0 | pConfig->srcMasterInterface | pConfig->dstMasterInterface | pConfig->flowControler | dmacHw_REG_CTL_INT_EN; } pRing->pHead->ctl.hi = dmacHw_REG_CTL_BLOCK_TS_MASK & 1; if (pRing->pProg == dmacHw_DESC_INIT) { pRing->pProg = pRing->pHead; } pRing->pEnd = pRing->pHead; dmacHw_NEXT_DESC(pRing, pHead); if (!dmacHw_DST_IS_MEMORY(pConfig->transferType)) { pRing->pTail = pRing->pHead; } return 0; }
int dmacHw_setVariableDataDescriptor(dmacHw_HANDLE_t handle, dmacHw_CONFIG_t *pConfig, void *pDescriptor, uint32_t srcAddr, void *(*fpAlloc) (int len), int len, int num ) { dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle); dmacHw_DESC_t *pProg = NULL; dmacHw_DESC_t *pLast = NULL; dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor); uint32_t dstAddr; uint32_t controlParam; int i; dmacHw_ASSERT(pConfig->transferType == dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM); if (num > pRing->num) { return -1; } pLast = pRing->pEnd; pProg = pRing->pHead; controlParam = pConfig->srcUpdate | pConfig->dstUpdate | pConfig->srcMaxTransactionWidth | pConfig->dstMaxTransactionWidth | pConfig->srcMasterInterface | pConfig->dstMasterInterface | pConfig->srcMaxBurstWidth | pConfig->dstMaxBurstWidth | dmacHw_REG_CTL_TTFC_PM_PERI | dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN | dmacHw_REG_CTL_INT_EN; for (i = 0; i < num; i++) { if (((pRing->pHead->ctl.hi & dmacHw_DESC_FREE) == 0) || ((dmacHw_DESC_t *) pRing->pHead->llp == pRing->pTail) ) { break; } pRing->pHead->sar = srcAddr; if (fpAlloc) { dstAddr = (uint32_t) (*fpAlloc) (len); if (dstAddr == 0) { if (i == 0) { return -1; } break; } pRing->pHead->dar = dstAddr; } pRing->pHead->ctl.lo = controlParam; pRing->pHead->devCtl = dmacHw_FREE_USER_MEMORY; pRing->pHead->ctl.hi = 0; pRing->pEnd = pRing->pHead; dmacHw_NEXT_DESC(pRing, pHead); } pRing->pEnd->ctl.lo &= ~(dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN); if (pLast != pProg) { pLast->ctl.lo |= dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN; } pCblk->descUpdated = 1; if (!pCblk->varDataStarted) { dmacHw_SET_LLP(pCblk->module, pCblk->channel, (uint32_t) pProg - pRing->virt2PhyOffset); pCblk->varDataStarted = 1; } return i; }