Linux的I2C驱动讲解

I2C简介

I2C是“Inter Integrated Circuit Bus”的缩写,中文译成“内部集成电路

  它是Philips 公司于20 世纪80 年代研发成功的一种具有多端控制功能的双线双向串行数据总线标准, 其具有模块化、电路结构简单等优点。在嵌入式系统中,I2C总线已经成为器件接口的标准之一, 常用于连接RAM、EEPROM 以及LCD 控制器等设备。另外,总线的数据传输是以字节为单位的。

目前,标准的I2C的传输速率可以达到100kbit/s,能支持128 个设备,增强型I2C传输速率可达400kbit/s,能支持多达1024 个设备,高速模式下的I2C 传输速率更高达3.4Mbit/s。

 I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据,每个器件都有一个惟一的地址识别。I2C 规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。总线必须由主 器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。SDA线上的数据状态仅在SCL为低电平的期间才 能改变,SCL为高电平的期间,SDA状态的改变被用来表示起始和停止条件。

I2C总线协议规定,各主机进行通信时都要有起始、结束、发送数据和应答信号。这些信号都是通信过程中的基本单元。主器件产生串行时钟(SCL),同时控制总线的传输方向,并产生开始和停止条件。空闲状态时SDA和SCL都为高电平;起始信号就是在SCL线为高时SDA线从高变化到低;停止信号就是在SCL线为高时SDA线从低变化到高;应答信号是在SCL为高时SDA为低;非应答信号相反,是在SCL为高时SDA为高。


  总线传送的每1帧数据均是1个字节。协议规定,在启动总线后的第1个字节的高7位是对从机的寻址地址,第8位为方向位(“0”表示主机对从机的写操作;“1”表示主机对从机的读操作),其余的字节为操作数据。 数据传送过程是:在I2C总线发送起始信号后,发送从机的7位寻址地址和1位表示这次操作性质的读写位,在有应答信号后开始传送数据,直到发送停止信号。主机每发送1个字节就要检测SDA线上有没有收到应答信号,有则继续发送,否则将停止发送数据。

 

I2C总线的接口电路结构如下图所示:

I2c数据传输图:

 

I2C传输进一步分析

1)I2C位传输
   数据传输:SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit;
   若SDA发生跳变,则用来表示一个会话的开始或结束
   数据改变:SCL为低电平时,SDA线才能改变传输的bit

 2)I2C开始和结束信号
   开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
   结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

  

3)I2C应答信号

   Master每发送完8bit数据后等待Slave的ACK。
   即在第9个clock,若从IC发ACK,SDA会被拉低。
   若没有ACK,SDA会被置高,这会引起Master发生RESTART或STOP流程,如下所示:

  4) I2C写流程
写寄存器的标准流程为:
1.    Master发起START
2.    Master发送I2Caddr(7bit)和w操作0(1bit),等待ACK
3.    Slave发送ACK
4.    Master发送reg addr(8bit),等待ACK
5.    Slave发送ACK
6.    Master发送data(8bit),即要写入寄存器中的数据,等待ACK
7.    Slave发送ACK
8.    第6步和第7步可以重复多次,即顺序写多个寄存器
9.    Master发起STOP

写一个寄存器

 写多个寄存器

 

  5)I2C读流程

读寄存器的标准流程为:
1.    Master发送I2Caddr(7bit)和w操作1(1bit),等待ACK
2.    Slave发送ACK
3.    Master发送reg addr(8bit),等待ACK
4.    Slave发送ACK
5.    Master发起START
6.    Master发送I2Caddr(7bit)和r操作1(1bit),等待ACK
7.    Slave发送ACK
8.    Slave发送data(8bit),即寄存器里的值
9.    Master发送ACK
10.    第8步和第9步可以重复多次,即顺序读多个寄存器

读一个寄存器

 读多个寄存器


I2C体系结构

Linux的I2C体系结构分为3个组成部分。

(1)I2c核心

I2C核心是I2c总线和I2c设备驱动的中间枢纽,它以通用的、与平台无关的接口实现了I2C中设备与适配器的沟通,提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(即“algorithm”)上层的、与具体适配器无关的代码以及探测设备、检测设备的地址的上层代码等。I2c总线驱动填充I2c_adapterI2c_algorithm结构体,I2c设备驱动填充I2c_driveri2c_client结构体并实现其本身所对应设备类型的驱动。

(2)I2C总线驱动

I2C总线驱动是对I2C硬件体系结构中适配器的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。

I2C总线驱动主要包含了I2C适配器数据结构i2c_adapterI2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。

经由I2C总线驱动的代码,我们可以控制I2C适配器以主控制方式产生开始、停止位、读写周期,以及以从设备方式读写、产生ACK等。

(3)I2C设备驱动

I2C设备驱动(也称为客户端驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。

I2C设备驱动主要包含了数据结构体i2c_driveri2c_client,我们需要具体设备实现其中的成员函数。

 

I2C体系结构图

系统中i2c-dev.c文件定义的主设备号为89的设备可以方便地给应用程序提供读写I2c设备寄存器的能力,使得工程师大多数时候并不需要为具体的I2c设备驱动定义文件操作接口。

如何理解adapterclient呢?它在s3c2440中对应的是什么?Adapterclient都是linux驱动软件抽象出来的东西,Linux I2C框架搞那么复杂是为了通用性及为了符合Linux内核驱动模式而制定的。简单的说,你的开发板上有几个I2C接口,就有几个adapter , 也就是有几条I2C bus , I2C client对应的就是你的外围I2C 设备,有几个就有几个client , 把这些设备插入开发板, 对应其中的一条bus, 那么相应的就对应了其中的一个adapter , 接下来的就是I2c核心部分使client与 adapter匹配成对。

在linux内核中,所有的I2C设备都在sysfs文件系统中显示,存在于/sys/bus/i2c/目录下,适配器地址和芯片地址的形式列出,例如:

[zhouyou@centos6 ~]$ tree /sys/bus/i2c/
/sys/bus/i2c/
|-- devices
|   |-- i2c-0 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-0
|   |-- i2c-1 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-1
|   |-- i2c-10 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-10
|   |-- i2c-11 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-11
|   |-- i2c-12 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-12
|   |-- i2c-13 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-13
|   |-- i2c-14 -> ../../../devices/pci0000:00/0000:00:1f.3/i2c-14
|   |-- i2c-2 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-2
|   |-- i2c-3 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-3
|   |-- i2c-4 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-4
|   |-- i2c-5 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-5
|   |-- i2c-6 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-6
|   |-- i2c-7 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-7
|   |-- i2c-8 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-8
|   `-- i2c-9 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-9
|-- drivers
|   `-- dummy
|       |-- bind
|       |-- module -> ../../../../module/i2c_core
|       |-- uevent
|       `-- unbind
|-- drivers_autoprobe
|-- drivers_probe
`-- uevent


19 directories, 6 files

内核中i2c.h这个头文件对i2c_driveri2c_clienti2c_adapteri2c_algorithm4个数据结构进行了定义。理解这4个结构的作用十分关键

struct i2c_adapter代表主芯片所支持的一个i2c主设备

struct i2c_adapter {
    struct module *owner;       /*所属模块*/
    unsigned int class;       /* classes to allow probing for */
    const struct i2c_algorithm *algo; /* 总线通信方法结构体指针 */
    void *algo_data;

    /* data fields that are valid for all devices   */
    struct rt_mutex bus_lock;

    int timeout;            /* in jiffies */
    int retries;
    struct device dev;      /* the adapter device */

    int nr;
    char name[48];          /*适配器的名字*/
    struct completion dev_released;   /*用于同步*/

    struct mutex userspace_clients_lock;
    struct list_head userspace_clients;    /*链表头*/
};

/*
 * 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 {
    /* 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 */
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
               int num);               /*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传输函数指针*/

    /* To determine what the adapter supports */
    u32 (*functionality) (struct i2c_adapter *);               /*返回适配器支持的功能*/
};

 

struct i2c_client代表一个挂载到i2c总线上的i2c从设备

/**
 * struct i2c_client - represent an I2C slave device
 * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
 *  I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
 * @addr: Address used on the I2C bus connected to the parent adapter.
 * @name: Indicates the type of the device, usually a chip name that's
 *  generic enough to hide second-sourcing and compatible revisions.
 * @adapter: manages the bus segment hosting this I2C device
 * @driver: device's driver, hence pointer to access routines
 * @dev: Driver model device node for the slave.
 * @irq: indicates the IRQ generated by this device (if any)
 * @detected: member of an i2c_driver.clients list or i2c-core's
 *  userspace_devices list
 *
 * An i2c_client identifies a single device (i.e. chip) connected to an
 * i2c bus. The behaviour exposed to Linux is defined by the driver
 * managing the device.
 */

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

 

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, it will be removed in a
     * near future.
     */
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;  /*依附I2C_adapter函数指针*/
    int (*detach_adapter)(struct i2c_adapter *) __deprecated;   /*脱离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 *);

    /* Alert callback, for example for the SMBus alert protocol.
     * The format and meaning of the data value depends on the protocol.
     * For the SMBus alert protocol, there is a single bit of data passed
     * as the alert response's low bit ("event flag").
     */
    void (*alert)(struct i2c_client *, unsigned int data);

    /* 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;
    const struct i2c_device_id *id_table;         /*该驱动所支持的设备ID表*/

    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
};

 (1)2c_adapteri2c_algorithm

i2c_adapter对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithmi2c_adapter什么也做不了,因此i2c_adapter中包含其使用的i2c_algorithm的指针。

I2c_algorithm中关键函数master_xfer用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位。I2c_msg结构体非常关键,代码清单5给出了它的定义。

 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          */

};

(2)i2c_driveri2c_client

i2c_driver对应一套驱动方法,其主要成员函数是probe()、remove()、suspend()resume()等,另外id_table是该驱动所支持的I2C设备的ID表。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。I2c_driveri2c_client的关系是一对多,一个i2c_driver上可以支持多个同类型的i2c_client

I2c_client信息通常在BSP的板文件中通过i2c_board_info填充,如下面代码就定义了一个I2C设备ID为“24c02”、地址为0x50i2c_client:

代码清单6 i2c_board_info结构体定义

1. static struct i2c_board_info __initdata smdk2440_i2c_devs[] = {

2.     {

3.         I2C_BOARD_INFO("24c02", 0x50),

4.         .platform_data = &at24c02,

5.     },

6.     /*  more devices can be added using expansion connectors */

7. };

I2C总线驱动i2c_bus_typematch()函数i2c_device_match()中,会调用i2c_match_id()函数匹配板文件中定义的IDi2c_driver所支持的ID表。

代码清单7 i2c_device_match函数在linux-3.0/drivers/i2c/i2c-core.c

