SiLabs: serial_api: Keep track of sleep mode changes

Keep a counter of sleeps blocked for every device, and do not try
to unblock sleep modes we did not block. This fixes problems where
serial events would cause EM1/EM2 to be unblocked too early,
causing the MCU to go to EM3 and not being able to wake up.
pull/1500/head
Mikko Polojarvi 2015-12-16 12:58:06 +02:00
parent 0cf5279356
commit 49f6313007
1 changed files with 38 additions and 30 deletions

View File

@ -99,6 +99,8 @@ static CMU_Clock_TypeDef serial_get_clock(serial_t *obj);
static void serial_dmaSetupChannel(serial_t *obj, bool tx_nrx); static void serial_dmaSetupChannel(serial_t *obj, bool tx_nrx);
static void serial_rx_abort_asynch_intern(serial_t *obj, int unblock_sleep); static void serial_rx_abort_asynch_intern(serial_t *obj, int unblock_sleep);
static void serial_tx_abort_asynch_intern(serial_t *obj, int unblock_sleep); static void serial_tx_abort_asynch_intern(serial_t *obj, int unblock_sleep);
static void serial_block_sleep(serial_t *obj);
static void serial_unblock_sleep(serial_t *obj);
/* ISRs for RX and TX events */ /* ISRs for RX and TX events */
#ifdef UART0 #ifdef UART0
@ -756,6 +758,11 @@ static void serial_switch_to_usart(serial_t *obj, int baudrate)
/* TODO: disable clocks? */ /* TODO: disable clocks? */
/* Disable sleep */
uint32_t sleep_count = obj->serial.sleep_blocked;
while( obj->serial.sleep_blocked > 0 )
serial_unblock_sleep(obj);
SerialParity par = ParityNone; SerialParity par = ParityNone;
switch( parity ) { switch( parity ) {
case LEUART_CTRL_PARITY_NONE: par = ParityNone; break; case LEUART_CTRL_PARITY_NONE: par = ParityNone; break;
@ -792,6 +799,10 @@ static void serial_switch_to_usart(serial_t *obj, int baudrate)
serial_uart_free(leuart); serial_uart_free(leuart);
serial_enable(obj, true); serial_enable(obj, true);
/* Restore sleep */
while( obj->serial.sleep_blocked < sleep_count )
serial_block_sleep(obj);
/* Restore interrupts */ /* Restore interrupts */
serial_irq_ids[serial_get_index(obj)] = irq_id; serial_irq_ids[serial_get_index(obj)] = irq_id;
@ -1708,15 +1719,7 @@ int serial_tx_asynch(serial_t *obj, const void *tx, size_t tx_length, uint8_t tx
serial_tx_enable_event(obj, event, true); serial_tx_enable_event(obj, event, true);
// Set up sleepmode // Set up sleepmode
#ifdef LEUART_USING_LFXO serial_block_sleep(obj);
if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){
blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART);
}else{
blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE);
}
#else
blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE);
#endif
// Determine DMA strategy // Determine DMA strategy
serial_dmaTrySetState(&(obj->serial.dmaOptionsTX), hint, obj, true); serial_dmaTrySetState(&(obj->serial.dmaOptionsTX), hint, obj, true);
@ -1788,15 +1791,7 @@ void serial_rx_asynch(serial_t *obj, void *rx, size_t rx_length, uint8_t rx_widt
obj->char_match = char_match; obj->char_match = char_match;
// Set up sleepmode // Set up sleepmode
#ifdef LEUART_USING_LFXO serial_block_sleep(obj);
if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){
blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART);
}else{
blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE);
}
#else
blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE);
#endif
// Determine DMA strategy // Determine DMA strategy
// If character match is enabled, we can't use DMA, sadly. We could when using LEUART though, but that support is not in here yet. // If character match is enabled, we can't use DMA, sadly. We could when using LEUART though, but that support is not in here yet.
@ -2201,7 +2196,14 @@ static void serial_tx_abort_asynch_intern(serial_t *obj, int unblock_sleep)
} }
/* Say that we can stop using this emode */ /* Say that we can stop using this emode */
if( unblock_sleep ) { if(unblock_sleep)
serial_unblock_sleep(obj);
}
static void serial_unblock_sleep(serial_t *obj)
{
if( obj->serial.sleep_blocked > 0 ) {
#ifdef LEUART_USING_LFXO #ifdef LEUART_USING_LFXO
if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){ if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){
unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART); unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART);
@ -2211,9 +2213,24 @@ static void serial_tx_abort_asynch_intern(serial_t *obj, int unblock_sleep)
#else #else
unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE);
#endif #endif
obj->serial.sleep_blocked--;
} }
} }
static void serial_block_sleep(serial_t *obj)
{
obj->serial.sleep_blocked++;
#ifdef LEUART_USING_LFXO
if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){
blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART);
}else{
blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE);
}
#else
blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE);
#endif
}
/** Abort the ongoing RX transaction It disables the enabled interrupt for RX and /** Abort the ongoing RX transaction It disables the enabled interrupt for RX and
* flush RX hardware buffer if RX FIFO is used * flush RX hardware buffer if RX FIFO is used
* *
@ -2273,17 +2290,8 @@ static void serial_rx_abort_asynch_intern(serial_t *obj, int unblock_sleep)
} }
/* Say that we can stop using this emode */ /* Say that we can stop using this emode */
if( unblock_sleep ) { if( unblock_sleep )
#ifdef LEUART_USING_LFXO serial_unblock_sleep(obj);
if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){
unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART);
}else{
unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE);
}
#else
unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE);
#endif
}
} }
#endif //DEVICE_SERIAL_ASYNCH #endif //DEVICE_SERIAL_ASYNCH