转载请标明出处floater的csdn blog,http://blog.csdn.net/flaoter
1 体系结构
linux的i2c体系由以下三部分组成:
1.1 i2c核心
由linux内核提供,定义基本数据结构,实现i2c总线,驱动和设备的注册、注销,通信方法等。与设备无关。
1.2 i2c控制器驱动
由i2c控制器厂商提供,目前i2c控制器大多集中在soc芯片中,所以大多由soc厂商提供。代表i2c master,主要实现控制器的设备注册和驱动注册。
1.3 i2c设备驱动
一般由外设厂商提供,代表i2c slave,主要实现从设备的注册和驱动注册。
2 数据结构
i2c_adapter对应一个适配器设备,用于描述i2c master控制器
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;
};
i2c总线传输方法,总线传输协议的就是在此结构体成员函数中实现的。
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 *);
};
上述两个数据结构i2c_adpter, i2c_algorithm都需要在控制器驱动中进行实现。
i2c_client是i2c slave设备的数据结构
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 device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
i2c设备驱动数据结构,是i2c_client设备的驱动。
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared. You should avoid
* using this, it will be removed in a near future.
*/
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 *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
*/
void (*alert)(struct i2c_client *, unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
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;
};
i2c_driver需要在i2c设备驱动中进行实现。
3 控制器驱动
内核源码目录结构及config配置如下:
drivers/i2c/
├── algos
│ ├── i2c-algo-bit.c
│ ├── i2c-algo-pca.c
│ ├── i2c-algo-pcf.c
│ ├── i2c-algo-pcf.h
│ ├── Kconfig
│ └── Makefile
├── busses
│ ├── i2c-gpio.c
│ ├── i2c-vendor.c
│ ├── Kconfig
│ ├── Makefile
├── i2c-boardinfo.c
├── i2c-core.c
├── i2c-core.h
├── i2c-dev.c
├── i2c-mux.c
├── i2c-slave-eeprom.c
├── i2c-smbus.c
├── i2c-stub.c
├── Kconfig
├── Makefile
└── muxes
├── Kconfig
└── Makefile
//gpio模拟
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-y += algos/ busses/ muxes/
obj-$(CONFIG_I2C_VENDORNAME) += i2c-vendor.o //控制器驱动,隐藏了厂商名
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
控制器驱动部分包括控制器设备注册,控制器驱动注册,总线传输和用户层接口四个部分,下面分别对它们进行说明。
3.1 控制器设备注册
该部分代码在dt解析中实现,请参见kernel初始化时dt解析的代码。i2c controller是platform device。
dts中定义如下:
i2c2: i2c@50d00000 {
compatible = "vendor-i2c";
reg = <0 0x70d00000 0 0x1000>;
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "i2c";
clocks = <&clk_i2c0>;
clock-frequency = <400000>;
};
3.2 控制器驱动注册
一般SOC厂商都会自己定义专有控制器结构体,不同厂商的数据结构成员不同,但肯定与自身控制器的使用密切相关的内容,比如内存基地址,clock,中断号等。linux驱动与设备是一对多的关系,在i2c_adapter设备注册时,控制器的结构体信息一般会提供给i2c_adapter作为私有数据。,本文中的结构体如下:
struct vendor_i2c {
struct i2c_msg *msg;
struct i2c_adapter adap;
void __iomem *membase;
struct clk *clk;
unsigned int src_clk;
int irq;
struct vendor_platform_i2c *pdata;
};
static struct of_device_id vendor_i2c_of_match[] = {
{ .compatible = "vendor-i2c", },
};
static struct platform_driver vendor_i2c_driver = {
.probe = vendor_i2c_probe,
.remove = vendor_i2c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "vendor-i2c",
.of_match_table = of_match_ptr(vendor_i2c_of_match),
.pm = &vendor_i2c_pm_ops,
},
};
static int __init vendor_i2c_init(void)
{
return platform_driver_register(&vendor_i2c_driver);
}
arch_initcall_sync(vendor_i2c_init);
i2c controller是platform_device,因此驱动通过platform_driver_register进行注册。由于compatible属性为”vendor-i2c”的platform device已经在3.1中注册完成,vendor_i2c_probe会被加载执行。
static int vendor_i2c_probe(struct platform_device *pdev)
{
struct vendor_i2c *pi2c;
struct resource *res;
struct device_node *np = pdev->dev.of_node;
...
pi2c = devm_kzalloc(&pdev->dev, sizeof(struct vendor_i2c), GFP_KERNEL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //dt中reg资源
//i2c_adapter数据结构的填充
i2c_set_adapdata(&pi2c->adap, pi2c); //adapter->dev->drvdata=pi2c
snprintf(pi2c->adap.name, sizeof(pi2c->adap.name), "%s", "vendor-i2c");
pi2c->adap.owner = THIS_MODULE;
pi2c->adap.retries = 3;
pi2c->adap.algo = &vendor_i2c_algo;
pi2c->adap.algo_data = pi2c;
pi2c->adap.dev.parent = &pdev->dev;
pi2c->adap.nr = pdev->id;
pi2c->adap.dev.of_node = pdev->dev.of_node;
pi2c->membase = devm_ioremap_nocache(&pdev->dev, res->start,
res->end - res->start);
//clock相关
pi2c->pdata = devm_kzalloc(&pdev->dev,
sizeof(struct vendor_platform_i2c), GFP_KERNEL);
memcpy(pi2c->pdata, &vendor_platform_i2c_default,
sizeof(struct vendor_platform_i2c));
if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",
&prop))
pi2c->pdata->bus_freq = prop;
ret = vendor_i2c_clk_init(pi2c);
clk_prepare_enable(pi2c->clk);
vendor_i2c_enable(pi2c);
clk_disable(pi2c->clk);
ret = i2c_add_numbered_adapter(&pi2c->adap); //adapter的注册过程在下面进行了展开
...
}
其中,最后的adapter注册如下代码,在i2c-core.c实现。
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
return __i2c_add_numbered_adapter(adap);
}
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
return i2c_register_adapter(adap);
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
...
dev_set_name(&adap->dev, "i2c-%d", adap->nr); //adapter->dev的name是i2c-0,1,2等,设备注册后udev会在/dev下创建对应的设备名节点
adap->dev.bus = &i2c_bus_type; //i2c_bus_type对adpater作用??
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev); //adapter设备注册
exit_recovery:
/* create pre-declared device nodes */
of_i2c_register_devices(adap); //(1)
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); //(2)
}
(1)通过对dt进行解析,对挂在i2c controller下的i2c从设备进行注册。i2c从设备在linux中是通过i2c_client数据结构进行描述的,因此注册过程主要是对i2c_client数据结构的填充并将它注册到内核中。dt中的描述如下,本例中的i2c从设备是BOSCH的加速度传感器:
&i2c2 {
status = "okay";
clock-frequency = <400000>;
bma2x2@19{
compatible = "BOSCH,bma2x2";
reg = <0x19>;
gpios = <&ap_gpio 92 0>;
};
}
继续i2c_client设备的注册,代码如下:
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;
of_i2c_register_device(adap, node); //调用of_i2c_register_device进行设备注册,下面有展开
}
}
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);
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { //解析dt中设备compiatble属性填充info.type
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);
}
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",
info.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;
result = i2c_new_device(adap, &info); //注册i2c device,在下面会进行展开
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;
}
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap; //client对应的adapter,建立client与adapter的对应关系
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;
strlcpy(client->name, info->type, sizeof(client->name)); //client->name的赋值,它来源于dt中的slave设备的compatible属性,后面设备与驱动的probe主要是通过判断此设备成员和设备驱动的id_table成员是否一致
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_busy(adap, i2c_encode_flags_to_addr(client));
if (status)
goto out_err;
client->dev.parent = &client->adapter->dev; //client设备与adapter设备的关系
client->dev.bus = &i2c_bus_type; //client设备的总线类型是i2c_bus_type,client与driver的probe就是通过i2c_bus_type的probe实现的
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 = 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;
}
(2) 扫描 __i2c_board_list,对已经添加到此list的i2c设备进行注册。在device tree应用之前,i2c从设备通过调用i2c_register_board_info将自己添加到 __i2c_board_list,目前此中方法已很少使用。
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info)) //设备注册
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
下面是在kernel的mach目录下找到的一个以此种方式添加到list中的例子。
static struct i2c_board_info i2c_devs[] __initdata = {
{ I2C_BOARD_INFO("wm8753", 0x1A), },
{ I2C_BOARD_INFO("24c08", 0x50), },
};
static void __init smdk6400_machine_init(void)
{
i2c_register_board_info(0, i2c_devs, ARRAY_SIZE(i2c_devs));
}
至此,在控制器驱动中不仅对i2c_adapter设备进行了注册,还对i2c_client设备进行了注册,并建立了adapter和client之间的关系,而且它们都是i2c_bus_type总线类型的设备。
此处要强调一下, i2c控制器是platform device,它的驱动注册是通过platform_bus, platform_device和platform_driver之间的关系进行probe的,与i2c_bus_type不要混淆。
3.3 总线传输
在controller驱动中还对i2c另一重要结构体进行了赋值,它是发送和接收数据的实现函数。
pi2c->adap.algo = &vendor_i2c_algo;
static const struct i2c_algorithm vendor_i2c_algo = {
.master_xfer = vendor_i2c_master_xfer,
.functionality = vendor_i2c_func,
};
static int
vendor_i2c_master_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs, int num)
{
int im = 0, ret = 0;
struct vendor_i2c *pi2c = i2c_adap->algo_data;
clk_enable(pi2c->clk);
for (im = 0; ret >= 0 && im != num; im++) {
ret = vendor_i2c_handle_msg(i2c_adap, &msgs[im], im == num - 1);
}
clk_disable(pi2c->clk);
return (ret >= 0) ? im : -1;
}
static int
vendor_i2c_handle_msg(struct i2c_adapter *i2c_adap,
struct i2c_msg *pmsg, int is_last_msg)
{
int rc;
struct vendor_i2c *pi2c = i2c_adap->algo_data;
rc = vendor_i2c_send_target_addr(pi2c, pmsg);
if ((pmsg->flags & I2C_M_RD))
return vendor_i2c_readbytes(pi2c, pmsg->buf, pmsg->len);
else
return vendor_i2c_writebytes(pi2c, pmsg->buf, pmsg->len,
is_last_msg);
}
vendor_i2c_send_target_addr, vendor_i2c_readbytes, vendor_i2c_writebytes等依赖于具体厂商的实现。
3.4 用户层接口
此外,i2c-dev.c中定义了与应用层交互接口的ops,用户可以直接打开/dev/i2c-0或其它设备节点,使用文件描述符进行读写等操作,不必关心底层实现。
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
读写操作最终都是通过i2c-core提供的i2c_transfer函数实现的,i2c_transfer又是通过调用adap->algo->master_xfer实现的,此处不再进行展开。
4 设备驱动
i2c设备驱动,即i2c master对应的slave设备的驱动,在前面我们已经提到controller驱动中已经对slave设备i2c_client进行了注册,此处是对i2c_client设备对应的驱动i2c_driver进行注册。
此例中的i2c_driver使用传感器的驱动bma2x2_driver,注册代码如下。
#define SENSOR_NAME "bma2x2"
static const struct i2c_device_id bma2x2_id[] = {
{ SENSOR_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bma2x2_id);
static const struct of_device_id bma2x2_of_match[] = {
{ .compatible = "BOSCH,bma2x2", },
{ }
};
MODULE_DEVICE_TABLE(of, bma2x2_of_match);
static struct i2c_driver bma2x2_driver = {
.driver = {
.owner = THIS_MODULE,
.name = SENSOR_NAME,
.of_match_table = bma2x2_of_match,
.pm = &bma2x2_pm_ops
},
.id_table = bma2x2_id,
.probe = bma2x2_probe,
.remove = bma2x2_remove,
.shutdown = bma2x2_shutdown,
};
static int __init BMA2X2_init(void)
{
return i2c_add_driver(&bma2x2_driver);
}
static void __exit BMA2X2_exit(void)
{
i2c_del_driver(&bma2x2_driver);
}
module_init(BMA2X2_init);
module_exit(BMA2X2_exit);
驱动注册i2c_add_driver函数的调用过程中会调用dev->bus->probe,其中dev是通过遍历所有dev设备得到的。当i2c_client设备注册完成后,client->dev.bus = &i2c_bus_type,因此,此处调用的bus->probe就是i2c_bus_type.probe函数指针指向的函数。
i2c_add_driver
i2c_register_driver(THIS_MODULE, driver)
res = driver_register(&driver->driver);
ret = bus_add_driver(drv);
driver_attach(drv);
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
__driver_attach
driver_probe_device(drv, dev);
ret = really_probe(dev, drv);
dev->bus->probe(dev);
i2c_bus_type在i2c-core.c中定义如下:
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,
};
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;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
status = of_clk_set_defaults(dev->of_node, false);
if (status < 0)
return status;
status = dev_pm_domain_attach(&client->dev, true);
if (status != -EPROBE_DEFER) {
status = driver->probe(client, i2c_match_id(driver->id_table,
client)); //driver->probe的调用依赖于i2c_match_id的结果
if (status)
dev_pm_domain_detach(&client->dev, true);
}
return status;
}
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_match_id的实现就是在比对client->name和id->name。client->name在controller驱动中注册i2c_client设备的过程中有过赋值,它来自于dt中slave设备的compatible属性。id_name在设备驱动中定义,本例中就是宏定义SENSOR_NAME,即 “bma2x2”,与dt中slave中的compatible属性逗号后面的字符串一致。因此driver->probe即 bma2x2_probe会被调用。
5 总结
理解linux i2c框架就要理清如下关系:
1. bus, device, device_driver的驱动模型。
2. controller和slave设备的关系。
3. i2c_client与i2c_driver的关系。
4. core层的价值。