Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/35725 )
Change subject: device/software_i2c: Add function to recover bus ......................................................................
device/software_i2c: Add function to recover bus
As the software bus might be in unknown state, add a function to reset it to known (idle) state.
Copied from linux kernel. Tested on Supermicro X11SSH-TF.
Change-Id: I264471e872cb353b28a6b71cc64a11aec59e63f2 Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M src/device/software_i2c.c M src/include/device/i2c_simple.h 2 files changed, 48 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/25/35725/1
diff --git a/src/device/software_i2c.c b/src/device/software_i2c.c index 5dc9990..eecb670 100644 --- a/src/device/software_i2c.c +++ b/src/device/software_i2c.c @@ -343,3 +343,49 @@ "SDA %d, SCL %d\n", bus, bits, software_i2c[bus]->get_sda(bus), software_i2c[bus]->get_scl(bus)); } + +/* + * Try to recover the bus from unknown state. + * Useful if the power rail or reset line isn't under firmware control. + */ +#define RECOVERY_CLK_CNT 9 + +int i2c_recover_bus(unsigned bus) +{ + int i = 0, scl = 1; + + printk(BIOS_INFO, "software_i2c(%d): Trying i2c bus recovery\n", bus); + + /* + * If we can set SDA, we will always create a STOP to ensure additional + * pulses will do no harm. This is achieved by letting SDA follow SCL + * half a cycle later. Check the 'incomplete_write_byte' fault injector + * for details. + */ + software_i2c[bus]->set_scl(bus, 1); + wait(bus); + software_i2c[bus]->set_sda(bus, 1); + wait(bus); + + /* + * By this time SCL is high, as we need to give 9 falling-rising edges + */ + while (i++ < RECOVERY_CLK_CNT * 2) { + if (scl) { + /* SCL shouldn't be low here */ + if (!software_i2c[bus]->get_scl(bus)) { + printk(BIOS_ERR, "software_i2c(%d): SCL stuck low\n", bus); + return 1; + break; + } + } + + scl = !scl; + software_i2c[bus]->set_scl(bus, scl); + /* Creating STOP again, see above */ + wait(bus); + software_i2c[bus]->set_sda(bus, scl); + wait(bus); + } + return 0; +} diff --git a/src/include/device/i2c_simple.h b/src/include/device/i2c_simple.h index e3cc892..7f508b2 100644 --- a/src/include/device/i2c_simple.h +++ b/src/include/device/i2c_simple.h @@ -45,6 +45,8 @@ int i2c_write_field(unsigned int bus, uint8_t slave, uint8_t reg, uint8_t data, uint8_t mask, uint8_t shift);
+int i2c_recover_bus(unsigned int bus); + /* * software_i2c is supposed to be a debug feature. It's usually not compiled in, * but when it is it can be dynamically enabled at runtime for certain busses.