[PATCH 34.2 v2 3/6] LEON3: add console interrupt mode support

Daniel Hellstrom daniel at gaisler.com
Thu Apr 19 08:21:22 CDT 2012


The comment in configure.ac is probably inherited from the LEON2
BSP. The LEON3 console driver implements a "flush" mechanism on
console_close() in order to solve the problem described with
sis/LEON2.

Signed-off-by: Daniel Hellstrom <daniel at gaisler.com>
---
 c/src/lib/libbsp/sparc/leon3/configure.ac      |    5 +-
 c/src/lib/libbsp/sparc/leon3/console/console.c |  172 +++++++++++++++++++++---
 2 files changed, 154 insertions(+), 23 deletions(-)

diff --git a/c/src/lib/libbsp/sparc/leon3/configure.ac b/c/src/lib/libbsp/sparc/leon3/configure.ac
index 6d86386..3f2d445 100644
--- a/c/src/lib/libbsp/sparc/leon3/configure.ac
+++ b/c/src/lib/libbsp/sparc/leon3/configure.ac
@@ -26,10 +26,7 @@ RTEMS_BSPOPTS_SET([CONSOLE_USE_INTERRUPTS],[*],[0])
 RTEMS_BSPOPTS_HELP([CONSOLE_USE_INTERRUPTS],
 [The leon3 console driver can operate in either polled or interrupt mode.
 Under the simulator (especially when FAST_UART is defined), polled seems to operate
-better.  It is common for a task to print a line (like the end of test message) and
-then exit.  In this case, the program returns control to the simulator command line
-before the program has even queued the output to the uart.  Thus sis has no chance
-of getting the data out.])
+better.])
 
 RTEMS_BSPOPTS_SET([SIMSPARC_FAST_IDLE],[*],[])
 RTEMS_BSPOPTS_HELP([SIMSPARC_FAST_IDLE],
diff --git a/c/src/lib/libbsp/sparc/leon3/console/console.c b/c/src/lib/libbsp/sparc/leon3/console/console.c
index 45ec82e..aece47a 100644
--- a/c/src/lib/libbsp/sparc/leon3/console/console.c
+++ b/c/src/lib/libbsp/sparc/leon3/console/console.c
@@ -15,6 +15,17 @@
  *  http://www.rtems.com/license/LICENSE.
  */
 
+/* Define CONSOLE_USE_INTERRUPTS to enable APBUART interrupt handling instead
+ * of polling mode.
+ *
+ * Note that it is not possible to use the interrupt mode of the driver
+ * together with the "old" APBUART and -u to GRMON. However the new
+ * APBUART core (from GRLIB 1.0.17-b2710) has the GRMON debug bit and can 
+ * handle interrupts.
+ *
+ * NOTE: This can be defined in the make/custom/leon3.cfg file.
+ */
+
 #include <bsp.h>
 #include <rtems/libio.h>
 #include <stdlib.h>
@@ -32,12 +43,6 @@
 int syscon_uart_index __attribute__((weak)) = 0;
 
 /*
- *  Should we use a polled or interrupt drived console?
- *
- *  NOTE: This is defined in the custom/leon.cfg file.
- */
-
-/*
  *  apbuart_outbyte_polled
  *
  *  This routine transmits a character using polling.
@@ -63,16 +68,86 @@ extern int apbuart_inbyte_nonblocking(ambapp_apb_uart *regs);
 struct apbuart_priv {
   ambapp_apb_uart *regs;
   unsigned int freq_hz;
+#if CONSOLE_USE_INTERRUPTS
+  int irq;
+  void *cookie;
+  volatile int sending;
+  char *buf;
+#endif
 };
 static struct apbuart_priv apbuarts[BSP_NUMBER_OF_TERMIOS_PORTS];
 static int uarts = 0;
 
+#if CONSOLE_USE_INTERRUPTS
+
+/* Handle UART interrupts */
+void console_isr(void *arg)
+{
+  struct apbuart_priv *uart = arg;
+  unsigned int status;
+  char data;
+
+  /* Get all received characters */
+  while ((status=uart->regs->status) & LEON_REG_UART_STATUS_DR) {
+    /* Data has arrived, get new data */
+    data = uart->regs->data;
+
+    /* Tell termios layer about new character */
+    rtems_termios_enqueue_raw_characters(uart->cookie, &data, 1);
+  }
+
+  if (status & LEON_REG_UART_STATUS_THE) {
+    /* Sent the one char, we disable TX interrupts */
+    uart->regs->ctrl &= ~LEON_REG_UART_CTRL_TI;
+
+    /* Tell close that we sent everything */
+    uart->sending = 0;
+
+    /* write_interrupt will get called from this function */
+    rtems_termios_dequeue_characters(uart->cookie, 1);
+  }
+}
+
+/*
+ *  Console Termios Write-Buffer Support Entry Point
+ *
+ */
+
+int console_write_interrupt(int minor, const char *buf, int len)
+{
+  struct apbuart_priv *uart;
+  unsigned int oldLevel;
+
+  if (minor == 0)
+    uart = &apbuarts[syscon_uart_index];
+  else
+    uart = &apbuarts[minor - 1];
+
+  /* Remember what position in buffer */
+
+  rtems_interrupt_disable(oldLevel);
+
+  /* Enable TX interrupt */
+  uart->regs->ctrl |= LEON_REG_UART_CTRL_TI;
+
+  /* start UART TX, this will result in an interrupt when done */
+  uart->regs->data = *buf;
+
+  uart->sending = 1;
+
+  rtems_interrupt_enable(oldLevel);
+
+  return 0;
+}
+
+#else
+
 /*
  *  Console Termios Support Entry Points
  *
  */
 
-ssize_t console_write_support (int minor, const char *buf, size_t len)
+ssize_t console_write_polled(int minor, const char *buf, size_t len)
 {
   int nwrite = 0, port;
 
@@ -100,6 +175,8 @@ int console_pollRead(int minor)
   return apbuart_inbyte_nonblocking(apbuarts[port].regs);
 }
 
+#endif
+
 int console_set_attributes(int minor, const struct termios *t)
 {
   unsigned int scaler;
@@ -174,6 +251,9 @@ int find_matching_apbuart(struct ambapp_dev *dev, int index, void *arg)
 
   /* Extract needed information of one APBUART */
   apbuarts[uarts].regs = (ambapp_apb_uart *)apb->start;
+#if CONSOLE_USE_INTERRUPTS
+  apbuarts[uarts].irq = apb->irq;
+#endif
   /* Get APBUART core frequency, it is assumed that it is the same
    * as Bus frequency where the UART is situated
    */
@@ -266,35 +346,70 @@ rtems_device_driver console_open(
 )
 {
   rtems_status_code sc;
-  int port;
+  struct apbuart_priv *uart;
+#if CONSOLE_USE_INTERRUPTS
+  rtems_libio_open_close_args_t *priv = arg;
 
-  static const rtems_termios_callbacks pollCallbacks = {
+  /* Interrupt mode routines */
+  static const rtems_termios_callbacks Callbacks = {
+    NULL,                        /* firstOpen */
+    NULL,                        /* lastClose */
+    NULL,                        /* pollRead */
+    console_write_interrupt,     /* write */
+    console_set_attributes,      /* setAttributes */
+    NULL,                        /* stopRemoteTx */
+    NULL,                        /* startRemoteTx */
+    1                            /* outputUsesInterrupts */
+  };
+#else
+  /* Polling mode routines */
+  static const rtems_termios_callbacks Callbacks = {
     NULL,                        /* firstOpen */
     NULL,                        /* lastClose */
     console_pollRead,            /* pollRead */
-    console_write_support,       /* write */
+    console_write_polled,        /* write */
     console_set_attributes,      /* setAttributes */
     NULL,                        /* stopRemoteTx */
     NULL,                        /* startRemoteTx */
     0                            /* outputUsesInterrupts */
   };
+#endif
 
   assert(minor <= uarts);
   if (minor > uarts || minor == (syscon_uart_index + 1))
     return RTEMS_INVALID_NUMBER;
 
-  sc = rtems_termios_open (major, minor, arg, &pollCallbacks);
+  sc = rtems_termios_open(major, minor, arg, &Callbacks);
   if (sc != RTEMS_SUCCESSFUL)
     return sc;
 
   if (minor == 0)
-    port = syscon_uart_index;
+    uart = &apbuarts[syscon_uart_index];
   else
-    port = minor - 1;
+    uart = &apbuarts[minor - 1];
+
+#if CONSOLE_USE_INTERRUPTS
+  if (priv && priv->iop)
+    uart->cookie = priv->iop->data1;
+  else
+    uart->cookie = NULL;
+
+  /* Register Interrupt handler */
+  sc = rtems_interrupt_handler_install(uart->irq, "console",
+                                       RTEMS_INTERRUPT_SHARED, console_isr,
+                                       uart);
+  if (sc != RTEMS_SUCCESSFUL)
+    return sc;
 
+  uart->sending = 0;
+  /* Enable Receiver and transmitter and Turn on RX interrupts */
+  uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE |
+                      LEON_REG_UART_CTRL_RI;
+#else
   /* Initialize UART on opening */
-  apbuarts[port]->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE;
-  apbuarts[port]->regs->status = 0;
+  uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE;
+#endif
+  uart->regs->status = 0;
 
   return RTEMS_SUCCESSFUL;
 }
@@ -305,7 +420,26 @@ rtems_device_driver console_close(
   void                    * arg
 )
 {
-  return rtems_termios_close (arg);
+#if CONSOLE_USE_INTERRUPTS
+  struct apbuart_priv *uart;
+
+  if (minor == 0)
+    uart = &apbuarts[syscon_uart_index];
+  else
+    uart = &apbuarts[minor - 1];
+
+  /* Turn off RX interrupts */
+  uart->regs->ctrl &= ~(LEON_REG_UART_CTRL_RI);
+
+  /**** Flush device ****/
+  while (uart->sending) {
+    /* Wait until all data has been sent */
+  }
+
+  /* uninstall ISR */
+  rtems_interrupt_handler_remove(uart->irq, console_isr, uart);
+#endif
+  return rtems_termios_close(arg);
 }
 
 rtems_device_driver console_read(
@@ -314,7 +448,7 @@ rtems_device_driver console_read(
   void                    * arg
 )
 {
-  return rtems_termios_read (arg);
+  return rtems_termios_read(arg);
 }
 
 rtems_device_driver console_write(
@@ -323,7 +457,7 @@ rtems_device_driver console_write(
   void                    * arg
 )
 {
-  return rtems_termios_write (arg);
+  return rtems_termios_write(arg);
 }
 
 rtems_device_driver console_control(
@@ -332,6 +466,6 @@ rtems_device_driver console_control(
   void                    * arg
 )
 {
-  return rtems_termios_ioctl (arg);
+  return rtems_termios_ioctl(arg);
 }
 
-- 
1.7.0.4



More information about the rtems-devel mailing list