diff options
Diffstat (limited to 'nuttx/arch/arm/src/nuc1xx/nuc_serial.c')
-rw-r--r-- | nuttx/arch/arm/src/nuc1xx/nuc_serial.c | 262 |
1 files changed, 211 insertions, 51 deletions
diff --git a/nuttx/arch/arm/src/nuc1xx/nuc_serial.c b/nuttx/arch/arm/src/nuc1xx/nuc_serial.c index fce9ea967..529f5c61b 100644 --- a/nuttx/arch/arm/src/nuc1xx/nuc_serial.c +++ b/nuttx/arch/arm/src/nuc1xx/nuc_serial.c @@ -324,18 +324,42 @@ static inline void up_serialout(struct nuc_dev_s *priv, int offset, uint32_t val } /**************************************************************************** - * Name: up_disableuartint + * Name: up_setier ****************************************************************************/ -static inline void up_disableuartint(struct nuc_dev_s *priv, uint32_t *ier) +static uint32_t up_setier(struct nuc_dev_s *priv, + uint32_t clrbits, uint32_t setbits) { - if (ier) - { - *ier = priv->ier & UART_IER_ALLIE; - } + irqstate_t flags; + uint32_t retval; + + /* Make sure that this is atomic */ - priv->ier &= ~UART_IER_ALLIE; + flags = irqsave(); + + /* Get the current IER setting */ + + retval = priv->ier; + + /* Modify and write the IER according to the inputs */ + + priv->ier &= ~clrbits; + priv->ier |= setbits; up_serialout(priv, NUC_UART_IER_OFFSET, priv->ier); + irqrestore(flags); + + /* Return the value of the IER before modification */ + + return retval; +} + +/**************************************************************************** + * Name: up_disableuartint + ****************************************************************************/ + +static inline void up_disableuartint(struct nuc_dev_s *priv, uint32_t *ier) +{ + *ier = up_setier(priv, UART_IER_ALLIE, 0); } /**************************************************************************** @@ -344,8 +368,67 @@ static inline void up_disableuartint(struct nuc_dev_s *priv, uint32_t *ier) static inline void up_restoreuartint(struct nuc_dev_s *priv, uint32_t ier) { - priv->ier |= ier & UART_IER_ALLIE; - up_serialout(priv, NUC_UART_IER_OFFSET, priv->ier); + uint32_t setbits = ier & UART_IER_ALLIE; + uint32_t clrbits = (~ier) & UART_IER_ALLIE; + (void)up_setier(priv, clrbits, setbits); +} + +/**************************************************************************** + * Name: up_rxto_disable + ****************************************************************************/ + +static void up_rxto_disable(struct nuc_dev_s *priv) +{ + uint32_t regval; + + /* This function is called at initialization time and also when a timeout + * interrupt is received when the RX FIFO is empty. + * + * Set Rx Trigger Level so that an interrupt will be generated when the + * very next byte is received. + */ + + regval = up_serialin(priv, NUC_UART_FCR_OFFSET); + regval &= ~UART_FCR_RFITL_MASK; + regval |= UART_FCR_RFITL_1; + up_serialout(priv, NUC_UART_FCR_OFFSET, regval); + + /* Disable the RX timeout interrupt and disable the timeout */ + + (void)up_setier(priv, (UART_IER_RTO_IEN | UART_IER_TIME_OUT_EN), 0); +} + +/**************************************************************************** + * Name: up_rxto_enable + ****************************************************************************/ + +static void up_rxto_enable(struct nuc_dev_s *priv) +{ + uint32_t regval; + + /* This function is called after each RX interrupt. Data has been received + * and more may or may not be received. + * + * Set the RX FIFO level so that interrupts are only received when there + * are 8 or 14 bytes in the FIFO (depending on the UART FIFO depth). + */ + + regval = up_serialin(priv, NUC_UART_FCR_OFFSET); + regval &= ~UART_FCR_RFITL_MASK; +#if defined(CONFIG_NUC_UART0) +# if defined(CONFIG_NUC_UART0) || defined(CONFIG_NUC_UART0) + regval |= priv->depth > 16 ? UART_FCR_RFITL_14 : UART_FCR_RFITL_8; +# else + regval |= UART_FCR_RFITL_14; +# endif +#else + regval |= UART_FCR_RFITL_8; +#endif + up_serialout(priv, NUC_UART_FCR_OFFSET, regval); + + /* Enable the RX timeout interrupt and enable the timeout */ + + (void)up_setier(priv, 0, (UART_IER_RTO_IEN | UART_IER_TIME_OUT_EN)); } /**************************************************************************** @@ -378,8 +461,8 @@ static int up_setup(struct uart_dev_s *dev) /* Set Rx Trigger Level */ - regval &= ~(UART_FCR_FRITL_MASK | UART_FCR_TFR | UART_FCR_RFR); - regval |= UART_FCR_FRITL_4; + regval &= ~(UART_FCR_RFITL_MASK | UART_FCR_TFR | UART_FCR_RFR); + regval |= UART_FCR_RFITL_1; up_serialout(priv, NUC_UART_FCR_OFFSET, regval); /* Set Parity & Data bits and Stop bits */ @@ -427,9 +510,9 @@ static int up_setup(struct uart_dev_s *dev) up_serialout(priv, NUC_UART_LCR_OFFSET, regval); - /* Set Time-Out values */ + /* Configure the RX timeout, but do not enable the interrupt yet */ - regval = UART_TOR_TOIC(40) | UART_TOR_DLY(0); + regval = UART_TOR_TOIC(60) | UART_TOR_DLY(0); up_serialout(priv, NUC_UART_TOR_OFFSET, regval); /* Set the baud */ @@ -529,8 +612,11 @@ static int up_interrupt(int irq, void *context) { struct uart_dev_s *dev = NULL; struct nuc_dev_s *priv; - uint32_t status; + uint32_t isr; + uint32_t regval; int passes; + bool rxto; + bool rxfe; #ifdef CONFIG_NUC_UART0 if (g_uart0priv.irq == irq) @@ -564,52 +650,94 @@ static int up_interrupt(int irq, void *context) for (passes = 0; passes < 256; passes++) { - /* Get the current UART interrupt status */ + /* Get the current UART interrupt status register (ISR) contents */ + + isr = up_serialin(priv, NUC_UART_ISR_OFFSET); + + /* Check if the RX FIFO is empty. Check if an RX timeout occur. These affect + * some later decisions. + */ - status = up_serialin(priv, NUC_UART_ISR_OFFSET); + rxfe = ((up_serialin(priv, NUC_UART_FSR_OFFSET) & UART_FSR_RX_EMPTY) != 0); + rxto = ((isr & UART_ISR_TOUT_INT) != 0); - /* Check if the RX FIFO is filled to the threshold value (OR if the RX - * timeout occurred without the FIFO being filled) + /* Check if the RX FIFO is filled to the threshold value OR if the RX + * timeout occurred with the FIFO non-empty. Both are cleared + * by reading from the RBR register. */ - if ((status & UART_ISR_RDA_INT) != 0 || (status & UART_ISR_TOUT_INT) != 0) + if ((isr & UART_ISR_RDA_INT) != 0 || (rxto && !rxfe)) { uart_recvchars(dev); } - /* Check if the transmit holding register is empty */ + /* Enable or disable RX timeouts based on the state of RX FIFO: + * + * DISABLE: If the timeout occurred and the RX FIFO was empty. + * ENABLE: Data was in RX FIFO (may have been removed), RX interrupts + * are enabled, and the timeout is not already enabled. + */ - if ((status & UART_ISR_THRE_INT) != 0) + if (rxto && rxfe) { - uart_xmitchars(dev); - } + /* A timeout interrupt occurred while the RX FIFO is empty. + * We need to read from the RBR to clear the interrupt. + */ - /* Check for modem status */ + (void)up_serialin(priv, NUC_UART_RBR_OFFSET); - if ((status & UART_ISR_MODEM_INT) != 0) - { - /* REVISIT: Do we clear this be reading the modem status register? */ + /* Disable, further RX timeout interrupts and set the RX FIFO + * threshold so that an interrupt will be generated when the + * very next byte is recieved. + */ - (void)up_serialin(priv, NUC_UART_MSR_OFFSET); + up_rxto_disable(priv); } - - /* Check for line status */ - if ((status & UART_ISR_RLS_INT) != 0) + /* Is the timeout enabled? Are RX interrupts enabled? Was there + * data in the RX FIFO when we entered the interrupt handler? + */ + + else if ((priv->ier & (UART_IER_RTO_IEN|UART_IER_RDA_IEN)) == UART_IER_RDA_IEN && !rxfe) { - /* REVISIT: Do we clear this be reading the FIFO status register? */ + /* We are receiving data and the RX timeout is not enabled. + * Set the RX FIFO threshold so that RX interrupts will only be + * generated after several bytes have been recevied and enable + * the RX timout. + */ + + up_rxto_enable(priv); + } - (void)up_serialin(priv, NUC_UART_FSR_OFFSET); + /* Check if the transmit holding register is empty. Cleared by writing + * to the THR register. + */ + + if ((isr & UART_ISR_THRE_INT) != 0) + { + uart_xmitchars(dev); } - /* Check for buffer errors */ + /* Check for modem status. */ - if ((status & UART_ISR_BUF_ERR_INT) != 0) + if ((isr & UART_ISR_MODEM_INT) != 0) { - /* REVISIT: Do we clear this by reading the FIFO status register? */ + /* Cleared by setting the DCTSF bit in the modem control register (MCR) */ - (void)up_serialin(priv, NUC_UART_FSR_OFFSET); + regval = up_serialin(priv, NUC_UART_MCR_OFFSET); + up_serialout(priv, NUC_UART_MCR_OFFSET, regval | UART_MSR_DCTSF); } + + /* Check for line status or buffer errors*/ + + if ((isr & UART_ISR_RLS_INT) != 0 || + (isr & UART_ISR_BUF_ERR_INT) != 0) + { + /* Both errors are cleared by reseting the RX FIFO */ + + regval = up_serialin(priv, NUC_UART_FCR_OFFSET); + up_serialout(priv, NUC_UART_FCR_OFFSET, regval | UART_FCR_RFR); + } } return OK; @@ -740,19 +868,51 @@ static int up_receive(struct uart_dev_s *dev, uint32_t *status) static void up_rxint(struct uart_dev_s *dev, bool enable) { struct nuc_dev_s *priv = (struct nuc_dev_s*)dev->priv; + if (enable) { #ifndef CONFIG_SUPPRESS_SERIAL_INTS - priv->ier |= (UART_IER_RDA_IEN | UART_IER_RLS_IEN | UART_IER_RTO_IEN | - UART_IER_BUF_ERR_IEN | UART_IER_TIME_OUT_EN); + /* Enable receive data, line status and buffer error interrupts */ + + irqstate_t flags = irqsave(); + (void)up_setier(priv, 0, + (UART_IER_RDA_IEN | UART_IER_RLS_IEN | + UART_IER_BUF_ERR_IEN)); + + /* Enable or disable timeouts based on the state of RX FIFO */ + + if ((up_serialin(priv, NUC_UART_FSR_OFFSET) & UART_FSR_RX_EMPTY) != 0) + { + /* The FIFO is empty. Disable RX timeout interrupts and set the + * RX FIFO threshold so that an interrupt will be generated when + * the very next byte is recieved. + */ + + up_rxto_disable(priv); + } + else + { + /* Otherwise, set the RX FIFO threshold so that RX interrupts will + * only be generated after several bytes have been recevied and + * enable* the RX timout. + */ + + up_rxto_enable(priv); + } + + irqrestore(flags); #endif } else { - priv->ier &= ~(UART_IER_RDA_IEN | UART_IER_RLS_IEN | UART_IER_RTO_IEN); - } + /* Enable receive data, line status, buffer error, and RX timeout + * interrupts. Also disables the RX timer. + */ - up_serialout(priv, NUC_UART_IER_OFFSET, priv->ier); + (void)up_setier(priv, 0, + (UART_IER_RDA_IEN | UART_IER_RLS_IEN | UART_IER_RTO_IEN | + UART_IER_BUF_ERR_IEN | UART_IER_TIME_OUT_EN)); + } } /**************************************************************************** @@ -766,7 +926,7 @@ static void up_rxint(struct uart_dev_s *dev, bool enable) static bool up_rxavailable(struct uart_dev_s *dev) { struct nuc_dev_s *priv = (struct nuc_dev_s*)dev->priv; - return ((up_serialin(priv, NUC_UART_FSR_OFFSET) & UART_FSR_RX_EMPTY) != 0); + return ((up_serialin(priv, NUC_UART_FSR_OFFSET) & UART_FSR_RX_EMPTY) == 0); } /**************************************************************************** @@ -794,29 +954,29 @@ static void up_send(struct uart_dev_s *dev, int ch) static void up_txint(struct uart_dev_s *dev, bool enable) { struct nuc_dev_s *priv = (struct nuc_dev_s*)dev->priv; - irqstate_t flags; - flags = irqsave(); if (enable) { #ifndef CONFIG_SUPPRESS_SERIAL_INTS - priv->ier |= (UART_IER_THRE_IEN | UART_IER_BUF_ERR_IEN); - up_serialout(priv, NUC_UART_IER_OFFSET, priv->ier); + /* Enable the THR empty interrupt */ + + irqstate_t flags = irqsave(); + (void)up_setier(priv, 0, UART_IER_THRE_IEN); /* Fake a TX interrupt here by just calling uart_xmitchars() with * interrupts disabled (note this may recurse). */ uart_xmitchars(dev); + irqrestore(flags); #endif } else { - priv->ier &= ~UART_IER_THRE_IEN; - up_serialout(priv, NUC_UART_IER_OFFSET, priv->ier); - } + /* Disable the THR empty interrupt */ - irqrestore(flags); + (void)up_setier(priv, UART_IER_THRE_IEN, 0); + } } /**************************************************************************** |