diff options
Diffstat (limited to 'bsps/sparc/shared/uart/apbuart_cons.c')
-rw-r--r-- | bsps/sparc/shared/uart/apbuart_cons.c | 867 |
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); + } +} + |