aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorge Cherian <george.cherian@cavium.com>2018-08-09 02:36:48 -0400
committerWolfram Sang <wsa@the-dreams.de>2018-08-09 11:41:13 -0400
commit5eb173f5c8f3a3cdc47b3952c368f10a28c81ab8 (patch)
treeb3e6375b12318923fe015c25f655bee29aefb462
parent1ffaddd029c867d134a1dde39f540dcc8c52e274 (diff)
i2c: xlp9xx: Fix case where SSIF read transaction completes early
During ipmi stress tests we see occasional failure of transactions at the boot time. This happens in the case of a I2C_M_RECV_LEN transactions, when the read transfer completes (with the initial read length of 34) before the driver gets a chance to handle interrupts. The current driver code expects at least 2 interrupts for I2C_M_RECV_LEN transactions. The length is updated during the first interrupt, and the buffer contents are only copied during subsequent interrupts. In case of just one interrupt, we will complete the transaction without copying out the bytes from RX fifo. Update the code to drain the RX fifo after the length update, so that the transaction completes correctly in all cases. Signed-off-by: George Cherian <george.cherian@cavium.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de> Cc: stable@kernel.org
-rw-r--r--drivers/i2c/busses/i2c-xlp9xx.c41
1 files changed, 28 insertions, 13 deletions
diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c
index 1f41a4f89c08..8a873975cf12 100644
--- a/drivers/i2c/busses/i2c-xlp9xx.c
+++ b/drivers/i2c/busses/i2c-xlp9xx.c
@@ -191,28 +191,43 @@ static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
191 if (priv->len_recv) { 191 if (priv->len_recv) {
192 /* read length byte */ 192 /* read length byte */
193 rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO); 193 rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
194
195 /*
196 * We expect at least 2 interrupts for I2C_M_RECV_LEN
197 * transactions. The length is updated during the first
198 * interrupt, and the buffer contents are only copied
199 * during subsequent interrupts. If in case the interrupts
200 * get merged we would complete the transaction without
201 * copying out the bytes from RX fifo. To avoid this now we
202 * drain the fifo as and when data is available.
203 * We drained the rlen byte already, decrement total length
204 * by one.
205 */
206
207 len--;
194 if (rlen > I2C_SMBUS_BLOCK_MAX || rlen == 0) { 208 if (rlen > I2C_SMBUS_BLOCK_MAX || rlen == 0) {
195 rlen = 0; /*abort transfer */ 209 rlen = 0; /*abort transfer */
196 priv->msg_buf_remaining = 0; 210 priv->msg_buf_remaining = 0;
197 priv->msg_len = 0; 211 priv->msg_len = 0;
198 } else { 212 xlp9xx_i2c_update_rlen(priv);
199 *buf++ = rlen; 213 return;
200 if (priv->client_pec)
201 ++rlen; /* account for error check byte */
202 /* update remaining bytes and message length */
203 priv->msg_buf_remaining = rlen;
204 priv->msg_len = rlen + 1;
205 } 214 }
215
216 *buf++ = rlen;
217 if (priv->client_pec)
218 ++rlen; /* account for error check byte */
219 /* update remaining bytes and message length */
220 priv->msg_buf_remaining = rlen;
221 priv->msg_len = rlen + 1;
206 xlp9xx_i2c_update_rlen(priv); 222 xlp9xx_i2c_update_rlen(priv);
207 priv->len_recv = false; 223 priv->len_recv = false;
208 } else {
209 len = min(priv->msg_buf_remaining, len);
210 for (i = 0; i < len; i++, buf++)
211 *buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
212
213 priv->msg_buf_remaining -= len;
214 } 224 }
215 225
226 len = min(priv->msg_buf_remaining, len);
227 for (i = 0; i < len; i++, buf++)
228 *buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
229
230 priv->msg_buf_remaining -= len;
216 priv->msg_buf = buf; 231 priv->msg_buf = buf;
217 232
218 if (priv->msg_buf_remaining) 233 if (priv->msg_buf_remaining)