Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7891
  • 博文数量: 3
  • 博客积分: 55
  • 博客等级: 民兵
  • 技术积分: 32
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-30 17:57
文章分类
文章存档

2015年(2)

2009年(1)

我的朋友
最近访客

分类: LINUX

2015-12-10 10:43:01

      要了解I2C驱动框架的话,要先了解一linux设备驱动,也可以通过i2c驱动反过来进一步理解 linux的设备驱动。玩多了,就知道了。

一、预备知识:linux设备驱动   bus device driver
        点击(此处)折叠或打开
  1. struct bus_type {
  2.     const char        *name;                    // 总线名称
  3.     struct bus_attribute    *bus_attrs;        // 总线属性,用于 /sys/bus/ 下的显示
  4.     struct device_attribute    *dev_attrs;
  5.     struct driver_attribute    *drv_attrs;

  6.     int (*match)(struct device *dev, struct device_driver *drv);        // device 和 driver 的匹配操作,总线上的匹配方法,例如,
  7.                                                                         // 会有很多的I2C 设备和I2C驱动,简单的可以通过名称来匹配
  8.                                                                         // 每个 i2c设备用 i2c_client,
  9.                                                                         // 每个 i2c driver 中,含有 id_table 其中含有对应的i2c设备的名称。
  10.                                                                         // 只要这两个对应上,这个 i2c_client 就是对应这个 i2c_driver
  11.                                                                         
  12.     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);        // 事件处理,未研究
  13.     int (*probe)(struct device *dev);                // i2c_driver 中也有 probe 不过,优先会用总线上的这个probe,当添加设备,或添加驱动时会调用到
  14.     int (*remove)(struct device *dev);                // 移除
  15.     void (*shutdown)(struct device *dev);

  16.     int (*suspend)(struct device *dev, pm_message_t state);
  17.     int (*resume)(struct device *dev);

  18.     const struct dev_pm_ops *pm;

  19.     struct bus_type_private *p;                // 总线的私有信息,为什么叫 private呢,因为这个数据外部调用者是不需要关心的。
  20.                                             // 其中有 device 和 driver链表。
  21.                                             // device 是指挂在总线上的设备,比如 i2c设备和 i2c控制器都是挂在这条链表上的,那通过什么来区分的呢?
  22.                                             // 每一个 device.type 就是用于这个时候了。
  23. };
1.1 API
 bus API

点击(此处)折叠或打开

  1. extern int __must_check bus_register(struct bus_type *bus);
  2. extern void bus_unregister(struct bus_type *bus);
  3. int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,
  4.          int (*fn)(struct device *dev, void *data));
  5. int __must_check bus_for_each_drv(struct bus_type *bus,
  6.   struct device_driver *start, void *data,
  7.   int (*fn)(struct device_driver *, void *));
  8.   
device API

点击(此处)折叠或打开

  1. extern int __must_check device_register(struct device *dev);
driver API
点击(此处)折叠或打开
  1. int driver_register(struct device_driver *drv)
二、i2c 设备的系统注册过程(以LM75 为例)
       源码路径:
            driver/i2c/        -- 其中 i2c-core.c 是最重要的,应该从这个文件开始
            driver/hwmon/lm75.c
            driver/hwmon/lm75.h
        i2c设备驱动框架中有三个重要的结构体:
            i2c_adapter: i2c 的控制器,提供了i2c传输的方法
            i2c_client:  i2c slave 就是i2c的设备
            i2c_driver: i2c 驱动
       按照 bus device driver 的框架思路,先从bus开始
2.1 注册i2c总线

点击(此处)折叠或打开

  1. struct bus_type i2c_bus_type = {
  2.     .name        = "i2c",
  3.     .match        = i2c_device_match,
  4.     .probe        = i2c_device_probe,
  5.     .remove        = i2c_device_remove,
  6.     .shutdown    = i2c_device_shutdown,
  7.     .suspend    = i2c_device_suspend,
  8.     .resume        = i2c_device_resume,
  9. };

  10. bus_register(&i2c_bus_type);
2.2  注册 i2c 设备驱动 LM75

点击(此处)折叠或打开

  1. static const struct i2c_device_id lm75_ids[] = {
  2.     { "ds1775", ds1775, },
  3.     { "ds75", ds75, },
  4.     { "lm75", lm75, },
  5.     { "lm75a", lm75a, },
  6.     { "max6625", max6625, },
  7.     { "max6626", max6626, },
  8.     { "mcp980x", mcp980x, },
  9.     { "stds75", stds75, },
  10.     { "tcn75", tcn75, },
  11.     { "tmp100", tmp100, },
  12.     { "tmp101", tmp101, },
  13.     { "tmp175", tmp175, },
  14.     { "tmp275", tmp275, },
  15.     { "tmp75", tmp75, },
  16.     { /* LIST END */ }
  17. };

  18. static struct i2c_driver lm75_driver = {
  19.     .class        = I2C_CLASS_HWMON,
  20.     .driver = {
  21.         .name    = "lm75",
  22.     },
  23.     .probe        = lm75_probe,
  24.     .remove        = lm75_remove,
  25.     .id_table    = lm75_ids,
  26.     .detect        = lm75_detect,
  27.     .address_data    = &addr_data,
  28. };

  29. static int __init sensors_lm75_init(void)
  30. {
  31.     return i2c_add_driver(&lm75_driver);
  32. }


