Skip to content

Commit c1a173a

Browse files
ABESTMkartben
authored andcommitted
driver: uart: stm32: Add workaround for DMAT errata in low-power modes
This change adds additional workaround for following errata: "USART does not generate DMA requests after setting/clearing DMAT bit" Instead of keepint DMAT bit set, it sends first byte by polling in firmware. This prevents additional power consumption in STOP mode, caused by keeping DMAT bit set. Signed-off-by: Adam Berlinger <adam.berlinger@st.com>
1 parent ccf07d4 commit c1a173a

File tree

2 files changed

+90
-23
lines changed

2 files changed

+90
-23
lines changed

drivers/serial/Kconfig.stm32

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,34 @@ config UART_STM32
2525

2626
if UART_STM32
2727

28-
config UART_STM32U5_ERRATA_DMAT
29-
bool
30-
default y
31-
depends on SOC_STM32U575XX || SOC_STM32U585XX || \
28+
if SOC_STM32U575XX || SOC_STM32U585XX || \
3229
SOC_STM32H562XX || SOC_STM32H563XX || SOC_STM32H573XX
30+
31+
choice UART_STM32U5_ERRATA_DMAT
32+
prompt "Workaround for DMAT errata on selected devices"
33+
default UART_STM32U5_ERRATA_DMAT_LOWPOWER if PM
34+
default UART_STM32U5_ERRATA_DMAT_NOCLEAR if !PM
3335
help
3436
Handles erratum "USART does not generate DMA requests after
3537
setting/clearing DMAT bit".
3638
Seen in Errata Sheet 0499 § 2.19.2 and §2.20.1 for stm32u57x/u58x,
3739
Errata Sheet 0565 § 2.14.1 and §2.15.1 for stm32h56x/h57x
3840

39-
endif
41+
config UART_STM32U5_ERRATA_DMAT_LOWPOWER
42+
bool "Send first byte by polling"
43+
help
44+
This option sends first byte via software polling and
45+
rest of the buffer is sent via DMA. This option is suitable
46+
for STOP low-power modes.
47+
48+
config UART_STM32U5_ERRATA_DMAT_NOCLEAR
49+
bool "Do not clear DMAT"
50+
help
51+
This option keeps DMAT bit set. This may cause additional power
52+
consumption in STOP low-power modes.
53+
54+
endchoice
55+
56+
endif # U575 || U585 || H562 || H563 || H573
57+
58+
endif # UART_STM32

drivers/serial/uart_stm32.c

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,7 @@ static inline void uart_stm32_dma_tx_enable(const struct device *dev)
14341434

