目录
源码:
drivers\i2c\busses\i2c-designware-platdrv.c
drivers\i2c\busses\i2c-designware-master.c
drivers\i2c\busses\i2c-designware-slave.c
drivers\misc\eeprom\at24.c
1. DTS
i2c2: i2c@f0d80000 {
compatible = "snps,designware-i2c";
status = "disabled";
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x0 0xf0d80000 0x0 0x1000>;
clock-frequency = <400000>;
#address-cells = <1>;
#size-cells = <0>;
};
&i2c2 {
status = "okay";
eeprom: eeprom@50 {
compatible = "atmel,24c32";
reg = <0x50>;
pagesize = <32>;
};
};
可以看出,i2c下面挂了一个eeprom设备。
2. I2C control实例化分析
static const struct of_device_id dw_i2c_of_match[] = {
{ .compatible = "snps,designware-i2c", },
{},
};
最终dw_i2c_plat_probe(drivers\i2c\busses\i2c-designware-platdrv.c)函数会调用。
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
......
ret = i2c_dw_probe_lock_support(dev);
if (ret)
goto exit_reset;
if (i2c_detect_slave_mode(&pdev->dev))
i2c_dw_configure_slave(dev);
else
i2c_dw_configure_master(dev);
......
if (dev->mode == DW_IC_SLAVE)
ret = i2c_dw_probe_slave(dev);
else
ret = i2c_dw_probe(dev);
......
}
根据该i2c控制器是主机还是从机,进行控制器配置并且调用响应的probe函数,这里我们分析主机模式,调用i2c_dw_probe函数。
static const struct i2c_algorithm i2c_dw_algo = {
.master_xfer = i2c_dw_xfer,
.functionality = i2c_dw_func,
};
int i2c_dw_probe(struct dw_i2c_dev *dev)
{
struct i2c_adapter *adap = &dev->adapter;
unsigned long irq_flags;
int ret;
.......
ret = dev->init(dev);
if (ret)
return ret;
snprintf(adap->name, sizeof(adap->name),
"Synopsys DesignWare I2C adapter");
adap->retries = 3;
adap->algo = &i2c_dw_algo;
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);
if (dev->pm_disabled) {
dev_pm_syscore_device(dev->dev, true);
irq_flags = IRQF_NO_SUSPEND;
} else {
irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND;
}
.....
i2c_dw_disable_int(dev);
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags,
dev_name(dev->dev), dev);
......
ret = i2c_add_numbered_adapter(adap);
}
这个很简单,设置struct i2c_adapter结构体,主要是adap->algo = &i2c_dw_algo,注册中断,最终调用i2c_add_numbered_adapter注册adapter,这个前文已经写过。
3. adapter如何注册slave device
3.1 i2c_register_adapter
i2c_add_numbered_adapter->__i2c_add_numbered_adapter->i2c_register_adapter,我们查看i2c_register_adapter函数。
static int i2c_register_adapter(struct i2c_adapter *adap)
{
.......
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);
.......
i2c_init_recovery(adap);
/* create pre-declared device nodes */
of_i2c_register_devices(adap);
i2c_acpi_install_space_handler(adap);
i2c_acpi_register_devices(adap);
......
}
i2c_register_adapter->of_i2c_register_devices->of_i2c_register_device,我们看of_i2c_register_device函数。
3.2 of_i2c_register_device
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
........
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(&adap->dev, "of_i2c: modalias failure on %pOF\n",
node);
return ERR_PTR(-EINVAL);
}
.......
result = i2c_new_device(adap, &info);
......
}
我们先看of_modalias_node函数。
3.2.1 of_modalias_node
int of_modalias_node(struct device_node *node, char *modalias, int len)
{
const char *compatible, *p;
int cplen;
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控制器的子节点的compatible字段,取后面的字段,根据前面的DTS介绍,最终把“at24”返回,赋值在info.type变量中。
3.2.2 i2c_new_device
/**
* i2c_new_device - instantiate an i2c device
* @adap: the adapter managing the device
* @info: describes one I2C device; bus_num is ignored
* Context: can sleep
*/
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
......
strlcpy(client->name, info->type, sizeof(client->name));
.......
i2c_dev_set_name(adap, client);
if (info->properties) {
status = device_add_properties(&client->dev, info->properties);
if (status) {
dev_err(&adap->dev,
"Failed to add properties to client %s: %d\n",
client->name, status);
goto out_err;
}
}
status = device_register(&client->dev);
......
}
4. i2c consumer driver注册
drivers\misc\eeprom\at24.c
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.acpi_match_table = ACPI_PTR(at24_acpi_ids),
},
.probe = at24_probe,
.remove = at24_remove,
.id_table = at24_ids,
};
前面已经写过,at24 device已经注册了,这里是at24 driver,最终调用at24_probe。