Arm嵌入式开发之I2C总线

ARM 75浏览

Linux内核源码中drivers目录下包含i2c的目录,其中含有如下文件和文件夹:

1.      
i2c-core.c
实现i2c核心功能和/proc/bus/i2c*接口

2.      
i2c-dev.c
实现i2c适配器设备文件的功能。主设备号为89
,次设备号0~255。提供了通用的read()、write()、close()等接口。

3.      
chips
文件夹
包含一些特定芯片的i2c设备驱动

4.      
buses
文件夹
包含一些i2c总线的驱动

5.      
algos
实现了一些i2c
总线适配器的algorithm(算法)

 

此外i2c.h i2c_driver, i2c_client, i2c_adapter,
i2c_algorithm
四个结构体进行了定义。

struct i2c_driver {

   
char name[32];

   
int id;

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

 

   
/* Notifies the driver that a new bus has appeared. This routine

   
 * can be used by the driver to test if the bus meets its conditions

   
 * & seek for the presence of the chip(s) it supports. If found, it

   
 * registers the client(s) that are on the bus to the i2c admin. via

   
 * i2c_attach_client.

   
 */

   
int (*attach_adapter)(struct i2c_adapter *);

 

   
/* tells the driver that a client is about to be deleted & gives it

   
 * the chance to remove its private data. Also, if the client struct

   
 * has been dynamically allocated by the driver in the function above,

   
 * it must be freed here.

   
 */

   
int (*detach_client)(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);

   

   
/* These two are mainly used for bookkeeping & dynamic unloading of

   
 * kernel modules. inc_use tells the driver that a client is being 

   
 * used by another module & that it should increase its ref. counter.

   
 * dec_use is the inverse operation.

   
 * NB: Make sure you have no circular dependencies, or else you get a

   
 * deadlock when trying to unload the modules.

   
* You should use the i2c_{inc,dec}_use_client functions instead of

   
* calling this function directly.

   
 */

   
void (*inc_use)(struct i2c_client *client);

   
void (*dec_use)(struct i2c_client *client);

};

 

/*

 * i2c_client identifies a single device (i.e. chip) that is connected to an

 * i2c bus. The behaviour is defined by the routines of the driver. This

 * function is mainly used for lookup & other admin. functions.

 */

struct i2c_client {

   
char name[32];

   
int id;

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

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

                   
/* addresses are stored in the  */

                   
/* _LOWER_ 7 bits of this char  */

   
/* addr: unsigned int to make lm_sensors i2c-isa adapter work

   
  more cleanly. It does not take any more memory space, due to

   
  alignment considerations */

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

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

   
void *data;         /* for the clients      */

   
int usage_count;        /* How many accesses currently 
*/

                   
/* to the client        */

};

 

 

/*

 * The following structs are for those who like to implement new bus drivers:

 * i2c_algorithm is the interface to a class of hardware solutions which can

 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584

 * to name two of the most common.

 */

struct i2c_algorithm {

   
char name[32];              /* textual description */

   
unsigned int id;

 

   
/* If an adapter algorithm can't to 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 */

   
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);

 

   
/* --- these optional/future use for some adapter types.*/

   
int (*slave_send)(struct i2c_adapter *,char*,int);

   
int (*slave_recv)(struct i2c_adapter *,char*,int);

 

   
/* --- ioctl like call to set div. parameters. */

   
int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long);

 

   
/* To determine what the adapter supports */

   
u32 (*functionality) (struct i2c_adapter *);

};

 

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)

struct proc_dir_entry;

#endif

 

/*

 * i2c_adapter is the structure used to identify a physical i2c bus along

 * with the access algorithms necessary to access it.

 */

struct i2c_adapter {

   
char name[32];  /* some useful name to identify the adapter
*/

   
unsigned int id;/* == is algo->id | hwdep.struct->id,       */

           
/* for registered values see below      */

   
struct i2c_algorithm *algo;/* the algorithm to access the bus  
*/

   
void *algo_data;

 

   
/* --- These may be NULL, but should increase the module use count */

   
void (*inc_use)(struct i2c_adapter *);

   
void (*dec_use)(struct i2c_adapter *);

 

   
/* --- administration stuff. */

   
int (*client_register)(struct i2c_client *);

   
int (*client_unregister)(struct i2c_client *);

 

   
void *data; /* private data for the adapter        
*/

           
/* some data fields that are used by all types  */

           
/* these data fields are readonly to the public */

           
/* and can be set via the i2c_ioctl call    */

 

           
/* data fields that are valid for all devices   */

   
struct semaphore lock; 

   
unsigned int flags;/* flags specifying div. data        */

 

   
struct i2c_client *clients[I2C_CLIENT_MAX];

   
int client_count;

 

   
int timeout;

   
int retries;

 

#ifdef CONFIG_PROC_FS

   
/* No need to set this when you initialize the adapter         
*/

   
int inode;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)

   
struct proc_dir_entry *proc_entry;

#endif

