* [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support
@ 2017-07-14 15:25 Stefan Roese
2017-07-17 2:13 ` Bin Meng
2017-07-25 0:44 ` [U-Boot] [U-Boot, " Tom Rini
0 siblings, 2 replies; 11+ messages in thread
From: Stefan Roese @ 2017-07-14 15:25 UTC (permalink / raw)
To: u-boot
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 interrupt
buffer support for NS16550 style UARTs. 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 <sr@denx.de>
Reviewed-by: Simon Glass <sjg@chromium.org>
Cc: Bin Meng <bmeng.cn@gmail.com>
---
v2:
- Added Simon's RB
- Added irq_free_handler to newly introduced driver remove function
- Added comments to the introduced variables on the ns16550_platdata
struct
drivers/serial/Kconfig | 10 ++++
drivers/serial/ns16550.c | 120 +++++++++++++++++++++++++++++++++++++++++++++--
include/ns16550.h | 10 ++++
3 files changed, 135 insertions(+), 5 deletions(-)
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index b7dd2ac103..8284febae3 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -64,6 +64,16 @@ config DM_SERIAL
implements serial_putc() etc. The uclass interface is
defined in include/serial.h.
+config SERIAL_IRQ_BUFFER
+ bool "Enable RX interrupt buffer for serial input"
+ depends on DM_SERIAL
+ default n
+ help
+ Enable RX interrupt 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 SPL_DM_SERIAL
bool "Enable Driver Model for serial drivers in SPL"
depends on DM_SERIAL
diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
index e0e70244ce..0c43cb1fe2 100644
--- a/drivers/serial/ns16550.c
+++ b/drivers/serial/ns16550.c
@@ -315,6 +315,80 @@ DEBUG_UART_FUNCS
#endif
#ifdef CONFIG_DM_SERIAL
+
+#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
+
+#define BUF_COUNT 256
+
+static void rx_fifo_to_buf(struct udevice *dev)
+{
+ struct NS16550 *const com_port = dev_get_priv(dev);
+ struct ns16550_platdata *plat = dev->platdata;
+
+ /* Read all available chars into buffer */
+ while ((serial_in(&com_port->lsr) & UART_LSR_DR)) {
+ plat->buf[plat->wr_ptr++] = serial_in(&com_port->rbr);
+ plat->wr_ptr %= BUF_COUNT;
+ }
+}
+
+static int rx_pending(struct udevice *dev)
+{
+ struct ns16550_platdata *plat = dev->platdata;
+
+ /*
+ * At startup it may happen, that some already received chars are
+ * "stuck" in the RX FIFO, even with the interrupt enabled. This
+ * RX FIFO flushing makes sure, that these chars are read out and
+ * the RX interrupts works as expected.
+ */
+ rx_fifo_to_buf(dev);
+
+ return plat->rd_ptr != plat->wr_ptr ? 1 : 0;
+}
+
+static int rx_get(struct udevice *dev)
+{
+ struct ns16550_platdata *plat = dev->platdata;
+ char val;
+
+ val = plat->buf[plat->rd_ptr++];
+ plat->rd_ptr %= BUF_COUNT;
+
+ return val;
+}
+
+void ns16550_handle_irq(void *data)
+{
+ struct udevice *dev = (struct udevice *)data;
+ struct NS16550 *const com_port = dev_get_priv(dev);
+
+ /* Check if interrupt is pending */
+ if (serial_in(&com_port->iir) & UART_IIR_NO_INT)
+ return;
+
+ /* Flush all available characters from the RX FIFO into the RX buffer */
+ rx_fifo_to_buf(dev);
+}
+
+#else /* CONFIG_SERIAL_IRQ_BUFFER */
+
+static int rx_pending(struct udevice *dev)
+{
+ struct NS16550 *const com_port = dev_get_priv(dev);
+
+ return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
+}
+
+static int rx_get(struct udevice *dev)
+{
+ struct NS16550 *const com_port = dev_get_priv(dev);
+
+ return serial_in(&com_port->rbr);
+}
+
+#endif /* CONFIG_SERIAL_IRQ_BUFFER */
+
static int ns16550_serial_putc(struct udevice *dev, const char ch)
{
struct NS16550 *const com_port = dev_get_priv(dev);
@@ -340,19 +414,17 @@ static int ns16550_serial_pending(struct udevice *dev, bool input)
struct NS16550 *const com_port = dev_get_priv(dev);
if (input)
- return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
+ return rx_pending(dev);
else
return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1;
}
static int ns16550_serial_getc(struct udevice *dev)
{
- struct NS16550 *const com_port = dev_get_priv(dev);
-
- if (!(serial_in(&com_port->lsr) & UART_LSR_DR))
+ if (!ns16550_serial_pending(dev, true))
return -EAGAIN;
- return serial_in(&com_port->rbr);
+ return rx_get(dev);
}
static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
@@ -375,6 +447,34 @@ int ns16550_serial_probe(struct udevice *dev)
com_port->plat = dev_get_platdata(dev);
NS16550_init(com_port, -1);
+#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
+ if (gd->flags & GD_FLG_RELOC) {
+ struct ns16550_platdata *plat = dev->platdata;
+
+ /* Allocate the RX buffer */
+ plat->buf = malloc(BUF_COUNT);
+
+ /* Install the interrupt handler */
+ irq_install_handler(plat->irq, ns16550_handle_irq, dev);
+
+ /* Enable RX interrupts */
+ serial_out(UART_IER_RDI, &com_port->ier);
+ }
+#endif
+
+ return 0;
+}
+
+static int ns16550_serial_remove(struct udevice *dev)
+{
+#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
+ if (gd->flags & GD_FLG_RELOC) {
+ struct ns16550_platdata *plat = dev->platdata;
+
+ irq_free_handler(plat->irq);
+ }
+#endif
+
return 0;
}
@@ -459,6 +559,15 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev)
if (port_type == PORT_JZ4780)
plat->fcr |= UART_FCR_UME;
+#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
+ plat->irq = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+ "interrupts", 0);
+ if (!plat->irq) {
+ debug("ns16550 interrupt not provided\n");
+ return -EINVAL;
+ }
+#endif
+
return 0;
}
#endif
@@ -506,6 +615,7 @@ U_BOOT_DRIVER(ns16550_serial) = {
#endif
.priv_auto_alloc_size = sizeof(struct NS16550),
.probe = ns16550_serial_probe,
+ .remove = ns16550_serial_remove,
.ops = &ns16550_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
};
diff --git a/include/ns16550.h b/include/ns16550.h
index 5fcbcd2e74..7e9944d0d9 100644
--- a/include/ns16550.h
+++ b/include/ns16550.h
@@ -51,6 +51,10 @@
* @base: Base register address
* @reg_shift: Shift size of registers (0=byte, 1=16bit, 2=32bit...)
* @clock: UART base clock speed in Hz
+ *
+ * @buf: Pointer to the RX interrupt buffer
+ * @rd_ptr: Read pointer in the RX interrupt buffer
+ * @wr_ptr: Write pointer in the RX interrupt buffer
*/
struct ns16550_platdata {
unsigned long base;
@@ -58,6 +62,12 @@ struct ns16550_platdata {
int clock;
int reg_offset;
u32 fcr;
+
+ int irq;
+
+ char *buf;
+ int rd_ptr;
+ int wr_ptr;
};
struct udevice;
--
2.13.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support
2017-07-14 15:25 [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support Stefan Roese
@ 2017-07-17 2:13 ` Bin Meng
2017-07-17 8:18 ` Stefan Roese
2017-07-25 0:44 ` [U-Boot] [U-Boot, " Tom Rini
1 sibling, 1 reply; 11+ messages in thread
From: Bin Meng @ 2017-07-17 2:13 UTC (permalink / raw)
To: u-boot
Hi Stefan,
On Fri, Jul 14, 2017 at 11:25 PM, Stefan Roese <sr@denx.de> wrote:
> 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 interrupt
> buffer support for NS16550 style UARTs. With this option enabled, I was
> able paste really long lines into the U-Boot console, without any
> characters missing.
>
Great to see this improvement! Some comments below:
> Signed-off-by: Stefan Roese <sr@denx.de>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> Cc: Bin Meng <bmeng.cn@gmail.com>
> ---
> v2:
> - Added Simon's RB
> - Added irq_free_handler to newly introduced driver remove function
> - Added comments to the introduced variables on the ns16550_platdata
> struct
>
> drivers/serial/Kconfig | 10 ++++
> drivers/serial/ns16550.c | 120 +++++++++++++++++++++++++++++++++++++++++++++--
> include/ns16550.h | 10 ++++
> 3 files changed, 135 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> index b7dd2ac103..8284febae3 100644
> --- a/drivers/serial/Kconfig
> +++ b/drivers/serial/Kconfig
> @@ -64,6 +64,16 @@ config DM_SERIAL
> implements serial_putc() etc. The uclass interface is
> defined in include/serial.h.
>
> +config SERIAL_IRQ_BUFFER
> + bool "Enable RX interrupt buffer for serial input"
> + depends on DM_SERIAL
> + default n
This line can be removed, as its default state is 'n'
> + help
> + Enable RX interrupt 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 SPL_DM_SERIAL
> bool "Enable Driver Model for serial drivers in SPL"
> depends on DM_SERIAL
> diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
> index e0e70244ce..0c43cb1fe2 100644
> --- a/drivers/serial/ns16550.c
> +++ b/drivers/serial/ns16550.c
> @@ -315,6 +315,80 @@ DEBUG_UART_FUNCS
> #endif
>
> #ifdef CONFIG_DM_SERIAL
> +
> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
> +
> +#define BUF_COUNT 256
Should this be configurable via Kconfig?
> +
> +static void rx_fifo_to_buf(struct udevice *dev)
> +{
> + struct NS16550 *const com_port = dev_get_priv(dev);
> + struct ns16550_platdata *plat = dev->platdata;
> +
> + /* Read all available chars into buffer */
> + while ((serial_in(&com_port->lsr) & UART_LSR_DR)) {
> + plat->buf[plat->wr_ptr++] = serial_in(&com_port->rbr);
> + plat->wr_ptr %= BUF_COUNT;
> + }
> +}
> +
> +static int rx_pending(struct udevice *dev)
> +{
> + struct ns16550_platdata *plat = dev->platdata;
> +
> + /*
> + * At startup it may happen, that some already received chars are
> + * "stuck" in the RX FIFO, even with the interrupt enabled. This
> + * RX FIFO flushing makes sure, that these chars are read out and
> + * the RX interrupts works as expected.
> + */
> + rx_fifo_to_buf(dev);
> +
> + return plat->rd_ptr != plat->wr_ptr ? 1 : 0;
> +}
> +
> +static int rx_get(struct udevice *dev)
> +{
> + struct ns16550_platdata *plat = dev->platdata;
> + char val;
> +
> + val = plat->buf[plat->rd_ptr++];
> + plat->rd_ptr %= BUF_COUNT;
> +
> + return val;
> +}
> +
> +void ns16550_handle_irq(void *data)
> +{
> + struct udevice *dev = (struct udevice *)data;
> + struct NS16550 *const com_port = dev_get_priv(dev);
> +
> + /* Check if interrupt is pending */
> + if (serial_in(&com_port->iir) & UART_IIR_NO_INT)
> + return;
> +
> + /* Flush all available characters from the RX FIFO into the RX buffer */
> + rx_fifo_to_buf(dev);
> +}
> +
> +#else /* CONFIG_SERIAL_IRQ_BUFFER */
> +
> +static int rx_pending(struct udevice *dev)
> +{
> + struct NS16550 *const com_port = dev_get_priv(dev);
> +
> + return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
> +}
> +
> +static int rx_get(struct udevice *dev)
> +{
> + struct NS16550 *const com_port = dev_get_priv(dev);
> +
> + return serial_in(&com_port->rbr);
> +}
> +
> +#endif /* CONFIG_SERIAL_IRQ_BUFFER */
> +
> static int ns16550_serial_putc(struct udevice *dev, const char ch)
> {
> struct NS16550 *const com_port = dev_get_priv(dev);
> @@ -340,19 +414,17 @@ static int ns16550_serial_pending(struct udevice *dev, bool input)
> struct NS16550 *const com_port = dev_get_priv(dev);
>
> if (input)
> - return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
> + return rx_pending(dev);
> else
> return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1;
> }
>
> static int ns16550_serial_getc(struct udevice *dev)
> {
> - struct NS16550 *const com_port = dev_get_priv(dev);
> -
> - if (!(serial_in(&com_port->lsr) & UART_LSR_DR))
> + if (!ns16550_serial_pending(dev, true))
> return -EAGAIN;
>
> - return serial_in(&com_port->rbr);
> + return rx_get(dev);
> }
>
> static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
> @@ -375,6 +447,34 @@ int ns16550_serial_probe(struct udevice *dev)
> com_port->plat = dev_get_platdata(dev);
> NS16550_init(com_port, -1);
>
> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
> + if (gd->flags & GD_FLG_RELOC) {
> + struct ns16550_platdata *plat = dev->platdata;
> +
> + /* Allocate the RX buffer */
> + plat->buf = malloc(BUF_COUNT);
> +
> + /* Install the interrupt handler */
> + irq_install_handler(plat->irq, ns16550_handle_irq, dev);
> +
> + /* Enable RX interrupts */
> + serial_out(UART_IER_RDI, &com_port->ier);
> + }
> +#endif
> +
> + return 0;
> +}
> +
> +static int ns16550_serial_remove(struct udevice *dev)
> +{
> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
> + if (gd->flags & GD_FLG_RELOC) {
> + struct ns16550_platdata *plat = dev->platdata;
> +
> + irq_free_handler(plat->irq);
We should also turn off interrupts on NS16550 too by writing zero to
com_port->ie.
> + }
> +#endif
> +
> return 0;
> }
>
> @@ -459,6 +559,15 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev)
> if (port_type == PORT_JZ4780)
> plat->fcr |= UART_FCR_UME;
>
> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
> + plat->irq = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
> + "interrupts", 0);
Should we handle NS16550 on the PCI bus? If not, better put a comment here.
> + if (!plat->irq) {
If this is not provided, I think we should print a warning and fall
back to use the original polling mode.
> + debug("ns16550 interrupt not provided\n");
> + return -EINVAL;
> + }
> +#endif
> +
> return 0;
> }
> #endif
> @@ -506,6 +615,7 @@ U_BOOT_DRIVER(ns16550_serial) = {
> #endif
> .priv_auto_alloc_size = sizeof(struct NS16550),
> .probe = ns16550_serial_probe,
> + .remove = ns16550_serial_remove,
> .ops = &ns16550_serial_ops,
> .flags = DM_FLAG_PRE_RELOC,
> };
> diff --git a/include/ns16550.h b/include/ns16550.h
> index 5fcbcd2e74..7e9944d0d9 100644
> --- a/include/ns16550.h
> +++ b/include/ns16550.h
> @@ -51,6 +51,10 @@
> * @base: Base register address
> * @reg_shift: Shift size of registers (0=byte, 1=16bit, 2=32bit...)
> * @clock: UART base clock speed in Hz
> + *
> + * @buf: Pointer to the RX interrupt buffer
> + * @rd_ptr: Read pointer in the RX interrupt buffer
> + * @wr_ptr: Write pointer in the RX interrupt buffer
> */
> struct ns16550_platdata {
> unsigned long base;
> @@ -58,6 +62,12 @@ struct ns16550_platdata {
> int clock;
> int reg_offset;
> u32 fcr;
> +
> + int irq;
> +
> + char *buf;
> + int rd_ptr;
> + int wr_ptr;
> };
>
> struct udevice;
> --
I've tested the patch on MinnowMax, and pasted a really long text.
Below are 5 times results: (using SecureCRT as my terminal application
on Windows)
=> Note: when interrupt transfer complets, the xHC generates an event
TRB with TRB type 'Transfer Event', which is exactly the same as a
control or bulk transfer. In our xHCI driver, xhci_wait_foer_event()
checks the event TRB t ype and depending on the timing,g it may
wrongly return an event TRB to the caller which originates from
another USB device (different slot ID is checked by the driver and if
different a "BUG" will be thrown).
Unknown command 'Note:' - try 'help'
=> Note: when interrupt transfer complets, thee xHC generates an event
TRB withn TRB type 'Transfer Event', which is exactly the same as a
control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
checks the event TRB type and depending on the timing,g it may wrongly
return an event rTRB to the caller which originates from another USB
device (different slot ID is checked by the driver and if different a
"BUG" will be thrown).
Unknown command 'Note:' - try 'help'
=> Note: when interrupt transfer complets, thee xHC generates an event
TRB withn TRB type 'Transfer Event', which is exactly the same as a
control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
checks the event TRB type and depending on the timing, it may wrongly
return an event rTRB to the caller which originates from another USB
device (different slot ID is checked by the driver and if different a
"BUG" will be thrown).
Unknown command 'Note:' - try 'help'
=> Note: when interrupt transfer complets, the xHC generates an event
TRB with TRB type 'Transfer Event', whicfh is exactly the same as a
control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
checks the event TRB type and depending on the timing, it may wrongly
return an event TRB to the caller which originatres from another USB
device (different slot ID is checked by the driver and if different a
"BUG" will be thrown).
Unknown command 'Note:' - try 'help'
=> Note: when interrupt transfer complets, thee xHC generates an event
TRB with TRB type 'Transfer Event', which is exactly the same as a
control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
checks the event TRB type and depending on the timing, it may wrongly
return an event rTRB to the caller which originatres from another USB
device (different slot ID is checked by the driver and if different a
"BUG" will be thrown).
Unknown command 'Note:' - try 'help'
As you see, none of 5 is completely correct. The 1st and 2nd have
issues in "t ype" and "timing,g". The 2nd/4th/5th have issues in "thee
xHC". The 2nd/3rd/5th have issues in "event rTRB". The 4th has
"whicfh".
Regards,
Bin
^ permalink raw reply [flat|nested] 11+ messages in thread
* [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support
2017-07-17 2:13 ` Bin Meng
@ 2017-07-17 8:18 ` Stefan Roese
2017-07-17 9:26 ` Bin Meng
0 siblings, 1 reply; 11+ messages in thread
From: Stefan Roese @ 2017-07-17 8:18 UTC (permalink / raw)
To: u-boot
Hi Bin,
On 17.07.2017 04:13, Bin Meng wrote:
> On Fri, Jul 14, 2017 at 11:25 PM, Stefan Roese <sr@denx.de> wrote:
>> 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 interrupt
>> buffer support for NS16550 style UARTs. With this option enabled, I was
>> able paste really long lines into the U-Boot console, without any
>> characters missing.
>>
>
> Great to see this improvement! Some comments below:
>
>> Signed-off-by: Stefan Roese <sr@denx.de>
>> Reviewed-by: Simon Glass <sjg@chromium.org>
>> Cc: Bin Meng <bmeng.cn@gmail.com>
>> ---
>> v2:
>> - Added Simon's RB
>> - Added irq_free_handler to newly introduced driver remove function
>> - Added comments to the introduced variables on the ns16550_platdata
>> struct
>>
>> drivers/serial/Kconfig | 10 ++++
>> drivers/serial/ns16550.c | 120 +++++++++++++++++++++++++++++++++++++++++++++--
>> include/ns16550.h | 10 ++++
>> 3 files changed, 135 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
>> index b7dd2ac103..8284febae3 100644
>> --- a/drivers/serial/Kconfig
>> +++ b/drivers/serial/Kconfig
>> @@ -64,6 +64,16 @@ config DM_SERIAL
>> implements serial_putc() etc. The uclass interface is
>> defined in include/serial.h.
>>
>> +config SERIAL_IRQ_BUFFER
>> + bool "Enable RX interrupt buffer for serial input"
>> + depends on DM_SERIAL
>> + default n
>
> This line can be removed, as its default state is 'n'
Okay, will update.
>> + help
>> + Enable RX interrupt 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 SPL_DM_SERIAL
>> bool "Enable Driver Model for serial drivers in SPL"
>> depends on DM_SERIAL
>> diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
>> index e0e70244ce..0c43cb1fe2 100644
>> --- a/drivers/serial/ns16550.c
>> +++ b/drivers/serial/ns16550.c
>> @@ -315,6 +315,80 @@ DEBUG_UART_FUNCS
>> #endif
>>
>> #ifdef CONFIG_DM_SERIAL
>> +
>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>> +
>> +#define BUF_COUNT 256
>
> Should this be configurable via Kconfig?
Good idea. I'll add it in v3.
>> +
>> +static void rx_fifo_to_buf(struct udevice *dev)
>> +{
>> + struct NS16550 *const com_port = dev_get_priv(dev);
>> + struct ns16550_platdata *plat = dev->platdata;
>> +
>> + /* Read all available chars into buffer */
>> + while ((serial_in(&com_port->lsr) & UART_LSR_DR)) {
>> + plat->buf[plat->wr_ptr++] = serial_in(&com_port->rbr);
>> + plat->wr_ptr %= BUF_COUNT;
>> + }
>> +}
>> +
>> +static int rx_pending(struct udevice *dev)
>> +{
>> + struct ns16550_platdata *plat = dev->platdata;
>> +
>> + /*
>> + * At startup it may happen, that some already received chars are
>> + * "stuck" in the RX FIFO, even with the interrupt enabled. This
>> + * RX FIFO flushing makes sure, that these chars are read out and
>> + * the RX interrupts works as expected.
>> + */
>> + rx_fifo_to_buf(dev);
>> +
>> + return plat->rd_ptr != plat->wr_ptr ? 1 : 0;
>> +}
>> +
>> +static int rx_get(struct udevice *dev)
>> +{
>> + struct ns16550_platdata *plat = dev->platdata;
>> + char val;
>> +
>> + val = plat->buf[plat->rd_ptr++];
>> + plat->rd_ptr %= BUF_COUNT;
>> +
>> + return val;
>> +}
>> +
>> +void ns16550_handle_irq(void *data)
>> +{
>> + struct udevice *dev = (struct udevice *)data;
>> + struct NS16550 *const com_port = dev_get_priv(dev);
>> +
>> + /* Check if interrupt is pending */
>> + if (serial_in(&com_port->iir) & UART_IIR_NO_INT)
>> + return;
>> +
>> + /* Flush all available characters from the RX FIFO into the RX buffer */
>> + rx_fifo_to_buf(dev);
>> +}
>> +
>> +#else /* CONFIG_SERIAL_IRQ_BUFFER */
>> +
>> +static int rx_pending(struct udevice *dev)
>> +{
>> + struct NS16550 *const com_port = dev_get_priv(dev);
>> +
>> + return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
>> +}
>> +
>> +static int rx_get(struct udevice *dev)
>> +{
>> + struct NS16550 *const com_port = dev_get_priv(dev);
>> +
>> + return serial_in(&com_port->rbr);
>> +}
>> +
>> +#endif /* CONFIG_SERIAL_IRQ_BUFFER */
>> +
>> static int ns16550_serial_putc(struct udevice *dev, const char ch)
>> {
>> struct NS16550 *const com_port = dev_get_priv(dev);
>> @@ -340,19 +414,17 @@ static int ns16550_serial_pending(struct udevice *dev, bool input)
>> struct NS16550 *const com_port = dev_get_priv(dev);
>>
>> if (input)
>> - return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
>> + return rx_pending(dev);
>> else
>> return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1;
>> }
>>
>> static int ns16550_serial_getc(struct udevice *dev)
>> {
>> - struct NS16550 *const com_port = dev_get_priv(dev);
>> -
>> - if (!(serial_in(&com_port->lsr) & UART_LSR_DR))
>> + if (!ns16550_serial_pending(dev, true))
>> return -EAGAIN;
>>
>> - return serial_in(&com_port->rbr);
>> + return rx_get(dev);
>> }
>>
>> static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
>> @@ -375,6 +447,34 @@ int ns16550_serial_probe(struct udevice *dev)
>> com_port->plat = dev_get_platdata(dev);
>> NS16550_init(com_port, -1);
>>
>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>> + if (gd->flags & GD_FLG_RELOC) {
>> + struct ns16550_platdata *plat = dev->platdata;
>> +
>> + /* Allocate the RX buffer */
>> + plat->buf = malloc(BUF_COUNT);
>> +
>> + /* Install the interrupt handler */
>> + irq_install_handler(plat->irq, ns16550_handle_irq, dev);
>> +
>> + /* Enable RX interrupts */
>> + serial_out(UART_IER_RDI, &com_port->ier);
>> + }
>> +#endif
>> +
>> + return 0;
>> +}
>> +
>> +static int ns16550_serial_remove(struct udevice *dev)
>> +{
>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>> + if (gd->flags & GD_FLG_RELOC) {
>> + struct ns16550_platdata *plat = dev->platdata;
>> +
>> + irq_free_handler(plat->irq);
>
> We should also turn off interrupts on NS16550 too by writing zero to
> com_port->ie.
Will add in v3.
>> + }
>> +#endif
>> +
>> return 0;
>> }
>>
>> @@ -459,6 +559,15 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev)
>> if (port_type == PORT_JZ4780)
>> plat->fcr |= UART_FCR_UME;
>>
>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>> + plat->irq = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
>> + "interrupts", 0);
>
> Should we handle NS16550 on the PCI bus? If not, better put a comment here.
Actually I'm also using this RX IRQ buffer for the HS-UART on BayTrail
connected via PCI (pciuart0: uart at 1e,3). I've added the interrupt
property to the DT for this to work for now. I'll check, if I can read
the interrupt from the PCI config space instead.
>> + if (!plat->irq) {
>
> If this is not provided, I think we should print a warning and fall
> back to use the original polling mode.
Good idea, will change in v3.
>> + debug("ns16550 interrupt not provided\n");
>> + return -EINVAL;
>> + }
>> +#endif
>> +
>> return 0;
>> }
>> #endif
>> @@ -506,6 +615,7 @@ U_BOOT_DRIVER(ns16550_serial) = {
>> #endif
>> .priv_auto_alloc_size = sizeof(struct NS16550),
>> .probe = ns16550_serial_probe,
>> + .remove = ns16550_serial_remove,
>> .ops = &ns16550_serial_ops,
>> .flags = DM_FLAG_PRE_RELOC,
>> };
>> diff --git a/include/ns16550.h b/include/ns16550.h
>> index 5fcbcd2e74..7e9944d0d9 100644
>> --- a/include/ns16550.h
>> +++ b/include/ns16550.h
>> @@ -51,6 +51,10 @@
>> * @base: Base register address
>> * @reg_shift: Shift size of registers (0=byte, 1=16bit, 2=32bit...)
>> * @clock: UART base clock speed in Hz
>> + *
>> + * @buf: Pointer to the RX interrupt buffer
>> + * @rd_ptr: Read pointer in the RX interrupt buffer
>> + * @wr_ptr: Write pointer in the RX interrupt buffer
>> */
>> struct ns16550_platdata {
>> unsigned long base;
>> @@ -58,6 +62,12 @@ struct ns16550_platdata {
>> int clock;
>> int reg_offset;
>> u32 fcr;
>> +
>> + int irq;
>> +
>> + char *buf;
>> + int rd_ptr;
>> + int wr_ptr;
>> };
>>
>> struct udevice;
>> --
>
> I've tested the patch on MinnowMax, and pasted a really long text.
> Below are 5 times results: (using SecureCRT as my terminal application
> on Windows)
>
> => Note: when interrupt transfer complets, the xHC generates an event
> TRB with TRB type 'Transfer Event', which is exactly the same as a
> control or bulk transfer. In our xHCI driver, xhci_wait_foer_event()
> checks the event TRB t ype and depending on the timing,g it may
> wrongly return an event TRB to the caller which originates from
> another USB device (different slot ID is checked by the driver and if
> different a "BUG" will be thrown).
> Unknown command 'Note:' - try 'help'
> => Note: when interrupt transfer complets, thee xHC generates an event
> TRB withn TRB type 'Transfer Event', which is exactly the same as a
> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
> checks the event TRB type and depending on the timing,g it may wrongly
> return an event rTRB to the caller which originates from another USB
> device (different slot ID is checked by the driver and if different a
> "BUG" will be thrown).
> Unknown command 'Note:' - try 'help'
> => Note: when interrupt transfer complets, thee xHC generates an event
> TRB withn TRB type 'Transfer Event', which is exactly the same as a
> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
> checks the event TRB type and depending on the timing, it may wrongly
> return an event rTRB to the caller which originates from another USB
> device (different slot ID is checked by the driver and if different a
> "BUG" will be thrown).
> Unknown command 'Note:' - try 'help'
> => Note: when interrupt transfer complets, the xHC generates an event
> TRB with TRB type 'Transfer Event', whicfh is exactly the same as a
> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
> checks the event TRB type and depending on the timing, it may wrongly
> return an event TRB to the caller which originatres from another USB
> device (different slot ID is checked by the driver and if different a
> "BUG" will be thrown).
> Unknown command 'Note:' - try 'help'
> => Note: when interrupt transfer complets, thee xHC generates an event
> TRB with TRB type 'Transfer Event', which is exactly the same as a
> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
> checks the event TRB type and depending on the timing, it may wrongly
> return an event rTRB to the caller which originatres from another USB
> device (different slot ID is checked by the driver and if different a
> "BUG" will be thrown).
> Unknown command 'Note:' - try 'help'
>
> As you see, none of 5 is completely correct. The 1st and 2nd have
> issues in "t ype" and "timing,g". The 2nd/4th/5th have issues in "thee
> xHC". The 2nd/3rd/5th have issues in "event rTRB". The 4th has
> "whicfh".
Interesting, I've not seen such issues. Probably since I only checked
the length of the received / echoed lines and roughly scanned the text
for some incorrectness. Is the length always correct in your tests?
Does increasing the buffer size help here?
Thanks,
Stefan
^ permalink raw reply [flat|nested] 11+ messages in thread
* [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support
2017-07-17 8:18 ` Stefan Roese
@ 2017-07-17 9:26 ` Bin Meng
2017-07-17 9:43 ` Stefan Roese
0 siblings, 1 reply; 11+ messages in thread
From: Bin Meng @ 2017-07-17 9:26 UTC (permalink / raw)
To: u-boot
Hi Stefan,
On Mon, Jul 17, 2017 at 4:18 PM, Stefan Roese <sr@denx.de> wrote:
> Hi Bin,
>
>
> On 17.07.2017 04:13, Bin Meng wrote:
>>
>> On Fri, Jul 14, 2017 at 11:25 PM, Stefan Roese <sr@denx.de> wrote:
>>>
>>> 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 interrupt
>>> buffer support for NS16550 style UARTs. With this option enabled, I was
>>> able paste really long lines into the U-Boot console, without any
>>> characters missing.
>>>
>>
>> Great to see this improvement! Some comments below:
>>
>>> Signed-off-by: Stefan Roese <sr@denx.de>
>>> Reviewed-by: Simon Glass <sjg@chromium.org>
>>> Cc: Bin Meng <bmeng.cn@gmail.com>
>>> ---
>>> v2:
>>> - Added Simon's RB
>>> - Added irq_free_handler to newly introduced driver remove function
>>> - Added comments to the introduced variables on the ns16550_platdata
>>> struct
>>>
>>> drivers/serial/Kconfig | 10 ++++
>>> drivers/serial/ns16550.c | 120
>>> +++++++++++++++++++++++++++++++++++++++++++++--
>>> include/ns16550.h | 10 ++++
>>> 3 files changed, 135 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
>>> index b7dd2ac103..8284febae3 100644
>>> --- a/drivers/serial/Kconfig
>>> +++ b/drivers/serial/Kconfig
>>> @@ -64,6 +64,16 @@ config DM_SERIAL
>>> implements serial_putc() etc. The uclass interface is
>>> defined in include/serial.h.
>>>
>>> +config SERIAL_IRQ_BUFFER
>>> + bool "Enable RX interrupt buffer for serial input"
>>> + depends on DM_SERIAL
>>> + default n
>>
>>
>> This line can be removed, as its default state is 'n'
>
>
> Okay, will update.
>
>>> + help
>>> + Enable RX interrupt 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 SPL_DM_SERIAL
>>> bool "Enable Driver Model for serial drivers in SPL"
>>> depends on DM_SERIAL
>>> diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
>>> index e0e70244ce..0c43cb1fe2 100644
>>> --- a/drivers/serial/ns16550.c
>>> +++ b/drivers/serial/ns16550.c
>>> @@ -315,6 +315,80 @@ DEBUG_UART_FUNCS
>>> #endif
>>>
>>> #ifdef CONFIG_DM_SERIAL
>>> +
>>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>>> +
>>> +#define BUF_COUNT 256
>>
>>
>> Should this be configurable via Kconfig?
>
>
> Good idea. I'll add it in v3.
>
>
>>> +
>>> +static void rx_fifo_to_buf(struct udevice *dev)
>>> +{
>>> + struct NS16550 *const com_port = dev_get_priv(dev);
>>> + struct ns16550_platdata *plat = dev->platdata;
>>> +
>>> + /* Read all available chars into buffer */
>>> + while ((serial_in(&com_port->lsr) & UART_LSR_DR)) {
>>> + plat->buf[plat->wr_ptr++] = serial_in(&com_port->rbr);
>>> + plat->wr_ptr %= BUF_COUNT;
>>> + }
>>> +}
>>> +
>>> +static int rx_pending(struct udevice *dev)
>>> +{
>>> + struct ns16550_platdata *plat = dev->platdata;
>>> +
>>> + /*
>>> + * At startup it may happen, that some already received chars are
>>> + * "stuck" in the RX FIFO, even with the interrupt enabled. This
>>> + * RX FIFO flushing makes sure, that these chars are read out and
>>> + * the RX interrupts works as expected.
>>> + */
>>> + rx_fifo_to_buf(dev);
>>> +
>>> + return plat->rd_ptr != plat->wr_ptr ? 1 : 0;
>>> +}
>>> +
>>> +static int rx_get(struct udevice *dev)
>>> +{
>>> + struct ns16550_platdata *plat = dev->platdata;
>>> + char val;
>>> +
>>> + val = plat->buf[plat->rd_ptr++];
>>> + plat->rd_ptr %= BUF_COUNT;
>>> +
>>> + return val;
>>> +}
>>> +
>>> +void ns16550_handle_irq(void *data)
>>> +{
>>> + struct udevice *dev = (struct udevice *)data;
>>> + struct NS16550 *const com_port = dev_get_priv(dev);
>>> +
>>> + /* Check if interrupt is pending */
>>> + if (serial_in(&com_port->iir) & UART_IIR_NO_INT)
>>> + return;
>>> +
>>> + /* Flush all available characters from the RX FIFO into the RX
>>> buffer */
>>> + rx_fifo_to_buf(dev);
>>> +}
>>> +
>>> +#else /* CONFIG_SERIAL_IRQ_BUFFER */
>>> +
>>> +static int rx_pending(struct udevice *dev)
>>> +{
>>> + struct NS16550 *const com_port = dev_get_priv(dev);
>>> +
>>> + return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
>>> +}
>>> +
>>> +static int rx_get(struct udevice *dev)
>>> +{
>>> + struct NS16550 *const com_port = dev_get_priv(dev);
>>> +
>>> + return serial_in(&com_port->rbr);
>>> +}
>>> +
>>> +#endif /* CONFIG_SERIAL_IRQ_BUFFER */
>>> +
>>> static int ns16550_serial_putc(struct udevice *dev, const char ch)
>>> {
>>> struct NS16550 *const com_port = dev_get_priv(dev);
>>> @@ -340,19 +414,17 @@ static int ns16550_serial_pending(struct udevice
>>> *dev, bool input)
>>> struct NS16550 *const com_port = dev_get_priv(dev);
>>>
>>> if (input)
>>> - return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
>>> + return rx_pending(dev);
>>> else
>>> return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 :
>>> 1;
>>> }
>>>
>>> static int ns16550_serial_getc(struct udevice *dev)
>>> {
>>> - struct NS16550 *const com_port = dev_get_priv(dev);
>>> -
>>> - if (!(serial_in(&com_port->lsr) & UART_LSR_DR))
>>> + if (!ns16550_serial_pending(dev, true))
>>> return -EAGAIN;
>>>
>>> - return serial_in(&com_port->rbr);
>>> + return rx_get(dev);
>>> }
>>>
>>> static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
>>> @@ -375,6 +447,34 @@ int ns16550_serial_probe(struct udevice *dev)
>>> com_port->plat = dev_get_platdata(dev);
>>> NS16550_init(com_port, -1);
>>>
>>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>>> + if (gd->flags & GD_FLG_RELOC) {
>>> + struct ns16550_platdata *plat = dev->platdata;
>>> +
>>> + /* Allocate the RX buffer */
>>> + plat->buf = malloc(BUF_COUNT);
>>> +
>>> + /* Install the interrupt handler */
>>> + irq_install_handler(plat->irq, ns16550_handle_irq, dev);
>>> +
>>> + /* Enable RX interrupts */
>>> + serial_out(UART_IER_RDI, &com_port->ier);
>>> + }
>>> +#endif
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int ns16550_serial_remove(struct udevice *dev)
>>> +{
>>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>>> + if (gd->flags & GD_FLG_RELOC) {
>>> + struct ns16550_platdata *plat = dev->platdata;
>>> +
>>> + irq_free_handler(plat->irq);
>>
>>
>> We should also turn off interrupts on NS16550 too by writing zero to
>> com_port->ie.
>
>
> Will add in v3.
>
>>> + }
>>> +#endif
>>> +
>>> return 0;
>>> }
>>>
>>> @@ -459,6 +559,15 @@ int ns16550_serial_ofdata_to_platdata(struct udevice
>>> *dev)
>>> if (port_type == PORT_JZ4780)
>>> plat->fcr |= UART_FCR_UME;
>>>
>>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>>> + plat->irq = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
>>> + "interrupts", 0);
>>
>>
>> Should we handle NS16550 on the PCI bus? If not, better put a comment
>> here.
>
>
> Actually I'm also using this RX IRQ buffer for the HS-UART on BayTrail
> connected via PCI (pciuart0: uart at 1e,3). I've added the interrupt
> property to the DT for this to work for now. I'll check, if I can read
> the interrupt from the PCI config space instead.
>
Yes, the interrupt line register is programmed with the value of its
interrupt vector in irq_router_probe(). But you should wait for the
IRQ device to be probed before NS16550.
>>> + if (!plat->irq) {
>>
>>
>> If this is not provided, I think we should print a warning and fall
>> back to use the original polling mode.
>
>
> Good idea, will change in v3.
>
>
>>> + debug("ns16550 interrupt not provided\n");
>>> + return -EINVAL;
>>> + }
>>> +#endif
>>> +
>>> return 0;
>>> }
>>> #endif
>>> @@ -506,6 +615,7 @@ U_BOOT_DRIVER(ns16550_serial) = {
>>> #endif
>>> .priv_auto_alloc_size = sizeof(struct NS16550),
>>> .probe = ns16550_serial_probe,
>>> + .remove = ns16550_serial_remove,
>>> .ops = &ns16550_serial_ops,
>>> .flags = DM_FLAG_PRE_RELOC,
>>> };
>>> diff --git a/include/ns16550.h b/include/ns16550.h
>>> index 5fcbcd2e74..7e9944d0d9 100644
>>> --- a/include/ns16550.h
>>> +++ b/include/ns16550.h
>>> @@ -51,6 +51,10 @@
>>> * @base: Base register address
>>> * @reg_shift: Shift size of registers (0=byte, 1=16bit,
>>> 2=32bit...)
>>> * @clock: UART base clock speed in Hz
>>> + *
>>> + * @buf: Pointer to the RX interrupt buffer
>>> + * @rd_ptr: Read pointer in the RX interrupt buffer
>>> + * @wr_ptr: Write pointer in the RX interrupt buffer
>>> */
>>> struct ns16550_platdata {
>>> unsigned long base;
>>> @@ -58,6 +62,12 @@ struct ns16550_platdata {
>>> int clock;
>>> int reg_offset;
>>> u32 fcr;
>>> +
>>> + int irq;
>>> +
>>> + char *buf;
>>> + int rd_ptr;
>>> + int wr_ptr;
>>> };
>>>
>>> struct udevice;
>>> --
>>
>>
>> I've tested the patch on MinnowMax, and pasted a really long text.
>> Below are 5 times results: (using SecureCRT as my terminal application
>> on Windows)
>>
>> => Note: when interrupt transfer complets, the xHC generates an event
>> TRB with TRB type 'Transfer Event', which is exactly the same as a
>> control or bulk transfer. In our xHCI driver, xhci_wait_foer_event()
>> checks the event TRB t ype and depending on the timing,g it may
>> wrongly return an event TRB to the caller which originates from
>> another USB device (different slot ID is checked by the driver and if
>> different a "BUG" will be thrown).
>> Unknown command 'Note:' - try 'help'
>> => Note: when interrupt transfer complets, thee xHC generates an event
>> TRB withn TRB type 'Transfer Event', which is exactly the same as a
>> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
>> checks the event TRB type and depending on the timing,g it may wrongly
>> return an event rTRB to the caller which originates from another USB
>> device (different slot ID is checked by the driver and if different a
>> "BUG" will be thrown).
>> Unknown command 'Note:' - try 'help'
>> => Note: when interrupt transfer complets, thee xHC generates an event
>> TRB withn TRB type 'Transfer Event', which is exactly the same as a
>> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
>> checks the event TRB type and depending on the timing, it may wrongly
>> return an event rTRB to the caller which originates from another USB
>> device (different slot ID is checked by the driver and if different a
>> "BUG" will be thrown).
>> Unknown command 'Note:' - try 'help'
>> => Note: when interrupt transfer complets, the xHC generates an event
>> TRB with TRB type 'Transfer Event', whicfh is exactly the same as a
>> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
>> checks the event TRB type and depending on the timing, it may wrongly
>> return an event TRB to the caller which originatres from another USB
>> device (different slot ID is checked by the driver and if different a
>> "BUG" will be thrown).
>> Unknown command 'Note:' - try 'help'
>> => Note: when interrupt transfer complets, thee xHC generates an event
>> TRB with TRB type 'Transfer Event', which is exactly the same as a
>> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
>> checks the event TRB type and depending on the timing, it may wrongly
>> return an event rTRB to the caller which originatres from another USB
>> device (different slot ID is checked by the driver and if different a
>> "BUG" will be thrown).
>> Unknown command 'Note:' - try 'help'
>>
>> As you see, none of 5 is completely correct. The 1st and 2nd have
>> issues in "t ype" and "timing,g". The 2nd/4th/5th have issues in "thee
>> xHC". The 2nd/3rd/5th have issues in "event rTRB". The 4th has
>> "whicfh".
>
>
> Interesting, I've not seen such issues. Probably since I only checked
> the length of the received / echoed lines and roughly scanned the text
> for some incorrectness. Is the length always correct in your tests?
> Does increasing the buffer size help here?
>
I increased the buffer size to 1024, but it did not help. I tested it
with the MinnowMax internal UART (0x3f8). As you can see above, the
length is not always equal due to these incorrectness.
Regards,
Bin
^ permalink raw reply [flat|nested] 11+ messages in thread
* [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support
2017-07-17 9:26 ` Bin Meng
@ 2017-07-17 9:43 ` Stefan Roese
2017-07-17 11:18 ` Stefan Roese
0 siblings, 1 reply; 11+ messages in thread
From: Stefan Roese @ 2017-07-17 9:43 UTC (permalink / raw)
To: u-boot
Hi Bin,
On 17.07.2017 11:26, Bin Meng wrote:
<snip>
>>>> + }
>>>> +#endif
>>>> +
>>>> return 0;
>>>> }
>>>>
>>>> @@ -459,6 +559,15 @@ int ns16550_serial_ofdata_to_platdata(struct udevice
>>>> *dev)
>>>> if (port_type == PORT_JZ4780)
>>>> plat->fcr |= UART_FCR_UME;
>>>>
>>>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>>>> + plat->irq = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
>>>> + "interrupts", 0);
>>>
>>>
>>> Should we handle NS16550 on the PCI bus? If not, better put a comment
>>> here.
>>
>>
>> Actually I'm also using this RX IRQ buffer for the HS-UART on BayTrail
>> connected via PCI (pciuart0: uart at 1e,3). I've added the interrupt
>> property to the DT for this to work for now. I'll check, if I can read
>> the interrupt from the PCI config space instead.
>>
>
> Yes, the interrupt line register is programmed with the value of its
> interrupt vector in irq_router_probe(). But you should wait for the
> IRQ device to be probed before NS16550.
I'm currently working on this and this dependency with the PCI interrupt
assignment is problematic. The serial driver is (intentionally) probed
earlier and the PCI interrupts are still unassigned at that stage.
I currently have no real good idea on how to solve this. We could
add a new "probe_late() / probe_irqs_enabled()" DM function for such
cases, but this seems overly complex and adds new bloat to the DM
infrastructure.
Do you have some other (simpler) ideas on how to solve this?
<snip>
>>> I've tested the patch on MinnowMax, and pasted a really long text.
>>> Below are 5 times results: (using SecureCRT as my terminal application
>>> on Windows)
>>>
>>> => Note: when interrupt transfer complets, the xHC generates an event
>>> TRB with TRB type 'Transfer Event', which is exactly the same as a
>>> control or bulk transfer. In our xHCI driver, xhci_wait_foer_event()
>>> checks the event TRB t ype and depending on the timing,g it may
>>> wrongly return an event TRB to the caller which originates from
>>> another USB device (different slot ID is checked by the driver and if
>>> different a "BUG" will be thrown).
>>> Unknown command 'Note:' - try 'help'
>>> => Note: when interrupt transfer complets, thee xHC generates an event
>>> TRB withn TRB type 'Transfer Event', which is exactly the same as a
>>> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
>>> checks the event TRB type and depending on the timing,g it may wrongly
>>> return an event rTRB to the caller which originates from another USB
>>> device (different slot ID is checked by the driver and if different a
>>> "BUG" will be thrown).
>>> Unknown command 'Note:' - try 'help'
>>> => Note: when interrupt transfer complets, thee xHC generates an event
>>> TRB withn TRB type 'Transfer Event', which is exactly the same as a
>>> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
>>> checks the event TRB type and depending on the timing, it may wrongly
>>> return an event rTRB to the caller which originates from another USB
>>> device (different slot ID is checked by the driver and if different a
>>> "BUG" will be thrown).
>>> Unknown command 'Note:' - try 'help'
>>> => Note: when interrupt transfer complets, the xHC generates an event
>>> TRB with TRB type 'Transfer Event', whicfh is exactly the same as a
>>> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
>>> checks the event TRB type and depending on the timing, it may wrongly
>>> return an event TRB to the caller which originatres from another USB
>>> device (different slot ID is checked by the driver and if different a
>>> "BUG" will be thrown).
>>> Unknown command 'Note:' - try 'help'
>>> => Note: when interrupt transfer complets, thee xHC generates an event
>>> TRB with TRB type 'Transfer Event', which is exactly the same as a
>>> control or bulk transfer. In our xHCI driver, xhci_wait_for_event()
>>> checks the event TRB type and depending on the timing, it may wrongly
>>> return an event rTRB to the caller which originatres from another USB
>>> device (different slot ID is checked by the driver and if different a
>>> "BUG" will be thrown).
>>> Unknown command 'Note:' - try 'help'
>>>
>>> As you see, none of 5 is completely correct. The 1st and 2nd have
>>> issues in "t ype" and "timing,g". The 2nd/4th/5th have issues in "thee
>>> xHC". The 2nd/3rd/5th have issues in "event rTRB". The 4th has
>>> "whicfh".
>>
>>
>> Interesting, I've not seen such issues. Probably since I only checked
>> the length of the received / echoed lines and roughly scanned the text
>> for some incorrectness. Is the length always correct in your tests?
>> Does increasing the buffer size help here?
>>
>
> I increased the buffer size to 1024, but it did not help. I tested it
> with the MinnowMax internal UART (0x3f8). As you can see above, the
> length is not always equal due to these incorrectness.
Thanks for testing and confirming. Currently I have no idea, where
this problem might come from. And my time is limited right now,
as I'll be on vacation for 10 days starting on Wednesday. I still think
that this RX interrupt buffer support is a big improvement over the
"polling only" mode, even in this current version. I can re-visit this
driver in a few weeks to work on a fix for the issue that you have
found.
Thanks,
Stefan
^ permalink raw reply [flat|nested] 11+ messages in thread
* [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support
2017-07-17 9:43 ` Stefan Roese
@ 2017-07-17 11:18 ` Stefan Roese
2017-07-17 14:49 ` Bin Meng
0 siblings, 1 reply; 11+ messages in thread
From: Stefan Roese @ 2017-07-17 11:18 UTC (permalink / raw)
To: u-boot
Hi Bin,
On 17.07.2017 11:43, Stefan Roese wrote:
> On 17.07.2017 11:26, Bin Meng wrote:
>
> <snip>
>
>>>>> + }
>>>>> +#endif
>>>>> +
>>>>> return 0;
>>>>> }
>>>>>
>>>>> @@ -459,6 +559,15 @@ int ns16550_serial_ofdata_to_platdata(struct
>>>>> udevice
>>>>> *dev)
>>>>> if (port_type == PORT_JZ4780)
>>>>> plat->fcr |= UART_FCR_UME;
>>>>>
>>>>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>>>>> + plat->irq = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
>>>>> + "interrupts", 0);
>>>>
>>>>
>>>> Should we handle NS16550 on the PCI bus? If not, better put a comment
>>>> here.
>>>
>>>
>>> Actually I'm also using this RX IRQ buffer for the HS-UART on BayTrail
>>> connected via PCI (pciuart0: uart at 1e,3). I've added the interrupt
>>> property to the DT for this to work for now. I'll check, if I can read
>>> the interrupt from the PCI config space instead.
>>>
>>
>> Yes, the interrupt line register is programmed with the value of its
>> interrupt vector in irq_router_probe(). But you should wait for the
>> IRQ device to be probed before NS16550.
>
> I'm currently working on this and this dependency with the PCI interrupt
> assignment is problematic. The serial driver is (intentionally) probed
> earlier and the PCI interrupts are still unassigned at that stage.
>
> I currently have no real good idea on how to solve this. We could
> add a new "probe_late() / probe_irqs_enabled()" DM function for such
> cases, but this seems overly complex and adds new bloat to the DM
> infrastructure.
>
> Do you have some other (simpler) ideas on how to solve this?
Another idea would be, to write the PCI interrupt vectors earlier
in the boot stage, before the serial driver is probed. The interrupts
don't need to be enabled for this.
What do you think of this idea?
Thanks,
Stefan
^ permalink raw reply [flat|nested] 11+ messages in thread
* [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support
2017-07-17 11:18 ` Stefan Roese
@ 2017-07-17 14:49 ` Bin Meng
2017-08-10 9:52 ` Stefan Roese
0 siblings, 1 reply; 11+ messages in thread
From: Bin Meng @ 2017-07-17 14:49 UTC (permalink / raw)
To: u-boot
+Simon,
Hi Stefan,
On Mon, Jul 17, 2017 at 7:18 PM, Stefan Roese <sr@denx.de> wrote:
> Hi Bin,
>
>
> On 17.07.2017 11:43, Stefan Roese wrote:
>>
>> On 17.07.2017 11:26, Bin Meng wrote:
>>
>> <snip>
>>
>>>>>> + }
>>>>>> +#endif
>>>>>> +
>>>>>> return 0;
>>>>>> }
>>>>>>
>>>>>> @@ -459,6 +559,15 @@ int ns16550_serial_ofdata_to_platdata(struct
>>>>>> udevice
>>>>>> *dev)
>>>>>> if (port_type == PORT_JZ4780)
>>>>>> plat->fcr |= UART_FCR_UME;
>>>>>>
>>>>>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>>>>>> + plat->irq = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
>>>>>> + "interrupts", 0);
>>>>>
>>>>>
>>>>>
>>>>> Should we handle NS16550 on the PCI bus? If not, better put a comment
>>>>> here.
>>>>
>>>>
>>>>
>>>> Actually I'm also using this RX IRQ buffer for the HS-UART on BayTrail
>>>> connected via PCI (pciuart0: uart at 1e,3). I've added the interrupt
>>>> property to the DT for this to work for now. I'll check, if I can read
>>>> the interrupt from the PCI config space instead.
>>>>
>>>
>>> Yes, the interrupt line register is programmed with the value of its
>>> interrupt vector in irq_router_probe(). But you should wait for the
>>> IRQ device to be probed before NS16550.
>>
>>
>> I'm currently working on this and this dependency with the PCI interrupt
>> assignment is problematic. The serial driver is (intentionally) probed
>> earlier and the PCI interrupts are still unassigned at that stage.
>>
>> I currently have no real good idea on how to solve this. We could
>> add a new "probe_late() / probe_irqs_enabled()" DM function for such
>> cases, but this seems overly complex and adds new bloat to the DM
>> infrastructure.
>>
>> Do you have some other (simpler) ideas on how to solve this?
>
>
> Another idea would be, to write the PCI interrupt vectors earlier
> in the boot stage, before the serial driver is probed. The interrupts
> don't need to be enabled for this.
>
> What do you think of this idea?
>
Yep, I believe we can move the following codes from
arch/x86/cpu/i386/interrupts.c
/* Try to set up the interrupt router, but don't require one */
ret = uclass_first_device_err(UCLASS_IRQ, &dev);
if (ret && ret != -ENODEV)
return ret;
to arch_cpu_init_dm() in board_f.c.
Simon, do you have other better solution for this?
> Thanks for testing and confirming. Currently I have no idea, where
> this problem might come from. And my time is limited right now,
> as I'll be on vacation for 10 days starting on Wednesday. I still think
> that this RX interrupt buffer support is a big improvement over the
> "polling only" mode, even in this current version. I can re-visit this
> driver in a few weeks to work on a fix for the issue that you have
> found.
Sure, let's re-visit this in the coming days.
Regards,
Bin
^ permalink raw reply [flat|nested] 11+ messages in thread
* [U-Boot] [U-Boot, v2] serial: ns16550: Add RX interrupt buffer support
2017-07-14 15:25 [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support Stefan Roese
2017-07-17 2:13 ` Bin Meng
@ 2017-07-25 0:44 ` Tom Rini
2017-07-25 1:18 ` Bin Meng
1 sibling, 1 reply; 11+ messages in thread
From: Tom Rini @ 2017-07-25 0:44 UTC (permalink / raw)
To: u-boot
On Fri, Jul 14, 2017 at 05:25:54PM +0200, Stefan Roese wrote:
> 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 interrupt
> buffer support for NS16550 style UARTs. 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 <sr@denx.de>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> Cc: Bin Meng <bmeng.cn@gmail.com>
Applied to u-boot/master, thanks!
--
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20170724/b00a92be/attachment.sig>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [U-Boot] [U-Boot, v2] serial: ns16550: Add RX interrupt buffer support
2017-07-25 0:44 ` [U-Boot] [U-Boot, " Tom Rini
@ 2017-07-25 1:18 ` Bin Meng
2017-07-25 1:20 ` Tom Rini
0 siblings, 1 reply; 11+ messages in thread
From: Bin Meng @ 2017-07-25 1:18 UTC (permalink / raw)
To: u-boot
Hi Tom,
On Tue, Jul 25, 2017 at 8:44 AM, Tom Rini <trini@konsulko.com> wrote:
> On Fri, Jul 14, 2017 at 05:25:54PM +0200, Stefan Roese wrote:
>
>> 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 interrupt
>> buffer support for NS16550 style UARTs. 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 <sr@denx.de>
>> Reviewed-by: Simon Glass <sjg@chromium.org>
>> Cc: Bin Meng <bmeng.cn@gmail.com>
>
> Applied to u-boot/master, thanks!
>
I believe Stefan will send a v3 to address my comments.
Regards,
Bin
^ permalink raw reply [flat|nested] 11+ messages in thread
* [U-Boot] [U-Boot, v2] serial: ns16550: Add RX interrupt buffer support
2017-07-25 1:18 ` Bin Meng
@ 2017-07-25 1:20 ` Tom Rini
0 siblings, 0 replies; 11+ messages in thread
From: Tom Rini @ 2017-07-25 1:20 UTC (permalink / raw)
To: u-boot
On Tue, Jul 25, 2017 at 09:18:02AM +0800, Bin Meng wrote:
> Hi Tom,
>
> On Tue, Jul 25, 2017 at 8:44 AM, Tom Rini <trini@konsulko.com> wrote:
> > On Fri, Jul 14, 2017 at 05:25:54PM +0200, Stefan Roese wrote:
> >
> >> 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 interrupt
> >> buffer support for NS16550 style UARTs. 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 <sr@denx.de>
> >> Reviewed-by: Simon Glass <sjg@chromium.org>
> >> Cc: Bin Meng <bmeng.cn@gmail.com>
> >
> > Applied to u-boot/master, thanks!
>
> I believe Stefan will send a v3 to address my comments.
Oh poop, sorry!
--
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20170724/e20bd0dd/attachment.sig>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support
2017-07-17 14:49 ` Bin Meng
@ 2017-08-10 9:52 ` Stefan Roese
0 siblings, 0 replies; 11+ messages in thread
From: Stefan Roese @ 2017-08-10 9:52 UTC (permalink / raw)
To: u-boot
Hi Bin,
On 17.07.2017 16:49, Bin Meng wrote:
> +Simon,
>
> Hi Stefan,
>
> On Mon, Jul 17, 2017 at 7:18 PM, Stefan Roese <sr@denx.de> wrote:
>> Hi Bin,
>>
>>
>> On 17.07.2017 11:43, Stefan Roese wrote:
>>>
>>> On 17.07.2017 11:26, Bin Meng wrote:
>>>
>>> <snip>
>>>
>>>>>>> + }
>>>>>>> +#endif
>>>>>>> +
>>>>>>> return 0;
>>>>>>> }
>>>>>>>
>>>>>>> @@ -459,6 +559,15 @@ int ns16550_serial_ofdata_to_platdata(struct
>>>>>>> udevice
>>>>>>> *dev)
>>>>>>> if (port_type == PORT_JZ4780)
>>>>>>> plat->fcr |= UART_FCR_UME;
>>>>>>>
>>>>>>> +#if CONFIG_IS_ENABLED(SERIAL_IRQ_BUFFER)
>>>>>>> + plat->irq = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
>>>>>>> + "interrupts", 0);
>>>>>>
>>>>>>
>>>>>>
>>>>>> Should we handle NS16550 on the PCI bus? If not, better put a comment
>>>>>> here.
>>>>>
>>>>>
>>>>>
>>>>> Actually I'm also using this RX IRQ buffer for the HS-UART on BayTrail
>>>>> connected via PCI (pciuart0: uart at 1e,3). I've added the interrupt
>>>>> property to the DT for this to work for now. I'll check, if I can read
>>>>> the interrupt from the PCI config space instead.
>>>>>
>>>>
>>>> Yes, the interrupt line register is programmed with the value of its
>>>> interrupt vector in irq_router_probe(). But you should wait for the
>>>> IRQ device to be probed before NS16550.
>>>
>>>
>>> I'm currently working on this and this dependency with the PCI interrupt
>>> assignment is problematic. The serial driver is (intentionally) probed
>>> earlier and the PCI interrupts are still unassigned at that stage.
>>>
>>> I currently have no real good idea on how to solve this. We could
>>> add a new "probe_late() / probe_irqs_enabled()" DM function for such
>>> cases, but this seems overly complex and adds new bloat to the DM
>>> infrastructure.
>>>
>>> Do you have some other (simpler) ideas on how to solve this?
>>
>>
>> Another idea would be, to write the PCI interrupt vectors earlier
>> in the boot stage, before the serial driver is probed. The interrupts
>> don't need to be enabled for this.
>>
>> What do you think of this idea?
>>
>
> Yep, I believe we can move the following codes from
> arch/x86/cpu/i386/interrupts.c
>
> /* Try to set up the interrupt router, but don't require one */
> ret = uclass_first_device_err(UCLASS_IRQ, &dev);
> if (ret && ret != -ENODEV)
> return ret;
>
> to arch_cpu_init_dm() in board_f.c.
This does not seem to work. This IRQ routing code is still only
called pretty late in the init process:
U-Boot 2017.09-rc1-00210-gab169101b0-dirty (Aug 10 2017 - 11:29:37 +0200)
CPU: x86_64, vendor Intel, device 30679h
DRAM: 4 GiB
MMC: pci_mmc: 0, pci_mmc: 1, pci_mmc: 2
SF: Detected w25q64cv with page size 256 Bytes, erase size 4 KiB, total 8 MiB
Model: theadorable-x86-DFI-BT700
SF: Detected w25q64cv with page size 256 Bytes, erase size 4 KiB, total 8 MiB
Net: No ethernet found.
irq_router_common_init (245)
Assigning IRQ 5 to PCI device 0.2.0 (INTA)
Assigning IRQ 5 to PCI device 0.11.0 (INTA)
Assigning IRQ 5 to PCI device 0.12.0 (INTA)
Assigning IRQ 5 to PCI device 0.13.0 (INTA)
Assigning IRQ 5 to PCI device 0.14.0 (INTA)
Assigning IRQ 5 to PCI device 0.15.0 (INTA)
Assigning IRQ 5 to PCI device 0.16.0 (INTA)
Assigning IRQ 5 to PCI device 0.17.0 (INTA)
Assigning IRQ 5 to PCI device 0.18.0 (INTA)
Assigning IRQ 7 to PCI device 0.18.1 (INTC)
Assigning IRQ 10 to PCI device 0.18.2 (INTD)
Assigning IRQ 6 to PCI device 0.18.3 (INTB)
Assigning IRQ 5 to PCI device 0.18.4 (INTA)
Assigning IRQ 7 to PCI device 0.18.5 (INTC)
Assigning IRQ 10 to PCI device 0.18.6 (INTD)
Assigning IRQ 6 to PCI device 0.18.7 (INTB)
Assigning IRQ 5 to PCI device 0.1c.0 (INTA)
Assigning IRQ 5 to PCI device 0.1e.0 (INTA)
Assigning IRQ 10 to PCI device 0.1e.1 (INTD)
Assigning IRQ 6 to PCI device 0.1e.2 (INTB)
Assigning IRQ 7 to PCI device 0.1e.3 (INTC)
Assigning IRQ 10 to PCI device 0.1e.4 (INTD)
Assigning IRQ 6 to PCI device 0.1e.5 (INTB)
Assigning IRQ 6 to PCI device 0.1f.3 (INTB)
scanning bus for devices...
I see that this PCI interrupt routing is taken from the dts file. An
easy solution would be, to just add an interrupt entry to the PCI
UART device tree node:
------------------------- arch/x86/dts/dfi-bt700.dtsi -------------------------
index b62e00ff1f..1ccdf5d24b 100644
@@ -115,6 +115,7 @@
reg-shift = <2>;
clock-frequency = <58982400>;
current-speed = <115200>;
+ interrupts = <7>;
};
No need to change any of the interrupt related code and its
init sequence this way.
What do you think? Would this be acceptable?
Thanks,
Stefan
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2017-08-10 9:52 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-07-14 15:25 [U-Boot] [PATCH v2] serial: ns16550: Add RX interrupt buffer support Stefan Roese
2017-07-17 2:13 ` Bin Meng
2017-07-17 8:18 ` Stefan Roese
2017-07-17 9:26 ` Bin Meng
2017-07-17 9:43 ` Stefan Roese
2017-07-17 11:18 ` Stefan Roese
2017-07-17 14:49 ` Bin Meng
2017-08-10 9:52 ` Stefan Roese
2017-07-25 0:44 ` [U-Boot] [U-Boot, " Tom Rini
2017-07-25 1:18 ` Bin Meng
2017-07-25 1:20 ` Tom Rini
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox