/*
* Enable the DMA channel
* ASSUMES All other configuration bits have been set
*as desired before this code is called
*/
void pl08x_enable_dmac_chan(unsigned int cnum)
{
void __iomem *cbase = pd.base + PL08X_OS_CHAN_BASE +
(cnum * PL08X_OS_CHAN);
unsigned int r = 0;
/*
* Do not access config register until channel shows as disabled
*/
/*等到对应channel是disable状态了,再接下来去enable它,会更安全*/
while ((readl(pd.base + PL08X_OS_ENCHNS) & (1 << cnum))
& PL08X_MASK_ENCHNS);
/*
* Do not access config register until channel shows as inactive
*/
/*等到对应channel是inactive状态了,再接下来去enable它,会更安全*/
r = readl(cbase + PL08X_OS_CCFG);
while ((r & PL08X_MASK_ACTIVE) || (r & PL08X_MASK_CEN))
r = readl(cbase + PL08X_OS_CCFG);
writel(r | PL08X_MASK_CEN, cbase + PL08X_OS_CCFG);
mb();
}
/*检测对应的位,得到是否是active的状态,对应着是否是busy */
static int pl08x_dma_busy(dmach_t ci)
{
unsigned int reg;
unsigned int chan_base = (unsigned int)pd.base
+ PL08X_OS_CHAN_BASE;
chan_base += ci * PL08X_OS_CHAN;
/*
* Check channel is inactive
*/
reg = readl(chan_base + PL08X_OS_CCFG);
return reg & PL08X_MASK_ACTIVE;
}
/*
* Function for the shared work queue
* - scheduled by the interrupt handler when sufficient
*list entries need releasing
*/
void pl08x_wqfunc(struct work_struct *work)
{
if (pd.pd)
pl08x_memrelease();
}
/*根据datasheet,PL080和PL081对应的设备ID分别是0x00041080和0x00041081
详情参考:
3.4.16 Peripheral Identification Registers 0-3
3.4.17 PrimeCell Identification Registers 0-3*/
static struct amba_id pl08x_ids[] = {
/* PL080 */
{
.id= 0x00041080,
.mask= 0x000fffff,
},
/* PL081 */
{
.id= 0x00041081,
.mask= 0x000fffff,
},
{ 0, 0 },
};
#define DRIVER_NAME"pl08xdmac"
static int pl08x_probe(struct amba_device *amba_dev, void *id);
static struct amba_driver pl08x_amba_driver = {
.drv.name= "pl08xdmaapi",
.id_table= pl08x_ids,
.probe= pl08x_probe,
};
/*
* This single pool easier to manage than one pool per channel
*/
int pl08x_make_LLIs(void)
{
int ret = 0;
/*
* Make a pool of LLI buffers
*/
pd.pool= dma_pool_create(pl08x_amba_driver.drv.name, &pd.dmac->dev,
PL08X_LLI_TSFR_SIZE, PL08X_ALIGN, PL08X_ALLOC);
if (!pd.pool) {
ret = -ENOMEM;
kfree(pd.chanllis);
}
pd.pool_ctr = 0;
return ret;
}
/*需要提到一点的是,如果你的驱动在初始化的时候,设置了对应的
上图中的config寄存器中的ITC位,即终止计数中断,那么就会在每一个LLI传完之后就调用下面的这个irq中断函数,如果没有设置ITC,而只是对最有一个LLI的ITC设置了,那么就是所有的传输完成了,才会调用下面的irq中断函数了 */
/*
* issue pending() will start the next transfer
* - it only need be loaded here
* CAUTION the work queue function may run during the handler
* CAUTION function callbacks may have interesting side effects
*/
static irqreturn_t pl08x_irq(int irq, void *dev)
{
u32 mask = 0;
u32 reg;
unsigned long flags;
int c;
/*如果发现有任何错误,就退出
实际上此处很多种错误,而目前的此版本的驱动,暂时没加入更多的判断,仅仅是报错而已*/
reg = readl(pd.base + PL08X_OS_ISR_ERR);
mb();
if (reg) {
/*
* An error interrupt (on one or more channels)
*/
dev_err(&pd.dmac->dev,
"%s - Error interrupt, register value 0x%08x\n",
__func__, reg);
/*
* Simply clear ALL PL08X error interrupts,
* regardless of channel and cause
*/
writel(0x000000FF, pd.base + PL08X_OS_ICLR_ERR);
}
reg = readl(pd.base + PL08X_OS_ISR);
mb();
for (c = 0; c < MAX_DMA_CHANNELS; c++) {
if ((1 << c) & reg) {
struct pl08x_txd *entry = NULL;
struct pl08x_txd *next;
spin_lock_irqsave(&pd.chanwrap[c]->lock, flags);
if (pd.chanwrap[c]->at) {
/*
* Update last completed
*/
pd.chanwrap[c]->lc =
(pd.chanwrap[c]->at->tx.cookie);
/*
* Callback peripheral driver for p/m
* to signal completion
*/
if (pd.chanwrap[c]->at->tx.callback) {
/*
* Pass channel number
*/
((struct pl08x_callback_param *)
pd.chanwrap[c]