[PATCH 34.2 3/6] LEON3: add console interrupt mode support
Daniel Hellstrom
daniel at gaisler.com
Wed Apr 18 15:19:32 UTC 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 | 164 +++++++++++++++++++++--
2 files changed, 150 insertions(+), 19 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 7eafaef..068aced 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;
@@ -196,6 +273,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
*/
@@ -288,35 +368,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;
}
@@ -327,6 +442,25 @@ rtems_device_driver console_close(
void * 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);
}
--
1.7.0.4
More information about the devel
mailing list