第四阶段:SPI 驱动注册与匹配 第四阶段SPI 驱动注册与匹配4.1 入口spi_register_driver 宏#definespi_register_driver(driver)\__spi_register_driver(THIS_MODULE,driver)知识点 6 宏技巧 知识点 2 函数指针。内核自动传入THIS_MODULE当前模块指针省去驱动作者每次填。4.2 __spi_register_driver —— 框架包装int__spi_register_driver(structmodule*owner,structspi_driver*sdrv){sdrv-driver.ownerowner;sdrv-driver.busspi_bus_type;// ① 绑定到 SPI 总线if(sdrv-probe)sdrv-driver.probespi_drv_probe;// ② 替换 probe 为 SPI 封装版if(sdrv-remove)sdrv-driver.removespi_drv_remove;// ③ 替换 remove 为 SPI 封装版if(sdrv-shutdown)sdrv-driver.shutdownspi_drv_shutdown;// ④ 替换 shutdownreturndriver_register(sdrv-driver);// ⑤ 进入驱动模型核心}① 绑定总线driver.bus spi_bus_type—— 告诉驱动模型这个驱动属于 SPI 总线。后面bus_add_driver会把它挂到/sys/bus/spi/drivers/下。② 替换 probe——知识点 2 实战驱动作者写的 probe 是sdrv-probe。框架把它藏起来替换成spi_drv_probe。以后really_probe调用spi_drv_probespi_drv_probe做通用准备工作时钟、中断、电源管理后才调用sdrv-probe。这就是知识点 2 的函数指针替换——框架插入自己的逻辑驱动作者无感知.4.3 driver_register → bus_add_driverdriver_register是驱动模型核心进入bus_add_driverdriver_register(sdrv-driver)└──bus_add_driver(drv)├── 在/sys/bus/spi/drivers/下创建目录如 spidev/ ├── klist_add_tail → klist_drivers驱动登记到总线的驱动链表 └──driver_attach(drv)← 尝试匹配已有设备 └──bus_for_each_dev(spi_bus_type,...)└──__driver_attach(dev,drv)└──driver_match_device(drv,dev)└──spi_match_device(dev,drv)← 核心注意方向driver_attach遍历的是已在总线上的所有设备——检查有没有设备在等这个驱动。反过来第三阶段device_add后遍历的是已注册的驱动——检查有没有驱动在等这个设备。两边的匹配代码是同一套。4.4 spi_match_device —— 四种匹配方式staticintspi_match_device(structdevice*dev,structdevice_driver*drv){conststructspi_device*spito_spi_device(dev);// 知识点1conststructspi_driver*sdrvto_spi_driver(drv);// 知识点1if(of_driver_match_device(dev,drv))// ① 设备树匹配return1;if(acpi_driver_match_device(dev,drv))// ② ACPI 匹配x86ARM跳过return1;if(sdrv-id_table)// ③ ID 表匹配return!!spi_match_id(sdrv-id_table,spi);returnstrcmp(spi-modalias,drv-name)0;// ④ 名称回退}四种方式的优先级① 设备树匹配最高优先级驱动侧定义of_match_tablestaticconststructof_device_idmy_of_match[]{{.compatiblejedec,spi-nor},{}};设备树的compatible jedec,spi-nor和驱动的of_match_table逐字符串比较。匹配成功后spi-dev.of_node指向设备节点。② ACPI 匹配x86 平台用ARM 跳过。③ ID 表匹配驱动侧定义id_tablestaticconststructspi_device_idmy_ids[]{{w25q128},{mx25l25635e},{}};spi_match_id遍历id_table和spi-modalias比较。④ 名称回退如果驱动没提供of_match_table也没提供id_table直接用drv-name和spi-modalias做字符串比较。spi-modalias是jedec,spi-nor不含spi:前缀时drv-name是w25q128——有一个配对就匹配成功。4.5 匹配成功 → probe 调用链driver_match_device 返回 1匹配成功 └── driver_probe_device(dev, drv) └── really_probe(dev, drv) └── drv-probe(dev) ← spi_drv_probe ├── of_clk_set_defaults ← 配置设备树时钟 ├── of_irq_get ← 获取中断号 ├── dev_pm_domain_attach ← 电源管理 └── sdrv-probe(spi) ← 驱动自己的 probe4.6 spi_drv_probe —— SPI 层的 probe 封装staticintspi_drv_probe(structdevice*dev){conststructspi_driver*sdrvto_spi_driver(dev-driver);// 知识点1structspi_device*spito_spi_device(dev);// 知识点1retof_clk_set_defaults(dev-of_node,false);// 设备树时钟配置if(ret)returnret;if(dev-of_node){spi-irqof_irq_get(dev-of_node,0);// 从设备树获取中断号if(spi-irq-EPROBE_DEFER)// 中断控制器还没好return-EPROBE_DEFER;// 告诉框架等会儿再试if(spi-irq0)spi-irq0;// 没中断给0}retdev_pm_domain_attach(dev,true);// 电源管理域if(ret!-EPROBE_DEFER){retsdrv-probe(spi);// 调用驱动自己的probeif(ret)dev_pm_domain_detach(dev,true);// probe失败 → 断开电源管理}returnret;}完整流程spi_drv_probe(dev) │ ├── 行1-2: container_of拿 spi_driver 和 spi_device ├── 行3: 设备树时钟配置有就配没有跳过 ├── 行5: 从设备树读中断号 ├── 行6: 如果是 EPROBE_DEFER → 等中断控制器加载 ├── 行7: 如果中断无效 → 设为 0 ├── 行8: 接电源管理 ├── 行9: sdrv-probe(spi) ← 驱动作者自己的 probe └── 行10: probe 失败 → 断电源管理关键细节EPROBE_DEFER这不是失败是等一下。of_irq_get找中断号时需要中断控制器GPIO 控制器驱动已经加载。如果 GPIO 驱动还没加载of_irq_get返回-EPROBE_DEFER意思是你要的东西还没准备好等会儿再试。spi_drv_probe收到这个信号直接返回-EPROBE_DEFER。内核上层really_probe看到这个返回值把设备放到一个延迟探测定队列里。等 GPIO 驱动加载后内核再次从队列里取出这个设备重新调用 probe。完整调用链驱动侧 设备侧如果设备先注册 spi_register_driver(sdrv) spi_add_device(spi) [第三阶段] └── __spi_register_driver └── device_add └── driver_register └── bus_add_device └── bus_add_driver └── device_initial_probe └── driver_attach └── driver_probe_device └── __driver_attach └── 同一条匹配链 └── driver_match_device └── spi_match_device ├── OF match ├── ACPI match ├── id_table match └── modalias match ↓ really_probe └── spi_drv_probe └── sdrv-probe(spi)无论驱动先注册还是设备先注册最终都走到同一条匹配链。后面注册的触发对方向的匹配。检验问题Q1L1__spi_register_driver中把sdrv-probe替换成了spi_drv_probe。spi_drv_probe在调用sdrv-probe之前做了哪些准备工作1. 配好设备树里指定的时钟 2. 读出设备树里指定的中断 3. 接好设备可能需要的电源管理 流程of_clk_set_defaults → of_irq_get → dev_pm_domain_attach → sdrv-probeQ2L2如果驱动的of_match_table和id_table都提供了设备树也匹配成功了最终用哪种方式匹配从spi_match_device的代码顺序说明原因。会先使用设备数匹配 如果匹配成功就return返回不再执行下面的流程若失败则继续往下执行从acpi、到id_table匹配 、再到名称匹配spi_match_device代码的匹配顺序是从设备树、acpe、id表匹配、名称匹配Q3L3of_irq_get返回-EPROBE_DEFER时spi_drv_probe不会调用sdrv-probe。那这个设备的 probe 什么时候会被重新触发需要谁来触发返回-EPROBE_DEFER是因为gpio驱动没加载当将来gpio驱动重新加载时会再次由driver_deferred_probe_trigger()触发具体流程如下完整的 EPROBE_DEFER 故事1. spi_drv_probe 调用 of_irq_get → 返回 -EPROBE_DEFERGPIO 驱动没加载 2. spi_drv_probe 返回 -EPROBE_DEFER 给 really_probe 3. really_probe 走到 probe_failed 清理路径 - 清空 dev-driver解绑 - 释放 devres 资源 - driver_deferred_probe_add(dev)设备加入等待队列 4. 将来 GPIO 驱动加载时触发 driver_deferred_probe_trigger() 5. 内核从等待队列取出设备重新跑 really_probe → spi_drv_probe → of_irq_get 6. 这次 GPIO 驱动已就绪of_irq_get 成功 → sdrv-probe(spi) 被调用