I2C总线驱动

I2C总线驱动

概要

   一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,我们只要专注于 I2C 设备驱动即可。对于 I2C 主机驱动,一旦编写完成就不需要再做修改,其他的 I2C 设备直接调用主机驱动提供的 API 函数完成读写操作即可。这个正好符合 Linux 的驱动分离与分层的思想,因此 Linux内核也将 I2C 驱动分为两部分:
 ①、 I2C 总线驱动,I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
 ②、I2C 设备驱动,I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。

整体架构流程

在这里插入图片描述
   当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之匹配的设备,如果有的话就将两者联系起来。同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找看有没有与之匹配的设备,有的话也联系起来。Linux 内核中大量的驱动程序都采用总线、驱动和设备模式, platform 驱动就是这一思想下的产物。

1、i2c总线驱动

   i2c_adapter是一种用于连接和控制I2C设备的接口。I2C是一种串行通信协议,用于在电子设备之间传输数据。i2c_adapter允许主机系统与I2C设备进行通信,以便读取和写入数据。它提供了一组函数和方法,使得开发人员可以轻松地与I2C设备进行交互。通过i2c_adapter,我们可以实现对I2C设备的配置、读取和写入操作,以满足特定的应用需求。
   i2c总线驱动除了匹配设备和设备驱动外,还用到了 i2c_adapter适配器。
1. i2c_adapter 注册/注销函数:
  (platform_driver、pci_driver、usb_driver(probe函数内使用了)注册函数)
  int i2c_add_adapter(struct i2c_adapter *adapter)
  int i2c_add_numbered_adapter(struct i2c_adapter *adap)
  void i2c_del_adapter(struct i2c_adapter * adap)
2. i2c_driver 注册/注销函数:
  (基本都是init里实现的内容)
  int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
  int i2c_add_driver (struct i2c_driver *driver)
  void i2c_del_driver(struct i2c_driver *driver)

 拓展:下面为和i2c驱动同级别的usb驱动的,代码结构。
示例:
在这里插入图片描述

I2C 适配器i2c_adapter和i2c_algorithm介绍

–>i2c_adapter
  -->i2c_algorithm /* 总线访问算法 */
   -->int (*master_xfer)(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);
 master_xfer 就是 I2C 适配器的传输函数,此函数完成与 IIC 设备间的通信。
 smbus_xfer 就是 SMBUS 总线的传输函数。

  I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。完成以后通过 i2c_add_numbered_adapter或 i2c_add_adapter 这两个函数向系统注册设置好的 i2c_adapter。
  int i2c_add_adapter(struct i2c_adapter *adapter)
  int i2c_add_numbered_adapter(struct i2c_adapter *adap) 这两个函数的区别在于 i2c_add_adapter 使用动态的总线号,而 i2c_add_numbered_adapter使用静态总线号。
  adapter 或 adap:要添加到 Linux 内核中的 i2c_adapter,也就是 I2C 适配器。
  返回值:0,成功;负值,失败。
  如果要删除 I2C 适配器的话使用 i2c_del_adapter 函数即可,函数原型如下:
  void i2c_del_adapter(struct i2c_adapter * adap)

2、设备驱动:

1、i2c_client 和 i2c_driver

  I2C 设备驱动重点关注两个数据结构:i2c_client 和 i2c_driver,i2c_client 就是描述设备信息的,i2c_driver 描述驱动内容,类似于 platform_driver。
  ①、i2c_client:存放设备信息,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client。
  ②、i2c_driver:类似 platform_driver,是我们编写 I2C 设备驱动重点要处理的内容。
–>i2c_driver
  -->driver                   //基类,提供最基础的驱动框架
    -->name
    -->owner
    --> i2c_device_id *of_match_table      //设备树匹配
  -->probe                   //驱动和设备匹配成功,probe会执行。
  -->remove
  --> i2c_device_id *id_table           //id_table表用来匹配驱动和设备
  当 I2C 设备和驱动匹配成功以后 probe 函数就会执行,和 platform 驱动一样。probe 函数里面基本就是标准的字符设备驱动那一套了。
  i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver。

