serial/uart_16550: Wait before setting Line Control Register (Synopsys DesignWare 8250)
Some UART Controllers (Synopsys DesignWare 8250) will trigger spurious interrupts when the Line Control Register (LCR) is set while the UART is busy. This patch provides the option (16550_WAIT_LCR) to wait for UART until it's not busy, before setting the LCR. (16550_WAIT_LCR is disabled by default) This patch fixes the spurious UART interrupts for the upcoming port of NuttX to StarFive JH7110 SoC (with Synopsys DesignWare 8250 UART). [The patch is explained here](https://lupyuen.github.io/articles/plic#appendix-fix-the-spurious-uart-interrupts) drivers/serial/uart_16550.c: If 16550_WAIT_LCR is enabled, wait until UART is not busy before setting LCR include/nuttx/serial/uart_16550.h: Define the UART Status Register (USR) for checking if UART is busy drivers/serial/Kconfig-16550: Added option 16550_WAIT_LCR to 16550 UART Config, disabled by default
This commit is contained in:
parent
1adef07a79
commit
b2c1930825
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
****************************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user