📄 dmadrv.c
字号:
// dmaGetRegPtr
//
// Returns address to DMA registers
//
DMAC_REGS_T *dmaGetRegPtr(DWORD dmaData)
{
DMADRVDATA_T *pDrvData = (DMADRVDATA_T *) dmaData;
return pDrvData->pDMARegs;
}
//-----------------------------------------------------------------------------
//
// dmaTranEntry
//
// Starts a DMA transfer with the passed attributes (not LLI)
//
DWORD dmaTranEntry(DWORD dmaData,
DWORD src, // Physical address
DWORD dst, // Physical address
DWORD bytes) // Number of bytes to transfer
{
DWORD ctrl;
DMADRVDATA_T *pDrvData = (DMADRVDATA_T *) dmaData;
DWORD trans;
trans = bytes / pDrvData->xferSize;
if (trans >= 4096)
{
return 0;
}
if (dmaIsOn(dmaData) != 0)
{
// Channel is currently on
return 0;
}
// Update config word
pDrvData->pDMAChan->config_ch = pDrvData->savedConfig;
// Setup control word
ctrl = (pDrvData->savedControl | DMAC_CHAN_TRANSFER_SIZE(trans) |
DMAC_CHAN_INT_TC_EN);
// Start transfer
dmaStartDMA((DMADRVDATA_T *) dmaData, src, dst, 0, ctrl);
return bytes;
}
//-----------------------------------------------------------------------------
//
// dmaListReset
//
// Disable and reset the current DMA list
//
void dmaListReset(DWORD dmaData)
{
DMADRVDATA_T *pDrvData = (DMADRVDATA_T *) dmaData;
dmaDisable(dmaData);
pDrvData->GetListIdx = 0;
pDrvData->PutListIdx = 0;
pDrvData->ListEntries = 0;
pDrvData->pDMAChan->lli = 0;
}
//-----------------------------------------------------------------------------
//
// dmaChanConfig
//
// Change the current channel's attributes, adjusts channel configuation
// without creating new buffers
//
DWORD dmaChanConfig(DWORD dmaData,
DMASETUP_T *pDmaSetup)
{
UINT32 tmp;
DMADRVDATA_T *pDrvData = (DMADRVDATA_T *) dmaData;
// Setup control word for the channel
// Source on AHB0, destination on AHB1
tmp = (DMAC_CHAN_SRC_AHB1 | pDmaSetup->SrcWidth | pDmaSetup->DestWidth |
// DMAC_CHAN_DEST_AHB1 | // TBD
// tmp = (0 | pDmaSetup->SrcWidth | pDmaSetup->DestWidth |
// DMAC_CHAN_DEST_AHB1 | // TBD
pDmaSetup->SrcBurstSize | pDmaSetup->DestBurstSize);
if (pDmaSetup->DestInc != 0)
{
tmp |= DMAC_CHAN_DEST_AUTOINC;
}
if (pDmaSetup->SrcInc != 0)
{
tmp |= DMAC_CHAN_SRC_AUTOINC;
}
pDrvData->savedControl = tmp;
pDrvData->pDMAChan->control = 0;
// Compute transfer size
if (pDmaSetup->SrcWidth == DMAC_CHAN_SRC_WIDTH_8)
{
pDrvData->xferSize = 1;
}
else if (pDmaSetup->SrcWidth == DMAC_CHAN_SRC_WIDTH_16)
{
pDrvData->xferSize = 2;
}
else
{
pDrvData->xferSize = 4;
}
// Setup channel configuration word for the channel
// DMA requests enabled, channel not enabled
tmp = (pDmaSetup->perFlowSource | pDmaSetup->destPeripheral |
pDmaSetup->srcPeripheral | DMAC_CHAN_ITC);
pDrvData->savedConfig = tmp;
pDrvData->pDMAChan->config_ch = tmp;
return 1;
}
//-----------------------------------------------------------------------------
//
// dmaDumpRegs
//
// Dumps current DMA registers
//
void dmaDumpRegs(DWORD dmaData)
{
int idx;
UINT32 *pReg;
DMADRVDATA_T *pDrvData = (DMADRVDATA_T *) dmaData;
RETAILMSG(1,
(TEXT("dmaDumpRegs: Dumping DMA common registers\r\n")));
idx = 0;
pReg = (UINT32 *) pDrvData->pDMARegs;
for (idx = 0; idx < 14; idx++)
{
RETAILMSG(1,
(TEXT("dmaDumpRegs: Reg 0x%x = 0x%x\r\n"), pReg, *pReg));
pReg++;
}
RETAILMSG(1,
(TEXT("dmaDumpRegs: Dumping DMA channel(%d) registers\r\n"), pDrvData->dmaInfo.dmaCh));
idx = 0;
pReg = (UINT32 *) pDrvData->pDMAChan;
for (idx = 0; idx < 5; idx++)
{
RETAILMSG(1,
(TEXT("dmaDumpRegs: Reg 0x%x = 0x%x\r\n"), pReg, *pReg));
pReg++;
}
}
//-----------------------------------------------------------------------------
//
// dmaCleanUp
//
// Pops a single list entry and returns the number of bytes sent in
// that list
//
DWORD dmaCleanUp(DWORD dmaData)
{
int toproc;
DWORD trans = 0;
DMADRVDATA_T *pDrvData = (DMADRVDATA_T *) dmaData;
DMAC_LL_T *PCurHWList, *pExpListVirt;
// Only process if entries need processing
if (pDrvData->ListEntries > 0)
{
// Current DMA list entry to check before resubmitting to pool
pExpListVirt = pDrvData->pDMAListVirt + pDrvData->GetListIdx;
// Current HW pointer and convert to virtual address
PCurHWList = (DMAC_LL_T *) (pDrvData->pDMAChan->lli &
~DMAC_CHAN_LLI_SEL_AHB1);
PCurHWList = dmaToVirt(pDrvData, PCurHWList);
// If DMA is stopped, then always pop the list
if (dmaIsOn(dmaData) == 0)
{
// Get size of the completed transfer
trans = pExpListVirt->next_ctrl;
trans = trans & DMAC_CHAN_TRANSFER_SIZE(0xFFF);
// One entry cleaned, can be used by dmaListEntry()
pDrvData->ListEntries--;
}
else if (PCurHWList != NULL)
{
// DMA is not idle and there is 2 or more entries on the
// list. Determine if an entry needs to be popped.
// Determine the number of entries the DMA has to process
if (PCurHWList > pExpListVirt)
{
toproc = (DWORD) PCurHWList - (DWORD) pExpListVirt;
}
else
{
toproc = (DWORD) pExpListVirt - (DWORD) PCurHWList;
}
toproc = toproc / sizeof (DMAC_LL_T);
// If there are less entries for DMA processing than what
// was submitted, then an entry can be popped off the list
if (toproc != pDrvData->ListEntries)
{
// Get size of the completed transfer
trans = pExpListVirt->next_ctrl;
trans = trans & DMAC_CHAN_TRANSFER_SIZE(0xFFF);
// One entry cleaned, can be used by dmaListEntry()
pDrvData->ListEntries--;
}
}
else
{
// Current HW list is NULL
if (pDrvData->ListEntries > 1)
{
// Get size of the completed transfer
trans = pExpListVirt->next_ctrl;
trans = trans & DMAC_CHAN_TRANSFER_SIZE(0xFFF);
// One entry cleaned, can be used by dmaListEntry()
pDrvData->ListEntries--;
}
}
}
// Switch to next check entry
if (trans > 0)
{
pDrvData->GetListIdx++;
if (pDrvData->GetListIdx >= DMALISTENTRIES)
{
pDrvData->GetListIdx = 0;
}
// Readjust for bytes
trans = trans * pDrvData->xferSize;
}
return trans;
}
//-----------------------------------------------------------------------------
//
// dmaListEntry
//
// Adds a new entry to the DMA linked list and starts DMA
//
DWORD dmaListEntry(DWORD dmaData,
DWORD src, // Physical address
DWORD dst, // Physical address
DWORD bytes) // Number of bytes to transfer
{
int prevIdx;
DWORD trans, added = 0;
DMADRVDATA_T *pDrvData = (DMADRVDATA_T *) dmaData;
DMAC_LL_T *pPrevListV, *pCurListV, *pCurListP, *PCurHWList;
trans = bytes / pDrvData->xferSize;
if ((trans == 0) || (trans >= 4096))
{
return 0;
}
// Only process if there is enough space
if (pDrvData->ListEntries < DMALISTENTRIES)
{
// Get pointer to next DMA list entry to put
pCurListV = pDrvData->pDMAListVirt + pDrvData->PutListIdx;
// Get pointer to previous DMA list entry
prevIdx = pDrvData->PutListIdx - 1;
if (prevIdx < 0)
{
prevIdx = DMALISTENTRIES - 1;
}
pPrevListV = pDrvData->pDMAListVirt + prevIdx;
// Get physical address for list entry to put
pCurListP = dmaToPhy(pDrvData, pCurListV);
// Increment buffer and put counts
pDrvData->ListEntries++;
pDrvData->PutListIdx++;
if (pDrvData->PutListIdx >= DMALISTENTRIES)
{
pDrvData->PutListIdx = 0;
}
// Add to next entry in the list
pCurListV->dma_src = src;
pCurListV->dma_dest = dst;
pCurListV->next_lli = 0;
trans = bytes / pDrvData->xferSize;
pCurListV->next_ctrl = (pDrvData->savedControl |
DMAC_CHAN_TRANSFER_SIZE(trans) | DMAC_CHAN_INT_TC_EN);
// Update previous link pointer to point to current link
pPrevListV->next_lli = ((UNS_32) pCurListP |
DMAC_CHAN_LLI_SEL_AHB1);
// If next link is NULL, then update the current DMA HW link to
// the next link
PCurHWList = (DMAC_LL_T *) pDrvData->pDMAChan->lli;
if (PCurHWList == 0)
{
pDrvData->pDMAChan->lli = ((UNS_32) pCurListP |
DMAC_CHAN_LLI_SEL_AHB1);
}
// If DMA is idle, then the DMA list was empty and the previous
// link update occured after DMA was disabled, so restart or
// start DMA
if (dmaIsOn(dmaData) == 0)
{
// Start transfer
dmaStartDMA(pDrvData, pCurListV->dma_src, pCurListV->dma_dest,
0, pCurListV->next_ctrl);
}
added = 1;
}
return added;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -