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>
2022-03-22 20:59:24 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <dm.h>
|
2022-04-04 18:18:00 +00:00
|
|
|
#include <malloc.h>
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
#include <serial.h>
|
|
|
|
#include <semihosting.h>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct smh_serial_priv - Semihosting serial private data
|
|
|
|
* @infd: stdin file descriptor (or error)
|
2022-04-04 18:18:00 +00:00
|
|
|
* @outfd: stdout file descriptor (or error)
|
2022-05-17 17:55:07 +00:00
|
|
|
* @counter: Counter used to fake pending every other call
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
*/
|
|
|
|
struct smh_serial_priv {
|
|
|
|
int infd;
|
|
|
|
int outfd;
|
2022-05-17 17:55:07 +00:00
|
|
|
unsigned counter;
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2022-04-04 18:18:00 +00:00
|
|
|
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);
|
2022-04-22 18:50:23 +00:00
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2022-04-04 18:18:00 +00:00
|
|
|
smh_puts(buf);
|
|
|
|
free(buf);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = smh_write(priv->outfd, s, len, &written);
|
|
|
|
if (written)
|
|
|
|
return written;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-05-17 17:55:07 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
static const struct dm_serial_ops smh_serial_ops = {
|
|
|
|
.putc = smh_serial_putc,
|
2022-04-04 18:18:00 +00:00
|
|
|
.puts = smh_serial_puts,
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
.getc = smh_serial_getc,
|
2022-05-17 17:55:07 +00:00
|
|
|
.pending = smh_serial_pending,
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
};
|
|
|
|
|
2022-03-22 20:59:32 +00:00
|
|
|
static int smh_serial_bind(struct udevice *dev)
|
|
|
|
{
|
|
|
|
if (semihosting_enabled())
|
|
|
|
return 0;
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
static int smh_serial_probe(struct udevice *dev)
|
|
|
|
{
|
|
|
|
struct smh_serial_priv *priv = dev_get_priv(dev);
|
|
|
|
|
|
|
|
priv->infd = smh_open(":tt", MODE_READ);
|
2022-04-04 18:18:00 +00:00
|
|
|
priv->outfd = smh_open(":tt", MODE_WRITE);
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
U_BOOT_DRIVER(smh_serial) = {
|
|
|
|
.name = "serial_semihosting",
|
|
|
|
.id = UCLASS_SERIAL,
|
2022-03-22 20:59:32 +00:00
|
|
|
.bind = smh_serial_bind,
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
.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;
|
2022-05-17 17:55:07 +00:00
|
|
|
static unsigned counter = 1;
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2022-05-17 17:55:07 +00:00
|
|
|
return counter++ & 1;
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2022-03-22 20:59:32 +00:00
|
|
|
if (semihosting_enabled())
|
|
|
|
serial_register(&serial_smh_device);
|
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>
2022-03-22 20:59:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
__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
|