1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
4  */
5 
6 #include <dm.h>
7 #include <malloc.h>
8 #include <serial.h>
9 #include <semihosting.h>
10 
11 /**
12  * struct smh_serial_priv - Semihosting serial private data
13  * @infd: stdin file descriptor (or error)
14  * @outfd: stdout file descriptor (or error)
15  * @counter: Counter used to fake pending every other call
16  */
17 struct smh_serial_priv {
18 	int infd;
19 	int outfd;
20 	unsigned counter;
21 };
22 
23 #if CONFIG_IS_ENABLED(DM_SERIAL)
smh_serial_getc(struct udevice * dev)24 static int smh_serial_getc(struct udevice *dev)
25 {
26 	char ch = 0;
27 	struct smh_serial_priv *priv = dev_get_priv(dev);
28 
29 	if (priv->infd < 0)
30 		return smh_getc();
31 
32 	smh_read(priv->infd, &ch, sizeof(ch));
33 	return ch;
34 }
35 
smh_serial_putc(struct udevice * dev,const char ch)36 static int smh_serial_putc(struct udevice *dev, const char ch)
37 {
38 	smh_putc(ch);
39 	return 0;
40 }
41 
smh_serial_puts(struct udevice * dev,const char * s,size_t len)42 static ssize_t smh_serial_puts(struct udevice *dev, const char *s, size_t len)
43 {
44 	int ret;
45 	struct smh_serial_priv *priv = dev_get_priv(dev);
46 	unsigned long written;
47 
48 	if (priv->outfd < 0) {
49 		char *buf;
50 
51 		/* Try and avoid a copy if we can */
52 		if (!s[len + 1]) {
53 			smh_puts(s);
54 			return len;
55 		}
56 
57 		buf = strndup(s, len);
58 		if (!buf)
59 			return -ENOMEM;
60 
61 		smh_puts(buf);
62 		free(buf);
63 		return len;
64 	}
65 
66 	ret = smh_write(priv->outfd, s, len, &written);
67 	if (written)
68 		return written;
69 	return ret;
70 }
71 
smh_serial_pending(struct udevice * dev,bool input)72 static int smh_serial_pending(struct udevice *dev, bool input)
73 {
74 	struct smh_serial_priv *priv = dev_get_priv(dev);
75 
76 	if (input)
77 		return priv->counter++ & 1;
78 	return false;
79 }
80 
81 static const struct dm_serial_ops smh_serial_ops = {
82 	.putc = smh_serial_putc,
83 	.puts = smh_serial_puts,
84 	.getc = smh_serial_getc,
85 	.pending = smh_serial_pending,
86 };
87 
smh_serial_bind(struct udevice * dev)88 static int smh_serial_bind(struct udevice *dev)
89 {
90 	if (semihosting_enabled())
91 		return 0;
92 	return -ENOENT;
93 }
94 
smh_serial_probe(struct udevice * dev)95 static int smh_serial_probe(struct udevice *dev)
96 {
97 	struct smh_serial_priv *priv = dev_get_priv(dev);
98 
99 	priv->infd = smh_open(":tt", MODE_READ);
100 	priv->outfd = smh_open(":tt", MODE_WRITE);
101 	return 0;
102 }
103 
104 U_BOOT_DRIVER(smh_serial) = {
105 	.name	= "serial_semihosting",
106 	.id	= UCLASS_SERIAL,
107 	.bind	= smh_serial_bind,
108 	.probe	= smh_serial_probe,
109 	.priv_auto = sizeof(struct smh_serial_priv),
110 	.ops	= &smh_serial_ops,
111 	.flags	= DM_FLAG_PRE_RELOC,
112 };
113 
114 U_BOOT_DRVINFO(smh_serial) = {
115 	.name = "serial_semihosting",
116 };
117 #else /* DM_SERIAL */
118 static int infd = -ENODEV;
119 static int outfd = -ENODEV;
120 static unsigned counter = 1;
121 
smh_serial_start(void)122 static int smh_serial_start(void)
123 {
124 	infd = smh_open(":tt", MODE_READ);
125 	outfd = smh_open(":tt", MODE_WRITE);
126 	return 0;
127 }
128 
smh_serial_stop(void)129 static int smh_serial_stop(void)
130 {
131 	if (outfd >= 0)
132 		smh_close(outfd);
133 	return 0;
134 }
135 
smh_serial_setbrg(void)136 static void smh_serial_setbrg(void)
137 {
138 }
139 
smh_serial_getc(void)140 static int smh_serial_getc(void)
141 {
142 	char ch = 0;
143 
144 	if (infd < 0)
145 		return smh_getc();
146 
147 	smh_read(infd, &ch, sizeof(ch));
148 	return ch;
149 }
150 
smh_serial_tstc(void)151 static int smh_serial_tstc(void)
152 {
153 	return counter++ & 1;
154 }
155 
smh_serial_puts(const char * s)156 static void smh_serial_puts(const char *s)
157 {
158 	ulong unused;
159 
160 	if (outfd < 0)
161 		smh_puts(s);
162 	else
163 		smh_write(outfd, s, strlen(s), &unused);
164 }
165 
166 struct serial_device serial_smh_device = {
167 	.name	= "serial_smh",
168 	.start	= smh_serial_start,
169 	.stop	= smh_serial_stop,
170 	.setbrg	= smh_serial_setbrg,
171 	.getc	= smh_serial_getc,
172 	.tstc	= smh_serial_tstc,
173 	.putc	= smh_putc,
174 	.puts	= smh_serial_puts,
175 };
176 
smh_serial_initialize(void)177 void smh_serial_initialize(void)
178 {
179 	if (semihosting_enabled())
180 		serial_register(&serial_smh_device);
181 }
182 
default_serial_console(void)183 __weak struct serial_device *default_serial_console(void)
184 {
185 	return &serial_smh_device;
186 }
187 #endif
188 
189 #ifdef CONFIG_DEBUG_UART_SEMIHOSTING
190 #include <debug_uart.h>
191 
_debug_uart_init(void)192 static inline void _debug_uart_init(void)
193 {
194 }
195 
_debug_uart_putc(int c)196 static inline void _debug_uart_putc(int c)
197 {
198 	smh_putc(c);
199 }
200 
201 DEBUG_UART_FUNCS
202 #endif
203