Linux i2c子系统
一、前言
因为自己在研发过程中经常要涉及到tp驱动程序,而tp驱动就涉及到i2c驱动。经常可以看到在驱动程序中会定义一个struct i2c_driver
的数据结构,并实现里面的某些成员,比如probe、resume、suspend、remove、id_table
,之后会在module_init()
中调用i2c_add_driver()
添加这个驱动。比如说像下面这个样子:
static const struct i2c_device_id xxx_ts_id[] = {
{xxx, 0},
{xxx_1,1}
};
static struct i2c_driver xxx_ts_driver = {
.driver = {
.name = XXX_I2C_NAME,
.owner = THIS_MODULE,
},
.probe = xxx_ts_probe,
.remove = xxx_ts_remove,
.id_table = xxx_ts_id,
};
static int __init xxx_ts_init(void)
{
int ret;
printk("==xxx_ts_init==\n");
ret = i2c_add_driver(&xxx_ts_driver);
printk("ret=%d\n",ret);
return ret;
}
module_init(xxx_ts_init);
按照仅有的一点知识就知道接下来会发生什么。当调用i2c_add_driver(&xxx_ts_driver);
,如果设备与驱动匹配,就会执行xxx_ts_probe()
的内容,后面就可以通过i2c读写接口操作该i2c设备了。这样看起来确实很简单,但对于我来说并不够,我想知道linux系统里面都做了一些什么事,于是我就看书查资料想要弄清楚linux i2c子系统的工作原理,因此就有了这一篇文章。本文章是根据整理的资料以及自身的理解来完成了,如有不足之处请指正。
文中对代码的验证是在Rockchip3126平台验证的。
二、i2c设备与驱动的匹配过程
调用i2c_add_driver()
实际上的调用i2c_register_driver()
。这个函数的详细内容如下:
/*
* An i2c_driver is used with one or more i2c_client (device) nodes to access
* i2c slave chips, on a bus instance associated with some i2c_adapter.
*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver);
if (res)
return res;
/* Drivers should switch to dev_pm_ops instead. */
if (driver->suspend)
pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
driver->driver.name);
if (driver->resume)
pr_warn("i2c-core: driver [%s] using legacy resume method\n",
driver->driver.name);
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
return 0;
}
从函数名就可以看出,这个函数的主要功能是注册i2c驱动。并且注释已经说明了:
i2c_driver
通过一个或多个i2c_client
的设备节点去访问i2c的从芯片。与一些i2c_adapter
相联系的总线上。这里面出现了三个名词
i2c_client
、i2c_adapter
还有总线。此处暂且不讨论这三者之间的联系。但是这几个非常之重要。
1、简要流程
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver);
if (res)
return res;
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
往驱动核心中的i2c drivers链表中添加i2c驱动,并且该驱动程序的总线类型为i2c_bus_type
。当调用driver_register()
成功的时候,依次会调用i2c_bus_type
中的i2c_device_match()
判断设备与驱动的匹配,当设备与驱动匹配成功后调用i2c_device_probe()
进而最终会调用xxx_ts_probe()
。
至于其详细的调用流程,可以参照我对linux理解之driver_register 。这里只列出一个大概的流程:
driver_register -> bus_add_driver -> driver_attach -> bus_for_each_dev -> __driver_attach -> driver_match_device -> i2c_device_match -> driver_probe_device -> really_probe -> i2c_device_probe
注:这几个函数的定义分别在 drivers\base 下的driver.c 和 dd.c 中。i2c相关的内容在 drivers\i2c\i2c-core.c中。
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
在really_probe()
有下面一段代码就会调用i2c_device_probe()
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
2、i2c_device_id
的匹配
i2c_device_match()
会根据情况调用不同的函数来判断驱动与设备是否匹配
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
printk("[I2C-TRACE] %s start, client->name = %s\n", __func__, client->name);
if (!client)
return 0;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* Then ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
printk("[I2C-TRACE] %s: driver->name = %s\n", __func__, driver->driver.name);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
首先会尝试通过of_driver_match_device
的接口来判断dts中配置的device与驱动是否匹配。 其实最后就是拿 device tree 中的 compatible 属性跟 driver 中的 of_match_table->compatible
做字符串的比較, 如果字符串相同, 那就匹配成功。
最后也通过调用i2c_match_id()
判断设备与驱动是否匹配。
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
通过i2c_device_id
中的name与client->name
是否一致来判断设备与驱动是否匹配。
在前面的i2c_driver
中有一个成员i2c_device_id
是用来标记此i2c驱动支持哪些设备。比如说同一个触控ic的厂商,他有多个产品型号xxx/xxx_1/xxx_2,他的xxx驱动程序可以同时支持这几个型号。
3、i2c_device_probe
接下来就很明显了,是probe的过程。
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;
if (!client)
return 0;
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}
4、小结
这里面的内容只是根据代码的流程解释了i2c设备与驱动的匹配过程。但是并没有涉及到linux i2c子系统的核心内容。在前面被忽略的i2c_cline
、i2c_adaptor
、i2c_algorithm
、i2c总线、i2c读写操作等内容将会在下面进行逐一的解释以及说明。
三、理论重点
1、概述
为了能够更好的理解i2c子系统,建议先看看宋宝华老师的《Linux设备驱动开发详解(第2版)》中关于i2c的详细描述。
此处摘抄文中的总结部分:
linux i2c驱动体系结构主要由3部分组成,即i2c核心、i2c总线驱动和i2c设备驱动。i2c核心是i2c总线驱动和i2c设备驱动的中间枢纽,它以通用的、与平台无关的接口实现了i2c中设备与适配器的沟通。i2c总线驱动填充
i2c_adapter
和i2c_algorithm
结构体,i2c设备驱动填充i2c_driver
结构体并实现其本身所对应设备类型的驱动。
2、软件拓扑
此处的linux i2c子系统的软件拓扑图摘自Linux I2C framework(1)_概述。
1)platform bus(/sys/bus/platform)是驱动工程师常见的bus,用于挂载和CPU通过系统总线连接的各类外设。在I2C framework中,I2C控制器直接从属于platform bus,我们在linux kernel中常说的I2C driver,都是指I2C controller driver,都是以platform driver的形式存在,当然,对应的控制器是platform device。
2)与此同时,kernel抽象出I2C bus(/sys/bus/i2c),用于挂载和I2C controller通过I2C总线连接的各个I2C slave device。
3)比较特殊的地方是,I2C core使用一个虚拟实体—-I2C adapter,抽象I2C controller有关的功能(主要是数据的收发),I2C adapter也挂载在I2C bus上。
4)I2C adapter和I2C slave device都挂载在I2C bus上,就可以方便的进行Master(I2C adapter)和Slave之间的匹配操作,并通过I2C core提供的统一接口,访问I2C salve device,进行数据的收发。
5)以上各实体在sysfs中的位置,已经在“图片2”中通过红色字体标注,大家可自行理解。
(1)、platform bus上挂载挂载和CPU通过系统总线连接的各类外设。这里的platform driver实际上是对应于代码中driver/i2c/bus/i2c-rockchip.c 文件中的
static struct platform_driver rockchip_i2c_driver = {
.probe = rockchip_i2c_probe,
.remove = rockchip_i2c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "rockchip_i2c",
.pm = ROCKCHIP_I2C_PM_OPS,
.of_match_table = of_match_ptr(rockchip_i2c_of_match),
},
};
(2)、 i2c controller实际上就是CPU访问i2c总线的硬件接口,在我的理解中就是某几个寄存器。i2c controller与i2c adapter是一一对应的关系。i2c adapter是软件抽象出来的i2c控制器接口,用于标识物理的i2c总线(physical i2c bus),且该总线需要有一套用于访问slave设备的算法(access algorithm)。
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
*/
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
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;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
};
下面的内容摘自Linux I2C framework(2)_I2C provider对i2c_adapter
数据结构的解释:
1)由它的注释可知,struct i2c_adapter是用于标识物理的I2C总线(physical i2c bus),且该总线需要有一套用于访问slave设备的算法(access algorithm)。
2)所谓的access algorithm,就是通过I2C总线发送和接收数据的方法,它保存在algo指针(struct i2c_algorithm,具体可参考后续2.2小节的描述)中。
3)基于I2C传输的特性,不一定每一次总线访问(发送或者接收数据)都会成功,在传输失败的时候,可以选择重试。重试的逻辑由I2C core自行完成,但I2C controller driver需要设定重试的次数,这就是retries字段的意义。另外,有些consumer对结果的返回是有时间要求的,因此不能无节制的重试,timeout字段(单位为jiffies)在retries基础上,增加了时间限制,超过这个时间,就不能重试了。
4)nr,该I2C bus的ID,会体现在sysfs中(/sys/bus/i2c/devices/i2c-n中的‘n’),可由I2C controller driver在注册adapter时指定,或者通过DTS解析(后面会介绍),或者自动分配。
5)class,该I2C bus支持哪些类型的slave device,只有匹配的slave device才能和bus绑定。具体的类型包括(可参考include/linux/i2c.h中的定义和注释):
I2C_CLASS_HWMON,硬件监控类,如lm_sensors等;
I2C_CLASS_DDC,DDC是数字显示通道(Digital Display Channel)的意思, 通常用于显示设备信息的获取;
I2C_CLASS_SPD,存储类的模组;
I2C_CLASS_DEPRECATED,不再使用的class。
6)userspace_clients,一个i2c controller可以挂接多个client,所以这个成员就是用来标记挂接在该adapter上的设备链表头。
一个i2c controller(以下统统称之为i2c adapter)中上可以挂接多个i2c client,所以其成员有一个链表;i2c adapter要与i2c client通信,需要有对应的i2c algorithm,所以有一个const struct i2c_algorithm *algo
的成员。
3、驱动框架
此处的linux i2c子系统的驱动架构摘自【驱动】linux下I2C驱动架构全面分析。
第一层:提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层
第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层
第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备的attach_adapter()、detach_adapter()方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中的driver驱动层
第四层:实现i2c设备所对应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体设备device的write()、read()、ioctl()等方法,赋值给file_operations,然后注册字符设备(多数是字符设备)。覆盖图中的driver驱动层
第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。
在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成所有总线bus,如usb、pci、i2c等等。并且总线bus中的(与特定硬件相关的代码)已由芯片提供商编写完成,例如三星的s3c-2440平台i2c总线bus为/drivers/i2c/buses/i2c-s3c2410.c
第三第四层与特定device相干的就需要驱动工程师来实现了。
第一层
前面说过,在 driver/i2c/bus/i2c-rockchip.c 文件中会添加一个rockchip_i2c_driver
的平台驱动(i2c 总线驱动),当驱动成功之后进入probe,就会进行i2c_adapter
相关内容的填充以及添加。第一层所有的内容都在driver/i2c/bus/i2c-rockchip.c文件中实现。
多数的i2c总线驱动中会定义一个 xxx_i2c的结构体,此处为rockchip_i2c
,通过对其相关成员的访问来控制对应的功能。
rockchip_i2c
struct rockchip_i2c {
spinlock_t lock;
wait_queue_head_t wait;
unsigned int suspended;
struct i2c_msg *msg;
unsigned int is_busy;
int error;
unsigned int msg_ptr;
unsigned int irq;
enum rockchip_i2c_state state;
unsigned int complete_what;
unsigned long clkrate;
void __iomem *regs;
struct clk *clk;
struct device *dev;
struct resource *ioarea;
struct i2c_adapter adap;
unsigned long scl_rate;
unsigned long i2c_rate;
unsigned int addr;
unsigned char addr_1st, addr_2nd;
unsigned int mode;
unsigned int count;
unsigned int check_idle;
int sda_gpio, scl_gpio;
struct pinctrl_state *gpio_state;
};
从其成员可以看出,主要用于提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号)。
rockchip_i2c_probe()
在这个函数中,可以看出它都做了以下几件事:
1> 初始化i2c_adapter
中对应的内容以及添加i2c_adapter
:
i2c->dev = &pdev->dev;
i2c->adap.owner = THIS_MODULE;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->adap.retries = 2;
i2c->adap.timeout = msecs_to_jiffies(100);
i2c->adap.algo = &rockchip_i2c_algorithm;
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = np;
....
/* setup info block for the i2c core */
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add adapter\n");
return ret;
}
2> 申请 i2c controller需要用到的io、中断,以及对其硬件的初始化。
i2c->check_idle = true;
of_property_read_u32(np, "rockchip,check-idle", &i2c->check_idle);
if (i2c->check_idle) {
i2c->sda_gpio = of_get_gpio(np, 0);
if (!gpio_is_valid(i2c->sda_gpio)) {
dev_err(&pdev->dev, "sda gpio is invalid\n");
return -EINVAL;
}
ret = devm_gpio_request(&pdev->dev, i2c->sda_gpio, dev_name(&i2c->adap.dev));
if (ret) {
dev_err(&pdev->dev, "failed to request sda gpio\n");
return ret;
}
i2c->scl_gpio = of_get_gpio(np, 1);
if (!gpio_is_valid(i2c->scl_gpio)) {
dev_err(&pdev->dev, "scl gpio is invalid\n");
return -EINVAL;
}
ret = devm_gpio_request(&pdev->dev, i2c->scl_gpio, dev_name(&i2c->adap.dev));
if (ret) {
dev_err(&pdev->dev, "failed to request scl gpio\n");
return ret;
}
i2c->gpio_state = pinctrl_lookup_state(i2c->dev->pins->p, "gpio");
if (IS_ERR(i2c->gpio_state)) {
dev_err(&pdev->dev, "no gpio pinctrl state\n");
return PTR_ERR(i2c->gpio_state);
}
pinctrl_select_state(i2c->dev->pins->p, i2c->gpio_state);
gpio_direction_input(i2c->sda_gpio);
gpio_direction_input(i2c->scl_gpio);
pinctrl_select_state(i2c->dev->pins->p, i2c->dev->pins->default_state);
}
.....
ret = devm_request_irq(&pdev->dev, i2c->irq, rockchip_i2c_irq, 0,
dev_name(&i2c->adap.dev), i2c);
if (ret) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
return ret;
}
ret = clk_prepare(i2c->clk);
if (ret < 0) {
dev_err(&pdev->dev, "Could not prepare clock\n");
return ret;
}
i2c->i2c_rate = clk_get_rate(i2c->clk);
rockchip_i2c_init_hw(i2c, 100 * 1000);
dev_info(&pdev->dev, "%s: Rockchip I2C adapter\n", dev_name(&i2c->adap.dev));
从上面可以知道,这个i2c controller配置默认的i2c clock是100KHz而不是400KHz。
3> 驱动soc控制的i2c adapter在硬件上产生信号
在 i2c-rockchip.c 的函数列表就可以看到它所要做的哪些功能了。
4> 最后是注册i2c 设备
of_i2c_register_devices(&i2c->adap);
第二层
实现i2c_adapter
中的i2c_algorithm
对应的成员:
i2c-rockchip.c
static const struct i2c_algorithm rockchip_i2c_algorithm = {
.master_xfer = rockchip_i2c_xfer,
.functionality = rockchip_i2c_func,
};
i2c->adap.algo = &rockchip_i2c_algorithm;
i2c_algorithm
结构体的定义在include/linux/i2c.h
/*
* The following structs are for those who like to implement new bus drivers:
* i2c_algorithm is the interface to a class of hardware solutions which can
* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
* to name two of the most common.
*/
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
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);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
1)functionality,通过一个bitmap,告诉调用者该I2C adapter支持的功能
master_xfer,I2C协议有关的数据传输接口,输入参数是`struct i2c_msg
类型的数组(大小由num指定)。返回值是成功传输的msg的个数,如有错误返回负值。
3)smbus_xfer,SMBUS有关的数据传输接口,如果为NULL,I2C core会尝试使用master_xfer模拟。
其中i2c_msg
是i2c通信的基本传输单位,i2c的读写操作都是要事先填充该数据结构的成员。i2c_msg
的详细定义如下:
include/uapi/linux/i2c.h
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
第三层
该内容在文章的最前面已实现。
第四层
i2c-dev.c文件完全可以被看做一个i2c设备驱动,不过,它实现的一个i2c_clinet
是虚拟、临时的,随着设备文件的打开而产生,并随设备文件的关闭而撤销,并没有被添加到i2c_adapter
的client链表中。应用层可通过它的接口访问挂接在i2c controller上的设备。
四、设备的注册
每一个i2c 设备都对应一个i2c_client
,该定义如下:
include/linux/i2c.h
/**
* struct i2c_client - represent an I2C slave device
* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
* @addr: Address used on the I2C bus connected to the parent adapter.
* @name: Indicates the type of the device, usually a chip name that's
* generic enough to hide second-sourcing and compatible revisions.
* @adapter: manages the bus segment hosting this I2C device
* @driver: device's driver, hence pointer to access routines
* @dev: Driver model device node for the slave.
* @irq: indicates the IRQ generated by this device (if any)
* @detected: member of an i2c_driver.clients list or i2c-core's
* userspace_devices list
*
* An i2c_client identifies a single device (i.e. chip) connected to an
* i2c bus. The behaviour exposed to Linux is defined by the driver
* managing the device.
*/
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
一个i2c设备需要知道其地址信息,所以有addr
的成员,一个i2c设备是挂接在i2c controller上的,所以有adapter
成员,i2c设备有对应的驱动程序,所以有driver
的成员。这样的数据结构是完全与驱动框架的图完全相匹配的。
1、i2c_client
信息的定义
i2c_client
信息的定义有两种方法,一种是在dts中直接配置,另外一种是在BSP的板文件中通过i2c_board_info
填充。
dts中配置的格式如下:
ts@40 {
compatible = "xxx";
reg = <0x40>;
wake-gpio = <&gpio0 GPIO_D3 GPIO_ACTIVE_LOW>;
irp-gpio = <&gpio0 GPIO_A2 IRQ_TYPE_LEVEL_HIGH>;
revert_x = <0>;
revert_y = <0>;
};
dts的配置最终会转换为i2c_board_info
的形式。具体是在rockchip_i2c_probe()
的最后调用of_i2c_register_devices()
注册i2c设备。
BSP的配置如下:
static struct i2c_board_info __initdata xxx_i2c_board_info[] = {
{
I2C_BOARD_INFO("xxx", 0x40),
.irq = IRQ_PF5,
},
......
}
2、i2c_client
的注册
无论是采用dts还是BSP的形式,都要最终转换为i2c_board_info
,最后都是通过i2c-core.c中的i2c_new_device()
注册一个新的i2c设备。
/**
* 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)
五、总结
在上述的内容中,都离不开i2c_adapter
、i2c_algorithm
、i2c_client
、i2c_driver
这四个数据结构以及i2c-core.c这个重要的文件。
1、i2c_adapter
对应于i2c controller,这个控制器上可以挂接多个i2c设备。该主要控制产生i2c时序,如果要产生i2c时序,肯定需要sda、scl有可能还需要irq,这些资源的申请以及中断的注册都在 driver/i2c/bus/i2c-rockchip.c的文件中实现;
2、i2c_algorithm
是i2c的通信方法,每个i2c controller都对应于一个与硬件相关的通信方法;
3、i2c_client
和i2c_driver
就是我们所理解的设备驱动了。
4、i2c-core.c提供了一组不依赖于硬件平台的接口函数,是作为i2c总线驱动和i2c设备驱动之间的纽带。
更多推荐
所有评论(0)