
Hi Sean,
On Tue, 22 Mar 2022 at 15:00, Sean Anderson sean.anderson@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.anderson@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.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 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