Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources
This commit is contained in:
216
sdk/driver/dma/dw_dmac.c
Normal file
216
sdk/driver/dma/dw_dmac.c
Normal file
@@ -0,0 +1,216 @@
|
||||
#include "typesdef.h"
|
||||
#include "list.h"
|
||||
#include "errno.h"
|
||||
#include "dev.h"
|
||||
#include "string.h"
|
||||
#include "osal/irq.h"
|
||||
#include "osal/semaphore.h"
|
||||
#include "osal/mutex.h"
|
||||
#include "hal/dma.h"
|
||||
#include "dev/dma/dw_dmac.h"
|
||||
#include "osal/string.h"
|
||||
|
||||
static void dw_dmac_irq_handler(void *data)
|
||||
{
|
||||
uint32 ch;
|
||||
struct hgdma_dw *dma = (struct hgdma_dw *)data;
|
||||
|
||||
for(ch=0; ch<DW_DMAC_MAX_DMAC_CHN; ch++) {
|
||||
if((dma->hw->MaskTfrL & BIT(ch)) && (dma->hw->RawTfrL & BIT(ch))) {
|
||||
dma->hw->ClearTfrL = BIT(ch);
|
||||
dma->state[ch] |= DW_DMAC_XFER_DONE(ch);
|
||||
if(dma->irq_hdl[ch]) {
|
||||
dma->irq_hdl[ch]((void *)dma, ch, DMA_IRQ_TYPE_DONE, dma->irq_data[ch]);
|
||||
}
|
||||
}
|
||||
if((dma->hw->MaskErrL & BIT(ch)) && (dma->hw->RawErrL & BIT(ch))) {
|
||||
dma->hw->ClearErrL = BIT(ch);
|
||||
dma->state[ch] |= DW_DMAC_XFER_ERR(ch);
|
||||
if(dma->irq_hdl[ch]) {
|
||||
dma->irq_hdl[ch]((void *)dma, ch, DMA_IRQ_TYPE_ERROR, dma->irq_data[ch]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32 dw_dmac_xfer(struct dma_device *dma, struct dma_xfer_data *data)
|
||||
{
|
||||
uint32 ch;
|
||||
uint32 flags;
|
||||
uint32 src_id = data->src_id;
|
||||
uint32 dest_id = data->dst_id;
|
||||
uint32 direct = data->dir;
|
||||
uint8 src_dir = (data->src_addr_mode == DMA_XFER_MODE_RECYCLE) ? DW_DMAC_ADDR_NO_CHANGE : DW_DMAC_ADDR_INC;
|
||||
uint8 dst_dir = (data->dst_addr_mode == DMA_XFER_MODE_RECYCLE) ? DW_DMAC_ADDR_NO_CHANGE : DW_DMAC_ADDR_INC;
|
||||
struct hgdma_dw *dev = (struct hgdma_dw *)dma;
|
||||
struct hgdma_dw_hw *hw = (struct hgdma_dw_hw *)dev->hw;
|
||||
|
||||
if(!dev->opened) {
|
||||
return RET_ERR;
|
||||
}
|
||||
|
||||
/* get free channel */
|
||||
flags = disable_irq();
|
||||
for(ch=0; ch<DW_DMAC_MAX_DMAC_CHN; ch++) {
|
||||
if(!(dev->chn_used_flag & BIT(ch))) {
|
||||
dev->chn_used_flag |= BIT(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enable_irq(flags);
|
||||
|
||||
if(ch >= DW_DMAC_MAX_DMAC_CHN)
|
||||
return -EBUSY;
|
||||
|
||||
ASSERT(data->element_per_width < DMA_SLAVE_BUSWIDTH_8_BYTES);
|
||||
if(data->element_per_width >= DMA_SLAVE_BUSWIDTH_8_BYTES) {
|
||||
return RET_ERR;
|
||||
}
|
||||
|
||||
hw->CH[ch].SARH = 0x0000;
|
||||
hw->CH[ch].DARH = 0x0000;
|
||||
hw->CH[ch].SARL = data->src;
|
||||
hw->CH[ch].DARL = data->dest;
|
||||
switch(direct) {
|
||||
case DMA_XFER_DIR_M2M:
|
||||
hw->CH[ch].CTLL = DW_DMAC_CTLL_SMS(1) | DW_DMAC_CTLL_DMS(1) |
|
||||
DW_DMAC_CTLL_FC(DW_DMAC_FC_D_M2M) |
|
||||
DW_DMAC_CTLL_SRC_DIR(src_dir) |
|
||||
DW_DMAC_CTLL_DST_DIR(dst_dir);
|
||||
/* Configure dma req channel */
|
||||
hw->CH[ch].CFGH = DW_DMAC_CFGH_DST_PER(0) |
|
||||
DW_DMAC_CFGH_SRC_PER(0) |
|
||||
DW_DMAC_CFGH_PROTCTL(1);
|
||||
break;
|
||||
case DMA_XFER_DIR_D2M:
|
||||
hw->CH[ch].CTLL = DW_DMAC_CTLL_SMS(0) | DW_DMAC_CTLL_DMS(1) |
|
||||
DW_DMAC_CTLL_FC(DW_DMAC_FC_D_P2M) |
|
||||
DW_DMAC_CTLL_SRC_DIR(src_dir) |
|
||||
DW_DMAC_CTLL_DST_DIR(dst_dir);
|
||||
/* Configure dma req channel */
|
||||
hw->CH[ch].CFGH = DW_DMAC_CFGH_DST_PER(0) |
|
||||
DW_DMAC_CFGH_SRC_PER(src_id) |
|
||||
DW_DMAC_CFGH_PROTCTL(1);
|
||||
break;
|
||||
case DMA_XFER_DIR_M2D:
|
||||
hw->CH[ch].CTLL = DW_DMAC_CTLL_SMS(1) | DW_DMAC_CTLL_DMS(0) |
|
||||
DW_DMAC_CTLL_FC(DW_DMAC_FC_D_M2P) |
|
||||
DW_DMAC_CTLL_SRC_DIR(src_dir) |
|
||||
DW_DMAC_CTLL_DST_DIR(dst_dir);
|
||||
/* Configure dma req channel */
|
||||
hw->CH[ch].CFGH = DW_DMAC_CFGH_DST_PER(dest_id) |
|
||||
DW_DMAC_CFGH_SRC_PER(0) |
|
||||
DW_DMAC_CFGH_PROTCTL(1);
|
||||
break;
|
||||
case DMA_XFER_DIR_D2D:
|
||||
hw->CH[ch].CTLL = DW_DMAC_CTLL_SMS(0) | DW_DMAC_CTLL_DMS(0) |
|
||||
DW_DMAC_CTLL_FC(DW_DMAC_FC_D_P2P) |
|
||||
DW_DMAC_CTLL_SRC_DIR(src_dir) |
|
||||
DW_DMAC_CTLL_DST_DIR(dst_dir);
|
||||
/* Configure dma req channel */
|
||||
hw->CH[ch].CFGH = DW_DMAC_CFGH_DST_PER(dest_id) |
|
||||
DW_DMAC_CFGH_SRC_PER(src_id) |
|
||||
DW_DMAC_CFGH_PROTCTL(1);
|
||||
break;
|
||||
default:
|
||||
dev->chn_used_flag &= ~ BIT(ch);
|
||||
return RET_ERR;
|
||||
}
|
||||
/* Configure block size */
|
||||
hw->CH[ch].CTLH = data->element_num;
|
||||
|
||||
hw->CH[ch].CTLL |= DW_DMAC_CTLL_INT_EN |
|
||||
DW_DMAC_CTLL_SRC_MSIZE(DW_DMAC_MSIZE_1) |
|
||||
DW_DMAC_CTLL_DST_MSIZE(DW_DMAC_MSIZE_1) |
|
||||
DW_DMAC_CTLL_SRC_WIDTH(data->element_per_width) |
|
||||
DW_DMAC_CTLL_DST_WIDTH(data->element_per_width);
|
||||
/* AMBA burst length no limited. */
|
||||
hw->CH[ch].CFGL = DW_DMAC_CFGL_MAX_BURST(0);
|
||||
|
||||
dev->state[ch] = 0x0000;
|
||||
dev->irq_hdl[ch] = data->irq_hdl;
|
||||
dev->irq_data[ch] = data->irq_data;
|
||||
|
||||
//start xfer
|
||||
hw->ChEnRegL = DW_DMAC_WRITE_EN(ch);
|
||||
return ch;
|
||||
}
|
||||
|
||||
static int32 dw_dmac_get_status(struct dma_device *dma, uint32 chn)
|
||||
{
|
||||
struct hgdma_dw *dev = (struct hgdma_dw *)dma;
|
||||
|
||||
if(!dev->opened) {
|
||||
return RET_ERR;
|
||||
}
|
||||
|
||||
if(dev->state[chn] & DW_DMAC_XFER_ERR(chn)) {
|
||||
return DMA_ERROR;
|
||||
} else if(dev->state[chn] & DW_DMAC_XFER_DONE(chn)) {
|
||||
return DMA_SUCCESS;
|
||||
} else {
|
||||
return DMA_IN_PROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
static int32 dw_dmac_stop(struct dma_device *dma, uint32 chn)
|
||||
{
|
||||
uint32 flags;
|
||||
|
||||
struct hgdma_dw *dev = (struct hgdma_dw *)dma;
|
||||
struct hgdma_dw_hw *p_dmac = (struct hgdma_dw_hw *)dev->hw;
|
||||
|
||||
flags = disable_irq();
|
||||
if(dev->chn_used_flag & BIT(chn)) {
|
||||
p_dmac->ChEnRegL = DW_DMAC_WRITE_DIS(chn);
|
||||
dev->chn_used_flag &= ~ BIT(chn);
|
||||
}
|
||||
enable_irq(flags);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static const struct dma_hal_ops dw_ops = {
|
||||
.xfer = dw_dmac_xfer,
|
||||
.get_status = dw_dmac_get_status,
|
||||
.stop = dw_dmac_stop,
|
||||
};
|
||||
|
||||
__init int32 dw_dmac_attach(uint32 dev_id, struct hgdma_dw *dmac)
|
||||
{
|
||||
uint32 ch;
|
||||
|
||||
dmac->opened = 1;
|
||||
dmac->dev.dev.ops = (const struct devobj_ops *)&dw_ops;
|
||||
|
||||
for(ch=0; ch<DW_DMAC_MAX_DMAC_CHN; ch++) {
|
||||
dmac->irq_hdl[ch] = NULL;
|
||||
dmac->irq_data[ch] = 0;
|
||||
dmac->state[ch] = 0;
|
||||
}
|
||||
|
||||
dmac->hw->ChEnRegL = 0xFF00;
|
||||
dmac->hw->DmaCfgRegL = 0x0000;
|
||||
|
||||
/* reset dmac */
|
||||
dmac->hw->ClearBlockL = DW_DMAC_MAX_DMAC_CHN_MASK;
|
||||
dmac->hw->ClearDstTranL = DW_DMAC_MAX_DMAC_CHN_MASK;
|
||||
dmac->hw->ClearErrL = DW_DMAC_MAX_DMAC_CHN_MASK;
|
||||
dmac->hw->ClearSrcTranL = DW_DMAC_MAX_DMAC_CHN_MASK;
|
||||
dmac->hw->ClearTfrL = DW_DMAC_MAX_DMAC_CHN_MASK;
|
||||
|
||||
/* disable mask */
|
||||
dmac->hw->MaskErrL = 0xFFFF;
|
||||
dmac->hw->MaskTfrL = 0xFFFF;
|
||||
dmac->hw->MaskBlockL = 0xFF00;
|
||||
dmac->hw->MaskDstTranL = 0xFF00;
|
||||
dmac->hw->MaskSrcTranL = 0xFF00;
|
||||
|
||||
/* enable DMAC */
|
||||
dmac->hw->DmaCfgRegL = DW_DMAC_CFG_DMA_EN;
|
||||
|
||||
irq_enable(dmac->irq_num);
|
||||
request_irq(dmac->irq_num, dw_dmac_irq_handler, dmac);
|
||||
dev_register(dev_id, (struct dev_obj *)dmac);
|
||||
return RET_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user