Linux i2c驱动框架分析 (一)
Linux i2c驱动框架分析 (二)
Linux i2c驱动框架分析 (三)
通用i2c设备驱动分析
i2c适配器驱动
i2c适配器驱动加载与卸载
i2c总线驱动模块的加载函数要完成两个工作。
-
初始化i2c适配器所使用的硬件资源,如申请I/O地址、中断号等。
-
初始化好i2c_adapter数据结构的成员,然后通过i2c_add_adapter()添加i2c_adapter的数据结构。
i2c总线驱动模块的卸载函数要完成的工作与加载函数相反。
-
释放i2c适配器所使用的硬件资源,如释放 I/O地址、中断号等。
-
通过i2c_del_adapter()删除i2c_adapter的数据结构。
i2c总线通信方法
我们需要为特定的i2c适配器实现其通信方法,主要实现i2c_algorithm的master_xfer()函数和functionality()函数。
functionality()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_ FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。
master_xfer()函数在i2c适配器上完成传递给它的i2c_msg数组中的每个i2c消息。
注册适配器驱动
调用i2c_add_adapter()函数注册一个适配器:
int i2c_add_adapter(struct i2c_adapter *adapter)
{
struct device *dev = &adapter->dev;
int id;
//对于在设备树定义的i2c适配器,则通过设备树获得总线号
if (dev->of_node) {
id = of_alias_get_id(dev->of_node, "i2c");
if (id >= 0) {
adapter->nr = id;
//已明确总线号的适配器,调用__i2c_add_numbered_adapter函数进行注册
return __i2c_add_numbered_adapter(adapter);
}
}
mutex_lock(&core_lock);
//从i2c_adapter_idr中申请一个可用的总线号
id = idr_alloc(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
mutex_unlock(&core_lock);
if (WARN(id < 0, "couldn't get idr"))
return id;
adapter->nr = id;
return i2c_register_adapter(adapter);
}
__i2c_add_numbered_adapter函数是对i2c_register_adapter函数的封装,真正注册函数是后者,定义如下:
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = -EINVAL;
......
/* 检查是否设置name */
if (WARN(!adap->name[0], "i2c adapter has no name"))
goto out_list;
//检查是否提供algo
if (!adap->algo) {
pr_err("adapter '%s': no algo supplied!\n", adap->name);
goto out_list;
}
if (!adap->lock_ops)
adap->lock_ops = &i2c_adapter_lock_ops;
//初始化一些锁及链表
rt_mutex_init(&adap->bus_lock);
rt_mutex_init(&adap->mux_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;
//设置adapter device
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
//注册adapter device
res = device_register(&adap->dev);
if (res) {
pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
goto out_list;
}
......
/* 前面章节提到过,可以在i2c适配器节点下描述的i2c设备节点
* of_i2c_register_devices函数就是处理这些i2c设备节点,并注册设备
*/
of_i2c_register_devices(adap);
......
/* 以前的内核不支持设备树,通过i2c_register_board_info函数注册i2c设备信息
* 这些设备信息会插入到一个__i2c_board_list链表里,注册适配器时,会从这个
* 链表里取出设备信息,并注册i2c设备
*/
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);
return 0;
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
}
注册在适配器节点下描述的i2c设备节点
在注册i2c适配器时,会调用of_i2c_register_devices函数来处理这些子节点:
static void of_i2c_register_devices(struct i2c_adapter *adap)
{
struct device_node *bus, *node;
struct i2c_client *client;
/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus");
if (!bus)
bus = of_node_get(adap->dev.of_node);
//遍历子节点,调用of_i2c_register_device函数注册i2c设备
for_each_available_child_of_node(bus, node) {
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue;
//注册i2c设备
client = of_i2c_register_device(adap, node);
if (IS_ERR(client)) {
dev_warn(&adap->dev,
"Failed to create I2C device for %s\n",
node->full_name);
of_node_clear_flag(node, OF_POPULATED);
}
}
of_node_put(bus);
}
of_i2c_register_device:
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
struct i2c_client *result;
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr_be;
u32 addr;
int len;
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
//从节点中获取name
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
node->full_name);
return ERR_PTR(-EINVAL);
}
//节点的reg属性,表示设备地址
addr_be = of_get_property(node, "reg", &len);
if (!addr_be || (len < sizeof(*addr_be))) {
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
node->full_name);
return ERR_PTR(-EINVAL);
}
......
//判断地址使用有效
if (i2c_check_addr_validity(addr, info.flags)) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
addr, node->full_name);
return ERR_PTR(-EINVAL);
}
//从节点中得到相关信息,用于初始化一个i2c_board_info
info.addr = addr;
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
//增添i2c设备
result = i2c_new_device(adap, &info);
if (result == NULL) {
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
node->full_name);
of_node_put(node);
return ERR_PTR(-EINVAL);
}
return result;
}
这里关注一下,从节点中是怎么获取name的,of_modalias_node:
int of_modalias_node(struct device_node *node, char *modalias, int len)
{
const char *compatible, *p;
int cplen;
/* 从compatible属性获取name,从代码上可知,如果compatible为"wlf,wm8960"
* 那么设备的name会取','之后的字符串,即wm8960,只是要注意的点
*/
compatible = of_get_property(node, "compatible", &cplen);
if (!compatible || strlen(compatible) > cplen)
return -ENODEV;
p = strchr(compatible, ',');
strlcpy(modalias, p ? p + 1 : compatible, len);
return 0;
}
注册静态定义的i2c设备
以前的内核不支持设备树,通过i2c_register_board_info函数注册i2c设备信息,这些设备信息会插入到一个__i2c_board_list链表里,注册适配器时,会从这个链表里取出设备信息,并注册i2c设备。
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
down_read(&__i2c_board_lock);
//判断总线num是否一致,一致则i2c_new_device
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
通知驱动
注册适配器时,会通知驱动程序去探测设备:
static int i2c_register_adapter(struct i2c_adapter *adap)
{
......
//遍历i2c bus上的驱动链表,调用__process_new_adapter函数
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
......
}
__process_new_adapter函数前面已分析,即去探测驱动的address_list定义的设备地址是否存在,存在则注册设备。