diff options
author | Dino Li <Dino.Li@ite.com.tw> | 2016-08-16 15:41:07 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-08-22 00:11:58 -0700 |
commit | 81fd1393fa3ecb4709a7e6f83c402d7a6a1ccf00 (patch) | |
tree | 9abd9872f8ad8706d992ee2391dfeea06321d1d8 | |
parent | e3297d725517de90a657e157f3ceafe53b2b13f2 (diff) | |
download | chrome-ec-81fd1393fa3ecb4709a7e6f83c402d7a6a1ccf00.tar.gz |
it83xx: fix i2c module
1. If one i2c transaction includes timeout and done events
at the same time, the transaction result will be timeout.
Fixed: The transaction is succeed instead of timeout.
2. The interrupt of i2c will not be enabled if an i2c write transaction
is split into two or more xfer.
Signed-off-by: Dino Li <dino.li@ite.com.tw>
BRANCH=none
BUG=none
TEST=1. Console commands:'i2cscan', 'i2cxfer', and 'battery'.
2. Use two or more i2c_xfer(the first with flag 'I2C_XFER_START'
and the last with flag 'I2C_XFER_STOP') to do a i2c write
transaction and no error.
Change-Id: Ieb2cb229748ac9504cf1636a2826bbb3097aa55c
Reviewed-on: https://chromium-review.googlesource.com/360762
Commit-Ready: Dino Li <Dino.Li@ite.com.tw>
Tested-by: Dino Li <Dino.Li@ite.com.tw>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | chip/it83xx/i2c.c | 34 |
1 files changed, 22 insertions, 12 deletions
diff --git a/chip/it83xx/i2c.c b/chip/it83xx/i2c.c index 2f5dd31fff..b76c04ba1e 100644 --- a/chip/it83xx/i2c.c +++ b/chip/it83xx/i2c.c @@ -184,9 +184,9 @@ const struct i2c_ctrl_t i2c_ctrl_regs[] = { enum i2c_ch_status { I2C_CH_NORMAL = 0, - I2C_CH_W2R, + I2C_CH_REPEAT_START, I2C_CH_WAIT_READ, - I2C_CH_DIRECT_W2R = 4, + I2C_CH_WAIT_NEXT_XFER, }; /* I2C port state data */ @@ -401,6 +401,10 @@ static int i2c_tran_write(int p) pd->widx++; /* W/C byte done for next byte */ IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE; + if (pd->i2ccs == I2C_CH_REPEAT_START) { + pd->i2ccs = I2C_CH_NORMAL; + task_enable_irq(i2c_ctrl_regs[p].irq); + } } else { /* done */ pd->out_size = 0; @@ -415,7 +419,7 @@ static int i2c_tran_write(int p) IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE; } else { - pd->i2ccs = I2C_CH_W2R; + pd->i2ccs = I2C_CH_REPEAT_START; return 0; } } @@ -451,9 +455,9 @@ static int i2c_tran_read(int p) else IT83XX_SMB_HOCTL(p) = 0x5D; } else { - if ((pd->i2ccs == I2C_CH_W2R) || + if ((pd->i2ccs == I2C_CH_REPEAT_START) || (pd->i2ccs == I2C_CH_WAIT_READ)) { - if (pd->i2ccs == I2C_CH_W2R) { + if (pd->i2ccs == I2C_CH_REPEAT_START) { /* write to read */ i2c_w2r_change_direction(p); } else { @@ -535,12 +539,16 @@ static int enhanced_i2c_tran_write(int p) /* Send Byte */ i2c_pio_trans_data(p, TX_DIRECT, out_data, 0); + if (pd->i2ccs == I2C_CH_WAIT_NEXT_XFER) { + pd->i2ccs = I2C_CH_NORMAL; + task_enable_irq(i2c_ctrl_regs[p].irq); + } } else { /* done */ pd->out_size = 0; if (pd->in_size > 0) { /* Write to read protocol */ - pd->i2ccs = I2C_CH_W2R; + pd->i2ccs = I2C_CH_REPEAT_START; /* Repeat Start */ i2c_pio_trans_data(p, RX_DIRECT, (pd->addr + 1), 1); @@ -555,7 +563,7 @@ static int enhanced_i2c_tran_write(int p) return 0; } /* Direct write with direct read */ - pd->i2ccs = I2C_CH_DIRECT_W2R; + pd->i2ccs = I2C_CH_WAIT_NEXT_XFER; return 0; } } @@ -586,7 +594,7 @@ static int enhanced_i2c_tran_read(int p) i2c_pio_trans_data(p, RX_DIRECT, (pd->addr + 1), 1); } else { if (pd->i2ccs) { - if (pd->i2ccs == I2C_CH_W2R) { + if (pd->i2ccs == I2C_CH_REPEAT_START) { pd->i2ccs = I2C_CH_NORMAL; /* Receive data */ i2c_pio_trans_data(p, RX_DIRECT, in_data, 0); @@ -718,9 +726,7 @@ int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, if (out_size == 0 && in_size == 0) return EC_SUCCESS; - if ((pd->i2ccs == I2C_CH_W2R) || - (pd->i2ccs == I2C_CH_WAIT_READ) || - (pd->i2ccs & I2C_CH_DIRECT_W2R)) { + if (pd->i2ccs) { if ((flags & I2C_XFER_SINGLE) == I2C_XFER_SINGLE) flags &= ~I2C_XFER_START; } @@ -774,12 +780,16 @@ int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, pd->task_waiting = TASK_ID_INVALID; /* Handle timeout */ - if (events & TASK_EVENT_TIMER) { + if (!(events & TASK_EVENT_I2C_IDLE)) { pd->err = EC_ERROR_TIMEOUT; /* reset i2c port */ i2c_reset(port, I2C_RC_TIMEOUT); } + /* reset i2c channel status */ + if (pd->err) + pd->i2ccs = I2C_CH_NORMAL; + return pd->err; } |