1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2024 Jiaxun Yang <jiaxun.yang@flygoat.com>
4  */
5 
6 #include <dm.h>
7 #include <malloc.h>
8 #include <serial.h>
9 
10 #include <asm/platform/simcall.h>
11 
12 /**
13  * struct simc_serial_priv - Semihosting serial private data
14  * @counter: Counter used to fake pending every other call
15  */
16 struct simc_serial_priv {
17 	unsigned int counter;
18 };
19 
simc_serial_getc(struct udevice * dev)20 static int simc_serial_getc(struct udevice *dev)
21 {
22 	char ch = 0;
23 
24 	simc_read(0, &ch, sizeof(ch));
25 
26 	return ch;
27 }
28 
simc_serial_putc(struct udevice * dev,const char ch)29 static int simc_serial_putc(struct udevice *dev, const char ch)
30 {
31 	char str[2] = {0};
32 
33 	str[0] = ch;
34 	simc_write(1, str, 1);
35 
36 	return 0;
37 }
38 
simc_serial_pending(struct udevice * dev,bool input)39 static int simc_serial_pending(struct udevice *dev, bool input)
40 {
41 	struct simc_serial_priv *priv = dev_get_priv(dev);
42 
43 	if (input) {
44 		int res = simc_poll(0);
45 		return res < 0 ? priv->counter++ & 1 : res;
46 	}
47 
48 	return false;
49 }
50 
smh_serial_puts(struct udevice * dev,const char * s,size_t len)51 static ssize_t smh_serial_puts(struct udevice *dev, const char *s, size_t len)
52 {
53 	int ret;
54 
55 	ret = simc_write(1, s, len);
56 
57 	return ret;
58 }
59 
60 static const struct dm_serial_ops simc_serial_ops = {
61 	.putc = simc_serial_putc,
62 	.puts = smh_serial_puts,
63 	.getc = simc_serial_getc,
64 	.pending = simc_serial_pending,
65 };
66 
67 U_BOOT_DRIVER(simc_serial) = {
68 	.name	= "serial_xtensa_semihosting",
69 	.id	= UCLASS_SERIAL,
70 	.priv_auto = sizeof(struct simc_serial_priv),
71 	.ops	= &simc_serial_ops,
72 	.flags	= DM_FLAG_PRE_RELOC,
73 };
74 
75 U_BOOT_DRVINFO(simc_serial) = {
76 	.name = "serial_xtensa_semihosting",
77 };
78 
79 #if CONFIG_IS_ENABLED(DEBUG_UART_XTENSA_SEMIHOSTING)
80 #include <debug_uart.h>
81 
_debug_uart_init(void)82 static inline void _debug_uart_init(void)
83 {
84 }
85 
_debug_uart_putc(int c)86 static inline void _debug_uart_putc(int c)
87 {
88 	simc_serial_putc(NULL, c);
89 }
90 
91 DEBUG_UART_FUNCS
92 #endif
93