blob: b7901f36a7e7f7f0a39ae3c8ffc95c729a341909 [file] [log] [blame]
// 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*/