Chinaunix首页 | 论坛 | 博客
  • 博客访问: 511716
  • 博文数量: 398
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 14
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-21 16:02
个人简介

嵌入式屌丝

文章分类

全部博文(398)

文章存档

2013年(398)

我的朋友

分类: LINUX

2013-08-23 09:38:18

原文地址:Linux设备之I2C 作者:_ChinaUnix

    在内核中已经提供I2C子系统,在linux系统中,I2C驱动结构如下图所示:
其中从上图可以I2C由三大部分组成:
1、I2C核心:I2C核心提供了总线驱动和设备驱动的注册、注销的方法,I2C通信方法,与具体适配器无关的代码以及检测设备地址的代码等。
2、I2C总线驱动:对I2C硬件体系结构中适配器的实现,控制I2C总线驱动的代码,控制I2C适配器以主控方式产生开始位,停止位,读写以及设备读写方式,产生ack等。
3、I2C客户驱动程序:是对I2C硬件体系结构设备端得实现。
我们在分析其子系统之前,还是来看看其数据结构
i2c_adapter结构体表示一个物理的i2c总线控制器

点击(此处)折叠或打开

  1. struct i2c_adapter {
  2.     struct module *owner;
  3.     unsigned int class;         /* classes to allow probing for */
  4.     const struct i2c_algorithm *algo; /* the algorithm to access the bus */
  5.     void *algo_data;

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

  8.     int timeout;            /* in jiffies */
  9.     int retries;
  10.     struct device dev;        /* the adapter device */

  11.     int nr;
  12.     char name[48];       //适配器名字
  13.     struct completion dev_released;   //用于同步

  14.     struct mutex userspace_clients_lock;
  15.     struct list_head userspace_clients;
  16. };
上面包含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,
  5.              unsigned short flags, char read_write,
  6.              u8 command, int size, union i2c_smbus_data *data);//SMBUS传输函数指针

  7.     u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能
  8. };
一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器产生特定的访问函数。其中提供了关键的函数master_xfer()用于产生i2c访问周期需要的信号,以i2c_msg为单位

点击(此处)折叠或打开

  1. struct i2c_msg {
  2.     __u16 addr;    /* slave address            */
  3.     __u16 flags;
  4.     __u16 len;        /* msg length                */
  5.     __u8 *buf;        /* pointer to msg data            */
  6. };
i2c_driver代表i2c从设备驱动

点击(此处)折叠或打开

  1. struct i2c_driver {
  2.     unsigned int class;

  3.     int (*attach_adapter)(struct i2c_adapter *) __deprecated;//适配器函数指针
  4.     int (*detach_adapter)(struct i2c_adapter *) __deprecated;

  5.     /* Standard driver model interfaces */
  6.     int (*probe)(struct i2c_client *, const struct i2c_device_id *);
  7.     int (*remove)(struct i2c_client *);

  8.     /* driver model interfaces that don't relate to enumeration */
  9.     void (*shutdown)(struct i2c_client *);
  10.     int (*suspend)(struct i2c_client *, pm_message_t mesg);
  11.     int (*resume)(struct i2c_client *);

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


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

  14.     struct device_driver driver;
  15.     const struct i2c_device_id *id_table;

  16.     /* Device detection callback for automatic device creation */
  17.     int (*detect)(struct i2c_client *, struct i2c_board_info *);
  18.     const unsigned short *address_list;
  19.     struct list_head clients;
  20. };
i2c_client代表i2c从设备

点击(此处)折叠或打开

  1. struct i2c_client {
  2.     unsigned short flags;        /* div., see below        */
  3.     unsigned short addr;        /* chip address - NOTE: 7bit    */
  4.                     /* addresses are stored in the    */
  5.                     /* _LOWER_ 7 bits        */
  6.     char name[I2C_NAME_SIZE];
  7.     struct i2c_adapter *adapter;    /* the adapter we sit on    */
  8.     struct i2c_driver *driver;    /* and our access routines    */
  9.     struct device dev;        /* the device structure        */
  10.     int irq;            /* irq issued by device        */
  11.     struct list_head detected;
  12. };
     i2c_driver与i2c_client是一对多的关系,一个i2c_driver上可以支持多个同等类型的i2c_client。i2c_adapter与i2c_client的关系与i2c硬件体系中适配器和从设备的关系一致,i2c_client依附在i2c_adapter。
