ubantu IIC驱动

1.   I2C特点介绍

IIC总线只需要两根总线(串行数据线SDA,串行时钟线SCK)就可以完成读写操作,而且通信协议简单,一条总线上可以挂载多个设备,因此被广泛使用。但是IIC总线有一个缺点,就是传输速率比较低。SDA负责数据传输,SCL负责时钟同步。

ubantu IIC驱动_第1张图片

 

1.1     I2C设备驱动的概述

这里简单地将I2C设备驱动的层次分为设备层、总线层。重点是理 解 4 个数据结构(i2c_driveri2c_clienti2c_algorithmi2c_adapter)i2c_driver2c_client 属于设备层;i2c_algorithmi2c_adapter 属于总线层。值得注意的是一个系统中可能有多个总线层, 也就包含多个总线控制器;也可能有多个设备层,包含不同的I2C设备。

ubantu IIC驱动_第2张图片

 

2.总线架构介绍

ubantu IIC驱动_第3张图片

ubantu IIC驱动_第4张图片

 

i2c设备驱动层组件(i2c-dev.c)给用户提供调用接口,众所周知,用户实现策略,驱动实现的是机制。
而i2c核心层起到承上启下的作用,如下

ubantu IIC驱动_第5张图片

2.1    核心层(i2c-core.c)

struct bus_type:

struct bus_type i2c_bus_type = {
    .name        = "i2c",
    .dev_attrs    = i2c_dev_attrs,
    .match        = i2c_device_match,
    .uevent        = i2c_device_uevent,
    .probe        = i2c_device_probe,
    .remove        = i2c_device_remove,
    .shutdown    = i2c_device_shutdown,
    .suspend    = i2c_device_suspend,
    .resume        = i2c_device_resume,
};

核心层,注册了i2c总线,以及驱动注册

ubantu IIC驱动_第6张图片

 

 

2.2    设备驱动层(i2c-dev.c)

struct i2c_driver:

struct  i2c_driver

{

  int id;                         //驱动标识ID

  unsigned int class;               //驱动的类型

  int (*attach_adapter)(struct i2c_adapter *);         //当检测到适配器时调用的函数

    int (*detach_adapter)(struct i2c_adapter*);       //卸载适配器时调用的函数

  int (*detach_client)(struct i2c_client *)   __deprecated;    //卸载设备时调用的函数


//以下是一种新类型驱动需要的函数,这些函数支持IIC设备动态插入和拔出。如果不想支持只实现上面3个。要不实现上面3个。要么实现下面5个。不能同时定义

   int  (*probe)(struct i2c_client *,const struct  i2c_device_id *); //新类型设备探测函数

   int (*remove)(struct i2c_client *);                   //新类型设备的移除函数

   void (*shutdown)(struct i2c_client *);              //关闭IIC设备

   int (*suspend)(struct  i2c_client *,pm_messge_t mesg);  //挂起IIC设备

   int (*resume)(struct  i2c_client *);                  //恢复IIC设备

   int (*command)(struct i2c_client *client,unsigned int cmd,void *arg);        //使用命令使设备完成特殊的功能。类似ioctl()函数

   struct devcie_driver  driver;                         //设备驱动结构体

   const struct  i2c_device_id *id_table;                       //设备ID表

   int (*detect)(struct i2c_client *,int  kind,struct  i2c_board_info *);          //自动探测设备的回调函数

   const  struct i2c_client_address_data     *address_data;   //设备所在的地址范围

   struct  list_head    clients;                    //指向驱动支持的设备

};

 

struct i2c_client:

ubantu IIC驱动_第7张图片

结构体i2c_driver和i2c_client的关系较为简单,其中i2c_driver表示一个IIC设备驱动,i2c_client表示一个IIC设备,关系如下

ubantu IIC驱动_第8张图片

ubantu IIC驱动_第9张图片

IIC总线适配器就是一个IIC总线控制器,在物理上连接若干个IIC设备。IIC总线适配器本质上是一个物理设备,其主要功能是完成IIC总线控制器相关的数据通信:

struct i2c_adapter:

struct i2c_adapter

{

        struct module *owner;                        //模块计数

        unsigned  int id;                 //alogorithm的类型,定义于i2c_id.h中

        unsigned   int  class;                           //允许探测的驱动类型

        const struct i2c_algorithm *algo;         //指向适配器的驱动程序

