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>
This commit is contained in:
parent
ccf07d45b5
commit
c1a173a884
2 changed files with 91 additions and 24 deletions
|
@ -25,15 +25,34 @@ config UART_STM32
|
|||
|
||||
if UART_STM32
|
||||
|
||||
config UART_STM32U5_ERRATA_DMAT
|
||||
bool
|
||||
default y
|
||||
depends on SOC_STM32U575XX || SOC_STM32U585XX || \
|
||||
if SOC_STM32U575XX || SOC_STM32U585XX || \
|
||||
SOC_STM32H562XX || SOC_STM32H563XX || SOC_STM32H573XX
|
||||
|
||||
choice UART_STM32U5_ERRATA_DMAT
|
||||
prompt "Workaround for DMAT errata on selected devices"
|
||||
default UART_STM32U5_ERRATA_DMAT_LOWPOWER if PM
|
||||
default UART_STM32U5_ERRATA_DMAT_NOCLEAR if !PM
|
||||
help
|
||||
Handles erratum "USART does not generate DMA requests after
|
||||
setting/clearing DMAT bit".
|
||||
Seen in Errata Sheet 0499 § 2.19.2 and §2.20.1 for stm32u57x/u58x,
|
||||
Errata Sheet 0565 § 2.14.1 and §2.15.1 for stm32h56x/h57x
|
||||
|
||||
endif
|
||||
config UART_STM32U5_ERRATA_DMAT_LOWPOWER
|
||||
bool "Send first byte by polling"
|
||||
help
|
||||
This option sends first byte via software polling and
|
||||
rest of the buffer is sent via DMA. This option is suitable
|
||||
for STOP low-power modes.
|
||||
|
||||
config UART_STM32U5_ERRATA_DMAT_NOCLEAR
|
||||
bool "Do not clear DMAT"
|
||||
help
|
||||
This option keeps DMAT bit set. This may cause additional power
|
||||
consumption in STOP low-power modes.
|
||||
|
||||
endchoice
|
||||
|
||||
endif # U575 || U585 || H562 || H563 || H573
|
||||
|
||||
endif # UART_STM32
|
||||
|
|
|
@ -1434,7 +1434,7 @@ static inline void uart_stm32_dma_tx_enable(const struct device *dev)
|
|||
|
||||
static inline void uart_stm32_dma_tx_disable(const struct device *dev)
|
||||
{
|
||||
#ifdef CONFIG_UART_STM32U5_ERRATA_DMAT
|
||||
#ifdef CONFIG_UART_STM32U5_ERRATA_DMAT_NOCLEAR
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
/*
|
||||
|
@ -1615,8 +1615,16 @@ static int uart_stm32_async_tx(const struct device *dev,
|
|||
const struct uart_stm32_config *config = dev->config;
|
||||
USART_TypeDef *usart = config->usart;
|
||||
struct uart_stm32_data *data = dev->data;
|
||||
__maybe_unused unsigned int key;
|
||||
int ret;
|
||||
|
||||
/* Check size of singl character (1 or 2 bytes) */
|
||||
const int char_size = (IS_ENABLED(CONFIG_UART_WIDE_DATA) &&
|
||||
(LL_USART_GetDataWidth(usart) == LL_USART_DATAWIDTH_9B) &&
|
||||
(LL_USART_GetParity(usart) == LL_USART_PARITY_NONE))
|
||||
? 2
|
||||
: 1;
|
||||
|
||||
if (data->dma_tx.dma_dev == NULL) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -1648,34 +1656,74 @@ static int uart_stm32_async_tx(const struct device *dev,
|
|||
/* Enable TC interrupt so we can signal correct TX done */
|
||||
LL_USART_EnableIT_TC(usart);
|
||||
|
||||
/* set source address */
|
||||
data->dma_tx.blk_cfg.source_address = (uint32_t)data->dma_tx.buffer;
|
||||
data->dma_tx.blk_cfg.block_size = data->dma_tx.buffer_length;
|
||||
/**
|
||||
* Setup DMA descriptor for TX.
|
||||
* If DMAT low-power errata workaround is enabled,
|
||||
* we send the first character using polling and the rest
|
||||
* using DMA; as such, single-character transfers use only
|
||||
* polling and don't need to prepare a DMA descriptor.
|
||||
* In other configurations, the DMA is always used.
|
||||
*/
|
||||
if (!IS_ENABLED(CONFIG_UART_STM32U5_ERRATA_DMAT_LOWPOWER) ||
|
||||
data->dma_tx.buffer_length > char_size) {
|
||||
if (IS_ENABLED(CONFIG_UART_STM32U5_ERRATA_DMAT_LOWPOWER)) {
|
||||
/* set source address */
|
||||
data->dma_tx.blk_cfg.source_address =
|
||||
((uint32_t)data->dma_tx.buffer) + char_size;
|
||||
data->dma_tx.blk_cfg.block_size = data->dma_tx.buffer_length - char_size;
|
||||
} else {
|
||||
/* set source address */
|
||||
data->dma_tx.blk_cfg.source_address = ((uint32_t)data->dma_tx.buffer);
|
||||
data->dma_tx.blk_cfg.block_size = data->dma_tx.buffer_length;
|
||||
}
|
||||
|
||||
ret = dma_config(data->dma_tx.dma_dev, data->dma_tx.dma_channel,
|
||||
&data->dma_tx.dma_cfg);
|
||||
ret = dma_config(data->dma_tx.dma_dev, data->dma_tx.dma_channel,
|
||||
&data->dma_tx.dma_cfg);
|
||||
|
||||
if (ret != 0) {
|
||||
LOG_ERR("dma tx config error!");
|
||||
return -EINVAL;
|
||||
if (ret != 0) {
|
||||
LOG_ERR("dma tx config error!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dma_start(data->dma_tx.dma_dev, data->dma_tx.dma_channel)) {
|
||||
LOG_ERR("UART err: TX DMA start failed!");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Start TX timer */
|
||||
async_timer_start(&data->dma_tx.timeout_work, data->dma_tx.timeout);
|
||||
}
|
||||
|
||||
if (dma_start(data->dma_tx.dma_dev, data->dma_tx.dma_channel)) {
|
||||
LOG_ERR("UART err: TX DMA start failed!");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Start TX timer */
|
||||
async_timer_start(&data->dma_tx.timeout_work, data->dma_tx.timeout);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* Do not allow system to suspend until transmission has completed */
|
||||
uart_stm32_pm_policy_state_lock_get_unconditional();
|
||||
#endif
|
||||
|
||||
/* Enable TX DMA requests */
|
||||
uart_stm32_dma_tx_enable(dev);
|
||||
if (IS_ENABLED(CONFIG_UART_STM32U5_ERRATA_DMAT_LOWPOWER)) {
|
||||
/**
|
||||
* Send first character using polling.
|
||||
* The DMA TX needs to be enabled before the UART transmits
|
||||
* the character and triggers transfer complete event.
|
||||
*/
|
||||
key = irq_lock();
|
||||
|
||||
if (char_size > 1) {
|
||||
LL_USART_TransmitData9(usart, *(const uint16_t *)tx_data);
|
||||
} else {
|
||||
LL_USART_TransmitData8(usart, *tx_data);
|
||||
}
|
||||
|
||||
if (data->dma_tx.buffer_length > char_size) {
|
||||
/* Enable TX DMA requests */
|
||||
uart_stm32_dma_tx_enable(dev);
|
||||
}
|
||||
|
||||
irq_unlock(key);
|
||||
} else {
|
||||
/* Enable TX DMA requests */
|
||||
uart_stm32_dma_tx_enable(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue