2、reset 接口
最近因为平台的i2c总线经常发生死锁,用逻辑分析仪检测发现通常为SDA和SCL都被拉低,于是在i2c-core中加入了reset机制,总体思路如下:
(1)在i2c.driver和i2c.adapter的结构中加入reset接口,即每一个i2c设备都可以注册reset函数,每条i2c总线都有相应的reset接口
(2)当发生死锁时,首先根据i2c-timeout的信息获取当前通信的设备地址和总线编号,然后依次执行当前总线下所有i2c设备的reset函数,再尝试发送是否成功;如果总线仍然处于死锁状态则执行i2c.adapter的reset函数;如果总线还是处于死锁状态就重启机器;总共3层reset机制
(3)i2c.driver的reset函数一般操作设备的reset pin或者电源(需要根据硬件设计进行相应操作)
(4)i2c.adapter的reset函数首选进行SCL的模拟解锁方案,然后再是操作整个总线上设备的电源(需要根据硬件设计进行相应操作)
(5)重启是最后的一层机制,此时无法恢复设备的正常使用就只能重启了
因为i2c.adapter层的需要,在i2c-core中加入了遍历当前总线所有设备并执行设备reset函数的接口i2c_reset_device:
[cpp]
/**
* i2c_reset_device - reset I2C device when bus dead
* @adapter: the adapter being reset
* @addr: the device address
*/
static int __i2c_reset_device(struct device *dev, void *addrp)
{
struct i2c_client *client = to_i2c_client(dev);
int addr = *(int *)addrp;
if (client && client->driver && client->driver->reset)
return client->driver->reset();
return 0;
}
int i2c_reset_device(struct i2c_adapter *adapter, int addr)
{
return device_for_each_child(&adapter->dev, &addr, __i2c_reset_device);
}
EXPORT_SYMBOL(i2c_reset_device);
需要注意的是i2c.driver的reset函数返回值需要为0,不然device_for_each_child不会继续后面的遍历.用GPIO模拟SCL解锁的参考代码如下:
[cpp]
static int i2c_reset_adapter(void)
{
int counter = 0;
gpio_request(I2C_BUS_DATA, “gpioxx”);
gpio_request(I2C_BUS_CLK, “gpioxx”);
/* try to recover I2C bus */
gpio_direction_input(I2C_BUS_DATA);
if (!__gpio_get_value(I2C_BUS_DATA)) {
while((!__gpio_get_value(I2C_BUS_DATA)) && ++counter < 10)
{
udelay(5);
gpio_direction_output(I2C_BUS_CLK, 1);
udelay(5);
gpio_direction_output(I2C_BUS_CLK, 0);
}
i2c_err(“try to recover i2c bus, retry times are %d\n”,counter);
if (counter < 10) {
udelay(5);
gpio_direction_output(I2C_BUS_DATA, 0);
udelay(5);
gpio_direction_output(I2C_BUS_CLK, 1);
udelay(5);
gpio_direction_output(I2C_BUS_DATA, 1);
msleep(10);
} else {
i2c_err(“try to recover i2c bus failed!\n”);
}
}
gpio_free(I2C_BUS_DATA);
gpio_free(I2C_BUS_CLK);
return 0;
}