上一节我们看到下面这函数了。
/**
* i2c_add_adapter - declare i2c adapter, use dynamic bus number
* @adapter: the adapter to add
* Context: can sleep
*
* This routine is used to declare an I2C adapter when its bus number
* doesn't matter or when its bus number is specified by an dt alias.
* Examples of bases when the bus number doesn't matter: I2C adapters
* dynamically added by USB links or PCI plugin cards.
*
* When this returns zero, a new bus number was allocated and stored
* in adap->nr, and the specified adapter became available for clients.
* Otherwise, a negative errno value is returned.
*/
int i2c_add_adapter(struct i2c_adapter *adapter)
{
struct device *dev = &adapter->dev;
int id;
if (dev->of_node) {
id = of_alias_get_id(dev->of_node, "i2c");
if (id >= 0) {
adapter->nr = id;
return __i2c_add_numbered_adapter(adapter);
}
}
mutex_lock(&core_lock);
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总线控制器是通过设备树来匹配的。那么就首选从别名列表中,找一下,看是不是有这个i2c总线控制器的别名。有的话,这里id,就直接用别名的id。
如果不是设备树匹配到的,或者别名列表中没有这个设备,那就只能从__i2c_first_dynamic_bus_num往后查找一个没用的编号使用了。
__i2c_first_dynamic_bus_num的意思也很明显,第一个动态查找的总线编号。主要就是从idr中它这个位置开始找空闲编号。idr当然还有一个作用就是记录好adapter。使在i2c_adapter_idr为树根的idr中,id和adapter一一对应。
前面已经找到adap可以在idr中放的位置了,那么这里只要把这个指针放进去就可以了。
/**
* __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1
* @adap: the adapter to register (with adap->nr initialized)
* Context: can sleep
*
* See i2c_add_numbered_adapter() for details.
*/
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;
mutex_lock(&core_lock);
id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL);
mutex_unlock(&core_lock);
if (WARN(id < 0, "couldn't get idr"))
return id == -ENOSPC ? -EBUSY : id;
return i2c_register_adapter(adap);
}
最后就是注册这个适配器了。
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = -EINVAL;
/* Can't register until after driver model init */
if (WARN_ON(!is_registered)) {
res = -EAGAIN;
goto out_list;
}
/* Sanity checks */
if (WARN(!adap->name[0], "i2c adapter has no name"))
goto out_list;
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;
/* register soft irqs for Host Notify */
res = i2c_setup_host_notify_irq_domain(adap);
if (res) {
pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n",
adap->name, res);
goto out_list;
}
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
if (res) {
pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
goto out_list;
}
res = of_i2c_setup_smbus_alert(adap);
if (res)
goto out_reg;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
pm_runtime_no_callbacks(&adap->dev);
pm_suspend_ignore_children(&adap->dev, true);
pm_runtime_enable(&adap->dev);
#ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility class link\n");
#endif
i2c_init_recovery(adap);
/* create pre-declared device nodes */
of_i2c_register_devices(adap);
i2c_acpi_register_devices(adap);
i2c_acpi_install_space_handler(adap);
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_reg:
init_completion(&adap->dev_released);
device_unregister(&adap->dev);
wait_for_completion(&adap->dev_released);
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
}
这里主要说一下之前没说过的内容。
第一点就是给这个i2c控制注册一个irq_domain
/* register soft irqs for Host Notify */
res = i2c_setup_host_notify_irq_domain(adap);
当然这个irq_domain注册是有条件的,就是要这个i2c控制器要有I2C_FUNC_SMBUS_HOST_NOTIFY功能,很明显,我们的是没有的。
那这里的主机通知功能的中断就注册不了。
static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap)
{
struct irq_domain *domain;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY))
return 0;
domain = irq_domain_create_linear(adap->dev.fwnode,
I2C_ADDR_7BITS_COUNT,
&i2c_host_notify_irq_ops, adap);
if (!domain)
return -ENOMEM;
adap->host_notify_domain = domain;
return 0;
}
第二点
我们.config是没这个CONFIG_I2C_SMBUS,同时设备树文件也是没smbus_alert属性值的
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
{
struct i2c_client *client;
int irq;
irq = of_property_match_string(adapter->dev.of_node, "interrupt-names",
"smbus_alert");
if (irq == -EINVAL || irq == -ENODATA)
return 0;
else if (irq < 0)
return irq;
client = i2c_setup_smbus_alert(adapter, NULL);
if (!client)
return -ENODEV;
return 0;
}
#endif
第三点,i2c总线有故障后,进行自我恢复的初始化。
目前来看,内核中这个用的还是比较少。
static void i2c_init_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
char *err_str;
if (!bri)
return;
if (!bri->recover_bus) {
err_str = "no recover_bus() found";
goto err;
}
if (bri->scl_gpiod && bri->recover_bus == i2c_generic_scl_recovery) {
bri->get_scl = get_scl_gpio_value;
bri->set_scl = set_scl_gpio_value;
if (bri->sda_gpiod) {
bri->get_sda = get_sda_gpio_value;
/* FIXME: add proper flag instead of '0' once available */
if (gpiod_get_direction(bri->sda_gpiod) == 0)
bri->set_sda = set_sda_gpio_value;
}
return;
}
if (bri->recover_bus == i2c_generic_scl_recovery) {
/* Generic SCL recovery */
if (!bri->set_scl || !bri->get_scl) {
err_str = "no {get|set}_scl() found";
goto err;
}
if (!bri->set_sda && !bri->get_sda) {
err_str = "either get_sda() or set_sda() needed";
goto err;
}
}
return;
err:
dev_err(&adap->dev, "Not using recovery: %s\n", err_str);
adap->bus_recovery_info = NULL;
}
第四点,也是做重要的一点
/* create pre-declared device nodes */
of_i2c_register_devices(adap);
就是,这个i2c总线下的设备怎么处理。
在没设备树的内核是在mach-xxx.c文件中定义,注册
static struct i2c_board_info h4_i2c_board_info[] __initdata = {
{
I2C_BOARD_INFO("isp1301_omap", 0x2d),
.irq = OMAP_GPIO_IRQ(125),
},
{ /* EEPROM on mainboard */
I2C_BOARD_INFO("24c01", 0x52),
.platform_data = &m24c01,
},
{ /* EEPROM on cpu card */
I2C_BOARD_INFO("24c01", 0x57),
.platform_data = &m24c01,
},
};
static void __init omap_h4_init(void)
{
(...)
i2c_register_board_info(1, h4_i2c_board_info,
ARRAY_SIZE(h4_i2c_board_info));
(...)
}
新内核因为设备相关的内容都要移动到设备树了,所以肯定要用设备树来处理了。
这个函数就是用来注册总线下的i2c设备的。
先看一下设备怎么在总线下放置
&i2c0 {
status = "okay";
audio-codec@1b {
compatible = "wlf,wm8580";
reg = <0x1b>;
};
eeprom@50 {
compatible = "atmel,24c08";
reg = <0x50>;
};
};
看到了设备,如何在总线下放置之后,分析就会简单很多
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);
/* 遍历总线节点下的子节点 */
for_each_available_child_of_node(bus, node) {
if (of_node_test_and_set_flag(node, OF_POPULATED)) //如果这个节点已经创建节点,这里就不再创建
continue;
/* 使用这个总线节点里的子节点注册一个i2c_client */
client = of_i2c_register_device(adap, node);
if (IS_ERR(client)) {
dev_err(&adap->dev,
"Failed to create I2C device for %pOF\n",
node);
of_node_clear_flag(node, OF_POPULATED);
}
}
of_node_put(bus);
}
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
struct i2c_client *client;
struct i2c_board_info info;
int ret;
dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node);
/* 获取设备信息 */
ret = of_i2c_get_board_info(&adap->dev, node, &info);
if (ret)
return ERR_PTR(ret);
/* 注册一个设备 */
client = i2c_new_device(adap, &info);
if (!client) {
dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node);
return ERR_PTR(-EINVAL);
}
return client;
}
设备信息就比较简单了,名字,i2c从机的地址
int of_i2c_get_board_info(struct device *dev, struct device_node *node,
struct i2c_board_info *info)
{
u32 addr;
int ret;
memset(info, 0, sizeof(*info));
/* 获取名字 */
if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) {
dev_err(dev, "of_i2c: modalias failure on %pOF\n", node);
return -EINVAL;
}
/* 获取从机地址 */
ret = of_property_read_u32(node, "reg", &addr);
if (ret) {
dev_err(dev, "of_i2c: invalid reg on %pOF\n", node);
return ret;
}
/* 根据地址,设备client的标志 */
if (addr & I2C_TEN_BIT_ADDRESS) {
addr &= ~I2C_TEN_BIT_ADDRESS;
info->flags |= I2C_CLIENT_TEN;
}
if (addr & I2C_OWN_SLAVE_ADDRESS) {
addr &= ~I2C_OWN_SLAVE_ADDRESS;
info->flags |= I2C_CLIENT_SLAVE;
}
info->addr = addr;
info->of_node = node;
if (of_property_read_bool(node, "host-notify"))
info->flags |= I2C_CLIENT_HOST_NOTIFY;
if (of_get_property(node, "wakeup-source", NULL))
info->flags |= I2C_CLIENT_WAKE;
return 0;
}