Message ID | 20220322205938.1721846-17-sean.anderson@seco.com |
---|---|
State | Accepted |
Commit | 74d11d37e2d33c616748d5572fd5718944826d17 |
Delegated to: | Tom Rini |
Headers | show |
Series | arm: semihosting: Cleanups and new features | expand |
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 >
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 > Reviewed-by: Simon Glass <sjg@chromium.org> But please can we drop the non-DM support?
On 3/28/22 2:35 AM, Simon Glass wrote: > 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 >> > > Reviewed-by: Simon Glass <sjg@chromium.org> > > But please can we drop the non-DM support? Unfortunately, Layerscape does not support DM serial. I tried converting it, but I ran into some unusual aborts. At the moment, I don't have time to debug things further. And I thought that non-DM serial was ok for SPL? --Sean
On Mon, Mar 28, 2022 at 11:36:46AM -0400, Sean Anderson wrote: > > > On 3/28/22 2:35 AM, Simon Glass wrote: > > 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 > >> > > > > Reviewed-by: Simon Glass <sjg@chromium.org> > > > > But please can we drop the non-DM support? > > Unfortunately, Layerscape does not support DM serial. I tried converting > it, but I ran into some unusual aborts. At the moment, I don't have time > to debug things further. And I thought that non-DM serial was ok for > SPL? It is OK for SPL, and it needs migration for non-SPL. Can you make another thread with your conversion-that-fails for layerscape please?
On Tue, Mar 22, 2022 at 04:59:24PM -0400, Sean Anderson 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> > Reviewed-by: Simon Glass <sjg@chromium.org> Applied to u-boot/next, thanks!
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);
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