Linux内核4.14版本——I2C子系统(5)_I2C死锁相关-Anlogic-安路社区-FPGA CPLD-ChipDebug

Linux内核4.14版本——I2C子系统(5)_I2C死锁相关

源码:

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,
};

 

请登录后发表评论

    没有回复内容