Prepare v2023.10
[platform/kernel/u-boot.git] / drivers / serial / serial_semihosting.c
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)
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
37 static int smh_serial_putc(struct udevice *dev, const char ch)
38 {
39         smh_putc(ch);
40         return 0;
41 }
42
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
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
89 static int smh_serial_bind(struct udevice *dev)
90 {
91         if (semihosting_enabled())
92                 return 0;
93         return -ENOENT;
94 }
95
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
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
130 static int smh_serial_stop(void)
131 {
132         if (outfd >= 0)
133                 smh_close(outfd);
134         return 0;
135 }
136
137 static void smh_serial_setbrg(void)
138 {
139 }
140
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
152 static int smh_serial_tstc(void)
153 {
154         return counter++ & 1;
155 }
156
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
178 void smh_serial_initialize(void)
179 {
180         if (semihosting_enabled())
181                 serial_register(&serial_smh_device);
182 }
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
193 static inline void _debug_uart_init(void)
194 {
195 }
196
197 static inline void _debug_uart_putc(int c)
198 {
199         smh_putc(c);
200 }
201
202 DEBUG_UART_FUNCS
203 #endif