精华内容
下载资源
问答
  • Linux I2C 几个结构体间的关系

    千次阅读 2012-08-21 16:16:40
    Linux内核源代码中的drivers目录包含一个i2c目录,而在i2c目录又包含如下文件和文件夹: • i2c-core.c 这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。 • i2c-dev.c 实 现了I2C适配器设备文件的...
    在Linux内核源代码中的drivers目录下包含一个i2c目录,而在i2c目录下又包含如下文件和文件夹:
    •  i2c-core.c
    这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。
    •  i2c-dev.c
    实 现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访问设备时的主设备号都为89,次设备号为0~255。应用程序通过 “i2c-%d” (i2c-0, i2c-1, ..., i2c-10, ...)文件名并使用文件操作接口open()、write()、read()、ioctl()和close()等来访问这个设备。
    i2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read()、write()和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器并控制I2C设备的工作方式。
    •  chips文件夹
    这个目录中包含了一些特定的I2C设备驱动,如Dallas公司的DS1337实时钟芯片、EPSON公司的RTC8564实时钟芯片和I2C接口的EEPROM驱动等。
    •  busses文件夹
    这个文件中包含了一些I2C总线的驱动,如S3C2410的I2C控制器驱动为i2c-s3c2410.c。
    •  algos文件夹
    实现了一些I2C总线适配器的algorithm。
    此外,内核中的i2c.h这个头文件对i2c_driver、i2c_client、i2c_adapter和i2c_algorithm这4个数据结构进行了定义。理解这4个结构体的作用十分关键,代码清单15.1、15.2、15.3、15.4分别给出了它们的定义。
    代码清单15.1 i2c_adapter结构体
    1  struct i2c_adapter {
    2   struct module *owner;/*所属模块*/
    3  unsigned int id;   /*algorithm的类型,定义于i2c-id.h,以I2C_ALGO_开始*/
    4  unsigned int class;
    5  struct i2c_algorithm *algo;/*总线通信方法结构体指针 */
    6  void *algo_data; /* algorithm数据 */
    7  int (*client_register)(struct i2c_client *);  /*client注册时调用*/
    8  int (*client_unregister)(struct i2c_client *); /*client注销时调用*/
    9  struct semaphore bus_lock;    /*控制并发访问的自旋锁*/
    10 struct semaphore clist_lock;
    11 int timeout;
    12 int retries;    /*重试次数*/
    13 struct device dev;  /* 适配器设备 */
    14 struct class_device class_dev; /* 类设备 */
    15 int nr;
    16 struct list_head clients;  /* client链表头*/
    17 struct list_head list;
    18 char name[I2C_NAME_SIZE];  /*适配器名称*/
    19 struct completion dev_released;    /*用于同步*/
    20 struct completion class_dev_released;
    21};
    代码清单15.2 i2c_algorithm结构体
    1  struct i2c_algorithm {
    2   int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,
    3                      int num);  /*i2c传输函数指针*/
    4   int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,   /*smbus传输函数指针*/
    5                      unsigned short flags, char read_write,
    6                      u8 command, int size, union i2c_smbus_data * data);
    7   int (*slave_send)(struct i2c_adapter *,char*,int);/*当i2c适配器为slave时,发送函数*/
    8   int (*slave_recv)(struct i2c_adapter *,char*,int); /*当i2c适配器为slave时,接收函数*/
    9   int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long); /*类似ioctl*/
    10  u32 (*functionality) (struct i2c_adapter *);/*返回适配器支持的功能*/
    11 };
    上述代码第4行对应为SMBus传输函数指针,SMBus大部分基于I2C总线规范,SMBus不需增加额外引脚。与I2C总线相比,SMBus增加了一些新的功能特性,在访问时序也有一定的差异。
    代码清单15.3 i2c_driver结构体
    1  struct i2c_driver {
    2   int id;
    3   unsigned int class;
    4   int (*attach_adapter)(struct i2c_adapter *); /*依附i2c_adapter函数指针 */
    5   int (*detach_adapter)(struct i2c_adapter *); /*脱离i2c_adapter函数指针*/
    6   int (*detach_client)(struct i2c_client *);  /*i2c client脱离函数指针*/
    7   int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); /*类似ioctl*/
    8   struct device_driver driver;    /*设备驱动结构体*/
    9   struct list_head list;         /*链表头*/
    10 };
    代码清单15.4 i2c_client结构体
    1  struct i2c_client {
    2   unsigned int flags;  /* 标志 */
    3   unsigned short addr;     /* 低7位为芯片地址 */
    4   struct i2c_adapter *adapter; /*依附的i2c_adapter*/
    5   struct i2c_driver *driver;    /*依附的i2c_driver */
    6   int usage_count;     /* 访问计数  */
    7   struct device dev;     /* 设备结构体 */
    8   struct list_head list;       /* 链表头 */
    9   char name[I2C_NAME_SIZE]; /* 设备名称 */
    10  struct completion released;   /* 用于同步 */
    11 };
    下面分析一下i2c_driver、i2c_client、i2c_adapter和i2c_algorithm这4个数据结构的作用及其盘根错节的关系。
    •  i2c_adapter与i2c_algorithm
    i2c_adapter 对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配 器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的 i2c_algorithm的指针。
    i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位。i2c_msg结构体也非常关键,代码清单15.5给出了它的定义。
    代码清单15.5 i2c_msg结构体
    1 struct i2c_msg {
    2  __u16 addr; /* 设备地址*/
    3   __u16 flags; /* 标志 */ 
    4   __u16 len;  /* 消息长度*/
    5   __u8 *buf;  /* 消息数据*/
    6 };
    •  i2c_driver与i2c_client
    i2c_driver对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client一般被包含在i2c字符设备的私有信息结构体中。
    i2c_driver 与i2c_client发生关联的时刻在i2c_driver的attach_adapter()函数被运行时。attach_adapter()会探测 物理设备,当确定一个client存在时,把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter, driver指针指向该i2c_driver,并会调用i2c_adapter的client_register()函数。相反的过程发生在 i2c_driver 的detach_client()函数被调用的时候。
    •  i2c_adpater与i2c_client
    i2c_adpater 与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adpater。由于一个适配器上可以连 接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,i2c_adpater中包括依附于它的i2c_client 的链表。
    假设I2C总线适配器xxx上有两个使用相同驱动程序的yyy I2C设备,在打开该I2C总线的设备结点后相关数据结构之间的逻辑组织关系将如图15.2所示。
     15.2 I2C驱动各数据结构关系

    图15.2 I2C驱动各数据结构关系
      从上面的分析可知,虽然I2C硬件体系结构比较简单,但是I2C体系结构在Linux中的实现却相当复杂。当工程师拿到实际的电路板,面对复杂的 Linux I2C子系统,应该如何下手写驱动呢?究竟有哪些是需要亲自做的,哪些是内核已经提供的呢?理清这个问题非常有意义,可以使我们面对具体问题时迅速地抓住 重点。
    一方面,适配器驱动可能是Linux内核本身还不包含的。另一方面,挂接在适配器上的具体设备驱动可能也是Linux不存在的。即便上述设备驱动都存在于Linux内核中,其基于的平台也可能与我们的电路板不一样。因此,工程师要实现的主要工作将包括:
    •  提供I2C适配器的硬件驱动,探测、初始化I2C适配器(如申请I2C的I/O地址和中断号)、驱动CPU控制的I2C适配器从硬件上产生各种信号以及处理I2C中断等。
    •  提供I2C适配器的algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋值给i2c_adapter的algo指针。
    •  实现I2C设备驱动与i2c_driver接口,用具体设备yyy的yyy_attach_adapter()函数指针、 yyy_detach_client()函数指针和yyy_command()函数指针的赋值给i2c_driver的attach_adapter、 detach_adapter和detach_client指针。
    •  实现I2C设备驱动的文件操作接口,即实现具体设备yyy的yyy_read()、yyy_write()和yyy_ioctl()函数等。
    上述工作中1、2属于I2C总线驱动,3、4属于I2C设备驱动,做完这些工作,系统会增加两个内核模块。本章第3~4节将详细分析这些工作的实施方法,给出设计模板,而5~6节将给出两个具体的实例。
    展开全文
  • Linux 设备驱动篇之I2c设备驱动

    万次阅读 多人点赞 2013-06-02 20:24:59
    虽然I2C硬件体系结构和协议都很容易理解,但是LinuxI2C驱动体系结构却相当的复杂度,它主要由3部分组成,即I2C设备驱动、I2C总线驱动和I2C核心。 1.I2C核心 I2C核心是I2c总线和I2c设备驱动的中间枢纽,它以通用的...

    Linux 设备驱动篇之I2c设备驱动

    fulinux

    一、I2C驱动体系

    虽然I2C硬件体系结构和协议都很容易理解,但是Linux I2C驱动体系结构却有相当的复杂度,它主要由3部分组成,即I2C设备驱动、I2C总线驱动和I2C核心。

    1.I2C核心

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

    2.I2C总线驱动

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

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

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

    3.I2C设备驱动

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

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

     

     

    图1 I2C驱动体系结构图1

     

     

    图2 I2C驱动体系结构图2

     

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

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

     

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

    1. [fulinux@ubuntu linux-3.0]$ tree /sys/bus/i2c/       

    2. /sys/bus/i2c/

    3. |-- devices

    4. |   |-- i2c-0 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-0

    5. |   |-- i2c-1 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-1

    6. |   |-- i2c-2 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-2

    7. |   |-- i2c-3 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-3

    8. |   |-- i2c-4 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-4

    9. |   |-- i2c-5 -> ../../../devices/pci0000:00/0000:00:02.0/i2c-5

    10. |   |-- i2c-6 -> ../../../devices/pci0000:00/0000:00:02.0/drm/card0/card0-DP-1/i2c-6

    11. |   `-- i2c-7 -> ../../../devices/pci0000:00/0000:00:02.0/drm/card0/card0-DP-2/i2c-7

    12. |-- drivers

    13. |   |-- 88PM860x

    14. |   |   |-- bind

    15. |   |   |-- uevent

    16. |   |   `-- unbind

    17. |   |-- aat2870

    18. |   |   |-- bind

    19. |   |   |-- uevent

    20. |   |   `-- unbind

    21. |   |-- ab3100

    22. |   |   |-- bind

    23. |   |   |-- uevent

    24. |   |   `-- unbind

    25. |   |-- adp5520

     

    下面我以s3c2440开发板及其之上的EEPROM芯片AT24C02和linux-3.0内核平台讲解I2c的三个部分。

    ********************************************************************************************

    转载声明:希望大家能转载此文谢谢  原文链接

    ********************************************************************************************

    4.s3c2440和at24c02硬件特性

    请看s3c2440.pdf

    芯片AT24C02的电气特性:

    • Low-voltage and Standard-voltage Operation

    – 2.7 (VCC= 2.7V to 5.5V)

    – 1.8 (VCC= 1.8V to 5.5V)

    • Internally Organized 128 x 8 (1K), 256 x 8 (2K), 512 x 8 (4K),

    1024 x 8 (8K) or 2048 x 8 (16K)

    • Two-wire Serial Interface

    • Schmitt Trigger, Filtered Inputs for Noise Suppression

    • Bidirectional Data Transfer Protocol

    • 100 kHz (1.8V) and 400 kHz (2.7V, 5V) Compatibility

    • Write Protect Pin for Hardware Data Protection

    • 8-byte Page (1K, 2K), 16-byte Page (4K, 8K, 16K) Write Modes

    • Partial Page Writes Allowed

    • Self-timed Write Cycle (5 ms max)

    • High-reliability

    – Endurance: 1 Million Write Cycles

    – Data Retention: 100 Years

    • Automotive Grade and Lead-free/Halogen-free Devices Available

    • 8-lead PDIP, 8-lead JEDEC SOIC, 8-lead MAP, 5-lead SOT23,

    8-lead TSSOP and 8-ball dBGA2 Packages

    • Die Sales: Wafer Form, Waffle Pack and Bumped Wafers

    主要是看AT24C02.pdf

     

    图3 S3c244开发板核心板电路图

     

    图4 AT24C02电路图

    5.i2c.h头文件

    内核中i2c.h这个头文件对i2c_driver、i2c_client、i2c_adapter和i2c_algorithm着4个数据结构进行了定义。理解这4个结构的作用十分关键,代码清单1、2、3、4分别给出了它们的定义。

    代码清单1 i2c_adapter结构体

     

    1. /*

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

    3.  * with the access algorithms necessary to access it.

    4.  */

    5. struct i2c_adapter {

    6.     struct module *owner;

    7.     unsigned int class;       /* classes to allow probing for */

    8.     const struct i2c_algorithm *algo; /* the algorithm to access the bus */

    9.     void *algo_data;

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

    11.     struct rt_mutex bus_lock;

    12.     int timeout;            /* in jiffies */

    13.     int retries;

    14.     struct device dev;      /* the adapter device */

    15.     int nr;

    16.     char name[48];

    17.     struct completion dev_released;

    18.     struct mutex userspace_clients_lock;

    19.     struct list_head userspace_clients;

    20. };

    代码清单2 i2c_algorithm结构体

    1. /*

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

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

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

    5.  * to name two of the most common.

    6.  */

    7. struct i2c_algorithm {

    8.     /* If an adapter algorithm can't do I2C-level access, set master_xfer

    9.        to NULL. If an adapter algorithm can do SMBus access, set

    10.        smbus_xfer. If set to NULL, the SMBus protocol is simulated

    11.        using common I2C messages */

    12.     /* master_xfer should return the number of messages successfully

    13.        processed, or a negative value on error */

    14.     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,

    15.                int num);

    16.     int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,

    17.                unsigned short flags, char read_write,

    18.                u8 command, int size, union i2c_smbus_data *data);

     

    19.     /* To determine what the adapter supports */

    20.     u32 (*functionality) (struct i2c_adapter *);

    21. };

    上述代码第4行对应为SMBus传输函数指针,SMBus大部分基于I2C总线规范,SMBus不需要增加额外引脚。与I2C总线相比,SMBus增加了一些新的功能特性,在访问时序也有一定的差异。

     

    代码清单3 i2c_driver结构体

    1. /*

    2.  * struct i2c_driver - represent an I2C device driver

    3.  * @class: What kind of i2c device we instantiate (for detect)

    4.  * @attach_adapter: Callback for bus addition (deprecated)

    5.  * @detach_adapter: Callback for bus removal (deprecated)

    6.  * @probe: Callback for device binding

    7.  * @remove: Callback for device unbinding

    8.  * @shutdown: Callback for device shutdown

    9.  * @suspend: Callback for device suspend

    10.  * @resume: Callback for device resume

    11.  * @alert: Alert callback, for example for the SMBus alert protocol

    12.  * @command: Callback for bus-wide signaling (optional)

    13.  * @driver: Device driver model driver

    14.  * @id_table: List of I2C devices supported by this driver

    15.  * @detect: Callback for device detection

    16.  * @address_list: The I2C addresses to probe (for detect)

    17.  * @clients: List of detected clients we created (for i2c-core use only)

    18.  *

    19.  * The driver.owner field should be set to the module owner of this driver.

    20.  * The driver.name field should be set to the name of this driver.

    21.  *

    22.  * For automatic device detection, both @detect and @address_data must

    23.  * be defined. @class should also be set, otherwise only devices forced

    24.  * with module parameters will be created. The detect function must

    25.  * fill at least the name field of the i2c_board_info structure it is

    26.  * handed upon successful detection, and possibly also the flags field.

    27.  *

    28.  * If @detect is missing, the driver will still work fine for enumerated

    29.  * devices. Detected devices simply won't be supported. This is expected

    30.  * for the many I2C/SMBus devices which can't be detected reliably, and

    31.  * the ones which can always be enumerated in practice.

    32.  *

    33.  * The i2c_client structure which is handed to the @detect callback is

    34.  * not a real i2c_client. It is initialized just enough so that you can

    35.  * call i2c_smbus_read_byte_data and friends on it. Don't do anything

    36.  * else with it. In particular, calling dev_dbg and friends on it is

    37.  * not allowed.

    38.  */

    39. struct i2c_driver {

    40.     unsigned int class;

     

    41.     /* Notifies the driver that a new bus has appeared or is about to be

    42.      * removed. You should avoid using this, it will be removed in a

    43.      * near future.

    44.      */

    45.     int (*attach_adapter)(struct i2c_adapter *) __deprecated;

    46.     int (*detach_adapter)(struct i2c_adapter *) __deprecated;

     

    47.     /* Standard driver model interfaces */

    48.     int (*probe)(struct i2c_client *, const struct i2c_device_id *);

    49.     int (*remove)(struct i2c_client *);

     

    50.     /* driver model interfaces that don't relate to enumeration  */

    51.     void (*shutdown)(struct i2c_client *);

    52.     int (*suspend)(struct i2c_client *, pm_message_t mesg);

    53.     int (*resume)(struct i2c_client *);

     

    54.     /* Alert callback, for example for the SMBus alert protocol.

    55.      * The format and meaning of the data value depends on the protocol.

    56.      * For the SMBus alert protocol, there is a single bit of data passed

    57.      * as the alert response's low bit ("event flag").

    58.      */

    59.     void (*alert)(struct i2c_client *, unsigned int data);

     

    60.     /* a ioctl like command that can be used to perform specific functions

    61.      * with the device.

    62.      */

    63.     int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

     

    64.     struct device_driver driver;

    65.     const struct i2c_device_id *id_table;

     

    66.     /* Device detection callback for automatic device creation */

    67.     int (*detect)(struct i2c_client *, struct i2c_board_info *);

    68.     const unsigned short *address_list;

    69.     struct list_head clients;

    70. };

     

    代码清单4 i2c_client结构体

    1. /*

    2.  * struct i2c_client - represent an I2C slave device

    3.  * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;

    4.  *  I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking

    5.  * @addr: Address used on the I2C bus connected to the parent adapter.

    6.  * @name: Indicates the type of the device, usually a chip name that's

    7.  *  generic enough to hide second-sourcing and compatible revisions.

    8.  * @adapter: manages the bus segment hosting this I2C device

    9.  * @driver: device's driver, hence pointer to access routines

    10.  * @dev: Driver model device node for the slave.

    11.  * @irq: indicates the IRQ generated by this device (if any)

    12.  * @detected: member of an i2c_driver.clients list or i2c-core's

    13.  *  userspace_devices list

    14.  *

    15.  * An i2c_client identifies a single device (i.e. chip) connected to an

    16.  * i2c bus. The behaviour exposed to Linux is defined by the driver

    17.  * managing the device.

    18.  */

    19. struct i2c_client {

    20.     unsigned short flags;       /* div., see below      */

    21.     unsigned short addr;        /* chip address - NOTE: 7bit    */

    22.                     /* addresses are stored in the  */

    23.                     /* _LOWER_ 7 bits       */

    24.     char name[I2C_NAME_SIZE];

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

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

    27.     struct device dev;      /* the device structure     */

    28.     int irq;            /* irq issued by device     */

    29.     struct list_head detected;

    30. };

    下面分析i2c_driver、i2c_client、i2c_adapter和i2c_algorithm这4个数据结构的作用及盘根错节的关系。

     

    (1)2c_adapter与i2c_algorithm

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

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

    代码清单5 i2c_msg结构体

    /**

     * struct i2c_msg - an I2C transaction segment beginning with START

     * @addr: Slave address, either seven or ten bits.  When this is a ten

     *  bit address, I2C_M_TEN must be set in @flags and the adapter

     *  must support I2C_FUNC_10BIT_ADDR.

     * @flags: I2C_M_RD is handled by all adapters.  No other flags may be

     *  provided unless the adapter exported the relevant I2C_FUNC_*

     *  flags through i2c_check_functionality().

     * @len: Number of data bytes in @buf being read from or written to the

     *  I2C slave address.  For read transactions where I2C_M_RECV_LEN

     *  is set, the caller guarantees that this buffer can hold up to

     *  32 bytes in addition to the initial length byte sent by the

     *  slave (plus, if used, the SMBus PEC); and this value will be

     *  incremented by the number of block data bytes received.

     * @buf: The buffer into which data is read, or from which it's written.

     *

     * An i2c_msg is the low level representation of one segment of an I2C

     * transaction.  It is visible to drivers in the @i2c_transfer() procedure,

     * to userspace from i2c-dev, and to I2C adapter drivers through the

     * @i2c_adapter.@master_xfer() method.

     *

     * Except when I2C "protocol mangling" is used, all I2C adapters implement

     * the standard rules for I2C transactions.  Each transaction begins with a

     * START.  That is followed by the slave address, and a bit encoding read

     * versus write.  Then follow all the data bytes, possibly including a byte

     * with SMBus PEC.  The transfer terminates with a NAK, or when all those

     * bytes have been transferred and ACKed.  If this is the last message in a

     * group, it is followed by a STOP.  Otherwise it is followed by the next

     * @i2c_msg transaction segment, beginning with a (repeated) START.

     *

     * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then

     * passing certain @flags may have changed those standard protocol behaviors.

     * Those flags are only for use with broken/nonconforming slaves, and with

     * adapters which are known to support the specific mangling options they

     * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).

     */

    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_driver与i2c_client

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

    I2c_client信息通常在BSP的板文件中通过i2c_board_info填充,如下面代码就定义了一个I2C设备ID为“24c02”、地址为0x50的i2c_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_type的match()函数i2c_device_match()中,会调用i2c_match_id()函数匹配板文件中定义的ID和i2c_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_adpater与i2c_client

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

    代码清单8 i2c_client的链表

    1. struct list_head userspace_clients;

     

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

     

     

    图5 I2C总线的设备节点后相关数据结构之间的逻辑组织关系图

     

    从上面的分析可知,虽然I2C硬件体系结构简单,但是I2C体系结构在linux中的实现却相当复杂。当工程师拿到实际的电路板,面对复杂的linux I2C子系统,应该如何下手写驱动呢?究竟要哪些是需要亲自做的,哪些是内核已经提供的呢?理清这个问题非常有意义,可以使我们面对具体问题时迅速地抓住重点。

    一方面,适配器驱动可能是linux内核本身还不包含的;另一方面,挂接在适配器上的就提设备可能也是linux内核还不包含的。因此,工程师要实现的主要工作如下。

    提供I2C适配器的硬件驱动,探测、初始化I2C适配器(如申请I2C的I/O地址和中断号)、驱动CPU控制的I2C适配器从硬件上产生各种信号以及处理I2C中断等。

    提供I2C适配器的algorithm,具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋值给i2c_adapter的algo指针。

    实现I2C设备驱动中的i2c_driver接口,具体设备yyy_probe()、yyy_remove()、yyy_suspend()、yyy_resume()函数指针和i2c_device_id设备ID表赋值给i2c_driver的probe、remove、suspend、resume和id_table指针。

    实现I2C设备所对应类型的具体驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的。例如,如果字符设备,就实现文件操作接口,即实现具体yyy的yyy_read()、yyy_write()和yyy_ioctl()函数等;如果是声卡,就实现ALSA驱动。

     

    二、I2C的第一部分

    1.Linux I2C核心

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

    2.增加/删除i2c_adapter

    代码清单9 i2c_add_adapter函数:

    1. /**

    2.  * i2c_add_adapter - declare i2c adapter, use dynamic bus number

    3.  * @adapter: the adapter to add

    4.  * Context: can sleep

    5.  *

    6.  * This routine is used to declare an I2C adapter when its bus number

    7.  * doesn't matter.  Examples: for I2C adapters dynamically added by

    8.  * USB links or PCI plugin cards.

    9.  *

    10.  * When this returns zero, a new bus number was allocated and stored

    11.  * in adap->nr, and the specified adapter became available for clients.

    12.  * Otherwise, a negative errno value is returned.

    13.  */

    14. int i2c_add_adapter(struct i2c_adapter *adapter)

    15. {

    16.     int id, res = 0;

    17. retry:

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

    19.         return -ENOMEM;

    20.     mutex_lock(&core_lock);

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

    22.     res = idr_get_new_above(&i2c_adapter_idr, adapter,

    23.                 __i2c_first_dynamic_bus_num, &id);

    24.     mutex_unlock(&core_lock);

    25.     if (res < 0) {

    26.         if (res == -EAGAIN)

    27.             goto retry;

    28.         return res;

    29.     }

    30.     adapter->nr = id;

    31.     return i2c_register_adapter(adapter);

    32. }

    33. EXPORT_SYMBOL(i2c_add_adapter);

     

    代码清单10 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_algorithm的master_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.2C总线驱动

    (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()函数的实现都与具体的CPU和I2C适配器硬件直接相关。

     

    2.I2C总线通信方法

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

    Functionality()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_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_adapter的algo_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. };

     

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

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

    l IICCON:I2C控制寄存器。

    l IICSTAT:I2C状态寄存器。

    l IICDS:I2C收发数据移位寄存器。

    l IICADD:I2C地址寄存器。

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

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

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

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

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

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

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

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

     

    图6 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_driver的remove指针方式被调用。Xxx_i2c_remove()的设计模块如代码清单26所示。

     

     

    ********************************************************************************************

    转载声明:希望大家能转载此文谢谢  原文链接

    ********************************************************************************************

     

     

     

    代码清单26 s3c2440 I2C总线驱动中的s3c24xx_i2c_remove函数

    1. /* s3c24xx_i2c_remove

    2.  *

    3.  * called when device is removed from the bus

    4. */

    5. 

    6. static int s3c24xx_i2c_remove(struct platform_device *pdev)

    7. {

    8.     struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);

    9. 

    10.     s3c24xx_i2c_deregister_cpufreq(i2c);

    11. 

    12.     i2c_del_adapter(&i2c->adap);

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

    14. 

    15.     clk_disable(i2c->clk);

    16.     clk_put(i2c->clk);

    17. 

    18.     iounmap(i2c->regs);

    19. 

    20.     release_resource(i2c->ioarea);

    21.     kfree(i2c->ioarea);

    22.     kfree(i2c);

    23. 

    24.     return 0;

    25. }

    上面的代码清单26中用到了s3c24xx_i2c结构体进行适配器所有信息的封装。类似于私有信息结构体,它与代码清单20所示的xxx_i2c结构体模板对应,代码清单27所示为s3c24xx_i2c结构体的定义。

    代码清单27 s3c24xx_i2c结构体

    1. struct s3c24xx_i2c {

    2.     spinlock_t      lock;

    3.     wait_queue_head_t   wait;

    4.     unsigned int        suspended:1;

    5. 

    6.     struct i2c_msg      *msg;

    7.     unsigned int        msg_num;

    8.     unsigned int        msg_idx;

    9.     unsigned int        msg_ptr;

    10. 

    11.     unsigned int        tx_setup;

    12.     unsigned int        irq;

    13. 

    14.     enum s3c24xx_i2c_state  state;

    15.     unsigned long       clkrate;

    16. 

    17.     void __iomem        *regs;

    18.     struct clk      *clk;

    19.     struct device       *dev;

    20.     struct resource     *ioarea;

    21.     struct i2c_adapter  adap;

    22. 

    23. #ifdef CONFIG_CPU_FREQ

    24.     struct notifier_block   freq_transition;

    25. #endif

    26. };

     

    5.s3c2440 I2C 总线通信方法

    由代码清单25的23行可以看出,I2C适配器对应的i2c_algorithm结构体实例为s3c24xx_i2c_algorithm,代码清单28所示为s3c24xx_i2c_algorithm的定义。

    代码清单28 s3c2440的i2c_algorithm结构体

    1. /* i2c bus registration info */

    2. 

    3. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {

    4.     .master_xfer        = s3c24xx_i2c_xfer,

    5.     .functionality      = s3c24xx_i2c_func,

    6. };

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

     

    代码清单29 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()首先将s3c2440的I2C适配器设置为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适配器上的中断来步步推进。代码清单32所示为s3c2440 I2C适配器中断处理函数以及其依赖的i2c_s3c_irq_nextbyte()函数的源码。

    代码清单32 s3c2440 I2C适配器中断处理函数

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

     

     

    四、I2C的第三部分

    1.Linux I2C 设备驱动

    I2C设备驱动要使用i2c_driver和i2c_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. };

     

    2.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. }

     

    3.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_transfer(client->adapter, msg, 2);

     

    4.Linux的i2c-dev.c文件分析

    I2c_dev.c文件完全可以被看作一个I2C设备驱动,不过,它实现的一个i2c_client是虚拟、临时的、随着设备文件的打开而产生,并随着设备文件的关闭而撤销,并没有被添加到i2c_adapter的client链表中。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()函数的作用有所误解。

     

     

    图7 i2cdev_read()和i2cdev_write()函数对应的时序

     

     

    图8 RepStart模式

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

    代码清单36 i2c-dev.c中的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。

     

    5.AT24C02 EEPROM的I2C设备驱动实例

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

    drivers/misc/eeprom/at24.c文件支持大多数I2C接口的EEPROM,正如我们前面所述,一个具体的I2C设备驱动有两部分组成,一部分是i2c_driver,用于将设备挂接于I2C总线,一部分是设备本身的驱动。对于EEPROM而言,设备本身的驱动以bin_attribute二进制sysfs结点形式呈现。代码清单37给出了该驱动的框架。

    代码清单37 at24_bin_read()函数

    1. static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,

    2.         struct bin_attribute *attr,

    3.         char *buf, loff_t off, size_t count)

    4. {

    5.     struct at24_data *at24;

    6. 

    7.     at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));

    8.     return at24_read(at24, buf, off, count);

    9. }

     

    代码清单38 at24_bin_write()函数

    1. /*

    2.  * Note that if the hardware write-protect pin is pulled high, the whole

    3.  * chip is normally write protected. But there are plenty of product

    4.  * variants here, including OTP fuses and partial chip protect.

    5.  *

    6.  * We only use page mode writes; the alternative is sloooow. This routine

    7.  * writes at most one page.

    8.  */

    9. static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,

    10.         unsigned offset, size_t count)

    11. {

    12.     struct i2c_client *client;

    13.     struct i2c_msg msg;

    14.     ssize_t status;

    15.     unsigned long timeout, write_time;

    16.     unsigned next_page;

    17. 

    18.     /* Get corresponding I2C address and adjust offset */

    19.     client = at24_translate_offset(at24, &offset);

    20. 

    21.     /* write_max is at most a page */

    22.     if (count > at24->write_max)

    23.         count = at24->write_max;

    24. 

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

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

    27.     if (offset + count > next_page)

    28.         count = next_page - offset;

    29. 

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

    31.     if (!at24->use_smbus) {

    32.         int i = 0;

    33. 

    34.         msg.addr = client->addr;

    35.         msg.flags = 0;

    36. 

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

    38.         msg.buf = at24->writebuf;

    39.         if (at24->chip.flags & AT24_FLAG_ADDR16)

    40.             msg.buf[i++] = offset >> 8;

    41. 

    42.         msg.buf[i++] = offset;

    43.         memcpy(&msg.buf[i], buf, count);

    44.         msg.len = i + count;

    45.     }

    46. 

    47.     /*

    48.      * Writes fail if the previous one didn't complete yet. We may

    49.      * loop a few times until this one succeeds, waiting at least

    50.      * long enough for one entire page write to work.

    51.      */

    52.     timeout = jiffies + msecs_to_jiffies(write_timeout);

    53.     do {

    54.         write_time = jiffies;

    55.         if (at24->use_smbus) {

    56.             status = i2c_smbus_write_i2c_block_data(client,

    57.                     offset, count, buf);

    58.             if (status == 0)

    59.                 status = count;

    60.         } else {

    61.             status = i2c_transfer(client->adapter, &msg, 1);

    62.             if (status == 1)

    63.                 status = count;

    64.         }

    65.         dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",

    66.                 count, offset, status, jiffies);

    67. 

    68.         if (status == count)

    69.             return count;

    70. 

    71.         /* REVISIT: at HZ=100, this is sloooow */

    72.         msleep(1);

    73.     } while (time_before(write_time, timeout));

    74. 

    75.     return -ETIMEDOUT;

    76. }

     

    代码清单39 i2c_device_id结构体

    1. static const struct i2c_device_id at24_ids[] = {

    2.     /* needs 8 addresses as A0-A2 are ignored */

    3.     { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },

    4.     /* old variants can't be handled with this generic entry! */

    5.     { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },

    6.     { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },

    7.     /* spd is a 24c02 in memory DIMMs */

    8.     { "spd", AT24_DEVICE_MAGIC(2048 / 8,

    9.         AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },

    10.     { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },

    11.     /* 24rf08 quirk is handled at i2c-core */

    12.     { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },

    13.     { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },

    14.     { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },

    15.     { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },

    16.     { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },

    17.     { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },

    18.     { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },

    19.     { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },

    20.     { "at24", 0 },

    21.     { /* END OF LIST */ }

    22. };

    23. MODULE_DEVICE_TABLE(i2c, at24_ids);

     

    代码清单40 初始化at24xx设备的i2c_driver结构体

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

    2. 

    3. static struct i2c_driver at24_driver = {

    4.     .driver = {

    5.         .name = "at24",

    6.         .owner = THIS_MODULE,

    7.     },

    8.     .probe = at24_probe,

    9.     .remove = __devexit_p(at24_remove),

    10.     .id_table = at24_ids,

    11. };

     

    代码清单41 at24_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.     int use_smbus = 0;

    6.     struct at24_data *at24;

    7.     int err;

    8.     unsigned i, num_addresses;

    9.     kernel_ulong_t magic;

    10. 

    11.     if (client->dev.platform_data) {

    12.         chip = *(struct at24_platform_data *)client->dev.platform_data;

    13.     } else {

    14.         if (!id->driver_data) {

    15.             err = -ENODEV;

    16.             goto err_out;

    17.         }

    18.         magic = id->driver_data;

    19.         chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));

    20.         magic >>= AT24_SIZE_BYTELEN;

    21.         chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);

    22.         /*

    23.          * This is slow, but we can't know all eeproms, so we better

    24.          * play safe. Specifying custom eeprom-types via platform_data

    25.          * is recommended anyhow.

    26.          */

    27.         chip.page_size = 1;

    28. 

    29.         /* update chipdata if OF is present */

    30.         at24_get_ofdata(client, &chip);

    31. 

    32.         chip.setup = NULL;

    33.         chip.context = NULL;

    34.     }

    35. 

    36.     if (!is_power_of_2(chip.byte_len))

    37.         dev_warn(&client->dev,

    38.             "byte_len looks suspicious (no power of 2)!\n");

    39.     if (!chip.page_size) {

    40.         dev_err(&client->dev, "page_size must not be 0!\n");

    41.         err = -EINVAL;

    42.         goto err_out;

    43.     }

    44.     if (!is_power_of_2(chip.page_size))

    45.         dev_warn(&client->dev,

    46.             "page_size looks suspicious (no power of 2)!\n");

    47.         

    48.     /* Use I2C operations unless we're stuck with SMBus extensions. */

    49.     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {

    50.         if (chip.flags & AT24_FLAG_ADDR16) {

    51.             err = -EPFNOSUPPORT;

    52.             goto err_out;

    53.         }   

    54.         if (i2c_check_functionality(client->adapter,

    55.                 I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {

    56.             use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;

    57.         } else if (i2c_check_functionality(client->adapter,

    58.                 I2C_FUNC_SMBUS_READ_WORD_DATA)) {

    59.             use_smbus = I2C_SMBUS_WORD_DATA;

    60.         } else if (i2c_check_functionality(client->adapter,

    61.                 I2C_FUNC_SMBUS_READ_BYTE_DATA)) {

    62.             use_smbus = I2C_SMBUS_BYTE_DATA;

    63.         } else {

    64.             err = -EPFNOSUPPORT;

    65.             goto err_out;

    66.         }

    67.     }

    68. 

    69.     if (chip.flags & AT24_FLAG_TAKE8ADDR)

    70.         num_addresses = 8;

    71.     else

    72.         num_addresses = DIV_ROUND_UP(chip.byte_len,

    73.             (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);

    74. 

    75.     at24 = kzalloc(sizeof(struct at24_data) +

    76.         num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);

    77.     if (!at24) {

    78.         err = -ENOMEM;

    79.         goto err_out;

    80.     }

    81. 

    82.     mutex_init(&at24->lock);

    83.     at24->use_smbus = use_smbus;

    84.     at24->chip = chip;

    85.     at24->num_addresses = num_addresses;

    86. 

    87.     /*

    88.      * Export the EEPROM bytes through sysfs, since that's convenient.

    89.      * By default, only root should see the data (maybe passwords etc)

    90.      */

    91.     sysfs_bin_attr_init(&at24->bin);

    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.     writable = !(chip.flags & AT24_FLAG_READONLY);

    100.     if (writable) {

    101.         if (!use_smbus || i2c_check_functionality(client->adapter,

    102.                 I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {

    103. 

    104.             unsigned write_max = chip.page_size;

    105. 

    106.             at24->macc.write = at24_macc_write;

    107. 

    108.             at24->bin.write = at24_bin_write;

    109.             at24->bin.attr.mode |= S_IWUSR;

    110. 

    111.             if (write_max > io_limit)

    112.                 write_max = io_limit;

    113.             if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)

    114.                 write_max = I2C_SMBUS_BLOCK_MAX;

    115.             at24->write_max = write_max;

    116. 

    117.             /* buffer (data + address at the beginning) */

    118.             at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);

    119.             if (!at24->writebuf) {

    120.                 err = -ENOMEM;

    121.                 goto err_struct;

    122.             }

    123.         } else {

    124.             dev_warn(&client->dev,

    125.                 "cannot write due to controller restrictions.");

    126.         }

    127.     }

    128. 

    129.     at24->client[0] = client;

    130. 

    131.     /* use dummy devices for multiple-address chips */

    132.     for (i = 1; i < num_addresses; i++) {

    133.         at24->client[i] = i2c_new_dummy(client->adapter,

    134.                     client->addr + i);

    135.         if (!at24->client[i]) {

    136.             dev_err(&client->dev, "address 0x%02x unavailable\n",

    137.                     client->addr + i);

    138.             err = -EADDRINUSE;

    139.             goto err_clients;

    140.         }

    141. }

    142.     err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);

    143.     if (err)

    144.         goto err_clients;

    145. 

    146.     i2c_set_clientdata(client, at24);

    147. 

    148.     dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",

    149.         at24->bin.size, client->name,

    150.         writable ? "writable" : "read-only", at24->write_max);

    151.     if (use_smbus == I2C_SMBUS_WORD_DATA ||

    152.         use_smbus == I2C_SMBUS_BYTE_DATA) {

    153.         dev_notice(&client->dev, "Falling back to %s reads, "

    154.                "performance will suffer\n", use_smbus ==

    155.                I2C_SMBUS_WORD_DATA ? "word" : "byte");

    156.     }

    157. 

    158.     /* export data to kernel code */

    159.     if (chip.setup)

    160.         chip.setup(&at24->macc, chip.context);

    161. 

    162.     return 0;

    163. 

    164. err_clients:

    165.     for (i = 1; i < num_addresses; i++)

    166.         if (at24->client[i])

    167.             i2c_unregister_device(at24->client[i]);

    168. 

    169.     kfree(at24->writebuf);

    170. err_struct:

    171.     kfree(at24);

    172. err_out:

    173.     dev_dbg(&client->dev, "probe error %d\n", err);

    174.     return err;

    175. }

    176. 

    177. 代码清单42 at24_remove函数

    178. static int __devexit at24_remove(struct i2c_client *client)

    179. {

    180.     struct at24_data *at24;

    181.     int i;

    182. 

    183.     at24 = i2c_get_clientdata(client);

    184.     sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);

    185. 

    186.     for (i = 1; i < at24->num_addresses; i++)

    187.         i2c_unregister_device(at24->client[i]);

    188. 

    189.     kfree(at24->writebuf);

    190.     kfree(at24);

    191.     return 0;

    192. }

     

    代码清单43 at24xx设备驱动模块的加载和卸载函数

    1. static int __init at24_init(void)

    2. {

    3.     if (!io_limit) {

    4.         pr_err("at24: io_limit must not be 0!\n");

    5.         return -EINVAL;

    6.     }   

    7.         

    8.     io_limit = rounddown_pow_of_two(io_limit);

    9.     return i2c_add_driver(&at24_driver);

    10. }   

    11. module_init(at24_init);

    12. 

    13. static void __exit at24_exit(void)

    14. {

    15.     i2c_del_driver(&at24_driver);

    16. }

    17. module_exit(at24_exit);

    18. 

    19. MODULE_DESCRIPTION("Driver for most I2C EEPROMs");

    20. MODULE_AUTHOR("David Brownell and Wolfram Sang");

    21. MODULE_LICENSE("GPL");

    At24_bin_read()和at24_bin_write()俩个函数是EEPROM驱动本身的读写实现即bin_attribute驱动,之后一部分是i2c_driver,两者在i2c_driver的probe()、remove函数中建立关联。I2c_driver的probe()函数中的初始化并通过sysfs_create_bin_file()注册了二进制sysfs结点,而remove()函数则通过sysfs_remove_bin_file()注销了sysfs结点。

    6.添加板级信息

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

    首先是要在内核中注册板级信息,因为设备和驱动需要匹配,它们是通过设备名和驱动名进行匹配的。因为AT24C02芯片是由2048bits构成,所以有2048 / 8 = 256byte,并将其分成32页每页有8byte大小,是8bits寻址,如果AT24C02芯片的A0,A1,A2,这三个引脚接地,着AT24C02芯片从地址是01010000b(即0x50),如果AT24C02芯片的A0结高电平,A1,A2两个引脚接地,着AT24C02芯片从地址是01010001b(即0x51),这些都是AT24C02的datasheet上有的,不同芯片不同情况。下面在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. };

     

     

    ********************************************************************************************

    装载声明:希望大家能转载此文谢谢:原文链接

    ********************************************************************************************

     

     

     

    展开全文
  • Linux I2C驱动框架(超详细)

    万次阅读 多人点赞 2019-06-07 17:04:26
    在讨论I2C驱动框架前,先讨论几个重要的概念 1、I2C总线 struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe, .remove = i2c_device_remove, .shut...

    Linux I2C驱动框架

    一、几个重要的对象

    在讨论I2C驱动框架前,先讨论几个重要的概念

    1、I2C总线

    struct bus_type i2c_bus_type = {
    	.name		= "i2c",
    	.match		= i2c_device_match,
    	.probe		= i2c_device_probe,
    	.remove		= i2c_device_remove,
    	.shutdown	= i2c_device_shutdown,
    	.pm		= &i2c_device_pm_ops,
    };
    

    I2C总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备I2C驱动匹配删除等操作,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,如果匹配就调用i2c_device_probe函数,进而调用I2C驱动probe函数

    特别提示:i2c_device_match会管理I2C设备I2C总线匹配规则,这将和如何编写I2C驱动程序息息相关

    2、I2C驱动

    struct i2c_driver {
    	int (*probe)(struct i2c_client *, const struct i2c_device_id *); //probe函数
    	struct device_driver driver; //表明这是一个驱动
    	const struct i2c_device_id *id_table; //要匹配的从设备信息(名称)
    	int (*detect)(struct i2c_client *, struct i2c_board_info *); //设备探测函数
    	const unsigned short *address_list; //设备地址
    	struct list_head clients; //设备链表
    };
    

    对应的是I2C驱动程序

    3、I2C设备

    struct i2c_client {
    	unsigned short addr; //设备地址
    	char name[I2C_NAME_SIZE]; //设备名称
    	struct i2c_adapter *adapter; //设配器,值I2C控制器
    	struct i2c_driver *driver; //设备对应的驱动
    	struct device dev; //表明这是一个设备
    	int irq; //中断号
    	struct list_head detected; //节点
    };
    

    对应的是I2C设备

    4、I2C设配器

    I2C设配器是什么?

    经过上面的介绍,知道有I2C驱动I2C设备,我们需要通过I2C驱动去和I2C设备通讯,这其中就需要一个I2C设配器I2C设配器对应的就是SOC上的I2C控制器

    struct i2c_adapter {
    	unsigned int id; //设备器的编号
    	const struct i2c_algorithm *algo; //算法,发送时序
    	struct device dev; //表明这是一个设备
    };
    

    其中的i2c_algorithm是算法的意思,对应的就是如何发送I2C时序

    struct i2c_algorithm {
        /* 作为主设备时的发送函数 */
    	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
    			   int num);
    
        /* 作为从设备时的发送函数 */
    	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
    			   unsigned short flags, char read_write,
    			   u8 command, int size, union i2c_smbus_data *data);
    };
    

    小结

    I2C驱动有4个重要的东西,I2C总线I2C驱动I2C设备I2C设备器

    • I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备I2C驱动的匹配和删除等
    • I2C驱动:对应的就是I2C设备的驱动程序
    • I2C设备:是具体硬件设备的一个抽象
    • I2C设配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象

    以注册I2C驱动为例,简单讲解I2C总线的运行机制(I2C设备道理相同)

    • 1、注册I2C驱动
    • 2、将I2C驱动添加到I2C总线的驱动链表中
    • 3、遍历I2C总线上的设备链表,根据i2c_device_match函数进行匹配,如果匹配调用i2c_device_probe函数
    • 4、i2c_device_probe函数会调用I2C驱动probe函数

    经过上面的讲解,对I2C驱动框架应该有了基本的了解了,下面通过分析内核源码来深入学习I2C驱动框架

    二、内核源码分析

    1、注册I2C驱动

    编写I2C驱动程序时,通过调用i2c_add_driver函数来注册驱动,下面来分析这个函数发生了什么

    static inline int i2c_add_driver(struct i2c_driver *driver)
    {
    	return i2c_register_driver(THIS_MODULE, driver);
    }
    
    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    {
        /*
        	struct bus_type i2c_bus_type = {
    			.name		= "i2c",
    			.match		= i2c_device_match,
    			.probe		= i2c_device_probe,
    			.remove		= i2c_device_remove,
    			.shutdown	= i2c_device_shutdown,
    			.pm		= &i2c_device_pm_ops,
    		};
        */
        driver->driver.bus = &i2c_bus_type; // 绑定总线
        
        driver_register(&driver->driver); // 向总线注册驱动
        
        /* 遍历I2C总线上所有设备,调用__process_new_driver函数 */
        bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
    }
    

    上面的程序可以看到,在调用i2c_add_driver后做了三件事,第一绑定总线,要记住这个总线结构体,第二向总线注册驱动,这算是I2C驱动框架的重点,第三遍历总线的设备,调用__process_new_driver函数

    乍一看,会绝对第三件事比较重要,好像匹配规则在这里面,其实这里面有一部分匹配规则,但并不是最重要的,最重要的是在driver_register函数中

    下面将重点分析driver_register函数

    int driver_register(struct device_driver *drv)
    {
        bus_add_driver(drv); // 将驱动添加到总线上
    }
    
    int bus_add_driver(struct device_driver *drv)
    {
        driver_attach(drv);
        
        /* 将驱动添加到总线的驱动链表(bus->p->klist_drivers) */
        klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 
    }
    

    接下来分析driver_attach(drv)这个函数,这个函数是重头

    int driver_attach(struct device_driver *drv)
    {
    	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
    }
    

    来看看bus_for_each_dev函数做了什么

    int bus_for_each_dev(struct bus_type *bus, struct device *start,
    		     void *data, int (*fn)(struct device *, void *))
    {
        /* 遍历总线的所有设备链表(bus->p->klist_devices)的所有设备,执行fn函数 */
    	while ((dev = next_device(&i)) && !error)
    		error = fn(dev, data);
    }
    

    会到上一个函数,我们知道bus_for_each_dev对应的fn函数是__driver_attach函数,也就是说,driver_attach函数会遍历总线上的所有设备执行__driver_attach函数,接下面来分析__driver_attach函数

    static int __driver_attach(struct device *dev, void *data)
    {
        /* 判断驱动和设备是否匹配 */
    	if (!driver_match_device(drv, dev))
    		return 0;
        
        /* 如果匹配的话,调用driver_probe_device函数 */
        if (!dev->driver)
    		driver_probe_device(drv, dev);
    }
    

    首先来看一看driver_match_device函数,通过这个函数我们可以知道匹配规则

    static inline int driver_match_device(struct device_driver *drv,
    				      struct device *dev)
    {
        /* 调用总线的macth函数 */
    	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
    }
    

    请不要忘了现在的总线式i2c总线,对应的结构体上面已经给出,如下

    struct bus_type i2c_bus_type = {
    	.name		= "i2c",
    	.match		= i2c_device_match,
    	.probe		= i2c_device_probe,
    	.remove		= i2c_device_remove,
    	.shutdown	= i2c_device_shutdown,
    	.pm		= &i2c_device_pm_ops,
    };
    

    drv->bus->match对应的就是i2c_bus_type.match

    下面来分析i2c_device_match函数

    static int i2c_device_match(struct device *dev, struct device_driver *drv)
    {
        struct i2c_client	*client = i2c_verify_client(dev); // 有device获得i2c_client
        struct i2c_driver	*driver = to_i2c_driver(drv); // 有device_driver获得i2c_driver
        
        /* 调用i2c_match_id进行匹配 */
        return i2c_match_id(driver->id_table, client) != NULL;
    }
    

    driver->id_table是什么,来看一看id_table的定义

    struct i2c_device_id {
    	char name[I2C_NAME_SIZE];
    };
    

    其实就是一个字符串,表示设备的名字

    i2c_driver中有一个我们自己i2c_device_id数组,用来匹配i2c设备

    来看看怎么匹配

    static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
    						const struct i2c_client *client)
    {
        /* 字符串匹配 */
    	strcmp(client->name, id->name);   
    }
    

    可以知道匹配规则是

    strcmp(i2c_client->name, i2c_driver->i2c_device_id->name); 
    

    就是匹配驱动和设备中的名字

    好了,现在弄清楚匹配规则了,下面来看一看如果匹配成功后要干嘛

    回到下面这个函数

    static int __driver_attach(struct device *dev, void *data)
    {
        /* 判断驱动和设备是否匹配 */
    	if (!driver_match_device(drv, dev))
    		return 0;
        
        /* 如果匹配的话,调用driver_probe_device函数 */
        if (!dev->driver)
    		driver_probe_device(drv, dev);
    }
    

    可以看到匹配成功后,会调用driver_probe_device函数,下面看一看这个函数

    int driver_probe_device(struct device_driver *drv, struct device *dev)
    {
    	really_probe(dev, drv);
    }
    
    static int really_probe(struct device *dev, struct device_driver *drv)
    {
        /* 调用了总线的probe函数 */
        dev->bus->probe(dev);
    }
    
    struct bus_type i2c_bus_type = {
    	.name		= "i2c",
    	.match		= i2c_device_match,
    	.probe		= i2c_device_probe,
    	.remove		= i2c_device_remove,
    	.shutdown	= i2c_device_shutdown,
    	.pm		= &i2c_device_pm_ops,
    };
    

    可以知道,总线的probe函数是i2c_device_probe

    static int i2c_device_probe(struct device *dev)
    {
        /* 调用i2c驱动的probe函数 */
        driver->probe(client, i2c_match_id(driver->id_table, client));
    }
    

    通过上面的分析,可以知道i2c总线进制,当向内核注册i2c驱动时,会将i2c驱动添加到总线的链表中,遍历总线上所有设备,通过i2c_client->name, i2c_driver->i2c_device_id->name进行字符串匹配,如果匹配,就调用驱动程序的probe函数

    继续回到上面的程序

    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    {
        /*
        	struct bus_type i2c_bus_type = {
    			.name		= "i2c",
    			.match		= i2c_device_match,
    			.probe		= i2c_device_probe,
    			.remove		= i2c_device_remove,
    			.shutdown	= i2c_device_shutdown,
    			.pm		= &i2c_device_pm_ops,
    		};
        */
        driver->driver.bus = &i2c_bus_type; // 绑定总线
        
        driver_register(&driver->driver); // 向总线注册驱动
        
        /* 遍历I2C总线上所有设备,调用__process_new_driver函数 */
        bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
    }
    

    刚刚我们分析了driver_register,下面来分析一下bus_for_each_dev

    其实bus_for_each_dev蕴含着i2c总线的另外一种匹配规则,不过不经常使用

    int bus_for_each_dev(struct bus_type *bus, struct device *start,
    		     void *data, int (*fn)(struct device *, void *))
    {
        /* 遍历总线上的设备链表(bus->p->klist_devices)的所有设备,调用fn函数 */
    	while ((dev = next_device(&i)) && !error)
    		error = fn(dev, data);	
    }
    

    由上面可知,fn函数对应的是__process_new_driver函数

    static int __process_new_driver(struct device *dev, void *data)
    {
        /* data对应驱动,to_i2c_adapter(dev)对应设备对应的适配器 */
    	return i2c_do_add_adapter(data, to_i2c_adapter(dev));
    }
    
    static int i2c_do_add_adapter(struct i2c_driver *driver,
    			      struct i2c_adapter *adap)
    {
    	i2c_detect(adap, driver);
    }
    
    static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
    {
        address_list = driver->address_list; // 获取驱动指定的地址
        
        temp_client->adapter = adapter; // 绑定好设配器(I2C控制器)
        
        /* 使用该是适配器,去探测所有地址 */
        for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1)
            i2c_detect_address(temp_client, driver);
    }
    
    static int i2c_detect_address(struct i2c_client *temp_client,
    			      struct i2c_driver *driver)
    {
        i2c_check_addr_busy(adapter, addr);
            
        i2c_default_probe(adapter, addr);
        
        /* 如果能到达这里,就说明该i2c控制器对应的总线上,该地址存在,调用驱动的detect函数进一步确认 */
        driver->detect(temp_client, &info);
        
        /* 如果驱动程序确认的话,生成i2c设备 */
        client = i2c_new_device(adapter, &info);
        list_add_tail(&client->detected, &driver->clients); // 将该设备添加到驱动程序的链表中
    }
    

    以上的做法是,遍历总线上的所有设备,拿到设备对应的设备器(I2C控制器),去给总线发送驱动指定好的地址,如果地址存在的话,就调用驱动的detect函数

    为什么会有这种方式?

    如果在不知道i2c设备在哪一条总线的情况下,这种方式就发挥了作用

    上面分析了注册I2C驱动,下面来分析注册I2C设备

    2、注册I2C设备

    内核通过i2c_new_device注册i2c设备

    struct i2c_client *
    i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    

    需要指定i2c设配器和设备信息

    i2c_adapter可以通过i2c_get_adapter来获取

    struct i2c_adapter *i2c_get_adapter(int id)
    

    i2c_get_adapter可以获取哪个i2c控制器,获取哪一个i2c控制器取决于你的i2c设备接在哪条i2c总线上

    i2c_board_info结构体描述了设备的硬件信息

    struct i2c_board_info {
    	char		type[I2C_NAME_SIZE]; // 设备名称,用于与驱动匹配
    	unsigned short	addr; // 设备地址
    	int		irq; // 中断号
    };
    

    下面来详细分析i2c_new_device函数

    struct i2c_client *
    i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    {
        struct i2c_client	*client;
        
        client->adapter = adap; // 设定设备的设配器
        client->addr = info->addr; // 地址
        client->irq = info->irq; // 中断号
        
        client->dev.bus = &i2c_bus_type; // 绑定总线
        
        device_register(&client->dev); // 向总线注册设备
        
        return client;
    }
    

    下面分析device_register的过程

    int device_register(struct device *dev)
    {
    	device_initialize(dev);
    	return device_add(dev);
    }
    
    int device_add(struct device *dev)
    {
        bus_add_device(dev);
        
        bus_probe_device(dev);
    }
    
    int bus_add_device(struct device *dev)
    {
        /* 将设备添加到总线的设备链表中(bus->p->klist_devices) */
        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
    }
    
    void bus_probe_device(struct device *dev)
    {
    	device_attach(dev);
    }
    
    int device_attach(struct device *dev)
    {
        bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
    }
    
    int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
    		     void *data, int (*fn)(struct device_driver *, void *))
    {
        /* 遍历总线的驱动链表上的所有驱动,调用fn函数 */
    	while ((drv = next_driver(&i)) && !error)
    		error = fn(drv, data);
    }
    

    这里的fn函数指的是__device_attach函数

    static int __device_attach(struct device_driver *drv, void *data)
    {
        /* 判断是够匹配 */
    	if (!driver_match_device(drv, dev))
    		return 0;
    
    	return driver_probe_device(drv, dev);
    }
    

    driver_match_devicedriver_probe_device函数跟上面分析的完全相同,这里不再累赘

    至此注册i2c设备已经分析完,流程为,注册i2c设备,将i2c加入总线的设备链表中,调用总线的匹配函数判断是够匹配,如果匹配,就调用驱动的probe函数

    内核还有静态一种注册i2c设备的方法

    通过i2c_register_board_info注册,起始最后还是调用了i2c_new_device,这里简单分析一下

    int __init
    i2c_register_board_info(int busnum,
    	struct i2c_board_info const *info, unsigned len)
    {
        list_add_tail(&devinfo->list, &__i2c_board_list); // 将设备信息添加到链表中
    }
    
    static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
    {
    	list_for_each_entry(devinfo, &__i2c_board_list, list) {
    		if (devinfo->busnum == adapter->nr
    				&& !i2c_new_device(adapter,
    						&devinfo->board_info))
    	}
    }
    

    3、驱动如果使用设配器给设备发送数据

    通过上面的分析,我们已经知道了i2c总线的工作机制,下面来看看当i2c设备和i2c驱动匹配之后,驱动程序要怎么去和设备通讯

    驱动程序一般调用i2c_transfer来发送信息

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

    需要指定i2c设配器

    上面说过驱动程序是 i2c设配器向设备发送数据的,那么i2c_transferv底层肯定是通过i2c_adapter发送数据的

    int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    {
        adap->algo->master_xfer(adap, msgs, num); // 通过设配器发送数据
    }
    

    三、总结

    i2c总线维护着两个链表,一个驱动链表,一个设备链表,每当注册进一个驱动(或设备),就会将其添加到总线上相应的链表上,然后遍历总线的设备(或驱动)链表的所有设备(或驱动),通过总线的匹配函数判断是够匹配,如果匹配,则调用驱动的probe函数,然后我们就可以在probe函数注册字符设备,创建设备节点,实现fops集等等

    展开全文
  • 这篇文档算上期末复习这段时间其实拖了好久了,因为从一开始接触linuxi2c驱动体系我就各种凌乱,经常性的在看内核代码时看着看着就把自己看飞了。结果就是无功而返,还使自己挫败感爆表,丧失信心。所以我决定...
    这篇文档算上期末复习这段时间其实拖了有好久了,因为从一开始接触linux的i2c驱动体系我就各种凌乱,因为起初脑海中既没有整体框架也不熟悉相关体系下的结构,所以四处乱看,经常性的在看内核代码时看着看着就把自己看飞了。结果就总是无功而返。现在我决定当前阶段把结构熟悉,知道大致的驱动体系框架。日后真正做到此类项目时再来深入理解、巩固、总结。

    因为我所要操作的EEPROM使用的是I2C接口。那么自然要先了解一下I2C总线协议。对于I2C总线协议我的理解:两条线“SCL和SDA”,一个数据信号一个时钟脉冲信号。俩线都是高电平时,SDA从高到低发出一个跳变,便是开始信号;然后就是发送数据。若SDA从低到高再跳变一次就是结束信号。这期间,SCL为高时SDA必须保持稳定无变化,因为SCL为高时会采样数据。若此时SDA变化,可能导致误发结束信号而产生终止。也就是说SCL高,SDA保持稳定,则数据有效,SDA的改变只能发生在SCL的低电平期间。当然接收器每接收一个字节数据都会产生一个ASK回应信号,表示已经收到数据。
    下面是I2C总线的传输时序图。

    再具体到Linux下驱动子系统I2C驱动体系结构(I2C总线驱动;I2c核心;I2C从设备驱动):
    首先我们要知道,I2c驱动体系中一般来说硬件上可含有一个或多个从设备,一个或多个适配器,这俩之间的关系:一个适配器可以控制多个从设备;且我们既可以通过直接编写从设备驱动来操控他,也可以通过操作适配器来操控从设备。

    现在这样来理解:linux下的I2C驱动子系统,相对硬件来说肯定必须得先有驱动。有了驱动从设备才能有效工作,才能软性的帮助适配器操控从设备工作。所以对于S3C2440开发板我们要知道:

     (1)2440中的I2C控制器(i2c-s3c2410)有一个驱动(s3c2440中的I2C适配器驱动基于platform实现)。这个用来操作控制器来产生特定的I2C的时序信号,来发送数据和接收数据。也就是让适配器工作。

    (2)挂接在I2C总线上的从设备AT24C02(e2prom)为例,它也有一个驱动,这个用来操作读写我们的芯片,读取和存放具体获得的数据。

    在Linux系统中,上述的两个驱动,第一个属于I2C总线驱动,第二个属于I2C设备驱动。

    ======================================================================================================

    上面说到了I2C总线驱动I2C设备驱动,对于I2C驱动体系结构还有个最重要的就是I2C核心(出厂时内核已经自带)。既然是核心,那自然是通过一系列的通信方法(algorithm)把总线驱动,设备驱动与用户层串起来,其中还包括有总线驱动和设备驱动的注册,注销方法。

    ======================================================================================================

    现在我们知道:I2C驱动体系结构分为三大部分:I2C总线驱动; I2C 核心; I2C设备驱动。 

    =====================================================================================================
    注意:一般来说,如果CPU中集成了I2C控制器并且Linux内核支持这个CPU,那么总线驱动方面就不用我们操心了,内核已经做好了。但如果CPU中没有I2C控制器,而是外接的话,那么就要我们自己实现总线驱动了。对于设备驱动来说,一般常用的驱动也都包含在内核中了,如果我们用了一个内核中没有的芯片,那么就要自己来写了。
    =====================================================================================================

    第一部分:

    Linux中的I2C总线驱动(s3c2440的总线驱动i2c-s3c2410.c)

    定义描述具体I2C总线适配器的i2c_adapter数据结构、实现在具体I2C适配器上的I2C总线通信方法,并由i2c_algorithm数据结构进行描述。 经过I2C总线驱动的的代码,可以为我们控制I2C产生开始位、停止位、读写周期以及从设备的读写、产生ACK等。
    I2C总线驱动具体实现在/drivers/i2c目录下busses文件夹。例如:Linux I2C GPIO总线驱动为i2c_gpio.c. I2C总线算法在/drivers/i2c目录下algos文件夹。例如:Linux I2C GPIO总线驱动算法实现在i2c_algo_bit.c.

    T:I2C总线适配器由i2c_adapter结构体描述;I2C适配器上的I2C总线通信方法由i2c_algorithm结构体描述。一个总线驱动用于支持一条特定的I2C总线的读写。一个总线驱动通常需要两个模块,struct i2c_adapter和struct i2c_algorithm来描述

    I2C_adapter:构造一个对I2C core层接口的数据结构,并通过接口函数向I2C core注册一个控制器adapter。

    i2c_algorithm主要实现对I2C总线访问通信的具体算法。

    第二部分:

    Linux中的I2C设备驱动(at24.c属于s3c2440的设备AT24C02的驱动代码)

    是对具体I2C硬件驱动的实现。I2C 设备驱动通过I2C适配器与CPU通信。其中主要包含i2c_driveri2c_client数据结构,i2c_driver结构对应一套具体的驱动方法,例如:probe、remove、suspend等,需要自己申明。i2c_client数据结构由内核根据具体的设备注册信息自动生成,设备驱动根据硬件具体情况填充。具体使用下面介绍。

    I2C 设备驱动具体实现放在在/drivers/i2c目录下chips文件夹。

    i2c-dev.c:提供了用户层对I2C设备的访问,包括open,read,write,ioctl,release等常规文件操作,我们可以通过open函数打开 I2C的设备文件,通过ioctl函数设定要访问从设备的地址,然后可以通过 read和write或者ioctl函数完成对I2C设备的读写操作。

    I2C_client:对应于真实的物理设备,结构体中包含了芯片地址,设备名称,设备使用的中断号,设备所依附的控制器,设备所依附的驱动等内容。

    第三部分:

    I2C core核心(s3c2440的代码i2c-core.c)

    提供了核心数据结构的定义和相关接口函数,用来实现I2C适配器驱动和设备驱动的注册、注销管理,以及I2C通信方法上层的、与具体适配器无关的代码以及探测设备,检测设备地址的上层代码,为系统中每个I2C总线增加相应的读写方法。

    增加/删除i2c_adapter
    int i2c_add_adapter(struct i2c_adapter *adapter)
    int i2c_del_adapter(struct i2c_adapter *adap)
    //int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    void i2c_del_driver(struct i2c_driver *driver)

    i2c_client依附/脱离
    int i2c_attach_client(struct i2c_client *client)
    int i2c_detach_client(struct i2c_client *client)

    I2C传输,发送和接收
    int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
    int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
    int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

    以上是I2C驱动的大致的框架。有了大概的了解以后,就涉及到我们具体的设备使用了。毕竟I2C驱动也是为了我们能够更好的使用设备。

    1.首先对于我们的开发板来说我们需要进入到内核中去对其进行配置:


    发现网上的这个图更方便,两张就概括了。所以直接截图了,另外我是linux3.0内核。注:配置图片来自:http://blog.csdn.net/yj4231/article/details/18145973

    2.修改文件: linux/arch/arm/mach-s3c2440/mach-smdk2440.c
    增加如下代码片段:

    #include <linux/i2c/at24.h>
    static struct at24_platform_data at24c02 = {
    	.byte_len	= SZ_2K / 8,
    	.page_size	= 8,
    	.flags		= 0,
    };
    
    static struct i2c_board_info __initdata smdk_i2c_devices[] = {
    	/* more devices can be added using expansion connectors */
    	{
    		I2C_BOARD_INFO("24c02", 0x50),
    		.platform_data = &at24c02,
    	},
    };
    
    在smdk2440_machine_init函数中增加如下:

    i2c_register_board_info(0, smdk_i2c_devices, ARRAY_SIZE(smdk_i2c_devices));

    =====================================================================================================


    ==================================================================================================


    ==================================================================================================

    由datasheet中的A0,A1,A2以及百度百科AT24C02的芯片地址介绍可知设备地址为0x 10100000,这里确实与我们平常代码中的0x50不一致。我找寻了许多资料,只发现下面这种较为合理且清晰的解释:手册中at24c02的设备地址是0b 1 0 1 0 0 0 0 R/W, 其最低位是读写标志位,但是在Linux中,I2C设备地址的最高位为0,而低七位地址就是手册中去掉R/W的剩余7位。因此,地址为0b 01010000(0x50)

    ==================================================================================================

    3.然后重新编译进内核,启动开发板后进入目录/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/可以查看到name与eeprom显示如下:


    现在我们来谈谈具体编程操作,网上说编写I2C设备驱动有两种方法:

    一种是利用系统给我们提供的i2c-dev.c来实现一个i2c适配器的设备文件。然后通过在应用层操作I2C适配器来控制i2c设备。另一种是为i2c设备,独立编写一个设备驱动。

    我这里实现的都是利用i2c-dev.c提供的函数接口在应用层通过适配器来控制I2c设备。

    code 1:

    /*********************************************************************************
     *      Copyright:  (C) 2015 songyong<handy_skyoutlook.com>
     *                  All rights reserved.
     *
     *       Filename:  i2c.c
     *    Description:  This file 
     *                 
     *        Version:  1.0.0(2015年05月23日)
     *         Author:  sky <handy_sky@outlook.com>
     *      ChangeLog:  1, Release initial version on "2015年05月23日 16时34分51秒"
     *                 
     ********************************************************************************/
    #include<stdio.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/ioctl.h>
    #include<stdlib.h>
    #include<fcntl.h>
    #include<sys/io.h>
    #include<errno.h>
    
    
    #define LEN 256
    #define PRINT_ERROR printf("error: %d:%s",errno,strerror(errno))
    
    
    /********************************************************************************
     *  Description:
     *   Input Args:
     *  Output Args:
     * Return Value:
     ********************************************************************************/
    int main (int argc, char **argv)
    {
       int ret,fd,i,j;
       char read_data[LEN];
       char write_data[LEN] ="<Sky_handy@outlook.com>\n";
       char offset[LEN];
    
       memset(offset,0,sizeof(offset));
       fd = open("/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom",O_RDWR);
       if(fd < 0)
       {
           printf("Open AT24C02 fail.\n"); 
            return -1;
       }
       /*  
       ret = read(fd, offset,256);
       if(ret < 0)
       {
           printf("Read AT24C02 fail.\n"); 
            return -1;
       }
        strncpy(read_data, offset,sizeof(offset));
        for(i = 0 ; i<sizeof(read_data); i++)
        {
          printf("%c\n",read_data[i]);
        }//读出当前EEPROM中的数据
    */
       ret = lseek(fd, 0, SEEK_SET);//调整偏移量,从开始位置写入
       if(-1 == ret)
       {
            PRINT_ERROR; 
            return -1;
       }
       ret = write(fd ,write_data, sizeof(write_data));
       if(ret < 0)
       {
        printf("Write error\n");
        return -1;
       }//将指定数据写入当前EEPROM中
       lseek(fd, 0, SEEK_SET);//重新设置偏移量从起始位置读取
       ret = read(fd , read_data, LEN);
        if(ret < 0)
        {
            printf("Read error\n"); 
            return -1;
        }
        else if(ret < 256)
        {
            printf("Incomplete read\n");
            return -1;
        }
        printf("%s\n",read_data);
        return 0;
    } /* ----- End of main() ----- */

    上面是通过at24.c的read和write以及lseek直接对I2C设备(EEPROM)进行读写操作,其实依然是利用i2c-dev.c,最终调用的还是其中的read,write;在open打开设备节点后能我们虽然能直接对从设备EEPROM进行读写操作,但不方便的是不能随意指定地址插入数据,只能通过偏移量来改变或者插入数据。(此类方法是把I2C设备当作一个普通的字符设备来处理

    code2:

    /*********************************************************************************
     *      Copyright:  (C) 2015 songyong<handy_skyoutlook.com>
     *                  All rights reserved.
     *
     *       Filename:  i2c_master.c
     *    Description:  This file 
     *                 
     *        Version:  1.0.0(2015年05月28日)
     *         Author:  sky <handy_sky@outlook.com>
     *      ChangeLog:  1, Release initial version on "2015年05月28日 21时06分40秒"
     *                 
     ********************************************************************************/
    #include<stdio.h>
    #include<stdlib.h>
    #include<linux/i2c.h>
    #include<linux/i2c-dev.h>
    #include<string.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<errno.h>
    /********************************************************************************
     *  Description:
     *   Input Args:
     *  Output Args:
     * Return Value:
     ********************************************************************************/
    int main (int argc, char **argv)
    {
        int i, fd, ret, length;
        unsigned char rdwr_addr = 0x00;  //eeprom 读写地址
        unsigned char device_addr = 0x50; //eeprom 设备地址
        unsigned char data[] = "test1234\n";
        struct i2c_rdwr_ioctl_data e2prom_data;
    
        printf("open i2c device...\n");
        fd = open("/dev/i2c-0",O_RDWR);
        if(fd < 0)
                {
                            printf("open faild");
                                    return -1;
                    
                }
         e2prom_data.msgs =(struct i2c_msg *)malloc(e2prom_data.nmsgs * sizeof(struct i2c_msg));
         if(e2prom_data.msgs == NULL)
         {
                    printf("malloc error");
                            return -1;
                                
         }
        
        ioctl(fd, I2C_TIMEOUT, 1);// 设置超时
        ioctl(fd, I2C_RETRIES, 2);// 设置重试次数
                
         /*  向e2prom中写入数据 */
         length = sizeof(data);
         e2prom_data.nmsgs = 1;
         e2prom_data.msgs[0].len = length;
         e2prom_data.msgs[0].addr = device_addr;
                              
         e2prom_data.msgs[0].buf =(unsigned char *)malloc(length);
         e2prom_data.msgs[0].buf[0] = rdwr_addr;
         for(i = 0;i<length; i++)
         e2prom_data.msgs[0].buf[1+i] = data[i]; /*  write data */
    
         ret = ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);
         if(ret <0)
            {
                        perror("write data error");
                                return -1;
            }
        printf("write data: %s to address %#x\n", data, rdwr_addr);
        return 0;
    } /* ----- End of main() ----- */

    同样是操作i2c设备eeprom,这份代码却和上面把设备当作普通文件来进行读写操作的代码不太相同,下面我们通过代码的具体实例来了解一下I2C驱动体系中的相关结构:

    在linux下的I2C驱动体系中,我们往往会通过I2C控制器来操控I2C从设备,而其在内核中消息的传递又涉及到内核中相关的结构体并且必须要遵循如下读写时序:


    上述代码我们是用cpu内部的I2C控制器通过i2c-dev.c提供的文件接口来操控从设备。其中我们首先要接触的就是i2c-dev.c里面的i2c_rdwr_ioctl_data结构体。这里要提一下,i2c-dev.c中的read、write函数不适用于多个开始信号的数据传送。只适合发了开始信号之后的一次性数据传输。一句话就是ioctl可替代read,write,且比它们都好用。(i2c_msg与i2c_rdwr_ioctl_data结构体都是定义在i2c-dev.h头文件中)

    struct i2c_rdwr_ioctl_data {  
             struct i2c_msg __user *msgs;         /* pointersto i2c_msgs */  
             __u32 nmsgs;                    /* number ofi2c_msgs */  
    };  //Msgs     表示单个开始信号传递的数据;
    	//Nmsgs     表示有多少个msgs
    从上面的结构体我们再来看看里面的i2c_msg这个结构体
    struct i2c_msg {  
             __u16 addr;     /* slave address                         */  
             __u16 flags;  /* 默认为写入 0 */  
             __u16 len;                  /* msg length                              */  
             __u8 *buf;                 /* pointer to msgdata                       */  
    };  
    

    我们在ioctl调用之前必须先填充好i2c_msg消息结构体以及nmsgs成员。我们可以看到消息结构体里面有从设备地址,读写标志,数据长度以及存储数据buf。这些成员我们看完之后会发现它大致符合先给设备地址,然后给写信号以及数据的时序。其实但我们写代码的时候并不一定是addr非得定义在flags前面,因为内核会自动帮助我们完成这些具体的时序操作。但有一点,我们必须要填充好nmsgs以及i2c_msg中的成员

    那么我们具体的i2c下的 ioctl 函数是怎么样的呢?我们暂且把i2c-dev.c看作一个设备驱动。里面的fops结构体显示


    我们继续追踪看看i2cdev_ioctl这个函数

     static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
        struct i2c_client *client = file->private_data;
        unsigned long funcs;
    
        dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
            cmd, arg);
    
        switch (cmd) {
        case I2C_SLAVE:
        case I2C_SLAVE_FORCE:
            /* NOTE:  devices set up to work with "new style" drivers
             * can't use I2C_SLAVE, even when the device node is not
             * bound to a driver.  Only I2C_SLAVE_FORCE will work.
             *
             * Setting the PEC flag here won't affect kernel drivers,
             * which will be using the i2c_client node registered with
             * the driver model core.  Likewise, when that client has
             * the PEC flag already set, the i2c-dev driver won't see
             * (or use) this setting.
             */
            if ((arg > 0x3ff) ||
                (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
                return -EINVAL;
            if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
    			            return -EBUSY;
            /* REVISIT: address could become busy later */
            client->addr = arg; //设置addr
            return 0;
        case I2C_TENBIT://设置10 bit地址模式
            if (arg)
                client->flags |= I2C_M_TEN;
            else
                client->flags &= ~I2C_M_TEN;
            return 0;
        case I2C_PEC://设置传输后增加PEC标志
            if (arg)
                client->flags |= I2C_CLIENT_PEC;
            else
                client->flags &= ~I2C_CLIENT_PEC;
            return 0;
        case I2C_FUNCS://获取函数支持
            funcs = i2c_get_functionality(client->adapter);
            return put_user(funcs, (unsigned long __user *)arg);
    
        case I2C_RDWR://读取和发送数据
            return i2cdev_ioctl_rdrw(client, arg);
    
        case I2C_SMBUS: //SMBUS协议数据传输
            return i2cdev_ioctl_smbus(client, arg);
    		
        case I2C_RETRIES://设置重试次数
            client->adapter->retries = arg;
            break;
        case I2C_TIMEOUT://设置超时时间
            /* For historical reasons, user-space sets the timeout
             * value in units of 10 ms.
             */
            client->adapter->timeout = msecs_to_jiffies(arg * 10);
            break;
        default:
            /* NOTE:  returning a fault code here could cause trouble
             * in buggy userspace code.  Some old kernel bugs returned
             * zero in this case, and userspace code might accidentally
             * have depended on that bug.
             */
            return -ENOTTY;
        }
        return 0;
    }
    对于简单使用来说,我现在并没有全深入整明白,所以暂且知道:

    ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。
    在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}的结构,每一个case对应一个cmd操作命令,并有相对应的操作。

    我这里cmd参数使用的是I2C_RDWR这个命令码,根据i2c-dev.c里的源码看i2cdev_ioctl_rdrw结构体可知

    I2C 设备的写操作经历了如下几个步骤。
    (1) 从用户空间到字符设备驱动写函数接口,写函数构造 I2C 消息数组。
    (2) 写函数把构造的 I2C 消息数组传递给 I2C 核心的传输函数 i2c_transfer()。
    (3) I2C 核心的传输函数 i2c_transfer()找到对应适配器 algorithm 的通信方法函数 master_xfer()去最终完成 I2C 消息的处理。

    PS:I2c_transfer这个函数实现了core与adapter的联系。想更深入的探究,可以自己去看看I2C总线驱动中的I2C_algorithm结构以及其中的s3c24xx_i2c_xfer(),s3c24xx_i2c_doxfer()和s3c24xx_i2c_message_start()函数和i2c_transfer()函数。

    下面我们看看代码在开发板中运行的现象:

    1.在把可执行文件放入开发板启动之前我们先检查下I2C控制器s3c2410-i2c节点是否配置好。


    上面的i2c_dev/i2c-0是在注册i2c-dev.c后产生的,代表一个可操作的适配器。如果不使用i2c-dev.c的方式,就没有,也不需要这个节点。

    2.确保出现I2C控制器s3c2410-i2c后即可通过交叉编译器将编译后可执行文件放入开发板中执行。


    仔细看上面的结果。可以看到我们原本的数据test1234\n在上面的显示中出了点问题。后来我又测试一次test123\n与test12345678

    我第一次是通过at24.c的read和write以及lseek直接对eeprom读写,所以可以一次写入超过8个字节,第二次通过内部i2c控制器的ioctl来间接读写eeprom时则遭遇了阻击,一次时序,若超过8个字节,超过的字节数自动将前面的数据覆盖掉。然后我想到at24c02是32个页,一页8个字节。通过网上查阅知:

    ===================================================================================================

    ================================================================================================================================

    由于E2PROM的半导体工艺特性,对E2PROM的写入时间要5~10ms,但AT24CXX系列串行E2PROM芯片内部设置了一个具有SRAM性质的输入缓冲器,称为页写缓冲器。CPU对该芯片写操作时,AT24CXX系列芯片先将CPU输入的数据暂存页写缓冲器内,然后,慢慢写入E2PROM中。因此,CPU对AT24CXX系列E2PROM一次写入的数据,受到该芯片页写缓冲器容量的限制。页写缓冲器的容量:AT24C01A/02为8B,AT24C04/08/16为16B,AT24C32/64为32B。

    ====================================================================================================
    注意:
    写AT24CXX应用时,若CPU需写入超过芯片页写缓冲器容量的字节数据,应在一页写完后,隔5~10ms重新启动一次写操作。其次,若不是从页写缓冲器零地址(指AT24CXX片内末位地址0或8)写起,一次写入不能超出页内地址111,若超出页写缓冲器最大地址时,也应将超出部分,隔5~10ms重新启动一次写操作。

    最后通我们回顾AT24C02的官方datasheet来看看本质:

    Byte write:


    ==================================================================================================================

    Byte write的操作时序如上图所示。主机在发送device address,并且接受到确定回应Ask后再接着发送需要写的地址(把这个数据写到芯片的哪个地址上),然后收到确定回应ask后再发送数据。当AT24C02接受完毕这个数据时会输出一个确认回应Ack,此时主机发送一个停止信号Stop,然后AT240C2进入写时序,将刚才接受到的数据从缓冲器写到存储单元中,并在此期间不响应任何输入,直到操作完成。

    ==================================================================================================================

    Pagewrite:


    ==================================================================================================================

    Page write前面几步的操作和Bytewrite操作类似,只是在成功发送第一个数据之后,主机在收到AT24C02的确认回应Ask之后不会发送停止信号Stop而是继续发送剩余的7个字节数据。直到一个page的8字节数据发送完毕之后才发送停止信号Stop。在页操作的时候word address用与表示业内的低地址的低3bit会每收到一个数据就自动增长,页地址维持不变。所以,当业内地址到顶端时,此时假如还有数据,则数据将会被放到页的起始地址处,页起始地址中之前存放的数据也将会被覆盖。即AT24C02页操作时,写入的数据大于8byte,则大于8byte的数据将重新从此页起始处存放,并覆盖掉之前写入的的数据

    ==================================================================================================================

    另外我们再说一下随机读取:


    ==================================================================================================================

    随机读写的操作就是主机先用一个写操作来骗过AT24C02器件,使AT24C02内部的data word address中的地址值修改,然后再通过current address read操作来读取所需地址上的数据。平时我们所说的读操作之前往往要先写之后才能读。这里也是如此,我们主机先发送一个写操作,但是发送完毕之后并不发送数据,而是发送一个Start开始信号,此时AT24C02中的data wordaddress中的地址值已经修改了,然后通过current address read去读取当前地址上的数据。

    ==================================================================================================================

    最后贴上Linux下I2C驱动体系容易理解的图解:



    Reference:

    http://www.360doc.com/content/13/1207/14/3038654_335207399.shtml#

    http://blog.csdn.net/paul_liao/article/details/6982883

    http://blog.csdn.net/ypoflyer/article/details/6376545

    展开全文
  • Linux下I2C驱动架构全面分析

    千次阅读 2015-12-22 09:37:01
    最近在看Linux驱动架构, 因为之前接触过无操作系统的I2C驱动的写法, 所以详细了解了下Linux下I2C驱动的架构和写法,过程中参考了许多网上的文章, 发现此篇文章写得最好, 故转载,以便以后查阅。 I2C是...
  • 前言:因为工作是音频驱动,所以经常涉及到I2C、I2S等常用的总线,想将I2C相关的东西总结一下,让自己更加了解I2C。方式一:使用arch/arm/mach-s3c24xx/mach-mini2440.c举例:static struct i2c_board_info mini2440...
  • 详解Linux-I2C驱动

    千次阅读 2016-02-25 18:06:54
    1.5 参考二、LinuxI2C驱动--I2C总线 2.1 I2C总线物理结构 2.2 I2C总线特性 2.3 开始和停止条件 2.4 数据传输格式 2.5 响应 2.6 总线仲裁三、LinuxI2C驱动--解析EEPROM的读写 3.1 概述 3.2 设备地址
  • linux下I2C驱动架构全面分析

    千次阅读 2017-10-17 16:57:21
    linux下I2C驱动架构全面分析 转载 2013年12月31日 11:04:31 I2C 概述  I2C是philips提出的外设总线.  I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就...
  • 上一篇,我介绍了如何在uboot中添加i2c设备,以及移植i2c的读写接口。简单来说uboot阶段使用i2c设备和平台关联性比较大,但不同平台套路是差不多的。你可以将uboot阶段看作是引导android系统起来的另外一系统。而...
  • Linux下使用IIC总线 读写i2c从设备寄存器 by 韩大卫 @吉林师范大学 handawei@jusontech.com 转载请务必表明出处 ******************* ********************************************** 2012.7.16 1,本文给...
  • linux下I2C驱动记录(RK)

    千次阅读 2013-09-04 16:49:48
    现在做的是RK平台的I2C驱动,不是说linux每个外设对设对应一个设备文件吗?可是找了一下一直没找到,今天特别搞了一下。I2C设备是注册为platform平台驱动的。下面看几个图。 下面是我用adb命令查看的  linux的设备...
  • linux驱动开发9之I2C驱动模型

    千次阅读 2018-08-12 19:00:19
    假设soc芯片个i2c adapter:i2c_adapter1,i2c_adapter1;然后外部个i2c接口的设备i2c_device1,i2c_device2,i2c_device3。 现在要求在裸机写出他们的驱动函数。那么肯定要写出6个不同的驱动函数: i2c...
  • Linux下I2C总线驱动

    千次阅读 2012-09-23 21:21:42
    版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127  原创作者:南京邮电大学 通信与信息系统专业 研二 魏清   一.... 1. I2C驱动体系概述 ... Linux I2C驱动体系结构主要由3部分组成,即I2
  • Linux I2C子系统分析-I2C总线驱动

    千次阅读 2012-01-19 15:56:32
    在drivers/i2c/busses包含各种I2C总线驱动,如S3C2440的I2C总线驱动i2c-s3c2410.c,使用GPIO模拟I2C总线的驱动i2c-gpio.c,这里只分析i2c-gpio.c。 i2c-gpio.c它是gpio模拟I2C总线的驱动,总线也是设备,在这里...
  • 总结在linux环境下四种读写I2C设备的方法:一:读写/dev/i2c-x设备结点 /dev/i2c-x设备结点对应的驱动文件为内核目录drivers/i2c下自带的i2c-dev.c文件,通读此文件可以发现它的工作流程。 1、通过register_chrdev...
  • Linux I2C总线详解

    万次阅读 2018-03-12 09:13:51
    概述: 1.I2C概念 2.I2C硬件结构图 3.I2C总线初始化 4.I2C控制器device 节点添加及driver注册 5.I2C设备节点添加及driver注册 5....7.设备是如何使用I2C通讯的一.I2C概念: I2C是philips提出的外设总线. ...
  •  本文主要介绍i2c总线框架,即对i2c的各个层次(i2c总线,i2c核心,i2c设备)进行分析。同时我也会结合程序对框架进行说明。所以本文将分为两部分,第一部分对i2c的框架进行介绍,而第二部分就是结合代码分析。 ...
  • Linux i2c子系统

    千次阅读 2016-06-20 20:44:07
    经常可以看到在驱动程序中会定义一struct i2c_driver的数据结构,并实现里面的某些成员,比如probe、resume、suspend、remove、id_table,之后会在module_init()中调用i2c_add_driver()添加这驱动。比如说像下面...
  • linux i2c驱动总结

    千次阅读 2011-03-01 11:36:00
    最近天一直在看I2C的驱动架构,看到目前的感觉是说明白吧又不是很透彻,说不明白吧又知道点,总之有点不明白。 明白的: 1)adapter驱动,I2C控制器驱动,如果要开发I2C控制器的,这部分是要实现...
  • Linux内核模块中操作I2C设备

    千次阅读 2013-12-04 14:07:22
    虽然在Linux中操作使用I2C设备并不复杂,但本人接触Linux内核驱动开发时间并不算长,此次学习中也算较为系统的了解了Linux中对I2C设备的操控方式,谨在此做记录。 通过Linux内核文档中关于操作I2C设备的文章...
  • 最近在做一个I2C键盘的Linux驱动,参考了其他芯片的一些代码,其中陆续发现有些让人迷惑的东西,把我的迷惑及理解在这里加以记录:1. i2c_driver结构体的probe成员的原型: int (*probe)(struct i2c_client *, ...
  • 编写i2c驱动-基于Linux3.10

    千次阅读 2014-11-16 08:52:47
    彻底的弄懂i2c设备驱动,对于理解其它驱动非常帮助,从投入和产出比而言还是值得的。 I2c设备分为两种,一种叫i2c适配器(adpater),一种客户端(client);与client类似的可以参考USB,我们通常使用的u盘、usb...
  • I2C总线因为它及简单的硬件连接和通讯方式,在现在的很多设备上它是一种不可或缺的通讯总线。...(一)I2C架构概述LinuxI2C体系架构分为3组成部分:(1)I2C核心:I2C核心提供了I2C总线驱动和...
  • Linux i2c_client和i2c_driver的创建与绑定

    千次阅读 2017-07-13 12:27:44
    linux下i2c驱动笔记 1. 几个基本概念 1.1. 设备模型 由 总线(bus_type) + 设备(device) + 驱动(device_driver) 组成,在该模型,所有的设备通过总线连接起来,即使有些设备...
  • 58 linux i2c设备驱动之eeprom驱动

    千次阅读 2017-07-02 22:54:55
    eeprom是i2c接口的at24c04芯片, at24c04中的”04”表示容量是4k位.at24c04的设备地址: 上图表示设备地址前4位是固定的”1010”, 后三位是由芯片的引脚A2、A1,A0的电平来决定。通常A0~A2脚都是接地址,表示设备...
  • linux I2C设备写操作错误的分析过程

    千次阅读 2014-09-24 22:00:34
    一般出现Input/output error错误,都跟硬件多少有点关系,但是在其他版本的SDK上测试确认这个I2C设备没问题,可以正常工作。所以认为是I2C通信没问题,肯定是其他问题导致的。 I2C的通信依靠SDA、SCL,这两根控制...
  • Linux I2C设备驱动编写(一)

    万次阅读 多人点赞 2014-03-16 23:24:20
    Linux驱动中I2C系统中主要包含以下几个成员: I2C adapter 即I2C适配器 I2C driver 某个I2C设备的设备驱动,可以以driver理解。 I2C client 某个I2C设备的设备声明,可以以device理解。 I2C adapter 是CPU集成或...
  • 虽然I2C硬件体系结构和协议都很容易理解,但是Linux I2C驱动体系结构却相当的复杂度,它主要由3部分组成,即I2C设备驱动、I2C总线驱动和I2C核心。I2C核心是I2c总线和I2c设备驱动的中间枢纽,它以通用的、与平台...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 350,359
精华内容 140,143
关键字:

linux下有几个i2c

linux 订阅