下面来看看内核提供任何去注册一个i2c的设备驱动
1.直接使用i2c_register_board_unfo完成设备注册,这种方法适合于i2c总线上预先已知设备,因此可以预先声明i2c设备在哪条总线上,在通过数组结构i2c_board_info,内核实例为arch\arm\mach-omap2\board-h4.c。

点击(此处)折叠或打开

  1. static struct i2c_board_info __initdata h4_i2c_board_info[] = {
  2.     {
  3.         I2C_BOARD_INFO("isp1301_omap", 0x2d),
  4.         .irq        = OMAP_GPIO_IRQ(125),
  5.     },
  6.     {    /* EEPROM on mainboard */
  7.         I2C_BOARD_INFO("24c01", 0x52),
  8.         .platform_data    = &m24c01,
  9.     },
  10.     {    /* EEPROM on cpu card */
  11.         I2C_BOARD_INFO("24c01", 0x57),
  12.         .platform_data    = &m24c01,
  13.     },
  14. };

点击(此处)折叠或打开

  1. static void __init omap_h4_init(void)
  2. {
  3.     (...)
  4.     i2c_register_board_info(1, h4_i2c_board_info,
  5.             ARRAY_SIZE(h4_i2c_board_info));
  6.     (...)
  7. }
那么内核怎么完成匹配呢?首先来看看i2c_register_board_info()

点击(此处)折叠或打开

  1. int __init
  2. i2c_register_board_info(int busnum,
  3.     struct i2c_board_info const *info, unsigned len)
  4. {
  5.     int status;

  6.     down_write(&__i2c_board_lock);

  7.     /* dynamic bus numbers will be assigned after the last static one */
  8.     if (busnum >= __i2c_first_dynamic_bus_num)
  9.         __i2c_first_dynamic_bus_num = busnum + 1;

  10.     for (status = 0; len; len--, info++) {
  11.         struct i2c_devinfo    *devinfo;

  12.         devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
  13.         if (!devinfo) {
  14.             pr_debug("i2c-core: can't register boardinfo!\n");
  15.             status = -ENOMEM;
  16.             break;
  17.         }

  18.         devinfo->busnum = busnum;
  19.         devinfo->board_info = *info;
  20.         list_add_tail(&devinfo->list, &__i2c_board_list);
  21.     }

  22.     up_write(&__i2c_board_lock);

  23.     return status;
  24. }
这上面只做了一个非常重要的,将i2c_board_info放入到__i2c_board_list链表,而这个info中存放的是i2c通信非常重要的,设备名字和设备地址。那么链表何时使用呢?这个在i2c_scan_static_board_info的时候会调用

点击(此处)折叠或打开

  1. static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
  2. {
  3.     struct i2c_devinfo    *devinfo;

  4.     down_read(&__i2c_board_lock);
  5.     list_for_each_entry(devinfo, &__i2c_board_list, list) {
  6.         if (devinfo->busnum == adapter->nr
  7.                 && !i2c_new_device(adapter,
  8.                         &devinfo->board_info))
  9.             dev_err(&adapter->dev,
  10.                 "Can't create device at 0x%02x\n",
  11.                 devinfo->board_info.addr);
  12.     }
  13.     up_read(&__i2c_board_lock);
  14. }
该函数遍历挂载__i2c_board_list链表上面的i2c设备的信息,也就是我们在启动的时候指出的i2c设备的信息,如果指定设备位于adapter所在的i2c总线上,那么就调用i2c_new_device()。
2.直接使用2c_new_device, i2c_new_probed_device

点击(此处)折叠或打开

  1. struct i2c_client *
  2. i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
  3. {
  4.     struct i2c_client    *client;
  5.     int            status;

  6.     client = kzalloc(sizeof *client, GFP_KERNEL);
  7.     if (!client)
  8.         return NULL;

  9.     client->adapter = adap;

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

  11.     if (info->archdata)
  12.         client->dev.archdata = *info->archdata;

  13.     client->flags = info->flags;
  14.     client->addr = info->addr;
  15.     client->irq = info->irq;

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

  17.     /* Check for address validity */
  18.     status = i2c_check_client_addr_validity(client);
  19.     if (status) {
  20.         dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
  21.             client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
  22.         goto out_err_silent;
  23.     }

  24.     /* Check for address business */
  25.     status = i2c_check_addr_busy(adap, client->addr);
  26.     if (status)
  27.         goto out_err;

  28.     client->dev.parent = &client->adapter->dev;
  29.     client->dev.bus = &i2c_bus_type;
  30.     client->dev.type = &i2c_client_type;
  31.     client->dev.of_node = info->of_node;

  32.     /* For 10-bit clients, add an arbitrary offset to avoid collisions */
  33.     dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
  34.          client->addr | ((client->flags & I2C_CLIENT_TEN)
  35.                  ? 0xa000 : 0));
  36.     status = device_register(&client->dev);
  37.     if (status)
  38.         goto out_err;

  39.     dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
  40.         client->name, dev_name(&client->dev));

  41.     return client;

  42. out_err:
  43.     dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
  44.         "(%d)\n", client->name, client->addr, status);
  45. out_err_silent:
  46.     kfree(client);
  47.     return NULL;
  48. }
