linux amba机制,详解ARM的AMBA设备中的DMA设备PL08X的Linux驱动-3

/*

* 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]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值