summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2018-08-31 16:10:16 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-09-20 11:39:53 -0700
commitc7ecee85c7448707ea80d35794a9d6834c1aa684 (patch)
tree9eb3f190e55400f089a4d3b62f943618be0e3127
parentdd818f7e1b80611f8a6ccf3a79f46e1b952db742 (diff)
downloadchrome-ec-c7ecee85c7448707ea80d35794a9d6834c1aa684.tar.gz
g: fix i2cm NACK processing
When g i2c master controller encounters a NACK it seems to stop processing instruction set included in the INST register and leaves SCL low holding up the bus. Issuing an explicit STOP request in this situation makes sure that the controller completes the NACKed access cycle. BRANCH=cr50, cr50-mp BUG=b:112283593, b:113906660 TEST=verified that when running i2cscan the NACKed cycles complete properly, and the command could be ram multiple times. On dragonegg: > i2csc Scanning 0 master................................................. 0x60................ 0x80. 0x82. 0x84. 0x86............. 0xa0............................................... > i2csc Scanning 0 master................................................. 0x60................ 0x80. 0x82. 0x84. 0x86............. 0xa0............................................... > Change-Id: I7ffff5f32c9f57eb2672318fc8ebd9f74441445d Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1200078 Reviewed-by: Scott Collyer <scollyer@chromium.org>
-rw-r--r--chip/g/i2cm.c16
1 files changed, 14 insertions, 2 deletions
diff --git a/chip/g/i2cm.c b/chip/g/i2cm.c
index b77bd91935..de56e20472 100644
--- a/chip/g/i2cm.c
+++ b/chip/g/i2cm.c
@@ -324,6 +324,7 @@ static int i2cm_execute_sequence(int port, int slave_addr, const uint8_t *out,
{
int rv;
uint32_t inst;
+ uint32_t status;
/* Build sequence instruction */
inst = i2cm_build_sequence(port, slave_addr, out, out_size, in,
@@ -338,8 +339,19 @@ static int i2cm_execute_sequence(int port, int slave_addr, const uint8_t *out,
return rv;
/* Check status value for errors */
- if (GREAD_I(I2C, port, STATUS) & I2CM_ERROR_MASK) {
- /* If failed, then clear INST register */
+ status = GREAD_I(I2C, port, STATUS);
+ if (status & I2CM_ERROR_MASK) {
+ if (status &
+ (GFIELD_MASK(I2C, STATUS, FIRSTSTOP) |
+ GFIELD_MASK(I2C, STATUS, FINALSTOP))) {
+ /*
+ * A stop was requested but not generated, let's make
+ * sure the bus is brought back to the idle state.
+ */
+ GWRITE_I(I2C, port, INST, INST_STOP);
+ i2cm_poll_for_complete(port);
+ }
+ /* Clear INST register after processing failure(s). */
GWRITE_I(I2C, port, INST, 0);
return EC_ERROR_UNKNOWN;
}