summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/bfin/serial/uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libcpu/bfin/serial/uart.c')
-rw-r--r--c/src/lib/libcpu/bfin/serial/uart.c412
1 files changed, 412 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/bfin/serial/uart.c b/c/src/lib/libcpu/bfin/serial/uart.c
new file mode 100644
index 0000000000..8637830e61
--- /dev/null
+++ b/c/src/lib/libcpu/bfin/serial/uart.c
@@ -0,0 +1,412 @@
+/* UART driver for Blackfin
+ *
+ * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA
+ * written by Allan Hessenflow <allanh@kallisti.com>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * $Id$
+ */
+
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/termiostypes.h>
+#include <termios.h>
+#include <stdlib.h>
+
+#include <libcpu/uartRegs.h>
+#include "uart.h"
+
+
+/* flags */
+#define BFIN_UART_XMIT_BUSY 0x01
+
+
+static bfin_uart_config_t *uartsConfig;
+
+
+static void initializeHardware(int minor) {
+ uint16_t divisor;
+ char *base;
+ uint16_t r;
+
+ base = uartsConfig->channels[minor].base_address;
+
+ *(uint16_t volatile *) (base + UART_IER_OFFSET) = 0;
+
+ if (uartsConfig->channels[minor].force_baud)
+ divisor = (uint16_t) (uartsConfig->freq /
+ (uartsConfig->channels[minor].force_baud * 16));
+ else
+ divisor = (uint16_t) (uartsConfig->freq / (9600 * 16));
+ *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_DLAB;
+ *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
+ *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
+
+ *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_WLS_8;
+
+ *(uint16_t volatile *) (base + UART_GCTL_OFFSET) = UART_GCTL_UCEN;
+
+ r = *(uint16_t volatile *) (base + UART_LSR_OFFSET);
+ r = *(uint16_t volatile *) (base + UART_RBR_OFFSET);
+ r = *(uint16_t volatile *) (base + UART_IIR_OFFSET);
+
+ return;
+}
+
+static int pollRead(int minor) {
+ int c;
+ char *base;
+
+ base = uartsConfig->channels[minor].base_address;
+
+ /* check to see if driver is using interrupts so this call will be
+ harmless (though non-functional) in case some debug code tries to
+ use it */
+ if (!uartsConfig->channels[minor].use_interrupts &&
+ *((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_DR)
+ c = *((uint16_t volatile *) (base + UART_RBR_OFFSET));
+ else
+ c = -1;
+
+ return c;
+}
+
+char bfin_uart_poll_read(int minor) {
+ int c;
+
+ do {
+ c = pollRead(minor);
+ } while (c == -1);
+
+ return c;
+}
+
+void bfin_uart_poll_write(int minor, char c) {
+ char *base;
+
+ base = uartsConfig->channels[minor].base_address;
+
+ while (!(*((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_THRE))
+ ;
+ *(uint16_t volatile *) (base + UART_THR_OFFSET) = c;
+}
+
+/* begin BISON */
+void debug_write_char(char c) {
+ bfin_uart_poll_write(0, c);
+}
+
+void debug_write_string(char *s) {
+
+ while (s && *s) {
+ if (*s == '\n')
+ debug_write_char('\r');
+ debug_write_char(*s++);
+ }
+}
+
+void debug_write_crlf(void) {
+
+ debug_write_char('\r');
+ debug_write_char('\n');
+}
+
+void debug_write_nybble(int nybble) {
+
+ nybble &= 0x0f;
+ debug_write_char((nybble > 9) ? 'a' + (nybble - 10) : '0' + nybble);
+}
+
+void debug_write_byte(int byte) {
+
+ byte &= 0xff;
+ debug_write_nybble(byte >> 4);
+ debug_write_nybble(byte & 0x0f);
+}
+
+void debug_write_half(int half) {
+
+ half &= 0xffff;
+ debug_write_byte(half >> 8);
+ debug_write_byte(half & 0xff);
+}
+
+void debug_write_word(int word) {
+
+ word &= 0xffffffff;
+ debug_write_half(word >> 16);
+ debug_write_half(word & 0xffff);
+}
+/* end BISON */
+
+/*
+ * Console Termios Support Entry Points
+ *
+ */
+
+static int pollWrite(int minor, const char *buf, int len) {
+
+ while (len-- > 0)
+ bfin_uart_poll_write(minor, *buf++);
+
+ return 0;
+}
+
+static void enableInterrupts(int minor) {
+ char *base;
+
+ base = uartsConfig->channels[minor].base_address;
+
+ *(uint16_t volatile *) (base + UART_IER_OFFSET) = UART_IER_ETBEI |
+ UART_IER_ERBFI;
+}
+
+static void disableAllInterrupts(void) {
+ int i;
+ char *base;
+
+ for (i = 0; i < uartsConfig->num_channels; i++) {
+ base = uartsConfig->channels[i].base_address;
+ *(uint16_t volatile *) (base + UART_IER_OFFSET) = 0;
+ }
+}
+
+static int interruptWrite(int minor, const char *buf, int len) {
+ char *base;
+
+ base = uartsConfig->channels[minor].base_address;
+
+ uartsConfig->channels[minor].flags |= BFIN_UART_XMIT_BUSY;
+ *(uint16_t volatile *) (base + UART_THR_OFFSET) = *buf;
+
+ return 0;
+}
+
+static int setAttributes(int minor, const struct termios *termios) {
+ char *base;
+ int baud;
+ uint16_t divisor;
+ uint16_t lcr;
+
+ base = uartsConfig->channels[minor].base_address;
+ switch (termios->c_cflag & CBAUD) {
+ case B0:
+ baud = 0;
+ break;
+ case B50:
+ baud = 50;
+ break;
+ case B75:
+ baud = 75;
+ break;
+ case B110:
+ baud = 110;
+ break;
+ case B134:
+ baud = 134;
+ break;
+ case B150:
+ baud = 150;
+ break;
+ case B200:
+ baud = 200;
+ break;
+ case B300:
+ baud = 300;
+ break;
+ case B600:
+ baud = 600;
+ break;
+ case B1200:
+ baud = 1200;
+ break;
+ case B1800:
+ baud = 1800;
+ break;
+ case B2400:
+ baud = 2400;
+ break;
+ case B4800:
+ baud = 4800;
+ break;
+ case B9600:
+ baud = 9600;
+ break;
+ case B19200:
+ baud = 19200;
+ break;
+ case B38400:
+ baud = 38400;
+ break;
+ case B57600:
+ baud = 57600;
+ break;
+ case B115200:
+ baud = 115200;
+ break;
+ case B230400:
+ baud = 230400;
+ break;
+ case B460800:
+ baud = 460800;
+ break;
+ default:
+ baud = -1;
+ break;
+ }
+ if (baud > 0 && uartsConfig->channels[minor].force_baud)
+ baud = uartsConfig->channels[minor].force_baud;
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ lcr = UART_LCR_WLS_5;
+ break;
+ case CS6:
+ lcr = UART_LCR_WLS_6;
+ break;
+ case CS7:
+ lcr = UART_LCR_WLS_7;
+ break;
+ case CS8:
+ default:
+ lcr = UART_LCR_WLS_8;
+ break;
+ }
+ switch (termios->c_cflag & (PARENB | PARODD)) {
+ case PARENB:
+ lcr |= UART_LCR_PEN | UART_LCR_EPS;
+ break;
+ case PARENB | PARODD:
+ lcr |= UART_LCR_PEN;
+ break;
+ default:
+ break;
+ }
+ if (termios->c_cflag & CSTOPB)
+ lcr |= UART_LCR_STB;
+
+ if (baud > 0) {
+ divisor = (uint16_t) (uartsConfig->freq / (baud * 16));
+ *(uint16_t volatile *) (base + UART_LCR_OFFSET) = lcr | UART_LCR_DLAB;
+ *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
+ *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
+ }
+ *(uint16_t volatile *) (base + UART_LCR_OFFSET) = lcr;
+
+ return 0;
+}
+
+void bfin_uart_isr(int source) {
+ int i;
+ char *base;
+ uint16_t uartStat;
+ char c;
+ uint8_t uartLSR;
+
+ /* Just use one ISR and check for all UART interrupt sources in it.
+ This is less efficient than making use of the vector to narrow down
+ the things we need to check, but not all Blackfins separate the
+ UART interrupt sources in the same ways. This way we don't have
+ to make this code dependent on the type of Blackfin. */
+ for (i = 0; i < uartsConfig->num_channels; i++) {
+ if (uartsConfig->channels[i].use_interrupts) {
+ base = uartsConfig->channels[i].base_address;
+ uartStat = *(uint16_t volatile *) (base + UART_IIR_OFFSET);
+ if ((uartStat & UART_IIR_NINT) == 0) {
+ switch (uartStat & UART_IIR_STATUS_MASK) {
+ case UART_IIR_STATUS_THRE:
+ if (uartsConfig->channels[i].termios &&
+ (uartsConfig->channels[i].flags & BFIN_UART_XMIT_BUSY)) {
+ uartsConfig->channels[i].flags &= ~BFIN_UART_XMIT_BUSY;
+ rtems_termios_dequeue_characters(uartsConfig->channels[i].termios,
+ 1);
+ }
+ break;
+ case UART_IIR_STATUS_RDR:
+ c = *(uint16_t volatile *) (base + UART_RBR_OFFSET);
+ if (uartsConfig->channels[i].termios)
+ rtems_termios_enqueue_raw_characters(
+ uartsConfig->channels[i].termios, &c, 1);
+ break;
+ case UART_IIR_STATUS_LS:
+ uartLSR = *(uint16_t volatile *) (base + UART_LSR_OFFSET);
+ /* break, framing error, parity error, or overrun error
+ has been detected */
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+rtems_status_code bfin_uart_initialize(rtems_device_major_number major,
+ bfin_uart_config_t *config) {
+ rtems_status_code status;
+ int i;
+
+ status = RTEMS_SUCCESSFUL;
+
+ rtems_termios_initialize();
+
+ /*
+ * Register Device Names
+ */
+
+ uartsConfig = config;
+ for (i = 0; i < config->num_channels; i++) {
+ config->channels[i].termios = NULL;
+ config->channels[i].flags = 0;
+ initializeHardware(i);
+ status = rtems_io_register_name(config->channels[i].name, major, i);
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_device_driver bfin_uart_open(rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg) {
+ rtems_status_code sc;
+ rtems_libio_open_close_args_t *args;
+ static const rtems_termios_callbacks pollCallbacks = {
+ NULL, /* firstOpen */
+ NULL, /* lastClose */
+ pollRead, /* pollRead */
+ pollWrite, /* write */
+ setAttributes, /* setAttributes */
+ NULL, /* stopRemoteTx */
+ NULL, /* startRemoteTx */
+ TERMIOS_POLLED /* outputUsesInterrupts */
+ };
+ static const rtems_termios_callbacks interruptCallbacks = {
+ NULL, /* firstOpen */
+ NULL, /* lastClose */
+ NULL, /* pollRead */
+ interruptWrite, /* write */
+ setAttributes, /* setAttributes */
+ NULL, /* stopRemoteTx */
+ NULL, /* startRemoteTx */
+ TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */
+ };
+
+ if (uartsConfig == NULL || minor < 0 || minor >= uartsConfig->num_channels)
+ return RTEMS_INVALID_NUMBER;
+
+ sc = rtems_termios_open(major, minor, arg,
+ uartsConfig->channels[minor].use_interrupts ?
+ &interruptCallbacks : &pollCallbacks);
+ args = arg;
+ uartsConfig->channels[minor].termios = args->iop->data1;
+
+ if (uartsConfig->channels[minor].use_interrupts)
+ enableInterrupts(minor);
+ atexit(disableAllInterrupts);
+
+ return sc;
+}
+