14351435
static inline void uart_stm32_dma_tx_disable(const struct device *dev)
14361436
{
1437-
#ifdef CONFIG_UART_STM32U5_ERRATA_DMAT
1437+
#ifdef CONFIG_UART_STM32U5_ERRATA_DMAT_NOCLEAR
14381438
ARG_UNUSED(dev);
14391439

14401440
/*
@@ -1615,8 +1615,16 @@ static int uart_stm32_async_tx(const struct device *dev,
16151615
const struct uart_stm32_config *config = dev->config;
16161616
USART_TypeDef *usart = config->usart;
16171617
struct uart_stm32_data *data = dev->data;
1618+
__maybe_unused unsigned int key;
16181619
int ret;
16191620

1621+
/* Check size of singl character (1 or 2 bytes) */
1622+
const int char_size = (IS_ENABLED(CONFIG_UART_WIDE_DATA) &&
1623+
(LL_USART_GetDataWidth(usart) == LL_USART_DATAWIDTH_9B) &&
1624+
(LL_USART_GetParity(usart) == LL_USART_PARITY_NONE))
1625+
? 2
1626+
: 1;
1627+
16201628
if (data->dma_tx.dma_dev == NULL) {
16211629
return -ENODEV;
16221630
}
@@ -1648,34 +1656,74 @@ static int uart_stm32_async_tx(const struct device *dev,
16481656
/* Enable TC interrupt so we can signal correct TX done */
16491657
LL_USART_EnableIT_TC(usart);
16501658

1651-
/* set source address */
1652-
data->dma_tx.blk_cfg.source_address = (uint32_t)data->dma_tx.buffer;
1653-
data->dma_tx.blk_cfg.block_size = data->dma_tx.buffer_length;
1659+
/**
1660+
* Setup DMA descriptor for TX.
1661+
* If DMAT low-power errata workaround is enabled,
1662+
* we send the first character using polling and the rest
1663+
* using DMA; as such, single-character transfers use only
1664+
* polling and don't need to prepare a DMA descriptor.
1665+
* In other configurations, the DMA is always used.
1666+
*/
1667+
if (!IS_ENABLED(CONFIG_UART_STM32U5_ERRATA_DMAT_LOWPOWER) ||
1668+
data->dma_tx.buffer_length > char_size) {
1669+
if (IS_ENABLED(CONFIG_UART_STM32U5_ERRATA_DMAT_LOWPOWER)) {
1670+
/* set source address */
1671+
data->dma_tx.blk_cfg.source_address =
1672+
((uint32_t)data->dma_tx.buffer) + char_size;
1673+
data->dma_tx.blk_cfg.block_size = data->dma_tx.buffer_length - char_size;
1674+
} else {
1675+
/* set source address */
1676+
data->dma_tx.blk_cfg.source_address = ((uint32_t)data->dma_tx.buffer);
1677+
data->dma_tx.blk_cfg.block_size = data->dma_tx.buffer_length;
1678+
}
16541679

1655-
ret = dma_config(data->dma_tx.dma_dev, data->dma_tx.dma_channel,
1656-
&data->dma_tx.dma_cfg);
1680+
ret = dma_config(data->dma_tx.dma_dev, data->dma_tx.dma_channel,
1681+
&data->dma_tx.dma_cfg);
16571682

1658-
if (ret != 0) {
1659-
LOG_ERR("dma tx config error!");
1660-
return -EINVAL;
1661-
}
1683+
if (ret != 0) {
1684+
LOG_ERR("dma tx config error!");
1685+
return -EINVAL;
1686+
}
16621687

1663-
if (dma_start(data->dma_tx.dma_dev, data->dma_tx.dma_channel)) {
1664-
LOG_ERR("UART err: TX DMA start failed!");
1665-
return -EFAULT;
1666-
}
1688+
if (dma_start(data->dma_tx.dma_dev, data->dma_tx.dma_channel)) {
1689+
LOG_ERR("UART err: TX DMA start failed!");
1690+
return -EFAULT;
1691+
}
16671692

1668-
/* Start TX timer */
1669-
async_timer_start(&data->dma_tx.timeout_work, data->dma_tx.timeout);
1693+
/* Start TX timer */
1694+
async_timer_start(&data->dma_tx.timeout_work, data->dma_tx.timeout);
1695+
}
16701696

16711697
#ifdef CONFIG_PM
16721698

16731699
/* Do not allow system to suspend until transmission has completed */
16741700
uart_stm32_pm_policy_state_lock_get_unconditional();
16751701
#endif
16761702

1677-
/* Enable TX DMA requests */
1678-
uart_stm32_dma_tx_enable(dev);
1703+
if (IS_ENABLED(CONFIG_UART_STM32U5_ERRATA_DMAT_LOWPOWER)) {
1704+
/**
1705+
* Send first character using polling.
1706+
* The DMA TX needs to be enabled before the UART transmits
1707+
* the character and triggers transfer complete event.
1708+
*/
1709+
key = irq_lock();
1710+
1711+
if (char_size > 1) {
1712+
LL_USART_TransmitData9(usart, *(const uint16_t *)tx_data);
1713+
} else {
1714+
LL_USART_TransmitData8(usart, *tx_data);
1715+
}
1716+
1717+
if (data->dma_tx.buffer_length > char_size) {
1718+
/* Enable TX DMA requests */
1719+
uart_stm32_dma_tx_enable(dev);
1720+
}
1721+
1722+
irq_unlock(key);
1723+
} else {
1724+
/* Enable TX DMA requests */
1725+
uart_stm32_dma_tx_enable(dev);
1726+
}
16791727

16801728
return 0;
16811729
}

0 commit comments

Comments
 (0)