summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Mooney <charliemooney@chromium.org>2012-08-15 16:57:15 -0700
committerGerrit <chrome-bot@google.com>2012-08-15 18:11:00 -0700
commit9c45a309b9d462358dfecd4713340ea23d9f12f2 (patch)
treefcc062fd31013922480d126dfc3a9b250197a0de
parent4aa491359c4f6147e2b1fd55d32ad5a196e160bd (diff)
downloadchrome-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.c19
-rw-r--r--chip/stm32/dma.h8
-rw-r--r--chip/stm32/i2c.c20
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);