summaryrefslogtreecommitdiff
path: root/chip/npcx/i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/npcx/i2c.c')
-rw-r--r--chip/npcx/i2c.c44
1 files changed, 28 insertions, 16 deletions
diff --git a/chip/npcx/i2c.c b/chip/npcx/i2c.c
index 2a394c7cfb..a7af8632f0 100644
--- a/chip/npcx/i2c.c
+++ b/chip/npcx/i2c.c
@@ -73,6 +73,7 @@ enum smb_oper_state_t {
SMB_MASTER_START,
SMB_WRITE_OPER,
SMB_READ_OPER,
+ SMB_DUMMY_READ_OPER,
SMB_REPEAT_START,
SMB_WRITE_SUSPEND,
SMB_READ_SUSPEND,
@@ -269,22 +270,24 @@ enum smb_error i2c_master_transaction(int controller)
p_status->tx_buf[p_status->idx_buf-1]);
}
} else if (p_status->oper_state == SMB_READ_SUSPEND) {
- /* Need to read the other bytes from next transaction */
- p_status->oper_state = SMB_READ_OPER;
- if (p_status->sz_rxbuf == 1) {
+ /*
+ * Do dummy read if read length is 1 and I2C_XFER_STOP is set
+ * simultaneously.
+ */
+ if (p_status->sz_rxbuf == 1 &&
+ (p_status->flags & I2C_XFER_STOP)) {
/*
* Since SCL is released after reading last byte from
- * previous transaction, we have no chance to set NACK
- * bit if the next transaction is only one byte. Master
- * cannot generate STOP when the last byte is ACK during
- * receiving.
+ * previous transaction, adding a dummy byte for next
+ * transaction which let ec sets NACK bit in time is
+ * necessary. Or i2c master cannot generate STOP
+ * when the last byte is ACK during receiving.
*/
- CPRINTS("I2C %d rxbuf size should exceed one byte in "
- "2th transaction", controller);
- p_status->err_code = SMB_NO_SUPPORT_PTL;
- i2c_recovery(controller, p_status);
- return EC_ERROR_UNKNOWN;
- }
+ p_status->sz_rxbuf++;
+ p_status->oper_state = SMB_DUMMY_READ_OPER;
+ } else
+ /* Need to read the other bytes from next transaction */
+ p_status->oper_state = SMB_READ_OPER;
} else
cprints(CC_I2C, "Unexpected i2c state machine! %d",
p_status->oper_state);
@@ -444,7 +447,8 @@ static void i2c_handle_sda_irq(int controller)
}
}
/* 3 Handle master read operation (read or after a write operation) */
- else if (p_status->oper_state == SMB_READ_OPER) {
+ else if (p_status->oper_state == SMB_READ_OPER ||
+ p_status->oper_state == SMB_DUMMY_READ_OPER) {
uint8_t data;
/* last byte is about to be read - end of transaction */
if (p_status->idx_buf == (p_status->sz_rxbuf - 1)) {
@@ -479,8 +483,12 @@ static void i2c_handle_sda_irq(int controller)
I2C_READ_BYTE(controller, data);
CPRINTS("-R(%02x)", data);
- /* Read to buffer */
- p_status->rx_buf[p_status->idx_buf++] = data;
+ /* Read to buf. Skip last byte if meet SMB_DUMMY_READ_OPER */
+ if (p_status->oper_state == SMB_DUMMY_READ_OPER &&
+ p_status->idx_buf == (p_status->sz_rxbuf - 1))
+ p_status->idx_buf++;
+ else
+ p_status->rx_buf[p_status->idx_buf++] = data;
/* last byte is read - end of transaction */
if (p_status->idx_buf == p_status->sz_rxbuf) {
@@ -628,6 +636,10 @@ int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size,
if (ctrl < 0)
return EC_ERROR_INVAL;
+ /* Skip unnecessary transaction */
+ if (out_size == 0 && in_size == 0)
+ return EC_SUCCESS;
+
p_status = i2c_stsobjs + ctrl;
/* Assign current task ID */