文章基于
源码路径 kernel-3.2\drivers\i2c
i2c/
|-- busses
| |-- i2c-gpio.c
| `-- i2c-omap.c
|-- algos
| |-- i2c-algo-bit.c
| |-- i2c-algo-pca.c
| |-- i2c-algo-pcf.c
| `-- i2c-algo-pcf.h
|-- muxes
| `-- gpio-i2cmux.c
|-- i2c-boardinfo.c
|-- i2c-core.c
|-- i2c-core.h
|-- i2c-dev.c
|-- i2c-mux.c
`-- i2c-smbus.c
要了解 i2c 框架,重点看 busses 目录下的适配器驱动和核心层代码 i2c-core.c
其他只是对于框架的一些补充和扩展
i2c读写的一次流程简单来说包括以下步骤:
i2c核心层提供一下功能:
i2c适配器层需要提供 struct i2c_adapter 结构体的 struct i2c_algorithm *algo
字段
该字段是操作i2c总线的算法,源码如下:
struct 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);
u32 (*functionality) (struct i2c_adapter *);
};
其中
I2C_FUNC_10BIT_ADDR
数据收发接口
收发数据源码: kernel-3.2\arch\arm\mach-omap2\board-am335xevm.c
将 board_info 注册到一条 i2c bus 上
一条 bus 对应一个平台设备
一般会调用几次将 cpu 的全部 i2c 总线都生成对应的适配器平台设备
/*外设I2C设备信息
包含一个与驱动层的平台驱动匹配的设备名称
和一个设备对应的I2C地址*/
static struct i2c_board_info i2c0_boardinfo[] = {
{
I2C_BOARD_INFO("tps65217", TPS65217_I2C_ID),
.platform_data = &beaglebone_tps65217_info,
},
{
I2C_BOARD_INFO("24c256", 0x50),
},
{
I2C_BOARD_INFO("pcf8563", 0x51),
},
};
static void __init am335x_evm_i2c_init(void)
omap_register_i2c_bus(1, 100, i2c0_boardinfo,ARRAY_SIZE(i2c0_boardinfo)); /*将一个/多个I2C外设挂在1号总线(I2C0)*/
->进入 int omap_register_i2c_bus(int bus_id, u32 clkrate, struct i2c_board_info const *info, unsigned len)
i2c_register_board_info(bus_id, info, len); /*注册 board_info*/
->进入 int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
list_add_tail(&devinfo->list, &__i2c_board_list); /*将 board_info 加到 __i2c_board_list 队列,在驱动层的平台设备/驱动匹配时有用到*/
omap_i2c_add_bus(bus_id);
omap2_i2c_add_bus(bus_id);
->进入 static inline int omap2_i2c_add_bus(int bus_id)
/*根据 bus_id 找到相关硬件信息 omap_hwmod*/
...
static const char name[] = "omap_i2c";
omap_device_build(name, ...); /*创建平台设备, name 为 omap_i2c*/
一个适配器对应一条总线
源码: kernel-3.2\drivers\i2c\busses\i2c-omap.c
通过omap_i2c_init_driver()函数注册一个平台驱动
omap_i2c_init_driver(void) /*适配器平台驱动初始化*/
platform_driver_register(&omap_i2c_driver); /*注册适配器平台驱动*/
struct platform_driver omap_i2c_driver = {
.probe = omap_i2c_probe,
.driver = {
.name = "omap_i2c",
},
};
适配器层的平台驱动与平台设备名字都是"omap_i2c",匹配后进入probe函数.
omap_i2c_probe(struct platform_device *pdev)
/*获取适配器平台设备资源,如i2c寄存器地址等*/
...
dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL); /*申请驱动结构体 omap_i2c_dev 的内存*/
omap_i2c_init(dev); /*i2c 总线 0/1/2/3... 的初始化,如时钟初始化*/
isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr :map_i2c_isr; /*设置中断回调,中断里面实现 i2c 协议的收发*/
request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev); /*申请中断*/
adap->class = I2C_CLASS_HWMON; /*适配器的 class 设置为 I2C_CLASS_HWMON*/
strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); /*初始化适配器的 name*/
adap->algo = &omap_i2c_algo; /*初始化适配器的算法相关函数, 包括收发函数和功能函数,实现cpu的i2c接口的收发*/
->static const struct i2c_algorithm omap_i2c_algo 定义
static const struct i2c_algorithm omap_i2c_algo = {
.master_xfer = omap_i2c_xfer, /*实现硬件上的收发的函数*/
.functionality = omap_i2c_func, /*返回该适配器支持的 i2c 的功能*/
};
adap->nr = pdev->id; /*从平台资源获取序号,初始化适配器的序号,对应就是 I2C0/1/2/3..*/
r = i2c_add_numbered_adapter(adap); /*添加一个 i2c 适配器,进入 i2c 核心层*/
/*进入 i2c 核心层,参考i2c核心层代码解析*/
...
驱动层的平台设备实际上不是在驱动层生成的,它的注册过程如下:
i2c_register_board_info()
将 i2c board info 注册到链表 __i2c_board_list
中i2c_add_numbered_adapter()
添加一个适配器,进入核心层i2c_scan_static_board_info()
函数中调用 i2c_scan_static_board_info()
轮询链表 __i2c_board_list
生成驱动层的平台设备具体的代码解析参照本文:
一个 i2c board info 包含平台设备名和地址,设备名用于与平台驱动匹配,如下:
static struct i2c_board_info i2c0_boardinfo[] = {
{
I2C_BOARD_INFO("pcf8563", 0x51),
},
};
实际上,驱动层的平台设备有多种注册的方式,上面步骤只是其中一种.
源码: kernel-3.2\drivers\rtc\rtc-pcf8563.c
/*驱动信息
提供驱动操作函数和id_table
根据id_table的name字段与平台设备进行匹配*/
static struct i2c_driver pcf8563_driver = {
.driver = {
.name = "rtc-pcf8563",
},
.probe = pcf8563_probe,
.remove = pcf8563_remove,
.id_table = pcf8563_id,
};
pcf8563_init(void)
i2c_add_driver(&pcf8563_driver); /*往 i2c 总线上添加一个驱动*/
/*进入 i2c 核心层,参考i2c核心层代码解析*/
...
平台设备与驱动匹配之后进去 probe 函数
static int pcf8563_probe(struct i2c_client *client, const struct i2c_device_id *id)
i2c_check_functionality(client->adapter, I2C_FUNC_I2C) /*检测i2c总线是否符合驱动要求功能*/
...
pcf8563 = kzalloc(sizeof(struct pcf8563), GFP_KERNEL);
...
rtc_device_register(pcf8563_driver.driver.name, &client->dev, &pcf8563_rtc_ops, THIS_MODULE); /*注册一个rtc设备*/
probe函数注册了一个rtc设备,并提供了下面这些操作接口
static const struct rtc_class_ops pcf8563_rtc_ops = {
.ioctl = pcf8563_rtc_ioctl,
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
};
当在终端输入下面的命令时,将进入 pcf8563_rtc_read_time 函数读取打印出当前的 rtc 时间
cat /proc/driver/rtc
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
return pcf8563_get_datetime(to_i2c_client(dev), tm);
->进入 static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
/*组 i2c 消息包*/
struct i2c_msg msgs[] = {
{/* setup read ptr */
.addr = client->addr,
.len = 1,
.buf = buf
},
{/* read status + date */
.addr = client->addr,
.flags = I2C_M_RD,
.len = 13,
.buf = buf
},
};
...
i2c_transfer(client->adapter, msgs, 2) /*发送组包好的 i2c 消息包*/
/*进入 i2c 核心层,参考i2c核心层代码解析*/
...
/*将读到的数据赋值到 tm 结构体返回*/
tm->tm_sec = bcd2bin(buf[PCF8563_REG_SC] & 0x7F);
tm->tm_min = bcd2bin(buf[PCF8563_REG_MN] & 0x7F);
tm->tm_hour = bcd2bin(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
tm->tm_mday = bcd2bin(buf[PCF8563_REG_DM] & 0x3F);
tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
tm->tm_mon = bcd2bin(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
tm->tm_year = bcd2bin(buf[PCF8563_REG_YR]);
...
源码: kernel-3.2\drivers\i2c\i2c-core.c
核心层初始化
规定总线的名称/匹配函数/探测函数
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
...
};
i2c核心层初始化
static int __init i2c_init(void) /*核心层初始化*/
retval = bus_register(&i2c_bus_type); /*注册一条总线,名为 i2c,将在 /sys/bus/ 生成 i2c 目录*/
i2c_adapter_compat_class = class_compat_register("i2c-adapter"); /*注册一个 class ,将在 /sys/class/ 生成 i2c-adapter 目录*/
i2c_add_driver(&dummy_driver); /*注册一个 i2c 驱动,名为 dummy,应该是测试用的*/
提供给适配器层调用, 注册一个适配器
驱动层 platform_device 在这里完成注册
int i2c_add_numbered_adapter(struct i2c_adapter *adap) /*添加一个 i2c 适配器,进入 i2c 核心层*/
status = i2c_register_adapter(adap); /*往 i2c 总线注册一个适配器*/
->进入 static int i2c_register_adapter(struct i2c_adapter *adap)
dev_set_name(&adap->dev, "i2c-%d", adap->nr); /*初始化设备名为 i2c-0/1/2/3...*/
adap->dev.bus = &i2c_bus_type; /*初始化设备的总线类型,该设备归属于 i2c 这条总线*/
-> struct bus_type i2c_bus_type 定义
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
...
}
adap->dev.type = &i2c_adapter_type; /*初始化设备的类型,该设备为 i2c 适配器类型*/
res = device_register(&adap->dev);
/*注册一个设备,该设备为总线设备
总线名为 i2c ,设备名为 i2c-0/1/2/3...
将在 /sys/bus/i2c/devices/ 生成 i2c-0/1/2/3...*/
class_compat_create_link(i2c_adapter_compat_class, &adap->dev, adap->dev.parent);
/*在 i2c_adapter_compat_class 中新建 link
将在 /sys/class/i2c-adapter/ 生成 i2c-0/1/2/3...*/
i2c_scan_static_board_info(adap); /*扫描 __i2c_board_list ,根据 board_info 生成所有的驱动层 platform_device*/
->进入 static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
list_for_each_entry(devinfo, &__i2c_board_list, list) /*轮询 __i2c_board_list 队列*/
i2c_new_device(adapter, &devinfo->board_info) /*根据 board_info 生成驱动层 platform_device*/
->进入 struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
client = kzalloc(sizeof *client, GFP_KERNEL); /*新增一个 i2c_client*/
status = i2c_check_client_addr_validity(client); /*检测 i2c 地址有效性*/
status = i2c_check_addr_busy(adap, client->addr); /*检查地址是否冲突*/
client->dev.bus = &i2c_bus_type; /*设备归属 i2c 总线*/
client->dev.type = &i2c_client_type; /*初始化设备的类型,该设备为 i2c 客户端类型,相当于一个节点(适配器可连接多个节点,如i2c下挂多个设备)*/
status = device_register(&client->dev); 注册设备
->进入 int device_register(struct device *dev)
device_add(dev);
->进入 int device_add(struct device *dev)
/*添加device的一些初始化过程*/
error = device_create_file(dev, &uevent_attr); 生成对应 uevent 文件
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
error = device_create_sys_dev_entry(dev);
devtmpfs_create_node(dev);
}
error = device_add_class_symlinks(dev);
error = device_add_attrs(dev);
error = bus_add_device(dev);
error = dpm_sysfs_add(dev);
device_pm_add(dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev); /*探测新设备的驱动程序*/
->进入 void bus_probe_device(struct device *dev)
ret = device_attach(dev);
->进入 int device_attach(struct device *dev)
if (dev->driver) { /*该设备已经匹配了驱动*/
klist_node_attached(&dev->p->knode_driver)
device_bind_driver(dev);
} else { /*该驱动为匹配过驱动*/
bus_for_each_drv(dev->bus, NULL, dev, __device_attach); /*为新加的这个设备轮询所有驱动,对每个驱动调用 __device_attach 与设备进行匹配*/
->进入 __device_attach(struct device_driver *drv, void *data)
driver_match_device(drv, dev)
->进入 driver_match_device(struct device_driver *drv, struct device *dev)
drv->bus->match(dev, drv); /*回调进入驱动配置好的总线回调函数中 i2c_bus_type.match*/
driver_probe_device(drv, dev);
->进入 driver_probe_device(struct device_driver *drv, struct device *dev)
really_probe(dev, drv);
dev->driver = drv; /*在数据结构体上关联驱动到设备的 driver 字段*/
dev->bus->probe(dev); /*回调设备的 probe 函数*/
drv->probe(dev);
->i2c_device_probe(struct device *dev) /*回调进入驱动配置好的总线回调函数中 i2c_bus_type.probe*/
driver->probe(client, i2c_match_id(driver->id_table, client)); /*回调进入平台驱动过的 probe 函数,即 pcf8563_probe 函数*/
}
...
/*遍历 i2c 总线上的全部驱动,为每个驱动调用 __process_new_adapter 函数
动态添加驱动层 platform_device ,这是生成驱动层平台设备的第二种方式*/
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
->进入 __process_new_adapter(struct device_driver *d, void *data)
i2c_do_add_adapter(to_i2c_driver(d), data);
->进入 i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
i2c_detect(adap, driver); /*i2c 检测,动态增加匹配到的设备*/
->进入 i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
int adap_id = i2c_adapter_id(adapter);
address_list = driver->address_list;
if (!driver->detect || !address_list) /*驱动 driver 需要实现 .address_list .detect 才能动态添加驱动层 platform_device*/
temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); /*新增一个 i2c_client,主要是保存 i2c 信息*/
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) { /*轮询 address_list 数组,匹配所有地址*/
temp_client->addr = address_list[i];
err = i2c_detect_address(temp_client, driver);
->进入 i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver)
err = i2c_check_addr_validity(addr); /*检测地址有效性*/
i2c_check_addr_busy(adapter, addr) /*检测地址是否冲突*/
i2c_default_probe(adapter, addr) /*与外接 i2c 设备通信一下看是否能正常通信,确保接了实际设备*/
->进入 i2c_default_probe(struct i2c_adapter *adap, unsigned short addr)
if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50) && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))
/*特殊处理地址为 0x30-0x37 和 0x50-0x5f 而且支持 I2C_FUNC_SMBUS_QUICK 功能的设备*/
i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0, I2C_SMBUS_QUICK, NULL);
else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) /*检测 i2c 适配器是否支持读字节, am335x 的 i2c 适配器支持该功能*/
i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy); /*i2c 读取一个字节*/
->进入 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr...)
flags &= I2C_M_TEN | I2C_CLIENT_PEC;
/*am335x 不支持 smbus 算法, 进入以下函数*/
i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, command, protocol, data); /*使用 i2c 协议模拟 smbus 协议收发*/
->进入 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr...)
/*打包 i2c 消息 msg*/
...
i2c_transfer(adapter, msg, num); /*i2c 发送消息 msg,进行一次读写,从而判断是否有外接 i2c 设备*/
...
memset(&info, 0, sizeof(struct i2c_board_info)); /*初始化新增的 board_info*/
info.addr = addr;
err = driver->detect(temp_client, &info); /*回调到驱动的 .detect 函数*/
client = i2c_new_device(adapter, &info); /*如果回调之后的 info.type[0] 被赋值了,则新建一个 加驱动层 platform_device*/
}
driver->attach_adapter(adap); /*i2c 驱动加载后的回调,内核以后会去掉这个回调, 一般不赋值这个回调接口*/
提供给驱动层调用, 注册一个驱动
i2c_add_driver(driver)
i2c_register_driver(THIS_MODULE, driver)
->进入 i2c_register_driver(struct module *owner, struct i2c_driver *driver)
driver->driver.bus = &i2c_bus_type; /*配置好 platform_driver 与 platform_device 匹配、检测的回调*/
-> struct bus_type i2c_bus_type 定义
struct bus_type i2c_bus_type = {
.match = i2c_device_match,
.probe = i2c_device_probe,
...
}
driver_register(&driver->driver); /*注册平台驱动*/
->进入 driver_register(struct device_driver *drv)
ret = bus_add_driver(drv); /*添加驱动到平台总线上*/
->进入 bus_add_driver(struct device_driver *drv)
error = driver_attach(drv); /*驱动检测*/
->进入 driver_attach(struct device_driver *drv)
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); /*轮询总线上的所有设备,对每个设备调用 __driver_attach 与驱动进行匹配*/
->进入 __driver_attach(struct device *dev, void *data)
driver_match_device(drv, dev) /*匹配设备和驱动*/
->进入 driver_match_device(struct device_driver *drv, struct device *dev)
drv->bus->match(dev, drv); /*回调进入驱动配置好的总线回调函数中 i2c_bus_type.match*/
driver_probe_device(drv, dev); /*驱动的探测*/
->进入 driver_probe_device(struct device_driver *drv, struct device *dev)
really_probe(dev, drv);
dev->driver = drv; /*在数据结构体上关联驱动到设备的 driver 字段*/
dev->bus->probe(dev); /*回调设备的 probe 函数*/
drv->probe(dev);
-> i2c_device_probe(struct device *dev) /*回调进入驱动配置好的总线回调函数中 i2c_bus_type.probe*/
driver->probe(client, i2c_match_id(driver->id_table, client)); /*回调进入平台驱动过的 probe 函数,即 pcf8563_probe 函数*/
/*遍历 i2c 总线上的全部设备,为每设备调用 __process_new_driver 函数
动态添加驱动层 platform_device ,这是生成驱动层平台设备的第二种方式*/
i2c_for_each_dev(driver, __process_new_driver);
->进入 __process_new_driver(struct device *dev, void *data)
if (dev->type != &i2c_adapter_type) /*如果不是 i2c 适配器类型,直接返回,这里就过滤掉了 board info 生成的 i2c 客户端(节点)类型的设备*/
return 0;
i2c_do_add_adapter(data, to_i2c_adapter(dev));
->进入 i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
/*下面的操作跟 i2c_add_numbered_adapter 的一致*/
...
6.2代码中第71行以及6.3代码中第30行中提及到的动态添加驱动层 platform_device ,这是生成驱动层平台设备的第二种方式
, 我修改了实时时钟pcf8563的驱动源码实现了这个功能, 驱动源码修改内容可以下载参考: https://download.csdn.net/download/bruno_mars/11034092
提供给驱动层调用, 通过 i2c 协议发送消息包 i2c_msg
i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (adap->algo->master_xfer) /*只有实现了 i2c 算法 algo 中的 master_xfer 才能正常使用*/
i2c_lock_adapter(adap); /*锁*/
ret = adap->algo->master_xfer(adap, msgs, num); /*回调到适配器层的发送函数 master_xfer*/
/*回调进入适配器层, 适配器实现了 master_xfer 函数读写寄存器将数据包 msgs 发送出去*/
...
i2c_unlock_adapter(adap); /*解锁*/
一个消息包 i2c_msg 由以下部分组成:
i2c 数据包结构体如下:
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
其他i2c核心层函数请参考内核源码