写 linux i2c 驱动,真正卡住新手的往往不是总线时序,而是那几个结构体到底谁管谁,以及接口的返回值到底代表什么。上一篇把 i2c 子系统的分层捋了一遍,这一篇专门啃结构体与接口:i2c_adapter、i2c_algorithm、i2c_client、i2c_driver、i2c_msg 这五个东西的字段含义、生命周期和相互引用关系,还有 i2c_transfer、i2c_master_send、i2c_smbus_read_byte_data 这批接口在什么场景下用哪个、出错时该盯着哪个错误码看。如果你刚接上一个传感器或者 EEPROM,能编译通过但 probe 不进来、能进来但读回 0xFF、读一次对读两次错,那这篇基本就是给你写的。我会从结构体的字段讲起,一路讲到控制器侧的 algorithm 怎么实现、上拉电阻怎么算、设备树和板级信息两条注册路径怎么选,最后把我在实际项目里踩过的坑整理成一张对照表。代码基于较新的内核主线写法,老版本内核的差异我会单独标出来。
1. 五个结构体撑起一条 I2C 总线
1.1 adapter 与 algorithm:一个代表总线,一个负责动手
struct i2c_adapter是内核眼里的一条物理总线。你插上一条 I2C,内核就有一个 adapter 实例,用户空间i2cdetect -l列出来的每一行,对应的就是一个 adapter。它的关键字段不多,但每一个都值得记住:
struct i2c_adapter { struct module *owner; unsigned int class; const struct i2c_algorithm *algo; void *algo_data; int nr; /* 总线编号,i2c-0、i2c-1 的那个数字 */ char name[48]; struct device dev; /* 内嵌设备,parent 指向控制器 */ struct rt_mutex bus_lock; int timeout; /* 毫秒 */ int retries; struct i2c_bus_recovery_info *bus_recovery_info; const struct i2c_adapter_quirks *quirks; };nr是总线号,它决定了/dev/i2c-N和/sys/bus/i2c/devices/i2c-N里的那个 N。dev.parent一般指向控制器自己的struct device,这一层父子关系在电源管理里很要紧——控制器休眠,挂在它下面的 adapter 和所有 client 都会跟着走一遍 suspend/resume。bus_lock是给设备驱动用的递归锁,i2c_transfer内部会拿它;lock是留给 algorithm 实现者自己用的,两者别搞混,混用会在多设备并发访问时出现死锁。
i2c_algorithm才是真正干活的那一层,它是一个函数指针集合,由控制器驱动实现:
struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*master_xfer_atomic)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); u32 (*functionality)(struct i2c_adapter *adap); };master_xfer是核心,参数是 msg 数组和数量,返回成功处理的 msg 个数。master_xfer_atomic是给原子上下文用的版本——比如系统掉电重启的最后一刻,你要给 PMIC 写一个关机序列,那时候不能睡眠,只能用轮询方式往寄存器里怼。绝大多数控制器驱动不会实现它,不实现也没关系,核心层会返回-EOPNOTSUPP,调用方自己得判断。functionality返回一组I2C_FUNC_*位掩码,用来说明这条总线支持什么:I2C_FUNC_I2C表示支持裸传输,I2C_FUNC_SMBUS_BYTE_DATA表示支持 SMBus 字节数据读写,I2C_FUNC_10BIT_ADDR表示支持 10 位地址。设备驱动在 probe 里第一件事就该是i2c_check_functionality(adapter, I2C_FUNC_I2C),不满足直接-EOPNOTSUPP返回,别等到传输时才炸。
1.2 client 与 driver:一次配对的两个人
i2c_client代表挂在总线上的一个从设备,i2c_driver是驱动这类的设备的代码。前者是"硬件描述",后者是"行为逻辑",内核的 driver core 负责把它们撮合到一起。
struct i2c_client { unsigned short flags; /* I2C_CLIENT_TEN、I2C_CLIENT_PEC 等 */ unsigned short addr; /* 7 位地址,左对齐前的那 7 位 */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; struct device dev; int irq; };addr这里必须强调一句:它是 7 位地址,不是 datasheet 上那个 8 位写地址。很多传感器手册给的地址形如0x90(写)/0x91(读),这是把 7 位地址左移一位后补了读写位。填进i2c_client.addr或者设备树reg属性里的,应该是0x48。这个坑每年都要埋掉一批人,因为填错的后果不是编译报错,而是 probe 时静默超时或者读回全 0xFF。irq字段是可选的中断号,很多传感器支持数据就绪中断,走request_threaded_irq比轮询省电得多。flags里I2C_CLIENT_TEN表示 10 位地址,I2C_CLIENT_PEC表示开启 SMBus 的包错误校验。
i2c_driver的骨架是这样的:
struct i2c_driver { unsigned int class; int (*probe)(struct i2c_client *client); void (*remove)(struct i2c_client *client); void (*shutdown)(struct i2c_client *client); int (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol, unsigned int data); const struct i2c_device_id *id_table; struct device_driver driver; /* 内含 name、of_match_table、pm */ };probe的签名在新内核里已经统一成只接收struct i2c_client *。早几年还有probe_new和老的probe(client, id)两套并存,现在老签名已经删干净了,probe_new也改名回probe。remove的返回值同样在往void收敛,中间有一段remove和remove_new并存的过渡期。所以拿一份老驱动往新内核上搬,最常见的编译错误就是这两个回调签名对不上——这不是你代码写错了,是内核接口在变。
alert是 SMBus Alert 协议的钩子,只有少数带 ALERT 引脚、支持该协议的器件用得上,一般驱动留空。
1.3 i2c_msg:真正描述"发什么"的东西
一次 I2C 传输在数据结构上就是"一组 msg 顺序执行"。i2c_msg只有四个字段,但它承载的信息决定了总线上波形长什么样:
struct i2c_msg { __u16 addr; /* 从设备地址 */ __u16 flags; /* I2C_M_RD、I2C_M_TEN、I2C_M_NOSTART 等 */ __u16 len; /* 本段数据长度 */ __u8 *buf; /* 数据缓冲 */ };常用的 flags 得背下来:
I2C_M_RD:这一段是读。没有它就是写。I2C_M_TEN:10 位地址模式,配合flags里 client 的I2C_CLIENT_TEN。I2C_M_NOSTART:这一段不发 START,直接接上一段的数据继续。拼接硬件地址、做块传输时会用到。I2C_M_RECV_LEN:SMBus 块读,接收长度由从设备在首字节给出。I2C_M_IGNORE_NAK:忽略从设备的 NACK,继续发完。慎用,一般只在扫描或某些特殊时序场景。
理解 msg 的关键在于"重复起始"这件事。假设你要读一个带 16 位寄存器地址的器件,标准动作是:先写 2 字节地址(不发 STOP),再发一次 START,然后是读。这个动作在内核里对应的是两个 msg 组成一次i2c_transfer,第一个 msg 是写、第二个 msg 是读。如果你写成两次i2c_transfer,那两次之间会插入 STOP,很多器件就会把状态机复位,读出来的只能是垃圾。这是我在调一颗光感时卡了整整一下午的问题,后来抓波形才看出来 STOP 的位置不对。
1.4 一张表把关系钉死
| 结构体 | 归属层 | 谁创建 | 关键作用 | 常见误用 |
|---|---|---|---|---|
| i2c_adapter | 核心层 | 控制器驱动 | 代表一条物理总线 | 把 adapter 当 client 用 |
| i2c_algorithm | 控制器驱动 | 控制器驱动 | 实现实际传输 | functionality 返回值乱填 |
| i2c_client | 从设备 | 设备树/板级信息/手动创建 | 描述从设备地址与中断 | addr 填成 8 位地址 |
| i2c_driver | 从设备驱动 | 模块代码 | 提供 probe/remove | 匹配表写漏导致不 probe |
| i2c_msg | 传输层 | 调用方临时构造 | 描述一段传输 | buf 放在无法 DMA 的内存 |
| i2c_board_info | 板级 | 板级代码/设备树 | 向核心注册 client | of_node 与 reg 冲突 |
这张表我建议打印出来贴在显示器边上,直到你能不假思索地说出"这个字段在哪个结构体里"为止。新手 debug 时最常见的一种描述是"我发了但我不知道发到哪儿去了",本质上就是没把 client(发给谁)和 msg(发什么)分清。
2. 注册与匹配:驱动是怎么被内核叫起来的
2.1 加 adapter:编号这件事值得单独说
控制器驱动在 probe 末尾要把自己的 adapter 挂到子系统上,两个接口:i2c_add_adapter()和i2c_add_numbered_adapter()。
前者让内核动态分配一个总线号,后者要求你先把adap->nr指定好,内核要么给你这个号,要么返回-EBUSY。什么时候必须用带编号的?当板子上有外部需求依赖固定总线号时——比如设备树里写死了i2c-2上的某个器件、用户空间脚本要打开/dev/i2c-2、或者有片选式的多路复用器依赖顺序。动态编号的问题在于总线号会随探测顺序变化,同一个内核镜像在不同板子上可能给出不同的号,这对产线测试脚本是灾难。
/* 动态编号 */ ret = i2c_add_adapter(adap); /* 固定编号,nr 预先填好 */ adap->nr = 2; ret = i2c_add_numbered_adapter(adap);现在主流的做法是交给设备树:控制器节点加上别名的形式,核心会自己解析出来并完成编号绑定,驱动里直接调devm_i2c_add_adapter()就够了。devm_前缀意味着你不需要在 remove 里手动i2c_del_adapter(),设备卸载时自动清理,少一处泄漏点。
注意:
i2c_add_adapter失败时不要再调i2c_del_adapter,核心内部已经回滚干净了。我见过有驱动在错误分支里补了一刀 delete,结果触发了引用计数下溢。
2.2 三张匹配表:id_table、of_match_table、acpi
驱动能不能被叫起来,取决于匹配。i2c 子系统里存在三套匹配路径,优先级大致是先设备树/ACPI,再是i2c_device_id。
第一张表是id_table,传统的名字匹配:
static const struct i2c_device_id my_sensor_id[] = { { "my-sensor", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, my_sensor_id);第二张是of_match_table,设备树匹配:
static const struct of_device_id my_sensor_of_match[] = { { .compatible = "vendor,my-sensor" }, { } }; MODULE_DEVICE_TABLE(of, my_sensor_of_match);第三张是 ACPI 的acpi_match_table,只有做 ACPI 平台(比如 x86 笔记本上的触控板、传感器中枢)才需要。
三张表要一起挂在i2c_driver上:
static struct i2c_driver my_sensor_driver = { .driver = { .name = "my-sensor", .of_match_table = my_sensor_of_match, .pm = &my_sensor_pm_ops, }, .probe = my_sensor_probe, .remove = my_sensor_remove, .id_table = my_sensor_id, }; module_i2c_driver(my_sensor_driver);一个很常见的困惑是"设备树里写好了,为什么还不 probe"。除了compatible拼错这种低级错误,最隐蔽的是驱动没编进内核——of_match_table匹配上了,但驱动模块本身没被加载,或者被编译成了模块而 initramfs 里没带。调这事的时候先看/sys/bus/i2c/devices/下有没有那个 client 目录,有说明设备树解析成功、client 已创建,问题在驱动侧;没有说明设备树那一段本身就没生效。
2.3 probe/remove 的签名变迁
前面提过签名变化,这里展开说清楚,因为它是老代码移植时最容易卡的地方。
老内核(大约 5.x 之前)的写法是:
static int my_probe(struct i2c_client *client, const struct i2c_device_id *id);驱动里要么用这个老签名,要么用后来引入的probe_new:
static int my_probe_new(struct i2c_client *client);新内核里probe_new被改名回probe,老签名被移除。所以你现在写驱动直接用int (*probe)(struct i2c_client *)就对。为什么要去掉 id 参数?因为i2c_client里已经有name,设备树匹配上之后i2c_device_id的driver_data也能通过i2c_get_device_id()或i2c_client_get_device_id()拿到,多传一个参数意义不大,反而让 probe 签名在不同总线上不一致(platform、spi 也是类似的收敛过程)。
remove的变化同步进行,方向是改成void (*remove)(struct i2c_client *)。返回int的 remove 在语义上本来就很别扭——驱动卸载失败内核也没法真的把模块留住,返回错误只会让引用计数出问题。
移植老驱动时的实操建议:先看目标内核版本include/linux/i2c.h里i2c_driver的定义,别信网上抄来的代码。这一步花两分钟,能省掉半小时的编译报错排查。
2.4 module_i2c_driver 宏省掉的四行
module_i2c_driver(my_sensor_driver);这一行展开之后等价于:
static int __init my_sensor_init(void) { return i2c_add_driver(&my_sensor_driver); } static void __exit my_sensor_exit(void) { i2c_del_driver(&my_sensor_driver); } module_init(my_sensor_init); module_exit(my_sensor_exit);它省掉的不只是四行代码,还统一了错误处理路径:i2c_add_driver失败时宏内部直接把错误码返给模块加载器,不用你手写return ret。同样的宏还有module_spi_driver、module_platform_driver,思路一致。
只有一个场景不能用它:驱动需要在内核启动的某个特定阶段之前完成注册,比如要做 early console 或者依赖某个 initcall 顺序。那种情况老老实实写subsys_initcall之类。
3. 传输接口:i2c_transfer 和它的几个语法糖
3.1 i2c_transfer 的返回值语义
i2c_transfer(adapter, msgs, num)是底层接口,返回值只有两种解读方式:
- 大于 0:成功处理的 msg 个数,正常情况等于
num。 - 小于 0:错误码。注意,即使前面几个 msg 成功了、最后一个失败,返回的也是那个负错误码,不会返回"成功了 1 个"。
这一点和read()那种"部分成功"的语义不同,所以判断时只写if (ret < 0)就够,别写if (ret != num),虽然更严格但很多控制器在特殊场景下会返回非 num 的正值(比如带 NOSTART 优化时合并了 msg)。
追求严格的话可以这么写:
ret = i2c_transfer(client->adapter, msgs, 2); if (ret < 0) { dev_err(&client->dev, "transfer failed: %d\n", ret); return ret; } if (ret != 2) { dev_warn(&client->dev, "partial transfer: %d/2\n", ret); return -EIO; }顺带说一个细节:i2c_transfer内部会根据adapter->retries做重试,默认值通常是 3。这个重试是针对 NACK 和超时的,对协议错误(比如控制器报的仲裁丢失)不一定重试。调试时如果看到同一个错误码反复出现又突然成功,很可能就是重试在起作用,说明你的时序贴边了,得回头看电气参数。
i2c_transfer会睡眠,因为它内部拿bus_lock,控制器驱动里也经常msleep等硬件状态。在中断上下文、软中断、自旋锁保护区内调用它是错的。如果确实需要在原子上下文里发数据,只能指望master_xfer_atomic,或者干脆把工作丢到 workqueue 里延后做。
3.2 master_send/recv 到底省了什么
i2c_master_send(client, buf, count)和i2c_master_recv(client, buf, count)是两个语法糖,内部帮你构造单 msg 然后调i2c_transfer。返回值是实际传输的字节数,成功时等于 count,失败返回负错误码。
它们省掉的是这段样板:
struct i2c_msg msg; msg.addr = client->addr; msg.flags = 0; /* 读的话是 I2C_M_RD */ msg.len = count; msg.buf = buf; ret = i2c_transfer(client->adapter, &msg, 1);但省掉样板也是有代价的:它们只能发单个 msg,做不了"写地址+重复起始+读数据"这种两段式操作。所以你只要涉及带寄存器地址的器件,最终还是要回到手写i2c_msg数组。
还有个小差异值得知道:i2c_master_send对 0 长度传输的处理在部分控制器上会返回-EOPNOTSUPP(配合I2C_AQ_NO_ZERO_LENquirk),某些器件用 0 长度写来当"探测"手段,遇到这个 quirk 就得换成实际读一个寄存器来判断设备是否存在。
3.3 smbus 接口的适用边界
SMBus 是 I2C 的一个子集,规定了更严格的时序和固定的数据格式。内核提供了一整套i2c_smbus_*接口:
s32 i2c_smbus_read_byte(const struct i2c_client *client); s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value); s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command); s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value); s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command); s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, u8 *values);用不用它们,判断标准很实在:你的器件是不是 SMBus 兼容的。如果它就是一个"写寄存器地址 + 读一个字节"的器件(大量传感器、EEPROM、IO 扩展器都是),那用i2c_smbus_read_byte_data最省事,语义清晰,返回值直接就是 8 位数据,不用自己拼 msg。
如果器件需要 16 位寄存器地址、或者需要原子性的多字节连读(读的过程中不允许被打断),那就得用裸i2c_transfer。SMBus 协议本身只定义了 8 位 command,塞不下 16 位地址。
有个隐藏机制要知道:如果控制器的algo->smbus_xfer为 NULL,核心会调用i2c_smbus_xfer_emulated(),把 SMBus 操作翻译成等价的i2c_msg序列再走master_xfer。这意味着哪怕控制器没有硬件 SMBus 支持,i2c_smbus_*也能用,只是多一层软件模拟的开销,而且模拟出来的时序在极少数挑剔的器件上可能有问题。
3.4 一次完整的寄存器读写封装
实际驱动里我习惯先封两个内部函数,把 msg 构造的细节收进去:
static int my_read_regs(struct i2c_client *client, u8 reg, u8 *buf, int len) { struct i2c_msg msgs[2]; int ret; msgs[0].addr = client->addr; msgs[0].flags = 0; msgs[0].len = 1; msgs[0].buf = ® msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = len; msgs[1].buf = buf; ret = i2c_transfer(client->adapter, msgs, 2); if (ret < 0) return ret; return ret == 2 ? 0 : -EIO; } static int my_write_regs(struct i2c_client *client, u8 reg, const u8 *buf, int len) { struct i2c_msg msg; u8 *tmp; int ret; tmp = kmalloc(len + 1, GFP_KERNEL); if (!tmp) return -ENOMEM; tmp[0] = reg; memcpy(tmp + 1, buf, len); msg.addr = client->addr; msg.flags = 0; msg.len = len + 1; msg.buf = tmp; ret = i2c_transfer(client->adapter, &msg, 1); kfree(tmp); if (ret < 0) return ret; return ret == 1 ? 0 : -EIO; }这里有几个刻意的设计。读函数用两个 msg 组成一次 transfer,保证寄存器地址写入和数据读取之间是重复起始而不是 STOP,绝大多数器件的状态机要求如此。写函数把寄存器和数据拼进一块连续的 buffer,因为 I2C 写没有"先写地址再写数据"的停顿,本来就是一串字节。这块 buffer 用kmalloc而不是栈上数组,是为了避开 DMA 的安全问题——后面会细说。
如果驱动里寄存器操作非常多,别自己维护这套封装,直接用 regmap:
static const struct regmap_config my_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0x7F, .cache_type = REGCACHE_RBTREE, }; regmap = devm_regmap_init_i2c(client, &my_regmap_config);regmap 的好处不止是省代码:它自带寄存器缓存,能减少总线访问次数;它会把读写统一走一套接口,换总线(从 I2C 换到 SPI)时驱动逻辑几乎不用动;它还处理了位域操作regmap_update_bits,读改写一条龙。代价是启动时多了一层间接调用,对时序极敏感的场景要评估一下。
4. 控制器侧:algorithm 实现与电气参数
4.1 master_xfer 的骨架该怎么搭
写从设备驱动的人多,写控制器驱动的人少,但两者都得看懂master_xfer的骨架,不然从设备出问题时你根本不知道该怀疑哪一层。
一个最小可用的master_xfer大致是这个形状:
static int my_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct my_i2c *i2c = i2c_get_adapdata(adap); int i, ret; if (!num) return 0; ret = my_i2c_hw_init(i2c); if (ret) return ret; for (i = 0; i < num; i++) { struct i2c_msg *msg = &msgs[i]; if (msg->flags & I2C_M_TEN) return -EOPNOTSUPP; if (msg->len == 0 && (adap->quirks && adap->quirks->flags & I2C_AQ_NO_ZERO_LEN)) return -EOPNOTSUPP; ret = (msg->flags & I2C_M_RD) ? my_i2c_read_msg(i2c, msg) : my_i2c_write_msg(i2c, msg); if (ret) return ret; } return num; }几个要点。第一,参数检查要放在最前面,10 位地址、0 长度这类不支持的情况早点返回-EOPNOTSUPP,比传到硬件里再报莫名错误强。第二,每个 msg 之间是否发 STOP 由硬件和I2C_M_NOSTART决定,简单实现可以每个 msg 都发 STOP,但那样就破坏了"重复起始"的语义,带寄存器的器件会读错——所以正经实现必须支持把多个 msg 串成一次连续传输。第三,返回num表示全部成功,中途失败返回负错误码,不要返回"成功了 i 个"。
quirks那个字段值得单独看一眼,它用来描述控制器的能力缺陷,比如I2C_AQ_NO_ZERO_LEN(不支持 0 长度传输)、I2C_AQ_NO_REP_START(不支持重复起始)、I2C_AQ_COMB_WRITE_THEN_READ(限制组合传输的字节数)。核心层依赖这些标志来提前拒绝不合法的请求,或者用软件方式拆解。如果你的控制器有这些毛病却没声明,上层驱动会遇到很多"看起来毫无道理"的失败。
4.2 上拉电阻和总线电容怎么算
这一节是硬件内容,但软件工程师也得会算,因为大量"能通信但偶发失败"的问题根源在这里。
I2C 是开漏总线,SDA/SCL 靠外部上拉电阻拉高。上拉电阻的取值被两个约束夹住:太大则上升沿太慢,太小的则灌电流超限。
上限由上升时间决定:
Rp(max) = tr / (0.8473 × Cb)tr是允许的最大上升时间,标准模式(100kHz)是 1000ns,快速模式(400kHz)是 300ns。Cb是总线总电容,包括 PCB 走线(大约 1pF/cm)、每个器件的引脚电容(典型 5~10pF)和连接器。假设一条板子上挂 4 个器件、走线 10cm,粗算 Cb ≈ 4×8pF + 10×1pF ≈ 42pF,留点余量按 100pF 算。在快速模式下:
Rp(max) = 300ns / (0.8473 × 100pF) ≈ 3.5kΩ下限由输出低电平时的灌电流决定:
Rp(min) = (VDD - VOL(max)) / IOL3.3V 供电、VOL最大 0.4V、IOL取 3mA,得到(3.3 - 0.4) / 0.003 ≈ 967Ω。
所以取值区间是 1k ~ 3.5k,工程上常用 1.8k 或 2.2k。很多开发板默认贴 4.7k,这在中低速、短走线、少器件的情况下能用,一旦把总线拉到 400kHz 并挂满设备,上升沿就可能不够陡,表现为通信偶尔 NACK 或读回数据错位。这时候先别怀疑驱动,用示波器看上升时间,超过规范值就减上拉。
还有一个常被忽略的点:总线上所有上拉是并联的。控制器板载一对 4.7k,你外接模块上又带一对 4.7k,并联后等效 2.35k,灌电流翻倍。有些模块还带 10k,并联之后变成 3.2k,反而更接近理想值。所以接了模块之后通信变差,第一件事是数一数板子上到底有几个上拉。
4.3 设备树与 board_info 两条注册路径
从设备的描述有两条路:设备树和i2c_board_info。
设备树是现在的主流,在控制器节点下挂子节点:
&i2c1 { clock-frequency = <400000>; status = "okay"; sensor@48 { compatible = "vendor,my-sensor"; reg = <0x48>; interrupt-parent = <&gpio1>; interrupts = <12 IRQ_TYPE_EDGE_FALLING>; }; };reg就是 7 位地址。clock-frequency写在控制器节点上,作用于整条总线,所有挂在上面的器件共享同一个速率。
板级信息是给没有设备树的老平台或者需要动态添加设备的场景用的:
static struct i2c_board_info my_sensor_info = { I2C_BOARD_INFO("my-sensor", 0x48), .irq = GPIO_TO_IRQ(12), }; i2c_register_board_info(1, &my_sensor_info, 1);i2c_register_board_info的第一个参数是总线号。这个函数有个限制:它只能在启动早期调用(通常是板级初始化阶段),运行期动态加设备得用i2c_new_client_device():
struct i2c_client *cli; struct i2c_board_info info = { I2C_BOARD_INFO("my-sensor", 0x48), }; cli = i2c_new_client_device(adapter, &info); if (IS_ERR(cli)) return PTR_ERR(cli);注意这个函数返回的是ERR_PTR风格的指针,得用IS_ERR判断,而且用完要i2c_unregister_device()。老代码里常见的i2c_new_device已经改成i2c_new_client_device,i2c_new_dummy改成了i2c_new_dummy_device——改名是为了统一错误返回风格,老的i2c_new_device在失败时返回 NULL,新接口返回 ERR_PTR,混着用会漏判。
5. 常见问题与排查实录
5.1 错误码对照表
I2C 驱动的错误码就那么几个,但每个背后的原因差别很大。我整理了一张表,按我遇到的频率排序:
| 错误码 | 字面含义 | 实际最常见原因 | 排查动作 |
|---|---|---|---|
| -ENXIO | 设备或地址不存在 | 从设备没应答,地址填错,器件没供电 | 用 i2cdetect 扫,量器件 VCC |
| -EREMOTEIO | 远端 IO 错误 | 传输中途 NACK,多数是时序或上拉问题 | 示波器看波形,减上拉 |
| -ETIMEDOUT | 超时 | 时钟被从设备拉低不放(clock stretching),或控制器没响应 | 检查是否有器件卡住 SCL |
| -EIO | 通用 IO 错误 | 控制器报错,常见于 FIFO 溢出或状态机异常 | 看控制器寄存器状态 |
| -EOPNOTSUPP | 不支持 | 调了控制器没实现的功能,比如原子传输 | 查 functionality 返回值 |
| -EBUSY | 忙 | 总线号被占用,或多路复用器未选中通道 | 查 adapter->nr 冲突 |
| -EPROBE_DEFER | 延后探测 | 依赖的资源(时钟、GPIO、稳压器)还没就绪 | 检查 probe 里资源获取顺序 |
-ENXIO和-EREMOTEIO这两个最容易混。前者是"根本没听到应答",后者是"听到了应答但后面出了问题"。如果你在 probe 里第一次读寄存器就拿到 -ENXIO,八成是地址错或者器件没上电;如果是通信一段时间之后偶发 -EREMOTEIO,那基本是电气问题。
5.2 用 sysfs 和 i2c-tools 定位
定位 I2C 问题,我基本按这个顺序走,从软到硬逐层排除:
第一步,看总线和设备有没有被枚举出来:
ls /sys/bus/i2c/devices/ cat /sys/bus/i2c/devices/i2c-1/name如果i2c-1存在但1-0048这样的 client 目录不存在,说明设备树那段没生效,问题在 DTS 或匹配表。如果 client 目录存在但没有绑定驱动,看看driver符号链接是不是空的。
第二步,用 i2c-tools 做裸访问,绕开驱动:
i2cdetect -l # 列出所有总线 i2cdetect -y -r 1 # 扫描总线 1,-r 用读方式探测 i2cget -y 1 0x48 0x00 # 读 0x48 的 0x00 寄存器 i2cset -y 1 0x48 0x01 0x80 # 写 i2cdump -y 1 0x48 # 整片 dumpi2cdetect -y -r里的-r有讲究:默认的探测方式是用SMBUS_QUICK写一个字节,有些器件被写一下会进入异常状态,用读方式(-r)更安全。扫描结果显示UU表示该地址已被驱动占用,不代表器件不存在。
第三步,如果 i2c-tools 能通而驱动不通,问题在你自己的驱动里;如果 i2c-tools 也不通,问题在硬件或控制器配置,跟从设备驱动没关系。这条分界线非常重要,能省掉大量在错误方向上瞎改的时间。
想要更细的追踪,可以打开内核的动态调试:
echo 'file i2c-core-base.c +p' > /sys/kernel/debug/dynamic_debug/control dmesg -w或者直接抓 I2C 层的 tracepoint:
echo 1 > /sys/kernel/debug/tracing/events/i2c/enable cat /sys/kernel/debug/tracing/trace_pipetracepoint 会把每一次 msg 的地址、方向、长度都打出来,一眼能看出来你发的是写还是读、地址对不对。
5.3 我踩过的坑
坑一:buf 用栈上变量,DMA 模式下读到垃圾。很多控制器走 DMA,要求 buffer 在物理连续的内存里。栈上的数组虽然虚拟地址连续,但物理页不保证,DMA 引擎会读到错误位置。表现是"单次读没问题,连续读偶尔错",特别难查。解决方式是kmalloc分配 buffer,或者给 msg 打上I2C_M_DMA_SAFE标志(前提是控制器支持并正确实现)。更稳的做法是在 probe 里预分配一块驱动私有的 DMA 安全 buffer,反复复用。
坑二:在 probe 里用devm_分配的 buffer 传给了异步任务。devm_kzalloc的生命周期绑定在设备上,设备卸载时自动释放。如果你的 buffer 被传给了 workqueue 或者定时器,而设备在任务执行前被卸载了,就是 use-after-free。这类问题往往只在模块反复加载卸载的压力测试里才暴露。
坑三:把 adapter 当成长期持有的对象。用i2c_get_adapter(nr)拿到 adapter 之后必须配对的i2c_put_adapter(),否则总线模块永远卸载不掉。我见过一个驱动在 probe 失败分支里漏了 put,结果 rmmod 控制器模块时一直报-EBUSY,查了半天以为是设备还在用。
坑四:寄存器地址的位宽搞错。有些器件的寄存器地址是 16 位大端,手册上写0x0100,实际传输时要发0x01, 0x00两个字节。如果你按 8 位发,器件会把它当成两个独立的 8 位命令,读出来的数据永远对不上。判断方法很朴素:看手册的时序图里地址后面跟着几个字节。
坑五:多设备共总线时的并发。两个驱动同时访问同一条总线,如果各自都用i2c_transfer,核心的bus_lock会保证互斥,不会串包。但如果你在驱动里做了"先读 A 再读 B,中间不允许插入"的复合操作,两次i2c_transfer之间是可能被别人插进来的,器件的内部状态机就可能错乱。这种情况要用i2c_lock_bus(adapter, I2C_LOCK_SEGMENT)把一组操作整体锁住,或者干脆合并成一次 transfer。
坑六:clock stretching 被控制器忽略。有些控制器默认不支持从设备拉低 SCL 来延长时钟,或者超时设得太短。表现是低速器件(比如某些温湿度传感器在做转换时会拉低 SCL)在大批量读取时偶发-ETIMEDOUT。解决要么调大adapter->timeout,要么在控制器驱动里打开 stretching 支持。
坑七:忽略了器件的上电时序。有些器件上电后需要几十毫秒的内部初始化,这段时间内不应答任何访问。驱动 probe 时如果立刻去读 ID 寄存器,就会拿到 -ENXIO。标准做法是在 probe 开头加一个msleep,或者在设备树里加reset-gpios和reset-delays-us,把复位动作做实。
提示:调 I2C 时先把
CONFIG_I2C_DEBUG_CORE和CONFIG_I2C_DEBUG_ALGO打开,日志会详细很多。调试完记得关掉,这些选项在量产内核里会拖慢传输。
最后分享一个我个人的习惯:新建一个 i2c 驱动时,probe 的第一版不写任何业务逻辑,只做三件事——打印 client 的 addr、adapter 的 nr 和 name、调用一次i2c_check_functionality打印结果,然后读一个已知的 ID 寄存器打印出来。这四行日志能把"设备在不在、总线对不对、能力够不够、通信通不通"四个问题一次性回答完。很多所谓的疑难问题,卡在这个最基础的验证环节上,先把它做实,后面的事情都是顺的。