From: Martin Aberg <mab...@gaisler.com>

The APBUART control register can be updated from both ISR and task context so
the device must be locked when manipulating the register.
There is also a scenario with RX FIFO interrupts where a few characters can be
in the FIFO without generating interrupt.
---
 c/src/lib/libbsp/sparc/shared/uart/apbuart_cons.c | 64 +++++++++++++++++------
 1 file changed, 48 insertions(+), 16 deletions(-)

diff --git a/c/src/lib/libbsp/sparc/shared/uart/apbuart_cons.c 
b/c/src/lib/libbsp/sparc/shared/uart/apbuart_cons.c
index 379d9ff..28066a3 100644
--- a/c/src/lib/libbsp/sparc/shared/uart/apbuart_cons.c
+++ b/c/src/lib/libbsp/sparc/shared/uart/apbuart_cons.c
@@ -358,8 +358,8 @@ static int apbuart_info(
 
        if (priv->mode == TERMIOS_POLLED)
                str1 = "TERMIOS_POLLED";
-       else if (priv->mode == TERMIOS_TASK_DRIVEN)
-               str1 = "TERMIOS_TASK_DRIVEN";
+       else if (priv->mode == TERMIOS_IRQ_DRIVEN)
+               str1 = "TERMIOS_IRQ_DRIVEN";
        else if (priv->mode == TERMIOS_TASK_DRIVEN)
                str1 = "TERMIOS_TASK_DRIVEN";
        else
@@ -489,7 +489,8 @@ static void last_close(
        if (uart->mode != TERMIOS_POLLED) {
                /* Turn off RX interrupts */
                rtems_termios_device_lock_acquire(base, &lock_context);
-               uart->regs->ctrl &= ~(APBUART_CTRL_RI | APBUART_CTRL_RF);
+               uart->regs->ctrl &=
+                       ~(APBUART_CTRL_DI | APBUART_CTRL_RI | APBUART_CTRL_RF);
                rtems_termios_device_lock_release(base, &lock_context);
 
                /**** Flush device ****/
@@ -515,29 +516,50 @@ static int read_polled(rtems_termios_device_context *base)
        return apbuart_inbyte_nonblocking(uart->regs);
 }
 
+/* This function is called from TERMIOS rxdaemon task without device lock. */
 static int read_task(rtems_termios_device_context *base)
 {
+       rtems_interrupt_lock_context lock_context;
        struct apbuart_priv *uart = base_get_priv(base);
-       int c, tot;
-       char buf[32];
+       struct apbuart_regs *regs = uart->regs;
+       int cnt;
+       char buf[33];
        struct rtems_termios_tty *tty;
+       uint32_t ctrl_add;
 
+       ctrl_add = APBUART_CTRL_RI;
+       if (uart->cap & CAP_DI) {
+               ctrl_add |= (APBUART_CTRL_DI | APBUART_CTRL_RF);
+       }
        tty = uart->tty;
-       tot = 0;
-       while ((c=apbuart_inbyte_nonblocking(uart->regs)) != EOF) {
-               buf[tot] = c;
-               tot++;
-               if (tot > 31) {
-                       rtems_termios_enqueue_raw_characters(tty, buf, tot);
-                       tot = 0;
+       do {
+               cnt = 0;
+               while (
+                       (regs->status & APBUART_STATUS_DR) &&
+                       (cnt < sizeof(buf))
+               ) {
+                       buf[cnt] = regs->data;
+                       cnt++;
+               }
+               if (0 < cnt) {
+                       /* Tell termios layer about new characters */
+                       rtems_termios_enqueue_raw_characters(tty, &buf[0], cnt);
                }
-       }
-       if (tot > 0)
-               rtems_termios_enqueue_raw_characters(tty, buf, tot);
+
+               /*
+                * Turn on RX interrupts. A new character in FIFO now may not
+                * cause interrupt so we must check data ready again
+                * afterwards.
+                */
+               rtems_termios_device_lock_acquire(base, &lock_context);
+               regs->ctrl |= ctrl_add;
+               rtems_termios_device_lock_release(base, &lock_context);
+       } while (regs->status & APBUART_STATUS_DR);
 
        return EOF;
 }
 
+
 struct apbuart_baud {
        unsigned int num;
        unsigned int baud;
@@ -779,6 +801,7 @@ static void write_interrupt(
 static void apbuart_cons_isr(void *arg)
 {
        rtems_termios_tty *tty = arg;
+       rtems_termios_device_context *base;
        struct console_dev *condev = rtems_termios_get_device_context(tty);
        struct apbuart_priv *uart = condev_get_priv(condev);
        struct apbuart_regs *regs = uart->regs;
@@ -787,7 +810,16 @@ static void apbuart_cons_isr(void *arg)
        int cnt;
 
        if (uart->mode == TERMIOS_TASK_DRIVEN) {
-               if ((status=regs->status) & APBUART_STATUS_DR) {
+               if ((status = regs->status) & APBUART_STATUS_DR) {
+                       rtems_interrupt_lock_context lock_context;
+
+                       /* Turn off RX interrupts */
+                       base = rtems_termios_get_device_context(tty);
+                       rtems_termios_device_lock_acquire(base, &lock_context);
+                       regs->ctrl &=
+                           ~(APBUART_CTRL_DI | APBUART_CTRL_RI |
+                             APBUART_CTRL_RF);
+                       rtems_termios_device_lock_release(base, &lock_context);
                        /* Activate termios RX daemon task */
                        rtems_termios_rxirq_occured(tty);
                }
-- 
2.7.4

_______________________________________________
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel

Reply via email to