ARM64 Linux 6.10 内核驱动(14):drivers/base/platform.c:板级设备主战场

§1.2 drivers/base/platform.c:板级设备主战场

平台:QEMU virt + ARM64。platform 是伪总线:把 SoC 上不会自己报名的 MMIO 设备接到驱动模型。


0. 清单这条要明白什么

core.c 管设备怎么出现。

dd.c 管 match 成功之后怎么 probe / remove

bus.c 管两张 klist。

本文件把这三份的槽填成 platform 业务

  1. 设备怎么变成 platformparent / bus / 名字 / claim 资源 / device_add
  2. 驱动怎么挂上这条总线__platform_driver_registerdriver.bus 指过来。
  3. 怎么匹配platform_match ------ virt 上是 of_match_tablecompatible
  4. 怎么进你的 probeplatform_probe 再转发 platform_driver.probe(pdev)

不是 把 1600 行读完。PM、DMA、irq affinity、platform_driver_probe()、bundle 跳过。

DT 节点怎么展开成 platform_devicedrivers/of/platform.c(1.3)。

精读函数:

函数 角色
platform_bus_init 启动早期登记:父设备 + 总线,两件东西
platform_device_add / register 设备接到 platform 总线
__platform_driver_register 驱动接到 platform 总线
platform_match bus->match:非 0 才 probe
platform_match_id 没 DT 时的旧名字表(第三档)
platform_probe / platform_remove bus->probe / bus->remove,再转到驱动
platform_get_resource / get_irq / devm_platform_ioremap_resource probe 里取 reg / IRQ(扫一眼)

可跳过:__platform_matchplatform_find_device_by_driver(查找封装,probe 不走)。


1. 两个对象,不是一个

复制代码
platform_bus_init()
  → device_register(&platform_bus)     // 一台 struct device,默认父设备
  → bus_register(&platform_bus_type)   // 那份 bus_type:两张 klist + 槽
对象 类型 出现在哪 干什么
platform_bus struct device /sys/devices/platform 没指定 parent 时的挂靠点
platform_bus_type struct bus_type /sys/bus/platform .match / .probe / .remove,以及 klist_devices / klist_drivers

platform_device_add 里若 !dev->parent,就 parent = &platform_bus

之后 DT 展开、你的 platform_driver 才有地方挂。

platform_bus_type 槽位(前面 bus.c / dd.c 调的都是这些指针):

填谁 谁调用
.match platform_match dd.cdriver_match_device
.probe platform_probe dd.ccall_driver_probe(优先 bus->probe
.remove platform_remove dd.cdevice_remove
.uevent platform_uevent 热插/模块别名:MODALIAS=of:N...Cdemo,hello

2. 设备怎么挂上去

2.1 platform_device_register = initialize + add

对应 device_register:一次做完。中间多 setup_pdev_dma_masks(没设过 DMA mask 就默认 32-bit)。

调用约定:

  • 你自己准备好 pdev(静态或 kzalloc),填好 name / id / resource,再调本函数。
  • 不要和 platform_device_alloc 叠用 :alloc 里已经 device_initialize。alloc 出来的走 platform_device_add
  • 返回 0 成功。进过 initialize 后失败不能 kfree,必须 platform_device_put(里面是 put_device)。
  • 注销成对:platform_device_unregister = platform_device_del + platform_device_put

2.2 platform_device_add 五步

清单把「设备怎么变成 platform」落在这里看最清楚。返回 0 成功;失败会把已 insert 的 resource / 自动 ID 吐回去。

  1. parent :没指定就挂到 &platform_bus/sys/devices/platform)。
  2. dev->bus = &platform_bus_type:之后 match/probe 走 platform,不是 PCI/AMBA。
  3. 名字 (sysfs 目录):
    • 普通整数:name.id
    • PLATFORM_DEVID_NONE:只有 name
    • PLATFORM_DEVID_AUTO:IDA 拿号,写成 name.id.auto;失败要把号还回去
  4. claim 资源IORESOURCE_MEMiomem_resourceIORESOURCE_IOioport_resource,避免两台设备抢同一段物理地址。
  5. device_add :进 core.c → 入总线设备表、建 sysfs,再 bus_probe_device

2.3 virt DT 多数不进 platform_device_add

of_platform 自己设 dev->bus = &platform_bus_type,再 device_addof_device_add)。

步骤等价:parent、bus、资源、进层次。改 virt.dts 加节点就是在枚举,不靠手写 platform_device_register


3. 驱动怎么挂上去

复制代码
platform_driver_register(drv)
  = __platform_driver_register(drv, THIS_MODULE)
      drv->driver.owner = owner
      drv->driver.bus   = &platform_bus_type   // ★ 挂到这条总线
      driver_register(&drv->driver)            // bus.c:入 klist_drivers,再 attach

写驱动填的是 platform_driver.probe(struct platform_device *) ,不是 device_driver.probe

driver.c 里那条 pr_warn:总线已经有 .probe 时不要再填 device_driver.probe(会被忽略)。

注销:platform_driver_unregisterdriver_unregister


4. 匹配:platform_match

