summaryrefslogtreecommitdiffstats
path: root/bsps/shared/grlib/uart
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-12-22 18:31:04 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2019-01-22 12:46:33 +0100
commit7eb606d393306da25fd6e6aa7f8595ffb2e924fc (patch)
tree085befd6fe5e29d229fec9683735516d48e9d41e /bsps/shared/grlib/uart
parentgrlib: Move header files (diff)
downloadrtems-7eb606d393306da25fd6e6aa7f8595ffb2e924fc.tar.bz2
grlib: Move source files
Update #3678.
Diffstat (limited to 'bsps/shared/grlib/uart')
-rw-r--r--bsps/shared/grlib/uart/apbuart_cons.c757
-rw-r--r--bsps/shared/grlib/uart/apbuart_polled.c52
-rw-r--r--bsps/shared/grlib/uart/apbuart_termios.c259
-rw-r--r--bsps/shared/grlib/uart/cons.c137
4 files changed, 1205 insertions, 0 deletions
diff --git a/bsps/shared/grlib/uart/apbuart_cons.c b/bsps/shared/grlib/uart/apbuart_cons.c
new file mode 100644
index 0000000000..8cd8a1ebf0
--- /dev/null
+++ b/bsps/shared/grlib/uart/apbuart_cons.c
@@ -0,0 +1,757 @@
+/* This file contains the driver for the GRLIB APBUART serial port. The driver
+ * is implemented by using the cons.c console layer. Interrupt/Polling/Task
+ * driven mode can be configured using driver resources:
+ *
+ * - mode (0=Polling, 1=Interrupt, 2=Task-Driven-Interrupt Mode)
+ * - syscon (0=Force not Ssystem Console, 1=Suggest System Console)
+ *
+ * The BSP define APBUART_INFO_AVAIL in order to add the info routine
+ * used for debugging.
+ *
+ * COPYRIGHT (c) 2010.
+ * Cobham Gaisler AB.
+ *
+ * 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.
+ */
+
+/******************* Driver manager interface ***********************/
+#include <bsp.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <rtems/bspIo.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <drvmgr/drvmgr.h>
+#include <grlib/ambapp_bus.h>
+#include <grlib/apbuart.h>
+#include <grlib/ambapp.h>
+#include <grlib/grlib.h>
+#include <grlib/cons.h>
+#include <rtems/termiostypes.h>
+#include <grlib/apbuart_cons.h>
+
+/*#define DEBUG 1 */
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+/* LEON3 Low level transmit/receive functions provided by debug-uart code */
+#ifdef LEON3
+extern struct apbuart_regs *leon3_debug_uart; /* The debug UART */
+#endif
+
+/* Probed hardware capabilities */
+enum {
+ CAP_FIFO = 0x01, /* FIFO available */
+ CAP_DI = 0x02, /* RX delayed interrupt available */
+};
+struct apbuart_priv {
+ struct console_dev condev;
+ struct drvmgr_dev *dev;
+ struct apbuart_regs *regs;
+ struct rtems_termios_tty *tty;
+ char devName[32];
+ volatile int sending;
+ int mode;
+ int cap;
+};
+
+/* Getters for different interfaces. It happens to be just casting which we do
+ * in one place to avoid getting cast away. */
+static struct console_dev *base_get_condev(rtems_termios_device_context *base)
+{
+ return (struct console_dev *) base;
+}
+
+static struct apbuart_priv *condev_get_priv(struct console_dev *condev)
+{
+ return (struct apbuart_priv *) condev;
+}
+
+static struct apbuart_priv *base_get_priv(rtems_termios_device_context *base)
+{
+ return condev_get_priv(base_get_condev(base));
+}
+
+/* TERMIOS Layer Callback functions */
+static bool first_open(
+ rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args
+);
+static void last_close(
+ rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ rtems_libio_open_close_args_t *args
+);
+static void write_interrupt(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+);
+static bool set_attributes(
+ rtems_termios_device_context *base,
+ const struct termios *t
+);
+static void get_attributes(
+ rtems_termios_device_context *base,
+ struct termios *t
+);
+static int read_polled(rtems_termios_device_context *base);
+static int read_task(rtems_termios_device_context *base);
+static void write_polled(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+);
+
+static void apbuart_cons_isr(void *arg);
+int apbuart_get_baud(struct apbuart_priv *uart);
+
+int apbuart_init1(struct drvmgr_dev *dev);
+#ifdef APBUART_INFO_AVAIL
+static int apbuart_info(
+ struct drvmgr_dev *dev,
+ void (*print_line)(void *p, char *str),
+ void *p, int, char *argv[]);
+#define APBUART_INFO_FUNC apbuart_info
+#else
+#define APBUART_INFO_FUNC NULL
+#endif
+
+struct drvmgr_drv_ops apbuart_ops =
+{
+ .init = {apbuart_init1, NULL, NULL, NULL},
+ .remove = NULL,
+ .info = APBUART_INFO_FUNC
+};
+
+static struct amba_dev_id apbuart_ids[] =
+{
+ {VENDOR_GAISLER, GAISLER_APBUART},
+ {0, 0} /* Mark end of table */
+};
+
+static struct amba_drv_info apbuart_drv_info =
+{
+ {
+ DRVMGR_OBJ_DRV, /* Driver */
+ NULL, /* Next driver */
+ NULL, /* Device list */
+ DRIVER_AMBAPP_GAISLER_APBUART_ID, /* Driver ID */
+ "APBUART_DRV", /* Driver Name */
+ DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */
+ &apbuart_ops,
+ NULL, /* Funcs */
+ 0, /* No devices yet */
+ sizeof(struct apbuart_priv), /*DrvMgr alloc private*/
+ },
+ &apbuart_ids[0]
+};
+
+void apbuart_cons_register_drv (void)
+{
+ DBG("Registering APBUART Console driver\n");
+ drvmgr_drv_register(&apbuart_drv_info.general);
+}
+
+static const rtems_termios_device_handler handler_interrupt = {
+ .first_open = first_open,
+ .last_close = last_close,
+ .write = write_interrupt,
+ .set_attributes = set_attributes,
+ .mode = TERMIOS_IRQ_DRIVEN
+};
+
+static const rtems_termios_device_handler handler_task = {
+ .first_open = first_open,
+ .last_close = last_close,
+ .poll_read = read_task,
+ .write = write_interrupt,
+ .set_attributes = set_attributes,
+ .mode = TERMIOS_TASK_DRIVEN
+};
+
+static const rtems_termios_device_handler handler_polled = {
+ .first_open = first_open,
+ .last_close = last_close,
+ .poll_read = read_polled,
+ .write = write_polled,
+ .set_attributes = set_attributes,
+ .mode = TERMIOS_POLLED
+};
+
+/*
+ * APBUART hardware instantiation is flexible. Probe features here and driver
+ * can select appropriate routines for the hardware. probecap() return value
+ * is a CAP_ bitmask.
+ */
+static int probecap(struct apbuart_regs *regs)
+{
+ int cap = 0;
+
+ /* Probe FIFO */
+ if (regs->ctrl & APBUART_CTRL_FA) {
+ cap |= CAP_FIFO;
+
+ /* Probe RX delayed interrupt */
+ regs->ctrl |= APBUART_CTRL_DI;
+ if (regs->ctrl & APBUART_CTRL_DI) {
+ regs->ctrl &= ~APBUART_CTRL_DI;
+ cap |= CAP_DI;
+ }
+ }
+
+ return cap;
+}
+
+int apbuart_init1(struct drvmgr_dev *dev)
+{
+ struct apbuart_priv *priv;
+ struct amba_dev_info *ambadev;
+ struct ambapp_core *pnpinfo;
+ union drvmgr_key_value *value;
+ char prefix[32];
+ unsigned int db;
+ static int first_uart = 1;
+
+ /* The default operation in AMP is to use APBUART[0] for CPU[0],
+ * APBUART[1] for CPU[1] and so on. The remaining UARTs is not used
+ * since we don't know how many CPU-cores there are. Note this only
+ * affects the on-chip amba bus (the root bus). The user can override
+ * the default resource sharing by defining driver resources for the
+ * APBUART devices on each AMP OS instance.
+ */
+#if defined(RTEMS_MULTIPROCESSING) && defined(LEON3)
+ if (drvmgr_on_rootbus(dev) && dev->minor_drv != LEON3_Cpu_Index &&
+ drvmgr_keys_get(dev, NULL) != 0) {
+ /* User hasn't configured on-chip APBUART, leave it untouched */
+ return DRVMGR_EBUSY;
+ }
+#endif
+
+ DBG("APBUART[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);
+ /* Private data was allocated and zeroed by driver manager */
+ priv = dev->priv;
+ if (!priv)
+ return DRVMGR_NOMEM;
+ priv->dev = dev;
+
+ /* Get device information from AMBA PnP information */
+ ambadev = (struct amba_dev_info *)priv->dev->businfo;
+ if (ambadev == NULL)
+ return -1;
+ pnpinfo = &ambadev->info;
+ priv->regs = (struct apbuart_regs *)pnpinfo->apb_slv->start;
+
+ /* Clear HW regs, leave baudrate register as it is */
+ priv->regs->status = 0;
+
+ /* leave Transmitter/receiver if this is the RTEMS debug UART (assume
+ * it has been setup by boot loader).
+ */
+ db = 0;
+#ifdef LEON3
+ if (priv->regs == leon3_debug_uart) {
+ db = priv->regs->ctrl & (LEON_REG_UART_CTRL_RE |
+ LEON_REG_UART_CTRL_TE |
+ LEON_REG_UART_CTRL_PE |
+ LEON_REG_UART_CTRL_PS);
+ }
+#endif
+ /* Let UART debug tunnelling be untouched if Flow-control is set.
+ *
+ * With old APBUARTs debug is enabled by setting LB and FL, since LB or
+ * DB are not reset we can not trust them. However since FL is reset we
+ * guess that we are debugging if FL is already set, the debugger set
+ * either LB or DB depending on UART capabilities.
+ */
+ if (priv->regs->ctrl & LEON_REG_UART_CTRL_FL) {
+ db |= priv->regs->ctrl & (LEON_REG_UART_CTRL_DB |
+ LEON_REG_UART_CTRL_LB | LEON_REG_UART_CTRL_FL);
+ }
+
+ priv->regs->ctrl = db;
+
+ priv->cap = probecap(priv->regs);
+
+ /* The system console and Debug console may depend on this device, so
+ * initialize it straight away.
+ *
+ * We default to have System Console on first APBUART, user may override
+ * this behaviour by setting the syscon option to 0.
+ */
+ if (drvmgr_on_rootbus(dev) && first_uart) {
+ priv->condev.flags = CONSOLE_FLAG_SYSCON;
+ first_uart = 0;
+ } else {
+ priv->condev.flags = 0;
+ }
+
+ value = drvmgr_dev_key_get(priv->dev, "syscon", DRVMGR_KT_INT);
+ if (value) {
+ if (value->i)
+ priv->condev.flags |= CONSOLE_FLAG_SYSCON;
+ else
+ priv->condev.flags &= ~CONSOLE_FLAG_SYSCON;
+ }
+
+ /* Select 0=Polled, 1=IRQ, 2=Task-Driven UART Mode */
+ value = drvmgr_dev_key_get(priv->dev, "mode", DRVMGR_KT_INT);
+ if (value)
+ priv->mode = value->i;
+ else
+ priv->mode = TERMIOS_POLLED;
+ /* TERMIOS device handlers */
+ if (priv->mode == TERMIOS_IRQ_DRIVEN) {
+ priv->condev.handler = &handler_interrupt;
+ } else if (priv->mode == TERMIOS_TASK_DRIVEN) {
+ priv->condev.handler = &handler_task;
+ } else {
+ priv->condev.handler = &handler_polled;
+ }
+
+ priv->condev.fsname = NULL;
+ /* Get Filesystem name prefix */
+ prefix[0] = '\0';
+ if (drvmgr_get_dev_prefix(dev, prefix)) {
+ /* Got special prefix, this means we have a bus prefix
+ * And we should use our "bus minor"
+ */
+ sprintf(priv->devName, "/dev/%sapbuart%d", prefix, dev->minor_bus);
+ priv->condev.fsname = priv->devName;
+ } else {
+ sprintf(priv->devName, "/dev/apbuart%d", dev->minor_drv);
+ }
+
+ /* Register it as a console device, the console driver will register
+ * a termios device as well
+ */
+ console_dev_register(&priv->condev);
+
+ return DRVMGR_OK;
+}
+
+#ifdef APBUART_INFO_AVAIL
+static int apbuart_info(
+ struct drvmgr_dev *dev,
+ void (*print_line)(void *p, char *str),
+ void *p, int argc, char *argv[])
+{
+ struct apbuart_priv *priv = dev->priv;
+ char *str1;
+ char buf[64];
+
+ if (dev->priv == NULL)
+ return -DRVMGR_EINVAL;
+
+ if (priv->mode == TERMIOS_POLLED)
+ str1 = "TERMIOS_POLLED";
+ else if (priv->mode == TERMIOS_IRQ_DRIVEN)
+ str1 = "TERMIOS_IRQ_DRIVEN";
+ else if (priv->mode == TERMIOS_TASK_DRIVEN)
+ str1 = "TERMIOS_TASK_DRIVEN";
+ else
+ str1 = "BAD MODE";
+
+ sprintf(buf, "UART Mode: %s", str1);
+ print_line(p, buf);
+ if (priv->condev.fsname) {
+ sprintf(buf, "FS Name: %s", priv->condev.fsname);
+ print_line(p, buf);
+ }
+ sprintf(buf, "STATUS REG: 0x%x", priv->regs->status);
+ print_line(p, buf);
+ sprintf(buf, "CTRL REG: 0x%x", priv->regs->ctrl);
+ print_line(p, buf);
+ sprintf(buf, "SCALER REG: 0x%x baud rate %d",
+ priv->regs->scaler, apbuart_get_baud(priv));
+ print_line(p, buf);
+
+ return DRVMGR_OK;
+}
+#endif
+
+static bool first_open(
+ rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args
+)
+{
+ struct apbuart_priv *uart = base_get_priv(base);
+
+ uart->tty = tty;
+
+ /* Inherit UART hardware parameters from bootloader on system console */
+ if (uart->condev.flags & CONSOLE_FLAG_SYSCON_GRANT) {
+ get_attributes(base, term);
+ term->c_oflag |= ONLCR;
+ set_attributes(base, term);
+ }
+
+ /* Enable TX/RX */
+ uart->regs->ctrl |= APBUART_CTRL_RE | APBUART_CTRL_TE;
+
+ if (uart->mode != TERMIOS_POLLED) {
+ int ret;
+ uint32_t ctrl;
+
+ /* Register interrupt and enable it */
+ ret = drvmgr_interrupt_register(
+ uart->dev, 0, uart->devName, apbuart_cons_isr, tty
+ );
+ if (ret) {
+ return false;
+ }
+
+ uart->sending = 0;
+
+ /* Turn on RX interrupts */
+ ctrl = uart->regs->ctrl;
+ ctrl |= APBUART_CTRL_RI;
+ if (uart->cap & CAP_DI) {
+ /* Use RX FIFO interrupt only if delayed interrupt available. */
+ ctrl |= (APBUART_CTRL_DI | APBUART_CTRL_RF);
+ }
+ uart->regs->ctrl = ctrl;
+ }
+
+ return true;
+}
+
+static void last_close(
+ rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ rtems_libio_open_close_args_t *args
+)
+{
+ struct apbuart_priv *uart = base_get_priv(base);
+ rtems_interrupt_lock_context lock_context;
+
+ if (uart->mode != TERMIOS_POLLED) {
+ /* Turn off RX interrupts */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ uart->regs->ctrl &=
+ ~(APBUART_CTRL_DI | APBUART_CTRL_RI | APBUART_CTRL_RF);
+ rtems_termios_device_lock_release(base, &lock_context);
+
+ /**** Flush device ****/
+ while (uart->sending) {
+ /* Wait until all data has been sent */
+ }
+ while (
+ (uart->regs->ctrl & APBUART_CTRL_TE) &&
+ !(uart->regs->status & APBUART_STATUS_TS)
+ ) {
+ /* Wait until all data has left shift register */
+ }
+
+ /* Disable and unregister interrupt handler */
+ drvmgr_interrupt_unregister(uart->dev, 0, apbuart_cons_isr, tty);
+ }
+
+#ifdef LEON3
+ /* Disable TX/RX if not used for DEBUG */
+ if (uart->regs != leon3_debug_uart)
+ uart->regs->ctrl &= ~(APBUART_CTRL_RE | APBUART_CTRL_TE);
+#endif
+}
+
+static int read_polled(rtems_termios_device_context *base)
+{
+ struct apbuart_priv *uart = base_get_priv(base);
+
+ return apbuart_inbyte_nonblocking(uart->regs);
+}
+
+/* This function is called from TERMIOS rxdaemon task without device lock. */
+static int read_task(rtems_termios_device_context *base)
+{
+ rtems_interrupt_lock_context lock_context;
+ struct apbuart_priv *uart = base_get_priv(base);
+ struct apbuart_regs *regs = uart->regs;
+ int cnt;
+ char buf[33];
+ struct rtems_termios_tty *tty;
+ uint32_t ctrl_add;
+
+ ctrl_add = APBUART_CTRL_RI;
+ if (uart->cap & CAP_DI) {
+ ctrl_add |= (APBUART_CTRL_DI | APBUART_CTRL_RF);
+ }
+ tty = uart->tty;
+ do {
+ cnt = 0;
+ while (
+ (regs->status & APBUART_STATUS_DR) &&
+ (cnt < sizeof(buf))
+ ) {
+ buf[cnt] = regs->data;
+ cnt++;
+ }
+ if (0 < cnt) {
+ /* Tell termios layer about new characters */
+ rtems_termios_enqueue_raw_characters(tty, &buf[0], cnt);
+ }
+
+ /*
+ * Turn on RX interrupts. A new character in FIFO now may not
+ * cause interrupt so we must check data ready again
+ * afterwards.
+ */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ regs->ctrl |= ctrl_add;
+ rtems_termios_device_lock_release(base, &lock_context);
+ } while (regs->status & APBUART_STATUS_DR);
+
+ return EOF;
+}
+
+int apbuart_get_baud(struct apbuart_priv *uart)
+{
+ unsigned int core_clk_hz;
+ unsigned int scaler;
+
+ /* Get current scaler setting */
+ scaler = uart->regs->scaler;
+
+ /* Get APBUART core frequency */
+ drvmgr_freq_get(uart->dev, DEV_APB_SLV, &core_clk_hz);
+
+ /* Calculate baud rate from generator "scaler" number */
+ return core_clk_hz / ((scaler + 1) * 8);
+}
+
+static bool set_attributes(
+ rtems_termios_device_context *base,
+ const struct termios *t
+)
+{
+ unsigned int core_clk_hz;
+ unsigned int scaler;
+ unsigned int ctrl;
+ int baud;
+ struct apbuart_priv *uart = base_get_priv(base);
+ rtems_interrupt_lock_context lock_context;
+
+ 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_device_lock_acquire(base, &lock_context);
+
+ /* Read out current value */
+ ctrl = uart->regs->ctrl;
+
+ switch(t->c_cflag & (PARENB|PARODD)){
+ case (PARENB|PARODD):
+ /* Odd parity */
+ ctrl |= LEON_REG_UART_CTRL_PE|LEON_REG_UART_CTRL_PS;
+ break;
+
+ case PARENB:
+ /* Even parity */
+ ctrl &= ~LEON_REG_UART_CTRL_PS;
+ ctrl |= LEON_REG_UART_CTRL_PE;
+ break;
+
+ default:
+ case 0:
+ case PARODD:
+ /* No Parity */
+ ctrl &= ~(LEON_REG_UART_CTRL_PS|LEON_REG_UART_CTRL_PE);
+ }
+
+ if (!(t->c_cflag & CLOCAL))
+ ctrl |= LEON_REG_UART_CTRL_FL;
+ else
+ ctrl &= ~LEON_REG_UART_CTRL_FL;
+
+ /* Update new settings */
+ uart->regs->ctrl = ctrl;
+
+ rtems_termios_device_lock_release(base, &lock_context);
+
+ /* Baud rate */
+ baud = rtems_termios_baud_to_number(t->c_ospeed);
+ if (baud > 0){
+ /* Get APBUART core frequency */
+ drvmgr_freq_get(uart->dev, DEV_APB_SLV, &core_clk_hz);
+
+ /* Calculate Baud rate generator "scaler" number */
+ scaler = (((core_clk_hz*10)/(baud*8))-5)/10;
+
+ /* Set new baud rate by setting scaler */
+ uart->regs->scaler = scaler;
+ }
+
+ return true;
+}
+
+static void get_attributes(
+ rtems_termios_device_context *base,
+ struct termios *t
+)
+{
+ struct apbuart_priv *uart = base_get_priv(base);
+ unsigned int ctrl;
+
+ t->c_cflag = t->c_cflag & ~(CSIZE|PARENB|PARODD|CLOCAL);
+
+ /* Hardware support only CS8 */
+ t->c_cflag |= CS8;
+
+ /* Read out current parity */
+ ctrl = uart->regs->ctrl;
+ if (ctrl & LEON_REG_UART_CTRL_PE) {
+ if (ctrl & LEON_REG_UART_CTRL_PS)
+ t->c_cflag |= PARENB|PARODD; /* Odd parity */
+ else
+ t->c_cflag |= PARENB; /* Even parity */
+ }
+
+ if ((ctrl & LEON_REG_UART_CTRL_FL) == 0)
+ t->c_cflag |= CLOCAL;
+
+ rtems_termios_set_best_baud(t, apbuart_get_baud(uart));
+}
+
+static void write_polled(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ struct apbuart_priv *uart = base_get_priv(base);
+ int nwrite = 0;
+
+ while (nwrite < len) {
+ apbuart_outbyte_polled(uart->regs, *buf++, 0, 0);
+ nwrite++;
+ }
+}
+
+static void write_interrupt(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ struct apbuart_priv *uart = base_get_priv(base);
+ struct apbuart_regs *regs = uart->regs;
+ int sending;
+ unsigned int ctrl;
+
+ ctrl = regs->ctrl;
+
+ if (len > 0) {
+ /*
+ * sending is used to remember how much we have outstanding so
+ * we can tell termios later.
+ */
+ /* Enable TX interrupt (interrupt is edge-triggered) */
+ regs->ctrl = ctrl | APBUART_CTRL_TI;
+
+ if (ctrl & APBUART_CTRL_FA) {
+ /* APBUART with FIFO.. Fill as many as FIFO allows */
+ sending = 0;
+ while (
+ ((regs->status & APBUART_STATUS_TF) == 0) &&
+ (sending < len)
+ ) {
+ regs->data = *buf;
+ buf++;
+ sending++;
+ }
+ } else {
+ /* start UART TX, this will result in an interrupt when done */
+ regs->data = *buf;
+
+ sending = 1;
+ }
+ } else {
+ /* No more to send, disable TX interrupts */
+ regs->ctrl = ctrl & ~APBUART_CTRL_TI;
+
+ /* Tell close that we sent everything */
+ sending = 0;
+ }
+
+ uart->sending = sending;
+}
+
+/* Handle UART interrupts */
+static void apbuart_cons_isr(void *arg)
+{
+ rtems_termios_tty *tty = arg;
+ rtems_termios_device_context *base;
+ struct console_dev *condev = rtems_termios_get_device_context(tty);
+ struct apbuart_priv *uart = condev_get_priv(condev);
+ struct apbuart_regs *regs = uart->regs;
+ unsigned int status;
+ char buf[33];
+ int cnt;
+
+ if (uart->mode == TERMIOS_TASK_DRIVEN) {
+ if ((status = regs->status) & APBUART_STATUS_DR) {
+ rtems_interrupt_lock_context lock_context;
+
+ /* Turn off RX interrupts */
+ base = rtems_termios_get_device_context(tty);
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ regs->ctrl &=
+ ~(APBUART_CTRL_DI | APBUART_CTRL_RI |
+ APBUART_CTRL_RF);
+ rtems_termios_device_lock_release(base, &lock_context);
+ /* Activate termios RX daemon task */
+ rtems_termios_rxirq_occured(tty);
+ }
+ } else {
+ /*
+ * Get all new characters from APBUART RX (FIFO) and store them
+ * on the stack. Then tell termios about the new characters.
+ * Maximum APBUART RX FIFO size is 32 characters.
+ */
+ cnt = 0;
+ while (
+ ((status=regs->status) & APBUART_STATUS_DR) &&
+ (cnt < sizeof(buf))
+ ) {
+ buf[cnt] = regs->data;
+ cnt++;
+ }
+ if (0 < cnt) {
+ /* Tell termios layer about new characters */
+ rtems_termios_enqueue_raw_characters(tty, &buf[0], cnt);
+ }
+ }
+
+ if (uart->sending && (status & APBUART_STATUS_TE)) {
+ /* Tell close that we sent everything */
+ cnt = uart->sending;
+
+ /*
+ * Tell termios how much we have sent. dequeue() may call
+ * write_interrupt() to refill the transmitter.
+ * write_interrupt() will eventually be called with 0 len to
+ * disable TX interrupts.
+ */
+ rtems_termios_dequeue_characters(tty, cnt);
+ }
+}
+
diff --git a/bsps/shared/grlib/uart/apbuart_polled.c b/bsps/shared/grlib/uart/apbuart_polled.c
new file mode 100644
index 0000000000..0fbfbc51ba
--- /dev/null
+++ b/bsps/shared/grlib/uart/apbuart_polled.c
@@ -0,0 +1,52 @@
+/*
+ * COPYRIGHT (c) 2010.
+ * Cobham Gaisler AB.
+ *
+ * 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 <grlib/apbuart.h>
+
+void apbuart_outbyte_polled(
+ struct apbuart_regs *regs,
+ unsigned char ch,
+ int do_cr_on_newline,
+ int wait_sent
+)
+{
+send:
+ while ( (regs->status & APBUART_STATUS_TE) == 0 ) {
+ /* Lower bus utilization while waiting for UART */
+ __asm__ volatile ("nop"::); __asm__ volatile ("nop"::);
+ __asm__ volatile ("nop"::); __asm__ volatile ("nop"::);
+ __asm__ volatile ("nop"::); __asm__ volatile ("nop"::);
+ __asm__ volatile ("nop"::); __asm__ volatile ("nop"::);
+ }
+
+ if ((ch == '\n') && do_cr_on_newline) {
+ regs->data = (unsigned int) '\r';
+ do_cr_on_newline = 0;
+ goto send;
+ }
+ regs->data = (unsigned int) ch;
+
+ /* Wait until the character has been sent? */
+ if (wait_sent) {
+ while ((regs->status & APBUART_STATUS_TE) == 0)
+ ;
+ }
+}
+
+int apbuart_inbyte_nonblocking(struct apbuart_regs *regs)
+{
+ /* Clear errors */
+ if (regs->status & APBUART_STATUS_ERR)
+ regs->status = ~APBUART_STATUS_ERR;
+
+ if ((regs->status & APBUART_STATUS_DR) == 0)
+ return -1;
+ else
+ return (int) regs->data;
+}
diff --git a/bsps/shared/grlib/uart/apbuart_termios.c b/bsps/shared/grlib/uart/apbuart_termios.c
new file mode 100644
index 0000000000..81df89c171
--- /dev/null
+++ b/bsps/shared/grlib/uart/apbuart_termios.c
@@ -0,0 +1,259 @@
+/*
+ * 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 <grlib/apbuart_termios.h>
+#include <grlib/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_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ struct apbuart_context *uart = (struct apbuart_context *) base;
+ 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_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ struct apbuart_context *uart = (struct apbuart_context *) base;
+ size_t nwrite = 0;
+
+ while (nwrite < len) {
+ apbuart_outbyte_polled(uart->regs, *buf++, 0, 0);
+ nwrite++;
+ }
+}
+
+static int apbuart_poll_read(rtems_termios_device_context *base)
+{
+ struct apbuart_context *uart = (struct apbuart_context *) base;
+
+ return apbuart_inbyte_nonblocking(uart->regs);
+}
+
+static bool apbuart_set_attributes(
+ rtems_termios_device_context *base,
+ const struct termios *t
+)
+{
+ struct apbuart_context *uart = (struct apbuart_context *) base;
+ 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_device_lock_acquire(base, &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_device_lock_release(base, &lock_context);
+
+ /* Baud rate */
+ baud = rtems_termios_baud_to_number(t->c_ospeed);
+ 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 void apbuart_set_best_baud(
+ const struct apbuart_context *uart,
+ struct termios *term
+)
+{
+ uint32_t baud = (uart->freq_hz * 10) / ((uart->regs->scaler * 10 + 5) * 8);
+
+ rtems_termios_set_best_baud(term, baud);
+}
+
+static bool apbuart_first_open_polled(
+ rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args
+)
+{
+ struct apbuart_context *uart = (struct apbuart_context *) base;
+
+ apbuart_set_best_baud(uart, term);
+
+ /* 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_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args
+)
+{
+ struct apbuart_context *uart = (struct apbuart_context *) base;
+ rtems_status_code sc;
+
+ apbuart_set_best_baud(uart, term);
+
+ /* 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_termios_device_context *base,
+ rtems_libio_open_close_args_t *args
+)
+{
+ struct apbuart_context *uart = (struct apbuart_context *) base;
+ rtems_interrupt_lock_context lock_context;
+
+ /* Turn off RX interrupts */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ uart->regs->ctrl &= ~(APBUART_CTRL_RI);
+ rtems_termios_device_lock_release(base, &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
+};
diff --git a/bsps/shared/grlib/uart/cons.c b/bsps/shared/grlib/uart/cons.c
new file mode 100644
index 0000000000..5fa41e6914
--- /dev/null
+++ b/bsps/shared/grlib/uart/cons.c
@@ -0,0 +1,137 @@
+/* This file contains the TTY driver for the serial ports. The driver
+ * is layered so that different UART hardware can be used. It is implemented
+ * using the Driver Manager.
+ *
+ * This driver uses the termios pseudo driver.
+ *
+ * COPYRIGHT (c) 2010.
+ * Cobham Gaisler AB.
+ *
+ * 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 <bsp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <grlib/cons.h>
+#include <rtems/console.h>
+
+#ifdef RTEMS_DRVMGR_STARTUP
+
+/* 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 1.0.17-b2710) has the GRMON debug bit and can
+ * handle interrupts.
+ */
+
+static int console_initialized = 0;
+
+#define FLAG_SYSCON 0x01
+struct console_priv {
+ int flags; /* 0x1=SystemConsole */
+ int minor;
+ struct console_dev *dev;
+};
+
+#define CONSOLE_MAX BSP_NUMBER_OF_TERMIOS_PORTS
+struct console_priv cons[CONSOLE_MAX] = {{0,0},};
+
+/* Install Console in TERMIOS layer */
+static void console_dev_init(struct console_priv *con)
+{
+ char name[16], *fsname;
+ rtems_status_code status;
+ int minor;
+
+ minor = con->minor;
+ if (!con->dev->fsname) {
+ strcpy(name, "/dev/console_a");
+ /* Special console name and MINOR for SYSTEM CONSOLE */
+ if (minor == 0)
+ name[12] = '\0'; /* /dev/console */
+ name[13] += minor; /* when minor=0, this has no effect... */
+ fsname = name;
+ } else {
+ fsname = con->dev->fsname;
+ }
+ status = rtems_termios_device_install(
+ fsname,
+ con->dev->handler,
+ NULL,
+ &con->dev->base
+ );
+ if (status != RTEMS_SUCCESSFUL) {
+ rtems_fatal_error_occurred(status);
+ }
+}
+
+/* Called by device driver to register itself to the cons interface. */
+void console_dev_register(struct console_dev *dev)
+{
+ int i, minor = 0;
+ struct console_priv *con = NULL;
+
+ if ((dev->flags & CONSOLE_FLAG_SYSCON) && !cons[0].dev) {
+ con = &cons[0];
+ con->flags = FLAG_SYSCON;
+ } else {
+ for (i=1; i<CONSOLE_MAX; i++) {
+ if (!cons[i].dev) {
+ con = &cons[i];
+ con->flags = 0;
+ minor = i;
+ break;
+ }
+ }
+ }
+ if (con == NULL) {
+ /* Not enough console structures */
+ return;
+ }
+ dev->flags &= ~CONSOLE_FLAG_SYSCON_GRANT;
+ if (con->flags & FLAG_SYSCON) {
+ dev->flags |= CONSOLE_FLAG_SYSCON_GRANT;
+ }
+
+ /* Assign Console */
+ con->dev = dev;
+ con->minor = minor;
+
+ if (console_initialized) {
+ /* Console layer is already initialized, that means that we can
+ * register termios interface directly.
+ */
+ console_dev_init(con);
+ }
+}
+
+#if 0
+void console_dev_unregister(struct console_dev *dev)
+{
+
+}
+#endif
+
+rtems_device_driver console_initialize(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg)
+{
+ int i;
+
+ rtems_termios_initialize();
+
+ /* Register all Console a file system device node */
+ for (i=0; i<CONSOLE_MAX; i++) {
+ if (cons[i].dev)
+ console_dev_init(&cons[i]);
+ }
+
+ console_initialized = 1;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+#endif