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