文章目录
- 1. 前言
- 2. 背景
- 3. PL330 简介
- 4. PL330 驱动加载流程
- 4.1 PL330 设备注册流程
- 4.2 PL330 驱动加载流程
- 5. 小结
- 6. 参考资料
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. 背景
本文基于 ARMv8 架构
、Linux 5.10
进行分析,DMA 控制器(DMAC: DMA Controller)
为 ARM
的 PL330。
3. PL330 简介
PL330
是 ARM
设计的 DMA 控制器(DMAC: DMA Controller)
,支持 Scatter/Gather 和 LLI(Linked-List Item) 特性。
上图是 PL330
的接口图,其中:
. AXI master 接口,用于 DMA 传输。
. APB slave 接口,用于配置/控制 DMAC PL330。
. Peripheral request interface [x:0],外设通过它发起 DMA 传输请求。
. Interrupts[x:0] 接口,用于发送中断给 CPU。
PL330
的典型应用框图如下:
更详细的 PL330 框图如下:
本文对 PL330
的介绍,就到此为止,更多关于 PL330
的细节,可参考 ARM
官方文档 DDI0424A_dmac_pl330_r0p0_trm.pdf
。
4. PL330 驱动加载流程
章节 3.
简单的介绍了 PL330
的功能和接口,本节将着重介绍 PL330 驱动的加载过程
。首先看一下 PL330
的 DTS
配置:
4.1 PL330 设备注册流程
dmac0: dma-controller@ff2c0000 {
compatible = "arm,pl330", "arm,primecell";
reg = <0x0 0xff2c0000 0x0 0x4000>;
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
arm,pl330-periph-burst;
clocks = <&cru ACLK_DMAC0>;
clock-names = "apb_pclk";
#dma-cells = <1>;
};
系统启动过程中,解析 DMAC PL330
的 DTS
配置:
kernel_init()
kernel_init_freeable()
...
do_one_initcall()
// arch_initcall_sync(of_platform_default_populate_init);
of_platform_default_populate_init()
of_platform_default_populate(NULL, NULL, NULL)
of_platform_populate(root, of_default_bus_match_table, lookup, parent)
rc = of_platform_bus_create(child, matches, lookup, parent, true)
/* drivers/of/platform.c */
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
...
if (of_device_is_compatible(bus, "arm,primecell")) {
/*
* Don't return an error here to keep compatibility with older
* device tree files.
*/
of_amba_device_create(bus, bus_id, platform_data, parent);
return 0;
}
...
}
#ifdef CONFIG_ARM_AMBA
static struct amba_device *of_amba_device_create(struct device_node *node,
const char *bus_id,
void *platform_data,
struct device *parent)
{
struct amba_device *dev;
...
/* 1. 创建 AMBA 设备: DMA PL330 */
dev = amba_device_alloc(NULL, 0, 0);
...
/* 2. AMBA 设备初始化 */
/* AMBA devices only support a single DMA mask */
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
/* setup generic device info */
dev->dev.of_node = of_node_get(node);
dev->dev.fwnode = &node->fwnode;
dev->dev.parent = parent ? : &platform_bus;
dev->dev.platform_data = platform_data;
if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id);
else
of_device_make_bus_id(&dev->dev);
...
/* Decode the IRQs and address ranges */
for (i = 0; i < AMBA_NR_IRQS; i++)
dev->irq[i] = irq_of_parse_and_map(node, i);
...
/* 3. 注册 AMBA 设备到系统 */
ret = amba_device_add(dev, &iomem_resource);
...
}
#else /* CONFIG_ARM_AMBA */
static struct amba_device *of_amba_device_create(struct device_node *node,
const char *bus_id,
void *platform_data,
struct device *parent)
{
return NULL;
}
#endif /* CONFIG_ARM_AMBA */
上面 amba_device_alloc()
创建设备过程中,绑定设备总线类型为 amba_bustype
,是 PL330
驱动匹配加载的重要一环:
amba_device_alloc()
struct amba_device *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev) {
amba_device_initialize(dev, name);
...
dev->dev.bus = &amba_bustype; /* 绑定 设备 的 总线类型 为 amba_bustype */
...
...
}
return dev;
而 amba_device_add()
在注册设备到系统前,扫描读取 AMBA(Advanced Microcontroller Bus Architecture)
设备的 {periphid, cid}
,是 PL330
驱动匹配加载的另一重要环节:
amba_device_add()
amba_device_try_add()
static int amba_device_try_add(struct amba_device *dev, struct resource *parent)
{
...
/* 扫描读取 AMBA 设备的 {periph_id,cid} */
ret = amba_get_enable_pclk(dev);
if (ret == 0) {
u32 pid, cid;
...
/*
* Read pid and cid based on size of resource
* they are located at end of region
*/
/* 读取 {periph_id,cid} */
for (pid = 0, i = 0; i < 4; i++)
pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8);
for (cid = 0, i = 0; i < 4; i++)
cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8);
...
amba_put_disable_pclk(dev);
/* 记录 {periphid, cid} 到设备对象 */
if (cid == AMBA_CID || cid == CORESIGHT_CID) {
dev->periphid = pid;
dev->cid = cid;
}
if (!dev->periphid)
ret = -ENODEV;
}
...
skip_probe:
/* 注册 AMBA 设备到系统 */
ret = device_add(&dev->dev);
...
}
读取到的 PL330
的 periphid
为 0x00241330
,cid
为 0xb105f00d
。
4.2 PL330 驱动加载流程
/* drivers/dma/pl330.c */
static const struct amba_id pl330_ids[] = {
{
.id = 0x00041330,
.mask = 0x000fffff,
},
{ 0, 0 },
};
MODULE_DEVICE_TABLE(amba, pl330_ids);
static struct amba_driver pl330_driver = {
.drv = {
.owner = THIS_MODULE,
.name = "dma-pl330",
.pm = &pl330_pm,
},
.id_table = pl330_ids,
.probe = pl330_probe,
.remove = pl330_remove,
};
module_amba_driver(pl330_driver);
/* include/linux/amba/bus.h */
#define module_amba_driver(__amba_drv) \
module_driver(__amba_drv, amba_driver_register, amba_driver_unregister)
/* drivers/amba/bus.c */
int amba_driver_register(struct amba_driver *drv)
{
if (!drv->probe)
return -EINVAL;
drv->drv.bus = &amba_bustype; /* 绑定 驱动 的 总线类型 为 amba_bustype */
drv->drv.probe = amba_probe;
drv->drv.remove = amba_remove;
drv->drv.shutdown = amba_shutdown;
return driver_register(&drv->drv);
}
driver_register()
bus_add_driver()
driver_attach()
__driver_attach()
...
/* 驱动匹配 */
ret = driver_match_device(drv, dev);
amba_match()
amba_lookup()
...
/* 驱动绑定 */
device_driver_attach(drv, dev);
driver_probe_device(drv, dev);
really_probe(dev, drv);
drv->probe(dev); /* pl330_probe() */
...
static const struct amba_id *
amba_lookup(const struct amba_id *table, struct amba_device *dev)
{
while (table->mask) {
if (((dev->periphid & table->mask) == table->id) &&
((dev->cid != CORESIGHT_CID) ||
(amba_cs_uci_id_match(table, dev))))
return table;
table++;
}
return NULL;
}
从上面的分析中,可以看到 AMBA
设备 (PL330
) 的匹配是通过 {periphid, cid}
进行匹配的。前面读到的 正好匹配到 pl330_ids[0]
,驱动加载流程进入了 pl330_probe()
。
pl330_probe()
...
/* 注册 DMA 中断处理接口 */
for (i = 0; i < AMBA_NR_IRQS; i++) {
irq = adev->irq[i];
if (irq) {
ret = devm_request_irq(&adev->dev, irq,
pl330_irq_handler, 0,
dev_name(&adev->dev), pl330);
...
} else {
break;
}
}
...
/* 注册 DMA 控制器设备到 DMA 子系统 */
ret = dma_async_device_register(pd);
...
5. 小结
虽然本文分析的 DMAC(DMA Controller)
设备的设备驱动注册加载,但其主干流程
也适用于其它 AMBA(Advanced Microcontroller Bus Architecture)
设备驱动。
6. 参考资料
[1] DDI0424A_dmac_pl330_r0p0_trm.pdf
[2] 102202_0100_01_Introduction_to_AMBA_AXI.pdf