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