1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2022 Ventana Micro Systems Inc.
4  */
5 
6 #include <common.h>
7 #include <dm.h>
8 #include <errno.h>
9 #include <fdtdec.h>
10 #include <log.h>
11 #include <watchdog.h>
12 #include <asm/global_data.h>
13 #include <asm/io.h>
14 #include <linux/compiler.h>
15 #include <serial.h>
16 #include <linux/err.h>
17 
18 DECLARE_GLOBAL_DATA_PTR;
19 
20 #define HTIF_DATA_BITS		48
21 #define HTIF_DATA_MASK		((1ULL << HTIF_DATA_BITS) - 1)
22 #define HTIF_DATA_SHIFT		0
23 #define HTIF_CMD_BITS		8
24 #define HTIF_CMD_MASK		((1ULL << HTIF_CMD_BITS) - 1)
25 #define HTIF_CMD_SHIFT		48
26 #define HTIF_DEV_BITS		8
27 #define HTIF_DEV_MASK		((1ULL << HTIF_DEV_BITS) - 1)
28 #define HTIF_DEV_SHIFT		56
29 
30 #define HTIF_DEV_SYSTEM		0
31 #define HTIF_DEV_CONSOLE	1
32 
33 #define HTIF_CONSOLE_CMD_GETC	0
34 #define HTIF_CONSOLE_CMD_PUTC	1
35 
36 #if __riscv_xlen == 64
37 # define TOHOST_CMD(dev, cmd, payload) \
38 	(((u64)(dev) << HTIF_DEV_SHIFT) | \
39 	 ((u64)(cmd) << HTIF_CMD_SHIFT) | \
40 	 (u64)(payload))
41 #else
42 # define TOHOST_CMD(dev, cmd, payload) ({ \
43 	if ((dev) || (cmd)) \
44 		__builtin_trap(); \
45 	(payload); })
46 #endif
47 #define FROMHOST_DEV(fromhost_value) \
48 	((u64)((fromhost_value) >> HTIF_DEV_SHIFT) & HTIF_DEV_MASK)
49 #define FROMHOST_CMD(fromhost_value) \
50 	((u64)((fromhost_value) >> HTIF_CMD_SHIFT) & HTIF_CMD_MASK)
51 #define FROMHOST_DATA(fromhost_value) \
52 	((u64)((fromhost_value) >> HTIF_DATA_SHIFT) & HTIF_DATA_MASK)
53 
54 struct htif_plat {
55 	void *fromhost;
56 	void *tohost;
57 	int console_char;
58 };
59 
__check_fromhost(struct htif_plat * plat)60 static void __check_fromhost(struct htif_plat *plat)
61 {
62 	u64 fh = readq(plat->fromhost);
63 
64 	if (!fh)
65 		return;
66 	writeq(0, plat->fromhost);
67 
68 	/* this should be from the console */
69 	if (FROMHOST_DEV(fh) != HTIF_DEV_CONSOLE)
70 		__builtin_trap();
71 	switch (FROMHOST_CMD(fh)) {
72 	case HTIF_CONSOLE_CMD_GETC:
73 		plat->console_char = 1 + (u8)FROMHOST_DATA(fh);
74 		break;
75 	case HTIF_CONSOLE_CMD_PUTC:
76 		break;
77 	default:
78 		__builtin_trap();
79 	}
80 }
81 
__set_tohost(struct htif_plat * plat,u64 dev,u64 cmd,u64 data)82 static void __set_tohost(struct htif_plat *plat,
83 			 u64 dev, u64 cmd, u64 data)
84 {
85 	while (readq(plat->tohost))
86 		__check_fromhost(plat);
87 	writeq(TOHOST_CMD(dev, cmd, data), plat->tohost);
88 }
89 
htif_serial_putc(struct udevice * dev,const char ch)90 static int htif_serial_putc(struct udevice *dev, const char ch)
91 {
92 	struct htif_plat *plat = dev_get_plat(dev);
93 
94 	__set_tohost(plat, HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_PUTC, ch);
95 	return 0;
96 }
97 
htif_serial_getc(struct udevice * dev)98 static int htif_serial_getc(struct udevice *dev)
99 {
100 	int ch;
101 	struct htif_plat *plat = dev_get_plat(dev);
102 
103 	if (plat->console_char < 0)
104 		__check_fromhost(plat);
105 
106 	if (plat->console_char >= 0) {
107 		ch = plat->console_char;
108 		plat->console_char = -1;
109 		__set_tohost(plat, HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_GETC, 0);
110 		return (ch) ? ch - 1 : -EAGAIN;
111 	}
112 
113 	return -EAGAIN;
114 }
115 
htif_serial_pending(struct udevice * dev,bool input)116 static int htif_serial_pending(struct udevice *dev, bool input)
117 {
118 	struct htif_plat *plat = dev_get_plat(dev);
119 
120 	if (!input)
121 		return 0;
122 
123 	if (plat->console_char < 0)
124 		__check_fromhost(plat);
125 
126 	return (plat->console_char >= 0) ? 1 : 0;
127 }
128 
htif_serial_probe(struct udevice * dev)129 static int htif_serial_probe(struct udevice *dev)
130 {
131 	struct htif_plat *plat = dev_get_plat(dev);
132 
133 	/* Queue first getc request */
134 	__set_tohost(plat, HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_GETC, 0);
135 
136 	return 0;
137 }
138 
htif_serial_of_to_plat(struct udevice * dev)139 static int htif_serial_of_to_plat(struct udevice *dev)
140 {
141 	fdt_addr_t addr;
142 	struct htif_plat *plat = dev_get_plat(dev);
143 
144 	addr = dev_read_addr_index(dev, 0);
145 	if (addr == FDT_ADDR_T_NONE)
146 		return -ENODEV;
147 	plat->fromhost = (void *)(uintptr_t)addr;
148 	plat->tohost = plat->fromhost + sizeof(u64);
149 
150 	addr = dev_read_addr_index(dev, 1);
151 	if (addr != FDT_ADDR_T_NONE)
152 		plat->tohost = (void *)(uintptr_t)addr;
153 
154 	plat->console_char = -1;
155 
156 	return 0;
157 }
158 
159 static const struct dm_serial_ops htif_serial_ops = {
160 	.putc = htif_serial_putc,
161 	.getc = htif_serial_getc,
162 	.pending = htif_serial_pending,
163 };
164 
165 static const struct udevice_id htif_serial_ids[] = {
166 	{ .compatible = "ucb,htif0" },
167 	{ }
168 };
169 
170 U_BOOT_DRIVER(serial_htif) = {
171 	.name		= "serial_htif",
172 	.id		= UCLASS_SERIAL,
173 	.of_match	= htif_serial_ids,
174 	.of_to_plat	= htif_serial_of_to_plat,
175 	.plat_auto	= sizeof(struct htif_plat),
176 	.probe		= htif_serial_probe,
177 	.ops		= &htif_serial_ops,
178 };
179