2、I2C 总线的数据结构为 i2c_bus_type

在这里插入图片描述

of_driver_match_device 函数用于完成设备树设备和驱动匹配。
  ①设备树采用的匹配:i2c_driver–>driver–> i2c_device_id(*of_match_table)和设备树compatible比较
  ② ACPI 匹配方式
  ③比较 I2C设备名字和 i2c_driver–>i2c_device_id(*id_table) 的 name 字段是否相等,相等的话就说明 I2C 设备和驱动匹配。 虽然 I2C 总线为i2c设备提供了一种总线驱动框架,但是 I2C 适配器却是 platform驱动。
在这里插入图片描述

  当设备和驱动匹配成功以后 i2c_imx_probe 函数就会执行,i2c_imx_probe 函数就会完成 I2C 适配器初始化工作。

补充:i2c_imx_probe 内部有:i2c_imx_start、i2c_imx_read、i2c_imx_write 和 i2c_imx_stop 这些函数就是 I2C 寄存器的具体操作函数。
  platform_get_resource 函数从设备树中获取 I2C1 控制器寄存器物理基地址,(返回值)
  devm_ioremap_resource 函数对其进行内存映射,得到可在 Linux 内核中使用的虚拟地址。
在这里插入图片描述

3、i2c_transfer适配器

  int i2c_transfer (struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
在这里插入图片描述

1.读的过程:(是①②连在一起)
struct i2c_msg msg[2] = {
{
  .addr = i2c->addr, //从机地址7位
  .flags = 0, //写标记,(0为写)
  .len = 3, //寄存器地址长度,3字节
  .buf = &reg, //寄存器地址
},
{
  .addr = i2c->addr, //从机地址7位
  .flags = 1, .//读标记(1为读)
  .len = 2, //表示期望读到数据的字节长度(寄存器长度),是以byte为单位
  .buf = buffer, //将读取到的数据保存在buffer中
},
};
i2c_transfer(i2c->i2c_adap, msg, 2); //返回值是实际传输数据的个数(第三个参数)
2.写的过程:
  msg.addr = client->addr;
  msg.flags = 0;
  msg.len = length + 1;   //+1,是因为&be_data第一个元素为寄存器的地址,
             //length代表要写入的数据长度。
  msg.buf = (u8 *)&be_data;
i2c_transfer(client->adapter, &msg, 1);

重要结构体

重要结构体:
struct bus_type { //总线
2 const char *name;
3 struct bus_attribute *bus_attrs;
4 struct device_attribute *dev_attrs;
5 struct driver_attribute *drv_attrs;
6 int (*match)(struct device *dev, struct device_driver *drv);
7 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
8 int (*probe)(struct device *dev);
9 int (*remove)(struct device *dev);
10 void (*shutdown)(struct device *dev);
11 int (*suspend)(struct device *dev, pm_message_t state);
12 int (*resume)(struct device *dev);
13 const struct dev_pm_ops *pm;
14 struct bus_type_private *p;
15 };
设备和驱动的匹配过程也是由 I2C 总线完成的,I2C 总线的数据结构为 i2c_bus_type
platform 总线是 bus_type 的一个具体实例

struct platform_driver {		//总线的驱动

2 int (*probe)(struct platform_device *); //驱动和设备匹配成功,probe会执行。
3 int (*remove)(struct platform_device *); //
4 void (*shutdown)(struct platform_device *);
5 int (*suspend)(struct platform_device *, pm_message_t state);
6 int (*resume)(struct platform_device *);
7 struct device_driver driver; //基类,提供最基础的驱动框架
8 const struct platform_device_id *id_table; //id_table表用来匹配驱动和设备
9 bool prevent_deferred_probe;
10 };
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;

const struct platform_device_id	*id_entry;
char *driver_override; /* Driver name to force a match */

/* MFD cell pointer */
struct mfd_cell *mfd_cell;

/* arch specific additions */
struct pdev_archdata	archdata;

};
struct i2c_adapter { //i2c适配器
2 struct module owner;
3 unsigned int class; /
classes to allow probing for */
4 const struct i2c_algorithm algo; / 总线访问算法 */
5 void algo_data;
6
7 /
data fields that are valid for all devices /
8 struct rt_mutex bus_lock;
9
10 int timeout; /
in jiffies /
11 int retries;
12 struct device dev; /
the adapter device */
13
14 int nr;
15 char name[48];
16 struct completion dev_released;
17
18 struct mutex userspace_clients_lock;
19 struct list_head userspace_clients;
20
21 struct i2c_bus_recovery_info *bus_recovery_info;
22 const struct i2c_adapter_quirks *quirks;
23 };
struct i2c_driver { //I2c的驱动
2 unsigned int class;
3 int (*attach_adapter)(struct i2c_adapter *) __deprecated;
4 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
5 int (*remove)(struct i2c_client *);
6 void (*shutdown)(struct i2c_client *);
7 void (*alert)(struct i2c_client *, unsigned int data);
8 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
9 struct device_driver driver;
10 const struct i2c_device_id *id_table;
11 int (*detect)(struct i2c_client , struct i2c_board_info );
12 const unsigned short address_list;
13 struct list_head clients;
14 };
struct i2c_client {
2 unsigned short flags; /
标志 /
3 unsigned short addr; /
芯片地址,7 位,存在低 7 位
/
4 …
5 char name[I2C_NAME_SIZE]; /
名字 */
6 struct i2c_adapter adapter; / 对应的 I2C 适配器 /
7 struct device dev; /
设备结构体 /
8 int irq; /
中断 */
9 struct list_head detected;
10 …
11 };
struct device_driver {
2 const char *name;
3 struct bus_type *bus;
4
5 struct module *owner;
6 const char mod_name; / used for built-in modules /
7
8 bool suppress_bind_attrs; /
disables bind/unbind via sysfs */
9
10 const struct of_device_id *of_match_table;
11 const struct acpi_device_id *acpi_match_table;
12
13 int (*probe) (struct device *dev);
14 int (*remove) (struct device *dev);
15 void (*shutdown) (struct device *dev);
16 int (*suspend) (struct device *dev, pm_message_t state);
17 int (*resume) (struct device *dev);
18 const struct attribute_group **groups;
19
20 const struct dev_pm_ops *pm;
21
22 struct driver_private *p;
23 };
struct class { //类
2 const char *name;
3 struct module *owner;
4
5 struct class_attribute *class_attrs;
6 const struct attribute_group **dev_groups;
7 struct kobject *dev_kobj;
8
9 int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
10 char *(*devnode)(struct device *dev, umode_t *mode);
11
12 void (*class_release)(struct class *class);
13 void (*dev_release)(struct device *dev);
14
15 int (*suspend)(struct device *dev, pm_message_t state);
16 int (*resume)(struct device *dev);
17 int (*shutdown)(struct device *dev);
18
19 const struct kobj_ns_type_operations *ns_type;
20 const void *(*namespace)(struct device *dev);
21
22 const struct dev_pm_ops *pm;
23
24 struct subsys_private *p;
25 };
struct i2c_algorithm { //i2c的算法结构体
2 …
3 int (*master_xfer)(struct i2c_adapter *adap,
4 struct i2c_msg *msgs,
5 int num);
6 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
7 unsigned short flags, char read_write,
8 u8 command, int size, union i2c_smbus_data data);
9
10 /
To determine what the adapter supports */
11 u32 (*functionality) (struct i2c_adapter *);
12 …
13 };
struct kobj_map { // 设备号的存放位置
2 . struct probe {
3 struct probe next; / 这样形成了链表结构 /
4 dev_t dev; /
设备号 /
5 unsigned long range; /
设备号的范围 */
6 struct module *owner;
7 kobj_probe_t *get;
8 int (*lock) (dev_t, void *);
9 void data; / 指向struct cdev对象 */
10 } *probes[255];
11 struct mutex *lock;
12 }

小结

记录比较乱,没有太多时间打磨,关于i2c驱动部分的介绍如果能看懂这篇文章,基本可以做到读懂I2C驱动代码。后续在实际项目中遇到I2C调试的相关问题,可以用到i2c_tools工具,比较简单,后续可以自行了解。

  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值