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