您的位置:首页 > 运维架构 > Linux

Linux2.6 I2C子系统分析

2012-08-21 15:47 519 查看
在linux中iic是以子系统的方式存在,我们查看2.6.32.2的内核源代码结构:



在/drivers/iic目录下有algos
busses chips文件夹,有i2c-core.c i2c-dev.c i2c-boardinfo.c
源文件,那么这些分别代表什么呢?

Algos目录下主要存放I2C总线适配器的algorithm。也就是I2C通信的一些算法,比如多少频率等等。Busses目录具体控制器的I2C总线驱动,其中就有s3c24xx的驱动。Chips目录主要存放特定设备的IIC驱动。比如EEPROM等。

那么i2c-core.c是整个I2C驱动的核心代码。是连接应用程序和具体驱动程序的枢纽。

I2C-dev.c存放着应用程序的接口,和rtc-dev.c的功能是类似的。i2c-boardinfo.c存放一些板级包的信息,对于我们并不是很重要。

结合上文所说,Linux的IIC体系可以用以下图解释。



其中Driver对应的是i2c_driver结构

struct i2c_driver {

 unsigned int class;

 /* Notifies the driver that a new bus has appeared or is about to be

  * removed. You should avoid using this if you can, it will probably

  * be removed in a near future.

  */

  /*当调用i2c_add_driver向内核添加i2c驱动设备时被调用*/

 int (*attach_adapter)(struct i2c_adapter *);

 /*当调用i2c_del_driver向内核删除i2c驱动设备时被调用*/

 int (*detach_adapter)(struct i2c_adapter *);

 

 /* Standard driver model interfaces */

 int (*probe)(struct i2c_client *, const struct i2c_device_id *);

 int (*remove)(struct i2c_client *);

 /* driver model interfaces that don't relate to enumeration  */

 void (*shutdown)(struct i2c_client *);

 int (*suspend)(struct i2c_client *, pm_message_t mesg);

 int (*resume)(struct i2c_client *);

 /* a ioctl like command that can be used to perform specific functions

  * with the device.

  */

 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

 /*设备驱动结构*/

 struct device_driver driver;

 /*如果一个驱动支持多个设备,idtable包含设备的ID*/

 const struct i2c_device_id *id_table;

 /* Device detection callback for automatic device creation */

 int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);

 const struct i2c_client_address_data *address_data;

 struct list_head clients;

};

i2c_client对应一个具体的设备:当我们使用open write系统调用就是通过这个结构传递信息:

struct i2c_client {

 unsigned short flags;  /* div., see below  */

 /*芯片低七位地址*/

 unsigned short addr;  /* chip address - NOTE: 7bit */

     /* addresses are stored in the */

     /* _LOWER_ 7 bits  */

 char name[I2C_NAME_SIZE];

 struct i2c_adapter *adapter; /* the adapter we sit on */

 struct i2c_driver *driver; /* and our access routines */

 struct device dev;  /* the device structure  */

 int irq;   /* irq issued by device  */

 struct list_head detected;

};

i2c_algorithm对应I2C的传输算法控制:

struct i2c_algorithm {

 /* If an adapter algorithm can't do I2C-level access, set master_xfer

    to NULL. If an adapter algorithm can do SMBus access, set

    smbus_xfer. If set to NULL, the SMBus protocol is simulated

    using common I2C messages */

 /* master_xfer should return the number of messages successfully

    processed, or a negative value on error */

    /*I2C传输函数指针*/

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

      int num);

 /*SMBus传输之后走*/

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

      unsigned short flags, char read_write,

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

 /* To determine what the adapter supports */

 /*返回适配器支持的功能*/

 u32 (*functionality) (struct i2c_adapter *);

};

i2c_algorithm

分析以上结构,我们知道

I2c_adapter对应一套iic适配器,什么是适配器呢,就是iic控制器,在2440中就是iic控制单元,在该结构中有i2c_algorithm结构,一个iic控制器需要这个结构提供iic总线的访问周期等,其中在i2c_algorithm的master_xfer函数指针中,有i2c_msg结构

struct i2c_msg {

 __u16 addr; /* slave address   */

 __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_NOSTART  0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */

#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   */

};

我们就是以这个结构来传输数据的。

Iic_driver是对应一套驱动方法,iic_client对应的是一个具体的物理设备,里面有物理地址,访问标志等,在iic_client结构中,有Iic_driver和iic_adapter结构,即一个具体的设备应该有相应的iic适配器(iic控制器)和对应的驱动。Iic_client依附于iic_adapter。

