Linux的i2c驱动详解

 

 

1 简介 

I2C 总线仅仅使用 SCL 、 SDA 两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和 PCB 板布线空间的占用。因此, I2C 总线被非常广泛地应用在 EEPROM 、实时钟、小型 LCD 等设备与 CPU 的接口中。

    Linux I2C GPIO驱动是在没有专用I2C芯片的情况下,用GPIO口来模拟I2C总线时序,完成Linux与I2C设备的通信过程。用两根GPIO,分别模拟SDA和SCL。它与使用i2c芯片的驱动有所不同的是传输算法的实现,GPIO模拟i2c驱动中有自己的一套传输算法。GPIO模拟I2C是要占用CPU资源的,而用I2C芯片是不占CPU资源的。使用i2c子系统,而不使用普通字符设备,有以下好处:

1)  使用Linux I2C子系统,不需要去过于详细了解I2C操作。

2)  编写驱动可移植性强。

3)  可以使用内核资源,当面对复杂I2C器件,工作量相对少得多。

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

2 架构

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

1)I2C core框架

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

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

    I2C core框架具体实现在/drivers/i2c目录下的i2c-core.c和i2c-dev.c

 2) I2C总线驱动

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

        I2C总线驱动具体实现在/drivers/i2c目录下busses文件夹。例如:Linux I2C GPIO总线驱动为i2c_gpio.c. I2C总线算法在/drivers/i2c目录下algos文件夹。例如:Linux I2C GPIO总线驱动算法实现在i2c_algo_bit.c.

 3) I2C 设备驱动

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

       I2C 设备驱动具体实现放在在/drivers/i2c目录下chips文件夹。

3 设备注册

       下面以GPIO模拟i2c总线的驱动为例,来介绍设备注册,对于使用i2c芯片的驱动都是大同小异,主要在传输算法上的区别。首先make menuconfig把i2c-gpio选上,让它能编进内核。设备注册包括两种设备的注册,i2c-gpio总线和i2c设备驱动。   

1)  i2c-gpio总线注册

   /drivers/i2c/busses/i2c_gpio.c是i2c-gpio总线驱动源码。在这里可以看到i2c-gpio的注册:

static struct platform_driver i2c_gpio_driver = {

       .driver            = {

              .name      = "i2c-gpio",           //驱动名字

              .owner    = THIS_MODULE,

       },

       .probe            = i2c_gpio_probe,

       .remove          = __devexit_p(i2c_gpio_remove),

};

 

static int __init i2c_gpio_init(void)

{

       int ret;

       ret = platform_driver_register(&i2c_gpio_driver);//注册成平台设备

       if (ret)

              printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);

       return ret;

}

module_init(i2c_gpio_init);

 

        platform是linux虚拟的总线,称为platform总线,相应的设备称为platform_device,相应的驱动称为platform_driver。我们知道i2c总线也对应一个设备,在这里就是对应的i2c_adapte结构,这在后面会有详细介绍。在这里可以看到它将i2c总线驱动注册成平台设备驱动platform_driver_register(&i2c_gpio_driver)。

        把i2c_gpio设备注册为平台设备,需要在mach_xxx的板级文件(devices.c)中添加i2c-gpio需要用到的资源定义,即将i2c总线设备封装成平台设备,下面首先定义总线占用的系统资源:

static struct i2c_gpio_platform_data i2c3_data = {

        .sda_pin = CONFIG_SDA_PIN;

        .scl_pin = CONFIG_SCL_PIN; //设置需要用到的gpio引脚

             .udelay = 0,  //设置I2C工作频率,如果没有默认值为50

             .timeout = 0, //设置I2C工作超时,如果没有默认值为10

};

由于i2c_gpio驱动需要注册到platform总线上面,还需要在mach_xxx的板级文件中添加i2c-gpio的platform_device结构。

static struct platform_device i2c3_device = {

    .name       = "i2c-gpio", //必须和i2c-gpio驱动的名字相同

    .id         = 2,          //总线ID号

    .dev = {

        .platform_data  = &i2c3_data,

    },

};

注册i2c-gpio驱动前要有一个GPIO的设置过程,设置过程如下:

{

//SDA

Pnx_gpio_set_mode(GPIO_F8,GPIO_MODE_MUX1)

Pnx_gpio_set_direction(GPIO_F8,GPIO_DIR_OUTPUT)

//SCL

Pnx_gpio_set_mode(GPIO_F7,GPIO_MODE_MUX1)

Pnx_gpio_set_direction(GPIO_F7,GPIO_DIR_OUTPUT)

 

};

