2016-03-03 15:33:20 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Open-RnD Sp. z o.o.
|
2016-11-14 11:53:52 +01:00
|
|
|
* Copyright (c) 2016 Linaro Limited.
|
2016-03-03 15:33:20 +01:00
|
|
|
*
|
2017-01-19 02:01:01 +01:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-03-03 15:33:20 +01:00
|
|
|
*/
|
|
|
|
|
2020-03-24 20:28:48 +01:00
|
|
|
#define DT_DRV_COMPAT st_stm32_uart
|
|
|
|
|
2016-03-03 15:33:20 +01:00
|
|
|
/**
|
2018-03-20 18:44:45 +01:00
|
|
|
* @brief Driver for UART port on STM32 family processor.
|
2019-03-15 19:51:09 +01:00
|
|
|
* @note LPUART and U(S)ART have the same base and
|
|
|
|
* majority of operations are performed the same way.
|
|
|
|
* Please validate for newly added series.
|
2016-03-03 15:33:20 +01:00
|
|
|
*/
|
|
|
|
|
2016-12-04 21:59:37 +01:00
|
|
|
#include <kernel.h>
|
2016-03-03 15:33:20 +01:00
|
|
|
#include <arch/cpu.h>
|
2019-06-26 16:33:39 +02:00
|
|
|
#include <sys/__assert.h>
|
2018-10-31 18:44:45 +01:00
|
|
|
#include <soc.h>
|
2016-03-03 15:33:20 +01:00
|
|
|
#include <init.h>
|
2019-06-25 21:54:01 +02:00
|
|
|
#include <drivers/uart.h>
|
2020-06-05 10:57:52 +02:00
|
|
|
#include <drivers/pinmux.h>
|
2021-06-10 15:55:31 +02:00
|
|
|
#include <pinmux/pinmux_stm32.h>
|
2019-06-25 21:53:47 +02:00
|
|
|
#include <drivers/clock_control.h>
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
#include <pm/pm.h>
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
#ifdef CONFIG_UART_ASYNC_API
|
2021-06-18 15:52:30 +02:00
|
|
|
#include <drivers/dma/dma_stm32.h>
|
2021-01-16 17:44:24 +01:00
|
|
|
#include <drivers/dma.h>
|
|
|
|
#endif
|
|
|
|
|
2017-06-17 17:30:47 +02:00
|
|
|
#include <linker/sections.h>
|
2020-01-25 12:34:53 +01:00
|
|
|
#include <drivers/clock_control/stm32_clock_control.h>
|
2016-03-03 15:33:20 +01:00
|
|
|
#include "uart_stm32.h"
|
|
|
|
|
2020-10-03 23:58:36 +02:00
|
|
|
#include <stm32_ll_usart.h>
|
|
|
|
#include <stm32_ll_lpuart.h>
|
|
|
|
|
2019-11-12 16:13:03 +01:00
|
|
|
#include <logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(uart_stm32);
|
|
|
|
|
2020-05-07 19:22:26 +02:00
|
|
|
#define HAS_LPUART_1 (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpuart1), \
|
|
|
|
st_stm32_lpuart, okay))
|
2020-04-17 12:33:29 +02:00
|
|
|
|
2016-03-03 15:33:20 +01:00
|
|
|
/* convenience defines */
|
|
|
|
#define DEV_CFG(dev) \
|
2021-01-16 17:44:24 +01:00
|
|
|
((const struct uart_stm32_config *const)(dev)->config)
|
2016-03-03 15:33:20 +01:00
|
|
|
#define DEV_DATA(dev) \
|
2021-01-16 17:44:24 +01:00
|
|
|
((struct uart_stm32_data *const)(dev)->data)
|
2016-03-03 15:33:20 +01:00
|
|
|
#define UART_STRUCT(dev) \
|
2016-11-14 11:53:52 +01:00
|
|
|
((USART_TypeDef *)(DEV_CFG(dev))->uconf.base)
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2016-11-14 11:53:52 +01:00
|
|
|
#define TIMEOUT 1000
|
2016-03-03 15:33:20 +01:00
|
|
|
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
static void uart_stm32_pm_constraint_set(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
if (!data->pm_constraint_on) {
|
|
|
|
data->pm_constraint_on = true;
|
|
|
|
pm_constraint_set(PM_STATE_SUSPEND_TO_IDLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uart_stm32_pm_constraint_release(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
if (data->pm_constraint_on) {
|
|
|
|
data->pm_constraint_on = false;
|
|
|
|
pm_constraint_release(PM_STATE_SUSPEND_TO_IDLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline void uart_stm32_set_baudrate(const struct device *dev,
|
|
|
|
uint32_t baud_rate)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
const struct uart_stm32_config *config = DEV_CFG(dev);
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2019-05-20 17:15:02 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t clock_rate;
|
2019-01-07 22:52:24 +01:00
|
|
|
|
|
|
|
/* Get clock rate */
|
2019-11-12 16:13:03 +01:00
|
|
|
if (clock_control_get_rate(data->clock,
|
2019-01-07 22:52:24 +01:00
|
|
|
(clock_control_subsys_t *)&config->pclken,
|
2019-11-12 16:13:03 +01:00
|
|
|
&clock_rate) < 0) {
|
|
|
|
LOG_ERR("Failed call clock_control_get_rate");
|
|
|
|
return;
|
|
|
|
}
|
2019-05-20 17:15:02 +02:00
|
|
|
|
|
|
|
|
2020-04-17 12:33:29 +02:00
|
|
|
#if HAS_LPUART_1
|
2019-01-07 22:52:24 +01:00
|
|
|
if (IS_LPUART_INSTANCE(UartInstance)) {
|
2019-05-20 17:15:02 +02:00
|
|
|
LL_LPUART_SetBaudRate(UartInstance,
|
|
|
|
clock_rate,
|
|
|
|
#ifdef USART_PRESC_PRESCALER
|
|
|
|
LL_USART_PRESCALER_DIV1,
|
|
|
|
#endif
|
|
|
|
baud_rate);
|
2019-01-07 22:52:24 +01:00
|
|
|
} else {
|
2020-04-17 12:33:29 +02:00
|
|
|
#endif /* HAS_LPUART_1 */
|
2021-01-08 10:54:46 +01:00
|
|
|
#ifdef USART_CR1_OVER8
|
|
|
|
LL_USART_SetOverSampling(UartInstance,
|
|
|
|
LL_USART_OVERSAMPLING_16);
|
|
|
|
#endif
|
2019-05-20 17:15:02 +02:00
|
|
|
LL_USART_SetBaudRate(UartInstance,
|
|
|
|
clock_rate,
|
|
|
|
#ifdef USART_PRESC_PRESCALER
|
|
|
|
LL_USART_PRESCALER_DIV1,
|
|
|
|
#endif
|
|
|
|
#ifdef USART_CR1_OVER8
|
|
|
|
LL_USART_OVERSAMPLING_16,
|
2019-01-07 22:52:24 +01:00
|
|
|
#endif
|
2019-05-20 17:15:02 +02:00
|
|
|
baud_rate);
|
|
|
|
|
2020-04-17 12:33:29 +02:00
|
|
|
#if HAS_LPUART_1
|
2019-05-20 17:15:02 +02:00
|
|
|
}
|
2020-04-17 12:33:29 +02:00
|
|
|
#endif /* HAS_LPUART_1 */
|
2019-01-07 22:52:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline void uart_stm32_set_parity(const struct device *dev,
|
|
|
|
uint32_t parity)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
LL_USART_SetParity(UartInstance, parity);
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline uint32_t uart_stm32_get_parity(const struct device *dev)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
return LL_USART_GetParity(UartInstance);
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline void uart_stm32_set_stopbits(const struct device *dev,
|
|
|
|
uint32_t stopbits)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
LL_USART_SetStopBitsLength(UartInstance, stopbits);
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline uint32_t uart_stm32_get_stopbits(const struct device *dev)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
return LL_USART_GetStopBitsLength(UartInstance);
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline void uart_stm32_set_databits(const struct device *dev,
|
|
|
|
uint32_t databits)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
LL_USART_SetDataWidth(UartInstance, databits);
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline uint32_t uart_stm32_get_databits(const struct device *dev)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
return LL_USART_GetDataWidth(UartInstance);
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline void uart_stm32_set_hwctrl(const struct device *dev,
|
|
|
|
uint32_t hwctrl)
|
2019-03-15 19:51:09 +01:00
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
LL_USART_SetHWFlowCtrl(UartInstance, hwctrl);
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline uint32_t uart_stm32_get_hwctrl(const struct device *dev)
|
2019-03-15 19:51:09 +01:00
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
return LL_USART_GetHWFlowCtrl(UartInstance);
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline uint32_t uart_stm32_cfg2ll_parity(enum uart_config_parity parity)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
switch (parity) {
|
|
|
|
case UART_CFG_PARITY_ODD:
|
|
|
|
return LL_USART_PARITY_ODD;
|
|
|
|
case UART_CFG_PARITY_EVEN:
|
|
|
|
return LL_USART_PARITY_EVEN;
|
|
|
|
case UART_CFG_PARITY_NONE:
|
|
|
|
default:
|
|
|
|
return LL_USART_PARITY_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline enum uart_config_parity uart_stm32_ll2cfg_parity(uint32_t parity)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
switch (parity) {
|
|
|
|
case LL_USART_PARITY_ODD:
|
|
|
|
return UART_CFG_PARITY_ODD;
|
|
|
|
case LL_USART_PARITY_EVEN:
|
|
|
|
return UART_CFG_PARITY_EVEN;
|
|
|
|
case LL_USART_PARITY_NONE:
|
|
|
|
default:
|
|
|
|
return UART_CFG_PARITY_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline uint32_t uart_stm32_cfg2ll_stopbits(enum uart_config_stop_bits sb)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
switch (sb) {
|
|
|
|
/* Some MCU's don't support 0.5 stop bits */
|
|
|
|
#ifdef LL_USART_STOPBITS_0_5
|
|
|
|
case UART_CFG_STOP_BITS_0_5:
|
|
|
|
return LL_USART_STOPBITS_0_5;
|
|
|
|
#endif /* LL_USART_STOPBITS_0_5 */
|
|
|
|
case UART_CFG_STOP_BITS_1:
|
|
|
|
return LL_USART_STOPBITS_1;
|
|
|
|
/* Some MCU's don't support 1.5 stop bits */
|
|
|
|
#ifdef LL_USART_STOPBITS_1_5
|
|
|
|
case UART_CFG_STOP_BITS_1_5:
|
|
|
|
return LL_USART_STOPBITS_1_5;
|
|
|
|
#endif /* LL_USART_STOPBITS_1_5 */
|
|
|
|
case UART_CFG_STOP_BITS_2:
|
|
|
|
default:
|
|
|
|
return LL_USART_STOPBITS_2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline enum uart_config_stop_bits uart_stm32_ll2cfg_stopbits(uint32_t sb)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
switch (sb) {
|
|
|
|
/* Some MCU's don't support 0.5 stop bits */
|
|
|
|
#ifdef LL_USART_STOPBITS_0_5
|
|
|
|
case LL_USART_STOPBITS_0_5:
|
|
|
|
return UART_CFG_STOP_BITS_0_5;
|
|
|
|
#endif /* LL_USART_STOPBITS_0_5 */
|
|
|
|
case LL_USART_STOPBITS_1:
|
|
|
|
return UART_CFG_STOP_BITS_1;
|
|
|
|
/* Some MCU's don't support 1.5 stop bits */
|
|
|
|
#ifdef LL_USART_STOPBITS_1_5
|
|
|
|
case LL_USART_STOPBITS_1_5:
|
|
|
|
return UART_CFG_STOP_BITS_1_5;
|
|
|
|
#endif /* LL_USART_STOPBITS_1_5 */
|
|
|
|
case LL_USART_STOPBITS_2:
|
|
|
|
default:
|
|
|
|
return UART_CFG_STOP_BITS_2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 13:23:00 +01:00
|
|
|
static inline uint32_t uart_stm32_cfg2ll_databits(enum uart_config_data_bits db,
|
|
|
|
enum uart_config_parity p)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
switch (db) {
|
2019-03-22 14:19:57 +01:00
|
|
|
/* Some MCU's don't support 7B or 9B datawidth */
|
2019-01-07 22:52:24 +01:00
|
|
|
#ifdef LL_USART_DATAWIDTH_7B
|
|
|
|
case UART_CFG_DATA_BITS_7:
|
2021-03-10 13:23:00 +01:00
|
|
|
if (p == UART_CFG_PARITY_NONE) {
|
|
|
|
return LL_USART_DATAWIDTH_7B;
|
|
|
|
} else {
|
|
|
|
return LL_USART_DATAWIDTH_8B;
|
|
|
|
}
|
2019-01-07 22:52:24 +01:00
|
|
|
#endif /* LL_USART_DATAWIDTH_7B */
|
2019-03-22 14:19:57 +01:00
|
|
|
#ifdef LL_USART_DATAWIDTH_9B
|
|
|
|
case UART_CFG_DATA_BITS_9:
|
|
|
|
return LL_USART_DATAWIDTH_9B;
|
|
|
|
#endif /* LL_USART_DATAWIDTH_9B */
|
2019-01-07 22:52:24 +01:00
|
|
|
case UART_CFG_DATA_BITS_8:
|
|
|
|
default:
|
2021-03-10 13:23:00 +01:00
|
|
|
if (p == UART_CFG_PARITY_NONE) {
|
|
|
|
return LL_USART_DATAWIDTH_8B;
|
|
|
|
#ifdef LL_USART_DATAWIDTH_9B
|
|
|
|
} else {
|
|
|
|
return LL_USART_DATAWIDTH_9B;
|
|
|
|
#endif
|
|
|
|
}
|
2019-01-07 22:52:24 +01:00
|
|
|
return LL_USART_DATAWIDTH_8B;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 13:23:00 +01:00
|
|
|
static inline enum uart_config_data_bits uart_stm32_ll2cfg_databits(uint32_t db,
|
|
|
|
uint32_t p)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
switch (db) {
|
2019-03-22 14:19:57 +01:00
|
|
|
/* Some MCU's don't support 7B or 9B datawidth */
|
2019-01-07 22:52:24 +01:00
|
|
|
#ifdef LL_USART_DATAWIDTH_7B
|
|
|
|
case LL_USART_DATAWIDTH_7B:
|
2021-03-10 13:23:00 +01:00
|
|
|
if (p == LL_USART_PARITY_NONE) {
|
|
|
|
return UART_CFG_DATA_BITS_7;
|
|
|
|
} else {
|
|
|
|
return UART_CFG_DATA_BITS_6;
|
|
|
|
}
|
2019-01-07 22:52:24 +01:00
|
|
|
#endif /* LL_USART_DATAWIDTH_7B */
|
2019-03-22 14:19:57 +01:00
|
|
|
#ifdef LL_USART_DATAWIDTH_9B
|
|
|
|
case LL_USART_DATAWIDTH_9B:
|
2021-03-10 13:23:00 +01:00
|
|
|
if (p == LL_USART_PARITY_NONE) {
|
|
|
|
return UART_CFG_DATA_BITS_9;
|
|
|
|
} else {
|
|
|
|
return UART_CFG_DATA_BITS_8;
|
|
|
|
}
|
2019-03-22 14:19:57 +01:00
|
|
|
#endif /* LL_USART_DATAWIDTH_9B */
|
2019-01-07 22:52:24 +01:00
|
|
|
case LL_USART_DATAWIDTH_8B:
|
|
|
|
default:
|
2021-03-10 13:23:00 +01:00
|
|
|
if (p == LL_USART_PARITY_NONE) {
|
|
|
|
return UART_CFG_DATA_BITS_8;
|
|
|
|
} else {
|
|
|
|
return UART_CFG_DATA_BITS_7;
|
|
|
|
}
|
2019-01-07 22:52:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-15 19:51:09 +01:00
|
|
|
/**
|
|
|
|
* @brief Get LL hardware flow control define from
|
|
|
|
* Zephyr hardware flow control option.
|
|
|
|
* @note Supports only UART_CFG_FLOW_CTRL_RTS_CTS.
|
|
|
|
* @param fc: Zephyr hardware flow control option.
|
|
|
|
* @retval LL_USART_HWCONTROL_RTS_CTS, or LL_USART_HWCONTROL_NONE.
|
|
|
|
*/
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline uint32_t uart_stm32_cfg2ll_hwctrl(enum uart_config_flow_control fc)
|
2019-03-15 19:51:09 +01:00
|
|
|
{
|
|
|
|
if (fc == UART_CFG_FLOW_CTRL_RTS_CTS) {
|
|
|
|
return LL_USART_HWCONTROL_RTS_CTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LL_USART_HWCONTROL_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-02-06 19:36:03 +01:00
|
|
|
* @brief Get Zephyr hardware flow control option from
|
2019-03-15 19:51:09 +01:00
|
|
|
* LL hardware flow control define.
|
|
|
|
* @note Supports only LL_USART_HWCONTROL_RTS_CTS.
|
2020-02-06 19:36:03 +01:00
|
|
|
* @param fc: LL hardware flow control definition.
|
2020-02-06 18:30:05 +01:00
|
|
|
* @retval UART_CFG_FLOW_CTRL_RTS_CTS, or UART_CFG_FLOW_CTRL_NONE.
|
2019-03-15 19:51:09 +01:00
|
|
|
*/
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline enum uart_config_flow_control uart_stm32_ll2cfg_hwctrl(uint32_t fc)
|
2019-03-15 19:51:09 +01:00
|
|
|
{
|
|
|
|
if (fc == LL_USART_HWCONTROL_RTS_CTS) {
|
|
|
|
return UART_CFG_FLOW_CTRL_RTS_CTS;
|
|
|
|
}
|
|
|
|
|
2020-02-06 18:30:05 +01:00
|
|
|
return UART_CFG_FLOW_CTRL_NONE;
|
2019-03-15 19:51:09 +01:00
|
|
|
}
|
|
|
|
|
2021-05-26 21:33:37 +02:00
|
|
|
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_configure(const struct device *dev,
|
2019-01-07 22:52:24 +01:00
|
|
|
const struct uart_config *cfg)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2020-05-27 18:26:57 +02:00
|
|
|
const uint32_t parity = uart_stm32_cfg2ll_parity(cfg->parity);
|
|
|
|
const uint32_t stopbits = uart_stm32_cfg2ll_stopbits(cfg->stop_bits);
|
2021-03-10 13:23:00 +01:00
|
|
|
const uint32_t databits = uart_stm32_cfg2ll_databits(cfg->data_bits,
|
|
|
|
cfg->parity);
|
2020-05-27 18:26:57 +02:00
|
|
|
const uint32_t flowctrl = uart_stm32_cfg2ll_hwctrl(cfg->flow_ctrl);
|
2019-01-07 22:52:24 +01:00
|
|
|
|
|
|
|
/* Hardware doesn't support mark or space parity */
|
2021-02-11 09:55:38 +01:00
|
|
|
if ((cfg->parity == UART_CFG_PARITY_MARK) ||
|
|
|
|
(cfg->parity == UART_CFG_PARITY_SPACE)) {
|
2019-01-07 22:52:24 +01:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2021-03-10 13:23:00 +01:00
|
|
|
/* Driver does not supports parity + 9 databits */
|
|
|
|
if ((cfg->parity != UART_CFG_PARITY_NONE) &&
|
|
|
|
(cfg->data_bits == UART_CFG_DATA_BITS_9)) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2020-04-17 12:33:29 +02:00
|
|
|
#if defined(LL_USART_STOPBITS_0_5) && HAS_LPUART_1
|
2019-01-07 22:52:24 +01:00
|
|
|
if (IS_LPUART_INSTANCE(UartInstance) &&
|
2021-02-11 09:55:38 +01:00
|
|
|
(cfg->stop_bits == UART_CFG_STOP_BITS_0_5)) {
|
2019-01-07 22:52:24 +01:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
#else
|
2021-02-11 09:55:38 +01:00
|
|
|
if (cfg->stop_bits == UART_CFG_STOP_BITS_0_5) {
|
2019-01-07 22:52:24 +01:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-04-17 12:33:29 +02:00
|
|
|
#if defined(LL_USART_STOPBITS_1_5) && HAS_LPUART_1
|
2019-01-07 22:52:24 +01:00
|
|
|
if (IS_LPUART_INSTANCE(UartInstance) &&
|
2021-02-11 09:55:38 +01:00
|
|
|
(cfg->stop_bits == UART_CFG_STOP_BITS_1_5)) {
|
2019-01-07 22:52:24 +01:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
#else
|
2021-02-11 09:55:38 +01:00
|
|
|
if (cfg->stop_bits == UART_CFG_STOP_BITS_1_5) {
|
2019-01-07 22:52:24 +01:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-03-22 14:19:57 +01:00
|
|
|
/* Driver doesn't support 5 or 6 databits and potentially 7 or 9 */
|
2021-02-11 09:55:38 +01:00
|
|
|
if ((cfg->data_bits == UART_CFG_DATA_BITS_5) ||
|
|
|
|
(cfg->data_bits == UART_CFG_DATA_BITS_6)
|
2019-01-07 22:52:24 +01:00
|
|
|
#ifndef LL_USART_DATAWIDTH_7B
|
2021-02-11 09:55:38 +01:00
|
|
|
|| (cfg->data_bits == UART_CFG_DATA_BITS_7)
|
2019-01-07 22:52:24 +01:00
|
|
|
#endif /* LL_USART_DATAWIDTH_7B */
|
2021-02-11 09:55:38 +01:00
|
|
|
|| (cfg->data_bits == UART_CFG_DATA_BITS_9)) {
|
2019-01-07 22:52:24 +01:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2019-03-15 19:51:09 +01:00
|
|
|
/* Driver supports only RTS CTS flow control */
|
2021-02-11 09:55:38 +01:00
|
|
|
if (cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) {
|
2019-03-15 19:51:09 +01:00
|
|
|
if (!IS_UART_HWFLOW_INSTANCE(UartInstance) ||
|
|
|
|
UART_CFG_FLOW_CTRL_RTS_CTS != cfg->flow_ctrl) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
2019-01-07 22:52:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
LL_USART_Disable(UartInstance);
|
|
|
|
|
|
|
|
if (parity != uart_stm32_get_parity(dev)) {
|
|
|
|
uart_stm32_set_parity(dev, parity);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stopbits != uart_stm32_get_stopbits(dev)) {
|
|
|
|
uart_stm32_set_stopbits(dev, stopbits);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (databits != uart_stm32_get_databits(dev)) {
|
|
|
|
uart_stm32_set_databits(dev, databits);
|
|
|
|
}
|
|
|
|
|
2019-03-15 19:51:09 +01:00
|
|
|
if (flowctrl != uart_stm32_get_hwctrl(dev)) {
|
|
|
|
uart_stm32_set_hwctrl(dev, flowctrl);
|
|
|
|
}
|
|
|
|
|
2019-01-07 22:52:24 +01:00
|
|
|
if (cfg->baudrate != data->baud_rate) {
|
|
|
|
uart_stm32_set_baudrate(dev, cfg->baudrate);
|
|
|
|
data->baud_rate = cfg->baudrate;
|
|
|
|
}
|
|
|
|
|
|
|
|
LL_USART_Enable(UartInstance);
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_config_get(const struct device *dev,
|
|
|
|
struct uart_config *cfg)
|
2019-01-07 22:52:24 +01:00
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
cfg->baudrate = data->baud_rate;
|
|
|
|
cfg->parity = uart_stm32_ll2cfg_parity(uart_stm32_get_parity(dev));
|
|
|
|
cfg->stop_bits = uart_stm32_ll2cfg_stopbits(
|
|
|
|
uart_stm32_get_stopbits(dev));
|
|
|
|
cfg->data_bits = uart_stm32_ll2cfg_databits(
|
2021-03-10 13:23:00 +01:00
|
|
|
uart_stm32_get_databits(dev), uart_stm32_get_parity(dev));
|
2019-03-15 19:51:09 +01:00
|
|
|
cfg->flow_ctrl = uart_stm32_ll2cfg_hwctrl(
|
|
|
|
uart_stm32_get_hwctrl(dev));
|
2019-01-07 22:52:24 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2021-05-26 21:33:37 +02:00
|
|
|
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
|
2019-01-07 22:52:24 +01:00
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_poll_in(const struct device *dev, unsigned char *c)
|
2016-03-03 15:33:20 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2018-11-02 14:34:56 +01:00
|
|
|
/* Clear overrun error flag */
|
|
|
|
if (LL_USART_IsActiveFlag_ORE(UartInstance)) {
|
|
|
|
LL_USART_ClearFlag_ORE(UartInstance);
|
|
|
|
}
|
|
|
|
|
2017-09-21 15:20:53 +02:00
|
|
|
if (!LL_USART_IsActiveFlag_RXNE(UartInstance)) {
|
2016-03-03 15:33:20 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2017-09-21 15:20:53 +02:00
|
|
|
|
|
|
|
*c = (unsigned char)LL_USART_ReceiveData8(UartInstance);
|
|
|
|
|
|
|
|
return 0;
|
2016-03-03 15:33:20 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void uart_stm32_poll_out(const struct device *dev,
|
2016-03-03 15:33:20 +01:00
|
|
|
unsigned char c)
|
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2017-09-21 15:20:53 +02:00
|
|
|
/* Wait for TXE flag to be raised */
|
2019-06-04 16:52:23 +02:00
|
|
|
while (!LL_USART_IsActiveFlag_TXE(UartInstance)) {
|
|
|
|
}
|
2017-09-21 15:20:53 +02:00
|
|
|
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
if (!data->tx_poll_stream_on) {
|
|
|
|
data->tx_poll_stream_on = true;
|
|
|
|
|
|
|
|
/* Don't allow system to suspend until stream
|
|
|
|
* transmission has completed
|
|
|
|
*/
|
|
|
|
uart_stm32_pm_constraint_set(dev);
|
|
|
|
|
|
|
|
/* Enable TC interrupt so we can release suspend
|
|
|
|
* constraint when done
|
|
|
|
*/
|
|
|
|
LL_USART_EnableIT_TC(UartInstance);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PM */
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
LL_USART_TransmitData8(UartInstance, (uint8_t)c);
|
2016-03-03 15:33:20 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_err_check(const struct device *dev)
|
2019-02-14 10:50:19 +01:00
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t err = 0U;
|
2019-02-14 10:50:19 +01:00
|
|
|
|
|
|
|
/* Check for errors, but don't clear them here.
|
|
|
|
* Some SoC clear all error flags when at least
|
|
|
|
* one is cleared. (e.g. F4X, F1X, and F2X)
|
|
|
|
*/
|
|
|
|
if (LL_USART_IsActiveFlag_ORE(UartInstance)) {
|
|
|
|
err |= UART_ERROR_OVERRUN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (LL_USART_IsActiveFlag_PE(UartInstance)) {
|
|
|
|
err |= UART_ERROR_PARITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (LL_USART_IsActiveFlag_FE(UartInstance)) {
|
|
|
|
err |= UART_ERROR_FRAMING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err & UART_ERROR_OVERRUN) {
|
|
|
|
LL_USART_ClearFlag_ORE(UartInstance);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err & UART_ERROR_PARITY) {
|
|
|
|
LL_USART_ClearFlag_PE(UartInstance);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err & UART_ERROR_FRAMING) {
|
|
|
|
LL_USART_ClearFlag_FE(UartInstance);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear noise error as well,
|
|
|
|
* it is not represented by the errors enum
|
|
|
|
*/
|
|
|
|
LL_USART_ClearFlag_NE(UartInstance);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static inline void __uart_stm32_get_clock(const struct device *dev)
|
2016-03-03 15:33:20 +01:00
|
|
|
{
|
2016-11-14 11:53:52 +01:00
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
2021-02-11 18:49:24 +01:00
|
|
|
const struct device *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2016-11-14 11:53:52 +01:00
|
|
|
data->clock = clk;
|
2016-03-03 15:33:20 +01:00
|
|
|
}
|
|
|
|
|
2016-03-13 19:37:25 +01:00
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_fifo_fill(const struct device *dev,
|
|
|
|
const uint8_t *tx_data,
|
2016-03-13 19:37:25 +01:00
|
|
|
int size)
|
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t num_tx = 0U;
|
2016-11-14 11:53:52 +01:00
|
|
|
|
2017-09-21 15:20:53 +02:00
|
|
|
while ((size - num_tx > 0) &&
|
|
|
|
LL_USART_IsActiveFlag_TXE(UartInstance)) {
|
2019-02-06 23:31:24 +01:00
|
|
|
/* TXE flag will be cleared with byte write to DR|RDR register */
|
2016-11-14 11:53:52 +01:00
|
|
|
|
|
|
|
/* Send a character (8bit , parity none) */
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_TransmitData8(UartInstance, tx_data[num_tx++]);
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return num_tx;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_fifo_read(const struct device *dev, uint8_t *rx_data,
|
2016-03-13 19:37:25 +01:00
|
|
|
const int size)
|
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t num_rx = 0U;
|
2016-11-14 11:53:52 +01:00
|
|
|
|
2017-09-21 15:20:53 +02:00
|
|
|
while ((size - num_rx > 0) &&
|
|
|
|
LL_USART_IsActiveFlag_RXNE(UartInstance)) {
|
2019-02-06 23:31:24 +01:00
|
|
|
/* RXNE flag will be cleared upon read from DR|RDR register */
|
2016-11-14 11:53:52 +01:00
|
|
|
|
|
|
|
/* Receive a character (8bit , parity none) */
|
2017-09-21 15:20:53 +02:00
|
|
|
rx_data[num_rx++] = LL_USART_ReceiveData8(UartInstance);
|
2018-11-02 14:34:56 +01:00
|
|
|
|
|
|
|
/* Clear overrun error flag */
|
|
|
|
if (LL_USART_IsActiveFlag_ORE(UartInstance)) {
|
|
|
|
LL_USART_ClearFlag_ORE(UartInstance);
|
|
|
|
}
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
2019-02-06 23:31:24 +01:00
|
|
|
|
2016-03-13 19:37:25 +01:00
|
|
|
return num_rx;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void uart_stm32_irq_tx_enable(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-13 19:37:25 +01:00
|
|
|
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#ifdef CONFIG_PM
|
2021-10-26 18:03:31 +02:00
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
data->tx_poll_stream_on = false;
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
uart_stm32_pm_constraint_set(dev);
|
|
|
|
#endif
|
2021-10-26 18:03:31 +02:00
|
|
|
LL_USART_EnableIT_TC(UartInstance);
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void uart_stm32_irq_tx_disable(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-13 19:37:25 +01:00
|
|
|
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_DisableIT_TC(UartInstance);
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
uart_stm32_pm_constraint_release(dev);
|
|
|
|
#endif
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_irq_tx_ready(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-13 19:37:25 +01:00
|
|
|
|
2021-01-08 15:59:53 +01:00
|
|
|
return LL_USART_IsActiveFlag_TXE(UartInstance) &&
|
|
|
|
LL_USART_IsEnabledIT_TC(UartInstance);
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_irq_tx_complete(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-13 19:37:25 +01:00
|
|
|
|
2019-09-25 19:57:35 +02:00
|
|
|
return LL_USART_IsActiveFlag_TC(UartInstance);
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void uart_stm32_irq_rx_enable(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-13 19:37:25 +01:00
|
|
|
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_EnableIT_RXNE(UartInstance);
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void uart_stm32_irq_rx_disable(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-13 19:37:25 +01:00
|
|
|
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_DisableIT_RXNE(UartInstance);
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_irq_rx_ready(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-13 19:37:25 +01:00
|
|
|
|
2021-01-15 14:10:40 +01:00
|
|
|
return LL_USART_IsActiveFlag_RXNE(UartInstance);
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void uart_stm32_irq_err_enable(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-11-14 11:53:52 +01:00
|
|
|
|
|
|
|
/* Enable FE, ORE interruptions */
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_EnableIT_ERROR(UartInstance);
|
2018-03-29 18:40:00 +02:00
|
|
|
#if !defined(CONFIG_SOC_SERIES_STM32F0X) || defined(USART_LIN_SUPPORT)
|
2016-11-14 11:53:52 +01:00
|
|
|
/* Enable Line break detection */
|
2018-03-29 18:40:00 +02:00
|
|
|
if (IS_UART_LIN_INSTANCE(UartInstance)) {
|
|
|
|
LL_USART_EnableIT_LBD(UartInstance);
|
|
|
|
}
|
2017-08-09 11:23:04 +02:00
|
|
|
#endif
|
2016-11-14 11:53:52 +01:00
|
|
|
/* Enable parity error interruption */
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_EnableIT_PE(UartInstance);
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void uart_stm32_irq_err_disable(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
2018-03-29 18:40:00 +02:00
|
|
|
/* Disable FE, ORE interruptions */
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_DisableIT_ERROR(UartInstance);
|
2018-03-29 18:40:00 +02:00
|
|
|
#if !defined(CONFIG_SOC_SERIES_STM32F0X) || defined(USART_LIN_SUPPORT)
|
|
|
|
/* Disable Line break detection */
|
|
|
|
if (IS_UART_LIN_INSTANCE(UartInstance)) {
|
|
|
|
LL_USART_DisableIT_LBD(UartInstance);
|
|
|
|
}
|
2017-08-09 11:23:04 +02:00
|
|
|
#endif
|
2018-03-29 18:40:00 +02:00
|
|
|
/* Disable parity error interruption */
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_DisableIT_PE(UartInstance);
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_irq_is_pending(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2016-03-13 19:37:25 +01:00
|
|
|
|
2017-12-05 20:46:13 +01:00
|
|
|
return ((LL_USART_IsActiveFlag_RXNE(UartInstance) &&
|
|
|
|
LL_USART_IsEnabledIT_RXNE(UartInstance)) ||
|
2018-11-13 15:26:06 +01:00
|
|
|
(LL_USART_IsActiveFlag_TC(UartInstance) &&
|
|
|
|
LL_USART_IsEnabledIT_TC(UartInstance)));
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_irq_update(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void uart_stm32_irq_callback_set(const struct device *dev,
|
2018-07-16 20:12:26 +02:00
|
|
|
uart_irq_callback_user_data_t cb,
|
|
|
|
void *cb_data)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
data->user_cb = cb;
|
2018-07-16 20:12:26 +02:00
|
|
|
data->user_data = cb_data;
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
|
|
|
|
#ifdef CONFIG_UART_ASYNC_API
|
|
|
|
|
|
|
|
static inline void async_user_callback(struct uart_stm32_data *data,
|
|
|
|
struct uart_event *event)
|
|
|
|
{
|
|
|
|
if (data->async_cb) {
|
|
|
|
data->async_cb(data->uart_dev, event, data->async_user_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void async_evt_rx_rdy(struct uart_stm32_data *data)
|
|
|
|
{
|
|
|
|
LOG_DBG("rx_rdy: (%d %d)", data->dma_rx.offset, data->dma_rx.counter);
|
|
|
|
|
|
|
|
struct uart_event event = {
|
|
|
|
.type = UART_RX_RDY,
|
|
|
|
.data.rx.buf = data->dma_rx.buffer,
|
|
|
|
.data.rx.len = data->dma_rx.counter - data->dma_rx.offset,
|
|
|
|
.data.rx.offset = data->dma_rx.offset
|
|
|
|
};
|
|
|
|
|
2021-02-04 10:00:49 +01:00
|
|
|
/* update the current pos for new data */
|
|
|
|
data->dma_rx.offset = data->dma_rx.counter;
|
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
/* send event only for new data */
|
|
|
|
if (event.data.rx.len > 0) {
|
|
|
|
async_user_callback(data, &event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void async_evt_rx_err(struct uart_stm32_data *data, int err_code)
|
|
|
|
{
|
|
|
|
LOG_DBG("rx error: %d", err_code);
|
|
|
|
|
|
|
|
struct uart_event event = {
|
|
|
|
.type = UART_RX_STOPPED,
|
|
|
|
.data.rx_stop.reason = err_code,
|
|
|
|
.data.rx_stop.data.len = data->dma_rx.counter,
|
|
|
|
.data.rx_stop.data.offset = 0,
|
|
|
|
.data.rx_stop.data.buf = data->dma_rx.buffer
|
|
|
|
};
|
|
|
|
|
|
|
|
async_user_callback(data, &event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void async_evt_tx_done(struct uart_stm32_data *data)
|
|
|
|
{
|
|
|
|
LOG_DBG("tx done: %d", data->dma_tx.counter);
|
|
|
|
|
|
|
|
struct uart_event event = {
|
|
|
|
.type = UART_TX_DONE,
|
|
|
|
.data.tx.buf = data->dma_tx.buffer,
|
|
|
|
.data.tx.len = data->dma_tx.counter
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Reset tx buffer */
|
|
|
|
data->dma_tx.buffer_length = 0;
|
|
|
|
data->dma_tx.counter = 0;
|
|
|
|
|
|
|
|
async_user_callback(data, &event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void async_evt_tx_abort(struct uart_stm32_data *data)
|
|
|
|
{
|
|
|
|
LOG_DBG("tx abort: %d", data->dma_tx.counter);
|
|
|
|
|
|
|
|
struct uart_event event = {
|
|
|
|
.type = UART_TX_ABORTED,
|
|
|
|
.data.tx.buf = data->dma_tx.buffer,
|
|
|
|
.data.tx.len = data->dma_tx.counter
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Reset tx buffer */
|
|
|
|
data->dma_tx.buffer_length = 0;
|
|
|
|
data->dma_tx.counter = 0;
|
|
|
|
|
|
|
|
async_user_callback(data, &event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void async_evt_rx_buf_request(struct uart_stm32_data *data)
|
|
|
|
{
|
|
|
|
struct uart_event evt = {
|
|
|
|
.type = UART_RX_BUF_REQUEST,
|
|
|
|
};
|
|
|
|
|
|
|
|
async_user_callback(data, &evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void async_evt_rx_buf_release(struct uart_stm32_data *data)
|
|
|
|
{
|
|
|
|
struct uart_event evt = {
|
|
|
|
.type = UART_RX_BUF_RELEASED,
|
|
|
|
.data.rx_buf.buf = data->dma_rx.buffer,
|
|
|
|
};
|
|
|
|
|
|
|
|
async_user_callback(data, &evt);
|
|
|
|
}
|
|
|
|
|
2021-04-08 12:09:58 +02:00
|
|
|
static inline void async_timer_start(struct k_work_delayable *work,
|
2021-01-16 17:44:24 +01:00
|
|
|
int32_t timeout)
|
|
|
|
{
|
2021-10-01 15:47:40 +02:00
|
|
|
if ((timeout != SYS_FOREVER_US) && (timeout != 0)) {
|
2021-01-16 17:44:24 +01:00
|
|
|
/* start timer */
|
2021-10-01 15:47:40 +02:00
|
|
|
LOG_DBG("async timer started for %d us", timeout);
|
|
|
|
k_work_reschedule(work, K_USEC(timeout));
|
2021-01-16 17:44:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uart_stm32_dma_rx_flush(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct dma_status stat;
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
if (dma_get_status(data->dma_rx.dma_dev,
|
2021-01-16 17:44:24 +01:00
|
|
|
data->dma_rx.dma_channel, &stat) == 0) {
|
|
|
|
size_t rx_rcv_len = data->dma_rx.buffer_length -
|
|
|
|
stat.pending_length;
|
|
|
|
if (rx_rcv_len > data->dma_rx.offset) {
|
|
|
|
data->dma_rx.counter = rx_rcv_len;
|
|
|
|
|
|
|
|
async_evt_rx_rdy(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_UART_ASYNC_API */
|
|
|
|
|
2021-09-21 09:34:09 +02:00
|
|
|
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || \
|
|
|
|
defined(CONFIG_UART_ASYNC_API) || \
|
|
|
|
defined(CONFIG_PM)
|
2021-01-16 17:44:24 +01:00
|
|
|
|
isr: Normalize usage of device instance through ISR
The goal of this patch is to replace the 'void *' parameter by 'struct
device *' if they use such variable or just 'const void *' on all
relevant ISRs
This will avoid not-so-nice const qualifier tweaks when device instances
will be constant.
Note that only the ISR passed to IRQ_CONNECT are of interest here.
In order to do so, the script fix_isr.py below is necessary:
from pathlib import Path
import subprocess
import pickle
import mmap
import sys
import re
import os
cocci_template = """
@r_fix_isr_0
@
type ret_type;
identifier P;
identifier D;
@@
-ret_type <!fn!>(void *P)
+ret_type <!fn!>(const struct device *P)
{
...
(
const struct device *D = (const struct device *)P;
|
const struct device *D = P;
)
...
}
@r_fix_isr_1
@
type ret_type;
identifier P;
identifier D;
@@
-ret_type <!fn!>(void *P)
+ret_type <!fn!>(const struct device *P)
{
...
const struct device *D;
...
(
D = (const struct device *)P;
|
D = P;
)
...
}
@r_fix_isr_2
@
type ret_type;
identifier A;
@@
-ret_type <!fn!>(void *A)
+ret_type <!fn!>(const void *A)
{
...
}
@r_fix_isr_3
@
const struct device *D;
@@
-<!fn!>((void *)D);
+<!fn!>(D);
@r_fix_isr_4
@
type ret_type;
identifier D;
identifier P;
@@
-ret_type <!fn!>(const struct device *P)
+ret_type <!fn!>(const struct device *D)
{
...
(
-const struct device *D = (const struct device *)P;
|
-const struct device *D = P;
)
...
}
@r_fix_isr_5
@
type ret_type;
identifier D;
identifier P;
@@
-ret_type <!fn!>(const struct device *P)
+ret_type <!fn!>(const struct device *D)
{
...
-const struct device *D;
...
(
-D = (const struct device *)P;
|
-D = P;
)
...
}
"""
def find_isr(fn):
db = []
data = None
start = 0
try:
with open(fn, 'r+') as f:
data = str(mmap.mmap(f.fileno(), 0).read())
except Exception as e:
return db
while True:
isr = ""
irq = data.find('IRQ_CONNECT', start)
while irq > -1:
p = 1
arg = 1
p_o = data.find('(', irq)
if p_o < 0:
irq = -1
break;
pos = p_o + 1
while p > 0:
if data[pos] == ')':
p -= 1
elif data[pos] == '(':
p += 1
elif data[pos] == ',' and p == 1:
arg += 1
if arg == 3:
isr += data[pos]
pos += 1
isr = isr.strip(',\\n\\t ')
if isr not in db and len(isr) > 0:
db.append(isr)
start = pos
break
if irq < 0:
break
return db
def patch_isr(fn, isr_list):
if len(isr_list) <= 0:
return
for isr in isr_list:
tmplt = cocci_template.replace('<!fn!>', isr)
with open('/tmp/isr_fix.cocci', 'w') as f:
f.write(tmplt)
cmd = ['spatch', '--sp-file', '/tmp/isr_fix.cocci', '--in-place', fn]
subprocess.run(cmd)
def process_files(path):
if path.is_file() and path.suffix in ['.h', '.c']:
p = str(path.parent) + '/' + path.name
isr_list = find_isr(p)
patch_isr(p, isr_list)
elif path.is_dir():
for p in path.iterdir():
process_files(p)
if len(sys.argv) < 2:
print("You need to provide a dir/file path")
sys.exit(1)
process_files(Path(sys.argv[1]))
And is run: ./fix_isr.py <zephyr root directory>
Finally, some files needed manual fixes such.
Fixes #27399
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2020-06-17 14:58:56 +02:00
|
|
|
static void uart_stm32_isr(const struct device *dev)
|
2016-03-13 19:37:25 +01:00
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#if defined(CONFIG_PM) || defined(CONFIG_UART_ASYNC_API)
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#endif
|
2016-03-13 19:37:25 +01:00
|
|
|
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
if (LL_USART_IsEnabledIT_TC(UartInstance) &&
|
|
|
|
LL_USART_IsActiveFlag_TC(UartInstance)) {
|
|
|
|
|
|
|
|
if (data->tx_poll_stream_on) {
|
|
|
|
/* A poll stream transmition just completed,
|
|
|
|
* allow system to suspend
|
|
|
|
*/
|
|
|
|
LL_USART_DisableIT_TC(UartInstance);
|
|
|
|
data->tx_poll_stream_on = false;
|
|
|
|
uart_stm32_pm_constraint_release(dev);
|
|
|
|
}
|
|
|
|
/* Stream transmition was either async or IRQ based,
|
|
|
|
* constraint will be released at the same time TC IT
|
|
|
|
* is disabled
|
|
|
|
*/
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
}
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#endif
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
2021-05-17 22:06:51 +02:00
|
|
|
if (data->user_cb) {
|
|
|
|
data->user_cb(dev, data->user_data);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
#ifdef CONFIG_UART_ASYNC_API
|
|
|
|
if (LL_USART_IsEnabledIT_IDLE(UartInstance) &&
|
|
|
|
LL_USART_IsActiveFlag_IDLE(UartInstance)) {
|
|
|
|
|
|
|
|
LL_USART_ClearFlag_IDLE(UartInstance);
|
|
|
|
|
|
|
|
LOG_DBG("idle interrupt occurred");
|
|
|
|
|
|
|
|
/* Start the RX timer */
|
|
|
|
async_timer_start(&data->dma_rx.timeout_work,
|
|
|
|
data->dma_rx.timeout);
|
|
|
|
|
|
|
|
if (data->dma_rx.timeout == 0) {
|
|
|
|
uart_stm32_dma_rx_flush(dev);
|
|
|
|
}
|
2021-03-30 12:20:46 +02:00
|
|
|
} else if (LL_USART_IsEnabledIT_TC(UartInstance) &&
|
2021-10-26 11:39:57 +02:00
|
|
|
LL_USART_IsActiveFlag_TC(UartInstance)) {
|
2021-03-30 12:20:46 +02:00
|
|
|
|
|
|
|
LL_USART_DisableIT_TC(UartInstance);
|
|
|
|
LL_USART_ClearFlag_TC(UartInstance);
|
|
|
|
/* Generate TX_DONE event when transmission is done */
|
|
|
|
async_evt_tx_done(data);
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
uart_stm32_pm_constraint_release(dev);
|
|
|
|
#endif
|
2021-10-26 11:39:57 +02:00
|
|
|
} else if (LL_USART_IsEnabledIT_RXNE(UartInstance) &&
|
|
|
|
LL_USART_IsActiveFlag_RXNE(UartInstance)) {
|
2021-11-08 11:43:53 +01:00
|
|
|
#ifdef USART_SR_RXNE
|
|
|
|
/* clear the RXNE flag, because Rx data was not read */
|
|
|
|
LL_USART_ClearFlag_RXNE(UartInstance);
|
|
|
|
#else
|
2021-10-26 11:39:57 +02:00
|
|
|
/* clear the RXNE by flushing the fifo, because Rx data was not read */
|
|
|
|
LL_USART_RequestRxDataFlush(UartInstance);
|
2021-11-08 11:43:53 +01:00
|
|
|
#endif /* USART_SR_RXNE */
|
2021-01-16 17:44:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear errors */
|
|
|
|
uart_stm32_err_check(dev);
|
|
|
|
#endif /* CONFIG_UART_ASYNC_API */
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
2021-09-21 09:34:09 +02:00
|
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN || CONFIG_UART_ASYNC_API || CONFIG_PM */
|
2021-01-16 17:44:24 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_UART_ASYNC_API
|
|
|
|
|
|
|
|
static int uart_stm32_async_callback_set(const struct device *dev,
|
|
|
|
uart_callback_t callback,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
data->async_cb = callback;
|
|
|
|
data->async_user_data = user_data;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void uart_stm32_dma_tx_enable(const struct device *dev)
|
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
LL_USART_EnableDMAReq_TX(UartInstance);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void uart_stm32_dma_tx_disable(const struct device *dev)
|
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
LL_USART_DisableDMAReq_TX(UartInstance);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void uart_stm32_dma_rx_enable(const struct device *dev)
|
|
|
|
{
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
LL_USART_EnableDMAReq_RX(UartInstance);
|
|
|
|
|
|
|
|
data->dma_rx.enabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void uart_stm32_dma_rx_disable(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
data->dma_rx.enabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uart_stm32_async_rx_disable(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
struct uart_event disabled_event = {
|
|
|
|
.type = UART_RX_DISABLED
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!data->dma_rx.enabled) {
|
|
|
|
async_user_callback(data, &disabled_event);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
LL_USART_DisableIT_IDLE(UartInstance);
|
|
|
|
|
|
|
|
uart_stm32_dma_rx_flush(dev);
|
|
|
|
|
|
|
|
async_evt_rx_buf_release(data);
|
|
|
|
|
|
|
|
uart_stm32_dma_rx_disable(dev);
|
|
|
|
|
2021-04-08 12:09:58 +02:00
|
|
|
(void)k_work_cancel_delayable(&data->dma_rx.timeout_work);
|
2021-01-16 17:44:24 +01:00
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
dma_stop(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
|
2021-01-16 17:44:24 +01:00
|
|
|
|
|
|
|
data->rx_next_buffer = NULL;
|
|
|
|
data->rx_next_buffer_len = 0;
|
|
|
|
|
2021-10-26 11:39:57 +02:00
|
|
|
/* When async rx is disabled, enable interruptable instance of uart to function normally*/
|
2021-08-22 20:04:05 +02:00
|
|
|
LL_USART_EnableIT_RXNE(UartInstance);
|
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
LOG_DBG("rx: disabled");
|
|
|
|
|
|
|
|
async_user_callback(data, &disabled_event);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void uart_stm32_dma_tx_cb(const struct device *dma_dev, void *user_data,
|
|
|
|
uint32_t channel, int status)
|
|
|
|
{
|
|
|
|
const struct device *uart_dev = user_data;
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(uart_dev);
|
|
|
|
struct dma_status stat;
|
|
|
|
unsigned int key = irq_lock();
|
|
|
|
|
|
|
|
/* Disable TX */
|
|
|
|
uart_stm32_dma_tx_disable(uart_dev);
|
|
|
|
|
2021-04-08 12:09:58 +02:00
|
|
|
(void)k_work_cancel_delayable(&data->dma_tx.timeout_work);
|
2021-01-16 17:44:24 +01:00
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
if (!dma_get_status(data->dma_tx.dma_dev,
|
2021-01-16 17:44:24 +01:00
|
|
|
data->dma_tx.dma_channel, &stat)) {
|
|
|
|
data->dma_tx.counter = data->dma_tx.buffer_length -
|
|
|
|
stat.pending_length;
|
|
|
|
}
|
|
|
|
|
drivers: serial: stm32: Fixes uart_event_tx len calculation
Resetting the dma_tx.buffer_length after the dma_tx.counter
calculation, instead of before.
We need to reset the buffer_length when the transmission is finished,
but in order to give the correct value to the uart_event_tx struct,
we use the dma_tx.buffer_length in the calculation of the
dma_tx.counter, used for the len of the event (number of bytes sent).
I found this problem, when I wanted to use the uart_event_tx.len for
freeing the used space inside a ring buffer (ring_buf_get_finish),
and it didn't work, I logged the values of the uart_event_tx struct,
and found out it always was 0, because the buffer_length was 0,
and the whole buffer was transmitted (stat.pending_length also 0).
Signed-off-by: Prema Jonathan van Win <jonathanvanwin@gmail.com>
2021-05-03 10:24:38 +02:00
|
|
|
data->dma_tx.buffer_length = 0;
|
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
irq_unlock(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uart_stm32_dma_replace_buffer(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
/* Replace the buffer and relod the DMA */
|
|
|
|
LOG_DBG("Replacing RX buffer: %d", data->rx_next_buffer_len);
|
|
|
|
|
|
|
|
/* reload DMA */
|
|
|
|
data->dma_rx.offset = 0;
|
|
|
|
data->dma_rx.counter = 0;
|
|
|
|
data->dma_rx.buffer = data->rx_next_buffer;
|
|
|
|
data->dma_rx.buffer_length = data->rx_next_buffer_len;
|
|
|
|
data->dma_rx.blk_cfg.block_size = data->dma_rx.buffer_length;
|
|
|
|
data->dma_rx.blk_cfg.dest_address = (uint32_t)data->dma_rx.buffer;
|
|
|
|
data->rx_next_buffer = NULL;
|
|
|
|
data->rx_next_buffer_len = 0;
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
dma_reload(data->dma_rx.dma_dev, data->dma_rx.dma_channel,
|
2021-01-16 17:44:24 +01:00
|
|
|
data->dma_rx.blk_cfg.source_address,
|
|
|
|
data->dma_rx.blk_cfg.dest_address,
|
|
|
|
data->dma_rx.blk_cfg.block_size);
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
dma_start(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
|
2021-01-16 17:44:24 +01:00
|
|
|
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
LL_USART_ClearFlag_IDLE(UartInstance);
|
|
|
|
|
|
|
|
/* Request next buffer */
|
|
|
|
async_evt_rx_buf_request(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void uart_stm32_dma_rx_cb(const struct device *dma_dev, void *user_data,
|
|
|
|
uint32_t channel, int status)
|
|
|
|
{
|
|
|
|
const struct device *uart_dev = user_data;
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(uart_dev);
|
|
|
|
|
|
|
|
if (status != 0) {
|
|
|
|
async_evt_rx_err(data, status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-04-08 12:09:58 +02:00
|
|
|
(void)k_work_cancel_delayable(&data->dma_rx.timeout_work);
|
2021-01-16 17:44:24 +01:00
|
|
|
|
|
|
|
/* true since this functions occurs when buffer if full */
|
|
|
|
data->dma_rx.counter = data->dma_rx.buffer_length;
|
|
|
|
|
|
|
|
async_evt_rx_rdy(data);
|
|
|
|
|
|
|
|
if (data->rx_next_buffer != NULL) {
|
|
|
|
async_evt_rx_buf_release(data);
|
|
|
|
|
|
|
|
/* replace the buffer when the current
|
|
|
|
* is full and not the same as the next
|
|
|
|
* one.
|
|
|
|
*/
|
|
|
|
uart_stm32_dma_replace_buffer(uart_dev);
|
|
|
|
} else {
|
|
|
|
/* Buffer full without valid next buffer,
|
|
|
|
* an UART_RX_DISABLED event must be generated,
|
|
|
|
* but uart_stm32_async_rx_disable() cannot be
|
|
|
|
* called in ISR context. So force the RX timeout
|
|
|
|
* to minimum value and let the RX timeout to do the job.
|
|
|
|
*/
|
2021-04-08 12:09:58 +02:00
|
|
|
k_work_reschedule(&data->dma_rx.timeout_work, K_TICKS(1));
|
2021-01-16 17:44:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uart_stm32_async_tx(const struct device *dev,
|
|
|
|
const uint8_t *tx_data, size_t buf_size, int32_t timeout)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
int ret;
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
if (data->dma_tx.dma_dev == NULL) {
|
2021-01-16 17:44:24 +01:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->dma_tx.buffer_length != 0) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->dma_tx.buffer = (uint8_t *)tx_data;
|
|
|
|
data->dma_tx.buffer_length = buf_size;
|
|
|
|
data->dma_tx.timeout = timeout;
|
|
|
|
|
|
|
|
LOG_DBG("tx: l=%d", data->dma_tx.buffer_length);
|
|
|
|
|
2021-03-30 12:20:46 +02:00
|
|
|
/* Clear TC flag */
|
|
|
|
LL_USART_ClearFlag_TC(UartInstance);
|
|
|
|
|
|
|
|
/* Enable TC interrupt so we can signal correct TX done */
|
|
|
|
LL_USART_EnableIT_TC(UartInstance);
|
2021-01-16 17:44:24 +01:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
ret = dma_config(data->dma_tx.dma_dev, data->dma_tx.dma_channel,
|
2021-01-16 17:44:24 +01:00
|
|
|
&data->dma_tx.dma_cfg);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG_ERR("dma tx config error!");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
if (dma_start(data->dma_tx.dma_dev, data->dma_tx.dma_channel)) {
|
2021-01-16 17:44:24 +01:00
|
|
|
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);
|
|
|
|
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
#ifdef CONFIG_PM
|
2021-09-21 09:34:09 +02:00
|
|
|
|
|
|
|
/* Do not allow system to suspend until transmission has completed */
|
drivers/uart: stm32: Fix pm_constraint handling
Introduce new logic to set/release pm_constraint during serial TX
transactions.
First change is to introduce an internal flag and utility functions
to control the set/release constraint balancing per uart device.
This way, whatever the mix of transactions or API calls, we
ensure a single uart device can only do 1 or 0 to the PM state
constraint. Constraint can't then be set more than once, released w/o
having been set or released more than it was set.
The last part of the change reworks the triggers for constraints
set/release operations.
In order not to disturb driver operations, if irq driven mode or PM is
enabled, don't enable TC interrupt handling by default.
Instead, map the pm_constraint setting to the way TC flag is handled
in normal mode of operations (irq driven or async).
As a consequence, in irq driven mode, pm_constraint is set/released on
tx_enable/tx_disable api calls, which gives API user full control
on transaction protection vs low power operations.
Finally, we emulate the same behavior on TX poll transaction, by
enabling TC irq at the start of a stream and disabling TC irq once
stream is completed. This is controlled with a dedicated device flag.
Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
2021-09-14 09:27:56 +02:00
|
|
|
uart_stm32_pm_constraint_set(dev);
|
|
|
|
#endif
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
/* Enable TX DMA requests */
|
|
|
|
uart_stm32_dma_tx_enable(dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uart_stm32_async_rx_enable(const struct device *dev,
|
|
|
|
uint8_t *rx_buf, size_t buf_size, int32_t timeout)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
int ret;
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
if (data->dma_rx.dma_dev == NULL) {
|
2021-01-16 17:44:24 +01:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->dma_rx.enabled) {
|
|
|
|
LOG_WRN("RX was already enabled");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->dma_rx.offset = 0;
|
|
|
|
data->dma_rx.buffer = rx_buf;
|
|
|
|
data->dma_rx.buffer_length = buf_size;
|
|
|
|
data->dma_rx.counter = 0;
|
|
|
|
data->dma_rx.timeout = timeout;
|
|
|
|
|
|
|
|
/* Disable RX interrupts to let DMA to handle it */
|
|
|
|
LL_USART_DisableIT_RXNE(UartInstance);
|
|
|
|
|
|
|
|
data->dma_rx.blk_cfg.block_size = buf_size;
|
|
|
|
data->dma_rx.blk_cfg.dest_address = (uint32_t)data->dma_rx.buffer;
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
ret = dma_config(data->dma_rx.dma_dev, data->dma_rx.dma_channel,
|
2021-01-16 17:44:24 +01:00
|
|
|
&data->dma_rx.dma_cfg);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG_ERR("UART ERR: RX DMA config failed!");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
if (dma_start(data->dma_rx.dma_dev, data->dma_rx.dma_channel)) {
|
2021-01-16 17:44:24 +01:00
|
|
|
LOG_ERR("UART ERR: RX DMA start failed!");
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable RX DMA requests */
|
|
|
|
uart_stm32_dma_rx_enable(dev);
|
|
|
|
|
|
|
|
/* Enable IRQ IDLE to define the end of a
|
|
|
|
* RX DMA transaction.
|
|
|
|
*/
|
|
|
|
LL_USART_ClearFlag_IDLE(UartInstance);
|
|
|
|
LL_USART_EnableIT_IDLE(UartInstance);
|
|
|
|
|
|
|
|
LL_USART_EnableIT_ERROR(UartInstance);
|
|
|
|
|
|
|
|
/* Request next buffer */
|
|
|
|
async_evt_rx_buf_request(data);
|
|
|
|
|
|
|
|
LOG_DBG("async rx enabled");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uart_stm32_async_tx_abort(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
size_t tx_buffer_length = data->dma_tx.buffer_length;
|
|
|
|
struct dma_status stat;
|
|
|
|
|
|
|
|
if (tx_buffer_length == 0) {
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
2021-04-08 12:09:58 +02:00
|
|
|
(void)k_work_cancel_delayable(&data->dma_tx.timeout_work);
|
2021-03-01 11:59:57 +01:00
|
|
|
if (!dma_get_status(data->dma_tx.dma_dev,
|
2021-01-16 17:44:24 +01:00
|
|
|
data->dma_tx.dma_channel, &stat)) {
|
|
|
|
data->dma_tx.counter = tx_buffer_length - stat.pending_length;
|
|
|
|
}
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
dma_stop(data->dma_tx.dma_dev, data->dma_tx.dma_channel);
|
2021-01-16 17:44:24 +01:00
|
|
|
async_evt_tx_abort(data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uart_stm32_async_rx_timeout(struct k_work *work)
|
|
|
|
{
|
|
|
|
struct uart_dma_stream *rx_stream = CONTAINER_OF(work,
|
|
|
|
struct uart_dma_stream, timeout_work);
|
|
|
|
struct uart_stm32_data *data = CONTAINER_OF(rx_stream,
|
|
|
|
struct uart_stm32_data, dma_rx);
|
|
|
|
const struct device *dev = data->uart_dev;
|
|
|
|
|
|
|
|
LOG_DBG("rx timeout");
|
|
|
|
|
|
|
|
if (data->dma_rx.counter == data->dma_rx.buffer_length) {
|
|
|
|
uart_stm32_async_rx_disable(dev);
|
|
|
|
} else {
|
|
|
|
uart_stm32_dma_rx_flush(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uart_stm32_async_tx_timeout(struct k_work *work)
|
|
|
|
{
|
|
|
|
struct uart_dma_stream *tx_stream = CONTAINER_OF(work,
|
|
|
|
struct uart_dma_stream, timeout_work);
|
|
|
|
struct uart_stm32_data *data = CONTAINER_OF(tx_stream,
|
|
|
|
struct uart_stm32_data, dma_tx);
|
|
|
|
const struct device *dev = data->uart_dev;
|
|
|
|
|
|
|
|
uart_stm32_async_tx_abort(dev);
|
|
|
|
|
|
|
|
LOG_DBG("tx: async timeout");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uart_stm32_async_rx_buf_rsp(const struct device *dev, uint8_t *buf,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
|
|
|
|
LOG_DBG("replace buffer (%d)", len);
|
|
|
|
data->rx_next_buffer = buf;
|
|
|
|
data->rx_next_buffer_len = len;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uart_stm32_async_init(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
|
|
|
|
|
|
|
data->uart_dev = dev;
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
if (data->dma_rx.dma_dev != NULL) {
|
|
|
|
if (!device_is_ready(data->dma_rx.dma_dev)) {
|
2021-01-16 17:44:24 +01:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-01 11:59:57 +01:00
|
|
|
if (data->dma_tx.dma_dev != NULL) {
|
|
|
|
if (!device_is_ready(data->dma_rx.dma_dev)) {
|
2021-01-16 17:44:24 +01:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable both TX and RX DMA requests */
|
|
|
|
uart_stm32_dma_rx_disable(dev);
|
|
|
|
uart_stm32_dma_tx_disable(dev);
|
|
|
|
|
2021-04-08 12:09:58 +02:00
|
|
|
k_work_init_delayable(&data->dma_rx.timeout_work,
|
2021-01-16 17:44:24 +01:00
|
|
|
uart_stm32_async_rx_timeout);
|
2021-04-08 12:09:58 +02:00
|
|
|
k_work_init_delayable(&data->dma_tx.timeout_work,
|
2021-01-16 17:44:24 +01:00
|
|
|
uart_stm32_async_tx_timeout);
|
|
|
|
|
|
|
|
/* Configure dma rx config */
|
|
|
|
memset(&data->dma_rx.blk_cfg, 0, sizeof(data->dma_rx.blk_cfg));
|
|
|
|
|
|
|
|
#if defined(CONFIG_SOC_SERIES_STM32F1X) || \
|
|
|
|
defined(CONFIG_SOC_SERIES_STM32F2X) || \
|
|
|
|
defined(CONFIG_SOC_SERIES_STM32F4X) || \
|
|
|
|
defined(CONFIG_SOC_SERIES_STM32L1X)
|
|
|
|
data->dma_rx.blk_cfg.source_address =
|
|
|
|
LL_USART_DMA_GetRegAddr(UartInstance);
|
|
|
|
#else
|
|
|
|
data->dma_rx.blk_cfg.source_address =
|
|
|
|
LL_USART_DMA_GetRegAddr(UartInstance,
|
|
|
|
LL_USART_DMA_REG_DATA_RECEIVE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
data->dma_rx.blk_cfg.dest_address = 0; /* dest not ready */
|
|
|
|
|
|
|
|
if (data->dma_rx.src_addr_increment) {
|
|
|
|
data->dma_rx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_INCREMENT;
|
|
|
|
} else {
|
|
|
|
data->dma_rx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->dma_rx.dst_addr_increment) {
|
|
|
|
data->dma_rx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT;
|
|
|
|
} else {
|
|
|
|
data->dma_rx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RX disable circular buffer */
|
|
|
|
data->dma_rx.blk_cfg.source_reload_en = 0;
|
|
|
|
data->dma_rx.blk_cfg.dest_reload_en = 0;
|
|
|
|
data->dma_rx.blk_cfg.fifo_mode_control = data->dma_rx.fifo_threshold;
|
|
|
|
|
|
|
|
data->dma_rx.dma_cfg.head_block = &data->dma_rx.blk_cfg;
|
|
|
|
data->dma_rx.dma_cfg.user_data = (void *)dev;
|
|
|
|
data->rx_next_buffer = NULL;
|
|
|
|
data->rx_next_buffer_len = 0;
|
|
|
|
|
|
|
|
/* Configure dma tx config */
|
|
|
|
memset(&data->dma_tx.blk_cfg, 0, sizeof(data->dma_tx.blk_cfg));
|
|
|
|
|
|
|
|
#if defined(CONFIG_SOC_SERIES_STM32F1X) || \
|
|
|
|
defined(CONFIG_SOC_SERIES_STM32F2X) || \
|
|
|
|
defined(CONFIG_SOC_SERIES_STM32F4X) || \
|
|
|
|
defined(CONFIG_SOC_SERIES_STM32L1X)
|
|
|
|
data->dma_tx.blk_cfg.dest_address =
|
|
|
|
LL_USART_DMA_GetRegAddr(UartInstance);
|
|
|
|
#else
|
|
|
|
data->dma_tx.blk_cfg.dest_address =
|
|
|
|
LL_USART_DMA_GetRegAddr(UartInstance,
|
|
|
|
LL_USART_DMA_REG_DATA_TRANSMIT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
data->dma_tx.blk_cfg.source_address = 0; /* not ready */
|
|
|
|
|
|
|
|
if (data->dma_tx.src_addr_increment) {
|
|
|
|
data->dma_tx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_INCREMENT;
|
|
|
|
} else {
|
|
|
|
data->dma_tx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->dma_tx.dst_addr_increment) {
|
|
|
|
data->dma_tx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT;
|
|
|
|
} else {
|
|
|
|
data->dma_tx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->dma_tx.blk_cfg.fifo_mode_control = data->dma_tx.fifo_threshold;
|
|
|
|
|
|
|
|
data->dma_tx.dma_cfg.head_block = &data->dma_tx.blk_cfg;
|
|
|
|
data->dma_tx.dma_cfg.user_data = (void *)dev;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_UART_ASYNC_API */
|
2016-03-13 19:37:25 +01:00
|
|
|
|
2016-10-24 09:38:49 +02:00
|
|
|
static const struct uart_driver_api uart_stm32_driver_api = {
|
2016-03-03 15:33:20 +01:00
|
|
|
.poll_in = uart_stm32_poll_in,
|
|
|
|
.poll_out = uart_stm32_poll_out,
|
2019-02-14 10:50:19 +01:00
|
|
|
.err_check = uart_stm32_err_check,
|
2021-05-26 21:33:37 +02:00
|
|
|
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
|
2019-01-07 22:52:24 +01:00
|
|
|
.configure = uart_stm32_configure,
|
|
|
|
.config_get = uart_stm32_config_get,
|
2021-05-26 21:33:37 +02:00
|
|
|
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
|
2016-03-13 19:37:25 +01:00
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
|
|
.fifo_fill = uart_stm32_fifo_fill,
|
|
|
|
.fifo_read = uart_stm32_fifo_read,
|
|
|
|
.irq_tx_enable = uart_stm32_irq_tx_enable,
|
|
|
|
.irq_tx_disable = uart_stm32_irq_tx_disable,
|
|
|
|
.irq_tx_ready = uart_stm32_irq_tx_ready,
|
2017-05-11 16:57:29 +02:00
|
|
|
.irq_tx_complete = uart_stm32_irq_tx_complete,
|
2016-03-13 19:37:25 +01:00
|
|
|
.irq_rx_enable = uart_stm32_irq_rx_enable,
|
|
|
|
.irq_rx_disable = uart_stm32_irq_rx_disable,
|
|
|
|
.irq_rx_ready = uart_stm32_irq_rx_ready,
|
|
|
|
.irq_err_enable = uart_stm32_irq_err_enable,
|
|
|
|
.irq_err_disable = uart_stm32_irq_err_disable,
|
|
|
|
.irq_is_pending = uart_stm32_irq_is_pending,
|
|
|
|
.irq_update = uart_stm32_irq_update,
|
|
|
|
.irq_callback_set = uart_stm32_irq_callback_set,
|
|
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
2021-01-16 17:44:24 +01:00
|
|
|
#ifdef CONFIG_UART_ASYNC_API
|
|
|
|
.callback_set = uart_stm32_async_callback_set,
|
|
|
|
.tx = uart_stm32_async_tx,
|
|
|
|
.tx_abort = uart_stm32_async_tx_abort,
|
|
|
|
.rx_enable = uart_stm32_async_rx_enable,
|
|
|
|
.rx_disable = uart_stm32_async_rx_disable,
|
|
|
|
.rx_buf_rsp = uart_stm32_async_rx_buf_rsp,
|
|
|
|
#endif /* CONFIG_UART_ASYNC_API */
|
2016-03-03 15:33:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Initialize UART channel
|
|
|
|
*
|
|
|
|
* This routine is called to reset the chip in a quiescent state.
|
|
|
|
* It is assumed that this function is called only once per UART.
|
|
|
|
*
|
|
|
|
* @param dev UART device struct
|
|
|
|
*
|
|
|
|
* @return 0
|
|
|
|
*/
|
2020-04-30 20:33:38 +02:00
|
|
|
static int uart_stm32_init(const struct device *dev)
|
2016-03-03 15:33:20 +01:00
|
|
|
{
|
2016-11-14 11:53:52 +01:00
|
|
|
const struct uart_stm32_config *config = DEV_CFG(dev);
|
2016-03-03 15:33:20 +01:00
|
|
|
struct uart_stm32_data *data = DEV_DATA(dev);
|
2017-09-21 15:20:53 +02:00
|
|
|
USART_TypeDef *UartInstance = UART_STRUCT(dev);
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t ll_parity;
|
|
|
|
uint32_t ll_datawidth;
|
2020-10-16 17:20:00 +02:00
|
|
|
int err;
|
2017-09-21 15:20:53 +02:00
|
|
|
|
2016-03-03 15:33:20 +01:00
|
|
|
__uart_stm32_get_clock(dev);
|
|
|
|
/* enable clock */
|
2018-12-07 11:09:28 +01:00
|
|
|
if (clock_control_on(data->clock,
|
|
|
|
(clock_control_subsys_t *)&config->pclken) != 0) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2020-06-05 10:57:52 +02:00
|
|
|
/* Configure dt provided device signals when available */
|
2020-10-16 17:20:00 +02:00
|
|
|
err = stm32_dt_pinctrl_configure(config->pinctrl_list,
|
|
|
|
config->pinctrl_list_size,
|
|
|
|
(uint32_t)UART_STRUCT(dev));
|
|
|
|
if (err < 0) {
|
|
|
|
return err;
|
2020-06-05 10:57:52 +02:00
|
|
|
}
|
|
|
|
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_Disable(UartInstance);
|
|
|
|
|
|
|
|
/* TX/RX direction */
|
|
|
|
LL_USART_SetTransferDirection(UartInstance,
|
|
|
|
LL_USART_DIRECTION_TX_RX);
|
|
|
|
|
2020-03-18 12:40:21 +01:00
|
|
|
/* Determine the datawidth and parity. If we use other parity than
|
|
|
|
* 'none' we must use datawidth = 9 (to get 8 databit + 1 parity bit).
|
|
|
|
*/
|
|
|
|
if (config->parity == 2) {
|
|
|
|
/* 8 databit, 1 parity bit, parity even */
|
|
|
|
ll_parity = LL_USART_PARITY_EVEN;
|
|
|
|
ll_datawidth = LL_USART_DATAWIDTH_9B;
|
|
|
|
} else if (config->parity == 1) {
|
|
|
|
/* 8 databit, 1 parity bit, parity odd */
|
|
|
|
ll_parity = LL_USART_PARITY_ODD;
|
|
|
|
ll_datawidth = LL_USART_DATAWIDTH_9B;
|
|
|
|
} else { /* Default to 8N0, but show warning if invalid value */
|
|
|
|
if (config->parity != 0) {
|
|
|
|
LOG_WRN("Invalid parity setting '%d'."
|
|
|
|
"Defaulting to 'none'.", config->parity);
|
|
|
|
}
|
|
|
|
/* 8 databit, parity none */
|
|
|
|
ll_parity = LL_USART_PARITY_NONE;
|
|
|
|
ll_datawidth = LL_USART_DATAWIDTH_8B;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set datawidth and parity, 1 start bit, 1 stop bit */
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_ConfigCharacter(UartInstance,
|
2020-03-18 12:40:21 +01:00
|
|
|
ll_datawidth,
|
|
|
|
ll_parity,
|
2017-09-21 15:20:53 +02:00
|
|
|
LL_USART_STOPBITS_1);
|
|
|
|
|
2019-02-08 18:39:35 +01:00
|
|
|
if (config->hw_flow_control) {
|
|
|
|
uart_stm32_set_hwctrl(dev, LL_USART_HWCONTROL_RTS_CTS);
|
|
|
|
}
|
|
|
|
|
2019-01-07 22:52:24 +01:00
|
|
|
/* Set the default baudrate */
|
|
|
|
uart_stm32_set_baudrate(dev, data->baud_rate);
|
2017-09-21 15:20:53 +02:00
|
|
|
|
|
|
|
LL_USART_Enable(UartInstance);
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2018-06-13 11:32:38 +02:00
|
|
|
#ifdef USART_ISR_TEACK
|
|
|
|
/* Wait until TEACK flag is set */
|
2019-06-04 16:52:23 +02:00
|
|
|
while (!(LL_USART_IsActiveFlag_TEACK(UartInstance))) {
|
|
|
|
}
|
2018-06-13 11:32:38 +02:00
|
|
|
#endif /* !USART_ISR_TEACK */
|
|
|
|
|
|
|
|
#ifdef USART_ISR_REACK
|
2018-06-18 18:01:06 +02:00
|
|
|
/* Wait until REACK flag is set */
|
2019-06-04 16:52:23 +02:00
|
|
|
while (!(LL_USART_IsActiveFlag_REACK(UartInstance))) {
|
|
|
|
}
|
2018-06-13 11:32:38 +02:00
|
|
|
#endif /* !USART_ISR_REACK */
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
|
2016-11-14 11:53:52 +01:00
|
|
|
config->uconf.irq_config_func(dev);
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
#elif defined(CONFIG_PM)
|
|
|
|
config->irq_config_func(dev);
|
|
|
|
#endif /* defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API) */
|
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
#ifdef CONFIG_UART_ASYNC_API
|
|
|
|
return uart_stm32_async_init(dev);
|
|
|
|
#else
|
2016-03-03 15:33:20 +01:00
|
|
|
return 0;
|
2021-01-16 17:44:24 +01:00
|
|
|
#endif
|
2016-03-03 15:33:20 +01:00
|
|
|
}
|
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
#ifdef CONFIG_UART_ASYNC_API
|
|
|
|
|
|
|
|
/* src_dev and dest_dev should be 'MEMORY' or 'PERIPHERAL'. */
|
|
|
|
#define UART_DMA_CHANNEL_INIT(index, dir, dir_cap, src_dev, dest_dev) \
|
2021-06-18 15:52:30 +02:00
|
|
|
.dma_dev = DEVICE_DT_GET(STM32_DMA_CTLR(index, dir)), \
|
2021-01-16 17:44:24 +01:00
|
|
|
.dma_channel = DT_INST_DMAS_CELL_BY_NAME(index, dir, channel), \
|
|
|
|
.dma_cfg = { \
|
2021-08-09 16:32:57 +02:00
|
|
|
.dma_slot = STM32_DMA_SLOT(index, dir, slot),\
|
2021-01-16 17:44:24 +01:00
|
|
|
.channel_direction = STM32_DMA_CONFIG_DIRECTION( \
|
2021-06-18 15:52:30 +02:00
|
|
|
STM32_DMA_CHANNEL_CONFIG(index, dir)),\
|
2021-01-16 17:44:24 +01:00
|
|
|
.channel_priority = STM32_DMA_CONFIG_PRIORITY( \
|
2021-06-18 15:52:30 +02:00
|
|
|
STM32_DMA_CHANNEL_CONFIG(index, dir)), \
|
2021-01-16 17:44:24 +01:00
|
|
|
.source_data_size = STM32_DMA_CONFIG_##src_dev##_DATA_SIZE(\
|
2021-06-18 15:52:30 +02:00
|
|
|
STM32_DMA_CHANNEL_CONFIG(index, dir)),\
|
2021-01-16 17:44:24 +01:00
|
|
|
.dest_data_size = STM32_DMA_CONFIG_##dest_dev##_DATA_SIZE(\
|
2021-06-18 15:52:30 +02:00
|
|
|
STM32_DMA_CHANNEL_CONFIG(index, dir)),\
|
2021-01-16 17:44:24 +01:00
|
|
|
.source_burst_length = 1, /* SINGLE transfer */ \
|
|
|
|
.dest_burst_length = 1, \
|
|
|
|
.block_count = 1, \
|
|
|
|
.dma_callback = uart_stm32_dma_##dir##_cb, \
|
|
|
|
}, \
|
|
|
|
.src_addr_increment = STM32_DMA_CONFIG_##src_dev##_ADDR_INC( \
|
2021-06-18 15:52:30 +02:00
|
|
|
STM32_DMA_CHANNEL_CONFIG(index, dir)), \
|
2021-01-16 17:44:24 +01:00
|
|
|
.dst_addr_increment = STM32_DMA_CONFIG_##dest_dev##_ADDR_INC( \
|
2021-06-18 15:52:30 +02:00
|
|
|
STM32_DMA_CHANNEL_CONFIG(index, dir)), \
|
2021-01-16 17:44:24 +01:00
|
|
|
.fifo_threshold = STM32_DMA_FEATURES_FIFO_THRESHOLD( \
|
2021-06-18 15:52:30 +02:00
|
|
|
STM32_DMA_FEATURES(index, dir)), \
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
#endif
|
|
|
|
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API) || \
|
|
|
|
defined(CONFIG_PM)
|
2020-02-28 14:54:37 +01:00
|
|
|
#define STM32_UART_IRQ_HANDLER_DECL(index) \
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
static void uart_stm32_irq_config_func_##index(const struct device *dev);
|
2020-02-28 14:54:37 +01:00
|
|
|
#define STM32_UART_IRQ_HANDLER(index) \
|
2020-04-30 20:33:38 +02:00
|
|
|
static void uart_stm32_irq_config_func_##index(const struct device *dev) \
|
2017-05-01 15:21:52 +02:00
|
|
|
{ \
|
2020-06-05 09:48:29 +02:00
|
|
|
IRQ_CONNECT(DT_INST_IRQN(index), \
|
|
|
|
DT_INST_IRQ(index, priority), \
|
2020-12-11 17:12:30 +01:00
|
|
|
uart_stm32_isr, DEVICE_DT_INST_GET(index), \
|
2017-05-01 15:21:52 +02:00
|
|
|
0); \
|
2020-06-05 09:48:29 +02:00
|
|
|
irq_enable(DT_INST_IRQN(index)); \
|
2016-03-13 19:37:25 +01:00
|
|
|
}
|
2017-01-23 17:55:57 +01:00
|
|
|
#else
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
#define STM32_UART_IRQ_HANDLER_DECL(index) /* Not used */
|
|
|
|
#define STM32_UART_IRQ_HANDLER(index) /* Not used */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
|
|
|
|
#define STM32_UART_IRQ_HANDLER_FUNC(index) \
|
|
|
|
.irq_config_func = uart_stm32_irq_config_func_##index,
|
|
|
|
#define STM32_UART_POLL_IRQ_HANDLER_FUNC(index) /* Not used */
|
|
|
|
#elif defined(CONFIG_PM)
|
|
|
|
#define STM32_UART_IRQ_HANDLER_FUNC(index) /* Not used */
|
|
|
|
#define STM32_UART_POLL_IRQ_HANDLER_FUNC(index) \
|
|
|
|
.irq_config_func = uart_stm32_irq_config_func_##index,
|
|
|
|
#else
|
|
|
|
#define STM32_UART_IRQ_HANDLER_FUNC(index) /* Not used */
|
|
|
|
#define STM32_UART_POLL_IRQ_HANDLER_FUNC(index) /* Not used */
|
2017-05-01 15:21:52 +02:00
|
|
|
#endif
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2021-01-16 17:44:24 +01:00
|
|
|
#ifdef CONFIG_UART_ASYNC_API
|
|
|
|
#define UART_DMA_CHANNEL(index, dir, DIR, src, dest) \
|
|
|
|
.dma_##dir = { \
|
|
|
|
COND_CODE_1(DT_INST_DMAS_HAS_NAME(index, dir), \
|
|
|
|
(UART_DMA_CHANNEL_INIT(index, dir, DIR, src, dest)), \
|
|
|
|
(NULL)) \
|
|
|
|
},
|
|
|
|
|
|
|
|
#else
|
|
|
|
#define UART_DMA_CHANNEL(index, dir, DIR, src, dest)
|
|
|
|
#endif
|
|
|
|
|
2020-02-28 14:54:37 +01:00
|
|
|
#define STM32_UART_INIT(index) \
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
STM32_UART_IRQ_HANDLER_DECL(index) \
|
2017-05-01 15:21:52 +02:00
|
|
|
\
|
2020-06-05 10:57:52 +02:00
|
|
|
static const struct soc_gpio_pinctrl uart_pins_##index[] = \
|
2020-10-08 17:24:07 +02:00
|
|
|
ST_STM32_DT_INST_PINCTRL(index, 0); \
|
2020-06-05 10:57:52 +02:00
|
|
|
\
|
2020-02-28 14:54:37 +01:00
|
|
|
static const struct uart_stm32_config uart_stm32_cfg_##index = { \
|
2017-05-01 15:21:52 +02:00
|
|
|
.uconf = { \
|
2020-06-05 09:48:29 +02:00
|
|
|
.base = (uint8_t *)DT_INST_REG_ADDR(index), \
|
2020-02-28 14:54:37 +01:00
|
|
|
STM32_UART_IRQ_HANDLER_FUNC(index) \
|
2017-05-01 15:21:52 +02:00
|
|
|
}, \
|
2020-06-05 09:48:29 +02:00
|
|
|
.pclken = { .bus = DT_INST_CLOCKS_CELL(index, bus), \
|
|
|
|
.enr = DT_INST_CLOCKS_CELL(index, bits) \
|
2018-11-08 14:38:48 +01:00
|
|
|
}, \
|
2020-06-05 09:48:29 +02:00
|
|
|
.hw_flow_control = DT_INST_PROP(index, hw_flow_control), \
|
2021-11-17 14:01:42 +01:00
|
|
|
.parity = DT_INST_ENUM_IDX_OR(index, parity, UART_CFG_PARITY_NONE), \
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
STM32_UART_POLL_IRQ_HANDLER_FUNC(index) \
|
2020-06-05 10:57:52 +02:00
|
|
|
.pinctrl_list = uart_pins_##index, \
|
|
|
|
.pinctrl_list_size = ARRAY_SIZE(uart_pins_##index), \
|
2017-05-01 15:21:52 +02:00
|
|
|
}; \
|
|
|
|
\
|
2020-02-28 14:54:37 +01:00
|
|
|
static struct uart_stm32_data uart_stm32_data_##index = { \
|
2020-06-05 09:48:29 +02:00
|
|
|
.baud_rate = DT_INST_PROP(index, current_speed), \
|
2021-01-16 17:44:24 +01:00
|
|
|
UART_DMA_CHANNEL(index, rx, RX, PERIPHERAL, MEMORY) \
|
|
|
|
UART_DMA_CHANNEL(index, tx, TX, MEMORY, PERIPHERAL) \
|
2017-05-01 15:21:52 +02:00
|
|
|
}; \
|
|
|
|
\
|
2020-12-11 17:12:30 +01:00
|
|
|
DEVICE_DT_INST_DEFINE(index, \
|
2017-05-01 15:21:52 +02:00
|
|
|
&uart_stm32_init, \
|
drivers: serial: stm32: use PM constraints to prevent suspension
In the current implementation the STM32 UART driver required to enable
`CONFIG_PM_DEVICE` when `CONFIG_PM=y` to function properly. The main
reason is that in some situations, like in polling mode, transmissions
are not fully synchronous. That is, a byte is pushed to the _queue_ if
it is empty and then the function returns without waiting for it to be
transmitted to the wire. This makes sense to make things like per-byte
transmission efficient. However, this introduces a problem: the system
may reach idle state, and so enter low power modes before the UART has
actually finished the last data in the queue. If this happens,
communications can be interrupted or garbage data may be put into the
UART line.
The proposed solution in this patch uses PM constraints to solve this
problem. For the IRQ/DMA case it is easy since we can set the constraint
before transmission start, and when the completion (TC) interrupt is
received we can clear it. However, the polling mode did not have the
capability to signal the completion. For this case, a simpler IRQ
routine is provided to just release the constraint. As a result, the PM
hooks are not required and so system can operate with just `CONFIG_PM`.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2021-09-09 22:41:35 +02:00
|
|
|
NULL, \
|
2020-02-28 14:54:37 +01:00
|
|
|
&uart_stm32_data_##index, &uart_stm32_cfg_##index, \
|
2021-10-14 16:38:10 +02:00
|
|
|
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
|
2017-05-01 15:21:52 +02:00
|
|
|
&uart_stm32_driver_api); \
|
|
|
|
\
|
2020-02-28 14:54:37 +01:00
|
|
|
STM32_UART_IRQ_HANDLER(index)
|
2016-03-03 15:33:20 +01:00
|
|
|
|
2020-05-06 20:23:07 +02:00
|
|
|
DT_INST_FOREACH_STATUS_OKAY(STM32_UART_INIT)
|