Hi Sean,
On Tue, 22 Mar 2022 at 15:00, Sean Anderson <sean.ander...@seco.com> wrote: > > 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.ander...@seco.com> > --- > > (no changes since v2) > > Changes in v2: > - Fix baud numbers being off by 10 > - Fix typos in commit message > - Rename non-DM driver struct to match format of other drivers > > drivers/serial/Kconfig | 22 +++++ > drivers/serial/Makefile | 1 + > drivers/serial/serial.c | 2 + > drivers/serial/serial_semihosting.c | 147 ++++++++++++++++++++++++++++ > include/serial.h | 1 + > 5 files changed, 173 insertions(+) > create mode 100644 drivers/serial/serial_semihosting.c > > diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig > index 345d1881f5..cc20759505 100644 > --- a/drivers/serial/Kconfig > +++ b/drivers/serial/Kconfig > @@ -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 > @@ -778,6 +787,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 > diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile > index 52e70aa191..b68b5e7b2b 100644 > --- a/drivers/serial/Makefile > +++ b/drivers/serial/Makefile > @@ -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 > diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c > index ebbd21916d..6cdbb89841 100644 > --- a/drivers/serial/serial.c > +++ b/drivers/serial/serial.c > @@ -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(); > > diff --git a/drivers/serial/serial_semihosting.c > b/drivers/serial/serial_semihosting.c > new file mode 100644 > index 0000000000..7c7c5d9455 > --- /dev/null > +++ b/drivers/serial/serial_semihosting.c > @@ -0,0 +1,147 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2022 Sean Anderson <sean.ander...@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 > diff --git a/include/serial.h b/include/serial.h > index 19a8c0c67d..2681d26c82 100644 > --- a/include/serial.h > +++ b/include/serial.h > @@ -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); > > -- > 2.25.1 >