驱动开发硬核特训 · Day 30(上篇):深入理解 I2C 总线驱动模型(以 at24 EEPROM 为例)

发布于:2025-05-09 ⋅ 阅读:(28) ⋅ 点赞:(0)

作者:嵌入式Jerry
视频教程请关注 B 站:“嵌入式Jerry”


一、写在前面

在上一阶段我们已经深入理解了字符设备驱动与设备模型之间的结合方式、sysfs 的创建方式以及平台驱动模型的实际运用。今天我们迈入总线驱动模型的世界,聚焦于 I2C 总线驱动模型,并选取一个典型、真实且广泛使用的驱动设备 —— at24 EEPROM,进行完整分析。

本篇内容将从 I2C 总线模型的架构出发,讲清楚:

  • 驱动如何注册到 I2C 总线上?
  • I2C client 是如何匹配的?
  • at24 是如何成为标准驱动代表的?
  • 如何配置设备树节点?
  • 如何验证实际读写?

并配套提供真实代码与调试示例。


二、I2C 总线驱动模型架构简介

在 Linux 内核中,I2C 总线驱动模型是基于总线-设备-驱动的三层结构之上实现的:

        I2C 总线(i2c_adapter) ←→  I2C 设备(i2c_client) ←→  I2C 驱动(i2c_driver)
  • i2c_adapter:抽象了一个物理 I2C 控制器(主机控制器)
  • i2c_client:抽象了挂在某条 adapter 上的 I2C 外设
  • i2c_driver:抽象了对某种类型 I2C 设备的驱动代码

这和设备模型中的 bus_typedevicedevice_driver 是一致的,i2c-core 就是 bus_type 的实现,负责完成 match 和 probe。


在这里插入图片描述

三、典型案例介绍:at24 EEPROM

我们选择 i2c 子系统中的一个经典外设:at24 系列 EEPROM。它们遵循 I2C 协议,容量常见为 16Kbit/32Kbit/64Kbit,支持页写和顺序读。

在 Linux 内核中,驱动文件为:

drivers/misc/eeprom/at24.c

其本质是一个标准 i2c_driver 驱动。


四、设备树配置

在 NXP i.MX8MP 平台中,at24 EEPROM 可以通过设备树添加,典型配置如下:

&i2c3 {
	status = "okay";
	clock-frequency = <400000>;

	eeprom@50 {
		compatible = "atmel,24c16";
		reg = <0x50>;
		pagesize = <16>;
	};
};

解释:

  • compatible:用于匹配驱动
  • reg:I2C 地址(7 位)
  • pagesize:页写大小(单位:字节)

五、驱动入口分析

at24.c 中的核心驱动结构如下:

static const struct of_device_id at24_of_match[] = {
	{ .compatible = "atmel,24c16", .data = (void *)AT24_DEVICE_MAGIC },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, at24_of_match);

static struct i2c_driver at24_driver = {
	.driver = {
		.name = "at24",
		.of_match_table = of_match_ptr(at24_of_match),
	},
	.probe_new = at24_probe,
	.remove = at24_remove,
	.id_table = at24_ids,
};
module_i2c_driver(at24_driver);

🔍 核心点解析:

  • 使用 of_match_table 进行设备树匹配;
  • 使用 module_i2c_driver 宏注册;
  • probe 函数负责初始化工作,注册字符设备。

六、字符设备注册逻辑

at24 实现为字符设备,允许用户从 /dev 直接读写 EEPROM 数据。

ret = devm_device_add_groups(&client->dev, at24_groups);
cdev_init(&at24->cdev, &at24_fops);
ret = cdev_add(&at24->cdev, at24->devt, 1);
device_create(...);

最终会创建出:

/dev/eeprom
/sys/class/eeprom/eeprom0

文件操作如下:

static const struct file_operations at24_fops = {
	.owner = THIS_MODULE,
	.llseek = no_llseek,
	.read = at24_read,
	.write = at24_write,
	.open = at24_open,
};

七、匹配过程剖析

整个驱动匹配过程:

  1. at24 驱动通过 of_match_table 注册支持 "atmel,24c16"
  2. i2c-core 扫描设备树时发现此节点
  3. 创建 i2c_client,并匹配 i2c_driver
  4. 调用 at24_probe(),完成驱动绑定

八、调试验证操作

加载驱动后会创建字符设备:

ls /dev/eeprom

我们可以直接写入:

echo "hello" > /dev/eeprom

再读取:

hexdump -C /dev/eeprom

若权限不足,可添加 udev 规则或通过 root 权限访问。


九、at24 驱动完整结构总览

+-----------------------------+
|        I2C Adapter         |
|   (控制器,如i.MX8MP)   |
+-------------+-------------+
              |
              v
+-------------+-------------+
|         I2C Client        | ←─────────────── 由设备树注册
|     i2c_client @ 0x50     |
+-------------+-------------+
              |
              v
+-------------+-------------+
|         I2C Driver        | ←─────────────── drivers/misc/eeprom/at24.c
|       i2c_driver: at24    |
+-------------+-------------+
              |
              v
+-------------+-------------+
|       Character Device    | ←─────────────── /dev/eeprom
|    file_operations: fops  |
+---------------------------+

🔟 总结与思考

通过 at24 EEPROM 的驱动分析,我们掌握了 I2C 总线驱动模型的完整机制:

关键组成 说明
i2c_adapter 控制器抽象,如 i2c3
i2c_client 挂载在 adapter 上的设备
i2c_driver 与 client 匹配的驱动
device tree 提供匹配入口
字符设备 提供用户访问接口

相比 platform 模型,I2C 总线驱动具有自动枚举能力,更适合标准器件(如 EEPROM、音频 codec、传感器等)。


✅ 今日任务巩固

  1. I2C 总线模型中,clientdriver 如何匹配?
  2. at24 驱动中,字符设备注册在哪个函数中?
  3. 如何通过设备树指定 I2C 地址与页大小?
  4. EEPROM 驱动是否需要实现 read/write
  5. 与 platform_driver 模型的区别有哪些?

📢 后续预告

在下一篇(Day 30 下篇)中,我们将围绕 lm48100q 音频 codec 展开,深入讲解 音频子系统 + I2C 驱动模型 的高级应用与集成技巧,敬请期待!


本文由 嵌入式Jerry 原创发布,转载请注明出处
视频教程请关注 B 站:“嵌入式Jerry”


网站公告

今日签到

点亮在社区的每一天
去签到