diff options
author | Alec Berg <alecaberg@chromium.org> | 2015-07-30 10:12:54 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-08-01 02:50:23 +0000 |
commit | 2356870a224474adfb7162cc8ed0b1c3abbdb034 (patch) | |
tree | 9db67b82a193c05f88c7b055bd7116a1597e356d | |
parent | d3692bec9f75c94d95c398d38ba903ac8c702019 (diff) | |
download | chrome-ec-2356870a224474adfb7162cc8ed0b1c3abbdb034.tar.gz |
mec1322: make i2c transactions faster by not sleeping task
Modify i2c driver on mec1322 to change from sleeping and waking
on i2c interrupt, to just doing a blocking wait for i2c transfer
to complete. This greatly improves the i2c transaction time on
fast busses.
BUG=chrome-os-partner:43416
BRANCH=none
TEST=test on glados. test can talk to battery and PD MCU. Use
logic analyzer to see delay between bytes during an i2c transfer.
The delay goes from ~70us to ~4us.
Change-Id: Iee2a903d27b2e50e54d64bd6d5ed4920293fe575
Signed-off-by: Alec Berg <alecaberg@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/289667
Reviewed-by: Shawn N <shawnn@chromium.org>
-rw-r--r-- | chip/mec1322/i2c.c | 65 |
1 files changed, 29 insertions, 36 deletions
diff --git a/chip/mec1322/i2c.c b/chip/mec1322/i2c.c index 36ecabdbc4..bf44659ce9 100644 --- a/chip/mec1322/i2c.c +++ b/chip/mec1322/i2c.c @@ -38,6 +38,14 @@ /* Maximum transfer of a SMBUS block transfer */ #define SMBUS_MAX_BLOCK_SIZE 32 +/* + * Amount of time to blocking wait for i2c bus to finish. After this blocking + * timeout, if the bus is still not finished, then allow other tasks to run. + * Note: this is just long enough for a 400kHz bus to finish transmitting one + * byte assuming the bus isn't being held. + */ +#define I2C_WAIT_BLOCKING_TIMEOUT_US 25 + /* I2C controller state data */ struct { /* Transaction timeout, or 0 to use default. */ @@ -115,47 +123,35 @@ static void reset_controller(int controller) } } -static int wait_for_interrupt(int controller, int *event) +static int wait_for_interrupt(int controller) { + int event; + cdata[controller].task_waiting = task_get_current(); task_enable_irq(MEC1322_IRQ_I2C_0 + controller); - /* - * We want to wait here quietly until the I2C interrupt comes - * along, but we don't want to lose any pending events that - * will be needed by the task that started the I2C transaction - * in the first place. So we save them up and restore them when - * the I2C is either completed or timed out. Refer to the - * implementation of usleep() for a similar situation. - */ - *event |= (task_wait_event(cdata[controller].timeout_us) - & ~TASK_EVENT_I2C_IDLE); + + /* Wait until I2C interrupt or timeout. */ + event = task_wait_event_mask(TASK_EVENT_I2C_IDLE, + cdata[controller].timeout_us); cdata[controller].task_waiting = TASK_ID_INVALID; - if (*event & TASK_EVENT_TIMER) { - /* Restore any events that we saw while waiting */ - task_set_event(task_get_current(), - (*event & ~TASK_EVENT_TIMER), 0); - return EC_ERROR_TIMEOUT; - } - return EC_SUCCESS; + + return (event & TASK_EVENT_TIMER) ? EC_ERROR_TIMEOUT : EC_SUCCESS; } static int wait_idle(int controller) { uint8_t sts = MEC1322_I2C_STATUS(controller); + uint64_t block_timeout = get_time().val + I2C_WAIT_BLOCKING_TIMEOUT_US; int rv; - int event = 0; while (!(sts & STS_NBB)) { - rv = wait_for_interrupt(controller, &event); - if (rv) - return rv; + if (get_time().val > block_timeout) { + rv = wait_for_interrupt(controller); + if (rv) + return rv; + } sts = MEC1322_I2C_STATUS(controller); } - /* - * Restore any events that we saw while waiting. TASK_EVENT_TIMER isn't - * one, because we've handled it above. - */ - task_set_event(task_get_current(), event, 0); if (sts & (STS_BER | STS_LAB)) return EC_ERROR_UNKNOWN; @@ -165,20 +161,17 @@ static int wait_idle(int controller) static int wait_byte_done(int controller) { uint8_t sts = MEC1322_I2C_STATUS(controller); + uint64_t block_timeout = get_time().val + I2C_WAIT_BLOCKING_TIMEOUT_US; int rv; - int event = 0; while (sts & STS_PIN) { - rv = wait_for_interrupt(controller, &event); - if (rv) - return rv; + if (get_time().val > block_timeout) { + rv = wait_for_interrupt(controller); + if (rv) + return rv; + } sts = MEC1322_I2C_STATUS(controller); } - /* - * Restore any events that we saw while waiting. TASK_EVENT_TIMER isn't - * one, because we've handled it above. - */ - task_set_event(task_get_current(), event, 0); return sts & STS_LRB; } |