一、AMBA 介绍
当我们查看一些芯片的设备树时,我们发现设备树上的很多片内外设的compatible属性为compatible = "arm,pl022", "arm,primecell",而且在内核源码中找不到compatible匹配的驱动,这是因为这些驱动使用了arm提供的amba协议。
AMBA是由ARM Holdings开发的一种广泛使用的片上互连规范,它为系统级芯片(SoC)设计中的各种IP(知识产权)模块(如微控制器、处理器、存储控制器、直接内存访问控制器和其他外设)提供了连接和互连框架。
AMBA由多个不同的规范组成,每个规范都有特定的用途。主要的规范包括:
1.AMBA AHB(高级高性能总线):AHB是一种高性能总线协议,为不同IP模块之间的高效数据传输提供多主多从接口。它支持突发传输、分裂事务和流水线等功能,以优化数据传输速率。
2.AMBA APB(高级外设总线):APB是一种低功耗、低成本的总线协议,主要用于将低带宽外设连接到系统。它被设计成简单易实现,适用于不需要高数据传输速率的外设接口。
3.AMBA AXI(高级可扩展接口):AXI是一种高性能、高带宽的总线协议,专为连接具有高数据吞吐量需求的复杂IP模块而设计。它支持无序事务、多个未完成事务以及独立的读写数据通道,从而实现高效且并发的数据传输。
ARM决定采用AMBA的原因有以下几点:
1.标准化:AMBA提供了一种统一的、标准化的片上互连规范。这意味着在使用AMBA的系统中,不同的IP模块可以遵循相同的通信协议和接口规范,从而简化了系统集成的复杂性。
2.可扩展性:AMBA规范具有良好的可扩展性,能够满足不同系统设计的需求。它提供了多种规范,如高性能的AHB、低功耗的APB和高带宽的AXI,使得设计人员可以根据系统的要求选择合适的总线协议。
3.高性能:AMBA规范针对高性能和高带宽应用进行了优化。通过支持诸如突发传输、流水线和无序事务等功能,AMBA可以提供高效的数据传输,满足处理器和外设之间的快速通信需求。
4.灵活性:AMBA规范允许设计人员根据具体应用需求进行定制。通过使用AMBA的IP模块,可以轻松地进行系统扩展和修改,以适应不同的设计要求和市场需求。
5.行业广泛应用:AMBA是业界广泛采用的片上互连规范之一。许多芯片设计和系统集成都使用AMBA作为其互联标准,这为开发者提供了更多的工具和支持资源。
综上所述,ARM选择采用AMBA的主要原因是为了提供一种标准化、可扩展且高性能的片上互连解决方案,简化系统设计、提高数据传输效率,并为设计者提供更大的灵活性和行业支持。
二、Linux AMBA驱动介绍
AMBA Linux驱动程序是Linux内核中的软件组件,用于支持与基于AMBA的IP模块或外设进行交互。这些驱动程序实现了Linux内核与系统中特定的AMBA设备之间的通信和控制。它们屏蔽了AMBA总线协议的底层细节,并提供了标准化的接口,用于访问和利用连接的外设。
AMBA Linux驱动程序通常包括以下关键组件:
1.设备驱动程序(Device Driver):设备驱动程序是实现与特定AMBA设备交互的核心组件。它定义了设备的功能、特性和操作方式,并提供了与设备进行通信的接口。设备驱动程序负责与硬件进行交互,包括读取和写入寄存器、配置设备参数以及处理中断等操作。
2.平台驱动程序(Platform Driver):平台驱动程序提供了与AMBA总线平台相关的初始化和管理功能。它负责识别和注册连接到AMBA总线的设备,并为这些设备分配资源和配置参数。平台驱动程序还可以处理总线特定的事件和操作,以确保设备在系统启动和运行过程中正常工作。
3.总线驱动程序(Bus Driver):总线驱动程序处理AMBA总线的底层操作和管理。它负责管理总线上的设备探测、地址映射、数据传输和错误处理等功能。总线驱动程序提供了与AMBA总线的物理层交互的接口,并确保设备在总线上的正常通信和协调。通过这些组件,AMBA Linux驱动程序实现了与AMBA设备之间的有效通信和控制。它们提供了一致的编程接口和抽象层,使开发者能够方便地编写应用程序和驱动程序,以实现对AMBA外设的访问和操作。
总的来说,AMBA驱动框架是为了让使用ARM片内外设的公司更容易使用基于片内外设的驱动程序。
三、Linux AMBA驱动框架分析
1、amba设备添加到总线
内核在启动开始后,会依次解析设备树的device node,并创建platform device,其流程如下:
//根据不同的设备属性,创建不同的设备类型
of_platform_bus_create->
//如果设备属于amba设备,则创建amba device
of_amba_device_create->
//amba设备申请
amba_device_alloc->
//获取amba设备的periphid(可能该参数设备树不存在)
of_get_property(.., "arm,primecell-periphid",..)
//向amba总线增加新的设备
amba_device_add->
//是否可以成功添加
amba_device_try_add->
//获取amba设备的寄存器资源
tmp = ioremap(dev->res.start, size)
//获取amba设备的periphid(该参数设备树不存在,这里也可以获取到)
pid = readl();
//如果可以,则加入amba总线链表
list_add_tail
2、amba驱动添加到总线并匹配amba device
下面以片内spi amba控制器驱动举例,源码位置:drivers/spi/spi-pl022.c
①、amba 设备驱动相关的数据结构
//amba id结构
static const struct amba_id pl022_ids[] = {
...
{
/*
* ST Micro derivative, this has 32bit wide
* and 32 locations deep TX/RX FIFO
*/
.id = 0x01080022, //当前我的芯片使用的片内spi控制器amba id
.mask = 0xffffffff,
.data = &vendor_st,
},
...
{ 0, 0 },
};
//amba_driver结构
static struct amba_driver pl022_driver = {
.drv = {
.name = "ssp-pl022",
.pm = &pl022_dev_pm_ops,
},
.id_table = pl022_ids,
.probe = pl022_probe,
.remove = pl022_remove,
};
//amba总线数据结构
struct bus_type amba_bustype = {
.name = "amba",
.dev_groups = amba_dev_groups,
.match = amba_match,
.uevent = amba_uevent,
.dma_configure = platform_dma_configure,
.pm = &amba_pm,
};
②、amba 设备驱动注册到amba总线
//pl022_driver控制器驱动注册到amba总线
amba_driver_register(&pl022_driver)->
drv->drv.bus = &amba_bustype
drv->drv.probe = amba_probe
drv->drv.remove = amba_remove
drv->drv.shutdown = amba_shutdown
//驱动注册,总线类型为amba
driver_register(&drv->drv)
③、amba 驱动匹配amba设备
driver_register->
//driver添加到ambabus
bus_add_driver->
//drivers在总线上搜索对应的device资源
driver_attach->
bus_for_each_dev(drv->bus, ..., __driver_attach)->
//在总线上循环遍历,实际就是遍历链表
klist_iter_init_node();
fn(dev, data)(这里的fn就是__driver_attach函数)->
//match device
amba_match(dev, data)->
amba_lookup(pcdrv->id_table, pcdev)->
//如果driver和device的amba id相同,那么匹配成功
if (((dev->periphid & table->mask) == table->id)
//匹配成功向下执行driver的probe函数
device_driver_attach->
driver_probe_device->
//最终执行drvier的probe函数
really_probe->
//到这里,匹配的流程结束,进入到设备的初始化
pl022_probe