From 3ca7a06afb7cbc867b7861a8b9aada74e5bbb559 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 16 Aug 2017 17:37:16 +0200 Subject: serial: serial-uclass: Add generic serial RX buffer support Pasting longer lines into the U-Boot console prompt sometimes leads to characters missing. One problem here is the small 16-byte FIFO of the legacy NS16550 UART, e.g. on x86 platforms. This patch now introduces a Kconfig option to enable RX buffer support for all DM based serial drivers. With this option enabled, I was able paste really long lines into the U-Boot console, without any characters missing. Signed-off-by: Stefan Roese Cc: Simon Glass Cc: Bin Meng Cc: Tom Rini Reviewed-by: Bin Meng Tested-by: Bin Meng --- drivers/serial/Kconfig | 15 +++++++++++++ drivers/serial/serial-uclass.c | 48 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 1c2c5d6..aeed538 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -64,6 +64,21 @@ config DM_SERIAL implements serial_putc() etc. The uclass interface is defined in include/serial.h. +config SERIAL_RX_BUFFER + bool "Enable RX buffer for serial input" + depends on DM_SERIAL + help + Enable RX buffer support for the serial driver. This enables + pasting longer strings, even when the RX FIFO of the UART is + not big enough (e.g. 16 bytes on the normal NS16550). + +config SERIAL_RX_BUFFER_SIZE + int "RX buffer size" + depends on SERIAL_RX_BUFFER + default 256 + help + The size of the RX buffer (needs to be power of 2) + config SPL_DM_SERIAL bool "Enable Driver Model for serial drivers in SPL" depends on DM_SERIAL diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 998d372..2e5116f 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -160,7 +160,7 @@ static void _serial_puts(struct udevice *dev, const char *str) _serial_putc(dev, *str++); } -static int _serial_getc(struct udevice *dev) +static int __serial_getc(struct udevice *dev) { struct dm_serial_ops *ops = serial_get_ops(dev); int err; @@ -174,7 +174,7 @@ static int _serial_getc(struct udevice *dev) return err >= 0 ? err : 0; } -static int _serial_tstc(struct udevice *dev) +static int __serial_tstc(struct udevice *dev) { struct dm_serial_ops *ops = serial_get_ops(dev); @@ -184,6 +184,44 @@ static int _serial_tstc(struct udevice *dev) return 1; } +#if CONFIG_IS_ENABLED(SERIAL_RX_BUFFER) +static int _serial_tstc(struct udevice *dev) +{ + struct serial_dev_priv *upriv = dev_get_uclass_priv(dev); + + /* Read all available chars into the RX buffer */ + while (__serial_tstc(dev)) { + upriv->buf[upriv->wr_ptr++] = __serial_getc(dev); + upriv->wr_ptr %= CONFIG_SERIAL_RX_BUFFER_SIZE; + } + + return upriv->rd_ptr != upriv->wr_ptr ? 1 : 0; +} + +static int _serial_getc(struct udevice *dev) +{ + struct serial_dev_priv *upriv = dev_get_uclass_priv(dev); + char val; + + val = upriv->buf[upriv->rd_ptr++]; + upriv->rd_ptr %= CONFIG_SERIAL_RX_BUFFER_SIZE; + + return val; +} + +#else /* CONFIG_IS_ENABLED(SERIAL_RX_BUFFER) */ + +static int _serial_getc(struct udevice *dev) +{ + return __serial_getc(dev); +} + +static int _serial_tstc(struct udevice *dev) +{ + return __serial_tstc(dev); +} +#endif /* CONFIG_IS_ENABLED(SERIAL_RX_BUFFER) */ + void serial_putc(char ch) { if (gd->cur_serial_dev) @@ -359,6 +397,12 @@ static int serial_post_probe(struct udevice *dev) sdev.puts = serial_stub_puts; sdev.getc = serial_stub_getc; sdev.tstc = serial_stub_tstc; + +#if CONFIG_IS_ENABLED(SERIAL_RX_BUFFER) + /* Allocate the RX buffer */ + upriv->buf = malloc(CONFIG_SERIAL_RX_BUFFER_SIZE); +#endif + stdio_register_dev(&sdev, &upriv->sdev); #endif return 0; -- cgit v1.1