serial: Add RISC-V HTIF console driver
authorAnup Patel <apatel@ventanamicro.com>
Thu, 27 Jan 2022 06:11:07 +0000 (11:41 +0530)
committerLeo Yu-Chi Liang <ycliang@andestech.com>
Thu, 10 Feb 2022 03:18:12 +0000 (11:18 +0800)
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>
drivers/serial/Kconfig
drivers/serial/Makefile
drivers/serial/serial_htif.c [new file with mode: 0644]

index 6c8fdda..345d188 100644 (file)
@@ -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
index 8168af6..52e70aa 100644 (file)
@@ -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
 
diff --git a/drivers/serial/serial_htif.c b/drivers/serial/serial_htif.c
new file mode 100644 (file)
index 0000000..5d2bf0a
--- /dev/null
@@ -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,
+};