Linux设备驱动子系统第一弹 - I2C

分类: Linux驱动   1729人阅读  评论(5)  收藏  举报

Linux设备驱动子系统第一弹 - I2CLinux设备驱动子系统第一弹 - I2C

1. Overview

2. Data Structure

3. Adapter

4. I2C-core

5. Slave Device

 

1. Overview

  1.1 Definition

  •  I2C           Inter-Integrated Circuit
  •  SMBUS      System Management Bus, the I2C subset

  1.2 Characteristics

  •  The amount of data exchanged is small.
  •  The required data transfer rate is low.

  1.3 Speed

  •  Fast speed     400 kbps
  •  Full speed      100 kbps

  1.4 Topology

 

 Linux设备驱动子系统第一弹 - I2C_第1张图片

 

2 Data Structure

     理解数据结构对理解整个驱动程序子系统是很重要的。I2C的主要有两大数据结构,struct i2c_client 和 struct i2c_adapter。

  2.1 i2c_client

     struct i2c_client {
        unsigned short flags;  /* div., see below  */
        unsigned short addr;  /* chip address */
        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 (or -1) */
        char driver_name[KOBJ_NAME_LEN];
        struct list_head list;  /* DEPRECATED */
        struct completion released;
};

      struct i2c_client代表一个挂载到i2c总线上的i2c从设备,该设备所需要的数据结构,其中包括

  • 该i2c从设备所依附的i2c主设备 struct i2c_adapter *adapter
  • 该i2c从设备的驱动程序struct i2c_driver *driver
  • 作为i2c从设备所通用的成员变量,比如addr, name等
  • 该i2c从设备驱动所特有的数据,依附于dev->driver_data下

  2.2 i2c_adapter

     struct i2c_adapter {
        struct module *owner;
        unsigned int id;
        unsigned int class;
        const struct i2c_algorithm *algo; /* the algorithm to access the bus */
        void *algo_data;

        ... ...

     };
     struct i2c_adapter代表主芯片所支持的一个i2c主设备,该设备所需要的数据结构,

     其中,struct i2c_algorithm *algo是该i2c主设备传输数据的一种算法,或者说是在i2c总线上完成主从设备间数据通信的一种能力。

     struct i2c_algorithm {
        int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs, 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);

        u32 (*functionality) (struct i2c_adapter *);
    };

    接下来,要实现整个i2c子系统的驱动,便围绕这两个数据结构展开,其主要步骤可总结为以下三步,

  • 实现i2c主设备驱动                (drivers/i2c/bus/*)
  • 注册i2c从设备的i2c_client    (drivers/i2c/i2c-core)
  • 实现i2c从设备驱动

 3 Adapter

   内核目录drivers/i2c下有两个文件夹,algorithm和bus,其中bus存放i2c主设备的驱动,主设备驱动完成两大任务,

  • 提供该i2c主设备与从设备间完成数据通信的能力
  • 完成该i2c_adapter和所有已知的i2c_client的注册

   以i2c-pxa.c为例,

  /* drivers/i2c/bus/i2c-pxa.c */

 static int __init i2c_adap_pxa_init(void)
{
 return platform_driver_register(&i2c_pxa_driver);
}

static struct platform_driver i2c_pxa_driver = {
 .probe  = i2c_pxa_probe,
 ... ...
 .id_table = i2c_pxa_id_table,
};

 static int i2c_pxa_probe(struct platform_device *dev)
{
 struct pxa_i2c *i2c;
  i2c->adap.algo = i2c_pxa_algorithm;             // 提供该i2c主设备与从设备间完成数据通信的能力

  i2c_add_numbered_adapter(&i2c->adap);      // 调用i2c-core.c中的接口函数,完成该i2c_adapter和i2c_client的注册

  ... ...
 }

static const struct i2c_algorithm i2c_pxa_algorithm = {
 .master_xfer = i2c_pxa_xfer,                  // 根据pxa具体芯片的要求,完成i2c数据传输
 .functionality = i2c_pxa_functionality,
};

 

 4 I2C-core

   内核目录drivers/i2c下的i2c-core.c,顾名思义,是内核为I2C提供的统一系统接口。

   看看i2c_add_numbered_adapter做了些什么,

   int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
   ... ...
  status = i2c_register_adapter(adap);
  return status;
}

 

