From 16c715d982c3dc93fc6ecafda7ca5e554f147a42 Mon Sep 17 00:00:00 2001 From: Nicolas Le Bayon <nicolas.le.bayon@st.com> Date: Wed, 11 Sep 2019 11:46:40 +0200 Subject: [PATCH] st: stm32: add UART driver Add driver for STM32 UART/USART. Change-Id: Ia9266e5d177fe7fd09c8a15b81da1a05b1bc8b2d Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com> Signed-off-by: Nicolas Le Bayon <nicolas.le.bayon@st.com> Reviewed-on: https://gerrit.st.com/c/mpu/oe/st/tf-a/+/180868 Reviewed-by: CITOOLS <smet-aci-reviews@lists.codex.cro.st.com> Reviewed-by: Yann GAUTIER <yann.gautier@st.com> Tested-by: Yann GAUTIER <yann.gautier@st.com> --- drivers/st/uart/stm32_uart.c | 405 ++++++++++++++++++++++++++++++++ include/drivers/st/stm32_uart.h | 170 ++++++++++++++ 2 files changed, 575 insertions(+) create mode 100644 drivers/st/uart/stm32_uart.c create mode 100644 include/drivers/st/stm32_uart.h diff --git a/drivers/st/uart/stm32_uart.c b/drivers/st/uart/stm32_uart.c new file mode 100644 index 000000000..a0d21dcf3 --- /dev/null +++ b/drivers/st/uart/stm32_uart.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#include <platform_def.h> + +#include <common/bl_common.h> +#include <drivers/delay_timer.h> +#include <drivers/st/stm32mp_clkfunc.h> +#include <drivers/st/stm32_uart.h> +#include <drivers/st/stm32_uart_regs.h> +#include <lib/mmio.h> + +/* UART time-out value */ +#define STM32_UART_TIMEOUT_US 20000U + +/* Mask to clear ALL the configuration register */ + +#define STM32_UART_CR1_FIELDS \ + (USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | \ + USART_CR1_RE | USART_CR1_OVER8 | USART_CR1_FIFOEN) + +#define STM32_UART_CR2_FIELDS \ + (USART_CR2_SLVEN | USART_CR2_DIS_NSS | USART_CR2_ADDM7 | \ + USART_CR2_LBDL | USART_CR2_LBDIE | USART_CR2_LBCL | \ + USART_CR2_CPHA | USART_CR2_CPOL | USART_CR2_CLKEN | \ + USART_CR2_STOP | USART_CR2_LINEN | USART_CR2_SWAP | \ + USART_CR2_RXINV | USART_CR2_TXINV | USART_CR2_DATAINV | \ + USART_CR2_MSBFIRST | USART_CR2_ABREN | USART_CR2_ABRMODE | \ + USART_CR2_RTOEN | USART_CR2_ADD) + +#define STM32_UART_CR3_FIELDS \ + (USART_CR3_EIE | USART_CR3_IREN | USART_CR3_IRLP | \ + USART_CR3_HDSEL | USART_CR3_NACK | USART_CR3_SCEN | \ + USART_CR3_DMAR | USART_CR3_DMAT | USART_CR3_RTSE | \ + USART_CR3_CTSE | USART_CR3_CTSIE | USART_CR3_ONEBIT | \ + USART_CR3_OVRDIS | USART_CR3_DDRE | USART_CR3_DEM | \ + USART_CR3_DEP | USART_CR3_SCARCNT | USART_CR3_WUS | \ + USART_CR3_WUFIE | USART_CR3_TXFTIE | USART_CR3_TCBGTIE | \ + USART_CR3_RXFTCFG | USART_CR3_RXFTIE | USART_CR3_TXFTCFG) + +#define STM32_UART_ISR_ERRORS \ + (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE | USART_ISR_PE) + +static const uint16_t presc_table[STM32_UART_PRESCALER_MAX + 1] = { + 1U, 2U, 4U, 6U, 8U, 10U, 12U, 16U, 32U, 64U, 128U, 256U +}; + +/* @brief BRR division operation to set BRR register in 8-bit oversampling + * mode. + * @param clockfreq: UART clock. + * @param baud_rate: Baud rate set by the user. + * @param prescaler: UART prescaler value. + * @retval Division result. + */ +static uint32_t uart_div_sampling8(unsigned long clockfreq, + uint32_t baud_rate, + uint32_t prescaler) +{ + uint32_t scaled_freq = clockfreq / presc_table[prescaler]; + + return ((scaled_freq * 2) + (baud_rate / 2)) / baud_rate; + +} + +/* @brief BRR division operation to set BRR register in 16-bit oversampling + * mode. + * @param clockfreq: UART clock. + * @param baud_rate: Baud rate set by the user. + * @param prescaler: UART prescaler value. + * @retval Division result. + */ +static uint32_t uart_div_sampling16(unsigned long clockfreq, + uint32_t baud_rate, + uint32_t prescaler) +{ + uint32_t scaled_freq = clockfreq / presc_table[prescaler]; + + return (scaled_freq + (baud_rate / 2)) / baud_rate; + +} + +/* + * @brief Return the UART clock frequency. + * @param huart: UART handle. + * @retval Frequency value in Hz. + */ +static unsigned long uart_get_clock_freq(struct stm32_uart_handle_s *huart) +{ + return fdt_get_uart_clock_freq((uintptr_t)huart->base); +} + +/* + * @brief Configure the UART peripheral. + * @param huart: UART handle. + * @retval UART status. + */ +static int uart_set_config(struct stm32_uart_handle_s *huart, + const struct stm32_uart_init_s *init) +{ + uint32_t tmpreg; + unsigned long clockfreq; + uint32_t brrtemp; + + /* + * ---------------------- USART CR1 Configuration -------------------- + * Clear M, PCE, PS, TE, RE and OVER8 bits and configure + * the UART word length, parity, mode and oversampling: + * - set the M bits according to init->word_length value, + * - set PCE and PS bits according to init->parity value, + * - set TE and RE bits according to init->mode value, + * - set OVER8 bit according to init->over_sampling value. + */ + tmpreg = init->word_length | + init->parity | + init->mode | + init->over_sampling | + init->fifo_mode; + mmio_clrsetbits_32(huart->base + USART_CR1, STM32_UART_CR1_FIELDS, tmpreg); + + /* + * --------------------- USART CR2 Configuration --------------------- + * Configure the UART Stop Bits: Set STOP[13:12] bits according + * to init->stop_bits value. + */ + mmio_clrsetbits_32(huart->base + USART_CR2, STM32_UART_CR2_FIELDS, + init->stop_bits); + + /* + * --------------------- USART CR3 Configuration --------------------- + * Configure: + * - UART HardWare Flow Control: set CTSE and RTSE bits according + * to init->hw_flow_control value, + * - one-bit sampling method versus three samples' majority rule + * according to init->one_bit_sampling (not applicable to + * LPUART), + * - set TXFTCFG bit according to init->tx_fifo_threshold value, + * - set RXFTCFG bit according to init->rx_fifo_threshold value. + */ + tmpreg = init->hw_flow_control | init->one_bit_sampling; + + if (init->fifo_mode == USART_CR1_FIFOEN) { + tmpreg |= init->tx_fifo_threshold | + init->rx_fifo_threshold; + } + + mmio_clrsetbits_32(huart->base + USART_CR3, STM32_UART_CR3_FIELDS, tmpreg); + + /* + * --------------------- USART PRESC Configuration ------------------- + * Configure UART Clock Prescaler : set PRESCALER according to + * init->prescaler value. + */ + assert(init->prescaler <= STM32_UART_PRESCALER_MAX); + mmio_clrsetbits_32(huart->base + USART_PRESC, USART_PRESC_PRESCALER, + init->prescaler); + + /*---------------------- USART BRR configuration --------------------*/ + clockfreq = uart_get_clock_freq(huart); + if (clockfreq == 0UL) { + return -ENODEV; + } + + if (init->over_sampling == STM32_UART_OVERSAMPLING_8) { + uint32_t usartdiv = uart_div_sampling8(clockfreq, + init->baud_rate, + init->prescaler); + + brrtemp = (usartdiv & USART_BRR_DIV_MANTISSA) | + ((usartdiv & USART_BRR_DIV_FRACTION) >> 1); + } else { + brrtemp = uart_div_sampling16(clockfreq, + init->baud_rate, + init->prescaler) & + (USART_BRR_DIV_FRACTION | USART_BRR_DIV_MANTISSA); + } + mmio_write_32(huart->base + USART_BRR, brrtemp); + + return 0U; +} + +/* + * @brief Handle UART communication timeout. + * @param huart: UART handle. + * @param flag: Specifies the UART flag to check. + * @retval UART status. + */ +static int stm32_uart_wait_flag(struct stm32_uart_handle_s *huart, uint32_t flag) +{ + uint64_t timeout_ref = timeout_init_us(STM32_UART_TIMEOUT_US); + + while ((mmio_read_32(huart->base + USART_ISR) & flag) == 0U) { + if (timeout_elapsed(timeout_ref)) { + return -ETIMEDOUT; + } + } + + return 0U; +} + +/* + * @brief Check the UART idle State. + * @param huart: UART handle. + * @retval UART status. + */ +static int stm32_uart_check_idle(struct stm32_uart_handle_s *huart) +{ + int ret; + + /* Check if the transmitter is enabled */ + if ((mmio_read_32(huart->base + USART_CR1) & USART_CR1_TE) == USART_CR1_TE) { + ret = stm32_uart_wait_flag(huart, USART_ISR_TEACK); + if (ret != 0U) { + return ret; + } + } + + /* Check if the receiver is enabled */ + if ((mmio_read_32(huart->base + USART_CR1) & USART_CR1_RE) == USART_CR1_RE) { + ret = stm32_uart_wait_flag(huart, USART_ISR_REACK); + if (ret != 0U) { + return ret; + } + } + + return 0U; +} + +/* + * @brief Compute RDR register mask depending on word length. + * @param huart: UART handle. + * @retval Mask value. + */ +static unsigned int stm32_uart_rdr_mask(const struct stm32_uart_init_s *init) +{ + unsigned int mask; + + switch (init->word_length) { + case STM32_UART_WORDLENGTH_9B: + mask = GENMASK(8, 0); + break; + case STM32_UART_WORDLENGTH_8B: + mask = GENMASK(7, 0); + break; + case STM32_UART_WORDLENGTH_7B: + mask = GENMASK(6, 0); + break; + default: + return 0U; + } + + if (init->parity != STM32_UART_PARITY_NONE) { + mask >>= 1; + } + + return mask; +} + +/* + * @brief Stop the UART. + * @param base: UART base address. + */ +void stm32_uart_stop(uintptr_t base) +{ + mmio_clrbits_32(base + USART_CR1, USART_CR1_UE); +} + +/* + * @brief Initialize UART. + * @param huart: UART handle. + * @param base_addr: base address of UART. + * @param init: UART initialization parameter. + * @retval UART status. + */ + +int stm32_uart_init(struct stm32_uart_handle_s *huart, + uintptr_t base_addr, + const struct stm32_uart_init_s *init) +{ + int ret; + + if (huart == NULL || init == NULL || base_addr == 0U) { + return -EINVAL; + } + + huart->base = base_addr; + + /* Disable the peripheral */ + stm32_uart_stop(huart->base); + + /* Computation of UART mask to apply to RDR register */ + huart->rdr_mask = stm32_uart_rdr_mask(init); + + /* Init the peripheral */ + ret = uart_set_config(huart, init); + if (ret != 0U) { + return ret; + } + + /* Enable the peripheral */ + mmio_setbits_32(huart->base + USART_CR1, USART_CR1_UE); + + /* TEACK and/or REACK to check */ + return stm32_uart_check_idle(huart); +} + +/* + * @brief Check interrupt and status errors. + * @retval True if error detected, false otherwise. + */ +static bool stm32_uart_error_detected(struct stm32_uart_handle_s *huart) +{ + return (mmio_read_32(huart->base + USART_ISR) & STM32_UART_ISR_ERRORS) != 0U; +} + +/* + * @brief Clear status errors. + */ +static void stm32_uart_error_clear(struct stm32_uart_handle_s *huart) +{ + mmio_write_32(huart->base + USART_ICR, STM32_UART_ISR_ERRORS); +} + +/* + * @brief Transmit one data in no blocking mode + * @param huart: UART handle. + * @param c: data to sent. + * @retval UART status. + */ +int stm32_uart_putc(struct stm32_uart_handle_s *huart, int c) +{ + int ret; + + if (huart == NULL) { + return -EINVAL; + } + + ret = stm32_uart_wait_flag(huart, USART_ISR_TXE); + if (ret != 0) { + return ret; + + } + mmio_write_32(huart->base + USART_TDR, c); + if (stm32_uart_error_detected(huart)) { + stm32_uart_error_clear(huart); + return -EFAULT; + } + + return 0U; +} + +/* + * @brief Flush TX Transmit fifo + * @param huart: UART handle. + * @retval UART status. + */ +int stm32_uart_flush(struct stm32_uart_handle_s *huart) +{ + int ret; + + if (huart == NULL) { + return -EINVAL; + } + ret = stm32_uart_wait_flag(huart, USART_ISR_TXE); + if (ret != 0) { + return ret; + + } + ret = stm32_uart_wait_flag(huart, USART_ISR_TC); + + return ret; +} + +/* + * @brief Receive a data in no blocking mode. + * @retval value if >0 or UART status. + */ +int stm32_uart_getc(struct stm32_uart_handle_s *huart) +{ + uint32_t data; + + if (huart == NULL) { + return -EINVAL; + } + + /* check if data is available */ + if ((mmio_read_32(huart->base + USART_ISR) & USART_ISR_RXNE) == 0U) { + return -EAGAIN; + } + + data = mmio_read_32(huart->base + USART_RDR) & huart->rdr_mask; + + if (stm32_uart_error_detected(huart)) { + stm32_uart_error_clear(huart); + return -EFAULT; + } + + return data; +} diff --git a/include/drivers/st/stm32_uart.h b/include/drivers/st/stm32_uart.h new file mode 100644 index 000000000..f00da852f --- /dev/null +++ b/include/drivers/st/stm32_uart.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32_UART_H +#define STM32_UART_H + +/* UART word length */ +#define STM32_UART_WORDLENGTH_7B USART_CR1_M1 +#define STM32_UART_WORDLENGTH_8B 0x00000000U +#define STM32_UART_WORDLENGTH_9B USART_CR1_M0 + +/* UART number of stop bits */ +#define STM32_UART_STOPBITS_0_5 USART_CR2_STOP_0 +#define STM32_UART_STOPBITS_1 0x00000000U +#define STM32_UART_STOPBITS_1_5 (USART_CR2_STOP_0 | USART_CR2_STOP_1) +#define STM32_UART_STOPBITS_2 USART_CR2_STOP_1 + +/* UART parity */ +#define STM32_UART_PARITY_NONE 0x00000000U +#define STM32_UART_PARITY_EVEN USART_CR1_PCE +#define STM32_UART_PARITY_ODD (USART_CR1_PCE | USART_CR1_PS) + +/* UART transfer mode */ +#define STM32_UART_MODE_RX USART_CR1_RE +#define STM32_UART_MODE_TX USART_CR1_TE +#define STM32_UART_MODE_TX_RX (USART_CR1_TE | USART_CR1_RE) + +/* UART hardware flow control */ +#define STM32_UART_HWCONTROL_NONE 0x00000000U +#define STM32_UART_HWCONTROL_RTS USART_CR3_RTSE +#define STM32_UART_HWCONTROL_CTS USART_CR3_CTSE +#define STM32_UART_HWCONTROL_RTS_CTS (USART_CR3_RTSE | USART_CR3_CTSE) + +/* UART over sampling */ +#define STM32_UART_OVERSAMPLING_16 0x00000000U +#define STM32_UART_OVERSAMPLING_8 USART_CR1_OVER8 + +/* UART prescaler */ +#define STM32_UART_PRESCALER_DIV1 0x00000000U +#define STM32_UART_PRESCALER_DIV2 0x00000001U +#define STM32_UART_PRESCALER_DIV4 0x00000002U +#define STM32_UART_PRESCALER_DIV6 0x00000003U +#define STM32_UART_PRESCALER_DIV8 0x00000004U +#define STM32_UART_PRESCALER_DIV10 0x00000005U +#define STM32_UART_PRESCALER_DIV12 0x00000006U +#define STM32_UART_PRESCALER_DIV16 0x00000007U +#define STM32_UART_PRESCALER_DIV32 0x00000008U +#define STM32_UART_PRESCALER_DIV64 0x00000009U +#define STM32_UART_PRESCALER_DIV128 0x0000000AU +#define STM32_UART_PRESCALER_DIV256 0x0000000BU +#define STM32_UART_PRESCALER_MAX STM32_UART_PRESCALER_DIV256 + +/* UART fifo mode */ +#define STM32_UART_FIFOMODE_EN USART_CR1_FIFOEN +#define STM32_UART_FIFOMODE_DIS 0x00000000U + +/* UART TXFIFO threshold level */ +#define STM32_UART_TXFIFO_THRESHOLD_1EIGHTHFULL 0x00000000U +#define STM32_UART_TXFIFO_THRESHOLD_1QUARTERFUL USART_CR3_TXFTCFG_0 +#define STM32_UART_TXFIFO_THRESHOLD_HALFFULL USART_CR3_TXFTCFG_1 +#define STM32_UART_TXFIFO_THRESHOLD_3QUARTERSFULL (USART_CR3_TXFTCFG_0 | USART_CR3_TXFTCFG_1) +#define STM32_UART_TXFIFO_THRESHOLD_7EIGHTHFULL USART_CR3_TXFTCFG_2 +#define STM32_UART_TXFIFO_THRESHOLD_EMPTY (USART_CR3_TXFTCFG_2 | USART_CR3_TXFTCFG_0) + +/* UART RXFIFO threshold level */ +#define STM32_UART_RXFIFO_THRESHOLD_1EIGHTHFULL 0x00000000U +#define STM32_UART_RXFIFO_THRESHOLD_1QUARTERFULL USART_CR3_RXFTCFG_0 +#define STM32_UART_RXFIFO_THRESHOLD_HALFFULL USART_CR3_RXFTCFG_1 +#define STM32_UART_RXFIFO_THRESHOLD_3QUARTERSFULL (USART_CR3_RXFTCFG_0 | USART_CR3_RXFTCFG_1) +#define STM32_UART_RXFIFO_THRESHOLD_7EIGHTHFULL USART_CR3_RXFTCFG_2 +#define STM32_UART_RXFIFO_THRESHOLD_FULL (USART_CR3_RXFTCFG_2 | USART_CR3_RXFTCFG_0) + +struct stm32_uart_init_s { + uint32_t baud_rate; /* + * Configures the UART communication + * baud rate. + */ + + uint32_t word_length; /* + * Specifies the number of data bits + * transmitted or received in a frame. + * This parameter can be a value of + * @ref STM32_UART_WORDLENGTH_*. + */ + + uint32_t stop_bits; /* + * Specifies the number of stop bits + * transmitted. This parameter can be + * a value of @ref STM32_UART_STOPBITS_*. + */ + + uint32_t parity; /* + * Specifies the parity mode. + * This parameter can be a value of + * @ref STM32_UART_PARITY_*. + */ + + uint32_t mode; /* + * Specifies whether the receive or + * transmit mode is enabled or + * disabled. This parameter can be a + * value of @ref @ref STM32_UART_MODE_*. + */ + + uint32_t hw_flow_control; /* + * Specifies whether the hardware flow + * control mode is enabled or + * disabled. This parameter can be a + * value of @ref STM32_UARTHWCONTROL_*. + */ + + uint32_t over_sampling; /* + * Specifies whether the over sampling + * 8 is enabled or disabled. + * This parameter can be a value of + * @ref STM32_UART_OVERSAMPLING_*. + */ + + uint32_t one_bit_sampling; /* + * Specifies whether a single sample + * or three samples' majority vote is + * selected. This parameter can be 0 + * or USART_CR3_ONEBIT. + */ + + uint32_t prescaler; /* + * Specifies the prescaler value used + * to divide the UART clock source. + * This parameter can be a value of + * @ref STM32_UART_PRESCALER_*. + */ + + uint32_t fifo_mode; /* + * Specifies if the FIFO mode will be + * used. This parameter can be a value + * of @ref STM32_UART_FIFOMODE_*. + */ + + uint32_t tx_fifo_threshold; /* + * Specifies the TXFIFO threshold + * level. This parameter can be a + * value of @ref + * STM32_UART_TXFIFO_THRESHOLD_*. + */ + + uint32_t rx_fifo_threshold; /* + * Specifies the RXFIFO threshold + * level. This parameter can be a + * value of @ref + * STM32_UART_RXFIFO_THRESHOLD_*. + */ +}; + +struct stm32_uart_handle_s { + uint32_t base; + uint32_t rdr_mask; +}; + +int stm32_uart_init(struct stm32_uart_handle_s *huart, + uintptr_t base_addr, + const struct stm32_uart_init_s *init); +void stm32_uart_stop(uintptr_t base_addr); +int stm32_uart_putc(struct stm32_uart_handle_s *huart, int c); +int stm32_uart_flush(struct stm32_uart_handle_s *huart); +int stm32_uart_getc(struct stm32_uart_handle_s *huart); + +#endif /* STM32_UART_H */ -- GitLab