[ 导读] 本文通过阅读内核代码, 来梳理一下 I2C 子系统的整体视图. 在开发 I2C 设备驱动程序时, 往往缺乏对于系统整体的认识, 导致没有一个清晰的思路. 所以从高层级来分析一下 I2C 系统的设计思路, 将有助于设计调试具体的驱动程序.
I2C/SMBUS 基础
I2C 是一种芯片间通讯总线技术, 最早由 Philips 设计制定. 下面内容参考 I2C 2.1 规格书
半双工通信方式, 通信采用主 / 从结构
支持多主模式, 下图来源于 I2C 2.0 规格书
其内部电气实现采用集电极开路 (Open-collector)/ 漏极开路(open-drain) 结构以实现线与功能, 这是总线的实现基础, 多芯片通过查询总线状态实现介质仲裁以实现总线控制.
总线信号由两线实现, 串行时钟线 SCL(Serial Clock Line)/ 串行数据线 SDA(serial Data Line).
具有三种通讯速率模式:
- standard mode:0-100 kbps (bps: bit/s)
- Fast mode:0-400 kbps
- High-speed mode : 0-3.4Mbps
可支持混速模式
不同的速率在硬件设计时需要注意信号的完整性, I2C 总线等效电容 cx
支持 7bit/10bit 两种芯片地址模式
I2C 总线电气特性, 这个非常重要, 须严格遵守标准的电气特性
SMBUS(system management bus) . 大多数 SMBus 系统也符合 I2C, 电气约束对于 SMBus 更为严格, 并且它标准化了特定的协议消息和习惯用语. 支持 I2C 的控制器也可以支持大多数 SMBus 操作, 但是 SMBus 控制器并不支持 I2C 控制器将支持的所有协议选项. 通过使用 I2C 原语或通过向不支持这些 I2C 操作的 i2c_adapter 设备发出 SMBus 命令, 可以执行各种 SMBus 协议操作.
- http://smbus.org/
- I2C bus(Inter-Integrated Circuit bus) https://www.i2c-bus.org/
I2C 在 Linux 设备中的拓扑结构
在 PC 体系中, 大体如下拓扑:
PC 体系中通过桥接芯片, 扩展出 PCI, 在由 PCI 扩展出 I2C 适配器, 进而得到 I2C 总线, 或者桥接芯片直接扩展出 SMBUS/I2C 总线.
在嵌入式应用中, 则可能为:
嵌入式应用中, 则可能更多的情况是处理器内置了 I2C/SMBUS 总线控制器, 直接可得到 I2C/SMBUS 总线. 嵌入式系统中常常会设计很多传感器挂载在 I2C 总线上, 比如温度检测, 压力检测等等, 又或者诸如电容触摸屏, 电源管理 IC 等等.
代码实现
I2C 的 core 实现位于./drivers/i2c / 下, 实现了 I2C 总线设备以及驱动 (适配器) 和设备驱动的注册, 注销方法, I2C 通信方法 algorithm 抽象, 以及与具体硬件无关的代码
I2C 主控制器驱动位于 ./drivers/i2c/busses/, 这里主要实现总线控制器, 具体体现为 i2c_adapter 的实现. 负责 I2C 适配器与从设备通信. I2C 总线驱动由 i2c_adapter 和 i2c_algorithm 来抽象描述.
设备驱动则分散在./driver / 下, 这取决于具体的实现, 种类繁多.
i2c-dev, 大多位于 drivers/i2c/i2c-dev.c, 这种方法只是封装了主机 (I2Cmaster, 一般是 SoC 中内置的 I2C 控制器) 的 I2C 基本操作, 并且向应用层提供相应的操作接口, 应用层代码需要自己去实现对 slave 的控制和操作, 所以这种 I2C 驱动相当于提供给应用层可以访问 slave 硬件设备的接口, 本身并未对硬件做任何操作, 应用需要实现对硬件的操作. 这种模式也称为应用驱动程序.
另一种 I2C 驱动是将所有的代码都放在驱动层实现, 直接向应用层提供最终结果. 应用层甚至不需要知道这里面有 I2C 存在, 譬如电容式触摸屏驱动, 直接向应用层提供 / dev/input/event1 的操作接口, 应用层编程的人根本不知道 event1 中涉及到了 I2C.
I2C 子系统的主要目的是, 对 I2C 总线以及设备利用面向对象编程思想实现统一建模, 以高内聚 - 低耦合的软件工程思想, 实现一个分层体系结构, 以便于内核统一管理 I2C 设备, 从而可以更容易的在 Linux 下实现 I2C 设备以及高可移植.
主要数据结构
其内部有几个关键数据结构, 来梳理一下:
i2c_client, 用于抽象挂载在 I2C 总线上的从设备
i2c_driver, 用于驱动挂载在 I2C 总线生的从设备, 也即从设备的设备驱动程序
i2c_adapter, 用于抽象 I2C 的主设备
i2c_algorithm, 抽象 I2C 总线操作接口
i2c_devinfo
该结构体主要用于板级 I2C 信息管理
i2c_msg
该结构体主要用于抽象 I2C 报文, 其内容如下:
i2c_timings
主要用于抽象 I2C 电气特性, 对于支持设备树的系统构建而言, 主要通过以下内核接口函数, 从设备树解析电气特性参数.
- void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)
- {
- int ret;
- memset(t, 0, sizeof(*t));
- ret = device_property_read_u32(dev, "clock-frequency", &t->bus_freq_hz);
- if (ret && use_defaults)
- t->bus_freq_hz = 100000;
- ret = device_property_read_u32(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns);
- if (ret && use_defaults) {
- if (t->bus_freq_hz <= 100000)
- t->scl_rise_ns = 1000;
- else if (t->bus_freq_hz <= 400000)
- t->scl_rise_ns = 300;
- else
- t->scl_rise_ns = 120;
- }
- ret = device_property_read_u32(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns);
- if (ret && use_defaults) {
- if (t->bus_freq_hz <= 400000)
- t->scl_fall_ns = 300;
- else
- t->scl_fall_ns = 120;
- }
- device_property_read_u32(dev, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns);
- ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
- if (ret && use_defaults)
- t->sda_fall_ns = t->scl_fall_ns;
- device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns);
- }
- EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
i2c_device_identity
该结构体主要用于抽象 I2C 设备的 ID 属性, 通过内核接口函数 i2c_get_device_id 以获取设备 ID 属性.
总体框架
概述
Linux I2C 编程接口支持总线交互的主端和从端. 从高层级看由两种驱动程序和两种设备构成:
适配器设备与适配器设备驱动对: I2C 适配器驱动程序用于抽象控制器硬件; 它绑定到一个物理设备 (可能是一个 PCI 设备(PC 体系多一些) 或 platform_device(嵌入式应用居多)), 并构建 i2c_adapter 实体以呈现所管理的 1 个 I2C 总线段.
platform_device. 比如: i2c-s3c2410, 如下:
- static const struct platform_device_id s3c24xx_driver_ids[] = {
- {
- .name = "s3c2410-i2c",
- .driver_data = 0,
- }, {
- .name = "s3c2440-i2c",
- .driver_data = QUIRK_S3C2440,
- }, {
- .name = "s3c2440-hdmiphy-i2c",
- .driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
- }, { },
- };
- MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
pci-I2C 适配器设备. 如在 i2c-ali1535.c 中:
- /* ALI1535 device address register bits */
- #define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */
- /* Address field */
- /* -> Write = 0 */
- /* -> Read = 1 */
- #define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */
- /*PCI 设备驱动 */
- static struct pci_driver ali1535_driver;
- static unsigned long ali1535_smba;
- static unsigned short ali1535_offset;
I2C 从设备及设备驱动对: 每个 I2C 总线段上将有一个由结构 i2c_client 表示的 I2C 设备. 这些设备将被绑定到一个 struct i2c_driver, 遵循标准的 Linux 驱动程序模型.
架构
来源: https://www.cnblogs.com/embInn/p/13289367.html