serial: Add RISC-V HTIF console driver
Quite a few RISC-V emulators and ISS (including Spike) have host transfer interface (HTIF) based console. This patch adds HTIF based console driver for RISC-V platforms which depends totally on DT node for HTIF register base address. Signed-off-by: Anup Patel <apatel@ventanamicro.com> Reviewed-by: Philipp Tomsich <philipp.tomsich@vrull.eu> Reviewed-by: Rick Chen <rick@andestech.com> Tested-by: Bin Meng <bmeng.cn@gmail.com>
This commit is contained in:
parent
531c008945
commit
d6ba787e32
@ -866,6 +866,14 @@ 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 HTIF_CONSOLE
|
||||
bool "RISC-V HTIF console support"
|
||||
depends on DM_SERIAL && 64BIT
|
||||
help
|
||||
Select this to enable host transfer interface (HTIF) based serial
|
||||
console. The HTIF device is quite common in RISC-V emulators and
|
||||
RISC-V ISS so this driver allows using U-Boot on such platforms.
|
||||
|
||||
config SIFIVE_SERIAL
|
||||
bool "SiFive UART support"
|
||||
depends on DM_SERIAL
|
||||
|
@ -73,6 +73,7 @@ obj-$(CONFIG_OWL_SERIAL) += serial_owl.o
|
||||
obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o
|
||||
obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o
|
||||
obj-$(CONFIG_MT7620_SERIAL) += serial_mt7620.o
|
||||
obj-$(CONFIG_HTIF_CONSOLE) += serial_htif.o
|
||||
obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o
|
||||
obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
|
||||
|
||||
|
178
drivers/serial/serial_htif.c
Normal file
178
drivers/serial/serial_htif.c
Normal file
@ -0,0 +1,178 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2022 Ventana Micro Systems Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <log.h>
|
||||
#include <watchdog.h>
|
||||
#include <asm/global_data.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <serial.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define HTIF_DATA_BITS 48
|
||||
#define HTIF_DATA_MASK ((1ULL << HTIF_DATA_BITS) - 1)
|
||||
#define HTIF_DATA_SHIFT 0
|
||||
#define HTIF_CMD_BITS 8
|
||||
#define HTIF_CMD_MASK ((1ULL << HTIF_CMD_BITS) - 1)
|
||||
#define HTIF_CMD_SHIFT 48
|
||||
#define HTIF_DEV_BITS 8
|
||||
#define HTIF_DEV_MASK ((1ULL << HTIF_DEV_BITS) - 1)
|
||||
#define HTIF_DEV_SHIFT 56
|
||||
|
||||
#define HTIF_DEV_SYSTEM 0
|
||||
#define HTIF_DEV_CONSOLE 1
|
||||
|
||||
#define HTIF_CONSOLE_CMD_GETC 0
|
||||
#define HTIF_CONSOLE_CMD_PUTC 1
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
# define TOHOST_CMD(dev, cmd, payload) \
|
||||
(((u64)(dev) << HTIF_DEV_SHIFT) | \
|
||||
((u64)(cmd) << HTIF_CMD_SHIFT) | \
|
||||
(u64)(payload))
|
||||
#else
|
||||
# define TOHOST_CMD(dev, cmd, payload) ({ \
|
||||
if ((dev) || (cmd)) \
|
||||
__builtin_trap(); \
|
||||
(payload); })
|
||||
#endif
|
||||
#define FROMHOST_DEV(fromhost_value) \
|
||||
((u64)((fromhost_value) >> HTIF_DEV_SHIFT) & HTIF_DEV_MASK)
|
||||
#define FROMHOST_CMD(fromhost_value) \
|
||||
((u64)((fromhost_value) >> HTIF_CMD_SHIFT) & HTIF_CMD_MASK)
|
||||
#define FROMHOST_DATA(fromhost_value) \
|
||||
((u64)((fromhost_value) >> HTIF_DATA_SHIFT) & HTIF_DATA_MASK)
|
||||
|
||||
struct htif_plat {
|
||||
void *fromhost;
|
||||
void *tohost;
|
||||
int console_char;
|
||||
};
|
||||
|
||||
static void __check_fromhost(struct htif_plat *plat)
|
||||
{
|
||||
u64 fh = readq(plat->fromhost);
|
||||
|
||||
if (!fh)
|
||||
return;
|
||||
writeq(0, plat->fromhost);
|
||||
|
||||
/* this should be from the console */
|
||||
if (FROMHOST_DEV(fh) != HTIF_DEV_CONSOLE)
|
||||
__builtin_trap();
|
||||
switch (FROMHOST_CMD(fh)) {
|
||||
case HTIF_CONSOLE_CMD_GETC:
|
||||
plat->console_char = 1 + (u8)FROMHOST_DATA(fh);
|
||||
break;
|
||||
case HTIF_CONSOLE_CMD_PUTC:
|
||||
break;
|
||||
default:
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
|
||||
static void __set_tohost(struct htif_plat *plat,
|
||||
u64 dev, u64 cmd, u64 data)
|
||||
{
|
||||
while (readq(plat->tohost))
|
||||
__check_fromhost(plat);
|
||||
writeq(TOHOST_CMD(dev, cmd, data), plat->tohost);
|
||||
}
|
||||
|
||||
static int htif_serial_putc(struct udevice *dev, const char ch)
|
||||
{
|
||||
struct htif_plat *plat = dev_get_plat(dev);
|
||||
|
||||
__set_tohost(plat, HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_PUTC, ch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int htif_serial_getc(struct udevice *dev)
|
||||
{
|
||||
int ch;
|
||||
struct htif_plat *plat = dev_get_plat(dev);
|
||||
|
||||
if (plat->console_char < 0)
|
||||
__check_fromhost(plat);
|
||||
|
||||
if (plat->console_char >= 0) {
|
||||
ch = plat->console_char;
|
||||
plat->console_char = -1;
|
||||
__set_tohost(plat, HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_GETC, 0);
|
||||
return (ch) ? ch - 1 : -EAGAIN;
|
||||
}
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int htif_serial_pending(struct udevice *dev, bool input)
|
||||
{
|
||||
struct htif_plat *plat = dev_get_plat(dev);
|
||||
|
||||
if (!input)
|
||||
return 0;
|
||||
|
||||
if (plat->console_char < 0)
|
||||
__check_fromhost(plat);
|
||||
|
||||
return (plat->console_char >= 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int htif_serial_probe(struct udevice *dev)
|
||||
{
|
||||
struct htif_plat *plat = dev_get_plat(dev);
|
||||
|
||||
/* Queue first getc request */
|
||||
__set_tohost(plat, HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_GETC, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int htif_serial_of_to_plat(struct udevice *dev)
|
||||
{
|
||||
fdt_addr_t addr;
|
||||
struct htif_plat *plat = dev_get_plat(dev);
|
||||
|
||||
addr = dev_read_addr_index(dev, 0);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -ENODEV;
|
||||
plat->fromhost = (void *)(uintptr_t)addr;
|
||||
plat->tohost = plat->fromhost + sizeof(u64);
|
||||
|
||||
addr = dev_read_addr_index(dev, 1);
|
||||
if (addr != FDT_ADDR_T_NONE)
|
||||
plat->tohost = (void *)(uintptr_t)addr;
|
||||
|
||||
plat->console_char = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_serial_ops htif_serial_ops = {
|
||||
.putc = htif_serial_putc,
|
||||
.getc = htif_serial_getc,
|
||||
.pending = htif_serial_pending,
|
||||
};
|
||||
|
||||
static const struct udevice_id htif_serial_ids[] = {
|
||||
{ .compatible = "ucb,htif0" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(serial_htif) = {
|
||||
.name = "serial_htif",
|
||||
.id = UCLASS_SERIAL,
|
||||
.of_match = htif_serial_ids,
|
||||
.of_to_plat = htif_serial_of_to_plat,
|
||||
.plat_auto = sizeof(struct htif_plat),
|
||||
.probe = htif_serial_probe,
|
||||
.ops = &htif_serial_ops,
|
||||
};
|
Loading…
Reference in New Issue
Block a user