Linux2.6 I2C子系统分析

linuxiic是以子系统的方式存在,我们查看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存放一些板级包的信息,对于我们并不是很重要。

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

其中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_algorithmmaster_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_driveriic_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_driveri2c_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_driverprobe remove shutdown一样了,其中platform_bus_type这个总线结构中有个match函数指针

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

接下来执行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.ci2c_register_driveri2c_register_driver再调用i2c-dev.ci2cdev_attach_adapter

 

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

1I2c-dev.c是应用程序系统调用的接口,例如open对于的就是i2cdev_openread对应的就是i2cdev_read,(2)那么通过i2cdev_xxx在调用i2c-core.crtc核心代码,比如i2c_master_recvi2c_master_recv再调用i2c_transfer,(3i2c_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中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值