/*
* 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).
*
* 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 <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, uint8_t intvec,
uint32_t 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)
{
uint32_t div;
uint32_t 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) = (uint8_t)((div >> 8) & 0xff);
*MCF5206E_UBG2(MBAR,uart->chn) = (uint8_t)(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 uint32_t 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;
uint8_t 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)
{
uint8_t usr;
int ch;
if (uart->parerr_mark_flag == true)
{
uart->parerr_mark_flag = false;
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 = true;
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
*/
ssize_t
mcfuart_poll_write(mcfuart *uart, const char *buf, size_t len)
{
size_t retval = len;
while (len--)
{
while ((*MCF5206E_USR(MBAR, uart->chn) & MCF5206E_USR_TXRDY) == 0);
*MCF5206E_UTB(MBAR, uart->chn) = *buf++;
}
return retval;
}
/* 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 uint8_t usr;
register uint8_t 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
*/
ssize_t
mcfuart_interrupt_write(mcfuart *uart, const char *buf, size_t 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;
}