在前面的一篇文章中,讲述了I2C死锁的相关概念,下面我们分析一下Linux内核怎么引入改机制的。
源码:
drivers\i2c\busses\i2c-st.c
drivers\i2c\i2c-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;
- };
从第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);
- }
我们以以下的i2c控制器为例,代码在drivers\i2c\busses\i2c-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。
这个函数可以是自己实现的,可以是使用内核提供的。如果使用内核提供的,那么需要补充一些内核函数需要的一些参数或者函数。
自己写的可以参考:drivers\i2c\busses\i2c-st.c
使用内核的可以参考:drivers\i2c\busses\i2c-davinci.c
- 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,
- };