summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/m68k/mcf5206/console/mcfuart.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libcpu/m68k/mcf5206/console/mcfuart.c')
-rw-r--r--c/src/lib/libcpu/m68k/mcf5206/console/mcfuart.c570
1 files changed, 570 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/m68k/mcf5206/console/mcfuart.c b/c/src/lib/libcpu/m68k/mcf5206/console/mcfuart.c
new file mode 100644
index 0000000000..5758b45afe
--- /dev/null
+++ b/c/src/lib/libcpu/m68k/mcf5206/console/mcfuart.c
@@ -0,0 +1,570 @@
+/*
+ * Generic UART Serial driver for Motorola Coldfire processors
+ *
+ * Copyright (C) 2000 OKTET Ltd., St.-Petersburg, Russian Fed.
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * COPYRIGHT (c) 1989-2000.
+ * On-Line Applications Research Corporation (OAR).
+ * Copyright assigned to U.S. Government, 1994.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ *
+ * http://www.OARcorp.com/rtems/license.html.
+ *
+ * $Id$
+ *
+ */
+
+#include <rtems.h>
+#include <termios.h>
+#include <rtems/libio.h>
+#include "mcf5206/mcfuart.h"
+
+/*
+ * int_driven_uart -- mapping between interrupt vector number and
+ * UART descriptor structures
+ */
+static struct {
+ mcfuart *uart;
+ int vec;
+} int_driven_uart[2];
+
+/* Forward function declarations */
+static rtems_isr
+mcfuart_interrupt_handler(rtems_vector_number vec);
+
+/*
+ * mcfuart_init --
+ * This function verifies the input parameters and perform initialization
+ * of the Motorola Coldfire on-chip UART descriptor structure.
+ *
+ * PARAMETERS:
+ * uart - pointer to the UART channel descriptor structure
+ * tty - pointer to termios structure
+ * int_driven - interrupt-driven (1) or polled (0) I/O mode
+ * chn - channel number (0/1)
+ * rx_buf - pointer to receive buffer
+ * rx_buf_len - receive buffer length
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if all parameters are valid, or error code
+ */
+rtems_status_code
+mcfuart_init(mcfuart *uart, void *tty, rtems_unsigned8 intvec,
+ rtems_unsigned32 chn)
+{
+ if (uart == NULL)
+ return RTEMS_INVALID_ADDRESS;
+
+ if ((chn <= 0) || (chn > MCF5206E_UART_CHANNELS))
+ return RTEMS_INVALID_NUMBER;
+
+ uart->chn = chn;
+ uart->intvec = intvec;
+ uart->tty = tty;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/* mcfuart_set_baudrate --
+ * Program the UART timer to specified baudrate
+ *
+ * PARAMETERS:
+ * uart - pointer to UART descriptor structure
+ * baud - termios baud rate (B50, B9600, etc...)
+ *
+ * RETURNS:
+ * none
+ */
+static void
+mcfuart_set_baudrate(mcfuart *uart, speed_t baud)
+{
+ rtems_unsigned32 div;
+ rtems_unsigned32 rate;
+ switch (baud)
+ {
+ case B50: rate = 50; break;
+ case B75: rate = 75; break;
+ case B110: rate = 110; break;
+ case B134: rate = 134; break;
+ case B150: rate = 150; break;
+ case B200: rate = 200; break;
+ case B300: rate = 300; break;
+ case B600: rate = 600; break;
+ case B1200: rate = 1200; break;
+ case B2400: rate = 2400; break;
+ case B4800: rate = 4800; break;
+ case B9600: rate = 9600; break;
+ case B19200: rate = 19200; break;
+ case B38400: rate = 38400; break;
+ case B57600: rate = 57600; break;
+#ifdef B115200
+ case B115200: rate = 115200; break;
+#endif
+#ifdef B230400
+ case B230400: rate = 230400; break;
+#endif
+ default: rate = 9600; break;
+ }
+
+ div = SYSTEM_CLOCK_FREQUENCY / (rate * 32);
+
+ *MCF5206E_UBG1(MBAR,uart->chn) = (rtems_unsigned8)((div >> 8) & 0xff);
+ *MCF5206E_UBG2(MBAR,uart->chn) = (rtems_unsigned8)(div & 0xff);
+}
+
+/*
+ * mcfuart_reset --
+ * This function perform the hardware initialization of Motorola
+ * Coldfire processor on-chip UART controller using parameters
+ * filled by the mcfuart_init function.
+ *
+ * PARAMETERS:
+ * uart - pointer to UART channel descriptor structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if channel is initialized successfully, error
+ * code in other case
+ *
+ * ALGORITHM:
+ * This function in general follows to algorith described in MCF5206e
+ * User's Manual, 12.5 UART Module Initialization Sequence
+ */
+rtems_status_code
+mcfuart_reset(mcfuart *uart)
+{
+ register rtems_unsigned32 chn;
+ rtems_status_code rc;
+
+ if (uart == NULL)
+ return RTEMS_INVALID_ADDRESS;
+
+ chn = uart->chn;
+
+ /* Reset the receiver and transmitter */
+ *MCF5206E_UCR(MBAR,chn) = MCF5206E_UCR_MISC_RESET_RX;
+ *MCF5206E_UCR(MBAR,chn) = MCF5206E_UCR_MISC_RESET_TX;
+
+ /*
+ * Program the vector number for a UART module interrupt, or
+ * disable UART interrupts if polled I/O. Enable the desired
+ * interrupt sources.
+ */
+ if (uart->intvec != 0)
+ {
+ int_driven_uart[chn - 1].uart = uart;
+ int_driven_uart[chn - 1].vec = uart->intvec;
+ rc = rtems_interrupt_catch(mcfuart_interrupt_handler, uart->intvec,
+ &uart->old_handler);
+ if (rc != RTEMS_SUCCESSFUL)
+ return rc;
+ *MCF5206E_UIVR(MBAR,chn) = uart->intvec;
+ *MCF5206E_UIMR(MBAR,chn) = MCF5206E_UIMR_FFULL;
+ *MCF5206E_UACR(MBAR,chn) = MCF5206E_UACR_IEC;
+ *MCF5206E_IMR(MBAR) &= ~MCF5206E_INTR_BIT(uart->chn == 1 ?
+ MCF5206E_INTR_UART_1 :
+ MCF5206E_INTR_UART_2);
+ }
+ else
+ {
+ *MCF5206E_UIMR(MBAR,chn) = 0;
+ }
+
+ /* Select the receiver and transmitter clock. */
+ mcfuart_set_baudrate(uart, B19200); /* dBUG defaults (unfortunately,
+ it is differ to termios default */
+ *MCF5206E_UCSR(MBAR,chn) =
+ MCF5206E_UCSR_RCS_TIMER | MCF5206E_UCSR_TCS_TIMER;
+
+ /* Mode Registers 1,2 - set termios defaults (8N1) */
+ *MCF5206E_UCR(MBAR,chn) = MCF5206E_UCR_MISC_RESET_MR;
+ *MCF5206E_UMR(MBAR,chn) =
+/* MCF5206E_UMR1_RXRTS | */
+ MCF5206E_UMR1_PM_NO_PARITY |
+ MCF5206E_UMR1_BC_8;
+ *MCF5206E_UMR(MBAR,chn) =
+ MCF5206E_UMR2_CM_NORMAL |
+/* MCF5206E_UMR2_TXCTS | */
+ MCF5206E_UMR2_SB_1;
+
+ /* Enable Receiver and Transmitter */
+ *MCF5206E_UCR(MBAR,chn) = MCF5206E_UCR_MISC_RESET_ERR;
+ *MCF5206E_UCR(MBAR,chn) = MCF5206E_UCR_TC_ENABLE;
+ *MCF5206E_UCR(MBAR,chn) = MCF5206E_UCR_RC_ENABLE;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * mcfuart_disable --
+ * This function disable the operations on Motorola Coldfire UART
+ * controller
+ *
+ * PARAMETERS:
+ * uart - pointer to UART channel descriptor structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if UART closed successfuly, or error code in
+ * other case
+ */
+rtems_status_code
+mcfuart_disable(mcfuart *uart)
+{
+ rtems_status_code rc;
+ *MCF5206E_UCR(MBAR,uart->chn) =
+ MCF5206E_UCR_TC_DISABLE |
+ MCF5206E_UCR_RC_DISABLE;
+ if (uart->intvec != 0)
+ {
+ *MCF5206E_IMR(MBAR) |= MCF5206E_INTR_BIT(uart->chn == 1 ?
+ MCF5206E_INTR_UART_1 :
+ MCF5206E_INTR_UART_2);
+ rc = rtems_interrupt_catch(uart->old_handler, uart->intvec, NULL);
+ int_driven_uart[uart->chn - 1].uart = NULL;
+ int_driven_uart[uart->chn - 1].vec = 0;
+ if (rc != RTEMS_SUCCESSFUL)
+ return rc;
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * mcfuart_set_attributes --
+ * This function parse the termios attributes structure and perform
+ * the appropriate settings in hardware.
+ *
+ * PARAMETERS:
+ * uart - pointer to the UART descriptor structure
+ * t - pointer to termios parameters
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL
+ */
+int
+mcfuart_set_attributes(mcfuart *uart, const struct termios *t)
+{
+ int level;
+ speed_t baud;
+ rtems_unsigned8 umr1, umr2;
+
+ baud = cfgetospeed(t);
+ umr1 = 0;
+ umr2 = MCF5206E_UMR2_CM_NORMAL;
+
+ /* Set flow control */
+ if ((t->c_cflag & CRTSCTS) != 0)
+ {
+ umr1 |= MCF5206E_UMR1_RXRTS;
+ umr2 |= MCF5206E_UMR2_TXCTS;
+ }
+
+ /* Set character size */
+ switch (t->c_cflag & CSIZE)
+ {
+ case CS5: umr1 |= MCF5206E_UMR1_BC_5; break;
+ case CS6: umr1 |= MCF5206E_UMR1_BC_6; break;
+ case CS7: umr1 |= MCF5206E_UMR1_BC_7; break;
+ case CS8: umr1 |= MCF5206E_UMR1_BC_8; break;
+ }
+
+ /* Set number of stop bits */
+ if ((t->c_cflag & CSTOPB) != 0)
+ {
+ if ((t->c_cflag & CSIZE) == CS5)
+ {
+ umr2 |= MCF5206E_UMR2_SB5_2;
+ }
+ else
+ {
+ umr2 |= MCF5206E_UMR2_SB_2;
+ }
+ }
+ else
+ {
+ if ((t->c_cflag & CSIZE) == CS5)
+ {
+ umr2 |= MCF5206E_UMR2_SB5_1;
+ }
+ else
+ {
+ umr2 |= MCF5206E_UMR2_SB_1;
+ }
+ }
+
+ /* Set parity mode */
+ if ((t->c_cflag & PARENB) != 0)
+ {
+ if ((t->c_cflag & PARODD) != 0)
+ {
+ umr1 |= MCF5206E_UMR1_PM_ODD;
+ }
+ else
+ {
+ umr1 |= MCF5206E_UMR1_PM_EVEN;
+ }
+ }
+ else
+ {
+ umr1 |= MCF5206E_UMR1_PM_NO_PARITY;
+ }
+
+ rtems_interrupt_disable(level);
+ *MCF5206E_UCR(MBAR,uart->chn) =
+ MCF5206E_UCR_TC_DISABLE | MCF5206E_UCR_RC_DISABLE;
+ mcfuart_set_baudrate(uart, baud);
+ *MCF5206E_UCR(MBAR,uart->chn) = MCF5206E_UCR_MISC_RESET_MR;
+ *MCF5206E_UMR(MBAR,uart->chn) = umr1;
+ *MCF5206E_UMR(MBAR,uart->chn) = umr2;
+ if ((t->c_cflag & CREAD) != 0)
+ {
+ *MCF5206E_UCR(MBAR,uart->chn) =
+ MCF5206E_UCR_TC_ENABLE | MCF5206E_UCR_RC_ENABLE;
+ }
+ else
+ {
+ *MCF5206E_UCR(MBAR,uart->chn) = MCF5206E_UCR_TC_ENABLE;
+ }
+ rtems_interrupt_enable(level);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * mcfuart_poll_read --
+ * This function tried to read character from MCF UART and perform
+ * error handling. When parity or framing error occured, return
+ * value dependent on termios input mode flags:
+ * - received character, if IGNPAR == 1
+ * - 0, if IGNPAR == 0 and PARMRK == 0
+ * - 0xff and 0x00 on next poll_read invocation, if IGNPAR == 0 and
+ * PARMRK == 1
+ *
+ * PARAMETERS:
+ * uart - pointer to UART descriptor structure
+ *
+ * RETURNS:
+ * code of received character or -1 if no characters received.
+ */
+int
+mcfuart_poll_read(mcfuart *uart)
+{
+ rtems_unsigned8 usr;
+ int ch;
+ if (uart->parerr_mark_flag == 1)
+ {
+ uart->parerr_mark_flag = 0;
+ return 0;
+ }
+ usr = *MCF5206E_USR(MBAR,uart->chn);
+ if ((usr & MCF5206E_USR_RXRDY) != 0)
+ {
+ if (((usr & (MCF5206E_USR_FE | MCF5206E_USR_PE)) != 0) &&
+ !(uart->c_iflag & IGNPAR))
+ {
+ ch = *MCF5206E_URB(MBAR,uart->chn); /* Clear error bits */
+ if (uart->c_iflag & PARMRK)
+ {
+ uart->parerr_mark_flag = 1;
+ ch = 0xff;
+ }
+ else
+ {
+ ch = 0;
+ }
+ }
+ else
+ {
+ ch = *MCF5206E_URB(MBAR,uart->chn);
+ }
+ }
+ else
+ ch = -1;
+ return ch;
+}
+
+/*
+ * mcfuart_poll_write --
+ * This function transmit buffer byte-by-byte in polling mode.
+ *
+ * PARAMETERS:
+ * uart - pointer to the UART descriptor structure
+ * buf - pointer to transmit buffer
+ * len - transmit buffer length
+ *
+ * RETURNS:
+ * 0
+ */
+int
+mcfuart_poll_write(mcfuart *uart, const char *buf, int len)
+{
+ while (len--)
+ {
+ while ((*MCF5206E_USR(MBAR, uart->chn) & MCF5206E_USR_TXRDY) == 0);
+ *MCF5206E_UTB(MBAR, uart->chn) = *buf++;
+ }
+ return 0;
+}
+
+/* mcfuart_interrupt_handler --
+ * UART interrupt handler routine
+ *
+ * PARAMETERS:
+ * vec - interrupt vector number
+ *
+ * RETURNS:
+ * none
+ */
+static rtems_isr
+mcfuart_interrupt_handler(rtems_vector_number vec)
+{
+ mcfuart *uart;
+ register rtems_unsigned8 usr;
+ register rtems_unsigned8 uisr;
+ register int chn;
+ register int bp = 0;
+
+ /* Find UART descriptor from vector number */
+ if (int_driven_uart[0].vec == vec)
+ uart = int_driven_uart[0].uart;
+ else if (int_driven_uart[1].vec == vec)
+ uart = int_driven_uart[1].uart;
+ else
+ return;
+
+ chn = uart->chn;
+
+ uisr = *MCF5206E_UISR(MBAR, chn);
+ if (uisr & MCF5206E_UISR_DB)
+ {
+ *MCF5206E_UCR(MBAR, chn) = MCF5206E_UCR_MISC_RESET_BRK;
+ }
+
+ /* Receiving */
+ while (1)
+ {
+ char buf[32];
+ usr = *MCF5206E_USR(MBAR,chn);
+ if ((bp < sizeof(buf) - 1) && ((usr & MCF5206E_USR_RXRDY) != 0))
+ {
+ /* Receive character and handle frame/parity errors */
+ if (((usr & (MCF5206E_USR_FE | MCF5206E_USR_PE)) != 0) &&
+ !(uart->c_iflag & IGNPAR))
+ {
+ if (uart->c_iflag & PARMRK)
+ {
+ buf[bp++] = 0xff;
+ buf[bp++] = 0x00;
+ }
+ else
+ {
+ buf[bp++] = 0x00;
+ }
+ }
+ else
+ {
+ buf[bp++] = *MCF5206E_URB(MBAR, chn);
+ }
+
+ /* Reset error condition if any errors has been detected */
+ if (usr & (MCF5206E_USR_RB | MCF5206E_USR_FE |
+ MCF5206E_USR_PE | MCF5206E_USR_OE))
+ {
+ *MCF5206E_UCR(MBAR, chn) = MCF5206E_UCR_MISC_RESET_ERR;
+ }
+ }
+ else
+ {
+ if (bp != 0)
+ rtems_termios_enqueue_raw_characters(uart->tty, buf, bp);
+ break;
+ }
+ }
+
+ /* Transmitting */
+ while (1)
+ {
+ if ((*MCF5206E_USR(MBAR, chn) & MCF5206E_USR_TXRDY) == 0)
+ break;
+ if (uart->tx_buf != NULL)
+ {
+ if (uart->tx_ptr >= uart->tx_buf_len)
+ {
+ register int dequeue = uart->tx_buf_len;
+ *MCF5206E_UIMR(MBAR, uart->chn) = MCF5206E_UIMR_FFULL;
+ uart->tx_buf = NULL;
+ uart->tx_ptr = uart->tx_buf_len = 0;
+ rtems_termios_dequeue_characters(uart->tty, dequeue);
+ }
+ else
+ {
+ *MCF5206E_UTB(MBAR, chn) = uart->tx_buf[uart->tx_ptr++];
+ }
+ }
+ else
+ break;
+ }
+}
+
+/* mcfuart_interrupt_write --
+ * This function initiate transmitting of the buffer in interrupt mode.
+ *
+ * PARAMETERS:
+ * uart - pointer to the UART descriptor structure
+ * buf - pointer to transmit buffer
+ * len - transmit buffer length
+ *
+ * RETURNS:
+ * 0
+ */
+int
+mcfuart_interrupt_write(mcfuart *uart, const char *buf, int len)
+{
+ int level;
+ rtems_interrupt_disable(level);
+ uart->tx_buf = buf;
+ uart->tx_buf_len = len;
+ uart->tx_ptr = 0;
+ *MCF5206E_UIMR(MBAR, uart->chn) =
+ MCF5206E_UIMR_FFULL | MCF5206E_UIMR_TXRDY;
+ while (((*MCF5206E_USR(MBAR,uart->chn) & MCF5206E_USR_TXRDY) != 0) &&
+ (uart->tx_ptr < uart->tx_buf_len))
+ {
+ *MCF5206E_UTB(MBAR,uart->chn) = uart->tx_buf[uart->tx_ptr++];
+ }
+ rtems_interrupt_enable(level);
+ return 0;
+}
+
+/* mcfuart_stop_remote_tx --
+ * This function stop data flow from remote device.
+ *
+ * PARAMETERS:
+ * uart - pointer to the UART descriptor structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL
+ */
+int
+mcfuart_stop_remote_tx(mcfuart *uart)
+{
+ *MCF5206E_UOP0(MBAR, uart->chn) = 1;
+ return RTEMS_SUCCESSFUL;
+}
+
+/* mcfuart_start_remote_tx --
+ * This function resume data flow from remote device.
+ *
+ * PARAMETERS:
+ * uart - pointer to the UART descriptor structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL
+ */
+int
+mcfuart_start_remote_tx(mcfuart *uart)
+{
+ *MCF5206E_UOP1(MBAR, uart->chn) = 1;
+ return RTEMS_SUCCESSFUL;
+}