最后把i2c-gpio设备注册进platform总线。

platform_device_register(&i2c3_device);

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

例如:设备驱动源码在/drivers/i2c/chips/lis35de.c,其注册到i2c总线需要的

做法如下。首先定义设备ID:

static const struct i2c_device_id lis35de_id[] = {

                     { "lis35de", 0 },//设备名和设备是有数据长度

                     { }

};

      然后声明i2c_driver结构:

static struct i2c_driver st_lis35de_driver = {

               .probe     = st_lis35de_probe,

               .remove        = st_lis35de_remove,

               .suspend   = st_lis35de_suspend,

               .resume        = st_lis35de_resume,//上面4个函数根据具体情况取舍

               .id_table = lis35de_id,

               .driver        = {

                     .name    = "lis35de",  //驱动名字

                },

};

    最后调用static inline int i2c_add_driver(struct i2c_driver *driver)注册lis35de驱动到I2C总线,如下:

static int __init st_lis35de_init(void)

{

         return i2c_add_driver(&st_lis35de_driver);//注册st_lis35de_driver

};

module_init(st_lis35de_init);

但是到目前还不知道注册到那根I2C总线,现在把lis35de设备驱动添加到我们想要的i2c-gpio总线上。使用内核提供的函数i2c_register_board_info,在mach_xxx的板级文件中把设备信息注册到需要注册的I2C总线上面。

int __init i2c_register_board_info(int busnum,//设备需要注册到的总线ID

         struct i2c_board_info const *info,//设备信息包括设备名,地址等

unsigned len)

例如:把lis35de驱动注册到i2c-gpio总线,总线ID为2。

static struct i2c_board_info i2c_devices_lis35de[] = {

    {

        I2C_BOARD_INFO("lis35de", 0x1C), //设备名和地址

    },

};

i2c_register_board_info(2,i2c_devices_lis35de,ARRAY_SIZE(i2c_devices_lis35de));

        arch/arm/mach-pnx67xx/board_pnx67xx_wavex.c中unsigned int pnx_modem_gpio_reserved[]下注释掉GPIO_F7,GPIO_F8,防止内核认为F8,F7已经使用过了,至此已经把i2c-gpio总线注册到系统,把设备驱动注册到i2c-gpio总线。

       前面说了那么多,是不是有点乱了,这里我们在来理一下:

(一)i2c总线驱动

1)在那个devices.c文件中,声明平台设备占用的系统资源,然后定义一个平台设备,并注册这个平台设备到平台总线上

2)在i2c-gpio.c文件中,声明该驱动支持的设备列表,然后定义一个平台驱动结构,并注册这个平台驱动到平台总线上

(二)i2c设备驱动

1)同样在devices.c文件下,在对应总线的设备列表中声明一个i2c设备结构,然后通过i2c_register_board_info()函数,将这个设备列表注册到i2c总线上

2)在lis35de.c文件中,声明支持的i2c设备列表和一个i2c设备驱动结构体i2c_driver,然后将其注册到i2c总线上

    注意:这里不管是设备还是驱动先注册到总线上,他们都会自动请求匹配总线上的所有驱动或设备。

4  I2C关键数据结构和详细注册流程

上面的描述都是i2c系统的框架,具体的数据结构注册流程下面会详细介绍。

4.1  关键数据结构

    在i2c.h头文件中定义了i2c_adapter、i2c_algorithm、i2c_driver和i2c_client 4个比较关键的数据结构。

1)i2c_algorithm对应一套通信方法。

      用来实现具体的收发算法,此数据结构非常重要,通过其中的收发函数会调用具体的硬件收发操作,对于i2c-gpio总线的通信方法实现在/drivers/i2c目录下algos文件夹i2c_algo_bit.c。

struct i2c_algorithm {

    int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);

     //i2c传输函数指针

    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,

unsigned short flags, char read_write,

u8 command, int size, union i2c_smbus_data * data);

    //smbus传输函数指针

u32 (*functionality) (struct i2c_adapter *);

 //返回适配器支持功能

};

 

2)i2c_adapter

      用来定义总线上的每一个adapter(适配器),每一个adapter都需要i2c_algorithm中提供的通信函数来控制适配器的访问周期,因此在i2c_adapter中包含i2c_algorithm指针。i2c_algorithm的关键函数master_xfer用于产生I2C访问信号,以i2c_msg为单位。

struct i2c_adapter {

struct module *owner;  //所属模块

unsigned int id;      //algor

  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值