diff options
author | Charlie Mooney <charliemooney@chromium.org> | 2012-08-15 16:57:15 -0700 |
---|---|---|
committer | Gerrit <chrome-bot@google.com> | 2012-08-15 18:11:00 -0700 |
commit | 9c45a309b9d462358dfecd4713340ea23d9f12f2 (patch) | |
tree | fcc062fd31013922480d126dfc3a9b250197a0de | |
parent | 4aa491359c4f6147e2b1fd55d32ad5a196e160bd (diff) | |
download | chrome-ec-9c45a309b9d462358dfecd4713340ea23d9f12f2.tar.gz |
Snow: Make i2c slave work in interrupt context
To make software Sync work, they need to be able to call
i2c_send_response() from within host_command_received() while still in
an interrupt context. This won't work if you're using interrupts to
know when the dma transfer has completed. This puts a switch in that
will toggle between interrupts and polling the interrupt flag based on
if the program in in an interrupt context or not.
BUG=chrome-os-partner:12688
TEST=Run "battery" "pmu" boot the machine and use the keyboard. Then
replace the in_interrupt_context() function with "0" to force it to use
polling and repeat the test. Everything should work in both cases.
Change-Id: Ie989c1a6ad29529a7ec390065b310ad4af8cf0bf
Signed-off-by: Charlie Mooney <charliemooney@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/30483
Reviewed-by: Simon Glass <sjg@chromium.org>
-rw-r--r-- | chip/stm32/dma.c | 19 | ||||
-rw-r--r-- | chip/stm32/dma.h | 8 | ||||
-rw-r--r-- | chip/stm32/i2c.c | 20 |
3 files changed, 39 insertions, 8 deletions
diff --git a/chip/stm32/dma.c b/chip/stm32/dma.c index 8281422fa3..9d7f72df6f 100644 --- a/chip/stm32/dma.c +++ b/chip/stm32/dma.c @@ -197,6 +197,25 @@ void dma_init(void) STM32_RCC_AHBENR |= RCC_AHBENR_DMA1EN; } +int dma_wait(int channel) +{ + struct dma_ctlr *dma; + uint32_t mask; + timestamp_t deadline; + + dma = dma_get_ctlr(channel); + mask = DMA_TCIF(channel); + + deadline.val = get_time().val + DMA_TRANSFER_TIMEOUT_US; + while ((REG32(&dma->isr) & mask) != mask) { + if (deadline.val <= get_time().val) + return -1; + else + usleep(DMA_POLLING_INTERVAL_US); + } + return 0; +} + int dma_get_irq(int channel) { ASSERT(channel < DMA_NUM_CHANNELS); diff --git a/chip/stm32/dma.h b/chip/stm32/dma.h index 606bcf72cb..c1d69a7c07 100644 --- a/chip/stm32/dma.h +++ b/chip/stm32/dma.h @@ -206,4 +206,12 @@ void dma_disable_tc_interrupt(int channel); */ struct dma_ctlr *dma_get_ctlr(int channel); +/** + * Wait for the DMA transfer to complete by polling the transfer complete flag + * + * @param channelĀ» Channel number to wait on (DMAC_...) + * @return -1 for timeout, 0 for sucess + */ +int dma_wait(int channel); + #endif diff --git a/chip/stm32/i2c.c b/chip/stm32/i2c.c index 7139695b23..f74479310c 100644 --- a/chip/stm32/i2c.c +++ b/chip/stm32/i2c.c @@ -62,7 +62,7 @@ static struct mutex i2c_mutex; static uint8_t host_buffer[EC_HOST_PARAM_SIZE + 4]; static struct host_cmd_handler_args host_cmd_args; -/* Flag indicating if a command is currently in the buffer*/ +/* Flag indicating if a command is currently in the buffer */ static uint8_t rx_pending; static inline void disable_i2c_interrupt(int port) @@ -113,20 +113,24 @@ static int i2c_write_raw_slave(int port, void *buf, int len) chan = dma_get_channel(DMAC_I2C_TX); dma_prepare_tx(chan, len, (void *)&STM32_I2C_DR(port), buf); - /* set up DMA interrupts to signal when the transfer is over */ - dma_enable_tc_interrupt(DMAC_I2C_TX); - /* Start the DMA */ dma_go(chan); - /* Configuring i2c2 to use DMA*/ + /* Configuring i2c2 to use DMA */ STM32_I2C_CR2(port) |= (1 << 11); - /* Wait for the transmission complete Interrupt */ - task_wait_event(DMA_TRANSFER_TIMEOUT_US); + if (!in_interrupt_context()) { + /* Poll for the transmission complete flag */ + dma_wait(DMAC_I2C_TX); + dma_clear_isr(DMAC_I2C_TX); + } else { + /* Wait for the transmission complete Interrupt */ + dma_enable_tc_interrupt(DMAC_I2C_TX); + task_wait_event(DMA_TRANSFER_TIMEOUT_US); + dma_disable_tc_interrupt(DMAC_I2C_TX); + } dma_disable(DMAC_I2C_TX); - dma_disable_tc_interrupt(DMAC_I2C_TX); STM32_I2C_CR2(port) &= ~(1 << 11); enable_i2c_interrupt(port); |