Linux的I2C子系统驱动框架简析

一、Linux的I2C子系统框架介绍

  1. I2C总线和I2C协议、SMBus协议

二、I2C子系统的分层介绍

  1. I2C设备驱动,包括一般的I2C设备驱动和i2c-dev.c,形成两种节点,/dev/xxx和/dev/i2c-N
  2. I2C核心层,如i2c-core-smbus.c、i2c-core-base.c,给上层提供各种API
  3. I2C控制器驱动,包含一般的I2C控制器驱动和i2c-gpio.c,后者使用GPIO模拟I2C的传输过程

三、典型的设备树文件

// 普通的I2C设备,挂在I2C0总线下,地址是0x50
&i2c0{
  i2c_dev@50 {
    compatible = "i2c_dev_compatible";
    reg = <0x50>;
  };
};
// 一般的I2C控制器
i2c_adap_virt: virtual_i2c_adapter {
  compatible = "user,virtual_i2c_adapter";
  #address-cells = <1>;
  #size-cells = <0>;
  ...      // 其他信息
};
// 使用GPIO模拟的I2C控制器
i2c-gpio-0 {
  compatible = "i2c-gpio";
  pinctrl-names = "default";
  pinctrl-0 = <&pinctrl_i2c_bitbang>;
  gpios = <&GPIOA 7 GPIO_ACTIVE_HIGH>,  //SCL
          <&GPIOA 8 GPIO_ACTIVE_HIGH>;  //SDA
  i2c-gpio,sda-open-drain;
  i2c-gpio,scl-open-drain;
  i2c-gpio,delay-us=<5>;  //100K
  #address-cells = <1>;
  #size-cells = <0>;
  ...      // 其他信息
};

四、I2C的重要结构体

struct i2c_adapter {    // 描述I2C控制器
  const struct i2c_algorithm *algo;      // I2C具体的传输实现
  int nr;              // 第几个I2C控制器
  ...
};
struct i2c_algorithm {  // 描述I2C的传输
  int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);    // 传输函数
  ...
  u32 (*functionality)(struct i2c_adapter *adap);  // 控制器支持哪些功能
};
struct i2c_msg {        // 描述I2C传递的消息
  __u16 addr;      // 从机地址
  __u16 flag;      // 标志位
  __u16 len;       // 长度
  __u8 *buf;       // 指向msg数据的指针
};
struct i2c_driver {    // 描述I2C驱动,类似platform_driver
  int (*probe_new)(struct i2c_client *client);
  int (*remove)(struct i2c_client *client);
  struct device_driver driver;  // compatible成员在这里
  const struct i2c_device_id *id_table;
  ...
};
struct i2c_client {    // 描述I2C设备,类似platform_device
  unsigned short flags;      // 标志
  unsigned short addr;      // I2C设备地址
  struct i2c_adapter *adapter;  // 控制器
  struct device dev;        // 必备的device结构体
};

五、驱动程序的流程

// I2C设备驱动
1. 入口函数中,使用i2c_add_driver注册一个i2c_driver,出口函数中,使用i2c_del_driver注销一个i2c_driver
2. I2C-BUS和Platform-BUS类似,入口函数注册相应driver,出口函数注销driver,compatible属性匹配时,调用probe函数
3. probe函数中,类似其他字符设备。使用alloc_chrdev_region、cdev_init、cdev_add、class_create、device_create
4. 关键的file_operations结构体中的读写函数,可以借助i2c_smbus_xx或者i2c_transfer函数实现
// I2C控制器驱动
1. 使用平台设备驱动程序的框架,注册I2C控制器驱动
2. probe函数中,分配、设置、注册一个i2c_adapter结构体,使用i2c_add_adapter注册一个控制器的结构体
3. 关键的成员i2c_algorithm需要最少实现master_xfer和functionality函数

六、i2c-dev.c介绍

1. 入口函数
  register_chrdev_region   // 主设备号89,名称为i2c
  class_create             // 创建类,辅助创建节点
  i2c_for_each_dev(NULL, i2cdev_attach_adapter);    // 绑定存在的I2C控制器,形成的/dev/i2c-N表示每个控制器
    cdev_init
    cdev_device_add
 2. 字符设备对应的fops
  open
    adap = i2c_get_adapter(minor);      // 得到控制器
    kzalloc                             // 分配一个i2c_client表示I2C设备
    设置I2C设备所属的控制器
  release
    i2c_put_adapter(client->adapter);    // 释放控制器
    kfree                                // 释放i2c_client的空间
  read
    i2c_master_recv
  write
    i2c_master_send
  ioctl
    I2C_SLAVE/I2C_SLAVE_FORCE        // 设置client的addr
    I2C_TENBIT                       // 设置client的flag
    I2C_PEC                          // 设置client的flag
    I2C_FUNCS                        // 调用i2c_algorithm的functionality函数获取能力
    I2C_RDWR                         // 构建msg,执行i2c_transfer
    I2C_SMBUS                        // 构建msg,执行i2c_smbus_xfer
    ...

七、i2c-gpio.c介绍

1. 使用平台设备驱动程序的框架,注册该platform_driver
2. probe函数
    解析设备树,获得时钟频率、SCL、SDA是否上拉等信息
    构建SCL和SDA对应的gpio_desc结构体
    构建adapter结构体对象
    i2c_bit_add_numbered_bus(adap)
      注册i2c_bit_algo,实现transfer函数和functionality函数
      调用add_adapter,注册一个adapter

八、总结

  1. 可以看到一般的I2C设备驱动和i2c-dev.c的实现类似,只不过两者生成的节点不同,后者对控制器生成了一个单独的节点,同样可以调用i2c协议或smbus协议的xfer函数
  2. i2c-gpio.c和一般的I2C控制器驱动类似,都使用add_adapter实现控制器的注册,关键之处在于xfer函数,前者使用GPIO做模拟实现I2C协议。
posted @ 2025-07-22 16:13  gramming  阅读(77)  评论(0)    收藏  举报