I2C协议和驱动框架分析
Posted bobuddy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了I2C协议和驱动框架分析相关的知识,希望对你有一定的参考价值。
二、I2C驱动框架浅析
1. I2C驱动框架概述
上一节主要介绍了协议和具体的 I2C 读写操作,本节开始介绍 Linux 内核下的 I2C 驱动框架。代码分析基于 RK3399 android 7.1 平台,kernel 版本是 4.4.126。下图是之前在网上找到的 Linux 下整个 I2C 子系统的软件框架图,出处已无从证实,借此引用,如有侵权,请联系本人。
在内核驱动层,I2C 子系统又可以分为三个部分:
1、I2C 核心(i2c-core)
I2C 核心层提供了 I2C 总线驱动和设备驱动的注册/注销接口,I2C 通信接口(algorithm),与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。具体代码在 kernel/drivers/i2c/i2c-core.c,核心层提供的主要接口如下:
添加/删除 I2C 适配器
int i2c_add_adapter(struct i2c_adapter *adapter);
int i2c_register_adapter(struct i2c_adapter *adap);
int i2c_del_adapter(struct i2c_adapter *adap);
增加/删除 I2C 从设备驱动
int i2c_add_driver(struct i2c_driver *driver);
void i2c_del_driver(struct i2c_driver *driver);
I2C 传输接口
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int i2c_master_send(const struct i2c_client *client, constchar *buf, int count);
int i2c_master_recv(const struct i2c_client *client, char *buf, int count);
此外,I2C 总线也定义在这里:
struct bus_type i2c_bus_type =
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
;
EXPORT_SYMBOL_GPL(i2c_bus_type);
其中 i2c_device_probe 这个回调函数很重要,后面在设备驱动那里会介绍到。
2、I2C 总线驱动(I2C adapter/algo driver)
I2C 总线驱动是 I2C 控制器的软件实现,提供 I2C 控制器与从设备间完成数据通信的能力。I2C 总线驱动主要由 i2c_adapter 和 i2c_algorithm 这两个结构体来描述。分析驱动首先就要分析数据结构:
struct i2c_adapter
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* 总线上进行数据传输的算法 */
void *algo_data; /* 算法结构体的私有数据 */
/* data fields that are valid for all devices */
struct rt_mutex bus_lock; /* 同步机制,解决并发问题的基础设施之一 */
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr; /* 适配器编号,在创建i2c_client时会根据这个编号来分类各个i2c_client */
char name[48]; /* 适配器名字 */
struct completion dev_released;
struct mutex userspace_clien ts_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
;
struct i2c_algorithm
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
/*
* 总线驱动需要完成的重点工作,具体的寄存器相关操作,上面的i2c_transfer调用的
* 就是这个函数指针
*/
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);
/*
* 该适配器支持什么通信协议,设备驱动一般会调用这个回调来确认适配器支持的协
* 议类型,常见的有I2C_FUNC_I2C,I2C_FUNC_10BIT_ADDR等
*/
u32 (*functionality)(struct i2c_adapter *);
;
3、I2C 客户驱动(I2C client driver)
I2C 客户驱动是对 I2C 从设备的软件实现。I2C 客户驱动程序由 i2c_driver 和 i2c_client 这两个结构体来描述。
struct i2c_driver
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver; /* of_match_table,重要,match时优先用这个 */
/* 该设备支持的设备ID表,会与i2c_client.name进行match */
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
;
struct i2c_client
/*
* I2C_CLIENT_TEN:使用10位从地址;
* I2C_CLIENT_PEC:使用包错误检测
* 或者直接赋值为0
*/
unsigned short flags;
unsigned short addr; /* 从设备的I2C地址:低7位 */
char name[I2C_NAME_SIZE]; /* 在i2c_driver和i2c_client进行match时会用到 */
struct i2c_adapter *adapter; /* 挂载的I2C控制器 */
struct i2c_driver *driver; /* 设备使用的驱动 */
struct device dev; /* the device structure */
int irq; /* 设备的中断号 */
struct list_head detected;
;
2、总线驱动分析
RK 平台 I2C 总线驱动采用 platform 驱动框架来实现。驱动模块的入口代码如下:
static const struct of_device_id rk3x_i2c_match[] =
.compatible = "rockchip,rk3066-i2c", .data = (void *)&rk3066_soc_data ,
.compatible = "rockchip,rk3188-i2c", .data = (void *)&rk3188_soc_data ,
.compatible = "rockchip,rk3228-i2c", .data = (void *)&rk3228_soc_data ,
.compatible = "rockchip,rk3288-i2c", .data = (void *)&rk3288_soc_data ,
.compatible = "rockchip,rk3399-i2c", .data = (void *)&rk3399_soc_data ,
,
;
MODULE_DEVICE_TABLE(of, rk3x_i2c_match);
static struct platform_driver rk3x_i2c_driver =
.probe = rk3x_i2c_probe,
.remove = rk3x_i2c_remove,
.driver =
.name = "rk3x-i2c",
.of_match_table = rk3x_i2c_match,
.pm = &rk3x_i2c_pm_ops,
,
;
module_platform_driver(rk3x_i2c_driver);
dts 文件里一个 i2c 控制器的设备结点信息如下(dts 是什么请自行上网查阅):
i2c1: i2c@ff110000
compatible = "rockchip,rk3399-i2c";
reg = <0x0 0xff110000 0x0 0x1000>;
clocks = <&cru SCLK_I2C1>, <&cru PCLK_I2C1>;
clock-names = "i2c", "pclk";
interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH 0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c1_xfer>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
;
平台总线和 IIC、USB 等物理总线不同,它是内核虚拟出来的一种总线。一些没有实际物理总线的设备驱动都可以用平台总线的驱动框架来实现。总线两边分别挂的是平台设备(struct platform_device 描述)和平台驱动(struct platform_driver 描述),平台驱动在注册时会遍历平台总线上的平台设备,当条件(compatible 属性或 id_table)匹配(严格来说有个得分机制,得分最高的才会最终匹配成功)时,便会调用平台驱动的 probe 函数。这里不过多解释了,中间还涉及 dts 设备结点转化成平台设备的流程。总之,驱动注册后会跑到 rk3x_i2c_probe 这个回调函数里。总线驱动具体的代码各家芯片厂商都不一样,但是基本的代码逻辑都是按照 I2C 协议来实现。我们重点关注 i2c_adapter 结构体经历了什么,以及和其它相关结构体是怎么样联系起来的。我们的分析从 rk3x_i2c_probe 这个入口函数开始:
static int rk3x_i2c_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct rk3x_i2c *i2c;
struct resource *mem;
int ret = 0;
int bus_nr;
u32 value;
int irq;
unsigned long clk_rate;
/*
* 申请内存,一个struct rk3x_i2c就是一个RK平台的适配器
*
* devm_kzalloc比kzalloc要好用,不用考虑释放的问题,
* 内核会为你做好内存回收工作
*/
i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
/*
* 找到rk3x_i2c_match数组里与之匹配的那个struct of_device_id
*
* 这个adapter驱动会兼容多个RK平台(如rk3188/rk3288/rk3399等)的i2c控制器,
* 各个平台有些差异,差异性的信息可以通过.data这个成员指针保存起来,
* 用到的时候再取出来
*/
match = of_match_node(rk3x_i2c_match, np);
i2c->soc_data = (struct rk3x_i2c_soc_data *)match->data;
...
/* adapter部分成员的初始化 */
strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
/* 算法,总线驱动需要实现的最重要的回调 */
i2c->adap.algo = &rk3x_i2c_algorithm;
i2c->adap.retries = 3;
i2c->adap.dev.of_node = np;
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
i2c->dev = &pdev->dev;
spin_lock_init(&i2c->lock);
/* 初始化等待队列头,进程调度要用到 */
init_waitqueue_head(&i2c->wait);
/* 通知链机制,当内核要重启之前会调用回调rk3x_i2c_restart_notify */
i2c->i2c_restart_nb.notifier_call = rk3x_i2c_restart_notify;
i2c->i2c_restart_nb.priority = 128;
ret = register_i2c_restart_handler(&i2c->i2c_restart_nb);
if (ret)
dev_err(&pdev->dev, "failed to setup i2c restart handler.\\n");
return ret;
...
/* IRQ setup */
/* 平台设备platform_device保存了各种资源,其中就有中断号 */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
dev_err(&pdev->dev, "cannot find rk3x IRQ\\n");
return irq;
ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq,
0, dev_name(&pdev->dev), i2c);
if (ret < 0)
dev_err(&pdev->dev, "cannot request IRQ\\n");
return ret;
platform_set_drvdata(pdev, i2c);
...
/* 重点关注i2c_add_adapter做了些什么 */
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0)
dev_err(&pdev->dev, "Could not register adapter\\n");
goto err_clk_notifier;
dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\\n", i2c->regs);
return 0;
err_clk_notifier:
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
err_pclk:
clk_unprepare(i2c->pclk);
err_clk:
clk_unprepare(i2c->clk);
return ret;
probe 回调里删掉了一些和 i2c 驱动框架无关的代码,主要是关于平台时钟等相关设置。struct rk3x_i2c 结构体是对 struct i2c_adapter 的又一层封装。我们知道,驱动是在 Linux 内核下运行,那么总线驱动就不可能只有那些关于 i2c 通信的代码,必然涉及到其他的模块,包括硬件的软件的,比如时钟模块,比如进程调度等。所以,RK 平台的驱动开发工程师定义了 struct rk3x_i2c 这么一个结构体,非常类似于面向对象语言中的子类,而父类正是 struct i2c_adapter。这个结构体不属于我们的分析重点,所以成员定义就不贴出来了。
之前我们说过,struct i2c_algorithm 这个结构体里面的回调是总线驱动一定要实现的,因为这直接涉及到硬件层面上的寄存器操作了。每家芯片厂商 SoC 内部的 I2C 控制器都不一样,所以涉及具体的硬件操作代码必须要由芯片商完成。可见,内核驱动代码的分层是多么的精准,分工明确。RK 平台的代码实现如下(删掉了无关代码):
static int rk3x_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data;
unsigned long timeout, flags;
u32 val;
int ret = 0;
int i;
i2c->is_last_msg = false;
/*
* Process msgs. We can handle more than one message at once (see
* rk3x_i2c_setup()).
*/
for (i = 0; i < num; i += ret)
ret = rk3x_i2c_setup(i2c, msgs + i, num - i);
if (i + ret >= num)
i2c->is_last_msg = true;
rk3x_i2c_start(i2c);
/*
* 如果!i2c->busy为真,那么当调用wake_up(i2c->wait)时,进程会被唤醒接着往下执行;
* 如果!i2c->busy为假且时间未到,那么当调用wake_up时无效,进程会继续被阻塞
* 如果!i2c->busy为假但是时间到了,那么当调用wake_up时,进程也会被唤醒
*/
timeout = wait_event_timeout(i2c->wait, !i2c->busy,
msecs_to_jiffies(WAIT_TIMEOUT));
if (timeout == 0)
/* Force a STOP condition without interrupt */
i2c_writel(i2c, 0, REG_IEN);
val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
val |= REG_CON_EN | REG_CON_STOP;
i2c_writel(i2c, val, REG_CON);
i2c->state = STATE_IDLE;
ret = -ETIMEDOUT;
break;
return ret < 0 ? ret : num;
static u32 rk3x_i2c_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
static const struct i2c_algorithm rk3x_i2c_algorithm =
.master_xfer = rk3x_i2c_xfer,
.functionality = rk3x_i2c_func,
;
可以看到,rk3x_i2c_xfer 里及进一步调用的相关函数基本上都是具体的寄存器操作了,本质上和我们之前介绍的 S3C2440 的I2C 控制器读写操作逻辑是一样的,寄存器操作的代码最好结合芯片的 TRM 来分析。当然,rk3x_i2c_xfer 实际做的工作不止寄存器操作这么简单,还涉及到进程调度的问题。
回到 probe 里,最后调用了 i2c_add_adapter,这个函数也是由核心层提供,我们看看它做了些什么:
/**
* i2c_add_adapter - declare i2c adapter, use dynamic bus number
* @adapter: the adapter to add
* Context: can sleep
*
* This routine is used to declare an I2C adapter when its bus number
* doesn't matter or when its bus number is specified by an dt alias.
* Examples of bases when the bus number doesn't matter: I2C adapters
* dynamically added by USB links or PCI plugin cards.
*
* When this returns zero, a new bus number was allocated and stored
* in adap->nr, and the specified adapter became available for clients.
* Otherwise, a negative errno value is returned.
*/
int i2c_add_adapter(struct i2c_adapter *adapter)
struct device *dev = &adapter->dev;
int id;
if (dev->of_node)
/*
* 在probe里说了这个adapter驱动可以兼容多个RK平台的i2c控制器,
* 同时对于同一个平台多个i2c控制器也是跑这一个驱动代码,
* 为此需要对同一个平台下的多个i2c控制器进行编号,对应到
* 软件层面就是每个struct i2c_adapter的ID号
*/
id = of_alias_get_id(dev->of_node, "i2c");
if (id >= 0)
adapter->nr = id;
return __i2c_add_numbered_adapter(adapter);
mutex_lock(&core_lock);
id = idr_alloc(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
mutex_unlock(&core_lock);
if (id < 0)
return id;
adapter->nr = id;
return i2c_register_adapter(adapter);
EXPORT_SYMBOL(i2c_add_adapter);
of_node 指针显然不为空,在前面 rk3x_i2c_probe 函数里已经进行了赋值,所以会跑进 if 分支里。if 里做的事情也很简单,拿到该 adapter 的 i2c 总线号,然后进一步去调用 __i2c_add_numbered_adapter 来注册该 adapter。总线号通过 of_alias_get_id 这个函数来获取,一般会在 dts 里指定,也即静态分配 ID 号。函数 of_alias_get_id 的代码如下:
/**
* of_alias_get_id - Get alias id for the given device_node
* @np: Pointer to the given device_node
* @stem: Alias stem of the given device_node
*
* The function travels the lookup table to get the alias id for the given
* device_node and alias stem. It returns the alias id if found.
*/
int of_alias_get_id(struct device_node *np, const char *stem)
struct alias_prop *app;
int id = -ENODEV;
mutex_lock(&of_mutex);
list_for_each_entry(app, &aliases_lookup, link)
if (strcmp(app->stem, stem) != 0)
continue;
if (np == app->np)
id = app->id;
break;
mutex_unlock(&of_mutex);
return id;
EXPORT_SYMBOL_GPL(of_alias_get_id);
函数会遍历全局链表 aliases_lookup 下的每一个成员(struct alias_prop 结构体),然后逐一比对字符串 stem,亦即 "i2c"。如果进一步连 device node 指针也匹配了,则说明找到该 adapter(i2c 控制器)设备结点的 id 号。要读懂这段代码,还得从全局链表 aliases_lookup 说起。这个链表会解析 dts 里的 aliases 结点,aliases 结点信息如下:
aliases
i2c0 = &i2c0;
i2c1 = &i2c1;
i2c2 = &i2c2;
i2c3 = &i2c3;
i2c4 = &i2c4;
i2c5 = &i2c5;
serial0 = &uart0;
serial1 = &uart1;
serial2 = &uart2;
serial3 = &uart3;
serial4 = &uart4;
spi0 = &spi0;
spi1 = &spi1;
spi2 = &spi2;
;
通过结点我们可以知道,SoC 一共集成了 6 个 I2C 控制器。第一个 if 过滤掉后面的串口和 spi 结点,只剩下 i2c 结点,第二个 if 就可以找到与该 adapter 对应的 ID 号。接着往下看函数 __i2c_add_numbered_adapter:
/**
* __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1
* @adap: the adapter to register (with adap->nr initialized)
* Context: can sleep
*
* See i2c_add_numbered_adapter() for details.
*/
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
int id;
mutex_lock(&core_lock);
/* IDR机制,一种让ID和数据结构关联的机制 */
id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1,
GFP_KERNEL);
mutex_unlock(&core_lock);
if (id < 0)
return id == -ENOSPC ? -EBUSY : id;
return i2c_register_adapter(adap);
从函数注释说明可以知道,只有指定了 adapter 的 ID 号(adapter->nr 不能为 -1)才能调用该函数。函数和 i2c_add_adapter 函数后半部分很像,只不过后者是动态分配了 ID 号。__i2c_add_numbered_adapter 主要做了两件事情:
1、调用 idr_alloc 使 ID 号和 adapter 结构体按照 IDR 机制关联起来。IDR 用类基数树结构来构造一个稀疏数组,以 ID 为索引找到对应数组元素,进而找到对应的数据结构指针。具体的代码没有分析过,就不班门弄斧了,有兴趣的朋友可以深入研究研究。
2、进一步调用 i2c_register_adapter:
static int i2c_register_adapter(struct i2c_adapter *adap)
int res = 0;
/* Can't register until after driver model init */
/* 必须要保证I2C总线注册之后才能注册adapter */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
res = -EAGAIN;
goto out_list;
/* Sanity checks */
/* 如果adapter的name或algo算法回调为空,那么直接返回 */
if (unlikely(adap->name[0] == '\\0'))
pr_err("i2c-core: Attempt to register an adapter with "
"no name!\\n");
return -EINVAL;
if (unlikely(!adap->algo))
pr_err("i2c-core: Attempt to register adapter '%s' with "
"no algo!\\n", adap->name);
return -EINVAL;
rt_mutex_init(&adap->bus_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
/*
* 本来这里进去会有调用总线probe的机会,但是由于i2c bus
* 没有指定match回调,所以会在中途返回,而且返回的是0
*/
res = device_register(&adap->dev);
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\\n", adap->name);
......
exit_recovery:
/* create pre-declared device nodes */
of_i2c_register_devices(adap);
acpi_i2c_register_devices(adap);
acpi_i2c_install_space_handler(adap);
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
/*
* 一般总线驱动会先于设备驱动加载,此时i2c总线上并没有设备驱动
*/
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);
return 0;
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
第一个 if 用来判断 i2c 总线(也可以理解为 i2c 核心层)是否注册,如果没有注册则不能注册 adapter,函数直接返回。一般来说,总线驱动模块会在系统启动很早的时候就加载进系统,那怎么判断总线是早于总线驱动加载呢?这就涉及到内核模块加载机制中的优先级问题了,简单来说,就是各个模块加载时会调用下面的宏函数,内核以此来决定谁先加载谁后加载,感兴趣的朋友可以深入了解下其实现机制。
#define __define_initcall(level,fn,id) \\
static initcall_t __initcall_##fn##id __attribute_used__ \\
__attribute__((__section__(".initcall" level ".init"))) = fn
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);
总线驱动的加载使用的是宏函数 module_platform_driver ,展开以后就是我们非常熟悉的 module_init,对应上表优先级是 6。而i2c 总线的注册用的是 postcore_initcall,优先级是 2。为了更好的理解前面的 if 判断,我们看看 i2c 总线加载的入口代码(删掉了部分无关代码):
static int __init i2c_init(void)
int retval;
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
return 0;
class_err:
bus_unregister(&i2c_bus_type);
return retval;
static void __exit i2c_exit(void)
i2c_del_driver(&dummy_driver);
bus_unregister(&i2c_bus_type);
tracepoint_synchronize_unregister();
/* We must initialize early, because some subsystems register i2c drivers
* in subsys_initcall() code, but are linked (and initialized) before i2c.
*/
postcore_initcall(i2c_init);
module_exit(i2c_exit);
bus_register 里对指针 p 做了初始化,这也是为什么 i2c_register_adapter 里第一个 if 会用 p 是否为空来判断 i2c 总线是否注册。关于 p 的相关代码如下:
/**
* bus_register - register a driver-core subsystem
* @bus: bus to register
*
* Once we have that, we register the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the subsystem.
*/
int bus_register(struct bus_type *bus)
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
......
接着往下分析 i2c_register_adapter。adapter 的 name 和 algo 算法回调不能为空,否则直接返回。device_register 的行为请看注释,如有兴趣可以跟进去进一步分析,代码跟的越深就越远离本文的主旨了,所以这里就此打住。接着往下走,就来到了函数 数 of_i2c_register_devices,该函数可谓总线驱动的最精彩的部分了,这里可以看到从设备的软件抽象 strcut i2c_client 结构是如何构建的,以及是如何与 adapter 联系上的。话不多说,上代码:
static void of_i2c_register_devices(struct i2c_adapter *adap)
struct device_node *node;
/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;
dev_dbg(&adap->dev, "of_i2c: walking child nodes\\n");
for_each_available_child_of_node(adap->dev.of_node, node)
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue;
/* 传过去的设备结点是DTS i2cx下的每一个从设备结点 */
of_i2c_register_device(adap, node);
for_each_available_child_of_node,根据名字也能猜出,函数会遍历 adap->dev.of_node 下的每一个子结点,然后调用 of_i2c_register_device。要分析清楚这个 for 循环,就不得不去了解 i2c 控制器和挂在其下的 i2c 从设备的硬件信息在 dts 里是如何组织的。自引入 DTS 以后,i2c 的硬件信息需要按照如下格式进行组织:
i2c1: i2c@ff140000
compatible = "rockchip,rk3399-i2c";
reg = <0x0 0xff140000 0x0 0x1000>;
interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
clock-names = "i2c";
clocks = <&cru PCLK_I2C1>;
pinctrl-names = "default";
pinctrl-0 = <&i2c1_xfer>;
status = "okay";
;
&i2c1
status = "okay";
syr827: syr827@40
compatible = "silergy,syr82x";
reg = <0x40>;
status = "okay";
......
;
syr828: syr828@41
compatible = "silergy,syr82x";
reg = <0x41>;
status = "okay";
......
;
...
;
以贴出来的 dts 代码为例,for_each_available_child_of_node 就会遍历 syr827 和 syr828 子结点,而这两个子结点也会对应实际硬件中的两个 i2c 从设备。接着往下走:
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
struct i2c_client *result;
struct i2c_board_info info = ;
struct dev_archdata dev_ad = ;
const __be32 *addr_be;
u32 addr;
int len;
dev_dbg(&adap->dev, "of_i2c: register %s\\n", node->full_name);
/* info.type很重要,后面会赋值给client->name */
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0)
dev_err(&adap->dev, "of_i2c: modalias failure on %s\\n",
node->full_name);
return ERR_PTR(-EINVAL);
/* 从设备地址 */
addr_be = of_get_property(node, "reg", &len);
if (!addr_be || (len < sizeof(*addr_be)))
dev_err(&adap->dev, "of_i2c: invalid reg on %s\\n",
node->full_name);
return ERR_PTR(-EINVAL);
/*
* 判断是10位地址还是7位地址
* 没太搞明白,通过判断addr的bit31和bit30
* 来判断是10位地址还是7位地址?
*/
addr = be32_to_cpup(addr_be);
if (addr & I2C_TEN_BIT_ADDRESS)
addr &= ~I2C_TEN_BIT_ADDRESS;
info.flags |= I2C_CLIENT_TEN;
if (addr & I2C_OWN_SLAVE_ADDRESS)
addr &= ~I2C_OWN_SLAVE_ADDRESS;
info.flags |= I2C_CLIENT_SLAVE;
/* 检查地址的合法性 */
if (i2c_check_addr_validity(addr, info.flags))
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\\n",
addr, node->full_name);
return ERR_PTR(-EINVAL);
info.addr = addr;
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
/* 实例化该设备结点,用struct i2c_client描述 */
result = i2c_new_device(adap, &info);
if (result == NULL)
dev_err(&adap->dev, "of_i2c: Failure registering %s\\n",
node->full_name);
of_node_put(node);
return ERR_PTR(-EINVAL);
return result;
of_modalias_node 用来处理 info.type 变量,正如注释说明的,很重要,后面从设备驱动和设备 match 时会用到,直接决定了设备驱动能否匹配上进而进入 probe 探测函数。of_modalias_node 的代码如下:
/**
* of_modalias_node - Lookup appropriate modalias for a device node
* @node: pointer to a device tree node
* @modalias: Pointer to buffer that modalias value will be copied into
* @len: Length of modalias value
*
* Based on the value of the compatible property, this routine will attempt
* to choose an appropriate modalias value for a particular device tree node.
* It does this by stripping the manufacturer prefix (as delimited by a ',')
* from the first entry in the compatible list property.
*
* This routine returns 0 on success, <0 on failure.
*/
int of_modalias_node(struct device_node *node, char *modalias, int len)
const char *compatible, *p;
int cplen;
compatible = of_get_property(node, "compatible", &cplen);
if (!compatible || strlen(compatible) > cplen)
return -ENODEV;
/*
* 查找字符串compatible中首次出现字符','的位置
* 返回的地址是:
* 1.compatible指向的字符串中第一次出现','字符的地址,
* 2.如果compatible中不存在','则返回NULL
*/
p = strchr(compatible, ',');
/*
* 如果没有逗号则直接把compatible copy给modalias,
* 即上一级函数的info.type
*/
strlcpy(modalias, p ? p + 1 : compatible, len);
return 0;
EXPORT_SYMBOL_GPL(of_modalias_node);
注释也说的很明白了,对应到上面 dts 里的结点 syr827,info.type就指向字符串 "syr82x"。再次回到 of_i2c_register_device,接下来的代码是获取从设备的 i2c 地址,dts 里 reg 属性后面的 value 即为从设备地址。接下来就是 i2c_new_device 这个函数登场了,构建 struct i2c_client 结构体,注意函数的第二参数就是前面对各个成员变量赋值初始化的 info。
/**
* i2c_new_device - instantiate an i2c device
* @adap: the adapter managing the device
* @info: describes one I2C device; bus_num is ignored
* Context: can sleep
*
* Create an i2c device. Binding is handled through driver model
* probe()/remove() methods. A driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe hotplugging will
* load the driver module). This call is not appropriate for use by mainboard
* initialization logic, which usually runs during an arch_initcall() long
* before any i2c_adapter could exist.
*
* This returns the new i2c client, which may be saved for later use with
* i2c_unregister_device(); or NULL to indicate an error.
*/
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
struct i2c_client *client;
int status;
/* 申请内存,分配一个struct i2c_client */
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
/* 自此该adapter就和该client扯上关系了 */
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
/*
* client->name的赋值,后面设备驱动注册时会用到这个name,
* match上了才能进一步跑进probe回调
*/
strlcpy(client->name, info->type, sizeof(client->name));
status = i2c_check_addr_validity(client->addr, client->flags);
if (status)
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
/* Check for address business */
status = i2c_check_addr_ex(adap, i2c_encode_flags_to_addr(client));
if (status != 0)
dev_err(&adap->dev, "%d i2c clients have been registered at 0x%02x",
status, client->addr);
client->dev.parent = &client->adapter->dev;
/* 设备挂在i2c_bus_type下 */
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
client->dev.fwnode = info->fwnode;
i2c_dev_set_name(adap, client, status);
/* client也是一个device,也需要调用device_register() */
status = device_register(&client->dev);
if (status)
goto out_err;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\\n",
client->name, dev_name(&client->dev));
return client;
out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
EXPORT_SYMBOL_GPL(i2c_new_device);
代码逻辑非常清晰,就不赘述了,这里只强调一点,函数头部上方的原著注释一定要看!
至此,总线驱动的核心代码基本就分析完了,主要做了两件事情:
1、分配 ID 号,因为一个 SoC 内部通常会有多个 i2c 控制器,而每一个控制器都共用一份总线驱动代码;
2、解析控制器下的每一个从设备,并构建 client 结构体,这一点非常重要,因为根据设备驱动模型,后面的从设备驱动必须要先找到从设备信息(即 client 结构体)并匹配上才能进一步走下去。下一节我们就重点分析从设备驱动是怎么加载运行起来的。
————————————————
原文链接:https://blog.csdn.net/weixin_43555423/article/details/90739753
以上是关于I2C协议和驱动框架分析的主要内容,如果未能解决你的问题,请参考以下文章