4328b3dac5b7f9b2dfa64e5325f63f5f6739bea6
[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  */
17 struct smh_serial_priv {
18         int infd;
19         int outfd;
20 };
21
22 #if CONFIG_IS_ENABLED(DM_SERIAL)
23 static int smh_serial_getc(struct udevice *dev)
24 {
25         char ch = 0;
26         struct smh_serial_priv *priv = dev_get_priv(dev);
27
28         if (priv->infd < 0)
29                 return smh_getc();
30
31         smh_read(priv->infd, &ch, sizeof(ch));
32         return ch;
33 }
34
35 static int smh_serial_putc(struct udevice *dev, const char ch)
36 {
37         smh_putc(ch);
38         return 0;
39 }
40
41 static ssize_t smh_serial_puts(struct udevice *dev, const char *s, size_t len)
42 {
43         int ret;
44         struct smh_serial_priv *priv = dev_get_priv(dev);
45         unsigned long written;
46
47         if (priv->outfd < 0) {
48                 char *buf;
49
50                 /* Try and avoid a copy if we can */
51                 if (!s[len + 1]) {
52                         smh_puts(s);
53                         return len;
54                 }
55
56                 buf = strndup(s, len);
57                 smh_puts(buf);
58                 free(buf);
59                 return len;
60         }
61
62         ret = smh_write(priv->outfd, s, len, &written);
63         if (written)
64                 return written;
65         return ret;
66 }
67
68 static const struct dm_serial_ops smh_serial_ops = {
69         .putc = smh_serial_putc,
70         .puts = smh_serial_puts,
71         .getc = smh_serial_getc,
72 };
73
74 static int smh_serial_bind(struct udevice *dev)
75 {
76         if (semihosting_enabled())
77                 return 0;
78         return -ENOENT;
79 }
80
81 static int smh_serial_probe(struct udevice *dev)
82 {
83         struct smh_serial_priv *priv = dev_get_priv(dev);
84
85         priv->infd = smh_open(":tt", MODE_READ);
86         priv->outfd = smh_open(":tt", MODE_WRITE);
87         return 0;
88 }
89
90 U_BOOT_DRIVER(smh_serial) = {
91         .name   = "serial_semihosting",
92         .id     = UCLASS_SERIAL,
93         .bind   = smh_serial_bind,
94         .probe  = smh_serial_probe,
95         .priv_auto = sizeof(struct smh_serial_priv),
96         .ops    = &smh_serial_ops,
97         .flags  = DM_FLAG_PRE_RELOC,
98 };
99
100 U_BOOT_DRVINFO(smh_serial) = {
101         .name = "serial_semihosting",
102 };
103 #else /* DM_SERIAL */
104 static int infd = -ENODEV;
105 static int outfd = -ENODEV;
106
107 static int smh_serial_start(void)
108 {
109         infd = smh_open(":tt", MODE_READ);
110         outfd = smh_open(":tt", MODE_WRITE);
111         return 0;
112 }
113
114 static int smh_serial_stop(void)
115 {
116         if (outfd >= 0)
117                 smh_close(outfd);
118         return 0;
119 }
120
121 static void smh_serial_setbrg(void)
122 {
123 }
124
125 static int smh_serial_getc(void)
126 {
127         char ch = 0;
128
129         if (infd < 0)
130                 return smh_getc();
131
132         smh_read(infd, &ch, sizeof(ch));
133         return ch;
134 }
135
136 static int smh_serial_tstc(void)
137 {
138         return 1;
139 }
140
141 static void smh_serial_puts(const char *s)
142 {
143         ulong unused;
144
145         if (outfd < 0)
146                 smh_puts(s);
147         else
148                 smh_write(outfd, s, strlen(s), &unused);
149 }
150
151 struct serial_device serial_smh_device = {
152         .name   = "serial_smh",
153         .start  = smh_serial_start,
154         .stop   = smh_serial_stop,
155         .setbrg = smh_serial_setbrg,
156         .getc   = smh_serial_getc,
157         .tstc   = smh_serial_tstc,
158         .putc   = smh_putc,
159         .puts   = smh_serial_puts,
160 };
161
162 void smh_serial_initialize(void)
163 {
164         if (semihosting_enabled())
165                 serial_register(&serial_smh_device);
166 }
167
168 __weak struct serial_device *default_serial_console(void)
169 {
170         return &serial_smh_device;
171 }
172 #endif
173
174 #ifdef CONFIG_DEBUG_UART_SEMIHOSTING
175 #include <debug_uart.h>
176
177 static inline void _debug_uart_init(void)
178 {
179 }
180
181 static inline void _debug_uart_putc(int c)
182 {
183         smh_putc(c);
184 }
185
186 DEBUG_UART_FUNCS
187 #endif