mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2025-08-25 15:51:41 +01:00

Add Cadence UART driver Signed-off-by: Jun Liang Tan <junliang.tan@linux.starfivetech.com> Signed-off-by: Wei Liang Lim <weiliang.lim@linux.starfivetech.com> Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Reviewed-by: Anup Patel <anup@brainfault.org>
129 lines
2.8 KiB
C
129 lines
2.8 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2022 StarFive Technology Co., Ltd.
|
|
*
|
|
* Author: Jun Liang Tan <junliang.tan@linux.starfivetech.com>
|
|
*/
|
|
|
|
#include <sbi/riscv_io.h>
|
|
#include <sbi/sbi_console.h>
|
|
#include <sbi_utils/serial/cadence-uart.h>
|
|
|
|
/* clang-format off */
|
|
|
|
#define UART_REG_CTRL 0x00
|
|
#define UART_REG_MODE 0x04
|
|
#define UART_REG_IDR 0x0C
|
|
#define UART_REG_BRGR 0x18
|
|
#define UART_REG_CSR 0x2C
|
|
#define UART_REG_RFIFO_TFIFO 0x30
|
|
#define UART_REG_BDIVR 0x34
|
|
|
|
#define UART_CTRL_RXRES 0x00000001
|
|
#define UART_CTRL_TXRES 0x00000002
|
|
#define UART_CTRL_RXEN 0x00000004
|
|
#define UART_CTRL_RXDIS 0x00000008
|
|
#define UART_CTRL_TXEN 0x00000010
|
|
#define UART_CTRL_TXDIS 0x00000020
|
|
|
|
#define UART_MODE_PAR_NONE 0x00000020 /* No parity set */
|
|
|
|
#define UART_BRGR_CD_CLKDIVISOR 0x00000001 /* baud_sample = sel_clk */
|
|
|
|
#define UART_CSR_REMPTY 0x00000002
|
|
#define UART_CSR_TFUL 0x00000010
|
|
|
|
/* clang-format on */
|
|
|
|
static volatile void *uart_base;
|
|
static u32 uart_in_freq;
|
|
static u32 uart_baudrate;
|
|
|
|
/*
|
|
* Find minimum divisor divides in_freq to max_target_hz;
|
|
* Based on SiFive UART driver (sifive-uart.c)
|
|
*/
|
|
static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
|
|
uint64_t max_target_hz)
|
|
{
|
|
uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
|
|
|
|
/* Avoid underflow */
|
|
if (quotient == 0)
|
|
return 0;
|
|
else
|
|
return quotient - 1;
|
|
}
|
|
|
|
static u32 get_reg(u32 offset)
|
|
{
|
|
return readl(uart_base + offset);
|
|
}
|
|
|
|
static void set_reg(u32 offset, u32 val)
|
|
{
|
|
writel(val, uart_base + offset);
|
|
}
|
|
|
|
static void cadence_uart_putc(char ch)
|
|
{
|
|
while (get_reg(UART_REG_CSR) & UART_CSR_TFUL)
|
|
;
|
|
|
|
set_reg(UART_REG_RFIFO_TFIFO, ch);
|
|
}
|
|
|
|
static int cadence_uart_getc(void)
|
|
{
|
|
u32 ret = get_reg(UART_REG_CSR);
|
|
|
|
if (!(ret & UART_CSR_REMPTY))
|
|
return get_reg(UART_REG_RFIFO_TFIFO);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static struct sbi_console_device cadence_console = {
|
|
.name = "cadence_uart",
|
|
.console_putc = cadence_uart_putc,
|
|
.console_getc = cadence_uart_getc
|
|
};
|
|
|
|
int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
|
|
{
|
|
uart_base = (volatile void *)base;
|
|
uart_in_freq = in_freq;
|
|
uart_baudrate = baudrate;
|
|
|
|
/* Disable interrupts */
|
|
set_reg(UART_REG_IDR, 0xFFFFFFFF);
|
|
|
|
/* Disable TX RX */
|
|
set_reg(UART_REG_CTRL, UART_CTRL_TXDIS | UART_CTRL_RXDIS);
|
|
|
|
/* Configure baudrate */
|
|
if (in_freq && baudrate) {
|
|
set_reg(UART_REG_BRGR, UART_BRGR_CD_CLKDIVISOR);
|
|
set_reg(UART_REG_BDIVR,
|
|
uart_min_clk_divisor(in_freq, baudrate));
|
|
}
|
|
|
|
/* Software reset TX RX data path and enable TX RX */
|
|
set_reg(UART_REG_CTRL, UART_CTRL_TXRES | UART_CTRL_RXRES
|
|
| UART_CTRL_TXEN | UART_CTRL_RXEN);
|
|
|
|
/*
|
|
* Set:
|
|
* 1 stop bit, bits[07:06] = 0x00,
|
|
* no parity set, bits[05:03] = 0x100,
|
|
* 8 bits character length, bits[02:01] = 0x00,
|
|
* sel_clk = uart_clk, bit[0] = 0x0
|
|
*/
|
|
set_reg(UART_REG_MODE, UART_MODE_PAR_NONE);
|
|
|
|
sbi_console_set_device(&cadence_console);
|
|
|
|
return 0;
|
|
}
|