forked from Imagelibrary/rtems
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:
committed by
Gedare Bloom
parent
07557c86fd
commit
62694fd68f
@@ -26,10 +26,7 @@ RTEMS_BSPOPTS_SET([CONSOLE_USE_INTERRUPTS],[*],[0])
|
|||||||
RTEMS_BSPOPTS_HELP([CONSOLE_USE_INTERRUPTS],
|
RTEMS_BSPOPTS_HELP([CONSOLE_USE_INTERRUPTS],
|
||||||
[The leon3 console driver can operate in either polled or interrupt mode.
|
[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
|
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
|
better.])
|
||||||
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.])
|
|
||||||
|
|
||||||
RTEMS_BSPOPTS_SET([SIMSPARC_FAST_IDLE],[*],[])
|
RTEMS_BSPOPTS_SET([SIMSPARC_FAST_IDLE],[*],[])
|
||||||
RTEMS_BSPOPTS_HELP([SIMSPARC_FAST_IDLE],
|
RTEMS_BSPOPTS_HELP([SIMSPARC_FAST_IDLE],
|
||||||
|
|||||||
@@ -15,6 +15,17 @@
|
|||||||
* http://www.rtems.com/license/LICENSE.
|
* 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 <bsp.h>
|
||||||
#include <rtems/libio.h>
|
#include <rtems/libio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -31,12 +42,6 @@
|
|||||||
*/
|
*/
|
||||||
int syscon_uart_index __attribute__((weak)) = 0;
|
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
|
* apbuart_outbyte_polled
|
||||||
*
|
*
|
||||||
@@ -63,16 +68,85 @@ extern int apbuart_inbyte_nonblocking(ambapp_apb_uart *regs);
|
|||||||
struct apbuart_priv {
|
struct apbuart_priv {
|
||||||
ambapp_apb_uart *regs;
|
ambapp_apb_uart *regs;
|
||||||
unsigned int freq_hz;
|
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 struct apbuart_priv apbuarts[BSP_NUMBER_OF_TERMIOS_PORTS];
|
||||||
static int uarts = 0;
|
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
|
* 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;
|
int nwrite = 0, port;
|
||||||
|
|
||||||
@@ -100,6 +174,8 @@ int console_pollRead(int minor)
|
|||||||
return apbuart_inbyte_nonblocking(apbuarts[port].regs);
|
return apbuart_inbyte_nonblocking(apbuarts[port].regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
int console_set_attributes(int minor, const struct termios *t)
|
int console_set_attributes(int minor, const struct termios *t)
|
||||||
{
|
{
|
||||||
unsigned int scaler;
|
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 */
|
/* Extract needed information of one APBUART */
|
||||||
apbuarts[uarts].regs = (ambapp_apb_uart *)apb->start;
|
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
|
/* Get APBUART core frequency, it is assumed that it is the same
|
||||||
* as Bus frequency where the UART is situated
|
* as Bus frequency where the UART is situated
|
||||||
*/
|
*/
|
||||||
@@ -266,35 +345,70 @@ rtems_device_driver console_open(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
rtems_status_code sc;
|
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, /* firstOpen */
|
||||||
NULL, /* lastClose */
|
NULL, /* lastClose */
|
||||||
console_pollRead, /* pollRead */
|
console_pollRead, /* pollRead */
|
||||||
console_write_support, /* write */
|
console_write_polled, /* write */
|
||||||
console_set_attributes, /* setAttributes */
|
console_set_attributes, /* setAttributes */
|
||||||
NULL, /* stopRemoteTx */
|
NULL, /* stopRemoteTx */
|
||||||
NULL, /* startRemoteTx */
|
NULL, /* startRemoteTx */
|
||||||
0 /* outputUsesInterrupts */
|
0 /* outputUsesInterrupts */
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
assert(minor <= uarts);
|
assert(minor <= uarts);
|
||||||
if (minor > uarts || minor == (syscon_uart_index + 1))
|
if (minor > uarts || minor == (syscon_uart_index + 1))
|
||||||
return RTEMS_INVALID_NUMBER;
|
return RTEMS_INVALID_NUMBER;
|
||||||
|
|
||||||
sc = rtems_termios_open (major, minor, arg, &pollCallbacks);
|
sc = rtems_termios_open(major, minor, arg, &Callbacks);
|
||||||
if (sc != RTEMS_SUCCESSFUL)
|
if (sc != RTEMS_SUCCESSFUL)
|
||||||
return sc;
|
return sc;
|
||||||
|
|
||||||
if (minor == 0)
|
if (minor == 0)
|
||||||
port = syscon_uart_index;
|
uart = &apbuarts[syscon_uart_index];
|
||||||
else
|
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 */
|
/* Initialize UART on opening */
|
||||||
apbuarts[port]->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE;
|
uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE;
|
||||||
apbuarts[port]->regs->status = 0;
|
#endif
|
||||||
|
uart->regs->status = 0;
|
||||||
|
|
||||||
return RTEMS_SUCCESSFUL;
|
return RTEMS_SUCCESSFUL;
|
||||||
}
|
}
|
||||||
@@ -305,6 +419,25 @@ rtems_device_driver console_close(
|
|||||||
void * arg
|
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);
|
return rtems_termios_close(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user