summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJes B. Klinke <jbk@chromium.org>2021-11-30 10:44:47 -0800
committerCommit Bot <commit-bot@chromium.org>2021-12-14 02:12:27 +0000
commit7df8f24e9e15359ec9849d78da453da8ab0dcc73 (patch)
treed51963e239c9491c5f9bc1a46767b485fcce2199
parente35ef61c9985c05ae0e7693dddb71067ab323139 (diff)
downloadchrome-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.c9
-rw-r--r--chip/stm32/usart-stm32f0.c9
-rw-r--r--chip/stm32/usart-stm32f3.c9
-rw-r--r--chip/stm32/usart_rx_interrupt-stm32l.c18
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);
}