diff --git a/drivers/serial/Kconfig-16550 b/drivers/serial/Kconfig-16550 index 576a2cb2fd8b7..6d57487b7fa1c 100644 --- a/drivers/serial/Kconfig-16550 +++ b/drivers/serial/Kconfig-16550 @@ -519,4 +519,13 @@ config 16550_ADDRWIDTH Default: 8 Note: 0 means auto detect address size (uintptr_t) +config 16550_WAIT_LCR + bool "Wait for UART before setting LCR" + default n + ---help--- + Before setting the Line Control Register (LCR), wait until UART is + not busy. This is required for Synopsys DesignWare 8250, which + will trigger spurious interrupts when setting the LCR without + waiting. Default: n + endif # 16550_UART diff --git a/drivers/serial/uart_16550.c b/drivers/serial/uart_16550.c index 3d5175617c5d6..418a91bca4cc7 100644 --- a/drivers/serial/uart_16550.c +++ b/drivers/serial/uart_16550.c @@ -52,6 +52,14 @@ #ifdef CONFIG_16550_UART +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Timeout for UART Busy Wait, in milliseconds */ + +#define UART_TIMEOUT_MS 100 + /**************************************************************************** * Private Types ****************************************************************************/ @@ -622,6 +630,43 @@ static inline void u16550_serialout(FAR struct u16550_s *priv, int offset, #endif } +#ifdef CONFIG_16550_WAIT_LCR +/**************************************************************************** + * Name: u16550_wait + * + * Description: + * Wait until UART is not busy. This is needed before writing to LCR. + * Otherwise we will get spurious interrupts on Synopsys DesignWare 8250. + * + * Input Parameters: + * priv: UART Struct + * + * Returned Value: + * Zero (OK) on success; ERROR if timeout. + * + ****************************************************************************/ + +static int u16550_wait(FAR struct u16550_s *priv) +{ + int i; + + for (i = 0; i < UART_TIMEOUT_MS; i++) + { + uint32_t status = u16550_serialin(priv, UART_USR_OFFSET); + + if ((status & UART_USR_BUSY) == 0) + { + return OK; + } + + up_mdelay(1); + } + + _err("UART timeout\n"); + return ERROR; +} +#endif /* CONFIG_16550_WAIT_LCR */ + /**************************************************************************** * Name: u16550_disableuartint ****************************************************************************/ @@ -667,6 +712,15 @@ static inline void u16550_enablebreaks(FAR struct u16550_s *priv, lcr &= ~UART_LCR_BRK; } +#ifdef CONFIG_16550_WAIT_LCR + /* Wait till UART is not busy before setting LCR */ + + if (u16550_wait(priv) < 0) + { + _err("UART wait failed\n"); + } +#endif /* CONFIG_16550_WAIT_LCR */ + u16550_serialout(priv, UART_LCR_OFFSET, lcr); } @@ -761,6 +815,16 @@ static int u16550_setup(FAR struct uart_dev_s *dev) lcr |= (UART_LCR_PEN | UART_LCR_EPS); } +#ifdef CONFIG_16550_WAIT_LCR + /* Wait till UART is not busy before setting LCR */ + + if (u16550_wait(priv) < 0) + { + _err("UART wait failed\n"); + return ERROR; + } +#endif /* CONFIG_16550_WAIT_LCR */ + /* Enter DLAB=1 */ u16550_serialout(priv, UART_LCR_OFFSET, (lcr | UART_LCR_DLAB)); @@ -771,6 +835,16 @@ static int u16550_setup(FAR struct uart_dev_s *dev) u16550_serialout(priv, UART_DLM_OFFSET, div >> 8); u16550_serialout(priv, UART_DLL_OFFSET, div & 0xff); +#ifdef CONFIG_16550_WAIT_LCR + /* Wait till UART is not busy before setting LCR */ + + if (u16550_wait(priv) < 0) + { + _err("UART wait failed\n"); + return ERROR; + } +#endif /* CONFIG_16550_WAIT_LCR */ + /* Clear DLAB */ u16550_serialout(priv, UART_LCR_OFFSET, lcr); diff --git a/include/nuttx/serial/uart_16550.h b/include/nuttx/serial/uart_16550.h index e2aaa72ac1237..8c4318f4bd31b 100644 --- a/include/nuttx/serial/uart_16550.h +++ b/include/nuttx/serial/uart_16550.h @@ -172,18 +172,19 @@ /* Register offsets *********************************************************/ -#define UART_RBR_INCR 0 /* (DLAB =0) Receiver Buffer Register */ -#define UART_THR_INCR 0 /* (DLAB =0) Transmit Holding Register */ -#define UART_DLL_INCR 0 /* (DLAB =1) Divisor Latch LSB */ -#define UART_DLM_INCR 1 /* (DLAB =1) Divisor Latch MSB */ -#define UART_IER_INCR 1 /* (DLAB =0) Interrupt Enable Register */ -#define UART_IIR_INCR 2 /* Interrupt ID Register */ -#define UART_FCR_INCR 2 /* FIFO Control Register */ -#define UART_LCR_INCR 3 /* Line Control Register */ -#define UART_MCR_INCR 4 /* Modem Control Register */ -#define UART_LSR_INCR 5 /* Line Status Register */ -#define UART_MSR_INCR 6 /* Modem Status Register */ -#define UART_SCR_INCR 7 /* Scratch Pad Register */ +#define UART_RBR_INCR 0 /* (DLAB =0) Receiver Buffer Register */ +#define UART_THR_INCR 0 /* (DLAB =0) Transmit Holding Register */ +#define UART_DLL_INCR 0 /* (DLAB =1) Divisor Latch LSB */ +#define UART_DLM_INCR 1 /* (DLAB =1) Divisor Latch MSB */ +#define UART_IER_INCR 1 /* (DLAB =0) Interrupt Enable Register */ +#define UART_IIR_INCR 2 /* Interrupt ID Register */ +#define UART_FCR_INCR 2 /* FIFO Control Register */ +#define UART_LCR_INCR 3 /* Line Control Register */ +#define UART_MCR_INCR 4 /* Modem Control Register */ +#define UART_LSR_INCR 5 /* Line Status Register */ +#define UART_MSR_INCR 6 /* Modem Status Register */ +#define UART_SCR_INCR 7 /* Scratch Pad Register */ +#define UART_USR_INCR 31 /* UART Status Register */ #define UART_RBR_OFFSET (CONFIG_16550_REGINCR*UART_RBR_INCR) #define UART_THR_OFFSET (CONFIG_16550_REGINCR*UART_THR_INCR) @@ -197,6 +198,7 @@ #define UART_LSR_OFFSET (CONFIG_16550_REGINCR*UART_LSR_INCR) #define UART_MSR_OFFSET (CONFIG_16550_REGINCR*UART_MSR_INCR) #define UART_SCR_OFFSET (CONFIG_16550_REGINCR*UART_SCR_INCR) +#define UART_USR_OFFSET (CONFIG_16550_REGINCR*UART_USR_INCR) /* Register bit definitions *************************************************/ @@ -298,6 +300,10 @@ #define UART_SCR_MASK (0xff) /* Bits 0-7: SCR data */ +/* USR UART Status Register */ + +#define UART_USR_BUSY (1 << 0) /* Bit 0: UART Busy */ + /**************************************************************************** * Public Types ****************************************************************************/