mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-30 08:03:32 +08:00
drivers: serial: Add SiFive UART driver
This patch adds SiFive UART driver. The driver is 100% DM driver and it determines input clock using clk framework. Signed-off-by: Anup Patel <anup@brainfault.org> Reviewed-by: Palmer Dabbelt <palmer@sifive.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com> Tested-by: Bin Meng <bmeng.cn@gmail.com>
This commit is contained in:
parent
dcad9b8d66
commit
e2842496ac
@ -343,6 +343,13 @@ config DEBUG_UART_SANDBOX
|
||||
start up driver model. The driver will be available until the real
|
||||
driver model serial is running.
|
||||
|
||||
config DEBUG_UART_SIFIVE
|
||||
bool "SiFive UART"
|
||||
help
|
||||
Select this to enable a debug UART using the serial_sifive driver. You
|
||||
will need to provide parameters to make this work. The driver will
|
||||
be available until the real driver-model serial is running.
|
||||
|
||||
config DEBUG_UART_STM32
|
||||
bool "STMicroelectronics STM32"
|
||||
depends on STM32_SERIAL
|
||||
@ -679,6 +686,12 @@ config PXA_SERIAL
|
||||
If you have a machine based on a Marvell XScale PXA2xx CPU you
|
||||
can enable its onboard serial ports by enabling this option.
|
||||
|
||||
config SIFIVE_SERIAL
|
||||
bool "SiFive UART support"
|
||||
depends on DM_SERIAL
|
||||
help
|
||||
This driver supports the SiFive UART. If unsure say N.
|
||||
|
||||
config STI_ASC_SERIAL
|
||||
bool "STMicroelectronics on-chip UART"
|
||||
depends on DM_SERIAL && ARCH_STI
|
||||
|
@ -67,6 +67,7 @@ obj-$(CONFIG_NULLDEV_SERIAL) += serial_nulldev.o
|
||||
obj-$(CONFIG_OWL_SERIAL) += serial_owl.o
|
||||
obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o
|
||||
obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o
|
||||
obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o
|
||||
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_USB_TTY) += usbtty.o
|
||||
|
215
drivers/serial/serial_sifive.c
Normal file
215
drivers/serial/serial_sifive.c
Normal file
@ -0,0 +1,215 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2018 Anup Patel <anup@brainfault.org>
|
||||
*/
|
||||
|
||||
#include <clk.h>
|
||||
#include <common.h>
|
||||
#include <debug_uart.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <watchdog.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <serial.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define UART_TXFIFO_FULL 0x80000000
|
||||
#define UART_RXFIFO_EMPTY 0x80000000
|
||||
#define UART_RXFIFO_DATA 0x000000ff
|
||||
#define UART_TXCTRL_TXEN 0x1
|
||||
#define UART_RXCTRL_RXEN 0x1
|
||||
|
||||
struct uart_sifive {
|
||||
u32 txfifo;
|
||||
u32 rxfifo;
|
||||
u32 txctrl;
|
||||
u32 rxctrl;
|
||||
u32 ie;
|
||||
u32 ip;
|
||||
u32 div;
|
||||
};
|
||||
|
||||
struct sifive_uart_platdata {
|
||||
unsigned int clock;
|
||||
int saved_input_char;
|
||||
struct uart_sifive *regs;
|
||||
};
|
||||
|
||||
/* Set up the baud rate in gd struct */
|
||||
static void _sifive_serial_setbrg(struct uart_sifive *regs,
|
||||
unsigned long clock, unsigned long baud)
|
||||
{
|
||||
writel((u32)((clock / baud) - 1), ®s->div);
|
||||
}
|
||||
|
||||
static void _sifive_serial_init(struct uart_sifive *regs)
|
||||
{
|
||||
writel(UART_TXCTRL_TXEN, ®s->txctrl);
|
||||
writel(UART_RXCTRL_RXEN, ®s->rxctrl);
|
||||
writel(0, ®s->ie);
|
||||
}
|
||||
|
||||
static int _sifive_serial_putc(struct uart_sifive *regs, const char c)
|
||||
{
|
||||
if (readl(®s->txfifo) & UART_TXFIFO_FULL)
|
||||
return -EAGAIN;
|
||||
|
||||
writel(c, ®s->txfifo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sifive_serial_getc(struct uart_sifive *regs)
|
||||
{
|
||||
int ch = readl(®s->rxfifo);
|
||||
|
||||
if (ch & UART_RXFIFO_EMPTY)
|
||||
return -EAGAIN;
|
||||
ch &= UART_RXFIFO_DATA;
|
||||
|
||||
return (!ch) ? -EAGAIN : ch;
|
||||
}
|
||||
|
||||
static int sifive_serial_setbrg(struct udevice *dev, int baudrate)
|
||||
{
|
||||
int err;
|
||||
struct clk clk;
|
||||
struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
|
||||
|
||||
err = clk_get_by_index(dev, 0, &clk);
|
||||
if (!err) {
|
||||
err = clk_get_rate(&clk);
|
||||
if (!IS_ERR_VALUE(err))
|
||||
platdata->clock = err;
|
||||
} else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) {
|
||||
debug("SiFive UART failed to get clock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!platdata->clock)
|
||||
platdata->clock = dev_read_u32_default(dev, "clock-frequency", 0);
|
||||
if (!platdata->clock) {
|
||||
debug("SiFive UART clock not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
_sifive_serial_setbrg(platdata->regs, platdata->clock, baudrate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_serial_probe(struct udevice *dev)
|
||||
{
|
||||
struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
|
||||
|
||||
/* No need to reinitialize the UART after relocation */
|
||||
if (gd->flags & GD_FLG_RELOC)
|
||||
return 0;
|
||||
|
||||
platdata->saved_input_char = 0;
|
||||
_sifive_serial_init(platdata->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_serial_getc(struct udevice *dev)
|
||||
{
|
||||
int c;
|
||||
struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
|
||||
struct uart_sifive *regs = platdata->regs;
|
||||
|
||||
if (platdata->saved_input_char > 0) {
|
||||
c = platdata->saved_input_char;
|
||||
platdata->saved_input_char = 0;
|
||||
return c;
|
||||
}
|
||||
|
||||
while ((c = _sifive_serial_getc(regs)) == -EAGAIN) ;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static int sifive_serial_putc(struct udevice *dev, const char ch)
|
||||
{
|
||||
int rc;
|
||||
struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
|
||||
|
||||
while ((rc = _sifive_serial_putc(platdata->regs, ch)) == -EAGAIN) ;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sifive_serial_pending(struct udevice *dev, bool input)
|
||||
{
|
||||
struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
|
||||
struct uart_sifive *regs = platdata->regs;
|
||||
|
||||
if (input) {
|
||||
if (platdata->saved_input_char > 0)
|
||||
return 1;
|
||||
platdata->saved_input_char = _sifive_serial_getc(regs);
|
||||
return (platdata->saved_input_char > 0) ? 1 : 0;
|
||||
} else {
|
||||
return !!(readl(®s->txfifo) & UART_TXFIFO_FULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int sifive_serial_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
|
||||
|
||||
platdata->regs = (struct uart_sifive *)dev_read_addr(dev);
|
||||
if (IS_ERR(platdata->regs))
|
||||
return PTR_ERR(platdata->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_serial_ops sifive_serial_ops = {
|
||||
.putc = sifive_serial_putc,
|
||||
.getc = sifive_serial_getc,
|
||||
.pending = sifive_serial_pending,
|
||||
.setbrg = sifive_serial_setbrg,
|
||||
};
|
||||
|
||||
static const struct udevice_id sifive_serial_ids[] = {
|
||||
{ .compatible = "sifive,uart0" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(serial_sifive) = {
|
||||
.name = "serial_sifive",
|
||||
.id = UCLASS_SERIAL,
|
||||
.of_match = sifive_serial_ids,
|
||||
.ofdata_to_platdata = sifive_serial_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct sifive_uart_platdata),
|
||||
.probe = sifive_serial_probe,
|
||||
.ops = &sifive_serial_ops,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_UART_SIFIVE
|
||||
static inline void _debug_uart_init(void)
|
||||
{
|
||||
struct uart_sifive *regs =
|
||||
(struct uart_sifive *)CONFIG_DEBUG_UART_BASE;
|
||||
|
||||
_sifive_serial_setbrg(regs, CONFIG_DEBUG_UART_CLOCK,
|
||||
CONFIG_BAUDRATE);
|
||||
_sifive_serial_init(regs);
|
||||
}
|
||||
|
||||
static inline void _debug_uart_putc(int ch)
|
||||
{
|
||||
struct uart_sifive *regs =
|
||||
(struct uart_sifive *)CONFIG_DEBUG_UART_BASE;
|
||||
|
||||
while (_sifive_serial_putc(regs, ch) == -EAGAIN)
|
||||
WATCHDOG_RESET();
|
||||
}
|
||||
|
||||
DEBUG_UART_FUNCS
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user