#endif /* def CONFIG_PROC_FS */

};

 

/*flags for the driver struct: */

#define I2C_DF_NOTIFY  
0x01        /* notify on bus (de/a)ttaches */

#define I2C_DF_DUMMY   
0x02        /* do not connect any clients */

 

/*flags for the client struct: */

#define I2C_CLIENT_ALLOW_USE       
0x01    /* Client allows access */

#define I2C_CLIENT_ALLOW_MULTIPLE_USE
  0x02    /* Allow multiple access-locks */

                       
/* on an i2c_client */

 

/* i2c_client_address_data is the struct for holding default client

 * addresses for a driver and for the parameters supplied on the

 * command line

 */

struct i2c_client_address_data {

   
unsigned short *normal_i2c;

   
unsigned short *normal_i2c_range;

   
unsigned short *probe;

   
unsigned short *probe_range;

   
unsigned short *ignore;

   
unsigned short *ignore_range;

   
unsigned short *force;

};

I2c_adapter对应物理上的一个适配器,i2c_algorithm对应一套通信方法。

I2c_driver对应一套驱动方法,不对应任何物理实体。

I2c_client对应物理实体设备,每个设备需要一个i2c_client来描述。

Clientdriver发送关联的时刻在i2c_driverattach_adapter()函数调用的运行时。attach_adapter()探测物理设备,确定一个client存在,把client数据结构的adapter指针指向i2c_adapter,driver指针指向i2c_driver,调用i2c_adapter的函数client_register()。相反的过程发生在i2c_driverdetach_client()被调用的时候。

I2c_adapteri2c_client关系是client依附于adapter。可多对一。工程师要做的:

1. 
I2c
适配器的硬件驱动

2. 
提供适配器的算法

用具体适配器的xxx_xfer()函数填充算法i2c_algorithm结构体的master_xfer指针,并把i2c_algorithm指针赋予i2c_adaperalgo指针。

3. 
实现设备驱动与i2c_driver接口

用具体设备yyyyyy_attach_adapter()函数指针、yyy_detach_client()函数指针和yyy_command()函数指针赋值给i2c_driverattach_adapterdetach_adapterdetach_client指针。

4.实现i2c设备驱动的文件操作接口。

 

I2c核心(driver/i2c/i2c-core.c)中提供了一组不依赖于硬件平台的接口函数,不需要工程师修改,总线驱动和设备驱动之间依赖于核心作为纽带。主要函数如下:

1. 
增加、删除i2c_adapter.

2. 
增加、删除i2c_driver.

3. 
I2c_client
依附、脱离。

Attach_client(),detach_client()

当具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册,相反,在client被取消关联的时候,sysfs文件和设备也被注销

4. 
I2c
传输、发送和接收。

Transfer() ,master_sent(),master_recv()

5. 
I2c
控制命令分配。

Int i2c_control(),void i2c_cliens_command()

 

Linux i2c
总线驱动

总线驱动模块加载函数完成两个工作:

1. 
初始化i2c适配器所使用的资源,如申请i/o地址,中断号等

2. 
通过i2c_add_adapter()
添加i2c_adapter的数据结构,此数据结构已经被xxx适配器的相应函数指针所初始化。

卸载工作相反。

 

通信方法主要实现两个函数:

master_xfer()在适配器上完成传递给它的i2c_msg数组中的每个i2c消息

functionality()函数用于返回algorithm所支持的通信协议

多数的i2c
总线驱动会定义一个xxx_i2c
结构体,作为i2c_adapteralgo_data(私有数据),包括消息指针,函数索引,适配器访问控制用的自旋锁,等待队列等,而master_xfer()函数完成消息数组中的消息处理也可以通过对此结构体相关成员的访问来控制。

 

I2c
设备驱动

要使用i2c_driveri2c_client数据结构并填充其中的成员函数。其中client包含在设备的私有信息结构体yyy_data中,而driver适合被定义为全局变量并初始化。

Static struct i2c_driver yyy_driver={

.diver={

.name=”yyy”,

},

.attach_adapter=yyy_attach_adapte,

.detach_client=yyy_ detach_client,

.command=yyy_command,

};

设备驱动的模块加载函数通用的方法是在加载函数中完成两件事:

通过register_chrdev()函数将i2c设备注册为一个字符设备;

通过i2c核心的i2c_add_driver()函数添加i2c_driver

卸载先用i2c_del_driver()函数删除i2c_driver,再通过unregister_chrdev()注销设备。

 

Linux i2c
设备驱动的文件操作接口

写操作:

1.      
从用户空间到字符设备驱动写函数接口,写函数构造i2c消息数组

2.      
把构造的i2c消息数组传递给i2c核心的传送函数i2c_transfer()

3.      
i2c_transfer()
找到对应的适配器algorithm的通信方法函数master_xfer()去完成i2c消息处理。

大多数情况,我们可以通过i2c-dev.c文件提供的i2c
适配器设备文件接口就可以完成对i2c设备的读写。