static int i2c_register_adapter(struct i2c_adapter *adap)
{
   ... ...

  device_register(&adap->dev);     //完成I2C主设备adapter的注册,即注册object和发送uevent等

  i2c_scan_static_board_info(adap);      

   ... ...
}
 i2c_scan_static_board_info(adap),此函数为整个I2C子系统的核心,它会去遍历一个由I2C从设备组成的双向循环链表,并完成所有I2C从设备的i2c_client的注册,具体过程如下,

 static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
 struct i2c_devinfo *devinfo;     //已经建立好了的I2C从设备链表


 list_for_each_entry(devinfo, &__i2c_board_list, list) {
     i2c_new_device(adapter,&devinfo->board_info);
     ... ...
 }
}

 struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
   ... ...
   i2c_attach_client(client);
   ... ...

}
int i2c_attach_client(struct i2c_client *client)
{
   ... ...
   device_register(&client->dev);     //完成I2C从设备client的注册
   ... ...
}

 那么,这个I2C从设备组成的双向循环链表,是什么时候通过什么方式建立起来的呢?

 以某重力感应设备为例,

/* /arch/arm/mach-pxa/starwood_p1.c */

static void __init saar_init(void)

{

   ... ...

   i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_bma220_info));

   ... ...

}

static struct i2c_board_info saar_i2c_bma220_info[] = {
 {
  .driver_name = "bma220",
  .addr  = 0x0B,
  .irq  = IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO15)), 
 },
};

 /* drivers/i2c/i2c-boardinfo.c */

 int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
{

   ... ...

  struct i2c_devinfo *devinfo;
  devinfo->board_info = *info;
  list_add_tail(&devinfo->list, &__i2c_board_list);    //将I2C从设备加入该链表中
   ... ...
}

 所以,在系统初始化的过程中,我们可以通过 i2c_register_board_info,将所需要的I2C从设备加入一个名为__i2c_board_list双向循环链表,系统在成功加载I2C主设备adapt后,就会对这张链表里所有I2C从设备逐一地完成 i2c_client的注册。

 

  5 Slave Driver

       如果说硬件方面,I2C主设备已经集成在主芯片内,软件方面,linux也为我们提供了相应的驱动程序,位于drivers/i2c/bus下,那么接下来I2C从设备驱动就变得容易得多。既然系统加载I2C主设备驱动时已经注册了i2c_adapter和i2c_client,那么I2C从设备主要完成三大任务,

  • 系统初始化时添加以i2c_board_info为结构的I2C从设备的信息
  • 在I2C从设备驱动程序里使用i2c_adapter里所提供的算法,即实现I2C通信。
  • 将I2C从设备的特有数据结构挂在到i2c_client.dev->driver_data下。

以重力感应装置为例,

static int __init BMA220_init(void)
{
 return i2c_add_driver(&bma220_driver);
}

static struct i2c_driver bma220_driver = { 
 .driver = {
  .owner = THIS_MODULE, 
  .name = "bma220",
 },
 .class  = I2C_CLASS_HWMON,
 .probe  = bma220_probe,
 .remove  = bma220_remove,
};

static int bma220_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
 struct bma220_data *data;      
 i2c_check_functionality(client->adapter, I2C_FUNC_I2C)

 i2c_smbus_read_word_data(client, 0x00);    // i2c-core提供的接口,利用i2c_adapter的算法实现I2C通信
 i2c_set_clientdata(bma220_client, data);      // 将设备的数据结构挂到i2c_client.dev->driver_data下
 misc_register(&bma_device);
 request_irq(client->irq, bma220_irq_handler, IRQF_TRIGGER_RISING, "bma220", &data->bma220);
 bma220_set_en_tt_xyz(0);
 bma220_reset_int();

 ... ...
}

 

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