一、platform平台总线的简介
(1)相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。
(2)cpu与外部通信的两种方式,地址总线式链接(32的cpu就有0-4G直接集成在cpu内部,以地址指针方式直接访问,没有具体的总线链接就用虚拟的platform平台总线来控制内部的外设)和专用接口式(iic,pci,usb等这些外部总线链接的)链接。平台总线对应地址总线式链接设备,也就是soc内部集成的各种内部外设
(3)思考:为什么要有平台总线?进一步思考:为什么要有总线的概念?
因为大多数设备都是集成在soc内部,和cpu直接连接,将其直接扩展到内部地址空间,因此他们本身就不该就有总线的概念,本身就不属于总线的链接方式,但是如果一部分设备设计得有总线,一部分没总线就太乱了,所以除了iic,pci,spi等的设备就归类到平台总线来便于管理
二、platform平台总线下管理的2员大将
(1)platform工作体系都定义在drivers/base/platform.c中
(2)两个结构体:platform_device和platform_driver
(3)两个接口函数:platform_device_register和platform_driver_register
1、platform_device
struct platform_device {
const char * name; // 平台总线下设备的名字
int id;
struct device dev; // 所有设备通用的属性部分
u32 num_resources; // 设备使用到的resource的个数
struct resource * resource; // 设备使用到的资源数组的首地址
const struct platform_device_id *id_entry; // 设备ID表
/* arch specific additions */
struct pdev_archdata archdata; // 自留地,用来提供扩展性的
};
*对平台总线下可利用的设备资源结构体进行分析struct resource resource
struct resource { // 资源结构体
resource_size_t start; // 资源的起始值,如果是地址,那么是物理地址,不是虚拟地址
resource_size_t end; // 资源的结束值,如果是地址,那么是物理地址,不是虚拟地址
const char *name; // 资源名
unsigned long flags; // 资源的标示,用来识别不同的资源
struct resource *parent, *sibling, *child; // 资源指针,可以构成链表
};
struct platform_driver {
int (*probe)(struct platform_device *); // 驱动探测函数
int (*remove)(struct platform_device *); // 去掉一个设备
void (*shutdown)(struct platform_device *); // 关闭一个设备
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver; // 所有设备共用的一些属性
const struct platform_device_id *id_table; // 设备ID表
};
三、平台总线本身的初始化函数platform_bus_init
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup(); // 进行一些早期的平台清理
error = device_register(&platform_bus); // 注册设备 (在/sys/devices/目录下建立 platform目录对应的设备对象 /sys/devices/platform/)
if (error)
return error;
error = bus_register(&platform_bus_type); // 总线注册
if (error)
device_unregister(&platform_bus);
return error;
}
四、platform平台总线工作原理
1、平台总线体系的工作流程
*(1)第一步:系统启动时在bus系统中注册platform
(2)第二步:内核移植的人负责提供platform_device
(3)第三步:写驱动的人负责提供platform_driver
(4)第四步:platform的match函数发现driver和device匹配后,调用driver的probe函数来完成驱动的初始化和安装,然后设备就工作起来了*
2、代码分析:platform本身注册
(1)每种总线(不光是platform,usb、i2c那些也是)都会带一个match方法,match方法用来对总线下的device和driver进行匹配。理论上每种总线的匹配算法是不同的,但是实际上一般都是看name的。
(2)platform_match函数就是平台总线的匹配方法。该函数的工作方法是:如果有id_table就说明驱动可能支持多个设备,所以这时候要去对比id_table中所有的name,只要找到一个相同的就匹配上了不再找了,如果找完id_table都还没找到就说明每匹配上;如果没有id_table或者每匹配上,那就直接对比device和driver的name,如果匹配上就匹配上了,如果还没匹配上那就匹配失败
3、以leds-s3c24xx.c为例来分析platform设备和驱动的注册过程
(1)platform_driver_register
(2)platform_device_register
platdata怎么玩
(1)platdata其实就是设备注册时提供的设备有关的一些数据(譬如设备对应的gpio、使用到的中断号、设备名称····)
(2)这些数据在设备和驱动match之后,会由设备方转给驱动方。驱动拿到这些数据后,通过这些数据得知设备的具体信息,然后来操作设备。
(3)这样做的好处是:驱动源码中不携带数据,只负责算法(对硬件的操作方法)。现代驱动设计理念就是算法和数据分离,这样最大程度保持驱动的独立性和适应性。