123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- /*
- * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #include <assert.h>
- #include <errno.h>
- #include <string.h>
- #include <common/bl_common.h>
- #include <drivers/clk.h>
- #include <drivers/delay_timer.h>
- #include <drivers/st/stm32_gpio.h>
- #include <drivers/st/stm32_uart.h>
- #include <drivers/st/stm32_uart_regs.h>
- #include <drivers/st/stm32mp_clkfunc.h>
- #include <lib/mmio.h>
- #include <platform_def.h>
- /* UART time-out value */
- #define STM32_UART_TIMEOUT_US 20000U
- /* Mask to clear ALL the configuration registers */
- #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_NB] = {
- 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;
- unsigned long int_div;
- uint32_t brrtemp;
- uint32_t over_sampling;
- /*---------------------- USART BRR configuration --------------------*/
- clockfreq = uart_get_clock_freq(huart);
- if (clockfreq == 0UL) {
- return -ENODEV;
- }
- int_div = clockfreq / init->baud_rate;
- if (int_div < 16U) {
- uint32_t usartdiv = uart_div_sampling8(clockfreq,
- init->baud_rate,
- init->prescaler);
- brrtemp = (usartdiv & USART_BRR_DIV_MANTISSA) |
- ((usartdiv & USART_BRR_DIV_FRACTION) >> 1);
- over_sampling = USART_CR1_OVER8;
- } else {
- brrtemp = uart_div_sampling16(clockfreq,
- init->baud_rate,
- init->prescaler) &
- (USART_BRR_DIV_FRACTION | USART_BRR_DIV_MANTISSA);
- over_sampling = 0x0U;
- }
- mmio_write_32(huart->base + USART_BRR, 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 baudrate and clock.
- */
- tmpreg = init->word_length |
- init->parity |
- init->mode |
- 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_NB);
- mmio_clrsetbits_32(huart->base + USART_PRESC, USART_PRESC_PRESCALER,
- init->prescaler);
- return 0;
- }
- /*
- * @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 0;
- }
- /*
- * @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 != 0) {
- 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 != 0) {
- return ret;
- }
- }
- return 0;
- }
- /*
- * @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 = 0U;
- 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:
- break; /* not reached */
- }
- if (init->parity != STM32_UART_PARITY_NONE) {
- mask >>= 1;
- }
- return mask;
- }
- /*
- * @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 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;
- int uart_node;
- int clk;
- void *fdt = NULL;
- if (huart == NULL || init == NULL || base_addr == 0U) {
- return -EINVAL;
- }
- huart->base = base_addr;
- /* Search UART instance in DT */
- if (fdt_get_address(&fdt) == 0) {
- return -FDT_ERR_NOTFOUND;
- }
- if (fdt == NULL) {
- return -FDT_ERR_NOTFOUND;
- }
- uart_node = dt_match_instance_by_compatible(DT_UART_COMPAT, base_addr);
- if (uart_node == -FDT_ERR_NOTFOUND) {
- return -FDT_ERR_NOTFOUND;
- }
- /* Pinctrl initialization */
- if (dt_set_pinctrl_config(uart_node) != 0) {
- return -FDT_ERR_BADVALUE;
- }
- /* Clock initialization */
- clk = fdt_get_clock_id(uart_node);
- if (clk < 0) {
- return -FDT_ERR_NOTFOUND;
- }
- clk_enable(clk);
- /* 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 != 0) {
- 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 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 0;
- }
- /*
- * @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;
- }
- return stm32_uart_wait_flag(huart, USART_ISR_TC);
- }
- /*
- * @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 (int)data;
- }
|