I2C Bus Recovery
Brief
I was working on a project where we had a problem with an I2C bus that was sporadically hanging during communication with a certain device. I2C works with open-drain lines, which means that devices can only pull the lines low, and a pull-up resistor is used to pull the line high. If a device misbehaves and holds one of the lines low, the bus will be stuck, and no further communication can take place.

I've seen this a couple of times before, so I would say that it is not that common to be honest, but still, it happens. I2C is basically a very simple protocol if you stick to the basics, which most chips do. Just to be clear; what I mean with basics is:
- No clockstretching
- No 10-bit addressing
- No repeated start conditions
- No multi-master setups
- No combined transactions
- No high-speed mode (3.4MHz)
- No PEC (Error checksum)
- ...
In other words, just the basic 7-bit addressing and standard/fast mode (100/400kHz).
In Linux, I2C bus recovery is a mechanism to get a hung/stuck I2C bus working again.
The I2C bus recovery mechanism
The I2C bus recovery mechanism is implemented in the I2C core and can be used by any I2C adapter/driver that supports it. It is implemented by the struct i2c_bus_recovery_info structure, which is defined in include/linux/i2c.h [1].
1struct i2c_bus_recovery_info {
2 int (*recover_bus)(struct i2c_adapter *adap);
3
4 int (*get_scl)(struct i2c_adapter *adap);
5 void (*set_scl)(struct i2c_adapter *adap, int val);
6 int (*get_sda)(struct i2c_adapter *adap);
7 void (*set_sda)(struct i2c_adapter *adap, int val);
8 int (*get_bus_free)(struct i2c_adapter *adap);
9
10 void (*prepare_recovery)(struct i2c_adapter *adap);
11 void (*unprepare_recovery)(struct i2c_adapter *adap);
12
13 /* gpio recovery */
14 struct gpio_desc *scl_gpiod;
15 struct gpio_desc *sda_gpiod;
16 struct pinctrl *pinctrl;
17 struct pinctrl_state *pins_default;
18 struct pinctrl_state *pins_gpio;
19};
A custom recovery function may be used by implementing the recover_bus function pointer. If not, the standard recovery function [2] will be used, which is implemented in the I2C core.
How it works
There could be several reasons to why a device misbehaves and holds the bus low, e.g.:
- A slave somehow crashes in mid-transfer
- Bad signal integrity on the bus causing hard to detect edges
- ...
Most of the time a few extra clock cycles is enough to get the slave back in sync. The standard recovery function will do the following:
- Check if the bus is really stuck (SCL or SDA low)
- If the bus is stuck, it will try to toggle the SCL line up to 9 times (to cover a full byte transfer)
- After each clock cycle, it will check if the SDA line is released (high)
- If the SDA line is released, it will generate a STOP condition by pulling SDA low while SCL is high, and then release SDA
- If the bus is still stuck after 9 clock cycles, it will return an error
This procedure is described in the I2C specification [3] in the Bus clear section:

How this is implemented on the DaVinci platform
As I'm currently using the DaVinci platform from Texas Instruments, lets look at how this is implemented there.
This I2C controller is a bit special as it contains register (ICPFUNC) to control if the pins should be used as I2C (open-drain) or GPIO (push-pull). In other words, there is no need to use pinctrl to mux the pins, which is the most common way to do this.

The bus driver defines the i2c_bus_recovery_info structure as follows:
1static struct i2c_bus_recovery_info davinci_i2c_scl_recovery_info = {
2 .recover_bus = i2c_generic_scl_recovery,
3 .set_scl = davinci_i2c_set_scl,
4 .get_scl = davinci_i2c_get_scl,
5 .get_sda = davinci_i2c_get_sda,
6 .prepare_recovery = davinci_i2c_scl_prepare_recovery,
7 .unprepare_recovery = davinci_i2c_scl_unprepare_recovery,
8};
Here we can see that it is using the generic recovery function, but it provides custom functions to set and get the SCL and SDA lines.
The prepare_recovery function will set the pins to GPIO mode by setting the appropriate bits in the I2C controller registers:
1static void davinci_i2c_scl_prepare_recovery(struct i2c_adapter *adap)
2{
3 struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
4
5 davinci_i2c_prepare_recovery(adap);
6
7 /* SCL output, SDA input */
8 davinci_i2c_write_reg(dev, DAVINCI_I2C_DIR_REG, DAVINCI_I2C_DIR_PDIR0);
9
10 /* change to GPIO mode */
11 davinci_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG,
12 DAVINCI_I2C_FUNC_PFUNC0);
13}
get_scl and get_sda will read the current state of the lines:
1static int davinci_i2c_get_scl(struct i2c_adapter *adap)
2{
3 struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
4 int val;
5
6 /* read the state of SCL */
7 val = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG);
8 return val & DAVINCI_I2C_DIN_PDIN0;
9}
10
11static int davinci_i2c_get_sda(struct i2c_adapter *adap)
12{
13 struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
14 int val;
15
16 /* read the state of SDA */
17 val = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG);
18 return val & DAVINCI_I2C_DIN_PDIN1;
19}
while set_scl will set or clear the SCL line:
1static void davinci_i2c_set_scl(struct i2c_adapter *adap, int val)
2{
3 struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
4
5 if (val)
6 davinci_i2c_write_reg(dev, DAVINCI_I2C_DSET_REG,
7 DAVINCI_I2C_DSET_PDSET0);
8 else
9 davinci_i2c_write_reg(dev, DAVINCI_I2C_DCLR_REG,
10 DAVINCI_I2C_DCLR_PDCLR0);
11}
The unprepare_recovery function will set the pins back to I2C mode:
1static void davinci_i2c_unprepare_recovery(struct i2c_adapter *adap)
2{
3 struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
4
5 i2c_davinci_init(dev);
6}
As we can see, there is not much code needed to implement this. Most of the logic resides in the I2C core.
Conclusion
It is often possible to recover a stuck I2C bus by using the I2C bus recovery mechanism in the Linux kernel. However, it is not guaranteed to work in all cases and this is not a substitute for proper hardware design and signal integrity on the bus.