什么是平台总线
平台总线(platform bus)是 Linux 为那些无法自动枚举的片上设备设计的一条"虚拟总线"。
它不对应任何真实的物理总线协议,不像 I2C、USB 那样有实际的电气信号,它纯粹是内核里的一层软件抽象。
平台总线上有三个角色:
-
platform_device:描述一个硬件实例,带着这个实例的资源,比如寄存器地址、中断号。现代 ARM64 BSP 中,它大多由设备树转换而来。 -
platform_driver:描述一类硬件的驱动逻辑,带着probe()回调和一张匹配表,告诉内核"我能驱动哪种设备"。 -
platform bus:维护着设备和驱动两份清单,负责把两边匹配起来。
可以平台总线理解成一个中介。
设备树相当于在中介这里登记了"有哪些房子、什么条件",每个 platform_driver 相当于登记了"我想租什么样的房子"。
平台总线这个中介则拿着两边的条件去撮合,一旦某个设备和某个驱动的条件对上了,就把它们拉到一起,通知驱动:你要找的硬件到了,来接手吧。
这个"通知驱动接手"的动作,就是调用驱动的 probe()。
这样一来,硬件描述(设备树)和驱动逻辑(platform_driver)就实现了解耦。
换一块板子,只改设备树里的地址和中断,驱动代码一行都不用动。
3. 平台总线与字符设备接口的关系
那是不是学习平台驱动以后,第三章讲过的字符设备是不是就被替代了?
答案是:不是的,它们负责的是驱动流程的不同阶段。
平台总线负责组织硬件资源和驱动,而字符设备接口用于将已经完成驱动初始化的硬件的接口暴露给用户。
具体关系可以看下面这个图:

平台总线负责把硬件实例和驱动组织起来,platform_device 记录硬件实例及其资源,platform_driver 提供匹配信息和驱动回调,两者匹配成功以后,内核就会调用驱动的 probe()。
对于设备接入过程来说,平台总线的主要工作到调用 probe() 就完成了。
至于怎样获取资源、怎样初始化硬件、最终注册到哪个内核框架,以及是否向用户态提供接口,都是具体驱动需要完成的工作。
平台驱动框架把硬件接入内核的过程整理成了一套固定结构,相当于 Linux 把驱动匹配流程变成了填空题。
BSP 工程师只需填写设备匹配表,实现 probe() 等回调;Linux 内核平台总线会负责完成匹配,并在匹配成功后调用这些入口。
对于驱动 Bringup 来说,probe() 是最主要的入口。驱动通常会在这里获取寄存器、中断、时钟、复位和 pinctrl 等资源,初始化硬件,申请中断,然后把设备提供的能力注册到相应的内核子系统。
我们之前学习的字符设备接口可以放在 probe函数中完成调用,实现驱动匹配加载后自动实现创建/dev节点以及对应的open、read、write接口实现。
然而大多数情况下不需要单独使用cdev,内核都提供了对应的接口,也就是我们常说的子系统。
这些子系统本身也就是以字符设备的形式出现的,但是并不是通过主动调用cdev接口实现的 ,例如:V4L2 提供 /dev/videoX,i2c-dev 提供 /dev/i2c-X。
I2C 控制器驱动通过 i2c_add_adapter() 注册到 I2C 子系统,SPI 控制器提供了 spi_register_controller() 接口,其他的GPIO 、 input子系统也都有对应的接口。
这些驱动不需要自己设计设备号,也不需要直接实现一套 cdev 和 file_operations。
只有当厂商自研的模块,内核中没有其合适的子系统,应用程序又确实需要通过 read()、write()、ioctl()、poll() 或 mmap() 直接操作设备,驱动才需要考虑自己注册 cdev 或 miscdevice。
因此,平台总线负责完成硬件实例与驱动的匹配,并把执行流程带到 probe();字符设备则是驱动完成硬件初始化以后,可以选择建立的一种用户态接口。
4. 本章内容安排
这一章的目标是建立一套完整的平台驱动开发思路。
我们会沿着"硬件怎样被描述、设备和驱动怎样完成匹配、驱动怎样进入 probe()"这条主线,逐步认识平台总线在 SoC 驱动中的位置。
理解平台总线的基本框架以后,我们还会学习平台总线的使用,并将之前实际开发过驱动移植到平台总线的框架中,建立从硬件描述到驱动正常工作的完整认识。
我们还会深入的梳理学习平台总线匹配加载的内核机制,理解probe调用的流程及其内部原理。学完这一章后,学会的不仅是概念和使用方法,还有其内核原理,而这些可能工作了很多年的 BSP 工程师都并没有完全掌握的。
5. 总结
这一节我们解释了 Linux 为什么需要平台总线:面对不同 SoC、不同板卡上的硬件差异,平台总线框架实现了把硬件描述和驱动逻辑解耦,让同一份驱动能够适配不同的硬件实例,从而提高了驱动的通用性。
在这个框架中,platform_device 记录硬件实例及其资源,platform_driver 提供匹配信息和驱动回调,platform bus 负责完成两者的匹配。
匹配成功以后,内核调用驱动的 probe(),驱动从这里开始接管硬件。
进入 probe() 以后,资源获取、硬件初始化以及后续接口注册都由具体驱动完成。
真实的控制器驱动通常会注册到所属的内核子系统,由子系统提供统一接口;只有确实需要直接向用户态提供文件式访问时,驱动才需要考虑注册字符设备。