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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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