Linux--内核---I2C总线驱动分析 以linux3.10.0 RK3288为例

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
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值