mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 03:58:17 +00:00 
			
		
		
		
	As part of bringing the master branch back in to next, we need to allow for all of these changes to exist here. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			203 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
 | |
|  */
 | |
| 
 | |
| #include <dm.h>
 | |
| #include <malloc.h>
 | |
| #include <serial.h>
 | |
| #include <semihosting.h>
 | |
| 
 | |
| /**
 | |
|  * struct smh_serial_priv - Semihosting serial private data
 | |
|  * @infd: stdin file descriptor (or error)
 | |
|  * @outfd: stdout file descriptor (or error)
 | |
|  * @counter: Counter used to fake pending every other call
 | |
|  */
 | |
| struct smh_serial_priv {
 | |
| 	int infd;
 | |
| 	int outfd;
 | |
| 	unsigned counter;
 | |
| };
 | |
| 
 | |
| #if CONFIG_IS_ENABLED(DM_SERIAL)
 | |
| static int smh_serial_getc(struct udevice *dev)
 | |
| {
 | |
| 	char ch = 0;
 | |
| 	struct smh_serial_priv *priv = dev_get_priv(dev);
 | |
| 
 | |
| 	if (priv->infd < 0)
 | |
| 		return smh_getc();
 | |
| 
 | |
| 	smh_read(priv->infd, &ch, sizeof(ch));
 | |
| 	return ch;
 | |
| }
 | |
| 
 | |
| static int smh_serial_putc(struct udevice *dev, const char ch)
 | |
| {
 | |
| 	smh_putc(ch);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static ssize_t smh_serial_puts(struct udevice *dev, const char *s, size_t len)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct smh_serial_priv *priv = dev_get_priv(dev);
 | |
| 	unsigned long written;
 | |
| 
 | |
| 	if (priv->outfd < 0) {
 | |
| 		char *buf;
 | |
| 
 | |
| 		/* Try and avoid a copy if we can */
 | |
| 		if (!s[len + 1]) {
 | |
| 			smh_puts(s);
 | |
| 			return len;
 | |
| 		}
 | |
| 
 | |
| 		buf = strndup(s, len);
 | |
| 		if (!buf)
 | |
| 			return -ENOMEM;
 | |
| 
 | |
| 		smh_puts(buf);
 | |
| 		free(buf);
 | |
| 		return len;
 | |
| 	}
 | |
| 
 | |
| 	ret = smh_write(priv->outfd, s, len, &written);
 | |
| 	if (written)
 | |
| 		return written;
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int smh_serial_pending(struct udevice *dev, bool input)
 | |
| {
 | |
| 	struct smh_serial_priv *priv = dev_get_priv(dev);
 | |
| 
 | |
| 	if (input)
 | |
| 		return priv->counter++ & 1;
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| static const struct dm_serial_ops smh_serial_ops = {
 | |
| 	.putc = smh_serial_putc,
 | |
| 	.puts = smh_serial_puts,
 | |
| 	.getc = smh_serial_getc,
 | |
| 	.pending = smh_serial_pending,
 | |
| };
 | |
| 
 | |
| static int smh_serial_bind(struct udevice *dev)
 | |
| {
 | |
| 	if (semihosting_enabled())
 | |
| 		return 0;
 | |
| 	return -ENOENT;
 | |
| }
 | |
| 
 | |
| static int smh_serial_probe(struct udevice *dev)
 | |
| {
 | |
| 	struct smh_serial_priv *priv = dev_get_priv(dev);
 | |
| 
 | |
| 	priv->infd = smh_open(":tt", MODE_READ);
 | |
| 	priv->outfd = smh_open(":tt", MODE_WRITE);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| U_BOOT_DRIVER(smh_serial) = {
 | |
| 	.name	= "serial_semihosting",
 | |
| 	.id	= UCLASS_SERIAL,
 | |
| 	.bind	= smh_serial_bind,
 | |
| 	.probe	= smh_serial_probe,
 | |
| 	.priv_auto = sizeof(struct smh_serial_priv),
 | |
| 	.ops	= &smh_serial_ops,
 | |
| 	.flags	= DM_FLAG_PRE_RELOC,
 | |
| };
 | |
| 
 | |
| U_BOOT_DRVINFO(smh_serial) = {
 | |
| 	.name = "serial_semihosting",
 | |
| };
 | |
| #else /* DM_SERIAL */
 | |
| static int infd = -ENODEV;
 | |
| static int outfd = -ENODEV;
 | |
| static unsigned counter = 1;
 | |
| 
 | |
| static int smh_serial_start(void)
 | |
| {
 | |
| 	infd = smh_open(":tt", MODE_READ);
 | |
| 	outfd = smh_open(":tt", MODE_WRITE);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int smh_serial_stop(void)
 | |
| {
 | |
| 	if (outfd >= 0)
 | |
| 		smh_close(outfd);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void smh_serial_setbrg(void)
 | |
| {
 | |
| }
 | |
| 
 | |
| static int smh_serial_getc(void)
 | |
| {
 | |
| 	char ch = 0;
 | |
| 
 | |
| 	if (infd < 0)
 | |
| 		return smh_getc();
 | |
| 
 | |
| 	smh_read(infd, &ch, sizeof(ch));
 | |
| 	return ch;
 | |
| }
 | |
| 
 | |
| static int smh_serial_tstc(void)
 | |
| {
 | |
| 	return counter++ & 1;
 | |
| }
 | |
| 
 | |
| static void smh_serial_puts(const char *s)
 | |
| {
 | |
| 	ulong unused;
 | |
| 
 | |
| 	if (outfd < 0)
 | |
| 		smh_puts(s);
 | |
| 	else
 | |
| 		smh_write(outfd, s, strlen(s), &unused);
 | |
| }
 | |
| 
 | |
| struct serial_device serial_smh_device = {
 | |
| 	.name	= "serial_smh",
 | |
| 	.start	= smh_serial_start,
 | |
| 	.stop	= smh_serial_stop,
 | |
| 	.setbrg	= smh_serial_setbrg,
 | |
| 	.getc	= smh_serial_getc,
 | |
| 	.tstc	= smh_serial_tstc,
 | |
| 	.putc	= smh_putc,
 | |
| 	.puts	= smh_serial_puts,
 | |
| };
 | |
| 
 | |
| void smh_serial_initialize(void)
 | |
| {
 | |
| 	if (semihosting_enabled())
 | |
| 		serial_register(&serial_smh_device);
 | |
| }
 | |
| 
 | |
| __weak struct serial_device *default_serial_console(void)
 | |
| {
 | |
| 	return &serial_smh_device;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_DEBUG_UART_SEMIHOSTING
 | |
| #include <debug_uart.h>
 | |
| 
 | |
| static inline void _debug_uart_init(void)
 | |
| {
 | |
| }
 | |
| 
 | |
| static inline void _debug_uart_putc(int c)
 | |
| {
 | |
| 	smh_putc(c);
 | |
| }
 | |
| 
 | |
| DEBUG_UART_FUNCS
 | |
| #endif
 |