这个DMA 控制器是PL080,代码分析如下:
1. 子系统 init/exit
static int __init pl080_dma_init(void)
{
return amba_driver_register(&pl080_dma_driver); //这是添加在amba总线上的一个控制器
}
static void __exit pl080_dma_exit(void)
{
amba_driver_unregister(&pl080_dma_driver);
}
subsys_initcall(pl080_dma_init);
module_exit(pl080_dma_exit);
pl080_dma_driver 驱动结构体如下:
static struct amba_driver pl080_dma_driver = {
.drv = {
.name = "pl080-dmac", //驱动名字pl080-dmac
.owner = THIS_MODULE,
},
.probe = pl080_dma_probe, //添加完该驱动后,就会自动运行probe,进行探测有没有dma设备。
.remove = __devexit_p(pl080_dma_remove),
.suspend = pl080_dma_suspend,
.resume = pl080_dma_resume,
.id_table = pl080_dma_ids, //这个不知道干啥用的
};
pl080_dma_ids 定义如下:
static struct amba_id pl080_dma_ids[] __initdata = {
{
.id = 0x8A280080,
.mask = 0xFFFFFFFF,
},
{ 0, 0 },
};
pl080_dma_suspend()/pl080_dma_resume(), 是电源管理相关函数,定义如下:
#ifdef CONFIG_PM
static int pl080_dma_suspend(struct amba_device *dev, pm_message_t state)
{
struct pl080_dma *pdma = amba_get_drvdata(dev);
/* TODO: to be updated */
pl080_dma_reset(pdma);
clk_disable(pdma->clk);
return 0;
}
static int pl080_dma_resume(struct amba_device *dev)
{
struct pl080_dma *pdma = amba_get_drvdata(dev);
/* TODO: to be updated */
clk_enable(pdma->clk);
return 0;
}
#else
#define pl080_dma_suspend NULL
#define pl080_dma_resume NULL
#endif
2. pl080_dma_probe() --- probe函数, 代码有点长:
static int __devinit pl080_dma_probe(struct amba_device *dev, struct amba_id *id)
{
struct pl080_dma_platform_data *plat = dev->dev.platform_data;
struct pl080_dma *pdma;
struct clk *clk;
int i, ret;
/* must have platform data */
if (!plat) { //platform 相关数据
dev_err(&dev->dev, "missing platform data\n");
return -EINVAL;
}
/* sanity check on configuration parameters */
if (!dev->res.start || !dev->res.end || dev->irq[0] < 0) { //amba_device 相关的内容
dev_err(&dev->dev, "device configuration error\n");
return -EINVAL;
}
if (plat->nr_channels > MAX_NR_CHANNELS) { //通道数
dev_err(&dev->dev, "too many channels, max %d\n", MAX_NR_CHANNELS);
return -EINVAL;
}
/* request all memory regions associated with this device */
ret = amba_request_regions(dev, NULL); //申请 amba_device 内存
if (ret) {
dev_err(&dev->dev, "unable to get device memory regions\n");
return ret;
}
/* get reference to device clock and enable it */
clk = clk_get(&dev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&dev->dev, "unable to get reference to device clock\n");
ret = PTR_ERR(clk);
goto exit_release;
}
ret = clk_enable(clk);
if (ret) {
dev_err(&dev->dev, "failed to enable device clock\n");
goto exit_clk_put;
}
/* allocate and save driver context */
pdma = kzalloc(sizeof(struct pl080_dma), GFP_KERNEL);
if (!pdma) {
dev_err(&dev->dev, "not enough memory for DMA controller status\n");
ret = -ENOMEM;
goto exit_clk_disable;
}
amba_set_drvdata(dev, pdma); //给 dev 赋值 pdma;
tasklet_init(&pdma->tasklet, pl080_dma_tasklet, (unsigned long)pdma); //初始化tasklet
/* initialize DMA controller status */
pdma->base = ioremap(dev->res.start, resource_size(&dev->res));
pdma->irq = dev->irq[0];
pdma->clk = clk;
pdma->ahb_master_memory = plat->ahb_master_memory; //内存
pdma->nr_descs_per_channel = plat->nr_descs_per_channel; //通道数
if (pdma->nr_descs_per_channel == 0)
pdma->nr_descs_per_channel = NR_DESCS_PER_CHANNEL;
pdma->all_chan_mask = (1 << plat->nr_channels) - 1;
pdma->dma.chancnt = plat->nr_channels;
INIT_LIST_HEAD(&pdma->dma.channels); //初始化dma通道队列
for (i = 0; i < plat->nr_channels; i++) {
struct pl080_dma_chan *pdc = &pdma->chan[i];
pdc->chan.device = &pdma->dma;
pdc->chan.cookie = pdc->completed = 0;
pdc->chan.chan_id = i;
list_add_tail(&pdc->chan.device_node, &