Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serial/uart_16550: Wait before setting Line Control Register (Synopsys DesignWare 8250) #10019

Merged
merged 1 commit into from Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions drivers/serial/Kconfig-16550
Expand Up @@ -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
74 changes: 74 additions & 0 deletions drivers/serial/uart_16550.c
Expand Up @@ -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
****************************************************************************/
Expand Down Expand Up @@ -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
****************************************************************************/
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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));
Expand All @@ -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);
Expand Down
30 changes: 18 additions & 12 deletions include/nuttx/serial/uart_16550.h
Expand Up @@ -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)
Expand All @@ -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 *************************************************/

Expand Down Expand Up @@ -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
****************************************************************************/
Expand Down