diff options
Diffstat (limited to 'c/src/lib/libcpu/m68k/mcf5206/console/mcfuart.c')
-rw-r--r-- | c/src/lib/libcpu/m68k/mcf5206/console/mcfuart.c | 570 |
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; +} |