Difference between revisions of "Tests:I2C-bus-recovery-write-byte-fix"
(first part) |
(second paragraph added) |
||
Line 30: | Line 30: | ||
== Software Environment == | == Software Environment == | ||
− | A busybox with standard commands like ''cd'', ''cat'', ''echo'' is needed. Also, ''i2cget | + | A busybox with standard commands like ''cd'', ''cat'', ''echo'' is needed. Also, ''i2cget'' from the i2c-tools package should be available. |
=Previous state= | =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 | 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 | + | resurrect a stalled bus. However, the implementation had a flaw which could lead to accidental writes to a device during recovery. To demonstrate this, |
− | a new fault injector was implemented. | + | a new fault injector was implemented. For a summary about I2C fault injection, have a look here: [[Tests:I2C-fault-injection]] |
− | + | The Renesas I2C IP core is special, because it cannot read the SDA status directly, only set SDA. This leads to an undesired write when the following | |
− | + | sequence of commands is executed: | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | Setup fault injector on the Renesas Lager board: | |
− | |||
− | |||
− | |||
− | |||
− | |||
<pre> | <pre> | ||
# echo 2 > /sys/devices/platform/i2c-11/current_master | # echo 2 > /sys/devices/platform/i2c-11/current_master | ||
i2c-gpio i2c-8: using lines 979 (SDA) and 978 (SCL) | i2c-gpio i2c-8: using lines 979 (SDA) and 978 (SCL) | ||
− | |||
− | |||
− | |||
− | |||
# echo 1 > /sys/devices/platform/i2c-12/current_master | # echo 1 > /sys/devices/platform/i2c-12/current_master | ||
i2c-rcar e6530000.i2c: probed | i2c-rcar e6530000.i2c: probed | ||
+ | # cd /sys/kernel/debug/i2c-fault-injector/i2c-8/ | ||
</pre> | </pre> | ||
− | + | Check original value of register 0 from device 0x12. It is 0x00 now: | |
<pre> | <pre> | ||
− | # | + | # i2cget -y -f 2 0x12 0 |
− | + | 0x00 | |
</pre> | </pre> | ||
− | + | Use the ''incomplete_write_byte'' fault injector. Then, read register 8 to enforce bus recovery. Note that the read value from register 8 succeeds, | |
− | + | so the stalled bus has been corrected. Also, the register value 0xe1 is correct: | |
− | |||
− | |||
<pre> | <pre> | ||
+ | # echo 0x12 > incomplete_write_byte | ||
# i2cget -y -f 2 0x12 8 | # i2cget -y -f 2 0x12 8 | ||
0xe1 | 0xe1 | ||
</pre> | </pre> | ||
− | + | But if we read out register 0 again, it has been modified to 0xff! Which is the result of having SDA high all the time while clocking SCL: | |
− | [[File:Wsa- | + | <pre> |
+ | # i2cget -y -f 2 0x12 0 | ||
+ | 0xff | ||
+ | </pre> | ||
+ | |||
+ | Here is a scope of that scenario: | ||
+ | |||
+ | [[File:Wsa-i2c_fi-recovery-unwanted-write.png|800x428px|]Scope of an I2C bus recovery causing an undesired write to a device.]] | ||
− | + | You can even see the device acknowledging the sent byte. | |
− | |||
− | |||
− | + | =Fixed state= | |
− | |||
− | + | https://elinux.org/File:Wsa-i2c_fi-recovery-write_byte_with_stop.png | |
− | |||
=Conclusion= | =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. | 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. | 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. |
Revision as of 08:54, 2 July 2018
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.
Contents
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 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 demonstrate this, a new fault injector was implemented. For a summary about I2C fault injection, have a look here: Tests:I2C-fault-injection
The Renesas I2C IP core is special, because it cannot read the SDA status directly, only set SDA. This leads to an undesired write when the following sequence of commands is executed:
Setup fault injector on the Renesas Lager board:
# echo 2 > /sys/devices/platform/i2c-11/current_master i2c-gpio i2c-8: using lines 979 (SDA) and 978 (SCL) # echo 1 > /sys/devices/platform/i2c-12/current_master i2c-rcar e6530000.i2c: probed # cd /sys/kernel/debug/i2c-fault-injector/i2c-8/
Check original value of register 0 from device 0x12. It is 0x00 now:
# i2cget -y -f 2 0x12 0 0x00
Use the incomplete_write_byte fault injector. Then, read register 8 to enforce bus recovery. Note that the read value from register 8 succeeds, so the stalled bus has been corrected. Also, the register value 0xe1 is correct:
# echo 0x12 > incomplete_write_byte # i2cget -y -f 2 0x12 8 0xe1
But if we read out register 0 again, it has been modified to 0xff! Which is the result of having SDA high all the time while clocking SCL:
# i2cget -y -f 2 0x12 0 0xff
Here is a scope of that scenario:
You can even see the device acknowledging the sent byte.
Fixed state
https://elinux.org/File:Wsa-i2c_fi-recovery-write_byte_with_stop.png
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.