| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright (c) 2017-2018, STMicroelectronics |
| */ |
| |
| #include <compiler.h> |
| #include <console.h> |
| #include <drivers/serial.h> |
| #include <drivers/stm32_uart.h> |
| #include <io.h> |
| #include <keep.h> |
| #include <kernel/delay.h> |
| #include <kernel/dt.h> |
| #include <kernel/panic.h> |
| #include <stm32_util.h> |
| #include <util.h> |
| |
| #define UART_REG_CR1 0x00 /* Control register 1 */ |
| #define UART_REG_CR2 0x04 /* Control register 2 */ |
| #define UART_REG_CR3 0x08 /* Control register 3 */ |
| #define UART_REG_BRR 0x0c /* Baud rate register */ |
| #define UART_REG_RQR 0x18 /* Request register */ |
| #define UART_REG_ISR 0x1c /* Interrupt & status reg. */ |
| #define UART_REG_ICR 0x20 /* Interrupt flag clear reg. */ |
| #define UART_REG_RDR 0x24 /* Receive data register */ |
| #define UART_REG_TDR 0x28 /* Transmit data register */ |
| #define UART_REG_PRESC 0x2c /* Prescaler register */ |
| |
| #define PUTC_TIMEOUT_US 1000 |
| #define FLUSH_TIMEOUT_US 16000 |
| |
| /* |
| * Uart Interrupt & status register bits |
| * |
| * Bit 5 RXNE: Read data register not empty/RXFIFO not empty |
| * Bit 6 TC: Transmission complete |
| * Bit 7 TXE/TXFNF: Transmit data register empty/TXFIFO not full |
| * Bit 27 TXFE: TXFIFO threshold reached |
| */ |
| #define USART_ISR_RXNE_RXFNE BIT(5) |
| #define USART_ISR_TC BIT(6) |
| #define USART_ISR_TXE_TXFNF BIT(7) |
| #define USART_ISR_TXFE BIT(27) |
| |
| static vaddr_t loc_chip_to_base(struct serial_chip *chip) |
| { |
| struct stm32_uart_pdata *pd = NULL; |
| |
| pd = container_of(chip, struct stm32_uart_pdata, chip); |
| |
| return io_pa_or_va(&pd->base); |
| } |
| |
| static void loc_flush(struct serial_chip *chip) |
| { |
| vaddr_t base = loc_chip_to_base(chip); |
| uint64_t timeout = timeout_init_us(FLUSH_TIMEOUT_US); |
| |
| while (!(io_read32(base + UART_REG_ISR) & USART_ISR_TXFE)) |
| if (timeout_elapsed(timeout)) |
| return; |
| } |
| |
| static void loc_putc(struct serial_chip *chip, int ch) |
| { |
| vaddr_t base = loc_chip_to_base(chip); |
| uint64_t timeout = timeout_init_us(PUTC_TIMEOUT_US); |
| |
| while (!(io_read32(base + UART_REG_ISR) & USART_ISR_TXE_TXFNF)) |
| if (timeout_elapsed(timeout)) |
| return; |
| |
| io_write32(base + UART_REG_TDR, ch); |
| } |
| |
| static bool loc_have_rx_data(struct serial_chip *chip) |
| { |
| vaddr_t base = loc_chip_to_base(chip); |
| |
| return io_read32(base + UART_REG_ISR) & USART_ISR_RXNE_RXFNE; |
| } |
| |
| static int loc_getchar(struct serial_chip *chip) |
| { |
| vaddr_t base = loc_chip_to_base(chip); |
| |
| while (!loc_have_rx_data(chip)) |
| ; |
| |
| return io_read32(base + UART_REG_RDR) & 0xff; |
| } |
| |
| static const struct serial_ops stm32_uart_serial_ops = { |
| .flush = loc_flush, |
| .putc = loc_putc, |
| .have_rx_data = loc_have_rx_data, |
| .getchar = loc_getchar, |
| |
| }; |
| KEEP_PAGER(stm32_uart_serial_ops); |
| |
| void stm32_uart_init(struct stm32_uart_pdata *pd, vaddr_t base) |
| { |
| pd->base.pa = base; |
| pd->chip.ops = &stm32_uart_serial_ops; |
| } |
| |
| #ifdef CFG_DT |
| static void register_secure_uart(struct stm32_uart_pdata *pd) |
| { |
| size_t n = 0; |
| |
| stm32mp_register_secure_periph_iomem(pd->base.pa); |
| for (n = 0; n < pd->pinctrl_count; n++) |
| stm32mp_register_secure_gpio(pd->pinctrl[n].bank, |
| pd->pinctrl[n].pin); |
| } |
| |
| static void register_non_secure_uart(struct stm32_uart_pdata *pd) |
| { |
| size_t n = 0; |
| |
| stm32mp_register_non_secure_periph_iomem(pd->base.pa); |
| for (n = 0; n < pd->pinctrl_count; n++) |
| stm32mp_register_non_secure_gpio(pd->pinctrl[n].bank, |
| pd->pinctrl[n].pin); |
| } |
| |
| struct stm32_uart_pdata *stm32_uart_init_from_dt_node(void *fdt, int node) |
| { |
| struct stm32_uart_pdata *pd = NULL; |
| struct dt_node_info info = { }; |
| struct stm32_pinctrl *pinctrl_cfg = NULL; |
| int count = 0; |
| |
| _fdt_fill_device_info(fdt, &info, node); |
| |
| if (info.status == DT_STATUS_DISABLED) |
| return NULL; |
| |
| assert(info.clock != DT_INFO_INVALID_CLOCK && |
| info.reg != DT_INFO_INVALID_REG); |
| |
| pd = calloc(1, sizeof(*pd)); |
| if (!pd) |
| panic(); |
| |
| pd->chip.ops = &stm32_uart_serial_ops; |
| pd->base.pa = info.reg; |
| pd->secure = (info.status == DT_STATUS_OK_SEC); |
| pd->clock = (unsigned int)info.clock; |
| |
| assert(cpu_mmu_enabled()); |
| pd->base.va = (vaddr_t)phys_to_virt(pd->base.pa, |
| pd->secure ? MEM_AREA_IO_SEC : |
| MEM_AREA_IO_NSEC); |
| |
| count = stm32_pinctrl_fdt_get_pinctrl(fdt, node, NULL, 0); |
| if (count < 0) |
| panic(); |
| |
| if (count) { |
| pinctrl_cfg = calloc(count, sizeof(*pinctrl_cfg)); |
| if (!pinctrl_cfg) |
| panic(); |
| |
| stm32_pinctrl_fdt_get_pinctrl(fdt, node, pinctrl_cfg, count); |
| stm32_pinctrl_load_active_cfg(pinctrl_cfg, count); |
| } |
| pd->pinctrl = pinctrl_cfg; |
| pd->pinctrl_count = count; |
| |
| if (pd->secure) |
| register_secure_uart(pd); |
| else |
| register_non_secure_uart(pd); |
| |
| return pd; |
| } |
| #endif /*CFG_DT*/ |