驱动层-3 I2C驱动

一、I2C驱动的架构。见下图。

驱动层-3 I2C驱动_第1张图片

如上图。I2C驱动框架分为三个部分:I2CCore层、I2C总线驱动层、I2C设备驱动层。

下面我们逐个展开

二、I2C Core层

I2C Core层源代码位于drivers/i2c/i2c-core.c中。

I2C Core层是系统实现的,与硬件无关的。它起着承上启下的作用,用来为设备驱动层和总线驱动层提供注册adapter、driver、client到系统中来的接口。这些接口如下:

int  i2c_add_adapter(struct i2c_adapter *);

int  i2c_del_adapter(struct i2c_adapter *);

 

int  i2c_attach_client(struct module *owner,struct i2c_ client *);

void  i2c_detach_client(struct i2c_ client *);

 

int  i2c_register_driver(struct module *owner,struct i2c_ driver *);

void  i2c_del_driver(struct i2c_ driver *);

 

int  i2c_transfer(struct i2c_adapter *, structi2c_msg *msgs, int num);

int  i2c_master_send(struct i2c_ client *, constchar *buf, int count);

int  i2c_master_rcv (struct i2c_ client *,char*buf, int count);

sendreceive分别都调用了transfer函数,而transfer也不是直接和硬件交互,而是调用 algorithm中的master_xfer()函数,所以我们要想进行数据传输,必须自己来实现这个master_xfer()函数

三、I2C总线驱动层

总线驱动层由algorithm和adapter组成。他们有各自对应的数据结构来描述。如下。

i2c_adapter结构体
 struct i2c_adapter {
 struct module *owner;//所属模块
 unsigned int id;//algorithm的类型,定义于i2c-id.h,
 unsigned int class;    
 const struct i2c_algorithm *algo; //总线通信方法结构体指针
 void *algo_data;//algorithm数据
 struct rt_mutex bus_lock;//控制并发访问的自旋锁
 int timeout;   
 int retries;//重试次数
 struct device dev; //适配器设备 
 int nr;
 char name[48];//适配器名称
 struct completion dev_released;//用于同步
 struct list_head userspace_clients;//client链表头
};

 

 I2c_algorithm结构体
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, intnum);//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 *);//返回适配器支持的功能
};

// master_xfer传递的信息msg 结构

struct i2c_msg {
 __u16 addr;//设备地址  
 __u16 flags;//标志
 __u16 len;//消息长度     
 __u8 *buf;//消息数据    
};

现在我们要实现自己的I2C 总线驱动,步骤一般如下:

1)      定义自己的I2c_algorithm,然后实现自己的master_xfer和functionality函数,然后赋值给自己定义的I2c_algorithm。原因我们再上面的I2C Core层说过了,i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)是要调用到algorithm中的master_xfer函数的。每款芯片的master_xfer都是不同的。

2)      定义自己的i2c_adapter,然后将自己定义的I2c_algorithm赋值给i2c_adapter的成员。

3)      我们最终要将总线驱动作为一个平台驱动注册到系统中,所以我们要自己定义一个平台注册函数,在平台probe函数中调用I2C Core层提供的接口i2c_add_adapter(struct i2c_adapter *)将我们的adapter添加到系统中。

4)      按照前面介绍过得平台驱动的构建方法,构建自己的platform_driver, 将probe, remove函数赋值给对应成员,然后调用platform_register_driver()将驱动注册到系统中即可。

我们来看一下DM365的i2c 总线驱动的例子吧。

//定义自己的I2c_algorithm

static struct i2c_algorithm i2c_davinci_algo = {

         .master_xfer   = i2c_davinci_xfer,

         .functionality   = i2c_davinci_func,

};

 

//定义自己的i2c_adapter, 在probe中调用i2c_add_adapter注册adapter

static intdavinci_i2c_probe(struct platform_device *pdev)

{

         struct davinci_i2c_dev *dev;

         struct i2c_adapter *adap;

         ….

                  adap= &dev->adapter;

         i2c_set_adapdata(adap, dev);

         adap->owner = THIS_MODULE;

         adap->class = I2C_CLASS_HWMON;

         strlcpy(adap->name, "DaVinciI2C adapter", sizeof(adap->name));

         adap->algo =&i2c_davinci_algo;

         adap->dev.parent =&pdev->dev;

 

         /* FIXME */

         adap->timeout = 1;

         adap->retries = 1;

 

         adap->nr = pdev->id;

         r =i2c_add_adapter(adap);

         …

}

 

//将总线驱动作为平台驱动注册到系统中

static struct platform_driver davinci_i2c_driver = {

         .probe                =davinci_i2c_probe,

         .remove            =davinci_i2c_remove,

         .driver                ={

                   .name       = "i2c_davinci",

                   .owner     = THIS_MODULE,

         },

};

 

/* I2C may beneeded to bring up other drivers */

static int__init davinci_i2c_init_driver(void)

{

         returnplatform_driver_register(&davinci_i2c_driver);

}

subsys_initcall(davinci_i2c_init_driver);

 

static void__exit davinci_i2c_exit_driver(void)

{

         platform_driver_unregister(&davinci_i2c_driver);

}

