blob: 56a5ec72428afdd97eccad739a46189380ba3e76 [file] [log] [blame]
Sean Andersond4f131a2022-03-22 16:59:24 -04001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
4 */
5
Sean Andersond4f131a2022-03-22 16:59:24 -04006#include <dm.h>
Sean Anderson760089a2022-04-04 14:18:00 -04007#include <malloc.h>
Sean Andersond4f131a2022-03-22 16:59:24 -04008#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)
Sean Anderson760089a2022-04-04 14:18:00 -040014 * @outfd: stdout file descriptor (or error)
Sean Anderson03113fb2022-05-17 13:55:07 -040015 * @counter: Counter used to fake pending every other call
Sean Andersond4f131a2022-03-22 16:59:24 -040016 */
17struct smh_serial_priv {
18 int infd;
19 int outfd;
Sean Anderson03113fb2022-05-17 13:55:07 -040020 unsigned counter;
Sean Andersond4f131a2022-03-22 16:59:24 -040021};
22
23#if CONFIG_IS_ENABLED(DM_SERIAL)
24static 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
36static int smh_serial_putc(struct udevice *dev, const char ch)
37{
38 smh_putc(ch);
39 return 0;
40}
41
Sean Anderson760089a2022-04-04 14:18:00 -040042static 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);
Sean Anderson537cf082022-04-22 14:50:23 -040058 if (!buf)
59 return -ENOMEM;
60
Sean Anderson760089a2022-04-04 14:18:00 -040061 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
Sean Anderson03113fb2022-05-17 13:55:07 -040072static 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
Sean Andersond4f131a2022-03-22 16:59:24 -040081static const struct dm_serial_ops smh_serial_ops = {
82 .putc = smh_serial_putc,
Sean Anderson760089a2022-04-04 14:18:00 -040083 .puts = smh_serial_puts,
Sean Andersond4f131a2022-03-22 16:59:24 -040084 .getc = smh_serial_getc,
Sean Anderson03113fb2022-05-17 13:55:07 -040085 .pending = smh_serial_pending,
Sean Andersond4f131a2022-03-22 16:59:24 -040086};
87
Sean Anderson5ac1ec72022-03-22 16:59:32 -040088static int smh_serial_bind(struct udevice *dev)
89{
90 if (semihosting_enabled())
91 return 0;
92 return -ENOENT;
93}
94
Sean Andersond4f131a2022-03-22 16:59:24 -040095static 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);
Sean Anderson760089a2022-04-04 14:18:00 -0400100 priv->outfd = smh_open(":tt", MODE_WRITE);
Sean Andersond4f131a2022-03-22 16:59:24 -0400101 return 0;
102}
103
104U_BOOT_DRIVER(smh_serial) = {
105 .name = "serial_semihosting",
106 .id = UCLASS_SERIAL,
Sean Anderson5ac1ec72022-03-22 16:59:32 -0400107 .bind = smh_serial_bind,
Sean Andersond4f131a2022-03-22 16:59:24 -0400108 .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
114U_BOOT_DRVINFO(smh_serial) = {
115 .name = "serial_semihosting",
116};
117#else /* DM_SERIAL */
118static int infd = -ENODEV;
119static int outfd = -ENODEV;
Sean Anderson03113fb2022-05-17 13:55:07 -0400120static unsigned counter = 1;
Sean Andersond4f131a2022-03-22 16:59:24 -0400121
122static 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
129static int smh_serial_stop(void)
130{
131 if (outfd >= 0)
132 smh_close(outfd);
133 return 0;
134}
135
136static void smh_serial_setbrg(void)
137{
138}
139
140static 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
151static int smh_serial_tstc(void)
152{
Sean Anderson03113fb2022-05-17 13:55:07 -0400153 return counter++ & 1;
Sean Andersond4f131a2022-03-22 16:59:24 -0400154}
155
156static 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
166struct 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
177void smh_serial_initialize(void)
178{
Sean Anderson5ac1ec72022-03-22 16:59:32 -0400179 if (semihosting_enabled())
180 serial_register(&serial_smh_device);
Sean Andersond4f131a2022-03-22 16:59:24 -0400181}
182
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
192static inline void _debug_uart_init(void)
193{
194}
195
196static inline void _debug_uart_putc(int c)
197{
198 smh_putc(c);
199}
200
201DEBUG_UART_FUNCS
202#endif