I2C设备驱动挂载

image2021-9-18_10-56-22.png

一、 概述:

image2021-9-18_10-56-22.png

I2C工作原理:

        I2C总线标准的两根传输线,SDA是数据线,Scl是时钟线,当SCL为高,SDA由高到低时,发送启动信息,发送9个脉冲,1-7是地址,8是读写控制位,9是ACK应答位,所以挂在I2C上的被控设备都接受所发送的信息,并把接收到的7位地址与自己的地址进行比较,如果相同ACK就会反馈应答。当SCL为低,SDA由低-à高,则发送停止信号。

Linux的I2C构架分为三个部分:

1)I2C core框架

        提供了核心数据结构的定义和相关接口函数,用来实现I2C适配器

        驱动和设备驱动的注册、注销管理,以及I2C通信方法上层的、与具体适配器无关的代码,为系统中每个I2C总线增加相应的读写方法。

2) I2C总线驱动

        定义描述具体I2C总线适配器的i2c_adapter数据结构、实现在具体I2C适配器上的I2C总线通信方法,并由i2c_algorithm数据结构进行描述。 经过I2C总线驱动的的代码,可以为我们控制I2C产生开始位、停止位、读写周期以及从设备的读写、产生ACK等。

3) I2C 设备驱动

        是对具体I2C硬件驱动的实现。I2C 设备驱动通过I2C适配器与CPU通信。其中主要包含i2c_driver和i2c_client数据结构,i2c_driver结构对应一套具体的驱动方法,例如:probe、remove、suspend等,需要自己申明。i2c_client数据结构由内核根据具体的设备注册信息自动生成,设备驱动根据硬件具体情况填充。

编写具体的I2C驱动时,工程师需要处理的主要工作如下:
 
  1).提供I2C适配器的硬件驱动,探测,初始化I2C适配器(如申请I2C的I/O地址和中断号),驱动CPU控制的I2C适配器从硬件上产生。
  2).提供I2C控制的algorithm, 用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋给i2c_adapter的algo指针。
  3).实现I2C设备驱动中的i2c_driver接口,用具体yyy的yyy_probe(),yyy_remove(),yyy_suspend(),yyy_resume()函数指针和i2c_device_id设备ID表赋给i2c_driver的probe,remove,suspend,resume和id_table指针。
  4).实现I2C设备所对应类型的具体 ,i2c_driver只是实现设备与总线的挂接。
  

二、下面详细描述I2C设备驱动如何挂载到总线。

  下面以GPIO模拟i2c总线的驱动为例,来介绍实现I2C设备驱动如何挂载到总线上

1)  i2c-gpio总线和i2c设备驱动设备树

首先要添加 i2c-gpio总线和i2c设备驱动设备树节点,总线设备树节点无需添加,只需在添加设备驱动时,将对应的的I2C总线打开,添加节点内容在不同平台所相对应的设备树dts文件里,做法如下:

&i2c4 {

    status = "okay";

    <name>[@<unit_address>]@{    //其中name就是设备名,最长可以是31个字符长度。unit_address一般是设备地址,用来唯一标识一个节点

        compatible = "name2";    //需要与name_ids的 .compatible一致,不然注册不成功

        reg = <unit_address>;

        irq_gpio = <&gpio7 GPIO_B2 IRQ_TYPE_LEVEL_LOW>;//中断脚GPIO

        reset_gpio = <&gpio7 GPIO_B1 GPIO_ACTIVE_HIGH>;  //复位脚GPIO



    };

}

2)  i2c-gpio总线和i2c设备驱动注册

i2c总线注册无需自己注册,把i2c设备驱动注册到i2c-gpio总线。做法如下,

I2C设备树
做法如下。首先定义设备ID:
 
static const struct i2c_device_id name_id[] = {
 
                     { "name1", 0 },//设备名和设备是有数据长度
 
                     { }
 
};
设备树名称:
static struct of_device_id name_ids[] = {
    { .compatible = "name2" },//需要与设备树compatible一致,不然注册不成功
    { }
};
 
然后声明i2c_driver结构:
 
static struct i2c_driver name_driver = {
 
               .probe     = name_probe, 
 
               .remove        = name_remove,
 
               .suspend   = name_suspend,
 
               .resume        = name_resume,//上面4个函数根据具体情况取舍
 
               .id_table = name_id,
 
               .driver        = {
 
                     .name    = "name1",  //需要与name_id里的设备名一致
               .of_match_table = of_match_ptr(name_ids),
                },
 
};
最后调用static inline int i2c_add_driver(struct i2c_driver *driver)注册name驱动到I2C总线,如下:
 
static int __init name_init(void)
 
{
 
         return i2c_add_driver(&name_driver);//注册name_driver
 
};
 
module_init(name_init);

3) 实现设备与总线的挂接

i2c_driver实现设备与总线的挂接。用具体yyy的yyy_probe(),yyy_remove(),yyy_suspend(),yyy_resume()函数指针和i2c_device_id设备ID表赋给i2c_driver的probe,remove,suspend,resume和id_table指针。其中.probe指针所对应的yyy_probe()函数指针则判断上电时序、I2C是否读取正确,返回值正确才能够实现I2C驱动设备的使用。

yyy_probe()函数指针判断大致流程如下,以上面缩写的设备树为例:

第一步;从设备数获取指定GPIO属性信息
    irq_gpio = of_get_named_gpio(np, "irq_gpio",0);
    reset_gpio = of_get_named_gpio(np, "irq_gpio", 0);
第二步:申请GPIO管脚
    ret = gpio_request(reset_gpio, "touch_gpio_reset");
    ret += gpio_request(irq_gpio, "touch_gpio_irq");
第三步:判断上电时序
不同设备有不同的上电时序,根绝供应商所提供的资料编写
第四步:读取I2C
s32 name_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
{
    struct i2c_msg msgs[2];
    s32 ret=-1;
    s32 retries = 0;
 
    GTP_DEBUG_FUNC();
 
    msgs[0].flags = !I2C_M_RD;
    msgs[0].addr  = client->addr;
    msgs[0].len   = GTP_ADDR_LENGTH;
    msgs[0].buf   = &buf[0];
#ifdef CONFIG_I2C_ROCKCHIP_COMPAT
    msgs[0].scl_rate=200 * 1000;
    //msgs[0].scl_rate = 300 * 1000;    // for Rockchip, etc.
#endif
    msgs[1].flags = I2C_M_RD;
    msgs[1].addr  = client->addr;
    msgs[1].len   = len - GTP_ADDR_LENGTH;
    msgs[1].buf   = &buf[GTP_ADDR_LENGTH];
#ifdef CONFIG_I2C_ROCKCHIP_COMPAT
    msgs[1].scl_rate=200 * 1000;
    //msgs[1].scl_rate = 300 * 1000;    // for Rockchip, etc.
#endif
 
 
    while(retries < 5)
    {
        ret = i2c_transfer(client->adapter, msgs, 2);
        if(ret == 2)break;
        retries++;
    }
    if((retries >= 5))
    {
     
        printk("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(buf[0] << 8)) | buf[1]), len-2, ret);
    }
    return ret;
}
第五步:读取到正确的I2C,将GPIO管脚释放
        gpio_free(reset_gpio);
        gpio_free(irq_gpio);

4) 实现I2C设备所对应类型的具体

        实现 I2C 设备驱动的文件操作接口,即实现具体设备 yyy 的 yyy_read() 、 yyy_write() 和 yyy_ioctl() 函数等

猜你喜欢

转载自blog.csdn.net/qq_53676406/article/details/132471009