mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +00:00
serial: Add semihosting driver
This adds a serial driver which uses semihosting calls to read and write to the host's console. For convenience, if CONFIG_DM_SERIAL is enabled, we will instantiate a serial driver. This allows users to enable this driver (which has no physical device) without modifying their device trees or board files. We also implement a non-DM driver for SPL, or for much faster output in U-Boot proper. There are three ways to print to the console: Method Baud ================== ===== smh_putc in a loop 170 smh_puts 1600 smh_write with :tt 20000 ================== ===== These speeds were measured using a 175 character message with a J-Link adapter. For reference, U-Boot typically prints around 2700 characters during boot on this board. There are two major factors affecting the speed of these functions. First, each breakpoint incurs a delay. Second, each debugger memory transaction incurs a delay. smh_putc has a breakpoint and memory transaction for every character. smh_puts has one breakpoint, but still has to use a transaction for every character. This is because we don't know the length up front, so OpenOCD has to check if each character is nul. smh_write has only one breakpoint and one memory transfer. DM serial drivers can only implement a putc interface, so we are stuck with the slowest API. Non-DM drivers can implement puts, which is vastly more efficient. When the driver starts up, we try to open :tt. Since this is an extension, this may fail. If it does, we fall back to smh_puts. We don't check :semihosting-features, since there are nonconforming implementations (OpenOCD) which don't implement it (but *do* implement :tt). Some semihosting implementations (QEMU) don't handle READC properly. To work around this, we try to use open/read (much like for stdin) if possible. There is no non-blocking I/O available, so we don't implement pending. This will cause __serial_tstc to always return true. If CONFIG_SERIAL_RX_BUFFER is enabled, _serial_tstc will try and read characters forever. To avoid this, we depend on this config being disabled. Signed-off-by: Sean Anderson <sean.anderson@seco.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
3ea744e873
commit
74d11d37e2
5 changed files with 173 additions and 0 deletions
|
@ -399,6 +399,15 @@ config DEBUG_UART_SANDBOX
|
|||
start up driver model. The driver will be available until the real
|
||||
driver model serial is running.
|
||||
|
||||
config DEBUG_UART_SEMIHOSTING
|
||||
bool "semihosting"
|
||||
depends on SEMIHOSTING_SERIAL
|
||||
help
|
||||
Select this to enable the debug UART using the semihosting driver.
|
||||
This provides basic serial output from the console without needing to
|
||||
start up driver model. The driver will be available until the real
|
||||
driver model serial is running.
|
||||
|
||||
config DEBUG_UART_SIFIVE
|
||||
bool "SiFive UART"
|
||||
depends on SIFIVE_SERIAL
|
||||
|
@ -782,6 +791,19 @@ config SCIF_CONSOLE
|
|||
on systems with RCar or SH SoCs, say Y to this option. If unsure,
|
||||
say N.
|
||||
|
||||
config SEMIHOSTING_SERIAL
|
||||
bool "Semihosting UART support"
|
||||
depends on SEMIHOSTING && !SERIAL_RX_BUFFER
|
||||
help
|
||||
Select this to enable a serial UART using semihosting. Special halt
|
||||
instructions will be issued which an external debugger (such as a
|
||||
JTAG emulator) may interpret. The debugger will display U-Boot's
|
||||
console output on the host system.
|
||||
|
||||
Enable this option only if you are using a debugger which supports
|
||||
semihosting. If you are not using a debugger, this driver will halt
|
||||
the boot.
|
||||
|
||||
config UNIPHIER_SERIAL
|
||||
bool "Support for UniPhier on-chip UART"
|
||||
depends on ARCH_UNIPHIER
|
||||
|
|
|
@ -52,6 +52,7 @@ endif
|
|||
obj-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o
|
||||
obj-$(CONFIG_SANDBOX_SERIAL) += sandbox.o
|
||||
obj-$(CONFIG_SCIF_CONSOLE) += serial_sh.o
|
||||
obj-$(CONFIG_SEMIHOSTING_SERIAL) += serial_semihosting.o
|
||||
obj-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o
|
||||
obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o
|
||||
obj-$(CONFIG_FSL_LINFLEXUART) += serial_linflexuart.o
|
||||
|
|
|
@ -126,6 +126,7 @@ serial_initfunc(mxc_serial_initialize);
|
|||
serial_initfunc(ns16550_serial_initialize);
|
||||
serial_initfunc(pl01x_serial_initialize);
|
||||
serial_initfunc(pxa_serial_initialize);
|
||||
serial_initfunc(smh_serial_initialize);
|
||||
serial_initfunc(sh_serial_initialize);
|
||||
serial_initfunc(mtk_serial_initialize);
|
||||
|
||||
|
@ -180,6 +181,7 @@ int serial_initialize(void)
|
|||
ns16550_serial_initialize();
|
||||
pl01x_serial_initialize();
|
||||
pxa_serial_initialize();
|
||||
smh_serial_initialize();
|
||||
sh_serial_initialize();
|
||||
mtk_serial_initialize();
|
||||
|
||||
|
|
147
drivers/serial/serial_semihosting.c
Normal file
147
drivers/serial/serial_semihosting.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <serial.h>
|
||||
#include <semihosting.h>
|
||||
|
||||
/**
|
||||
* struct smh_serial_priv - Semihosting serial private data
|
||||
* @infd: stdin file descriptor (or error)
|
||||
*/
|
||||
struct smh_serial_priv {
|
||||
int infd;
|
||||
int outfd;
|
||||
};
|
||||
|
||||
#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 const struct dm_serial_ops smh_serial_ops = {
|
||||
.putc = smh_serial_putc,
|
||||
.getc = smh_serial_getc,
|
||||
};
|
||||
|
||||
static int smh_serial_probe(struct udevice *dev)
|
||||
{
|
||||
struct smh_serial_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->infd = smh_open(":tt", MODE_READ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(smh_serial) = {
|
||||
.name = "serial_semihosting",
|
||||
.id = UCLASS_SERIAL,
|
||||
.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 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 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)
|
||||
{
|
||||
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
|
|
@ -23,6 +23,7 @@ struct serial_device {
|
|||
void default_serial_puts(const char *s);
|
||||
|
||||
extern struct serial_device serial_smc_device;
|
||||
extern struct serial_device serial_smh_device;
|
||||
extern struct serial_device serial_scc_device;
|
||||
extern struct serial_device *default_serial_console(void);
|
||||
|
||||
|
|
Loading…
Reference in a new issue