tegra: i2c: Add a timeout to I2C bit clear recovery mechanism

Our tests with the I2C bit clear mechanism (recovering from "lost
arbitration" errors) show that the bit clear hardware does not work
correctly in some situations. When a wedged slave device tries to send
more than one 0-to-1-to-0 transition to the host (e.g. leftover bits
from an aborted read), the controller never transitions the BC_ENABLE
bit back to zero.

This patch adds a long timeout to the bit clear code that waits for
register transitions as a safeguard. This way, We will still eventually
exit the function (probably followed by a reboot). Our tests show that
this will recover from all conditions after at most a few reboots.

BRANCH=nyan
BUG=chrome-os-partner:28323
TEST=Ran wedge_ack and wedge_read tests with software_i2c patch, system
recovered as expected in all cases.

Original-Change-Id: I6c37119130e1240e1ef3a5944582abbcd2e39ff0
Original-Signed-off-by: Julius Werner <jwerner@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/200265
Original-Reviewed-by: Tom Warren <twarren@nvidia.com>
Original-Reviewed-by: Gabe Black <gabeblack@chromium.org>
(cherry picked from commit 4c8d0af25cf107a38c856b38067b8f2f74384f22)
Signed-off-by: Marc Jones <marc.jones@se-eng.com>

Change-Id: I600d5c9a8e68719cf8795c083c5fac63f626f5bf
Reviewed-on: http://review.coreboot.org/7948
Tested-by: build bot (Jenkins)
Reviewed-by: David Hendricks <dhendrix@chromium.org>
This commit is contained in:
Julius Werner 2014-05-16 11:27:46 -07:00 committed by Marc Jones
parent 37d7ac8b5b
commit 83d1ba7e3d
1 changed files with 10 additions and 4 deletions

View File

@ -19,6 +19,7 @@
#include <arch/io.h>
#include <console/console.h>
#include <delay.h>
#include <device/i2c.h>
#include <stdlib.h>
#include <string.h>
@ -30,6 +31,7 @@ static void do_bus_clear(int bus)
struct tegra_i2c_bus_info *info = &tegra_i2c_info[bus];
struct tegra_i2c_regs * const regs = info->base;
uint32_t bc;
int i, timeout_ms = 10;
// BUS CLEAR regs (from TRM):
// 1. Reset the I2C controller (already done)
@ -41,16 +43,20 @@ static void do_bus_clear(int bus)
write32(bc, &regs->bus_clear_config);
// 4.1 Set MSTR_CONFIG_LOAD and wait for clear
write32(I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD_ENABLE, &regs->config_load);
do {
for (i = 0; i < timeout_ms * 10 && (read32(&regs->config_load) &
I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD_ENABLE); i++) {
printk(BIOS_DEBUG, "%s: wait for MSTR_CONFIG_LOAD to clear\n",
__func__);
} while (read32(&regs->config_load) & I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD_ENABLE);
udelay(100);
}
// 5. Set ENABLE to start the bus clear op
write32(bc | I2C_BUS_CLEAR_CONFIG_BC_ENABLE, &regs->bus_clear_config);
do {
for (i = 0; i < timeout_ms * 10 && (read32(&regs->bus_clear_config) &
I2C_BUS_CLEAR_CONFIG_BC_ENABLE); i++) {
printk(BIOS_DEBUG, "%s: wait for bus clear completion\n",
__func__);
} while (read32(&regs->bus_clear_config) & I2C_BUS_CLEAR_CONFIG_BC_ENABLE);
udelay(100);
}
}
static int tegra_i2c_send_recv(int bus, int read,