以上的代码在\diver\i2c\buses下,i2c-davinci文件中。开篇的平台驱动我们说过,有驱动就必然有设备与之匹配。既然上面把i2c总线驱动作为平台驱动注册到系统中,那么对应的i2c总线平台设备在哪里呢?我们可以在\arch\arm\mach-davinci\Devices文件中找到答案。

如我们之前分析过的平台设备的构建三个步骤:构建struct resource, 构建platform_device,然后用platform_add_devices()添加设备。I2C总线的平台设备当然也是按照这个步骤进行。我们看一下davinci平台的linux 6.18内核的I2C总线平台设备代码。

//构建struct resource

static struct resource i2c_resources[] = {

         {

                   .start                 = DAVINCI_I2C_BASE,

                   .end          = DAVINCI_I2C_BASE + 0x40,

                   .flags                  = IORESOURCE_MEM,

         },

         {

                   .start                 = IRQ_I2C,

                   .flags                  = IORESOURCE_IRQ,

         },

};

//构建platform_device

static struct platform_device davinci_i2c_device = {

         .name           = "i2c_davinci",

         .id             = 1,

         .num_resources       = ARRAY_SIZE(i2c_resources),

         .resource = i2c_resources,

};

static struct platform_device *devices[] __initdata = {

         &i2c_device,

         &watchdog_device,

         &usb_device,

};

//用platform_add_devices()添加设备

static int __init davinci_init_devices(void)

{

         davinci_init_cpu_i2c();

         davinci_init_cpu_usb();

         platform_add_devices(devices, ARRAY_SIZE(devices));

         davinci_init_cpu_emac_register();

         return 0;

}

可见,平台设备和平台驱动时靠名字来匹配的。这里的I2C总线驱动是使用"i2c_davinci"名字来匹配驱动和设备。

四、I2C设备驱动层

如上图,I2C设备驱动层i2c_client、i2c_driver组成,他们都有对应的结构体来描述。

i2c_driver结构体

struct i2c_driver {
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter函数指针
int (*detach_adapter)(struct i2c_adapter *);//脱离i2c_adapter函数指针

int (*probe)(struct i2c_client *, conststruct i2c_device_id *);
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void*arg);//命令列表
struct device_driver driver;
const struct i2c_device_id *id_table;//该驱动所支持的设备ID表
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};

//i2c_client结构体
struct i2c_client {
 unsigned short flags;//标志  
 unsigned short addr; //低7位为芯片地址  
 char name[I2C_NAME_SIZE];//设备名称
 struct i2c_adapter *adapter;//依附的i2c_adapter
 struct i2c_driver *driver;//依附的i2c_driver 
 struct device dev;//设备结构体  
 int irq;//设备所使用的结构体  
 struct list_head detected;//链表头
 };

实际应用我们要注册一个I2C设备到系统,我们需要做的工作的是:

1.      构建自己的attach函数,在函数中定义自己的i2c_client, 调用i2c_client_attach()来注册设备。

2.      构建自己的i2c_driver, 然后将自己定义的attach函数赋值给i2c_driver的成员。

3.      调用i2c_add_driver注册driver.

我们来看个例子吧,tvp5150的例子。在驱动中,tvp5150是通过I2C和DM365通信的,所以tvp5150是作为一个i2c设备挂载到系统中。

 

         //用i2c_client_attach()来注册设备

         staticint tvp5150_i2c_attach_client(struct i2c_client *client,

                                          struct i2c_driver *driver,

                                          struct i2c_adapter *adap, int addr)

{

         int err = 0;

         if (client->adapter)

                   err =-EBUSY;   /* our client is alreadyattached */

         else {

                   client->addr= addr;

                   client->driver= driver;

                   client->adapter= adap;

                   err =i2c_attach_client(client);

                   if (err)

                            client->adapter= NULL;

         }

         return err;

}

 

//构建自己的i2c_driver,然后将自己定义的attach函数赋值给i2c_driver的成员。

driver = &tvp5150_channel_info[i].i2c_dev.driver;

driver->driver.name= strings[i];

driver->id = I2C_DRIVERID_MISC;

if (0 == i)

         driver->attach_adapter= tvp5150A_i2c_probe_adapter;

else

         driver->attach_adapter= tvp5150B_i2c_probe_adapter;

driver->detach_client = tvp5150_i2c_detach_client;

err = i2c_add_driver(&tvp5150_channel_info[i].i2c_dev.driver);

 

五、i2c_adapter、i2c_client、i2c_driver、i2c_algorithm的关系

1)      i2c_adapter对应一个物理适配器,i2c_algorithm对应一种通信方法。i2c_adapter需要i2c_algorithm提供的通信方法来让I2C产生特定访问周期。所以i2c_algorithm是i2c_adapter的一个成员。

2)      i2c_client对应一个设备,i2c_driver则对应操作设备一套驱动方法,例如probe, remove, suspend, resume等。每个真是的I2C设备都必须要有一个i2c_client来描述。多个设备可以使用同一套驱动方法。所以i2c_adapter 跟i2c_client是一对多得关系。

3)       在6.30的内核中,i2c_client通常在BSP中使用I2C_BOARD_INFO()来填充。I2C中线驱动会调用i2c_match_id()来将BSP中的ID和i2c_driver中的ID表匹配。

你可能感兴趣的:(TI,DAVINCI)