mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-31 11:01:19 +00:00
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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user