bsps/sparc: Add and use shared APBUART console

Move the APBUART console driver support to the shared SPARC area so that
it can be reused by other BSPs.  Only the console driver initialization
is now BSP specific.
This commit is contained in:
Sebastian Huber
2014-06-30 09:33:36 +02:00
parent a0eb21ebab
commit 21abc43fb5
5 changed files with 315 additions and 325 deletions

View File

@@ -65,6 +65,7 @@ libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_old.c
libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_names.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_names.c
libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_show.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_show.c
# console # console
libbsp_a_SOURCES += ../../shared/console-termios.c
libbsp_a_SOURCES += console/console.c libbsp_a_SOURCES += console/console.c
# debugio # debugio
libbsp_a_SOURCES += console/printk_support.c libbsp_a_SOURCES += console/printk_support.c
@@ -109,9 +110,11 @@ libbsp_a_SOURCES += ../../sparc/shared/spw/grspw.c \
# UART # UART
include_HEADERS += ../../sparc/shared/include/apbuart.h \ include_HEADERS += ../../sparc/shared/include/apbuart.h \
../../sparc/shared/include/apbuart_pci.h ../../sparc/shared/include/apbuart_pci.h \
../../sparc/shared/include/apbuart_termios.h
libbsp_a_SOURCES += ../../sparc/shared/uart/apbuart.c \ libbsp_a_SOURCES += ../../sparc/shared/uart/apbuart.c \
../../sparc/shared/uart/apbuart_pci.c ../../sparc/shared/uart/apbuart_pci.c \
../../sparc/shared/uart/apbuart_termios.c
# I2CMST # I2CMST
include_HEADERS += ../../sparc/shared/include/i2cmst.h include_HEADERS += ../../sparc/shared/include/i2cmst.h

View File