        void *algo_data;                //指向适配器的私有数据,根据不同的情况使用方法不同

        int (*client_register)(struct  i2c_client *);          //设备client注册时调用

        int (*client_unregister(struct  i2c_client *);       //设备client注销时调用

        u8 level;                                                         

        struct  mutex  bus_lock;                  //对总线进行操作时,将获得总线锁

        struct  mutex  clist_lock ;                            //链表操作的互斥锁

        int timeout;                                              //超时

  int retries;                                           //重试次数

       struct device dev;                              //指向 适配器的设备结构体

       int  nr ;                                                          

       struct  list_head      clients;                   //连接总线上的设备的链表

        char name[48];                                  //适配器名称

        struct completion     dev_released;               //用于同步的完成量

};

这里重点是  const struct i2c_algorithm *algo(见下方);         //指向适配器的驱动程序

 

IIC总线适配器就是一个IIC总线控制器,在物理上连接若干个IIC设备。IIC总线适配器本质上是一个物理设备,其主要功能是完成IIC总线控制器相关的数据通信:

struct i2c_algorithm:

每一个适配器对应一个驱动程序,该驱动程序描述了适配器与设备之间的通信方法:

struct  i2c_algorithm

{
   //传输函数指针,指向实现IIC总线通信协议的函数,用来确定适配器支持那些传输类型

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

   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方式传输函数指针,指向实现SMBus总线通信协议的函数。
//SMBus和IIC之间可以通过软件方式兼容,所以这里提供了一个函数,但是一般都赋值为NULL

   u32  (*functionality)(struct  i2c_adapter *);          //返回适配器支持的功能

};

 

struct i2c_msg

ubantu IIC驱动_第10张图片

 

struct  i2c_board_info

ubantu IIC驱动_第11张图片

 

2.3   I2C总线驱动层(i2c-s3c2410.c

先进入init入口函数,如下图所示:

ubantu IIC驱动_第12张图片

在init函数中,注册了一个 “s3c2440-i2c”的platform_driver平台驱动,我们来看看probe函数做了些什么

s3c24xx_i2c_probe函数

struct i2c_adapter  adap;

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
    struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
       ... ...

       /*获取,使能I2C时钟*/
       i2c->clk = clk_get(&pdev->dev, "i2c");               //获取i2c时钟
       clk_enable(i2c->clk);                                         //使能i2c时钟

       ... ....
       /*获取资源*/
       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
       i2c->regs = ioremap(res->start, (res->end-res->start)+1);

       ... ....

       /*设置i2c_adapter适配器结构体, 将i2c结构体设为adap的私有数据成员*/
    i2c->adap.algo_data = i2c;          //i2c_adapter适配器指向s3c24xx_i2c;
       i2c->adap.dev.parent = &pdev->dev;

 
    /* initialise the i2c controller */
       /*初始化2440的I2C相关的寄存器*/
       ret = s3c24xx_i2c_init(i2c);
       if (ret != 0)
              goto err_iomap;

       ... ...
       /*注册中断服务函数*/
       ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);
       ... ...

       /*注册i2c_adapter适配器结构体*/
       ret = i2c_add_adapter(&i2c->adap);
       ... ...
}

其中i2c_adapter结构体是放在s3c24xx_i2c->adap下,如下图所示:(具体此结构体在上面已经介绍了,这里是直接调用)

ubantu IIC驱动_第13张图片

接下来我们进入i2c_add_adapter()函数看看,到底如何注册的

int i2c_add_adapter(struct i2c_adapter *adapter)
{
       int   id, res = 0;

retry:
       if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) //调用idr_pre_get()为i2c_adapter预留内存空间
              return -ENOMEM;

       mutex_lock(&core_lists);

       /* "above" here means "above or equal to", sigh */
       res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);
       //调用idr_get_new_above()将结构插入i2c_adapter_idr中,并将插入的位置赋给id,以后可以通过id在i2c_adapter_idr中找到相应的i2c_adapter结构体

       mutex_unlock(&core_lists);

       if (res < 0) {
              if (res == -EAGAIN)
                    goto retry;
              return res;
       }
       adapter->nr = id;
       return i2c_register_adapter(adapter);  //调用i2c_register_adapter()函数进一步来注册.
}

其中调用i2c_register_adapter(),函数代码如下所示:

