---
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)