Tests:I2C-bus-recovery-write-byte-fix

From eLinux.org
Revision as of 05:54, 1 July 2018 by W sang (talk | contribs) (first part)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

This document describes how to test if I2C bus recovery was fixed to not accidently write bytes during recovery. A Renesas I2C IP core used on a Renesas Lager board serves as an example platform here.

Setup

Kernel Version and Configuration

Support for this feature is expected to land upstream in v4.19. It is currently available in a topic branch:

git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git renesas/i2c/recovery/write-byte-fix

The following kernel options need to be set in addition to the defconfig:

CONFIG_I2C_GPIO=y
CONFIG_I2C_GPIO_FAULT_INJECTOR=y

Hardware Environment

Lager/r8a7790 (R-Car H2 SoC)

You need to connect the pins:

EXIO_C 78 <-> EXIO_A 58 (for SCL)
EXIO_C 80 <-> EXIO_A 60 (for SDA)

This will connect the I2C1 and the I2C2 bus. We will use the I2C demultiplexer at runtime to switch I2C1 to GPIO:

# echo 2 > /sys/devices/platform/i2c-11/current_master

Software Environment

A busybox with standard commands like cd, cat, echo is needed. Also, i2cget and i2cdump from the i2c-tools package should be available.

Previous state

As documented in the test page for Tests:I2C-bus-recovery, the bus recovery mechanism of the Linux I2C core was able to resurrect a stalled bus. However, the implementation had a flaw which could lead to accidental writes to a device during recovery. To demomstrate this, a new fault injector was implemented. From its documentation:

       "incomplete_write_byte"
       -----------------------
       
       Similar to above, this file is write only and you need to write the address of
       an existing I2C client device to it.
       
       The injector will again stop at one ACK phase, so the device will keep SDA low
       because it acknowledges data. However, there are two differences compared to
       'incomplete_address_phase':
       
       a) the message sent out will be a write message
       b) after the address byte, a 0x00 byte will be transferred. Then, stop at ACK.
       
       This is a highly delicate state, the device is set up to write any data to
       register 0x00 (if it has registers) when further clock pulses happen on SCL.
       This is why bus recovery (up to 9 clock pulses) must either check SDA or send
       additional STOP conditions to ensure the bus has been released. Otherwise
       random data will be written to a device!

If you are not familiar with I2C fault injection yet, have a look here: Tests:I2C-fault-injection


Testing I2C bus recovery

After booting, we activate the I2C GPIO fault injector:

# echo 2 > /sys/devices/platform/i2c-11/current_master
i2c-gpio i2c-8: using lines 979 (SDA) and 978 (SCL)

Then, we switch I2C2 from the IIC to the I2C IP core, where bus recovery was implemented:

# echo 1 > /sys/devices/platform/i2c-12/current_master
i2c-rcar e6530000.i2c: probed

Now, we create the stalled SDA case again using the I2C fault injector:

# cd /sys/kernel/debug/i2c-fault-injector/i2c-8/
# echo 0x12 > incomplete_transfer

Using a scope, we verify that SDA is stuck low and needs to be recovered: Incomplete transfer on Lager, device does not react

Now, we try to read register 8 from the audio codec via the stalled I2C bus:

# i2cget -y -f 2 0x12 8
0xe1

So, we got a valid response from this read request. Using a scope, we see the following happening: Successful example of a I2C recovery releasing a stuck SDA

We see left of the markers that SCL was toggled until the audio codec released SDA again. There is one clock cycle done more than necessary. This is due to the fact that the I2C IP core can't read back the status of the SDA line, so we always have to perform the maximum number of clock cycles to be sure the line gets released. Nonetheless, the stuck SDA condition was detected and successfully recovered from.

Within the markers, we see that a valid STOP condition has been put onto the bus, to create a known bus state again. This is now possible because SDA was released by the device and can now be controlled by the master again.

Right of the markers, we see the originally intended I2C transfer which reads out register 8 from the audio codec without any problems. Also, the bus is in the correct idle state again after that transfer.

Conclusion

It could be shown that the implementation of I2C bus recovery in the R-Car I2C driver using the I2C core helpers was able to recover from a stalled bus. Even though SDA was held low by a device in an inconsistent state, it was recovered and the whole bus was brought back to a known state.