Linux I2C 框架源码分析

本文深入分析Linux I2C框架,包括核心层、适配器层和驱动层。核心层提供适配器和驱动注册接口,适配器层实现I2C收发函数,驱动层根据外设需求实现读写操作。通过i2c_transfer()进行数据传输,平台设备与驱动通过platform_device和platform_driver匹配。
摘要由CSDN通过智能技术生成

1.i2c 框架源码分析

文章基于

内核版本
linux-3.2.0
CPU
TI 的 am3352
源码路径 kernel-3.2\drivers\i2c\busses\i2c-omap.c
i2c从机设备
Philips PCF8563 RTC
源码路径 kernel-3.2\drivers\rtc\rtc-pcf8563.c

2.linux i2c 代码目录结构

源码路径 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
busses
适配器驱动,对应不同的cpu有不同的适配器驱动,通过对 cpu i2c 寄存器控制 i2c 外设进行协议收发
目录下的 i2c-omap.c 就是 ti335x 系列芯片的适配器驱动,而 i2c-gpio.c 就是通过 gpio 实现的模拟 i2c 协议收发的一个适配器驱动
algos
一般 i2c 算法(如开始/结束/应答/发送)由适配器驱动自行实现,这个目录下提供一些特殊的 i2c 收发算法
像 i2c-algo-bit.c 提供了 i2c-gpio.c 所需的一些算法
muxes
i2c 多路复用的适配器驱动
实际使用上可能有些 i2c 需要多路复用的情况,例如同一个总线上接了多个 i2c 地址相同的 i2c 设备,这时候就需要用到 i2c 多路复用芯片
这个目录提供了 i2c 多路复用芯片的适配器驱动,像 gpio-i2cmux.c 就是利用 gpio 进行片选的 i2c 多路复用芯片的适配器驱动
i2c-boardinfo.c
提供注册 boardinfo 的接口代码
i2c-core.c i2c-core.h
i2c 核心层代码
i2c-dev.c
为注册好的 i2c 总线注册生成设备,将在路径 /dev/ 下生成 i2c-1/i2c-2/i2c-3… 等 i2c 总线设备
为应用提供直接控制总线的方法
i2c-mux.c
i2c 多路复用的核心层代码
i2c-smbus.c
smbus 协议(跟 i2c 协议类似)代码

要了解 i2c 框架,重点看 busses 目录下的适配器驱动和核心层代码 i2c-core.c
其他只是对于框架的一些补充和扩展

3.linux i2c 框架

在这里插入图片描述
linux i2c 框架分为三层,分别是:

  • i2c核心层
  • i2c适配器层
  • i2c驱动层

i2c读写的一次流程简单来说包括以下步骤:

  1. i2c驱动层组 i2c 消息包,调用i2c核心层接口,进i2c入核心层
  2. i2c核心层回调i2c适配器层已经注册好的 i2c 发送接口,进入i2c适配器层
  3. i2c适配器层通过读写寄存器完成一次与外接i2c设备的读写

3.1.i2c核心层

i2c核心层提供一下功能:

  • 提供适配器注册接口
  • 提供驱动注册接口
  • 提供数据收发接口
  • 管理适配器,对应结构体是 struct i2c_adapter
  • 管理驱动,对应结构体是 struct i2c_client

3.2.i2c适配器层

  • 根据cpu,提供i2c的收发函数,供i2c核心层调用
  • 填充适配器结构体 struct i2c_adapter,调用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 *);
};

其中

master_xfer 函数
是 i2c协议收发函数.核心层的 i2c_transfer() 接口回调了这个函数
smbus_xfer 函数
是 smbus协议收发函数,smbus协议跟i2c类似,如果芯片要驱动支持smbus协议的外接芯片,则需要实现这个函数.核心层的 i2c_smbus_xfer() 等接口回调了这个函数
functionality 函数
返回该适配器支持的功能,例如如果支持十位地址,则返回 I2C_FUNC_10BIT_ADDR

3.3.i2c驱动层

  • 根据外接i2c设备需求,实现读写操作,提供write、read、ioctl接口供应用程序调用
  • 读写操作中调用i2c核心层数据收发接口收发数据
  • 调用i2c核心层接口注册驱动进i2c总线

4.适配器层

4.1.平台设备 platform_device

源码: 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*/

4.2.平台驱动 platform_driver

一个适配器对应一条总线

源码: 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))
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值