1. static int i2c_device_match(struct device *dev, struct device_driver *drv)

2. {

3.     struct i2c_client   *client = i2c_verify_client(dev);

4.     struct i2c_driver   *driver;

5.     if (!client)

6.         return 0;

7.     /* Attempt an OF style match */

8.     if (of_driver_match_device(dev, drv))

9.         return 1;

10.     driver = to_i2c_driver(drv);

11.     /* match on an id table if there is one */

12.     if (driver->id_table)

13.         return i2c_match_id(driver->id_table, client) != NULL;

14.     return 0;

15. }

(3)i2c_adpateri2c_client

i2c_adapteri2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adapter。由于一个适配器上可以连接多个I2C设备,所以一个i2c_adapter也可以被多个i2c_client依附,i2c_adapter中包含依附于它的i2c_client的链表。


假设I2C总线适配器xxx上有两个使用相同驱动程序的yyyI2C设备,在打开I2C总线的设备节点后相关数据结构之间的逻辑组织关系将如下图所示:



1.Linux I2C核心

I2C核心(driver/i2c/i2c-core.c)文件中提供了一组不依赖与硬件平台的接口函数,这个文件一般不需要被工程师修改,但是理解其中的主要函数非常关键,因为I2C总线驱动和设备驱动之间依赖于I2C核心作为纽带I2C核心中的主要函数如下

2.增加/删除i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adapter)

{

     int id, res = 0;

      retry:

     if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)

         return -ENOMEM;

     mutex_lock(&core_lock);

     /* "above" here means "above or equal to", sigh */

     res = idr_get_new_above(&i2c_adapter_idr, adapter,

                 __i2c_first_dynamic_bus_num, &id);

     mutex_unlock(&core_lock);

     if (res < 0) {

         if (res == -EAGAIN)

              goto retry;

         return res;

     }

     adapter->nr = id;

     return i2c_register_adapter(adapter);

 }

 EXPORT_SYMBOL(i2c_add_adapter);

I2c_del_adapter函数:

1. /**

2.  * i2c_del_adapter - unregister I2C adapter

3.  * @adap: the adapter being unregistered

4.  * Context: can sleep

5.  *

6.  * This unregisters an I2C adapter which was previously registered

7.  * by @i2c_add_adapter or @i2c_add_numbered_adapter.

8.  */

9. int i2c_del_adapter(struct i2c_adapter *adap)

10. {

11.     int res = 0;

12.     struct i2c_adapter *found;

13.     struct i2c_client *client, *next;

14.     /* First make sure that this adapter was ever added */

15.     mutex_lock(&core_lock);

16.     found = idr_find(&i2c_adapter_idr, adap->nr);

17.     mutex_unlock(&core_lock);

18.     if (found != adap) {

19.         pr_debug("i2c-core: attempting to delete unregistered "

20.              "adapter [%s]\n", adap->name);

21.         return -EINVAL;

22.     }

23.     /* Tell drivers about this removal */

24.     mutex_lock(&core_lock);

25.     res = bus_for_each_drv(&i2c_bus_type, NULL, adap,

26.                    __process_removed_adapter);

27.     mutex_unlock(&core_lock);

28.     if (res)

29.         return res;

30.     /* Remove devices instantiated from sysfs */

31.     mutex_lock(&adap->userspace_clients_lock);

32.     list_for_each_entry_safe(client, next, &adap->userspace_clients,

33.                  detected) {

34.         dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name,

35.             client->addr);

36.         list_del(&client->detected);

37.         i2c_unregister_device(client);

38.     }

39.     mutex_unlock(&adap->userspace_clients_lock);

40. 

41.     /* Detach any active clients. This can't fail, thus we do not

42.      * check the returned value. This is a two-pass process, because

43.      * we can't remove the dummy devices during the first pass: they

44.      * could have been instantiated by real devices wishing to clean

45.      * them up properly, so we give them a chance to do that first. */

46.     res = device_for_each_child(&adap->dev, NULL, __unregister_client);

47.     res = device_for_each_child(&adap->dev, NULL, __unregister_dummy);

48. 

49. #ifdef CONFIG_I2C_COMPAT

50.     class_compat_remove_link(i2c_adapter_compat_class, &adap->dev,

51.                  adap->dev.parent);

52. #endif

53. 

54.     /* device name is gone after device_unregister */

55.     dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);

56. 

57.     /* clean up the sysfs representation */

58.     init_completion(&adap->dev_released);

59.     device_unregister(&adap->dev);

60. 

61.     /* wait for sysfs to drop all references */

62.     wait_for_completion(&adap->dev_released);

63. 

64.     /* free bus id */

65.     mutex_lock(&core_lock);

66.     idr_remove(&i2c_adapter_idr, adap->nr);

67.     mutex_unlock(&core_lock);

68. 

69.     /* Clear the device structure in case this adapter is ever going to be

70.        added again */

71.     memset(&adap->dev, 0, sizeof(adap->dev));

72. 

73.     return 0;

74. }

75. EXPORT_SYMBOL(i2c_del_adapter);

3.增加/删除i2c_driver

代码清单11 I2c_register_driver函数:

1. static int i2c_register_adapter(struct i2c_adapter *adap)

2. {

3.     int res = 0;

4. 

5.     /* Can't register until after driver model init */

6.     if (unlikely(WARN_ON(!i2c_bus_type.p))) {

7.         res = -EAGAIN;

8.         goto out_list;

9.     }  

10. 

11.     /* Sanity checks */

12.     if (unlikely(adap->name[0] == '\0')) {   

13.         pr_err("i2c-core: Attempt to register an adapter with "

14.                "no name!\n");

15.         return -EINVAL;

16.     }  

17.     if (unlikely(!adap->algo)) {        

18.         pr_err("i2c-core: Attempt to register adapter '%s' with "

19.                "no algo!\n", adap->name);        

20.         return -EINVAL;

21.     }  

22. 

23.     rt_mutex_init(&adap->bus_lock);     

24.     mutex_init(&adap->userspace_clients_lock);

25.     INIT_LIST_HEAD(&adap->userspace_clients);

26. 

27.     /* Set default timeout to 1 second if not already set */

28.     if (adap->timeout == 0)

29.         adap->timeout = HZ;             

30. 

31.     dev_set_name(&adap->dev, "i2c-%d", adap->nr);

32.     adap->dev.bus = &i2c_bus_type;

33.     adap->dev.type = &i2c_adapter_type;

34.     res = device_register(&adap->dev);

35.     if (res)

36.         goto out_list;

37. 

38.     dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

39. 

40. #ifdef CONFIG_I2C_COMPAT

41.     res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,

42.                        adap->dev.parent);

43.     if (res)

44.         dev_warn(&adap->dev,

45.              "Failed to create compatibility class link\n");

46. #endif

47. 

48.     /* create pre-declared device nodes */

49.     if (adap->nr < __i2c_first_dynamic_bus_num)

50.         i2c_scan_static_board_info(adap);

51. 

52.     /* Notify drivers */

53.     mutex_lock(&core_lock);

54.     bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);

55.     mutex_unlock(&core_lock);

56. 

57.     return 0;

58. 

59. out_list:

60.     mutex_lock(&core_lock);

61.     idr_remove(&i2c_adapter_idr, adap->nr);

62.     mutex_unlock(&core_lock);

63.     return res;

64. }

代码清单12 i2c_del_driver函数:

1. /*

2.  * i2c_del_driver - unregister I2C driver

3.  * @driver: the driver being unregistered

4.  * Context: can sleep

5.  */

6. void i2c_del_driver(struct i2c_driver *driver)

7. {

8.     i2c_for_each_dev(driver, __process_removed_driver);

9. 

10.     driver_unregister(&driver->driver);

11.     pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name);

12. }

13. EXPORT_SYMBOL(i2c_del_driver);

4.i2c_client依附/脱离

当一个具体的client被侦测到并被关联的时候,设备和使用爽肤水文件件被注册。相反地,在client杯取消关联的时候,sysfs文件和设备也被注销。如下代码清单13

代码清单13 i2c_new_device函数:

1. /**

2.  * i2c_new_device - instantiate an i2c device

3.  * @adap: the adapter managing the device

4.  * @info: describes one I2C device; bus_num is ignored

5.  * Context: can sleep

6.  *

7.  * Create an i2c device. Binding is handled through driver model

8.  * probe()/remove() methods.  A driver may be bound to this device when we

9.  * return from this function, or any later moment (e.g. maybe hotplugging will

10.  * load the driver module).  This call is not appropriate for use by mainboard

11.  * initialization logic, which usually runs during an arch_initcall() long

12.  * before any i2c_adapter could exist.

13.  *

14.  * This returns the new i2c client, which may be saved for later use with

15.  * i2c_unregister_device(); or NULL to indicate an error.

16.  */

17. struct i2c_client *

18. i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

19. {

20.     struct i2c_client   *client;

21.     int         status;

22. 

23.     client = kzalloc(sizeof *client, GFP_KERNEL);

24.     if (!client)

25.         return NULL;

26. 

27.     client->adapter = adap;

28. 

29.     client->dev.platform_data = info->platform_data;

30. 

31.     if (info->archdata)

32.         client->dev.archdata = *info->archdata;

33. 

34.     client->flags = info->flags;

35.     client->addr = info->addr;

36.     client->irq = info->irq;

37. 

38.     strlcpy(client->name, info->type, sizeof(client->name));

39. 

40.     /* Check for address validity */

41.     status = i2c_check_client_addr_validity(client);

42.     if (status) {

43.         dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",

44.             client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);

45.         goto out_err_silent;

46.     }

47. 

48.     /* Check for address business */

49.     status = i2c_check_addr_busy(adap, client->addr);

50.     if (status)

51.         goto out_err;

52. 

53.     client->dev.parent = &client->adapter->dev;

54.     client->dev.bus = &i2c_bus_type;

55.     client->dev.type = &i2c_client_type;

56.     client->dev.of_node = info->of_node;

57. 

58.     dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),

59.              client->addr);

60.     status = device_register(&client->dev);

61.     if (status)

62.         goto out_err;

63. 

64.     dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",

65.         client->name, dev_name(&client->dev));

66. 

67.     return client;

68. 

69. out_err:

70.     dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "

71.         "(%d)\n", client->name, client->addr, status);

72. out_err_silent:

73.     kfree(client);

74.     return NULL;

75. }

76. EXPORT_SYMBOL_GPL(i2c_new_device);

77. 

78. 代码清单14 i2c_unregister_device函数

79. /**

80.  * i2c_unregister_device - reverse effect of i2c_new_device()

81.  * @client: value returned from i2c_new_device()

82.  * Context: can sleep

83.  */

84. void i2c_unregister_device(struct i2c_client *client)

85. {

86.     device_unregister(&client->dev);

87. }

