diff options
author | Vadim Bendebury <vbendeb@chromium.org> | 2018-08-31 16:10:16 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-09-20 11:39:53 -0700 |
commit | c7ecee85c7448707ea80d35794a9d6834c1aa684 (patch) | |
tree | 9eb3f190e55400f089a4d3b62f943618be0e3127 | |
parent | dd818f7e1b80611f8a6ccf3a79f46e1b952db742 (diff) | |
download | chrome-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.c | 16 |
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; } |