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