88. EXPORT_SYMBOL_GPL(i2c_unregister_device);

5.I2C传输、发送和接收

I2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithmmaster_xfer()函数真正驱动硬件流程。

代码清单15 i2c_transfer函数

1. /* ----------------------------------------------------

2.  * the functional interface to the i2c busses.

3.  * ----------------------------------------------------

4.  */

5. 

6. /**

7.  * i2c_transfer - execute a single or combined I2C message

8.  * @adap: Handle to I2C bus

9.  * @msgs: One or more messages to execute before STOP is issued to

10.  *  terminate the operation; each message begins with a START.

11.  * @num: Number of messages to be executed.

12.  *

13.  * Returns negative errno, else the number of messages executed.

14.  *

15.  * Note that there is no requirement that each message be sent to

16.  * the same slave address, although that is the most common model.

17.  */

18. int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

19. {

20.     unsigned long orig_jiffies;

21.     int ret, try;

22. 

23.     /* REVISIT the fault reporting model here is weak:

24.      *

25.      *  - When we get an error after receiving N bytes from a slave,

26.      *    there is no way to report "N".

27.      *

28.      *  - When we get a NAK after transmitting N bytes to a slave,

29.      *    there is no way to report "N" ... or to let the master

30.      *    continue executing the rest of this combined message, if

31.      *    that's the appropriate response.

32.      *

33.      *  - When for example "num" is two and we successfully complete

34.      *    the first message but get an error part way through the

35.      *    second, it's unclear whether that should be reported as

36.      *    one (discarding status on the second message) or errno

37.      *    (discarding status on the first one).

38.      */

39. 

40.     if (adap->algo->master_xfer) {

41. #ifdef DEBUG

42.         for (ret = 0; ret < num; ret++) {

43.             dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "

44.                 "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)

45.                 ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,

46.                 (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");

47.         }

48. #endif

49. 

50.         if (in_atomic() || irqs_disabled()) {

51.             ret = i2c_trylock_adapter(adap);

52.             if (!ret)

53.                 /* I2C activity is ongoing. */

54.                 return -EAGAIN;

55.         } else {

56.             i2c_lock_adapter(adap);

57.         }

58. 

59.         /* Retry automatically on arbitration loss */

60.         orig_jiffies = jiffies;

61.         for (ret = 0, try = 0; try <= adap->retries; try++) {

62.             ret = adap->algo->master_xfer(adap, msgs, num);

63.             if (ret != -EAGAIN)

64.                 break;

65.             if (time_after(jiffies, orig_jiffies + adap->timeout))

66.                 break;

67.         }

68.         i2c_unlock_adapter(adap);

69. 

70.         return ret;

71.     } else {

72.         dev_dbg(&adap->dev, "I2C level transfers not supported\n");

73.         return -EOPNOTSUPP;

74.     }

75. }

76. EXPORT_SYMBOL(i2c_transfer);

代码清单16 i2c_master_send函数

1. /**

2.  * i2c_master_send - issue a single I2C message in master transmit mode

3.  * @client: Handle to slave device

4.  * @buf: Data that will be written to the slave

5.  * @count: How many bytes to write, must be less than 64k since msg.len is u16

6.  *

7.  * Returns negative errno, or else the number of bytes written.

8.  */

9. int i2c_master_send(const struct i2c_client *client, const char *buf, int count)

10. {

11.     int ret;

12.     struct i2c_adapter *adap = client->adapter;

13.     struct i2c_msg msg;

14. 

15.     msg.addr = client->addr;

16.     msg.flags = client->flags & I2C_M_TEN;

17.     msg.len = count;

18.     msg.buf = (char *)buf;

19. 

20.     ret = i2c_transfer(adap, &msg, 1);

21. 

22.     /* If everything went ok (i.e. 1 msg transmitted), return #bytes

23.        transmitted, else error code. */

24.     return (ret == 1) ? count : ret;

25. }

26. EXPORT_SYMBOL(i2c_master_send);

代码清单17 i2c_master_recv函数

1. /**

2.  * i2c_master_recv - issue a single I2C message in master receive mode

3.  * @client: Handle to slave device

4.  * @buf: Where to store data read from slave

5.  * @count: How many bytes to read, must be less than 64k since msg.len is u16

6.  *

7.  * Returns negative errno, or else the number of bytes read.

8.  */

9. int i2c_master_recv(const struct i2c_client *client, char *buf, int count)

10. {

11.     struct i2c_adapter *adap = client->adapter;

12.     struct i2c_msg msg;

13.     int ret;

14. 

15.     msg.addr = client->addr;

16.     msg.flags = client->flags & I2C_M_TEN;

17.     msg.flags |= I2C_M_RD;

18.     msg.len = count;

19.     msg.buf = buf;

20. 

21.     ret = i2c_transfer(adap, &msg, 1);

22. 

23.     /* If everything went ok (i.e. 1 msg transmitted), return #bytes

24.        transmitted, else error code. */

25.     return (ret == 1) ? count : ret;

26. }

27. EXPORT_SYMBOL(i2c_master_recv);

i2c_transfer()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer函数分别完成一条写消息和一条读消息

I2C总线驱动

(1)I2c总线驱动模块的加载函数要完成两个工作。

l 第一个是初始化i2c适配器所使用的硬件资源,如申请I/O地址、中断号等。

l 第二个是通过i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adapter数据结构的成员已经被xxx适配器的相应的函数指针所初始化。

(2)I2C总线驱动模块的卸载函数要完成的工作与加载函数相反。

l 释放I2C适配器所使用的硬件资源,如释放I/O地址,中断号等。

l 通过i2c_del_adapter()删除i2c_adapter的函数数据结构。

代码清单18所示为I2C适配器驱动的模块加载和卸载函数的模板。

代码清单18 I2C总线驱动的模板加载和卸载函数模板

1. static int __init i2c_adap_xxx_init(void)

2. {

3.     xxx_adapter_hw_init();

4.     I2c_add_adapter(&xxx_adapter)

5. }

6. subsys_initcall(i2c_adap_xxx_init);

7. 

8. static void __exit i2c_adap_xxx_exit(void)

9. {

10.     xxx_adapter_hw_free();

11.     i2c_del_adapter(&xxx_adapter);

12. }

13. module_exit(i2c_adap_xxx_exit);

上述代码中xxx_adapter_hw_init()xxx_adapter_hw_free()函数的实现都与具体的CPUI2C适配器硬件直接相关。

2.I2C总线通信方法

我们需要为特定的I2C适配器实现其通信方法,主要实现i2c_algorithmmaster_xfer()函数和functionality()函数。

Functionality()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2CI2C_FUNC_10BIT_ADDRI2C_FUNC_SMBUS_READ_BYTEI2C_FUNC_SUMBUS_WRITE_BYTE等。

Master_xfer()函数在I2C适配器上完成传递给它的i2c_msg数组中每个I2C消息,代码清单19所示为xxx设备的master_xfer()函数模板。

代码清单19 I2C总线驱动master_xfer()函数模板

1. static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num) 

2. { 

3. ...... 

4. for (i = 0; i < num; i++) { 

5. i2c_adapter_xxx_start(); /*产生起始位*/ 

6. if (msgs[i]->flags & I2C_M_RD) { /*读取*/ 

7. i2c_adapter_xxx_setaddr((msg->addr << 1) | 1); /*发送从设备地址*/ 

8. i2c_adapter_xxx_wait_ack(); /*获得从设备的ACK*/ 

9. i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len); /*读取len长度的数据到buf*/ 

10. } else { 

11. i2c_adapter_xxx_setaddr(msg->addr << 1); 

12. i2c_adapter_xxx_wait_ack(); 

13. i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len); 

14. } 

15. } 

16. i2c_adapter_xxx_stop(); /*产生停止位*/ 

17. } 

