blob: cfa1ec3148c5a1d8d6cc3198b501fe76ee36ec64 [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
6#include <common.h>
7#include <dm.h>
Sean Anderson760089a2022-04-04 14:18:00 -04008#include <malloc.h>
Sean Andersond4f131a2022-03-22 16:59:24 -04009#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)
Sean Anderson760089a2022-04-04 14:18:00 -040015 * @outfd: stdout file descriptor (or error)
Sean Anderson03113fb2022-05-17 13:55:07 -040016 * @counter: Counter used to fake pending every other call
Sean Andersond4f131a2022-03-22 16:59:24 -040017 */
18struct smh_serial_priv {
19 int infd;
20 int outfd;
Sean Anderson03113fb2022-05-17 13:55:07 -040021 unsigned counter;
Sean Andersond4f131a2022-03-22 16:59:24 -040022};
23
24#if CONFIG_IS_ENABLED(DM_SERIAL)
25static 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
37static int smh_serial_putc(struct udevice *dev, const char ch)
38{
39 smh_putc(ch);
40 return 0;
41}
42
Sean Anderson760089a2022-04-04 14:18:00 -040043static 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);
Sean Anderson537cf082022-04-22 14:50:23 -040059 if (!buf)
60 return -ENOMEM;
61
Sean Anderson760089a2022-04-04 14:18:00 -040062 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
Sean Anderson03113fb2022-05-17 13:55:07 -040073static 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
Sean Andersond4f131a2022-03-22 16:59:24 -040082static const struct dm_serial_ops smh_serial_ops = {
83 .putc = smh_serial_putc,
Sean Anderson760089a2022-04-04 14:18:00 -040084 .puts = smh_serial_puts,
Sean Andersond4f131a2022-03-22 16:59:24 -040085 .getc = smh_serial_getc,
Sean Anderson03113fb2022-05-17 13:55:07 -040086 .pending = smh_serial_pending,
Sean Andersond4f131a2022-03-22 16:59:24 -040087};
88
Sean Anderson5ac1ec72022-03-22 16:59:32 -040089static int smh_serial_bind(struct udevice *dev)
90{
91 if (semihosting_enabled())
92 return 0;
93 return -ENOENT;
94}
95
Sean Andersond4f131a2022-03-22 16:59:24 -040096static 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);
Sean Anderson760089a2022-04-04 14:18:00 -0400101 priv->outfd = smh_open(":tt", MODE_WRITE);
Sean Andersond4f131a2022-03-22 16:59:24 -0400102 return 0;
103}
104
105U_BOOT_DRIVER(smh_serial) = {
106 .name = "serial_semihosting",
107 .id = UCLASS_SERIAL,
Sean Anderson5ac1ec72022-03-22 16:59:32 -0400108 .bind = smh_serial_bind,
Sean Andersond4f131a2022-03-22 16:59:24 -0400109 .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
115U_BOOT_DRVINFO(smh_serial) = {
116 .name = "serial_semihosting",
117};
118#else /* DM_SERIAL */
119static int infd = -ENODEV;
120static int outfd = -ENODEV;
Sean Anderson03113fb2022-05-17 13:55:07 -0400121static unsigned counter = 1;
Sean Andersond4f131a2022-03-22 16:59:24 -0400122
123static 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
130static int smh_serial_stop(void)
131{
132 if (outfd >= 0)
133 smh_close(outfd);
134 return 0;
135}
136
137static void smh_serial_setbrg(void)
138{
139}
140
141static 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
152static int smh_serial_tstc(void)
153{
Sean Anderson03113fb2022-05-17 13:55:07 -0400154 return counter++ & 1;
Sean Andersond4f131a2022-03-22 16:59:24 -0400155}
156
157static 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
167struct 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
178void smh_serial_initialize(void)
179{
Sean Anderson5ac1ec72022-03-22 16:59:32 -0400180 if (semihosting_enabled())
181 serial_register(&serial_smh_device);
Sean Andersond4f131a2022-03-22 16:59:24 -0400182}
183
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
193static inline void _debug_uart_init(void)
194{
195}
196
197static inline void _debug_uart_putc(int c)
198{
199 smh_putc(c);
200}
201
202DEBUG_UART_FUNCS
203#endif