Linux 3.10.0 iic总线注册过程
I2C总线驱动包括I2C适配器驱动加载与卸载以及I2C总线通信方法
I2C核心提供了i2c_adapter的增加和删除函数、i2c_driver的增加和删除函数、i2c_client的依附和脱离函数
以及i2c传输、发送和接收函数。
1、I2C核心 i2c_core.c
I2C核心提供了i2c_adapter的增加和删除函数、i2c_driver的增加和删除函数、i2c_client的依附和脱离函数
以及i2c传输、发送和接收函数。
2、I2C总线驱动 i2c_rockchip.c
I2C总线驱动包括I2C适配器驱动加载与卸载以及I2C总线通信方法。其中I2C适配器驱动加载(与卸载)要完成初
始化(释放)I2C适配器所使用的硬件资源,申请I/0地址、中断号、通过i2c_add_ adapter()添加i2c_adapter
的数据结构(通过i2c_del_adapter()删除i2c_adapter的数据结构)的工作。12C总线通信方法主要对特定的I2C
适配器实现i2c_algorithm的master_xfer()方法来实现i2c_ msg的传输。不同的适配器对应的master_xfer()
方法由其处理器的硬件特性决定。
3、I2C设备驱动 i2c_mydriver.c
I2C设备驱动主要用于I2C设备驱动模块加载与卸载以及提供I2C设备驱动文件操作接口。I2C设备驱动的模块加
载通用的方法遵循以下流程:首先通过register_chrdev()将I2C设备注册为一个字符设备,然后利用I2C核心
中的i2c_add_adapter()添加i2c_driver。调用i2c_add_adapter()过程中会引发i2c_driver结构体中的
YYY_attach_adapter()的执行,它通过调用I2C核心的i2c_probe()实现物理设备的探测。i2c_probe()会引发
yyy_detect()的调用。yyy_detect()中会初始化i2c_ client,然后调用内核的i2c_attach_client()通知I2C
核心此时系统中包含了一个新的I2C设备。之后会引发I2C设备驱动中yyy_init_client()来初始化设备。卸载
过程执行相反的操作。
关心的主要文件
kernel\drivers\i2c\busses\i2c-rockchip.c总线驱动程序,不同的芯片有不同的总线驱动程序
kernel\drivers\i2c\i2c-core.c 内核i2c核心程序
关心的几个主要结构体
static struct platform_driver rockchip_i2c_driver = { //总线驱动是一个platform_driver类型
.probe = rockchip_i2c_probe, //在probe里完成adapter,client注册(硬件资源的绑定中断号,引脚,时钟,控制器寄存器等)
.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),
},
};
static int __init rockchip_i2c_init_driver(void)
{
return platform_driver_register(&rockchip_i2c_driver);
}
struct rockchip_i2c {
spinlock_t lock;
wait_queue_head_t wait;
unsigned int suspended:1;
struct i2c_msg *msg; //发送的从机地址和数据,时钟频率
unsigned int is_busy;
int error;
unsigned int msg_ptr;
unsigned int irq;//中断号
enum rockchip_i2c_state state; //i2c状态 开始 结束 读/写
unsigned int complete_what;
unsigned long clkrate;
void __iomem *regs; //存放对应控制器的寄存器
struct clk *clk;
struct device *dev;
struct resource *ioarea;
struct i2c_adapter adap; //i2c适配器 对应一个硬件i2c控制器
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;
};
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 */ //其中master_xfer()接口是硬件相关的,就是收发函数,操作控制器寄存器
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 */ dev->p->driver_data = rockchip_i2c;私有值指向当前i2c结构体(包含控制器寄存器io脚等
特定资源),of_node指向当前注册的platform_device->dev.of_node;也包含特定的设备树资源//会被注册进dev设备挂接到内核中的链表中来管理。
int nr; //id号
char name[48]; //在i2c总线驱动里命名
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients; //将一个i2c_adapter对象和它所属的i2c_client对象以及相应的i2c_driver对象连接到一起
struct i2c_bus_recovery_info *bus_recovery_info;
};
struct i2c_client {
unsigned short flags; /* div., see below */ 各种模式标识 地址类型,是否有wakeuo-source等
unsigned short addr; /* chip address - NOTE: 7bit */子节点里定义的reg属性
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE]; //request_module("%s%s", I2C_MODULE_PREFIX, info.type);#define I2C_MODULE_PREFIX "i2c:"
struct i2c_adapter *adapter; /* the adapter we sit on */总线注册的时候指向对应adapter
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */表示这是一个device,dev.of_node指向子节点的device_node dev.bus=&i2c_bus_type
int irq; /* irq issued by device */使用的中断号
struct list_head detected; //将所有i2c_client连在一起的节点
};
///注册过程,目的:完成adapter,i2c_client的注册//
两个问题,也是主要的两个设备
1,adapter是什么,里面有些什么?
答:在i2c种一个adapter对应一个硬件上的控制器,里面包含i2c的收发函数,控制器的寄存器地址,io口等资源;它被i2c总线驱动创建, 并和i2c_client相连(如果设备树里有子节点)
2,i2c_client是什么,里面有些什么?
答:i2c_client对应一个挂载在i2c总线上的i2c设备,里面包含设备的地址,adapter的指针(注册时会设置),设备驱动driver的指针(设备驱动注册时绑定)。
描述一个挂接在硬件i2c总线上的设备的设备信息,即i2c设备的设备对象
linux内核里 i2c_adapter,i2c_client,i2c_driver这几个结构体真的是我中有你你中有我啊,绕来绕去
I2C总线驱动是一个platform_driver类型的驱动所以使用platform_driver_register来注册
platform_driver_register(&rockchip_i2c_driver);
关于platform_driver_register的过程在上篇文章中分析,这里只要知道调用这个函数注册后就会进入i2c总线驱动的probe,完成申请资源,初始化,提供接口等工作,接下来分析总线的rockchip_i2c_probe()
rockchip_i2c_probe(struct platform_device *pdev)//获取platform_device的资源
i2c_add_adapter(&i2c->adap);配置adapter资源:将资源填入adapter的结构体中包含1、收发函数指针;2、设备树资源:名字,寄存器地址,中断,id,io口
__i2c_add_numbered_adapter(adapter);
i2c_register_adapter(adap);配置adapter资源在sysfs里生成
device_register(&adap->dev);注册adapter设备 dev里已经包含了rockchip_i2c; 设备树对应的of_node等特定资源,已经对应上了一个i2c控制器
of_i2c_register_devices(&i2c->adap);//建立i2c_client 也就是挂在i2c总线上的从设备
for_each_available_child_of_node(adap->dev.of_node, node)遍历设备树里的每个子节点
i2c_new_device(adap, &info);如果子节点存在则调用
device_register(&client->dev);//注册i2c_client设备 这个dev会找到这个i2c_client,i2c_client里包含了adapter
之后自己写的i2c设备驱动就会匹配上这个client,在驱动的probe里返回这个client的指针,这时就可以进行数据的收发了。
//adapter收发函数的分析
adapter的收发函数共用一个rockchip_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
由i2c_msg指定是发还是收,设备地址和数据
rockchip_i2c_xfer
rockchip_i2c_doxfer
i2c_writel
writel(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));//对应寄存器写入数据就行了
struct i2c_msg {
__u16 addr; //设备地址
__u16 flags; //读写标识
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */数据长度
__u8 *buf; /* pointer to msg data */ 要发送或者接收的数据指针
#ifdef CONFIG_I2C_ROCKCHIP_COMPAT
__u32 scl_rate; /* add by kfx */
#endif
};