上述代码实际上给出了一个master_xfer()函数处理I2C消息数组的流程,对于数组中的每个消息,判断消息类型,若为读消息,则赋从设备地址为(msg->addr<<1|1,否则为msg->addr<<1,对每个消息产生一个开始位,紧接着传送从设备的地址,然后开始数据的发送或接收,队最后的消息还需产生一个停止位。

master_xfer()函数模板中i2c_adapter_xxx_start()i2c_adapter_xxx_setaddr()i2c_adapter_xxx_wait_ack()i2c_adapter_xxx_readbytes()i2c_adapter_xxx_stop()函数用于完成适配器底层硬件操作,与I2C适配器和CPU的具体硬件直接相关,需要由工程师根据芯片的数据手册来实现。

I2c_adapter_xxx_readbytes()用于从设备上接收一串数据,i2c_adapter_xxx_writebytes()用于向从设备写入一串数据,这两个函数的内部也会涉及I2C总线协议中的ACK应答。

master_xfer()函数的实现在形式上会有很多样,即便是linux内核源代码中已经给出了一些I2C总线驱动的master_xfer()函数,由于由不同的组织或个人完成,风格上的差别也非常大,不一定能与模板完全对应,如master_xfer()函数模板给出的消息处理顺序进行的,而有的驱动以中断方式来完成这个流程。不管具体怎么实施,流程的本质都是不变的。因为这个流程不以驱动工程师的意志为转移,最终由I2C总线硬件上的通信协议决定。

多数I2C总线驱动会定义一个xxx_i2c结构体,作为i2c_adapteralgo_data(类似“是有数据”),其中包含I2C消息数组指针、数组索引及I2C适配器algorithm访问控制用的自旋锁、等待队列等,而master_xfer()函数完成消息数组中消息的处理也可通过对xxx_i2c结构体相关成员的访问来控制。代码清单20所示为xxx_i2c结构体的定义

代码清单20 xxx_i2c结构体模板

1. struct xxx_i2c {

2.     spinlock_t          lock;

3.     wait_queue_head_t   wait;

4.     struct i2c_msg      *msg;

5.     unsigned int        msg_num;

6.     unsigned int        msg_idx;

7.     unsigned int        msg_ptr;

8.     struct i2c_adapter  adap;

9. };

对于s3c2440i2c模块而言内核中做了如下的工作:

S3c2440处理器内部集成了一个I2C控制器,通过4个寄存器就可以方便地对其进行控制,这4个寄存器如下:

l IICCONI2C控制寄存器。

l IICSTAT:I2C状态寄存器。

l IICDSI2C收发数据移位寄存器。

l IICADDI2C地址寄存器。

S3c2440处理器内部集成的I2C控制器可支持主、从两种模式,我们主要使用其主模式。通过对IICCONIICDSIICADD寄存器的操作,可

I2C总线上产生开始位、停止位、数据和地址,而传输的状态则通过IICSTAT寄存器获取。

3.s3c2440 I2C 总线驱动总体分析

s3c_2440I2C总线驱动driver/i2c/busses/i2c-s3c2410.c支持s3c24xxs3c64xxs5pc1xxs5p64xx处理器,在我们使用的3.0内核版本中,其名称任然叫2410,显然是历史原因引起的。它主要完成以下工作。

设计对应于i2c_adapter_xxx_init()模板的s3c_2440的模块加载函数和对应于i2c_adapter_xxx_exit()函数模板的模块卸载函数。

设计对应于i2c_adapter_xxx_xfer()模板的s3c_2440适配器的通信方法函数。

针对s3c24xxs3c64xxs5pc1xxs5p64xx处理器,functionality()函数s3c24xx_i2c_func()只需要简单地返回I2C_FUNC_I2C|I2C_FUNC_SUMBUS_EMUL|I2C_FUNU_PROTOCOL_MANGLING表明其支持的功能。

下图给出了s3c2440驱动中的主要函数与总线模板函数的对应关系,由于实现通信方法的方式不一样,模板的一个函数可能对应于s3c2440 I2C总线驱动的多个函数。

总线驱动模板于s3c2440 I2C总线驱动的映射

4.S3c2440 I2C适配器驱动的模板加载于卸载

I2C适配器驱动被作为一个单独的模块加载进内核,在模块的加载和卸载函数中,只需注册和注销一个platform——driver结构体,如代码清单21所示。

代码清单21 S3c2440 I2C

1. static int __init i2c_adap_s3c_init(void)

2. {

3.     return platform_driver_register(&s3c24xx_i2c_driver);

4. }

5. subsys_initcall(i2c_adap_s3c_init);

6. 

7. static void __exit i2c_adap_s3c_exit(void)

8. {

9.     platform_driver_unregister(&s3c24xx_i2c_driver);

10. }

11. module_exit(i2c_adap_s3c_exit);

代码清单22 platfrom_driver_register()platfrom_driver_unregister()函数

1. /**

2.  * platform_driver_register - register a driver for platform-level devices

3.  * @drv: platform driver structure

4.  */

5. int platform_driver_register(struct platform_driver *drv)

6. {

7.     drv->driver.bus = &platform_bus_type;

8.     if (drv->probe)

9.         drv->driver.probe = platform_drv_probe;

10.     if (drv->remove)

11.         drv->driver.remove = platform_drv_remove;

12.     if (drv->shutdown)

13.         drv->driver.shutdown = platform_drv_shutdown;

14. 

15.     return driver_register(&drv->driver);

16. }

17. EXPORT_SYMBOL_GPL(platform_driver_register);

18. 

19. /**     

20.  * platform_driver_unregister - unregister a driver for platform-level devices

21.  * @drv: platform driver structure

22.  */

23. void platform_driver_unregister(struct platform_driver *drv)

24. {   

25.     driver_unregister(&drv->driver);

26. }   

27. EXPORT_SYMBOL_GPL(platform_driver_unregister);

Platfrom_driver结构体包含了具体适配器的probe()函数、remove()函数、resume()函数指针等信息,它需要被定义和赋值,如代码清单23所示。

代码清单23 platfrom_driver结构体

1. static struct platform_driver s3c24xx_i2c_driver = {

2.     .probe      = s3c24xx_i2c_probe,

3.     .remove     = s3c24xx_i2c_remove,

4.     .id_table   = s3c24xx_driver_ids,

5.     .driver     = {

6.         .owner  = THIS_MODULE,

7.         .name   = "s3c-i2c",

8.         .pm = S3C24XX_DEV_PM_OPS,

9.     },

10. };

当通过linux内核源代码/drivers/base/platform.c文件中定义platform_driver_register()函数注册platfrom_driver结构体时,其中probe指针指向s3c24xx_i2c_probe()函数将被调用,以初始化适配器硬件。s3c24xx_i2c_init()函数会调用函数。

代码清单24 s3c24xx_i2c_init()函数

1. /* s3c24xx_i2c_init

2.  *

3.  * initialise the controller, set the IO lines and frequency

4. */

5. 

6. static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)

7. {

8.     unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;

9.     struct s3c2410_platform_i2c *pdata;

10.     unsigned int freq;

11. 

12.     /* get the plafrom data */

13. 

14.     pdata = i2c->dev->platform_data;

15. 

16.     /* inititalise the gpio */

17. 

18.     if (pdata->cfg_gpio)

19.         pdata->cfg_gpio(to_platform_device(i2c->dev));

20. 

21.     /* write slave address */

22. 

23.     writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);

24. 

25.     dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);

26. 

27.     writel(iicon, i2c->regs + S3C2410_IICCON);

28. 

29.     /* we need to work out the divisors for the clock... */

30. 

31.     if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {

32.         writel(0, i2c->regs + S3C2410_IICCON);

33.         dev_err(i2c->dev, "cannot meet bus frequency required\n");

34.         return -EINVAL;

35.     }

36. 

37.     /* todo - check that the i2c lines aren't being dragged anywhere */

38. 

39.     dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);

40.     dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);

41. 

42.     return 0;

43. }

44. 

代码清单25 s3c24xx_i2c_probe()函数

1. /* s3c24xx_i2c_probe

2.  *

3.  * called by the bus driver when a suitable device is found

4. */

5. 

6. static int s3c24xx_i2c_probe(struct platform_device *pdev)

7. {

8.     struct s3c24xx_i2c *i2c;

9.     struct s3c2410_platform_i2c *pdata;

10.     struct resource *res;

11.     int ret;

12. 

13.     pdata = pdev->dev.platform_data;

14.     if (!pdata) {

15.         dev_err(&pdev->dev, "no platform data\n");

16.         return -EINVAL;

17.     }

18. 

19.     i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);

20.     if (!i2c) {

21.         dev_err(&pdev->dev, "no memory for state\n");

22.         return -ENOMEM;

23.     }

24. 

25.     strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));

26.     i2c->adap.owner   = THIS_MODULE;

27.     i2c->adap.algo    = &s3c24xx_i2c_algorithm;

28.     i2c->adap.retries = 2;

29.     i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;

30.     i2c->tx_setup     = 50;

31. 

32.     spin_lock_init(&i2c->lock);

33.     init_waitqueue_head(&i2c->wait);

34. 

35.     /* find the clock and enable it */

36. 

37.     i2c->dev = &pdev->dev;

38.     i2c->clk = clk_get(&pdev->dev, "i2c");

39.     if (IS_ERR(i2c->clk)) {

40.         dev_err(&pdev->dev, "cannot get clock\n");

41.         ret = -ENOENT;

42.         goto err_noclk;

43.     }

44. 

45.     dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);

46. 

47.     clk_enable(i2c->clk);

48. 

49.     /* map the registers */

50. 

51.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

52.     if (res == NULL) {

53.         dev_err(&pdev->dev, "cannot find IO resource\n");

54.         ret = -ENOENT;

55.         goto err_clk;

56.     }

57. 

58.     i2c->ioarea = request_mem_region(res->start, resource_size(res),

59.                      pdev->name);

60. 

61.     if (i2c->ioarea == NULL) {

62.         dev_err(&pdev->dev, "cannot request IO\n");

63.         ret = -ENXIO;

64.         goto err_clk;

65.     }

66. 

67.     i2c->regs = ioremap(res->start, resource_size(res));

68. 

69.     if (i2c->regs == NULL) {

70.         dev_err(&pdev->dev, "cannot map IO\n");

71.         ret = -ENXIO;

72.         goto err_ioarea;

73.     }

74. 

75.     dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",

76.         i2c->regs, i2c->ioarea, res);

77. 

78.     /* setup info block for the i2c core */

79. 

80.     i2c->adap.algo_data = i2c;

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

82. 

83.     /* initialise the i2c controller */

84. 

85.     ret = s3c24xx_i2c_init(i2c);

86.     if (ret != 0)

87.         goto err_iomap;

88. 

89.     /* find the IRQ for this unit (note, this relies on the init call to

90.      * ensure no current IRQs pending

91.      */

92. 

93.     i2c->irq = ret = platform_get_irq(pdev, 0);

94.     if (ret <= 0) {

95.         dev_err(&pdev->dev, "cannot find IRQ\n");

96.         goto err_iomap;

97.     }

98. 

99.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,

100.               dev_name(&pdev->dev), i2c);

101. 

102.     if (ret != 0) {

103.         dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);

104.         goto err_iomap;

105.     }

106. 

107.     ret = s3c24xx_i2c_register_cpufreq(i2c);

108.     if (ret < 0) {

109.         dev_err(&pdev->dev, "failed to register cpufreq notifier\n");

110.         goto err_irq;

111.     }

112. 

113.     /* Note, previous versions of the driver used i2c_add_adapter()

114.      * to add the bus at any number. We now pass the bus number via

115.      * the platform data, so if unset it will now default to always

116.      * being bus 0.

117.      */

118. 

119.     i2c->adap.nr = pdata->bus_num;

120. 

121.     ret = i2c_add_numbered_adapter(&i2c->adap);

122.     if (ret < 0) {

123.         dev_err(&pdev->dev, "failed to add bus to i2c core\n");

124.         goto err_cpufreq;

125.     }

126. 

127.     platform_set_drvdata(pdev, i2c);

128. 

129.     dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));

130.     clk_disable(i2c->clk);

131.     return 0;

132. 

133.  err_cpufreq:

134.     s3c24xx_i2c_deregister_cpufreq(i2c);

135. 

136.  err_irq:

137.     free_irq(i2c->irq, i2c);

138. 

139.  err_iomap:

140.     iounmap(i2c->regs);

141. 

142.  err_ioarea:

143.     release_resource(i2c->ioarea);

144.     kfree(i2c->ioarea);

145. 

146.  err_clk:

147.     clk_disable(i2c->clk);

148.     clk_put(i2c->clk);

149. 

150.  err_noclk:

151.     kfree(i2c);

152.     return ret;

153. }

上述代码中的主体工作是使能硬件并且申请I2C适配器使用I/O地址、中断号等,在这些工作都完成无误后,通过I2C核心提供i2c_add_adapter函数添加这个适配器。当处理器包含多个I2C控制器时,我们通过板文件定义的platform数据中bus_num进行区分。

s3c24xx_i2c_probe()函数完全相反的功能的函数是s3c24xx_i2c_remove()函数,它在适配器模块函数调用platform_driver_unregister函数是通过platfrom_driverremove指针方式被调用.

s3c2440 I2C 总线通信方法

I2C适配器对应的i2c_algorithm结构体实例为s3c24xx_i2c_algorithm

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {

    .master_xfer        = s3c24xx_i2c_xfer,

     .functionality      = s3c24xx_i2c_func,

 };

上述代码指定了 s3c2440 I2C 总线通信传输函数 s3c24xx_i2c_xfer(), 这个函数非常关键,所有 I2C 总线上对设备的访问最终应该由它来完成,下面为这个重要函数以及其依赖的s3c24xx_i2c_doxfer()函数和s3c24xx_i2c_message_start()函数的源代码。