bus_type.matchdd.cdriver_match_device 调这里,非 0 才 probe

英文 kerneldoc 还停留在旧的名字比对;今日 virt 真正走 OF 那一档。

四档顺序(有 driver_override 时只认指定驱动名,后面全跳过):

顺序 比什么 virt 上
0 pdev->driver_override == drv->name sysfs 强制绑定,清单可后放
1 of_driver_match_device 主路径
2 acpi_driver_match_device 用不到
3 pdrv->id_tableplatform_match_id 没 DT 的旧名字表
4 pdev->name == drv->name 最后兜底

第 1 档展开:

复制代码
of_driver_match_device(dev, drv)
  = of_match_device(drv->of_match_table, dev) != NULL
      → of_match_node(matches, dev->of_node)
          → 扫 of_device_id[],对节点 compatible(1.3 再细看)

驱动侧表在 platform_driver.driver.of_match_table(类型 struct of_device_id,字段 compatible[128],见 mod_devicetable.h)。

设备侧字符串在 DT 节点的 compatible,展开后挂在 dev->of_node

写对 compatible = "demo,hello":第 1 档命中,进 platform_probe

写错:设备照样进 klist_devices 和 sysfs,卡在 platform_match,不 probe

第 3 档细节(platform_match_id):

  • 表是 struct platform_device_id[],以空 name{ })收尾。
  • 比的是 pdev->name ,不是 sysfs 的 name.id
  • 命中写入 pdev->id_entry(probe 可从里面读 driver_data),返回该条;否则 NULL
  • id_table 却一条都不中,不再用 drv->name 兜底。

5. probe / remove

5.1 platform_probe

call_driver_probe 优先调 bus->probe,所以会进这里,再转到 platform_driver.probe(pdev)

进这里时 match 已经成功 。参数仍是 struct device *,先 to_platform_device

返回值交给 dd.c

返回 含义
0 绑定成功
非 0 拆掉半绑状态(devm_* 一并清)
-EPROBE_DEFER 进延迟队列稍后再试

probe 之前两件板级准备(virt 上多数空转):

  1. of_clk_set_defaults:DT assigned-clocks,没有 of_node 就空转。
  2. dev_pm_domain_attach:挂 power domain。你的 probe 失败会 detach,避免半挂。

platform_driver_probe() 是另一条旧路径(probe 放在 __init 里、不允许 defer)。命中时 .probe 已被换成 platform_probe_fail,直接 -ENXIO。清单跳过。

5.2 platform_remove

对称卸:优先 remove_newvoid),否则旧的 remove(返回值会被忽略并警告),再 dev_pm_domain_detach


6. probe 里怎么拿 reg / IRQ(扫一眼)

DT 的 reg / interrupts 展开后躺在 pdev->resource[](填表是 1.3)。本文件只提供取用:

API 干什么
platform_get_resource(pdev, type, n) 按类型+下标取一条 struct resource,没有则 NULL
platform_get_irq(pdev, n) 取 IRQ 号;OF 上会走 of_irq_get,可能 -EPROBE_DEFER
devm_platform_ioremap_resource(pdev, n) get MEM 资源 + devm_ioremap_resource;设备走时自动 unmap

为何优先 devm_*:寿命挂在 struct device 上,remove / probe 失败时 devres_release_all 一起放。机制在 devres.c,本文件只是 platform 包装。


7. 查找封装

__platform_match:给 bus_find_device 用的适配器。回调原型是 (device, const void *),把 data 转回 device_driver * 再交给 platform_match

不是 bus_type.match。probe 匹配走的是 platform_bus_type.match = platform_match

platform_find_device_by_driver(start, drv):在设备表里找一台 match 能对上 的设备(不是看已经绑了谁)。

  • start = NULL 从头扫;非 NULL 从下一个开始。
  • 命中多计一次引用,用完必须 put_device;没有则 NULL
  • DRM 等用它找子设备再 device_link_add。probe 不走这里。

相关推荐
小孩玩什么1 小时前
深入理解字符串匹配算法:BF算法,KMP算法
java·c语言·开发语言·数据结构·c++·算法
微露清风1 小时前
Linux 虚拟地址空间:一道屏障、一张地图、一场延迟兑现的博弈
linux·服务器
zx_741484811 小时前
【Linux入门】Shell 与第一个 Shell 脚本:认识命令行解释器,写出 Hello World
linux·运维
clz13145211 小时前
设备反欺诈的四大支柱:从设备指纹到关联图谱
开发语言·php
clear sky .2 小时前
内存8字节对齐是什么?
c语言·stm32·嵌入式硬件
无忧.芙桃2 小时前
C++内存管理
c语言·开发语言·c++·青少年编程
Patrick在香港2 小时前
Python 分析香港 AQHI 归档 1644 天:同一天的空气,早上 8 点报 3,下午 5 点报 10
开发语言·python·数据分析·api·restful·数据可视化·开放数据
晴天162 小时前
JavaScript 与 Java 垃圾回收机制对比
java·开发语言·javascript
金金计较.2 小时前
Go语言-2
开发语言·算法·golang