要了解I2C驱动框架的话,要先了解一linux设备驱动,也可以通过i2c驱动反过来进一步理解 linux的设备驱动。玩多了,就知道了。
一、预备知识:linux设备驱动 bus device driver
点击(此处)折叠或打开
-
struct bus_type {
-
const char *name; // 总线名称
-
struct bus_attribute *bus_attrs; // 总线属性,用于 /sys/bus/ 下的显示
-
struct device_attribute *dev_attrs;
-
struct driver_attribute *drv_attrs;
-
-
int (*match)(struct device *dev, struct device_driver *drv); // device 和 driver 的匹配操作,总线上的匹配方法,例如,
-
// 会有很多的I2C 设备和I2C驱动,简单的可以通过名称来匹配
-
// 每个 i2c设备用 i2c_client,
-
// 每个 i2c driver 中,含有 id_table 其中含有对应的i2c设备的名称。
-
// 只要这两个对应上,这个 i2c_client 就是对应这个 i2c_driver
-
-
int (*uevent)(struct device *dev, struct kobj_uevent_env *env); // 事件处理,未研究
-
int (*probe)(struct device *dev); // i2c_driver 中也有 probe 不过,优先会用总线上的这个probe,当添加设备,或添加驱动时会调用到
-
int (*remove)(struct device *dev); // 移除
-
void (*shutdown)(struct device *dev);
-
-
int (*suspend)(struct device *dev, pm_message_t state);
-
int (*resume)(struct device *dev);
-
-
const struct dev_pm_ops *pm;
-
-
struct bus_type_private *p; // 总线的私有信息,为什么叫 private呢,因为这个数据外部调用者是不需要关心的。
-
// 其中有 device 和 driver链表。
-
// device 是指挂在总线上的设备,比如 i2c设备和 i2c控制器都是挂在这条链表上的,那通过什么来区分的呢?
-
// 每一个 device.type 就是用于这个时候了。
-
};
1.1 API
bus API
-
extern int __must_check bus_register(struct bus_type *bus);
-
extern void bus_unregister(struct bus_type *bus);
-
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,
-
int (*fn)(struct device *dev, void *data));
-
int __must_check bus_for_each_drv(struct bus_type *bus,
-
struct device_driver *start, void *data,
-
int (*fn)(struct device_driver *, void *));
-
device API
-
extern int __must_check device_register(struct device *dev);
driver API
点击(此处)折叠或打开
-
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总线
-
struct bus_type i2c_bus_type = {
-
.name = "i2c",
-
.match = i2c_device_match,
-
.probe = i2c_device_probe,
-
.remove = i2c_device_remove,
-
.shutdown = i2c_device_shutdown,
-
.suspend = i2c_device_suspend,
-
.resume = i2c_device_resume,
-
};
-
-
bus_register(&i2c_bus_type);
2.2 注册 i2c 设备驱动 LM75
-
static const struct i2c_device_id lm75_ids[] = {
-
{ "ds1775", ds1775, },
-
{ "ds75", ds75, },
-
{ "lm75", lm75, },
-
{ "lm75a", lm75a, },
-
{ "max6625", max6625, },
-
{ "max6626", max6626, },
-
{ "mcp980x", mcp980x, },
-
{ "stds75", stds75, },
-
{ "tcn75", tcn75, },
-
{ "tmp100", tmp100, },
-
{ "tmp101", tmp101, },
-
{ "tmp175", tmp175, },
-
{ "tmp275", tmp275, },
-
{ "tmp75", tmp75, },
-
{ /* LIST END */ }
-
};
-
-
static struct i2c_driver lm75_driver = {
-
.class = I2C_CLASS_HWMON,
-
.driver = {
-
.name = "lm75",
-
},
-
.probe = lm75_probe,
-
.remove = lm75_remove,
-
.id_table = lm75_ids,
-
.detect = lm75_detect,
-
.address_data = &addr_data,
-
};
-
-
static int __init sensors_lm75_init(void)
-
{
-
return i2c_add_driver(&lm75_driver);
-
}
-
static const struct i2c_device_id lm75_ids[] = {
-
{ "ds1775", ds1775, },
-
{ "ds75", ds75, },
-
{ "lm75", lm75, },
-
{ "lm75a", lm75a, },
-
{ "max6625", max6625, },
-
{ "max6626", max6626, },
-
{ "mcp980x", mcp980x, },
-
{ "stds75", stds75, },
-
{ "tcn75", tcn75, },
-
{ "tmp100", tmp100, },
-
{ "tmp101", tmp101, },
-
{ "tmp175", tmp175, },
-
{ "tmp275", tmp275, },
-
{ "tmp75", tmp75, },
-
{ /* LIST END */ }
-
};
-
-
static struct i2c_driver lm75_driver = {
-
.class = I2C_CLASS_HWMON,
-
.driver = {
-
.name = "lm75",
-
},
-
.probe = lm75_probe,
-
.remove = lm75_remove,
-
.id_table = lm75_ids,
-
.detect = lm75_detect,
-
.address_data = &addr_data,
-
};
-
-
static int __init sensors_lm75_init(void)
-
{
-
return i2c_add_driver(&lm75_driver);
-
}
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之后,硬编码设备。
-
struct i2c_client *
-
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
-
-
struct i2c_board_info {
-
char type[I2C_NAME_SIZE]; // 注意 type 要和 i2c_driver.id_table 一致
-
unsigned short flags;
-
unsigned short addr; // i2c 地址,硬件工程师必须知道!
-
void *platform_data;
-
struct dev_archdata *archdata;
-
struct device_node *of_node;
-
int irq;
-
};
4. 数据读写
SMBus 和 I2C 的问题:这两者差不多,所以驱动方面是绝大多数是兼容的。只要在具体传输数据命令部分做一下区分就可以了。
当然,也有兼容两者的控制器,牛逼得要死。
-
struct i2c_adapter {
-
struct module *owner;
-
unsigned int id;
-
unsigned int class; /* classes to allow probing for */
-
const struct i2c_algorithm *algo; /* the algorithm to access the bus */ // 就是这个
-
void *algo_data; // 通过其他方式访问 i2c 所需要的数据结构
-
-
/* data fields that are valid for all devices */
-
u8 level; /* nesting level for lockdep */
-
struct mutex bus_lock;
-
-
int timeout; /* in jiffies */
-
int retries;
-
struct device dev; /* the adapter device */
-
-
int nr;
-
char name[48];
-
struct completion dev_released;
-
};
-
-
struct i2c_algorithm {
-
/* If an adapter algorithm can't do I2C-level access, set master_xfer
-
to NULL. If an adapter algorithm can do SMBus access, set
-
smbus_xfer. If set to NULL, the SMBus protocol is simulated
-
using common I2C messages */
-
/* master_xfer should return the number of messages successfully
-
processed, or a negative value on error */
-
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
-
int num);
-
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
-
unsigned short flags, char read_write,
-
u8 command, int size, union i2c_smbus_data *data);
-
-
/* To determine what the adapter supports */
-
u32 (*functionality) (struct i2c_adapter *);
-
};
注释已经很清楚了。
来看一下 octeon的 functionality
-
static u32 octeon_i2c_functionality(struct i2c_adapter *adap)
-
{
-
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-
}
有些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驱动的一种,设计应该算是比较合理的,值得学习。
阅读(519) | 评论(0) | 转发(0) |