s3c2440 I2C总线驱动的master_xfer函数

1. /* s3c24xx_i2c_xfer

2.  *

3.  * first port of call from the i2c bus code when an message needs

4.  * transferring across the i2c bus.

5. */

6. 

7. static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,

8.             struct i2c_msg *msgs, int num)

9. {

10.     struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;

11.     int retry;

12.     int ret;

13. 

14.     clk_enable(i2c->clk);

15. 

16.     for (retry = 0; retry < adap->retries; retry++) {

17. 

18.         ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

19. 

20.         if (ret != -EAGAIN) {

21.             clk_disable(i2c->clk);

22.             return ret;

23.         }

24. 

25.         dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

26. 

27.         udelay(100);

28.     }

29. 

30.     clk_disable(i2c->clk);

31.     return -EREMOTEIO;

32. }

s3c24xx_i2c_xfer()函数调用s3c24xx_i2c_doxfer()函数传输I2C消息,第13行的循环意味着最多可以重试adap->retres次。

代码清单30 s3c24xx_i2c_doxfer()函数

1. /* s3c24xx_i2c_doxfer

2.  *

3.  * this starts an i2c transfer

4. */

5. 

6. static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,

7.                   struct i2c_msg *msgs, int num)

8. {

9.     unsigned long iicstat, timeout;

10.     int spins = 20;

11.     int ret;

12. 

13.     if (i2c->suspended)

14.         return -EIO;

15. 

16.     ret = s3c24xx_i2c_set_master(i2c);

17.     if (ret != 0) {

18.         dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);

19.         ret = -EAGAIN;

20.         goto out;

21.     }

22. 

23.     spin_lock_irq(&i2c->lock);

24. 

25.     i2c->msg     = msgs;

26.     i2c->msg_num = num;

27.     i2c->msg_ptr = 0;

28.     i2c->msg_idx = 0;

29.     i2c->state   = STATE_START;

30. 

31.     s3c24xx_i2c_enable_irq(i2c);

32.     s3c24xx_i2c_message_start(i2c, msgs);

33.     spin_unlock_irq(&i2c->lock);

34. 

35.     timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);

36. 

37.     ret = i2c->msg_idx;

38. 

39.     /* having these next two as dev_err() makes life very

40.      * noisy when doing an i2cdetect */

41. 

42.     if (timeout == 0)

43.         dev_dbg(i2c->dev, "timeout\n");

44.     else if (ret != num)

45.         dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);

46. 

47.     /* ensure the stop has been through the bus */

48. 

49.     dev_dbg(i2c->dev, "waiting for bus idle\n");

50. 

51.     /* first, try busy waiting briefly */

52.     do {

53.         iicstat = readl(i2c->regs + S3C2410_IICSTAT);

54. } while ((iicstat & S3C2410_IICSTAT_START) && --spins);

55. 

56.     /* if that timed out sleep */

57.     if (!spins) {

58.         msleep(1);

59.         iicstat = readl(i2c->regs + S3C2410_IICSTAT);

60.     }

61. 

62.     if (iicstat & S3C2410_IICSTAT_START)

63.         dev_warn(i2c->dev, "timeout waiting for bus idle\n");

64. 

65.  out:

66.     return ret;

67. }

s3c24xx_i2c_doxfer()首先将s3c2440I2C适配器设置为I2C主设备,其后初始化s3c24xx_i2c结构体,使能I2C中断,并调用s3c24xx_i2c_message_start()函数启动I2C消息的传输。

代码清单31 s3c24xx_i2c_message_start()函数

1. /* s3c24xx_i2c_message_start

2.  *

3.  * put the start of a message onto the bus

4. */

5. 

6. static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,

7.                       struct i2c_msg *msg)

8. {

9.     unsigned int addr = (msg->addr & 0x7f) << 1;

10.     unsigned long stat;

11.     unsigned long iiccon;

12. 

13.     stat = 0;

14.     stat |=  S3C2410_IICSTAT_TXRXEN;

15. 

16.     if (msg->flags & I2C_M_RD) {

17.         stat |= S3C2410_IICSTAT_MASTER_RX;

18.         addr |= 1;

19.     } else

20.         stat |= S3C2410_IICSTAT_MASTER_TX;

21. 

22.     if (msg->flags & I2C_M_REV_DIR_ADDR)

23.         addr ^= 1;

24. 

25.     /* todo - check for wether ack wanted or not */

26.     s3c24xx_i2c_enable_ack(i2c);

27. 

28.     iiccon = readl(i2c->regs + S3C2410_IICCON);

29.     writel(stat, i2c->regs + S3C2410_IICSTAT);

30. 

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

32.     writeb(addr, i2c->regs + S3C2410_IICDS);

33. 

34.     /* delay here to ensure the data byte has gotten onto the bus

35.      * before the transaction is started */

36. 

37.     ndelay(i2c->tx_setup);

38.     dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);

39.     writel(iiccon, i2c->regs + S3C2410_IICCON);

40. 

41.     stat |= S3C2410_IICSTAT_START;

42.     writel(stat, i2c->regs + S3C2410_IICSTAT);

43. }

s3c24xx_i2c_message_start()函数写s3c2440适配器对应的控制寄存器,向I2C从设备传递开始位和从设备地址。

上述代码只是启动了I2C消息数组的传输周期,并没有完整实现algorithm master_xfer时序的流程。这个流程的完整实现需要借助I2C适配器上的中断来步步推进。

下面为 s3c2440 I2C 适配器中断处理函数以及其依赖的 i2c_s3c_irq_nextbyte() 函数的源码。

1. /* i2c_s3c_irq_nextbyte

2.  *

3.  * process an interrupt and work out what to do

4.  */

5. 

6. static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)

7. {

8.     unsigned long tmp;

9.     unsigned char byte;

10.     int ret = 0;

11. 

12.     switch (i2c->state) {

13. 

14.     case STATE_IDLE:

15.         dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);

16.         goto out;

17. 

18.     case STATE_STOP:

19.         dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__);

20.         s3c24xx_i2c_disable_irq(i2c);

21.         goto out_ack;

22. 

23.     case STATE_START:

24.         /* last thing we did was send a start condition on the

25.          * bus, or started a new i2c message

26.          */

27. 

28.         if (iicstat & S3C2410_IICSTAT_LASTBIT &&

29.             !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {

30.             /* ack was not received... */

31. 

32.             dev_dbg(i2c->dev, "ack was not received\n");

33.             s3c24xx_i2c_stop(i2c, -ENXIO);

34.             goto out_ack;

35.         }

36. 

37.         if (i2c->msg->flags & I2C_M_RD)

38.             i2c->state = STATE_READ;

39.         else

40.             i2c->state = STATE_WRITE;

41. 

42.         /* terminate the transfer if there is nothing to do

43.          * as this is used by the i2c probe to find devices. */

44. 

45.         if (is_lastmsg(i2c) && i2c->msg->len == 0) {

46.             s3c24xx_i2c_stop(i2c, 0);

47.             goto out_ack;

48.         }

49. 

50.         if (i2c->state == STATE_READ)

51.             goto prepare_read;

52. 

53.         /* fall through to the write state, as we will need to

54.          * send a byte as well */

55. 

56.     case STATE_WRITE:

57.         /* we are writing data to the device... check for the

58.          * end of the message, and if so, work out what to do

59.          */

60. 

61.         if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {

62.             if (iicstat & S3C2410_IICSTAT_LASTBIT) {

63.                 dev_dbg(i2c->dev, "WRITE: No Ack\n");

64. 

65.                 s3c24xx_i2c_stop(i2c, -ECONNREFUSED);

66.                 goto out_ack;

67.             }

68.         }

69. 

70.  retry_write:

71. 

72.         if (!is_msgend(i2c)) {

73.             byte = i2c->msg->buf[i2c->msg_ptr++];

74.             writeb(byte, i2c->regs + S3C2410_IICDS);

75. 

76.             /* delay after writing the byte to allow the

77.              * data setup time on the bus, as writing the

78.              * data to the register causes the first bit

79.              * to appear on SDA, and SCL will change as

80.              * soon as the interrupt is acknowledged */

81. 

82.             ndelay(i2c->tx_setup);

83. 

84.         } else if (!is_lastmsg(i2c)) {

85.             /* we need to go to the next i2c message */

86. 

87.             dev_dbg(i2c->dev, "WRITE: Next Message\n");

88. 

89.             i2c->msg_ptr = 0;

90.             i2c->msg_idx++;

91.             i2c->msg++;

92. 

93.             /* check to see if we need to do another message */

94.             if (i2c->msg->flags & I2C_M_NOSTART) {

95. 

96.                 if (i2c->msg->flags & I2C_M_RD) {

97.                     /* cannot do this, the controller

98.                      * forces us to send a new START

99.                      * when we change direction */

100. 

101.                     s3c24xx_i2c_stop(i2c, -EINVAL);

102.                 }

103. 

104.                 goto retry_write;

105.             } else {

106.                 /* send the new start */

107.                 s3c24xx_i2c_message_start(i2c, i2c->msg);

108.                 i2c->state = STATE_START;

109.             }

110. 

111.         } else {

112.             /* send stop */

113. 

114.             s3c24xx_i2c_stop(i2c, 0);

115.         }

116.         break;

117. 

118.     case STATE_READ:

119.         /* we have a byte of data in the data register, do

120.          * something with it, and then work out wether we are

121.          * going to do any more read/write

122.          */

123. 

124.         byte = readb(i2c->regs + S3C2410_IICDS);

125.         i2c->msg->buf[i2c->msg_ptr++] = byte;

126. 

127.  prepare_read:

128.         if (is_msglast(i2c)) {

129.             /* last byte of buffer */

130. 

131.             if (is_lastmsg(i2c))

132.                 s3c24xx_i2c_disable_ack(i2c);

133. 

134.         } else if (is_msgend(i2c)) {

135.             /* ok, we've read the entire buffer, see if there

136.              * is anything else we need to do */

137. 

138.             if (is_lastmsg(i2c)) {

139.                 /* last message, send stop and complete */

140.                 dev_dbg(i2c->dev, "READ: Send Stop\n");

141. 

142.                 s3c24xx_i2c_stop(i2c, 0);

143.             } else {

144.                 /* go to the next transfer */

145.                 dev_dbg(i2c->dev, "READ: Next Transfer\n");

146. 

147.                 i2c->msg_ptr = 0;

148.                 i2c->msg_idx++;

149.                 i2c->msg++;

150.             }

151.         }

152. 

153.         break;

154.     }

155. 

156.     /* acknowlegde the IRQ and get back on with the work */

157. 

158.  out_ack:

159.     tmp = readl(i2c->regs + S3C2410_IICCON);

160.     tmp &= ~S3C2410_IICCON_IRQPEND;

161.     writel(tmp, i2c->regs + S3C2410_IICCON);

162.  out:

163.     return ret;

164. }

中断处理函数s3c24xx_i2c_irq()主要通过调用i2c_s3c_irq_nextbyte()函数进行传输工作的进一步推进。I2c_s3c_irq_nextbyte()函数通过switchi2c->state)的不同状态进行处理,在每种状态下,先检查i2c->state的状态与硬件寄存器应该处于的状态是否一致,如果不一致,则证明有误,直接返回。当I2C处于读状态STATE_READ或是写状态STATE_WRITE时,通过is_lastmsg()函数判断是否传输的最后一条I2C消息,如果是,则产生停止位,否则通过i2c->msg_idx++i2c->msg++推进到下一条消息。

Linux I2C 设备驱动

I2C设备驱动要使用i2c_driveri2c_client数据结构并填充i2c_driver中的成员函数。I2c_client一般被包含在设备的私有信息结构体yyy_data中,而i2c_driver则适合被定义为全局变量并初始化,代码清单33所示为已被初始化的i2c_driver

代码清单33 已被初始化的i2c_driver

1. static struct i2c_driver yyy_driver= {

2. .driver = {

3. .name = “yyy”,

4. },

5. .probe = yyy_probe,

6. .remove = yyy_remove,

7. .id_table = yyy_id,

8. };

Linux I2C 设备驱动的模块加载与卸载

I2C设备驱动的模块加载函数通用的方法是在I2C设备驱动模块加载函数进行通过I2C核心的i2c_add_driver()函数添加i2c_driver的工作,而在模块卸载函数中需要做相反的工作:通过I2C核心的i2c_del_driver()函数删除i2c_driver。代码清单34所示为I2C设备驱动的加载与卸载函数模板

代码清单34 I2C设备驱动的加载与卸载函数模板

1. Static int __init yyy_init(void)

2. {

3. return i2c_add_driver(&yyy_driver);

4. };

5. void __exit yyy_exit(void)

6. {

7. i2c_del_driver(&yyy_driver);

8. }

Linux I2C设备驱动的数据传输

I2C设备上读写数据的时序和数据通常通过i2c_msg数组组织,最后i2c_transfer()函数完成,代码清单35所示为一个读取指定偏移offs寄存器的例子。

代码清单35 I2C设备驱动数据传输范例

1. struct i2c_msg msg[2];

2. /*第一条消息是写消息*/

3. msg[0].addr = client->addr;

4. msg[0].flags = 0;

5. msg[0].len = 1;

6. msg[0].buf = &offs;

7. /*第二条消息是读消息*/

8. msg[1].addr = client->addr;

9. msg[1].flags = I2C_M_RD;

10. msg[1].len = sizeof(buf);

11. msg[1].buf = &buf[0];

i2c_transferclient->adapter, msg, 2;

Linuxi2c-dev.c文件分析

I2c_dev.c文件完全可以被看作一个I2C设备驱动,不过,它实现的一个i2c_client是虚拟、临时的、随着设备文件的打开而产生,并随着设备文件的关闭而撤销,并没有被添加到i2c_adapterclient链表中。i2c-dev.c针对每个I2C适配器生成一个主设备号为89的设备文件,实现了i2c_driver的成员函数以及文件操作接口,所以i2c-dev.c的主体是“i2c_driver成员函数+字符设备驱动”。

i2c-dev.c中提供i2cdev_read()i2cdev_write()函数来对应用户空间要使用的read()write()文件操作接口,这两个函数分别调用I2C核心的i2c_master_recv()i2c_master_send()函数来构造一条I2C消息并引发适配器algorithm通信函数的调用,完成消息的传输,对应于图所示的时序。但是很遗憾,大多数稍微复杂一点I2C设备的读写流程并不对应于一条消息,往往需要两条甚至跟多的消息来进行一次读写周期(即如图所示的重复开始位RepStart模式),这种情况下,在应用层仍然调用read()write()文件API来读写I2C设备,将不能正确地读写。许多工程师碰到过类似的问题,往往经过相当长时间的调试都没法解决I2C设备的读写,连错误的原因也无法找到,显然是对i2cdev_read()i2cdev_write()函数的作用有所误解。

 

                             i2cdev_read()i2cdev_write()函数对应的时序

 

                                     RepStart模式

鉴于上述的原因,i2c-dev.ci2cdev_read()i2cdev_write()函数不具备太强的通用性,没有太大的实用价值,只能实用于非RepStart模式的情况。对于两条以上消息组成的读写,在用户空间需要组织i2c_msg消息数组并调用I2C_RDWRIOCTL命令。代码清单36所示i2cdev_ioctl()函数的框架。

1. static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

2. {

3.     struct i2c_client *client = file->private_data;

4.     unsigned long funcs;

5.     dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",

6.         cmd, arg);

7.     switch (cmd) {

8.     case I2C_SLAVE:

9.     case I2C_SLAVE_FORCE:

10.         /* NOTE:  devices set up to work with "new style" drivers

11.          * can't use I2C_SLAVE, even when the device node is not

12.          * bound to a driver.  Only I2C_SLAVE_FORCE will work.

13.          *

14.          * Setting the PEC flag here won't affect kernel drivers,

15.          * which will be using the i2c_client node registered with

16.          * the driver model core.  Likewise, when that client has

17.          * the PEC flag already set, the i2c-dev driver won't see

18.          * (or use) this setting.

19.          */

20.         if ((arg > 0x3ff) ||

21.             (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))

22.             return -EINVAL;

23.         if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))

24.             return -EBUSY;

25.         /* REVISIT: address could become busy later */

26.         client->addr = arg;

27.         return 0;

28.     case I2C_TENBIT:

29.         if (arg)

30.             client->flags |= I2C_M_TEN;

31.         else

32.             client->flags &= ~I2C_M_TEN;

33.         return 0;

34.     case I2C_PEC:

35.         if (arg)

36.             client->flags |= I2C_CLIENT_PEC;

37.         else

38.             client->flags &= ~I2C_CLIENT_PEC;

39.         return 0;

40.     case I2C_FUNCS:

41.         funcs = i2c_get_functionality(client->adapter);

42.         return put_user(funcs, (unsigned long __user *)arg);

43.     case I2C_RDWR:

44.         return i2cdev_ioctl_rdrw(client, arg);

45.     case I2C_SMBUS:

46.         return i2cdev_ioctl_smbus(client, arg);

47.     case I2C_RETRIES:

48.         client->adapter->retries = arg;

49.         break;

50. case I2C_TIMEOUT:

51.         /* For historical reasons, user-space sets the timeout

52.          * value in units of 10 ms.

53.          */

54.         client->adapter->timeout = msecs_to_jiffies(arg * 10);

55.         break;

56.     default:

57.         /* NOTE:  returning a fault code here could cause trouble

58.          * in buggy userspace code.  Some old kernel bugs returned

59.          * zero in this case, and userspace code might accidentally

60.          * have depended on that bug.

61.          */

62.         return -ENOTTY;

63.     }

64.     return 0;

65. }

常用的IOCTL包含I2C_SLAVE(设备从设备地址)、I2C_RETRIES(没有收到设备ACK情况下的重试次数,默认为1)、I2C_TIMEOUT以及I2C_RDWR



AT24C02 EEPROMI2C设备驱动实例

目标平台:FL2440

内核版本:linux-3.0

CPU:s3c2440

EEPROM:AT24C02

所有的代码均位于内核源码:linux/drivers/misc/eeprom/at24.c中

设备驱动绑定

熟悉I2C驱动架构的可能会知道I2C驱动的match函数,该函数将使用id表(struct i2c_device_id)和i2c设备(struct i2c_client)进行匹配,判断是否有name字段相同,如果相同则匹配完成,即可完成设备和驱动的绑定,接着便会调用驱动提供的probe方法。我们来看下驱动提供的id表。

  1. static struct i2c_driver at24_driver = {  
  2.     .driver = {  
  3.         .name = "at24",  
  4.         .owner = THIS_MODULE,  
  5.     },  
  6.     .probe = at24_probe,  
  7.     .remove = __devexit_p(at24_remove),  
  8.     .id_table = at24_ids,  
  9. };  