该函数只是device_register,但是上面出现了一个新的结构,i2c_client,其实它就是一个struct device的i2c设备的封装。在client里保存该设备的相关信息,client->adapter指向了它所在的adapter。clent->dev所在的bus为i2c_bus_type,在device_register注册的时候,会调用总线的match函数。

点击(此处)折叠或打开

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

点击(此处)折叠或打开

  1. static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
  2.                         const struct i2c_client *client)
  3. {
  4.     while (id->name[0]) {
  5.         if (strcmp(client->name, id->name) == 0)
  6.             return id;
  7.         id++;
  8.     }
  9.     return NULL;
  10. }
上面是匹配driver的id_table的名字和client的名字是否相同,那么会调用驱动的probe函数。
下面在分析下i2c_new_probed_device有啥不一样,前面一个认为设备肯定存在,而后面的是对于已经识别出来的设备,才会创建。

点击(此处)折叠或打开

  1. for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
  2.         /* Check address validity */
  3.         if (i2c_check_addr_validity(addr_list[i]) < 0) {
  4.             dev_warn(&adap->dev, "Invalid 7-bit address "
  5.                  "0x%02x\n", addr_list[i]);
  6.             continue;
  7.         }

  8.         /* Check address availability */
  9.         if (i2c_check_addr_busy(adap, addr_list[i])) {
  10.             dev_dbg(&adap->dev, "Address 0x%02x already in "
  11.                 "use, not probing\n", addr_list[i]);
  12.             continue;
  13.         }

  14.         /* Test address responsiveness */
  15.         if (probe(adap, addr_list[i]))
  16.             break;
  17.     }
3.直接i2c_add_driver
前面的2中方法,都要实现确定适配器,如果我们不知道这个i2c设备在那个适配器上,怎么办?内核提供了一种去class表示在所有的适配器上查找一些i2c设备的地址。

点击(此处)折叠或打开

  1. static struct i2c_driver at24_driver = {
  2.     .driver = {
  3.         .name = "at24",
  4.         .owner = THIS_MODULE,
  5.     },
  6.     .probe = at24_probe,
  7.     .remove = __devexit_p(at24_remove),
  8.     .id_table = at24_ids,
  9. };

点击(此处)折叠或打开

  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.     io_limit = rounddown_pow_of_two(io_limit);
  8.     return i2c_add_driver(&at24_driver);
  9. }
而i2c_add_diver只是调用i2c_register_driver了

点击(此处)折叠或打开

  1. int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
  2. {
  3.     int res;

  4.     /* Can't register until after driver model init */
  5.     if (unlikely(WARN_ON(!i2c_bus_type.p)))
  6.         return -EAGAIN;

  7.     /* add the driver to the list of i2c drivers in the driver core */
  8.     driver->driver.owner = owner;
  9.     driver->driver.bus = &i2c_bus_type;

  10.     /* When registration returns, the driver core
  11.      * will have called probe() for all matching-but-unbound devices.
  12.      */
  13.     res = driver_register(&driver->driver);
  14.     if (res)
  15.         return res;

  16.     /* Drivers should switch to dev_pm_ops instead. */
  17.     if (driver->suspend)
  18.         pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
  19.             driver->driver.name);
  20.     if (driver->resume)
  21.         pr_warn("i2c-core: driver [%s] using legacy resume method\n",
  22.             driver->driver.name);

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

  24.     INIT_LIST_HEAD(&driver->clients);
  25.     /* Walk the adapters that are already present */
  26.     i2c_for_each_dev(driver, __process_new_driver);

  27.     return 0;
  28. }
上面比较主要的是driver_register,同事遍历driver->client链表,会调用driver->detect的函数。

阅读(599) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~