diff --git a/drivers/serial/uart_stm32.c b/drivers/serial/uart_stm32.c index ef95b6b40a914..9243b10d7e73c 100644 --- a/drivers/serial/uart_stm32.c +++ b/drivers/serial/uart_stm32.c @@ -20,9 +20,7 @@ #include #include #include -#include #include -#include #include #include @@ -461,12 +459,12 @@ static inline enum uart_config_flow_control uart_stm32_ll2cfg_hwctrl(uint32_t fc return UART_CFG_FLOW_CTRL_NONE; } -#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE -static int uart_stm32_configure(const struct device *dev, - const struct uart_config *cfg) +static void uart_stm32_parameters_set(const struct device *dev, + const struct uart_config *cfg) { const struct uart_stm32_config *config = dev->config; struct uart_stm32_data *data = dev->data; + struct uart_config *uart_cfg = data->uart_cfg; const uint32_t parity = uart_stm32_cfg2ll_parity(cfg->parity); const uint32_t stopbits = uart_stm32_cfg2ll_stopbits(config, cfg->stop_bits); const uint32_t databits = uart_stm32_cfg2ll_databits(cfg->data_bits, @@ -476,6 +474,60 @@ static int uart_stm32_configure(const struct device *dev, bool driver_enable = cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RS485; #endif + if (cfg == uart_cfg) { + /* Called via (re-)init function, so the SoC either just booted, + * or is returning from a low-power state where it lost register + * contents + */ + LL_USART_ConfigCharacter(config->usart, + databits, + parity, + stopbits); + uart_stm32_set_hwctrl(dev, flowctrl); + uart_stm32_set_baudrate(dev, cfg->baudrate); + } else { + /* Called from application/subsys via uart_configure syscall */ + 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); + } + + if (flowctrl != uart_stm32_get_hwctrl(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 != uart_cfg->baudrate) { + uart_stm32_set_baudrate(dev, cfg->baudrate); + uart_cfg->baudrate = cfg->baudrate; + } + } +} + +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE +static int uart_stm32_configure(const struct device *dev, + const struct uart_config *cfg) +{ + const struct uart_stm32_config *config = dev->config; + struct uart_stm32_data *data = dev->data; + struct uart_config *uart_cfg = data->uart_cfg; + const uint32_t parity = uart_stm32_cfg2ll_parity(cfg->parity); + const uint32_t stopbits = uart_stm32_cfg2ll_stopbits(config, cfg->stop_bits); + const uint32_t databits = uart_stm32_cfg2ll_databits(cfg->data_bits, + cfg->parity); + /* Hardware doesn't support mark or space parity */ if ((cfg->parity == UART_CFG_PARITY_MARK) || (cfg->parity == UART_CFG_PARITY_SPACE)) { @@ -516,34 +568,18 @@ static int uart_stm32_configure(const struct device *dev, LL_USART_Disable(config->usart); - 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); - } - - if (flowctrl != uart_stm32_get_hwctrl(dev)) { - uart_stm32_set_hwctrl(dev, flowctrl); - } + /* Set basic parmeters, such as data-/stop-bit, parity, and baudrate */ + uart_stm32_parameters_set(dev, cfg); -#if HAS_DRIVER_ENABLE - if (driver_enable != uart_stm32_get_driver_enable(dev)) { - uart_stm32_set_driver_enable(dev, driver_enable); - } -#endif + LL_USART_Enable(config->usart); - if (cfg->baudrate != data->baud_rate) { - uart_stm32_set_baudrate(dev, cfg->baudrate); - data->baud_rate = cfg->baudrate; - } + /* Upon successful configuration, persist the syscall-passed + * uart_config. + * This allows restoring it, should the device return from a low-power + * mode in which register contents are lost. + */ + *uart_cfg = *cfg; - LL_USART_Enable(config->usart); return 0; }; @@ -551,8 +587,9 @@ static int uart_stm32_config_get(const struct device *dev, struct uart_config *cfg) { struct uart_stm32_data *data = dev->data; + struct uart_config *uart_cfg = data->uart_cfg; - cfg->baudrate = data->baud_rate; + cfg->baudrate = uart_cfg->baudrate; cfg->parity = uart_stm32_ll2cfg_parity(uart_stm32_get_parity(dev)); cfg->stop_bits = uart_stm32_ll2cfg_stopbits( uart_stm32_get_stopbits(dev)); @@ -1805,22 +1842,10 @@ static const struct uart_driver_api uart_stm32_driver_api = { #endif /* CONFIG_UART_ASYNC_API */ }; -/** - * @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 - */ -static int uart_stm32_init(const struct device *dev) +static int uart_stm32_clocks_enable(const struct device *dev) { const struct uart_stm32_config *config = dev->config; struct uart_stm32_data *data = dev->data; - uint32_t ll_parity; - uint32_t ll_datawidth; int err; __uart_stm32_get_clock(dev); @@ -1847,59 +1872,31 @@ static int uart_stm32_init(const struct device *dev) } } - /* Configure dt provided device signals when available */ - err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); - if (err < 0) { - return err; - } + return 0; +} + +static int uart_stm32_registers_configure(const struct device *dev) +{ + const struct uart_stm32_config *config = dev->config; + struct uart_stm32_data *data = dev->data; + struct uart_config *uart_cfg = data->uart_cfg; LL_USART_Disable(config->usart); - if (!device_is_ready(data->reset.dev)) { + if (!device_is_ready(config->reset.dev)) { LOG_ERR("reset controller not ready"); return -ENODEV; } /* Reset UART to default state using RCC */ - (void)reset_line_toggle_dt(&data->reset); + (void)reset_line_toggle_dt(&config->reset); /* TX/RX direction */ LL_USART_SetTransferDirection(config->usart, LL_USART_DIRECTION_TX_RX); - /* 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 */ - LL_USART_ConfigCharacter(config->usart, - ll_datawidth, - ll_parity, - LL_USART_STOPBITS_1); - - if (config->hw_flow_control) { - uart_stm32_set_hwctrl(dev, LL_USART_HWCONTROL_RTS_CTS); - } - - /* Set the default baudrate */ - uart_stm32_set_baudrate(dev, data->baud_rate); + /* Set basic parmeters, such as data-/stop-bit, parity, and baudrate */ + uart_stm32_parameters_set(dev, uart_cfg); /* Enable the single wire / half-duplex mode */ if (config->single_wire) { @@ -1955,6 +1952,40 @@ static int uart_stm32_init(const struct device *dev) } #endif /* !USART_ISR_REACK */ + return 0; +} + +/** + * @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 + */ +static int uart_stm32_init(const struct device *dev) +{ + const struct uart_stm32_config *config = dev->config; + int err; + + err = uart_stm32_clocks_enable(dev); + if (err < 0) { + return err; + } + + /* Configure dt provided device signals when available */ + err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (err < 0) { + return err; + } + + err = uart_stm32_registers_configure(dev); + if (err < 0) { + return err; + } + #if defined(CONFIG_PM) || \ defined(CONFIG_UART_INTERRUPT_DRIVEN) || \ defined(CONFIG_UART_ASYNC_API) @@ -2135,6 +2166,112 @@ static void uart_stm32_irq_config_func_##index(const struct device *dev) \ #define STM32_UART_PM_WAKEUP(index) /* Not used */ #endif +/* Ensure DTS doesn't present an incompatible parity configuration. + * Mark/space parity isn't supported on the STM32 family. + * If 9 data bits are configured, ensure that a parity bit isn't set. + */ +#define STM32_UART_CHECK_DT_PARITY(index) \ +BUILD_ASSERT( \ + !(DT_INST_ENUM_IDX_OR(index, parity, STM32_UART_DEFAULT_PARITY) \ + == UART_CFG_PARITY_MARK || \ + DT_INST_ENUM_IDX_OR(index, parity, STM32_UART_DEFAULT_PARITY) \ + == UART_CFG_PARITY_SPACE), \ + "Node " DT_NODE_PATH(DT_DRV_INST(index)) \ + " has unsupported parity configuration"); \ +BUILD_ASSERT( \ + !(DT_INST_ENUM_IDX_OR(index, parity, STM32_UART_DEFAULT_PARITY) \ + != UART_CFG_PARITY_NONE && \ + DT_INST_ENUM_IDX_OR(index, data_bits, \ + STM32_UART_DEFAULT_DATA_BITS) \ + == UART_CFG_DATA_BITS_9), \ + "Node " DT_NODE_PATH(DT_DRV_INST(index)) \ + " has unsupported parity + data bits combination"); + +/* Ensure DTS doesn't present an incompatible data bits configuration + * The STM32 family doesn't support 5 data bits, or 6 data bits without parity. + * Only some series support 7 data bits. + */ +#ifdef LL_USART_DATAWIDTH_7B +#define STM32_UART_CHECK_DT_DATA_BITS(index) \ +BUILD_ASSERT( \ + !(DT_INST_ENUM_IDX_OR(index, data_bits, \ + STM32_UART_DEFAULT_DATA_BITS) \ + == UART_CFG_DATA_BITS_5 || \ + (DT_INST_ENUM_IDX_OR(index, data_bits, \ + STM32_UART_DEFAULT_DATA_BITS) \ + == UART_CFG_DATA_BITS_6 && \ + DT_INST_ENUM_IDX_OR(index, parity, \ + STM32_UART_DEFAULT_PARITY) \ + == UART_CFG_PARITY_NONE)), \ + "Node " DT_NODE_PATH(DT_DRV_INST(index)) \ + " has unsupported data bits configuration"); +#else +#define STM32_UART_CHECK_DT_DATA_BITS(index) \ +BUILD_ASSERT( \ + !(DT_INST_ENUM_IDX_OR(index, data_bits, \ + STM32_UART_DEFAULT_DATA_BITS) \ + == UART_CFG_DATA_BITS_5 || \ + DT_INST_ENUM_IDX_OR(index, data_bits, \ + STM32_UART_DEFAULT_DATA_BITS) \ + == UART_CFG_DATA_BITS_6 || \ + (DT_INST_ENUM_IDX_OR(index, data_bits, \ + STM32_UART_DEFAULT_DATA_BITS) \ + == UART_CFG_DATA_BITS_7 && \ + DT_INST_ENUM_IDX_OR(index, parity, \ + STM32_UART_DEFAULT_PARITY) \ + == UART_CFG_PARITY_NONE)), \ + "Node " DT_NODE_PATH(DT_DRV_INST(index)) \ + " has unsupported data bits configuration"); +#endif + +/* Ensure DTS doesn't present an incompatible stop bits configuration. + * Some STM32 series USARTs don't support 0.5 stop bits, and it generally isn't + * supported for LPUART. + */ +#ifndef LL_USART_STOPBITS_0_5 +#define STM32_UART_CHECK_DT_STOP_BITS_0_5(index) \ +BUILD_ASSERT( \ + !(DT_INST_ENUM_IDX_OR(index, stop_bits, \ + STM32_UART_DEFAULT_STOP_BITS) \ + == UART_CFG_STOP_BITS_0_5), \ + "Node " DT_NODE_PATH(DT_DRV_INST(index)) \ + " has unsupported stop bits configuration"); +/* LPUARTs don't support 0.5 stop bits configurations */ +#else +#define STM32_UART_CHECK_DT_STOP_BITS_0_5(index) \ +BUILD_ASSERT( \ + !(DT_HAS_COMPAT_STATUS_OKAY(st_stm32_lpuart) && \ + DT_INST_ENUM_IDX_OR(index, stop_bits, \ + STM32_UART_DEFAULT_STOP_BITS) \ + == UART_CFG_STOP_BITS_0_5), \ + "Node " DT_NODE_PATH(DT_DRV_INST(index)) \ + " has unsupported stop bits configuration"); +#endif + +/* Ensure DTS doesn't present an incompatible stop bits configuration. + * Some STM32 series USARTs don't support 1.5 stop bits, and it generally isn't + * supported for LPUART. + */ +#ifndef LL_USART_STOPBITS_1_5 +#define STM32_UART_CHECK_DT_STOP_BITS_1_5(index) \ +BUILD_ASSERT( \ + DT_INST_ENUM_IDX_OR(index, stop_bits, \ + STM32_UART_DEFAULT_STOP_BITS) \ + != UART_CFG_STOP_BITS_1_5, \ + "Node " DT_NODE_PATH(DT_DRV_INST(index)) \ + " has unsupported stop bits configuration"); +/* LPUARTs don't support 1.5 stop bits configurations */ +#else +#define STM32_UART_CHECK_DT_STOP_BITS_1_5(index) \ +BUILD_ASSERT( \ + !(DT_HAS_COMPAT_STATUS_OKAY(st_stm32_lpuart) && \ + DT_INST_ENUM_IDX_OR(index, stop_bits, \ + STM32_UART_DEFAULT_STOP_BITS) \ + == UART_CFG_STOP_BITS_1_5), \ + "Node " DT_NODE_PATH(DT_DRV_INST(index)) \ + " has unsupported stop bits configuration"); +#endif + #define STM32_UART_INIT(index) \ STM32_UART_IRQ_HANDLER_DECL(index) \ \ @@ -2143,12 +2280,25 @@ PINCTRL_DT_INST_DEFINE(index); \ static const struct stm32_pclken pclken_##index[] = \ STM32_DT_INST_CLOCKS(index);\ \ +static struct uart_config uart_cfg_##index = { \ + .baudrate = DT_INST_PROP_OR(index, current_speed, \ + STM32_UART_DEFAULT_BAUDRATE), \ + .parity = DT_INST_ENUM_IDX_OR(index, parity, \ + STM32_UART_DEFAULT_PARITY), \ + .stop_bits = DT_INST_ENUM_IDX_OR(index, stop_bits, \ + STM32_UART_DEFAULT_STOP_BITS), \ + .data_bits = DT_INST_ENUM_IDX_OR(index, data_bits, \ + STM32_UART_DEFAULT_DATA_BITS), \ + .flow_ctrl = DT_INST_PROP(index, hw_flow_control) \ + ? UART_CFG_FLOW_CTRL_RTS_CTS \ + : UART_CFG_FLOW_CTRL_NONE, \ +}; \ + \ static const struct uart_stm32_config uart_stm32_cfg_##index = { \ .usart = (USART_TypeDef *)DT_INST_REG_ADDR(index), \ + .reset = RESET_DT_SPEC_GET(DT_DRV_INST(index)), \ .pclken = pclken_##index, \ .pclk_len = DT_INST_NUM_CLOCKS(index), \ - .hw_flow_control = DT_INST_PROP(index, hw_flow_control), \ - .parity = DT_INST_ENUM_IDX_OR(index, parity, UART_CFG_PARITY_NONE), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \ .single_wire = DT_INST_PROP_OR(index, single_wire, false), \ .tx_rx_swap = DT_INST_PROP_OR(index, tx_rx_swap, false), \ @@ -2163,8 +2313,7 @@ static const struct uart_stm32_config uart_stm32_cfg_##index = { \ }; \ \ static struct uart_stm32_data uart_stm32_data_##index = { \ - .baud_rate = DT_INST_PROP(index, current_speed), \ - .reset = RESET_DT_SPEC_GET(DT_DRV_INST(index)), \ + .uart_cfg = &uart_cfg_##index, \ UART_DMA_CHANNEL(index, rx, RX, PERIPHERAL, MEMORY) \ UART_DMA_CHANNEL(index, tx, TX, MEMORY, PERIPHERAL) \ }; \ @@ -2178,6 +2327,11 @@ DEVICE_DT_INST_DEFINE(index, \ PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \ &uart_stm32_driver_api); \ \ -STM32_UART_IRQ_HANDLER(index) +STM32_UART_IRQ_HANDLER(index) \ + \ +STM32_UART_CHECK_DT_PARITY(index) \ +STM32_UART_CHECK_DT_DATA_BITS(index) \ +STM32_UART_CHECK_DT_STOP_BITS_0_5(index) \ +STM32_UART_CHECK_DT_STOP_BITS_1_5(index) DT_INST_FOREACH_STATUS_OKAY(STM32_UART_INIT) diff --git a/drivers/serial/uart_stm32.h b/drivers/serial/uart_stm32.h index 15cf0a501e59c..ed8e8584cd66d 100644 --- a/drivers/serial/uart_stm32.h +++ b/drivers/serial/uart_stm32.h @@ -13,21 +13,26 @@ #define ZEPHYR_DRIVERS_SERIAL_UART_STM32_H_ #include +#include +#include #include +#define STM32_UART_DEFAULT_BAUDRATE 115200 +#define STM32_UART_DEFAULT_PARITY UART_CFG_PARITY_NONE +#define STM32_UART_DEFAULT_STOP_BITS UART_CFG_STOP_BITS_1 +#define STM32_UART_DEFAULT_DATA_BITS UART_CFG_DATA_BITS_8 + /* device config */ struct uart_stm32_config { /* USART instance */ USART_TypeDef *usart; + /* Reset controller device configuration */ + const struct reset_dt_spec reset; /* clock subsystem driving this peripheral */ const struct stm32_pclken *pclken; /* number of clock subsystems */ size_t pclk_len; - /* initial hardware flow control, 1 for RTS/CTS */ - bool hw_flow_control; - /* initial parity, 0 for none, 1 for odd, 2 for even */ - int parity; /* switch to enable single wire / half duplex feature */ bool single_wire; /* enable tx/rx pin swap */ @@ -44,6 +49,7 @@ struct uart_stm32_config { uint8_t de_deassert_time; /* enable de pin inversion */ bool de_invert; + /* pin muxing */ const struct pinctrl_dev_config *pcfg; #if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API) || \ defined(CONFIG_PM) @@ -78,12 +84,10 @@ struct uart_dma_stream { /* driver data */ struct uart_stm32_data { - /* Baud rate */ - uint32_t baud_rate; /* clock device */ const struct device *clock; - /* Reset controller device configuration */ - const struct reset_dt_spec reset; + /* uart config */ + struct uart_config *uart_cfg; #ifdef CONFIG_UART_INTERRUPT_DRIVEN uart_irq_callback_user_data_t user_cb; void *user_data;