diff options
author | Jes B. Klinke <jbk@chromium.org> | 2021-11-30 10:44:47 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-12-14 02:12:27 +0000 |
commit | 7df8f24e9e15359ec9849d78da453da8ab0dcc73 (patch) | |
tree | d51963e239c9491c5f9bc1a46767b485fcce2199 | |
parent | e35ef61c9985c05ae0e7693dddb71067ab323139 (diff) | |
download | chrome-ec-7df8f24e9e15359ec9849d78da453da8ab0dcc73.tar.gz |
chip/stm32: Properly clear UART overrun bit
UART interrupt code is shared between STM32Lxxx chips. However, it
seems that between the original L1 series, and the newer L4 and L5
series, the way to clear the "overrun" status bit has changed without
the code being updated.
While deveopling code for a STML5 chip with DMA disabled, I noticed
occasional watchdog reset as a result of infinite UART interrupt,
caused by the overrun flag not being cleared by the interrupt handler.
This probably does not happen much in production since DMA will reduce
the risk of overrun, (and only the "munna" board uses an affected L4
chip).
Signed-off-by: Jes B. Klinke <jbk@chromium.org>
BUG=b:192262089
BRANCH=munna
TEST=Observe hyperdebug board not lock up
Change-Id: I3df1f269bdb4a37c855295f7fe9641e9a95af998
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3309358
Tested-by: Jes Klinke <jbk@chromium.org>
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Commit-Queue: Jes Klinke <jbk@chromium.org>
-rw-r--r-- | chip/stm32/uart.c | 9 | ||||
-rw-r--r-- | chip/stm32/usart-stm32f0.c | 9 | ||||
-rw-r--r-- | chip/stm32/usart-stm32f3.c | 9 | ||||
-rw-r--r-- | chip/stm32/usart_rx_interrupt-stm32l.c | 18 |
4 files changed, 42 insertions, 3 deletions
diff --git a/chip/stm32/uart.c b/chip/stm32/uart.c index bafca58c46..6be0790c63 100644 --- a/chip/stm32/uart.c +++ b/chip/stm32/uart.c @@ -176,7 +176,14 @@ static void uart_interrupt(void) #if defined(CHIP_FAMILY_STM32F4) STM32_USART_SR(UARTN_BASE) &= ~STM32_USART_SR_TC; #else - STM32_USART_ICR(UARTN_BASE) |= STM32_USART_SR_TC; + /* + * ST reference code does blind write to this register, as is + * usual with the "write 1 to clear" convention, despite the + * datasheet listing the bits as "keep at reset value", (which + * we assume is due to copying from the description of + * reserved bits in read/write registers.) + */ + STM32_USART_ICR(UARTN_BASE) = STM32_USART_SR_TC; #endif if (!(STM32_USART_SR(UARTN_BASE) & ~STM32_USART_SR_TC)) return; diff --git a/chip/stm32/usart-stm32f0.c b/chip/stm32/usart-stm32f0.c index b4e7c924a8..740d3929bc 100644 --- a/chip/stm32/usart-stm32f0.c +++ b/chip/stm32/usart-stm32f0.c @@ -82,7 +82,14 @@ DECLARE_HOOK(HOOK_FREQ_CHANGE, freq_change, HOOK_PRIO_DEFAULT); void usart_clear_tc(struct usart_config const *config) { - STM32_USART_ICR(config->hw->base) |= STM32_USART_ICR_TCCF; + /* + * ST reference code does blind write to this register, as is usual + * with the "write 1 to clear" convention, despite the datasheet + * listing the bits as "keep at reset value", (which we assume is due + * to copying from the description of reserved bits in read/write + * registers.) + */ + STM32_USART_ICR(config->hw->base) = STM32_USART_ICR_TCCF; } /* diff --git a/chip/stm32/usart-stm32f3.c b/chip/stm32/usart-stm32f3.c index 18452cb3fb..887d79d21f 100644 --- a/chip/stm32/usart-stm32f3.c +++ b/chip/stm32/usart-stm32f3.c @@ -56,7 +56,14 @@ static struct usart_hw_ops const usart_variant_hw_ops = { void usart_clear_tc(struct usart_config const *config) { - STM32_USART_ICR(config->hw->base) |= STM32_USART_ICR_TCCF; + /* + * ST reference code does blind write to this register, as is usual + * with the "write 1 to clear" convention, despite the datasheet + * listing the bits as "keep at reset value", (which we assume is due + * to copying from the description of reserved bits in read/write + * registers.) + */ + STM32_USART_ICR(config->hw->base) = STM32_USART_ICR_TCCF; } /* diff --git a/chip/stm32/usart_rx_interrupt-stm32l.c b/chip/stm32/usart_rx_interrupt-stm32l.c index 7c3f765f19..a89d474d05 100644 --- a/chip/stm32/usart_rx_interrupt-stm32l.c +++ b/chip/stm32/usart_rx_interrupt-stm32l.c @@ -30,7 +30,24 @@ static void usart_rx_interrupt_handler(struct usart_config const *config) * we can't disable it. */ if (status & STM32_USART_SR_ORE) { +#ifdef STM32_USART_ICR_ORECF /* + * Newer series (STM32L4xx and STM32L5xx) have an explicit + * "interrupt clear" register. + * + * ST reference code does blind write to this register, as is + * usual with the "write 1 to clear" convention, despite the + * datasheet listing the bits as "keep at reset value", (which + * we assume is due to copying from the description of + * reserved bits in read/write registers.) + */ + STM32_USART_ICR(config->hw->base) = STM32_USART_ICR_ORECF; +#else + /* + * On the older series STM32L1xx, the overrun bit is cleared + * by a read of the status register, followed by a read of the + * data register. + * * In the unlikely event that the overrun error bit was set but * the RXNE bit was not (possibly because a read was done from * RDR without first reading the status register) we do a read @@ -38,6 +55,7 @@ static void usart_rx_interrupt_handler(struct usart_config const *config) */ if (!(status & STM32_USART_SR_RXNE)) (void)STM32_USART_RDR(config->hw->base); +#endif atomic_add((atomic_t *)&(config->state->rx_overrun), 1); } |