--- title: i2c driver url: https://hackmd.io/NlfiWjacQiCMbqh4UU_Dcg lastSync: 2025-07-05T10:41:39.776Z --- ## pmbus 最主要是 `module_i2c_driver(mpc42013_driver);` ,其 `struct` 定義: - `static struct i2c_driver mpc42013_driver` - `.driver` - `.probe_new` - .`remove` - `.id_table` | 欄位名稱 | 類型 | 作用 | 什麼時候會被呼叫 | | ------------ | ------------------------------------- | ---------------------------------------------- | ------------------------------------- | | `.driver` | `struct device_driver` | 提供「驅動的基本資訊」與「配對資訊」,像是名字、支援哪些 Device Tree 相容字串等 | 核心 driver model 用來做比對與註冊 | | `.probe_new` | `int (*)(struct i2c_client *client)` | 驅動成功匹配裝置時會執行的初始化函式(新 API,建議使用) | 有相容裝置註冊時由 `i2c_device_probe()` 呼叫 | | `.remove` | `void (*)(struct i2c_client *client)` | 當裝置被移除時(例如 hotplug 拔除、模組卸載),這個函式會負責做清理 | 在驅動卸載、裝置移除時由 `i2c_device_remove()` 呼叫 | | `.id_table` | `const struct i2c_device_id *` | 用於 legacy 比對(非 Device Tree 的平台),像 x86 上的硬體 | 傳統平台配對用,會傳入 `probe()` 的第二參數 | 這是來自 [`include/linux/i2c.h` ](https://elixir.bootlin.com/linux/v6.15.4/source/include/linux/i2c.h#L974) 的 marco ### `module_i2c_driver` - module_i2c_driver marc ``` #define module_i2c_driver(__i2c_driver) \ module_driver(__i2c_driver, i2c_add_driver, i2c_del_driver) ``` - 再展開在`include/linux/device.h`的 `module_driver` ``` #define module_driver(__driver, __register, __unregister) \ static int __init __driver##_init(void) \ { return __register(&(__driver)); } \ static void __exit __driver##_exit(void) \ { __unregister(&(__driver)); } \ module_init(__driver##_init); \ module_exit(__driver##_exit); ``` 裡面的要填寫的結構定義在 `include/linux/i2c.h` ``` struct i2c_driver { unsigned int class; // 當 kernel 找到匹配的 I2C 裝置後會呼叫 probe() int (*probe)(struct i2c_client *client); // 裝置移除時呼叫 void (*remove)(struct i2c_client *client); // 系統關機或重新啟動時會呼叫 void (*shutdown)(struct i2c_client *client); // SMBus alert 機制(可選) void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol, unsigned int data); // 可選:如 ioctl 介面給使用者空間 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); // 核心的 generic driver model 結構 struct device_driver driver; // 用來匹配 I2C 裝置 ID(legacy) const struct i2c_device_id *id_table; // 若是 Device Tree 平台,這是用來 match 的 DT compatible list const struct of_device_id *of_match_table; // 用於非 OF 平台上的 auto-detect(常用於 hwmon) int (*detect)(struct i2c_client *client, struct i2c_board_info *info); const unsigned short *address_list; // 核心維護的 device list struct list_head clients; u32 flags; // enum i2c_driver_flags 中的位元旗標 }; ``` 其中 `.driver` 是在 `include/linux/device.h` 的`struct device_driver` - `include/linux/device.h` ``` /** * struct device_driver - The basic device driver structure * @name: Name of the device driver. * @bus: The bus which the device of this driver belongs to. * @probe: Callback to check if the driver can handle a given device. * @remove: Callback to remove the device. * ... */ struct device_driver { const char *name; struct bus_type *bus; struct module *owner; const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; int (*probe) (struct device *dev); void (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); ... }; ``` | 欄位 | 說明 | | ------------------ | --------------------------------------------------- | | `name` | 驅動名稱,用於 sysfs (`/sys/bus/.../drivers/`) | | `bus` | 所屬的 bus(如 `i2c_bus_type`, `pci_bus_type`) | | `owner` | 通常填入 `THIS_MODULE`,讓 kernel 知道這個 driver 的 module 是誰 | | `probe()` | 當有裝置與此驅動匹配時,會被呼叫 | | `remove()` | 裝置移除時呼叫 | | `of_match_table` | Device Tree 的 `compatible` 比對表 | | `acpi_match_table` | ACPI 裝置比對表(在 x86 平台常用) | ### `.driver` - name - of_match_table: - `.compatible` : 會去找對應在 Device Tree 中的 compatible 欄位 - `.data` (optional) ### `.probe` - 定義在 `include/linux/i2c.h` 裡的 `struct i2c_driver` 中: ``` struct i2c_driver { ... int (*probe)(struct i2c_client *client, const struct i2c_device_id *id); ... }; ``` - 會在 [`drivers/i2c/i2c-core-base.c`](https://elixir.bootlin.com/linux/v6.15.4/source/drivers/i2c/i2c-core-base.c#L491) 裡面的 `i2c_device_probe()` 被呼叫,這是當有驅動與裝置配對成功時 ``` // drivers/i2c/i2c-core-base.c // 函式名稱:i2c_device_probe() static int i2c_device_probe(struct device *dev) { ... const struct i2c_device_id *id; // 把 driver 轉成 i2c_driver 型別 struct i2c_driver *driver = to_i2c_driver(dev->driver); ... if (driver->probe_new) { // 如果你實作的是 probe_new,就用這個 status = driver->probe_new(client); } else { // 否則使用傳統的 probe(),這時還會找 id_table id = i2c_match_id(driver->id_table, client); if (!id) return -ENODEV; status = driver->probe(client, id); } ... } ``` ```mermaid graph TD subgraph 系統事件 A1[Device Tree/ACPI 宣告一個 I2C device] A2["或是手動呼叫 i2c_new_client_device()"] end subgraph I2C Core B1[i2c-core 建立 struct device] B2[呼叫 bus_type.match: i2c_device_match] B3[match 成功 → 呼叫 bus_type.probe: i2c_device_probe] B4["進入驅動的 probe() or probe_new()"] end A1 --> B1 A2 --> B1 B1 --> B2 B2 -->|match| B3 --> B4 ``` ```mermaid graph TD A[Device Tree / Platform 裝置註冊] --> B[i2c-core-base.c] B --> C["i2c_device_probe()"] C -->|driver->probe_new 存在?| D["是 → 呼叫 probe_new()"] C -->|否 → 找 id_table 比對| E["呼叫 probe(client, id)"] ``` | 名稱 | 傳統 API | 新 API(建議用) | | ------------- | ------------- | ---------- | | `probe()` | 帶 `id_table` | 否 | | `probe_new()` | 不帶 `id_table` | ✅ 較簡單現代寫法 | |差異|`probe()`|`probe_new()`| |---|---|---| |參數|`struct i2c_client *, const struct i2c_device_id *`|`struct i2c_client *`| |匹配來源|傳統 I2C core 用 `.id_table` 傳入|Device Tree / ACPI 直接用 `.of_match_table`| |適用平台|非 OF、非 ACPI 系統|現代嵌入式系統(使用 Device Tree 或 ACPI)| |哪個比較新?|✅ 比較舊|✅ 建議使用的新式方式| ### `.id_table` legacy 平台(非 Device Tree)配對用 - 用於非 Device Tree 的平台(例如 x86 BIOS 平台) - 比對方式是透過 `client->name` 比對 `mychip` 字串 - 用 `probe_new` 的時候不一定要寫 ## debug - `dev_info(&client->dev, "Using probe_new() for device %s\n", dev_name(&client->dev));` ## TODO - https://www.kernel.org/doc/html/v6.12/driver-api/i2c.html - https://jasonblog.github.io/note/linux_kernel/Modern_Maker/Day11.html - [今晚我想來點不一樣的 Driver: I2C Device](https://hackmd.io/@RinHizakura/BJDTZnUsF)