源码:
driversi2cbussesi2c-st.c
driversi2ci2c-core-base.c
1. i2c_init_recovery(死锁复位机制初始化)
i2c_add_adapter->i2c_register_adapter->i2c_init_recovery
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = -EINVAL;
................
i2c_init_recovery(adap);
...........
}
static void i2c_init_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
char *err_str, *err_level = KERN_ERR;
if (!bri)
return;
if (!bri->recover_bus) { //(1)
err_str = "no suitable method provided";
err_level = KERN_DEBUG;
goto err;
}
/* Generic GPIO recovery */
if (bri->recover_bus == i2c_generic_gpio_recovery) { //(2)
if (!gpio_is_valid(bri->scl_gpio)) {
err_str = "invalid SCL gpio";
goto err;
}
if (gpio_is_valid(bri->sda_gpio))
bri->get_sda = get_sda_gpio_value;
else
bri->get_sda = NULL;
bri->get_scl = get_scl_gpio_value;
bri->set_scl = set_scl_gpio_value;
} else if (bri->recover_bus == i2c_generic_scl_recovery) { //(3)
/* Generic SCL recovery */
if (!bri->set_scl || !bri->get_scl) {
err_str = "no {get|set}_scl() found";
goto err;
}
}
return;
err:
dev_printk(err_level, &adap->dev, "Not using recovery: %s\n", err_str);
adap->bus_recovery_info = NULL;
}
从 I2C 适配器中得到 struct i2c_bus_recovery_info 结构体变量 bri。
(1)检查是否支持死锁复位。
(2)死锁复位方式是 GPIO 的话,进行必要的函数检查。
(3)死锁复位方式是 SCL 的话,进行必要的函数检查。
/**
* struct i2c_bus_recovery_info - I2C bus recovery information
* @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or
* i2c_generic_scl_recovery() or i2c_generic_gpio_recovery().
* @get_scl: This gets current value of SCL line. Mandatory for generic SCL
* recovery. Used internally for generic GPIO recovery.
* @set_scl: This sets/clears SCL line. Mandatory for generic SCL recovery. Used
* internally for generic GPIO recovery.
* @get_sda: This gets current value of SDA line. Optional for generic SCL
* recovery. Used internally, if sda_gpio is a valid GPIO, for generic GPIO
* recovery.
* @prepare_recovery: This will be called before starting recovery. Platform may
* configure padmux here for SDA/SCL line or something else they want.
* @unprepare_recovery: This will be called after completing recovery. Platform
* may configure padmux here for SDA/SCL line or something else they want.
* @scl_gpio: gpio number of the SCL line. Only required for GPIO recovery.
* @sda_gpio: gpio number of the SDA line. Only required for GPIO recovery.
*/
struct i2c_bus_recovery_info {
int (*recover_bus)(struct i2c_adapter *);
int (*get_scl)(struct i2c_adapter *);
void (*set_scl)(struct i2c_adapter *, int val);
int (*get_sda)(struct i2c_adapter *);
void (*prepare_recovery)(struct i2c_adapter *);
void (*unprepare_recovery)(struct i2c_adapter *);
/* gpio recovery */
int scl_gpio;
int sda_gpio;
};
2. i2c_recover_bus(死锁复位机制的调用)
从第 1 节中可以知道 recover_bus 是最终要调用的函数,搜索本文,找到函数 i2c_recover_bus。
int i2c_recover_bus(struct i2c_adapter *adap)
{
if (!adap->bus_recovery_info)
return -EOPNOTSUPP;
dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
return adap->bus_recovery_info->recover_bus(adap);
}
3. 如何在自己的 I2C 控制器中加入该机制?
我们以以下的 i2c 控制器为例,代码在 driversi2cbussesi2c-st.c。
static int st_i2c_probe(struct platform_device *pdev)
{
........
adap->bus_recovery_info = &st_i2c_recovery_info;
........
ret = i2c_add_adapter(adap);
.......
return 0;
}
static struct i2c_bus_recovery_info st_i2c_recovery_info = {
.recover_bus = st_i2c_recover_bus,
};
简单来说就是在 i2c probe 函数中,填充 struct i2c_bus_recovery_info 结构体,并赋值给 adapter。
这个函数可以是自己实现的,可以是使用内核提供的。如果使用内核提供的,那么需要补充一些内核函数需要的一些参数或者函数。
自己写的可以参考:driversi2cbussesi2c-st.c
使用内核的可以参考:driversi2cbussesi2c-davinci.c
4. I2C Adapter 最终的使用
static int st_i2c_wait_free_bus(struct st_i2c_dev *i2c_dev)
{
u32 sta;
int i, ret;
for (i = 0; i < 10; i++) {
sta = readl_relaxed(i2c_dev->base + SSC_STA);
if (!(sta & SSC_STA_BUSY))
return 0;
usleep_range(2000, 4000);
}
dev_err(i2c_dev->dev, "bus not free (status = 0x%08x)\n", sta);
ret = i2c_recover_bus(&i2c_dev->adap);
if (ret) {
dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret);
return ret;
}
return -EBUSY;
}
一般会在 i2c 的 master_xfer 中使用。
static const struct i2c_algorithm st_i2c_algo = {
.master_xfer = st_i2c_xfer,
.functionality = st_i2c_func,
};
没有回复内容