From b95cdb2a334b380a384be6d1fd042c8312f346b9 Mon Sep 17 00:00:00 2001 From: Armin Brauns Date: Mon, 22 May 2023 14:29:23 +0000 Subject: [PATCH] drivers: serial: stm32: add support for RS485 configuration The stm32 UART can output a "driver enable" signal on the RTS pin that allows controlling e.g. external RS-485 drivers. This can already be configured through the devicetree using the `de-*` properties, but not through uart_configure(). This commit enables the use of .flow_ctrl=UART_CFG_FLOW_CTRL_RS485. This is supported on all devices other than l1, f1, f2, and f4 as found by this search: $ grep -rLw USART_CR3_DEM ../modules/hal/stm32/stm32cube/*/soc/*.h |\ grep -vE 'system_|partition_|stm32[^0-9]+[0-9]?xx\.h' |\ cut -d/ -f6 |\ sort -u Signed-off-by: Armin Brauns --- drivers/serial/uart_stm32.c | 67 ++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/drivers/serial/uart_stm32.c b/drivers/serial/uart_stm32.c index 004679b5011..45a16e9e893 100644 --- a/drivers/serial/uart_stm32.c +++ b/drivers/serial/uart_stm32.c @@ -56,6 +56,13 @@ LOG_MODULE_REGISTER(uart_stm32, CONFIG_UART_LOG_LEVEL); #define HAS_LPUART_1 (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpuart1), \ st_stm32_lpuart, okay)) +/* Available everywhere except l1, f1, f2, f4. */ +#ifdef USART_CR3_DEM +#define HAS_DRIVER_ENABLE 1 +#else +#define HAS_DRIVER_ENABLE 0 +#endif + #if HAS_LPUART_1 #ifdef USART_PRESC_PRESCALER uint32_t lpuartdiv_calc(const uint64_t clock_rate, const uint16_t presc_idx, @@ -256,6 +263,27 @@ static inline uint32_t uart_stm32_get_hwctrl(const struct device *dev) return LL_USART_GetHWFlowCtrl(config->usart); } +#if HAS_DRIVER_ENABLE +static inline void uart_stm32_set_driver_enable(const struct device *dev, + bool driver_enable) +{ + const struct uart_stm32_config *config = dev->config; + + if (driver_enable) { + LL_USART_EnableDEMode(config->usart); + } else { + LL_USART_DisableDEMode(config->usart); + } +} + +static inline bool uart_stm32_get_driver_enable(const struct device *dev) +{ + const struct uart_stm32_config *config = dev->config; + + return LL_USART_IsEnabledDEMode(config->usart); +} +#endif + static inline uint32_t uart_stm32_cfg2ll_parity(enum uart_config_parity parity) { switch (parity) { @@ -388,7 +416,7 @@ static inline enum uart_config_data_bits uart_stm32_ll2cfg_databits(uint32_t db, /** * @brief Get LL hardware flow control define from * Zephyr hardware flow control option. - * @note Supports only UART_CFG_FLOW_CTRL_RTS_CTS. + * @note Supports only UART_CFG_FLOW_CTRL_RTS_CTS and UART_CFG_FLOW_CTRL_RS485. * @param fc: Zephyr hardware flow control option. * @retval LL_USART_HWCONTROL_RTS_CTS, or LL_USART_HWCONTROL_NONE. */ @@ -396,6 +424,9 @@ static inline uint32_t uart_stm32_cfg2ll_hwctrl(enum uart_config_flow_control fc { if (fc == UART_CFG_FLOW_CTRL_RTS_CTS) { return LL_USART_HWCONTROL_RTS_CTS; + } else if (fc == UART_CFG_FLOW_CTRL_RS485) { + /* Driver Enable is handled separately */ + return LL_USART_HWCONTROL_NONE; } return LL_USART_HWCONTROL_NONE; @@ -428,6 +459,9 @@ static int uart_stm32_configure(const struct device *dev, const uint32_t databits = uart_stm32_cfg2ll_databits(cfg->data_bits, cfg->parity); const uint32_t flowctrl = uart_stm32_cfg2ll_hwctrl(cfg->flow_ctrl); +#if HAS_DRIVER_ENABLE + bool driver_enable = cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RS485; +#endif /* Hardware doesn't support mark or space parity */ if ((cfg->parity == UART_CFG_PARITY_MARK) || @@ -473,12 +507,16 @@ static int uart_stm32_configure(const struct device *dev, return -ENOTSUP; } - /* Driver supports only RTS CTS flow control */ - if (cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) { - if (!IS_UART_HWFLOW_INSTANCE(config->usart) || - UART_CFG_FLOW_CTRL_RTS_CTS != cfg->flow_ctrl) { - return -ENOTSUP; - } + /* Driver supports only RTS/CTS and RS485 flow control */ + if (!(cfg->flow_ctrl == UART_CFG_FLOW_CTRL_NONE + || (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS && + IS_UART_HWFLOW_INSTANCE(config->usart)) +#if HAS_DRIVER_ENABLE + || (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RS485 && + IS_UART_DRIVER_ENABLE_INSTANCE(config->usart)) +#endif + )) { + return -ENOTSUP; } LL_USART_Disable(config->usart); @@ -499,6 +537,12 @@ static int uart_stm32_configure(const struct device *dev, uart_stm32_set_hwctrl(dev, flowctrl); } +#if HAS_DRIVER_ENABLE + if (driver_enable != uart_stm32_get_driver_enable(dev)) { + uart_stm32_set_driver_enable(dev, driver_enable); + } +#endif + if (cfg->baudrate != data->baud_rate) { uart_stm32_set_baudrate(dev, cfg->baudrate); data->baud_rate = cfg->baudrate; @@ -521,6 +565,11 @@ static int uart_stm32_config_get(const struct device *dev, uart_stm32_get_databits(dev), uart_stm32_get_parity(dev)); cfg->flow_ctrl = uart_stm32_ll2cfg_hwctrl( uart_stm32_get_hwctrl(dev)); +#if HAS_DRIVER_ENABLE + if (uart_stm32_get_driver_enable(dev)) { + cfg->flow_ctrl = UART_CFG_FLOW_CTRL_RS485; + } +#endif return 0; } #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ @@ -1705,14 +1754,14 @@ static int uart_stm32_init(const struct device *dev) } #endif -#ifdef USART_CR3_DEM +#if HAS_DRIVER_ENABLE if (config->de_enable) { if (!IS_UART_DRIVER_ENABLE_INSTANCE(config->usart)) { LOG_ERR("%s does not support driver enable", dev->name); return -EINVAL; } - LL_USART_EnableDEMode(config->usart); + uart_stm32_set_driver_enable(dev, true); LL_USART_SetDEAssertionTime(config->usart, config->de_assert_time); LL_USART_SetDEDeassertionTime(config->usart, config->de_deassert_time);