LEON3: add console interrupt mode support

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@gaisler.com>
This commit is contained in:
Daniel Hellstrom
2012-04-19 15:46:10 +02:00
committed by Gedare Bloom
parent 07557c86fd
commit 62694fd68f
2 changed files with 153 additions and 23 deletions

View File

@@ -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],

View File

@@ -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>
@@ -31,12 +42,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
*
@@ -63,16 +68,85 @@ 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 +174,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 +250,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 +345,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 +419,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 +447,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 +456,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 +465,6 @@ rtems_device_driver console_control(
void * arg
)
{
return rtems_termios_ioctl (arg);
return rtems_termios_ioctl(arg);
}