下面介绍一个具体的i2c驱动在内核是如何注册的:拿s3c2440为例子

如果内核支持了iic驱动,内核会首先注册一个iic-core.c中的i2c_init函数,在i2c_init中调用i2c-core.c
的i2c_register_driver.

static int __init i2c_init(void)

{

 static int res;

 static int first = 1;

 if (!first)

  return res;

 first = 0;

 /* Setup and enable the DATA and CLK pins */

 res = crisv32_io_get_name(&cris_i2c_data,

  CONFIG_ETRAX_V32_I2C_DATA_PORT);

 if (res < 0)

  return res;

 res = crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_V32_I2C_CLK_PORT);

 crisv32_io_set_dir(&cris_i2c_clk, crisv32_io_dir_out);

 return res;

}

static struct i2c_driver dummy_driver = {

 .driver.name = "dummy",

 .probe  = dummy_probe,

 .remove  = dummy_remove,

 .id_table = dummy_id,

};

在执行i2c_add_driver向内核添加i2c驱动时,会调用i2c_register_driver,i2c_register_driver就调用dummy_driver中的dummy_probe函数。

那么为什么要先执行一个i2c_init呢。内核中的解释是:



大概意思就是因为一些子系统在subsys_initcall中注册i2c
driver(例如i2c-s3c2410),而这些子系统是跟这个有关系的

 



 
然后会注册具体平台的iic驱动,比如i2c-s3c2410.c

 

static int __init i2c_adap_s3c_init(void)

{

 return platform_driver_register(&s3c24xx_i2c_driver);

}

其中subsys_initcall在内存中的位置比module_init的位置更优先执行,所以先执行i2c_adap_s3c_init

在之前rtc已经降到,在mach_mini2440中,在mini2440_machine_init()函数中会添加各种platform
device,其中就有iic设备:

在执行platform_driver_register时,

 

通过这个代码我们知道,platform_driver中的device_driver结构的probe以及remove
shutdown这些函数指针就和platform_driver的probe
remove shutdown一样了,其中platform_bus_type这个总线结构中有个match函数指针

这样我们可以知道,如果platform_driver结构中存在id_table域先测试platform_driver这个结构中的id_table和platform_device的name域是否一致,然后测试platform_driver和platform_device结构是否一致。如果一致就会执行platform_driver的probe函数。

接下来执行s3c24xx_i2c_probe函数

 

在该函数中主要有以下工作

启动IIC时钟

1
相应寄存器映射

2
申请IIC中断

3
调用s3c24xx_i2c_init初始化IIC控制器

4调用i2c_add_numbered_adapter添加i2c_adapter结构

下面主要讲解第四点:

调用i2c_add_numbered_adapter

 

函数最终调用i2c_register_adapter注册一个adapter结构

 
 

在i2c_register_adapter函数中

 

该函数的意思是遍历整个IIC总线,当匹配值执行i2c_do_add_adapter函数,

 

这样就会执行i2c_driver结构中的attacg_adapter函数。但是这时候的driver->attach_adapter是空的,所以不会执行。

下面就执行i2c-dev.c中的i2c_dev_init函数,

 

该函数主要做了以下事情:

1
注册cdev结构,主设备号为89

2
向i2c子系统添加i2c_driver

 

对于第二点,这里就会调用i2c-core.c的i2c_register_driver,i2c_register_driver再调用i2c-dev.c的i2cdev_attach_adapter。

 

从上分析:I2C驱动时很复杂的,那么我们如果要重新写一个I2C驱动,具体要怎么做呢?

(1)I2c-dev.c是应用程序系统调用的接口,例如open对于的就是i2cdev_open,read对应的就是i2cdev_read,(2)那么通过i2cdev_xxx在调用i2c-core.c的rtc核心代码,比如i2c_master_recv,i2c_master_recv再调用i2c_transfer,(3)i2c_transfer再通过i2c_adapter结构调用具体平台的函数。

(1)(2)内核已经写好了,我门要做的就是(3),要定义一个i2c_algorithm通信方法,并将通信函数写好。我们可以参照i2c-s3c2410,定义一个s3c24xx_i2c结构。

其中结构中包括传递信息的msg,适配器结构
i2c_adapter, i2c_algorithm就放在i2c_adapter中

 

其中结构中包括传递信息的msg,适配器结构
i2c_adapter, i2c_algorithm就放在i2c_adapter中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息