点击(此处)折叠或打开

  1. static const struct i2c_device_id lm75_ids[] = {
  2.     { "ds1775", ds1775, },
  3.     { "ds75", ds75, },
  4.     { "lm75", lm75, },
  5.     { "lm75a", lm75a, },
  6.     { "max6625", max6625, },
  7.     { "max6626", max6626, },
  8.     { "mcp980x", mcp980x, },
  9.     { "stds75", stds75, },
  10.     { "tcn75", tcn75, },
  11.     { "tmp100", tmp100, },
  12.     { "tmp101", tmp101, },
  13.     { "tmp175", tmp175, },
  14.     { "tmp275", tmp275, },
  15.     { "tmp75", tmp75, },
  16.     { /* LIST END */ }
  17. };

  18. static struct i2c_driver lm75_driver = {
  19.     .class        = I2C_CLASS_HWMON,
  20.     .driver = {
  21.         .name    = "lm75",
  22.     },
  23.     .probe        = lm75_probe,
  24.     .remove        = lm75_remove,
  25.     .id_table    = lm75_ids,
  26.     .detect        = lm75_detect,
  27.     .address_data    = &addr_data,
  28. };

  29. static int __init sensors_lm75_init(void)
  30. {
  31.     return i2c_add_driver(&lm75_driver);
  32. }

3. i2c设备添加
    这部分与产品是相关的,不同的产品 i2c控制器 和外围的设备可能存在差异。
    在 i2c/busses 中,就定义了各种 i2c adapter的探测,最终会调用
            result = i2c_add_adapter(&i2c->adap);
    添加 adapter。
    那 i2c 的设备的添加就比较多样了,通过设备表来添加是比较灵活的。
            of_i2c_register_devices(&i2c->adap);
     在 cavium octeon 中, dts 表位于 arch/mips/cavium-octeon/dts 目录中
    更烂的办法是添加完adapter之后,硬编码设备。


点击(此处)折叠或打开

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

  3. struct i2c_board_info {
  4.     char        type[I2C_NAME_SIZE];        // 注意 type 要和 i2c_driver.id_table 一致
  5.     unsigned short    flags;
  6.     unsigned short    addr;                    // i2c 地址,硬件工程师必须知道!
  7.     void        *platform_data;
  8.     struct dev_archdata    *archdata;
  9.     struct device_node *of_node;
  10.     int        irq;
  11. };
4. 数据读写
    SMBus 和 I2C 的问题:这两者差不多,所以驱动方面是绝大多数是兼容的。只要在具体传输数据命令部分做一下区分就可以了。
    当然,也有兼容两者的控制器,牛逼得要死。

点击(此处)折叠或打开

  1. struct i2c_adapter {
  2.     struct module *owner;
  3.     unsigned int id;
  4.     unsigned int class;         /* classes to allow probing for */
  5.     const struct i2c_algorithm *algo; /* the algorithm to access the bus */    // 就是这个
  6.     void *algo_data;        // 通过其他方式访问 i2c 所需要的数据结构

  7.     /* data fields that are valid for all devices    */
  8.     u8 level;             /* nesting level for lockdep */
  9.     struct mutex bus_lock;

  10.     int timeout;            /* in jiffies */
  11.     int retries;
  12.     struct device dev;        /* the adapter device */

  13.     int nr;
  14.     char name[48];
  15.     struct completion dev_released;
  16. };

  17. struct i2c_algorithm {
  18.     /* If an adapter algorithm can't do I2C-level access, set master_xfer
  19.      to NULL. If an adapter algorithm can do SMBus access, set
  20.      smbus_xfer. If set to NULL, the SMBus protocol is simulated
  21.      using common I2C messages */
  22.     /* master_xfer should return the number of messages successfully
  23.      processed, or a negative value on error */
  24.     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
  25.              int num);
  26.     int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
  27.              unsigned short flags, char read_write,
  28.              u8 command, int size, union i2c_smbus_data *data);

  29.     /* To determine what the adapter supports */
  30.     u32 (*functionality) (struct i2c_adapter *);
  31. };
        注释已经很清楚了。

        来看一下 octeon的 functionality

点击(此处)折叠或打开

  1. static u32 octeon_i2c_functionality(struct i2c_adapter *adap)
  2. {
  3.         return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
  4. }
有些GPIO模拟的等 就是在algo_data中实际,然后,由 i2c/algos 统一对应抽象出 i2c_bit_algo 来提供对i2c设备的访问

2.5 i2c-dev
    i2c-dev 对应对象为adapter,为adapter 创建 /dev/i2c-%d 文件,创建 i2c 类设备,ioctl 等。
    可以通过 i2c-dev 操作 i2c controller 来与 i2c 设备交互。

总结:
    i2c驱动是比较典型的LINUX驱动的一种,设计应该算是比较合理的,值得学习。









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