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>
This commit is contained in:
Charlie Mooney
2012-08-15 16:57:15 -07:00
committed by Gerrit
parent 4aa491359c
commit 9c45a309b9
3 changed files with 39 additions and 8 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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);