
在你这个PCIe-FPGA-I2C例子里Linux 的“总线-设备-驱动模型”不是只出现一次而是出现了两层第一层PCIe 总线设备驱动模型 PCIe bus └── PCIe FPGA device └── fpga_pci_driver 第二层I2C 总线设备驱动模型 I2C bus也就是 i2c_adapter └── I2C client device比如 tmp102 / eeprom / pmbus └── 对应的 i2c_driver也就是说PCIe 负责发现 FPGA 这个大设备I2C 子系统负责管理 FPGA 内部 I2C 控制器下面挂的 I2C 小设备。1. Linux 总线设备驱动模型的通用形式Linux driver model 可以抽象成bus ├── device └── driver匹配成功后bus.match(device, driver) ↓ driver.probe(device)内核文档中也说明驱动的probe()会在设备和驱动绑定时被调用并且驱动通常会把通用struct device转成 bus-specific 类型例如struct pci_dev或其他设备类型。(内核文档)所以最核心是设备挂在哪条 bus 上就由那条 bus 的规则来匹配 driver。2. 第一层PCIe 总线模型你的 FPGA 板卡先是一个 PCIe Endpoint。Linux PCI core 枚举后会创建structpci_dev这个pci_dev挂在 PCI bus 上。然后你写 PCIe 父驱动staticconststructpci_device_idfpga_ids[]{{PCI_DEVICE(0x1234,0x5678)},{0,}};staticstructpci_driverfpga_pci_driver{.namefpga-pcie,.id_tablefpga_ids,.probefpga_pci_probe,.removefpga_pci_remove,};PCI 驱动文档说明struct pci_driver里包含驱动名、感兴趣的设备 ID 表、probe等成员PCI core 会用id_table匹配设备并调用probe()。(内核文档)这时第一层模型是PCI bus ├── device: struct pci_dev │ └── 代表 FPGA PCIe Endpoint │ └── driver: struct pci_driver └── fpga_pci_driver匹配过程PCI core 发现 FPGA ↓ 创建 pci_dev ↓ 用 Vendor ID / Device ID 匹配 pci_driver.id_table ↓ 匹配成功 ↓ 调用 fpga_pci_probe()fpga_pci_probe()里做pci_enable_device() pci_request_regions() pci_iomap() / pcim_iomap() 得到 BAR0 初始化 FPGA到这里为止Linux 只知道有一个 PCIe FPGA 设备。还不知道FPGA 内部有一个 I2C 控制器。3. PCIe 父驱动创建/注册 FPGA-I2C 控制器在fpga_pci_probe()里你可以做两种事情。简单方式直接注册 i2c_adapter工程化方式创建一个 fpga-i2c 子设备 让 fpga-i2c 子驱动 probe 在子驱动里注册 i2c_adapter不管哪种方式结果都是Linux I2C core 里多了一条 I2C bus也就是一个 i2c_adapter。例如fi2c-adap.algofpga_i2c_algo;fi2c-adap.dev.parentpdev-dev;i2c_set_adapdata(fi2c-adap,fi2c);devm_i2c_add_adapter(pdev-dev,fi2c-adap);Linux I2C 文档说I2C controller 在 Linux 中也叫 adapter 或 buscontroller driver 通常在drivers/i2c/busses/目录下。(内核文档)I2C 文档也说明I2C adapter driver 用来抽象控制器硬件它可以绑定到 PCI 设备或 platform device并暴露struct i2c_adapter。(内核文档)这句话正好对应你的场景FPGA I2C Controller 是硬件 fpga-i2c adapter driver 是它的 Linux 驱动 struct i2c_adapter 是 Linux I2C core 看到的总线对象。4. 第二层I2C 总线模型注册i2c_adapter后Linux 里出现一条 I2C 总线例如i2c-5这时候第二层总线模型开始体现I2C bus / i2c_adapter ├── device: struct i2c_client │ ├── tmp102 at 0x48 │ ├── eeprom at 0x50 │ └── pmbus device at 0x58 │ └── driver: struct i2c_driver ├── tmp102_driver ├── at24_driver └── pmbus_driver也就是说i2c_adapter 代表 I2C 控制器/总线 i2c_client 代表挂在这条总线上的 I2C 从设备 i2c_driver 代表这些 I2C 从设备的驱动。I2C client driver 文档说明I2C/SMBus 设备驱动是面向挂在 I2C 总线上的 client 设备来写的。(内核文档)5. I2C 设备和 I2C 驱动怎么匹配假设 FPGA I2C 总线上有一个 TMP102 温度传感器地址0x48。你可以创建一个 I2C clientechotmp102 0x48/sys/bus/i2c/devices/i2c-5/new_device这会创建类似5-0048也就是i2c-5 总线上的 0x48 设备Linux I2C core 会尝试匹配对应的i2c_driver比如tmp102_driver。匹配成功后调用tmp102_probe()这就是第二层 bus-device-driver 模型I2C core 创建 i2c_client ↓ I2C bus 匹配 i2c_driver ↓ 调用 tmp102_driver.probe()6. 关键两个 probe 分别干不同的事这里很重要。第一层 probePCIe 父驱动 probefpga_pci_probe(structpci_dev*pdev,...)它负责识别 PCIe FPGA 映射 BAR 初始化 FPGA 全局资源 注册/创建 FPGA-I2C adapter它面对的是PCIe 设备第二层 probeI2C client driver probe例如tmp102_probe(structi2c_client*client)它负责识别 TMP102 芯片 读取 TMP102 寄存器 注册 hwmon 暴露 temp1_input它面对的是I2C 从设备它不关心底层 I2C controller 是SoC I2C USB-I2C GPIO bitbang I2C PCIe-FPGA-I2C它只知道自己有一个struct i2c_client可以通过 I2C core 发传输。7. FPGA-I2C adapter driver 在中间起什么作用它是第二层 I2C 总线的“控制器驱动”。它注册structi2c_adapter并实现structi2c_algorithm{.master_xferfpga_i2c_master_xfer,.functionalityfpga_i2c_func,};当 TMP102 驱动要读温度时tmp102 driver ↓ i2c_smbus_read_word_data() ↓ Linux I2C core ↓ i2c_transfer() ↓ fpga_i2c_master_xfer() ↓ writel/readl BAR ↓ FPGA I2C 状态机 ↓ SCL/SDA ↓ TMP102所以在第二层 I2C 模型里fpga_i2c_adapter I2C bus/controller tmp102/eeprom I2C device/client tmp102_driver/eeprom_driver I2C device driver8. 用完整层级图表示你的系统可以画成这样PCIe bus │ ├── device: FPGA PCIe Endpoint │ 类型struct pci_dev │ 匹配Vendor ID / Device ID │ └── driver: fpga_pci_driver 类型struct pci_driver probe: fpga_pci_probe() 作用 - 映射 BAR0 - 初始化 FPGA - 注册 fpga_i2c_adapter │ ▼ I2C bus / adapter: i2c-5 类型struct i2c_adapter 传输函数fpga_i2c_master_xfer() │ ├── device: tmp102 at 0x48 │ 类型struct i2c_client │ driver: tmp102_driver │ ├── device: eeprom at 0x50 │ 类型struct i2c_client │ driver: at24_driver │ └── device: pmbus at 0x58 类型struct i2c_client driver: pmbus_driver这就是总线设备驱动模型在这个例子里的体现。9. 你可以把它理解成两次“注册到标准模型”第一次FPGA 作为 PCIe 设备注册到 PCI bus 模型对应pci_dev pci_driver第二次FPGA 内部 I2C 控制器作为 I2C adapter 注册到 I2C bus 模型对应i2c_adapter i2c_client i2c_driver所以完整过程是PCIe 枚举发现 FPGA ↓ PCI bus 匹配 fpga_pci_driver ↓ fpga_pci_probe() ↓ 映射 BAR发现内部有 I2C 控制器 ↓ 注册 i2c_adapter ↓ Linux I2C core 出现 i2c-X ↓ 在 i2c-X 上创建设备 0x48 / 0x50 ↓ I2C bus 匹配 tmp102 / eeprom 驱动 ↓ 这些 I2C 驱动通过 fpga_i2c_master_xfer() 间接访问 FPGA BAR10. 最核心一句话在这个例子中PCIe 总线模型负责把 FPGA 这个“大设备”绑定到 PCIe 父驱动I2C 总线模型负责把 FPGA 内部 I2C 控制器抽象成一条 I2C bus再把挂在这条 bus 上的温度传感器、EEPROM 等“小设备”绑定到各自的 I2C 驱动。换句话说PCIe 层解决Linux 怎么找到 FPGA I2C adapter 层解决Linux 怎么把 FPGA 内部 I2C 控制器当成一条 I2C 总线 I2C client 层解决Linux 怎么把这条总线上的具体 I2C 芯片交给标准驱动所以你前面理解的“适配层桥接”没有错只是放到总线设备驱动模型里看就是pci_driver.probe() 创建/注册 i2c_adapter i2c_adapter 成为新的 I2C bus i2c_client 挂到这个 I2C bus 上 i2c_driver 匹配并驱动这些 I2C client