static int i2c_register_adapter(struct i2c_adapter *adap)
{
       struct list_head  *item;               //链表头,用来存放i2c_driver结构体的表头
       struct i2c_driver *driver;                     //i2c_driver,用来描述一个IIC设备驱动
        list_add_tail(&adap->list, &adapters);       //添加到内核的adapter链表中
        ... ...
       list_for_each(item,&drivers) {        //for循环,从drivers链表里找到i2c_driver结构体的表头
              driver = list_entry(item, struct i2c_driver, list); //通过list_head表头,找到i2c_driver结构体
              if (driver->attach_adapter)  
                     /* We ignore the return code; if it fails, too bad */
                     driver->attach_adapter(adap);                    //调用i2c_driver的attach_adapter函数来看看,这个新注册的设配器是否支持i2c_driver }}

在i2c_register_adapter()函数里主要执行以下几步:

将adapter放入i2c_bus_type的adapter链表
将所有的i2c设备调出来,执行i2c_driver设备的attach_adapter函数来匹配

 

i2c_adapter表示物理上的一个i2C设备(适配器), 在i2c-s3c2410.c中,是存放在s3c24xx_i2c结构体下的(struct  i2c_adapter  adap)成员中

其中s3c24xx_i2c的结构体成员如下所示

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {            
       .master_xfer          = s3c24xx_i2c_xfer,  //主机传输
       .functionality          = s3c24xx_i2c_func,                    
};

static struct s3c24xx_i2c s3c24xx_i2c = {
       .lock              = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
       .wait              = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
       .tx_setup = 50,                        //用来延时,等待SCL被释放
       .adap             = {                                             // i2c_adapter适配器结构体
              .name                   = "s3c2410-i2c",
              .owner                  = THIS_MODULE,
              .algo                     = &s3c24xx_i2c_algorithm,           //存放i2c_algorithm算法结构体
              .retries           = 2,                                       //重试次数
              .class                    = I2C_CLASS_HWMON,
       },
};

显然这里是直接设置了i2c_adapter结构体,所以在s3c24xx_i2c_probe ()函数中没有分配i2c_adapter适配器结构体,

其中, i2c_adapter结构体的名称等于"s3c2410-i2c",它的通信方式等于s3c24xx_i2c_algorithm,重试次数等于2

PS:如果缺少i2c_algorithm的i2c_adapter什么也做不了,就只是个I2C设备,而没有通信方式

s3c24xx_i2c_algorithm中的关键函数master_xfer()就是用于产生i2c访问周期需要的start stop ack等信号

比如,在s3c24xx_i2c_algorithm(见上方)中的关键函数master_xfer()里,调用了:

s3c24xx_i2c_xfer -> s3c24xx_i2c_doxfer()->s3c24xx_i2c_message_start()

来启动传输message信息, 其中s3c24xx_i2c_message_start()函数代码如下:

 

static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg)
{

 unsigned int addr = (msg->addr & 0x7f) << 1;              //IIC从设备地址的最低位为读写标志位
       ... ...

       stat = 0;
       stat |=  S3C2410_IICSTAT_TXRXEN;     //设置标志位启动IIC收发使能

       if (msg->flags & I2C_M_RD) {                     //判断是读,还是写
              stat |= S3C2410_IICSTAT_MASTER_RX;       
              addr |= 1;                                          //设置从IIC设备地址为读标志
       } else
              stat |= S3C2410_IICSTAT_MASTER_TX;

       s3c24xx_i2c_enable_ack(i2c);                //使能ACK信号

    iiccon = readl(i2c->regs + S3C2410_IICCON);    //读出IICCON寄存器

       writel(stat, i2c->regs + S3C2410_IICSTAT);   //写入IICSTAT寄存器,使能IIC的读或写标志

       dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);

       writeb(addr, i2c->regs + S3C2410_IICDS);  //将IIC从设备地址写入IICDS寄存器

       /* delay here to ensure the data byte has gotten onto the bus
        * before the transaction is started */

       ndelay(i2c->tx_setup);         //延时,等待SCL被释放,下面便可以发送起始信号+IIC设备地址值


       dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
       writel(iiccon, i2c->regs + S3C2410_IICCON);            

       stat |=  S3C2410_IICSTAT_START;              
       writel(stat, i2c->regs + S3C2410_IICSTAT); 
            //设置IICSTAT寄存器的bit5=1,开始发送起始信号+IIC从设备地址值,并回应ACK
}

 

你可能感兴趣的:(ubantu IIC驱动)