summaryrefslogtreecommitdiffstats
path: root/bsps/sparc/shared/uart/apbuart_cons.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/sparc/shared/uart/apbuart_cons.c')
-rw-r--r--bsps/sparc/shared/uart/apbuart_cons.c867
1 files changed, 867 insertions, 0 deletions
diff --git a/bsps/sparc/shared/uart/apbuart_cons.c b/bsps/sparc/shared/uart/apbuart_cons.c
new file mode 100644
index 0000000000..e406bc01b7
--- /dev/null
+++ b/bsps/sparc/shared/uart/apbuart_cons.c
@@ -0,0 +1,867 @@
+/* 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 <drvmgr/ambapp_bus.h>
+#include <bsp/apbuart.h>
+#include <ambapp.h>
+#include <grlib.h>
+#include <bsp/cons.h>
+#include <rtems/termiostypes.h>
+#include <bsp/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 */
+extern void apbuart_outbyte_polled(
+ struct apbuart_regs *regs,
+ unsigned char ch,
+ int do_cr_on_newline,
+ int wait_sent);
+extern int apbuart_inbyte_nonblocking(struct apbuart_regs *regs);
+#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
+
+#ifndef LEON3
+/* This routine transmits a character, it will busy-wait until on character
+ * fits in the APBUART Transmit FIFO
+ */
+void apbuart_outbyte_polled(
+ struct apbuart_regs *regs,
+ unsigned char ch,
+ int do_cr_on_newline,
+ int wait_sent)
+{
+send:
+ while ((regs->status & LEON_REG_UART_STATUS_THE) == 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"::);
+ }
+ regs->data = (unsigned int) ch;
+
+ if ((ch == '\n') && do_cr_on_newline) {
+ ch = '\r';
+ goto send;
+ }
+
+ /* Wait until the character has been sent? */
+ if (wait_sent) {
+ while ((regs->status & LEON_REG_UART_STATUS_THE) == 0)
+ ;
+ }
+}
+
+/* This routine polls for one character, return EOF if no character is available */
+int apbuart_inbyte_nonblocking(struct apbuart_regs *regs)
+{
+ if (regs->status & LEON_REG_UART_STATUS_ERR) {
+ regs->status = ~LEON_REG_UART_STATUS_ERR;
+ }
+
+ if ((regs->status & LEON_REG_UART_STATUS_DR) == 0)
+ return EOF;
+
+ return (int)regs->data;
+}
+#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;
+}
+
+
+struct apbuart_baud {
+ unsigned int num;
+ unsigned int baud;
+};
+static struct apbuart_baud apbuart_baud_table[] = {
+ {B50, 50},
+ {B75, 75},
+ {B110, 110},
+ {B134, 134},
+ {B150, 150},
+ {B200, 200},
+ {B300, 300},
+ {B600, 600},
+ {B1200, 1200},
+ {B1800, 1800},
+ {B2400, 2400},
+ {B4800, 4800},
+ {B9600, 9600},
+ {B19200, 19200},
+ {B38400, 38400},
+ {B57600, 57600},
+ {B115200, 115200},
+ {B230400, 230400},
+ {B460800, 460800},
+};
+#define BAUD_NUM (sizeof(apbuart_baud_table)/sizeof(struct apbuart_baud))
+
+static int apbuart_baud_num2baud(unsigned int num)
+{
+ int i;
+
+ for(i=0; i<BAUD_NUM; i++)
+ if (apbuart_baud_table[i].num == num)
+ return apbuart_baud_table[i].baud;
+ return -1;
+}
+
+static struct apbuart_baud *apbuart_baud_find_closest(unsigned int baud)
+{
+ int i, diff;
+
+ for(i=0; i<BAUD_NUM-1; i++) {
+ diff = apbuart_baud_table[i+1].baud -
+ apbuart_baud_table[i].baud;
+ if (baud < (apbuart_baud_table[i].baud + diff/2))
+ return &apbuart_baud_table[i];
+ }
+ return &apbuart_baud_table[BAUD_NUM-1];
+}
+
+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 struct apbuart_baud *apbuart_get_baud_closest(struct apbuart_priv *uart)
+{
+ return apbuart_baud_find_closest(apbuart_get_baud(uart));
+}
+
+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 = apbuart_baud_num2baud(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;
+ struct apbuart_baud *baud;
+
+ 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;
+
+ baud = apbuart_get_baud_closest(uart);
+ t->c_cflag |= baud->num;
+}
+
+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);
+ }
+}
+