diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-12-22 18:31:04 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2019-01-22 12:46:33 +0100 |
commit | 7eb606d393306da25fd6e6aa7f8595ffb2e924fc (patch) | |
tree | 085befd6fe5e29d229fec9683735516d48e9d41e /bsps/shared/grlib/uart | |
parent | grlib: Move header files (diff) | |
download | rtems-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.c | 757 | ||||
-rw-r--r-- | bsps/shared/grlib/uart/apbuart_polled.c | 52 | ||||
-rw-r--r-- | bsps/shared/grlib/uart/apbuart_termios.c | 259 | ||||
-rw-r--r-- | bsps/shared/grlib/uart/cons.c | 137 |
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 |