static struct i2c_driver at24_driver = {
	.driver = {
		.name = "at24",
		.owner = THIS_MODULE,
	},
	.probe = at24_probe,
	.remove = __devexit_p(at24_remove),
	.id_table = at24_ids,
};
驱动提供的id为at24_ids,如下

       static const struct i2c_device_id at24_ids[] = {  

  1.     /* needs 8 addresses as A0-A2 are ignored */  
  2.     { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },  
  3.     /* old variants can't be handled with this generic entry! */  
  4.     { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },  
  5.     { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },  
  6.     /* spd is a 24c02 in memory DIMMs */  
  7.     { "spd", AT24_DEVICE_MAGIC(2048 / 8,  
  8.         AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },  
  9.     { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },  
  10.     /* 24rf08 quirk is handled at i2c-core */  
  11.     { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },  
  12.     { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },  
  13.     { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },  
  14.     { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },  
  15.     { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },  
  16.     { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },  
  17.     { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },  
  18.     { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },  
  19.     { "at24", 0 },  
  20.     { /* END OF LIST */ }  
  21. };  
static const struct i2c_device_id at24_ids[] = {
	/* needs 8 addresses as A0-A2 are ignored */
	{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
	/* old variants can't be handled with this generic entry! */
	{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
	{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
	/* spd is a 24c02 in memory DIMMs */
	{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
		AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
	{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
	/* 24rf08 quirk is handled at i2c-core */
	{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
	{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
	{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
	{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
	{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
	{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
	{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
	{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
	{ "at24", 0 },
	{ /* END OF LIST */ }
};

结构体成员的第一个参数即为name,表示设备的名字。第二个参数,在该驱动中,为一个幻术(magic),通过AT24_DEVICE_MAGIC宏计算。

宏第一个参数为eeprom的大小,第二参数为一些标志位。我们看下这个宏:

  1. #define AT24_SIZE_BYTELEN 5   
  2. #define AT24_SIZE_FLAGS 8   
  3.   
  4. /* create non-zero magic value for given eeprom parameters */  
  5. #define AT24_DEVICE_MAGIC(_len, _flags)         \   
  6.     ((1 << AT24_SIZE_FLAGS | (_flags))        \  
  7.         << AT24_SIZE_BYTELEN | ilog2(_len))  
#define AT24_SIZE_BYTELEN 5
#define AT24_SIZE_FLAGS 8

/* create non-zero magic value for given eeprom parameters */
#define AT24_DEVICE_MAGIC(_len, _flags) 		\
	((1 << AT24_SIZE_FLAGS | (_flags)) 		\
	    << AT24_SIZE_BYTELEN | ilog2(_len))

在这个表中,针对这里讲解的24c02,其大小为256字节,标志位为空。

probe函数

当i2c总线完成设备驱动绑定后,就会调用probe方法了。具体看下这个函数。

  1. static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  
  2. {  
  3.     struct at24_platform_data chip;  
  4.     bool writable;  
  5.     bool use_smbus = false;  
  6.     struct at24_data *at24;  
  7.     int err;  
  8.     unsigned i, num_addresses;  
  9.     kernel_ulong_t magic;  
  10.   
  11.     /* 获取板级设备信息*/  
  12.     if (client->dev.platform_data) {  
  13.         chip = *(struct at24_platform_data *)client->dev.platform_data;  
  14.     } else {  
  15.         /* 没有板级设备信息,也没有driver_data,直接出错*/  
  16.         if (!id->driver_data) {  
  17.             err = -ENODEV;  
  18.             goto err_out;  
  19.         }  
  20.         magic = id->driver_data;  
  21.         chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));  
  22.         magic >>= AT24_SIZE_BYTELEN;  
  23.         chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);  
  24.         /* 
  25.          * This is slow, but we can't know all eeproms, so we better 
  26.          * play safe. Specifying custom eeprom-types via platform_data 
  27.          * is recommended anyhow. 
  28.          */  
  29.         chip.page_size = 1;  
  30.   
  31.         chip.setup = NULL;  
  32.         chip.context = NULL;  
  33.     }  
  34.   
  35.     /* 检查参数, 
  36.         byte_len和page_size必须为2的幂,不是则打印警告*/  
  37.     if (!is_power_of_2(chip.byte_len))  
  38.         dev_warn(&client->dev,  
  39.             "byte_len looks suspicious (no power of 2)!\n");  
  40.     if (!is_power_of_2(chip.page_size))  
  41.         dev_warn(&client->dev,  
  42.             "page_size looks suspicious (no power of 2)!\n");  
  43.   
  44.     /* Use I2C operations unless we're stuck with SMBus extensions. */  
  45.     /* 检查是否支持I2C协议, 
  46.         如果不支持,则检查是否使用SMBUS协议*/  
  47.     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {  
  48.         /* 不支持I2C协议,但是使用16位地址,出错*/  
  49.         if (chip.flags & AT24_FLAG_ADDR16) {  
  50.             err = -EPFNOSUPPORT;  
  51.             goto err_out;  
  52.         }  
  53.         /*  不支持I2C协议,使用8位地址, 
  54.            但是不支持I2C_FUNC_SMBUS_READ_I2C_BLOCK,出错*/  
  55.         if (!i2c_check_functionality(client->adapter,  
  56.                 I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {  
  57.             err = -EPFNOSUPPORT;  
  58.             goto err_out;  
  59.         }  
  60.         use_smbus = true/*使用 SMBUS协议*/  
  61.     }  
  62.   
  63.     /*是否使用8个地址,根据id表, 
  64.         目前只有AT24C00使用8个地址,其他都为1个*/  
  65.     if (chip.flags & AT24_FLAG_TAKE8ADDR)  
  66.         num_addresses = 8;  
  67.     else  
  68.         /* 24C02需要1个地址,24C04为2个,以此类推*/  
  69.         num_addresses = DIV_ROUND_UP(chip.byte_len,  
  70.             (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);  
  71.   
  72.     /* 分配struct at24_data,同时根据地址个数分配struct i2c_client*/  
  73.     at24 = kzalloc(sizeof(struct at24_data) +  
  74.         num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);  
  75.     if (!at24) {  
  76.         err = -ENOMEM;  
  77.         goto err_out;  
  78.     }  
  79.   
  80.     /* 初始化struct at24_data*/  
  81.     mutex_init(&at24->lock);  
  82.     at24->use_smbus = use_smbus;  
  83.     at24->chip = chip;  
  84.     at24->num_addresses = num_addresses;  
  85.   
  86.     /* 
  87.      * Export the EEPROM bytes through sysfs, since that's convenient. 
  88.      * By default, only root should see the data (maybe passwords etc) 
  89.      */  
  90.      /* 设置bin_attribute字段,二进制文件名为eeprom, 
  91.          通过它即可读写设备 */  
  92.     at24->bin.attr.name = "eeprom";  
  93.     at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;  
  94.     at24->bin.read = at24_bin_read;  
  95.     at24->bin.size = chip.byte_len;  
  96.   
  97.     at24->macc.read = at24_macc_read;  /***  先忽略***/  
  98.   
  99.     /* 判断设备是否可写*/  
  100.     writable = !(chip.flags & AT24_FLAG_READONLY);  
  101.     if (writable) {  
  102.         if (!use_smbus || i2c_check_functionality(client->adapter,  
  103.                 I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {  
  104.   
  105.             unsigned write_max = chip.page_size;  
  106.   
  107.             at24->macc.write = at24_macc_write;  /***  先忽略***/  
  108.   
  109.             at24->bin.write = at24_bin_write;    /* 写函数*/  
  110.             at24->bin.attr.mode |= S_IWUSR;    /* 文件拥有者可写*/  
  111.   
  112.             if (write_max > io_limit)  /* 一次最多写io_limit个字节*/  
  113.                 write_max = io_limit;  
  114.             /* 如果使用smbus,对write_max检查*/  
  115.             if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)  
  116.                 write_max = I2C_SMBUS_BLOCK_MAX;  
  117.             at24->write_max = write_max;    
  118.   
  119.             /* buffer (data + address at the beginning) */  
  120.             /* 分配写缓冲区,多余两个字节用于保存寄存器地址*/  
  121.             at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);  
  122.             if (!at24->writebuf) {  
  123.                 err = -ENOMEM;  
  124.                 goto err_struct;  
  125.             }  
  126.         } else {  
  127.             dev_warn(&client->dev,  
  128.                 "cannot write due to controller restrictions.");  
  129.         }  
  130.     }  
  131.   
  132.     at24->client[0] = client;  /* 保存i2c设备client*/  
  133.   
  134.     /* use dummy devices for multiple-address chips */  
  135.     /* 为其余设备地址注册一个dummy设备*/  
  136.     for (i = 1; i < num_addresses; i++) {  
  137.         at24->client[i] = i2c_new_dummy(client->adapter,  
  138.                     client->addr + i);     /* 设备地址每次加1 */  
  139.         if (!at24->client[i]) {  
  140.             dev_err(&client->dev, "address 0x%02x unavailable\n",  
  141.                     client->addr + i);  
  142.             err = -EADDRINUSE;  
  143.             goto err_clients;  
  144.         }  
  145.     }  
  146.   
  147.     /* 创建二进制属性*/  
  148.     err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);  
  149.     if (err)  
  150.         goto err_clients;  
  151.   
  152.     i2c_set_clientdata(client, at24);  /* 保存驱动数据*/  
  153.   
  154.     /* 打印设备信息*/  
  155.     dev_info(&client->dev, "%zu byte %s EEPROM %s\n",  
  156.         at24->bin.size, client->name,  
  157.         writable ? "(writable)" : "(read-only)");  
  158.     dev_dbg(&client->dev,  
  159.         "page_size %d, num_addresses %d, write_max %d%s\n",  
  160.         chip.page_size, num_addresses,  
  161.         at24->write_max,  
  162.         use_smbus ? ", use_smbus" : "");  
  163.   
  164.     /* export data to kernel code */  
  165.     if (chip.setup)  
  166.         chip.setup(&at24->macc, chip.context);  
  167.   
  168.     return 0;  
  169.   
  170. err_clients:  
  171.     for (i = 1; i < num_addresses; i++)  
  172.         if (at24->client[i])  
  173.             i2c_unregister_device(at24->client[i]);  
  174.   
  175.     kfree(at24->writebuf);  
  176. err_struct:  
  177.     kfree(at24);  
  178. }  
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	struct at24_platform_data chip;
	bool writable;
	bool use_smbus = false;
	struct at24_data *at24;
	int err;
	unsigned i, num_addresses;
	kernel_ulong_t magic;

	/* 获取板级设备信息*/
	if (client->dev.platform_data) {
		chip = *(struct at24_platform_data *)client->dev.platform_data;
	} else {
		/* 没有板级设备信息,也没有driver_data,直接出错*/
		if (!id->driver_data) {
			err = -ENODEV;
			goto err_out;
		}
		magic = id->driver_data;
		chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
		magic >>= AT24_SIZE_BYTELEN;
		chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
		/*
		 * This is slow, but we can't know all eeproms, so we better
		 * play safe. Specifying custom eeprom-types via platform_data
		 * is recommended anyhow.
		 */
		chip.page_size = 1;

		chip.setup = NULL;
		chip.context = NULL;
	}

	/* 检查参数,
	    byte_len和page_size必须为2的幂,不是则打印警告*/
	if (!is_power_of_2(chip.byte_len))
		dev_warn(&client->dev,
			"byte_len looks suspicious (no power of 2)!\n");
	if (!is_power_of_2(chip.page_size))
		dev_warn(&client->dev,
			"page_size looks suspicious (no power of 2)!\n");

	/* Use I2C operations unless we're stuck with SMBus extensions. */
	/* 检查是否支持I2C协议,
	    如果不支持,则检查是否使用SMBUS协议*/
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		/* 不支持I2C协议,但是使用16位地址,出错*/
		if (chip.flags & AT24_FLAG_ADDR16) {
			err = -EPFNOSUPPORT;
			goto err_out;
		}
		/*  不支持I2C协议,使用8位地址,
		   但是不支持I2C_FUNC_SMBUS_READ_I2C_BLOCK,出错*/
		if (!i2c_check_functionality(client->adapter,
				I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
			err = -EPFNOSUPPORT;
			goto err_out;
		}
		use_smbus = true; /*使用 SMBUS协议*/
	}

	/*是否使用8个地址,根据id表,
	    目前只有AT24C00使用8个地址,其他都为1个*/
	if (chip.flags & AT24_FLAG_TAKE8ADDR)
		num_addresses = 8;
	else
		/* 24C02需要1个地址,24C04为2个,以此类推*/
		num_addresses =	DIV_ROUND_UP(chip.byte_len,
			(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);

	/* 分配struct at24_data,同时根据地址个数分配struct i2c_client*/
	at24 = kzalloc(sizeof(struct at24_data) +
		num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
	if (!at24) {
		err = -ENOMEM;
		goto err_out;
	}

	/* 初始化struct at24_data*/
	mutex_init(&at24->lock);
	at24->use_smbus = use_smbus;
	at24->chip = chip;
	at24->num_addresses = num_addresses;

	/*
	 * Export the EEPROM bytes through sysfs, since that's convenient.
	 * By default, only root should see the data (maybe passwords etc)
	 */
	 /* 设置bin_attribute字段,二进制文件名为eeprom,
	     通过它即可读写设备 */
	at24->bin.attr.name = "eeprom";
	at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
	at24->bin.read = at24_bin_read;
	at24->bin.size = chip.byte_len;

	at24->macc.read = at24_macc_read;  /***  先忽略***/

	/* 判断设备是否可写*/
	writable = !(chip.flags & AT24_FLAG_READONLY);
	if (writable) {
		if (!use_smbus || i2c_check_functionality(client->adapter,
				I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {

			unsigned write_max = chip.page_size;

			at24->macc.write = at24_macc_write;  /***  先忽略***/

			at24->bin.write = at24_bin_write;    /* 写函数*/
			at24->bin.attr.mode |= S_IWUSR;    /* 文件拥有者可写*/

			if (write_max > io_limit)  /* 一次最多写io_limit个字节*/
				write_max = io_limit;
			/* 如果使用smbus,对write_max检查*/
			if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
				write_max = I2C_SMBUS_BLOCK_MAX;
			at24->write_max = write_max;  

			/* buffer (data + address at the beginning) */
			/* 分配写缓冲区,多余两个字节用于保存寄存器地址*/
			at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
			if (!at24->writebuf) {
				err = -ENOMEM;
				goto err_struct;
			}
		} else {
			dev_warn(&client->dev,
				"cannot write due to controller restrictions.");
		}
	}

	at24->client[0] = client;  /* 保存i2c设备client*/

	/* use dummy devices for multiple-address chips */
	/* 为其余设备地址注册一个dummy设备*/
	for (i = 1; i < num_addresses; i++) {
		at24->client[i] = i2c_new_dummy(client->adapter,
					client->addr + i);     /* 设备地址每次加1 */
		if (!at24->client[i]) {
			dev_err(&client->dev, "address 0x%02x unavailable\n",
					client->addr + i);
			err = -EADDRINUSE;
			goto err_clients;
		}
	}

	/* 创建二进制属性*/
	err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
	if (err)
		goto err_clients;

	i2c_set_clientdata(client, at24);  /* 保存驱动数据*/

	/* 打印设备信息*/
	dev_info(&client->dev, "%zu byte %s EEPROM %s\n",
		at24->bin.size, client->name,
		writable ? "(writable)" : "(read-only)");
	dev_dbg(&client->dev,
		"page_size %d, num_addresses %d, write_max %d%s\n",
		chip.page_size, num_addresses,
		at24->write_max,
		use_smbus ? ", use_smbus" : "");

	/* export data to kernel code */
	if (chip.setup)
		chip.setup(&at24->macc, chip.context);

	return 0;

err_clients:
	for (i = 1; i < num_addresses; i++)
		if (at24->client[i])
			i2c_unregister_device(at24->client[i]);

	kfree(at24->writebuf);
err_struct:
	kfree(at24);
}
 

 
驱动首先获取板级设备信息(client->dev.platform_data),我们假设驱动移植时,添加了该板级设备信息。  

判断是使用I2C协议还是SMBus协议。在这里,I2C adpater使用I2C协议。

然后,判断设备需要多少个i2c设备地址。

这里补充下:根据at24c02的datasheet,设备地址的第1位到第3位,将根据不同的设备来进行设置。

例如,如果是at24c04,则设备地址的第1位将用来表示寄存器地址,因为内存大小为512字节,而寄存器地址只有8位(256字节),

需要额外的一位用来表示512字节,因此使用了设备地址当中的一位来实现此目的。具体的请看datasheet。

这里使用at24c02,num_addresses将为1。

接着分配struct at24_data和struct i2c_client指针数组空间。

然后对struct at24_data进行了初始化工作。

接着,对二进制属性进行了配置。名字为eeprom,同时配置了其读方法(at24_bin_read),如果设备可写,还将配置其写方法(at24_bin_write)。

接下来很重要的一步,如果设备使用多个地址,则需要为所有地址(除了第一个地址)分配一个dummy device,这样这些地址就不会被其他的I2C设备占用了。

最后,向sys文件系统注册了二进制属性文件,通过该二进制文件,用户即可访问该设备。


写函数(at24_bin_write)

tatic ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
        struct bin_attribute *attr,
        char *buf, loff_t off, size_t count)
{
    struct at24_data *at24;


   /* 通过kobj获取device,再获取driver_data */  
    at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
    return at24_write(at24, buf, off, count);
}

该函数首先通过kobj获取了struct device的指针,再获取了at24。

接着直接调用了at24_write。如下:

static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
              size_t count)
{   
    ssize_t retval = 0;


    if (unlikely(!count))
        return count;


    /*
     * Write data to chip, protecting against concurrent updates
     * from this host, but not from other I2C masters.
     */
    mutex_lock(&at24->lock);


    while (count) {
        ssize_t status;
    
        status = at24_eeprom_write(at24, buf, off, count);
        if (status <= 0) {
            if (retval == 0)
                retval = status;
            break;
        }
        buf += status;
        off += status;
        count -= status;
        retval += status;
    }


    mutex_unlock(&at24->lock);


    return retval;
}

该函数在访问设备前,首先加锁互斥体,以防止竞态。然后根据count来调用at24_eeprom_write函数将数据写入设备。写入成功后,更新偏移量等信息,如果还需要写入,则再次调用at24_eeprom_write函数。

看下at24_eeprom_write函数:

static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
        unsigned offset, size_t count)
{
    struct i2c_client *client;
    struct i2c_msg msg;
    ssize_t status;
    unsigned long timeout, write_time;
    unsigned next_page;


    /* Get corresponding I2C address and adjust offset */
    client = at24_translate_offset(at24, &offset);


    /* write_max is at most a page */

      /* 检查写入的字节数*/ 
    if (count > at24->write_max)    
 

        count = at24->write_max;


    /* Never roll over backwards, to the start of this page *


    /* 写入不会超过下一页的边界*/  

    next_page = roundup(offset + 1, at24->chip.page_size)

      /* 根据页大小调整count*/  

    if (offset + count > next_page)
        count = next_page - offset;


    /* If we'll use I2C calls for I/O, set up the message */

    /* 使用I2C协议,需要填充msg*/

    if (!at24->use_smbus) {
        int i = 0;


        msg.addr = client->addr;   
/*设备地址*/

        msg.flags = 0;


        /* msg.buf is u8 and casts will mask the values */


       /* 使用writebuf作为发送缓冲区 */ 

        msg.buf = at24->writebuf;

/* 根据是8位还是16位地址,msg.buf的前一(两)个字节为设备内部的寄存器地址*/  


        if (at24->chip.flags & AT24_FLAG_ADDR16)
         msg.buf[i++] = offset >> 8;  
/* 16位地址,先写高位地址*/  

        msg.buf[i++] = offset;

       /* 复制需要发送的数据 */

        memcpy(&msg.buf[i], buf, count);
        msg.len = i + count;    
 /* 发送长度为数据长度加上地址长度*/

    }


    /*
     * Writes fail if the previous one didn't complete yet. We may
     * loop a few times until this one succeeds, waiting at least
     * long enough for one entire page write to work.
     */
    timeout = jiffies + msecs_to_jiffies(write_timeout);
    do {
        write_time = jiffies;
        if (at24->use_smbus) {
            status = i2c_smbus_write_i2c_block_data(client,
                    offset, count, buf);
            if (status == 0)
                status = count;
        } else {        
/* 使用I2C协议发送*/
 
            status = i2c_transfer(client->adapter, &msg, 1);
            if (status == 1)
                status = count;
        }
        dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
                count, offset, status, jiffies);


        if (status == count)
            return count;

       /* REVISIT: at HZ=100, this is sloooow */
        msleep(1);
    } while (time_before(write_time, timeout));   
/* 使用timeout */  
    return -ETIMEDOUT;
}

该函数首先调用了at24_translate_offset函数,来获取地址对应的client

然后,对写入的字节数(count)进行了调整。

随后,如果使用I2C协议,则要组建msg用于发送。

最后,根据使用I2C还是SMBus协议,调用相应的发送函数来发送数据。

注意的是,这里使用了超时,超时时间write_timeout为驱动模块参数,可由用户设置,默认为25ms。如果发送超时了,while循环将终止。

至此,at24c02的写入过程就结束了.写函数和读函数非常相似,只是在使用I2C协议时,组建的msg有所不同。同样读函数也使用了超时.这里暂时就不分析了。

static struct i2c_client *at24_translate_offset(struct at24_data *at24,
        unsigned *offset)
{

    unsigned i;


/* 有多个I2C设备地址,根据offset获取该地址对应的client*/
    if (at24->chip.flags & AT24_FLAG_ADDR16) {
        i = *offset >> 16;
        *offset &= 0xffff;
    } else {
        i = *offset >> 8;
        *offset &= 0xff;
    }


    return at24->client[i];
}

加板级信息

 S3c244开发板核心板电路图

 AT24C02电路图


drivers/misc/eeprom/at24.c不依赖于具体的CPUI2C控制寄存器硬件特性,因此,如果某一电路板包含该外设,只需要在板文件中添加对应的i2c_board_info,如对于s3c2440要使其支持at24c02 eeprom只需要作如下工作:

首先是要在内核中注册板级信息,因为设备和驱动需要匹配,它们是通过设备名和驱动名进行匹配的。因为AT24C02芯片是由2048bits构成,所以有2048 / 8 = 256byte,并将其分成32页每页有8byte大小,是8bits寻址,AT24C02的芯片地址高4位为1010,如果AT24C02芯片的A0A1,A2,这三个引脚接地,AT24C02芯片从地址是01010000b(0x50),如果AT24C02芯片的A0结高电平,A1A2两个引脚接地,着AT24C02芯片从地址是01010001b(0x51),这些都是AT24C02datasheet上有的,不同芯片不同情况。下面在linux-3.0/arch/arm/mach-s3c2440/mach-smdk2440.c添加AT24C02设备的板级信息如下:

1. [fulinux@ubuntu linux-3.0]$ vim arch/arm/mach-s3c2440/mach-smdk2440.c 

2.  #include <linux/i2c.h>     

3. #include <linux/i2c/at24.h> 

4. 

5. static struct at24_platform_data at24c02= {

6.      .byte_len   = SZ_2K / 8,

7.      .page_size  = 8,

8.      .flags      = AT24_FLAG_ADDR8,

9. };

10.  

11.  static struct i2c_board_info __initdata smdk2440_i2c_devs[] = {

12.      {

13.          I2C_BOARD_INFO("24c02", 0x50),

14.          .platform_data = &at24c02,

15.      },

16.      /*  more devices can be added using expansion connectors */

17. };


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值