@@ -28,34 +28,17 @@
#include <bsp.h> #include <bsp.h>
#include <bsp/fatal.h> #include <bsp/fatal.h>
#include <rtems/libio.h> #include <apbuart_termios.h>
#include <stdlib.h>
#include <assert.h>
#include <rtems/bspIo.h>
#include <leon.h>
#include <rtems/termiostypes.h>
#include <apbuart.h>
int syscon_uart_index __attribute__((weak)) = 0; int syscon_uart_index __attribute__((weak)) = 0;
/* body is in debugputs.c */ /* body is in debugputs.c */
static struct apbuart_context apbuarts[BSP_NUMBER_OF_TERMIOS_PORTS];
struct apbuart_priv {
struct apbuart_regs *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; static int uarts = 0;
static struct apbuart_priv *leon3_console_get_uart(int minor) static struct apbuart_context *leon3_console_get_uart(int minor)
{ {
struct apbuart_priv *uart; struct apbuart_context *uart;
if (minor == 0) if (minor == 0)
uart = &apbuarts[syscon_uart_index]; uart = &apbuarts[syscon_uart_index];
@@ -65,160 +48,6 @@ static struct apbuart_priv *leon3_console_get_uart(int minor)
return uart; return uart;
} }
#if CONSOLE_USE_INTERRUPTS
/* Handle UART interrupts */
static void leon3_console_isr(void *arg)
{
struct apbuart_priv *uart = arg;
unsigned int status;
char data;
/* Get all received characters */
while ((status=uart->regs->status) & APBUART_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 & APBUART_STATUS_TE)
&& (uart->regs->ctrl & APBUART_CTRL_TI) != 0
) {
/* write_interrupt will get called from this function */
rtems_termios_dequeue_characters(uart->cookie, 1);
}
}
/*
* Console Termios Write-Buffer Support Entry Point
*
*/
static int leon3_console_write_support(int minor, const char *buf, size_t len)
{
struct apbuart_priv *uart = leon3_console_get_uart(minor);
int sending;
if (len > 0) {
/* Enable TX interrupt (interrupt is edge-triggered) */
uart->regs->ctrl |= APBUART_CTRL_TI;
/* start UART TX, this will result in an interrupt when done */
uart->regs->data = *buf;
sending = 1;
} else {
/* No more to send, disable TX interrupts */
uart->regs->ctrl &= ~APBUART_CTRL_TI;
/* Tell close that we sent everything */
sending = 0;
}
uart->sending = sending;
return 0;
}
#else
/*
* Console Termios Support Entry Points
*/
static ssize_t leon3_console_write_polled(
int minor,
const char *buf,
size_t len
)
{
struct apbuart_priv *uart = leon3_console_get_uart(minor);
int nwrite = 0;
while (nwrite < len) {
apbuart_outbyte_polled(uart->regs, *buf++, 1, 0);
nwrite++;
}
return nwrite;
}
static int leon3_console_pollRead(int minor)
{
struct apbuart_priv *uart = leon3_console_get_uart(minor);
return apbuart_inbyte_nonblocking(uart->regs);
}
#endif
static int leon3_console_set_attributes(int minor, const struct termios *t)
{
struct apbuart_priv *uart = leon3_console_get_uart(minor);
unsigned int scaler;
unsigned int ctrl;
int baud;
switch (t->c_cflag & CSIZE) {
default:
case CS5:
case CS6:
case CS7:
/* Hardware doesn't support other than CS8 */
return -1;
case CS8:
break;
}
/*
* FIXME: This read-modify-write sequence is broken since interrupts may
* interfere.
*/
/* Read out current value */
ctrl = uart->regs->ctrl;
switch (t->c_cflag & (PARENB|PARODD)) {
case (PARENB|PARODD):
/* Odd parity */
ctrl |= APBUART_CTRL_PE|APBUART_CTRL_PS;
break;
case PARENB:
/* Even parity */
ctrl &= ~APBUART_CTRL_PS;
ctrl |= APBUART_CTRL_PE;
break;
default:
case 0:
case PARODD:
/* No Parity */
ctrl &= ~(APBUART_CTRL_PS|APBUART_CTRL_PE);
}
if (!(t->c_cflag & CLOCAL)) {
ctrl |= APBUART_CTRL_FL;
} else {
ctrl &= ~APBUART_CTRL_FL;
}
/* Update new settings */
uart->regs->ctrl = ctrl;
/* Baud rate */
baud = rtems_termios_baud_to_number(t->c_cflag);
if (baud > 0) {
/* Calculate Baud rate generator "scaler" number */
scaler = (((uart->freq_hz * 10) / (baud * 8)) - 5) / 10;
/* Set new baud rate by setting scaler */
uart->regs->scaler = scaler;
}
return 0;
}
/* AMBA PP find routine. Extract AMBA PnP information into data structure. */ /* AMBA PP find routine. Extract AMBA PnP information into data structure. */
static int find_matching_apbuart(struct ambapp_dev *dev, int index, void *arg) static int find_matching_apbuart(struct ambapp_dev *dev, int index, void *arg)
{ {
@@ -226,9 +55,7 @@ static 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 = (struct apbuart_regs *)apb->start; apbuarts[uarts].regs = (struct apbuart_regs *)apb->start;
#if CONSOLE_USE_INTERRUPTS
apbuarts[uarts].irq = apb->irq; 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
*/ */
@@ -251,17 +78,18 @@ static void leon3_console_scan_uarts(void)
GAISLER_APBUART, find_matching_apbuart, NULL); GAISLER_APBUART, find_matching_apbuart, NULL);
} }
/*
* Console Device Driver Entry Points
*
*/
rtems_device_driver console_initialize( rtems_device_driver console_initialize(
rtems_device_major_number major, rtems_device_major_number major,
rtems_device_minor_number minor, rtems_device_minor_number minor,
void *arg void *arg
) )
{ {
const rtems_termios_device_handler *handler =
#if CONSOLE_USE_INTERRUPTS
&apbuart_handler_interrupt;
#else
&apbuart_handler_polled;
#endif
rtems_status_code status; rtems_status_code status;
int i; int i;
char console_name[16]; char console_name[16];
@@ -297,7 +125,14 @@ rtems_device_driver console_initialize(
* On a MP system one should not open UARTs that other OS instances use. * On a MP system one should not open UARTs that other OS instances use.
*/ */
if (syscon_uart_index < uarts) { if (syscon_uart_index < uarts) {
status = rtems_io_register_name("/dev/console", major, 0); minor = 0;
status = rtems_termios_device_install(
CONSOLE_DEVICE_NAME,
major,
minor,
handler,
leon3_console_get_uart(syscon_uart_index)
);
if (status != RTEMS_SUCCESSFUL) if (status != RTEMS_SUCCESSFUL)
bsp_fatal(LEON3_FATAL_CONSOLE_REGISTER_DEV); bsp_fatal(LEON3_FATAL_CONSOLE_REGISTER_DEV);
} }
@@ -306,147 +141,15 @@ rtems_device_driver console_initialize(
if (i == syscon_uart_index) if (i == syscon_uart_index)
continue; /* skip UART that is registered as /dev/console */ continue; /* skip UART that is registered as /dev/console */
console_name[13] = 'a' + i; console_name[13] = 'a' + i;
rtems_io_register_name( console_name, major, i+1); minor = i + 1;
rtems_termios_device_install(
console_name,
major,
minor,
handler,
leon3_console_get_uart(syscon_uart_index)
);
} }
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
} }
#if CONSOLE_USE_INTERRUPTS
static struct rtems_termios_tty *leon3_console_get_tty(
rtems_libio_open_close_args_t *args
)
{
return args->iop->data1;
}
#endif
static int leon3_console_first_open(int major, int minor, void *arg)
{
struct apbuart_priv *uart = leon3_console_get_uart(minor);
#if CONSOLE_USE_INTERRUPTS
rtems_status_code sc;
uart->cookie = leon3_console_get_tty(arg);
/* Register Interrupt handler */
sc = rtems_interrupt_handler_install(uart->irq, "console",
RTEMS_INTERRUPT_SHARED,
leon3_console_isr,
uart);
if (sc != RTEMS_SUCCESSFUL)
return -1;
uart->sending = 0;
/* Enable Receiver and transmitter and Turn on RX interrupts */
uart->regs->ctrl |= APBUART_CTRL_RE | APBUART_CTRL_TE |
APBUART_CTRL_RI;
#else
/* Initialize UART on opening */
uart->regs->ctrl |= APBUART_CTRL_RE | APBUART_CTRL_TE;
#endif
uart->regs->status = 0;
return 0;
}
#if CONSOLE_USE_INTERRUPTS
static int leon3_console_last_close(int major, int minor, void *arg)
{
struct rtems_termios_tty *tty = leon3_console_get_tty(arg);
struct apbuart_priv *uart = leon3_console_get_uart(minor);
rtems_interrupt_lock_context lock_ctx;
/* Turn off RX interrupts */
rtems_termios_interrupt_lock_acquire(tty, &lock_ctx);
uart->regs->ctrl &= ~(APBUART_CTRL_RI);
rtems_termios_interrupt_lock_release(tty, &lock_ctx);
/**** Flush device ****/
while (uart->sending) {
/* Wait until all data has been sent */
}
/* uninstall ISR */
rtems_interrupt_handler_remove(uart->irq, leon3_console_isr, uart);
return 0;
}
#endif
rtems_device_driver console_open(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
#if CONSOLE_USE_INTERRUPTS
/* Interrupt mode routines */
static const rtems_termios_callbacks Callbacks = {
leon3_console_first_open, /* firstOpen */
leon3_console_last_close, /* lastClose */
NULL, /* pollRead */
leon3_console_write_support, /* write */
leon3_console_set_attributes,/* setAttributes */
NULL, /* stopRemoteTx */
NULL, /* startRemoteTx */
1 /* outputUsesInterrupts */
};
#else
/* Polling mode routines */
static const rtems_termios_callbacks Callbacks = {
leon3_console_first_open, /* firstOpen */
NULL, /* lastClose */
leon3_console_pollRead, /* pollRead */
leon3_console_write_polled, /* write */
leon3_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;
return rtems_termios_open(major, minor, arg, &Callbacks);
}
rtems_device_driver console_close(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
return rtems_termios_close(arg);
}
rtems_device_driver console_read(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
return rtems_termios_read(arg);
}
rtems_device_driver console_write(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
return rtems_termios_write(arg);
}
rtems_device_driver console_control(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
return rtems_termios_ioctl(arg);
}

View File

@@ -157,6 +157,10 @@ $(PROJECT_INCLUDE)/apbuart_pci.h: ../../sparc/shared/include/apbuart_pci.h $(PRO
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/apbuart_pci.h $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/apbuart_pci.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/apbuart_pci.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/apbuart_pci.h
$(PROJECT_INCLUDE)/apbuart_termios.h: ../../sparc/shared/include/apbuart_termios.h $(PROJECT_INCLUDE)/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/apbuart_termios.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/apbuart_termios.h
$(PROJECT_INCLUDE)/i2cmst.h: ../../sparc/shared/include/i2cmst.h $(PROJECT_INCLUDE)/$(dirstamp) $(PROJECT_INCLUDE)/i2cmst.h: ../../sparc/shared/include/i2cmst.h $(PROJECT_INCLUDE)/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/i2cmst.h $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/i2cmst.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/i2cmst.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/i2cmst.h

View File

@@ -0,0 +1,40 @@
/*
* COPYRIGHT (c) 1989-1998.
* On-Line Applications Research Corporation (OAR).
*
* Modified for LEON3 BSP.
* COPYRIGHT (c) 2004.
* Gaisler Research.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifndef APBUART_TERMIOS_H
#define APBUART_TERMIOS_H
#include <rtems/termiostypes.h>
#include <grlib.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct apbuart_context {
struct apbuart_regs *regs;
unsigned int freq_hz;
rtems_vector_number irq;
volatile int sending;
char *buf;
};
const rtems_termios_device_handler apbuart_handler_interrupt;
const rtems_termios_device_handler apbuart_handler_polled;
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* APBUART_TERMIOS_H */

View File

@@ -0,0 +1,240 @@
/*
* COPYRIGHT (c) 1989-1998.
* On-Line Applications Research Corporation (OAR).
*
* Modified for LEON3 BSP.
* COPYRIGHT (c) 2004.
* Gaisler Research.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <apbuart_termios.h>
#include <apbuart.h>
#include <bsp.h>
static void apbuart_isr(void *arg)
{
rtems_termios_tty *tty = arg;
struct apbuart_context *uart = rtems_termios_get_device_context(tty);
unsigned int status;
char data;
/* Get all received characters */
while ((status=uart->regs->status) & APBUART_STATUS_DR) {
/* Data has arrived, get new data */
data = uart->regs->data;
/* Tell termios layer about new character */
rtems_termios_enqueue_raw_characters(tty, &data, 1);
}
if (
(status & APBUART_STATUS_TE)
&& (uart->regs->ctrl & APBUART_CTRL_TI) != 0
) {
/* write_interrupt will get called from this function */
rtems_termios_dequeue_characters(tty, 1);
}
}
static void apbuart_write_support(
rtems_termios_tty *tty,
const char *buf,
size_t len
)
{
struct apbuart_context *uart = rtems_termios_get_device_context(tty);
int sending;
if (len > 0) {
/* Enable TX interrupt (interrupt is edge-triggered) */
uart->regs->ctrl |= APBUART_CTRL_TI;
/* start UART TX, this will result in an interrupt when done */
uart->regs->data = *buf;
sending = 1;
} else {
/* No more to send, disable TX interrupts */
uart->regs->ctrl &= ~APBUART_CTRL_TI;
/* Tell close that we sent everything */
sending = 0;
}
uart->sending = sending;
}
static void apbuart_write_polled(
rtems_termios_tty *tty,
const char *buf,
size_t len
)
{
struct apbuart_context *uart = rtems_termios_get_device_context(tty);
size_t nwrite = 0;
while (nwrite < len) {
apbuart_outbyte_polled(uart->regs, *buf++, 1, 0);
nwrite++;
}
}
static int apbuart_poll_read(rtems_termios_tty *tty)
{
struct apbuart_context *uart = rtems_termios_get_device_context(tty);
return apbuart_inbyte_nonblocking(uart->regs);
}
static bool apbuart_set_attributes(
rtems_termios_tty *tty,
const struct termios *t
)
{
struct apbuart_context *uart = rtems_termios_get_device_context(tty);
rtems_interrupt_lock_context lock_context;
unsigned int scaler;
unsigned int ctrl;
int baud;
switch (t->c_cflag & CSIZE) {
default:
case CS5:
case CS6:
case CS7:
/* Hardware doesn't support other than CS8 */
return false;
case CS8:
break;
}
rtems_termios_interrupt_lock_acquire(tty, &lock_context);
/* Read out current value */
ctrl = uart->regs->ctrl;
switch (t->c_cflag & (PARENB|PARODD)) {
case (PARENB|PARODD):
/* Odd parity */
ctrl |= APBUART_CTRL_PE|APBUART_CTRL_PS;
break;
case PARENB:
/* Even parity */
ctrl &= ~APBUART_CTRL_PS;
ctrl |= APBUART_CTRL_PE;
break;
default:
case 0:
case PARODD:
/* No Parity */
ctrl &= ~(APBUART_CTRL_PS|APBUART_CTRL_PE);
}
if (!(t->c_cflag & CLOCAL)) {
ctrl |= APBUART_CTRL_FL;
} else {
ctrl &= ~APBUART_CTRL_FL;
}
/* Update new settings */
uart->regs->ctrl = ctrl;
rtems_termios_interrupt_lock_release(tty, &lock_context);
/* Baud rate */
baud = rtems_termios_baud_to_number(t->c_cflag);
if (baud > 0) {
/* Calculate Baud rate generator "scaler" number */
scaler = (((uart->freq_hz * 10) / (baud * 8)) - 5) / 10;
/* Set new baud rate by setting scaler */
uart->regs->scaler = scaler;
}
return true;
}
static bool apbuart_first_open_polled(
rtems_termios_tty *tty,
rtems_libio_open_close_args_t *args
)
{
struct apbuart_context *uart = rtems_termios_get_device_context(tty);
/* Initialize UART on opening */
uart->regs->ctrl |= APBUART_CTRL_RE | APBUART_CTRL_TE;
uart->regs->status = 0;
return true;
}
static bool apbuart_first_open_interrupt(
rtems_termios_tty *tty,
rtems_libio_open_close_args_t *args
)
{
struct apbuart_context *uart = rtems_termios_get_device_context(tty);
rtems_status_code sc;
/* Register Interrupt handler */
sc = rtems_interrupt_handler_install(uart->irq, "console",
RTEMS_INTERRUPT_SHARED,
apbuart_isr,
tty);
if (sc != RTEMS_SUCCESSFUL)
return false;
uart->sending = 0;
/* Enable Receiver and transmitter and Turn on RX interrupts */
uart->regs->ctrl |= APBUART_CTRL_RE | APBUART_CTRL_TE |
APBUART_CTRL_RI;
/* Initialize UART on opening */
uart->regs->ctrl |= APBUART_CTRL_RE | APBUART_CTRL_TE;
uart->regs->status = 0;
return true;
}
static void apbuart_last_close_interrupt(
rtems_termios_tty *tty,
rtems_libio_open_close_args_t *args
)
{
struct apbuart_context *uart = rtems_termios_get_device_context(tty);
rtems_interrupt_lock_context lock_context;
/* Turn off RX interrupts */
rtems_termios_interrupt_lock_acquire(tty, &lock_context);
uart->regs->ctrl &= ~(APBUART_CTRL_RI);
rtems_termios_interrupt_lock_release(tty, &lock_context);
/**** Flush device ****/
while (uart->sending) {
/* Wait until all data has been sent */
}
/* uninstall ISR */
rtems_interrupt_handler_remove(uart->irq, apbuart_isr, tty);
}
const rtems_termios_device_handler apbuart_handler_interrupt = {
.first_open = apbuart_first_open_interrupt,
.last_close = apbuart_last_close_interrupt,
.write = apbuart_write_support,
.set_attributes = apbuart_set_attributes,
.mode = TERMIOS_IRQ_DRIVEN
};
const rtems_termios_device_handler apbuart_handler_polled = {
.first_open = apbuart_first_open_polled,
.poll_read = apbuart_poll_read,
.write = apbuart_write_polled,
.set_attributes = apbuart_set_attributes,
.mode = TERMIOS_POLLED
};