summaryrefslogtreecommitdiffstats
path: root/bsps/shared/grlib/uart/apbuart_termios.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/shared/grlib/uart/apbuart_termios.c')
-rw-r--r--bsps/shared/grlib/uart/apbuart_termios.c259
1 files changed, 259 insertions, 0 deletions
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
+};