diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-19 06:28:01 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-20 13:08:32 +0200 |
commit | d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc (patch) | |
tree | caa54b4229e86a68c84ab5961af34e087dce5302 /bsps/m68k | |
parent | bsps/powerpc: Move shared btimer support (diff) | |
download | rtems-d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc.tar.bz2 |
bsps: Move console drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/m68k')
21 files changed, 11774 insertions, 0 deletions
diff --git a/bsps/m68k/av5282/console/console.c b/bsps/m68k/av5282/console/console.c new file mode 100644 index 0000000000..dd557660f8 --- /dev/null +++ b/bsps/m68k/av5282/console/console.c @@ -0,0 +1,711 @@ +/* + * Multi UART console serial I/O. + * + * TO DO: Add DMA input/output + */ + +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> +#include <bsp.h> +#include <malloc.h> + +#include <rtems/bspIo.h> +#include <rtems/console.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> + +#define UART_INTC0_IRQ_VECTOR(x) (64+13+(x)) + +#define MCF5282_UART_USR_ERROR ( MCF5282_UART_USR_RB | \ + MCF5282_UART_USR_FE | \ + MCF5282_UART_USR_PE | \ + MCF5282_UART_USR_OE ) + +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len); +static ssize_t IntUartInterruptWrite (int minor, const char *buf, size_t len); + +static void _BSP_null_char( char c ) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + while ( (MCF5282_UART_USR(CONSOLE_PORT) & MCF5282_UART_USR_TXRDY) == 0 ) + continue; + + MCF5282_UART_UTB(CONSOLE_PORT) = c; + while ( (MCF5282_UART_USR(CONSOLE_PORT) & MCF5282_UART_USR_TXRDY) == 0 ) + continue; + + rtems_interrupt_enable(level); +} +BSP_output_char_function_type BSP_output_char = _BSP_null_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +#define MAX_UART_INFO 3 +#define RX_BUFFER_SIZE 512 + +struct IntUartInfoStruct +{ + int iomode; + volatile int uimr; + int baud; + int databits; + int parity; + int stopbits; + int hwflow; + int rx_in; + int rx_out; + char rx_buffer[RX_BUFFER_SIZE]; + void *ttyp; +}; + +struct IntUartInfoStruct IntUartInfo[MAX_UART_INFO]; + +/* + * Function : IntUartSet + * + * Description : This updates the hardware UART settings. + */ +static void IntUartSet( + int minor, + int baud, + int databits, + int parity, + int stopbits, + int hwflow +) +{ + int divisor; + uint32_t clock_speed; + uint8_t umr1 = 0; + uint8_t umr2 = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + + /* disable interrupts, clear RTS line, and disable the UARTS */ + MCF5282_UART_UIMR(minor) = 0; + MCF5282_UART_UOP0(minor) = 1; + MCF5282_UART_UCR(minor) = + (MCF5282_UART_UCR_TX_DISABLED | MCF5282_UART_UCR_RX_DISABLED); + + /* save the current values */ + info->uimr = 0; + info->baud = baud; + info->databits = databits; + info->parity = parity; + info->stopbits = stopbits; + info->hwflow = hwflow; + + clock_speed = get_CPU_clock_speed(); + /* determine the baud divisor value */ + divisor = (clock_speed / ( 32 * baud )); + if ( divisor < 2 ) + divisor = 2; + + /* check to see if doing hardware flow control */ + if ( hwflow ) { + /* set hardware flow options */ + umr1 |= MCF5282_UART_UMR1_RXRTS; + umr2 |= MCF5282_UART_UMR2_TXCTS; + } + + /* determine the new umr values */ + umr1 |= (parity | databits); + umr2 |= (stopbits); + + /* reset the uart */ + MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_ERROR; + MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_RX; + MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_TX; + + /* reset the uart mode register and update values */ + MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_MR; + MCF5282_UART_UMR(minor) = umr1; + MCF5282_UART_UMR(minor) = umr2; + + /* set the baud rate values */ + MCF5282_UART_UCSR(minor) = + (MCF5282_UART_UCSR_RCS_SYS_CLK | MCF5282_UART_UCSR_TCS_SYS_CLK); + MCF5282_UART_UBG1(minor) = (divisor & 0xff00) >> 8; + MCF5282_UART_UBG2(minor) = (divisor & 0x00ff); + + /* enable the uart */ + MCF5282_UART_UCR(minor) = + (MCF5282_UART_UCR_TX_ENABLED | MCF5282_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if ( info->iomode != TERMIOS_POLLED ) { + /* enable rx interrupts */ + info->uimr |= MCF5282_UART_UIMR_FFULL; + MCF5282_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if ( hwflow ) { + /* assert the RTS line */ + MCF5282_UART_UOP1(minor) = 1; + } + + rtems_interrupt_enable(level); +} + +/* + * Function : IntUartSetAttributes + * + * Description : This provides the hardware-dependent portion of tcsetattr(). + * value and sets it. At the moment this just sets the baud rate. + * + * Note: The highest baudrate is 115200 as this stays within + * an error of +/- 5% at 25MHz processor clock + */ +static int IntUartSetAttributes( + int minor, + const struct termios *t +) +{ + /* set default index values */ + int baud = (int)19200; + int databits = (int)MCF5282_UART_UMR1_BC_8; + int parity = (int)MCF5282_UART_UMR1_PM_NONE; + int stopbits = (int)MCF5282_UART_UMR2_STOP_BITS_1; + int hwflow = (int)0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* check to see if input is valid */ + if ( t != (const struct termios *)0 ) { + /* determine baud rate index */ + baud = rtems_termios_baud_to_number(t->c_ospeed); + + /* determine data bits */ + switch ( t->c_cflag & CSIZE ) { + case CS5: + databits = (int)MCF5282_UART_UMR1_BC_5; + break; + case CS6: + databits = (int)MCF5282_UART_UMR1_BC_6; + break; + case CS7: + databits = (int)MCF5282_UART_UMR1_BC_7; + break; + case CS8: + databits = (int)MCF5282_UART_UMR1_BC_8; + break; + } + + /* determine if parity is enabled */ + if ( t->c_cflag & PARENB ) { + if ( t->c_cflag & PARODD ) { + /* odd parity */ + parity = (int)MCF5282_UART_UMR1_PM_ODD; + } else { + /* even parity */ + parity = (int)MCF5282_UART_UMR1_PM_EVEN; + } + } + + /* determine stop bits */ + if ( t->c_cflag & CSTOPB ) { + /* two stop bits */ + stopbits = (int)MCF5282_UART_UMR2_STOP_BITS_2; + } + + /* check to see if hardware flow control */ + if ( t->c_cflag & CRTSCTS ) { + hwflow = 1; + } + } + + /* check to see if values have changed */ + if ( ( baud != info->baud ) || + ( databits != info->databits ) || + ( parity != info->parity ) || + ( stopbits != info->stopbits ) || + ( hwflow != info->hwflow ) ) { + + /* call function to set values */ + IntUartSet(minor, baud, databits, parity, stopbits, hwflow); + } + + return RTEMS_SUCCESSFUL; +} + +/* + * Function : IntUartInterruptHandler + * + * Description : This is the interrupt handler for the internal uart. It + * determines which channel caused the interrupt before queueing any received + * chars and dequeueing chars waiting for transmission. + */ +static rtems_isr IntUartInterruptHandler(rtems_vector_number v) +{ + unsigned int chan = v - UART_INTC0_IRQ_VECTOR(0); + struct IntUartInfoStruct *info = &IntUartInfo[chan]; + + /* check to see if received data */ + if ( MCF5282_UART_UISR(chan) & MCF5282_UART_UISR_RXRDY ) { + /* read data and put into the receive buffer */ + while ( MCF5282_UART_USR(chan) & MCF5282_UART_USR_RXRDY ) { + + if ( MCF5282_UART_USR(chan) & MCF5282_UART_USR_ERROR ) { + /* clear the error */ + MCF5282_UART_UCR(chan) = MCF5282_UART_UCR_RESET_ERROR; + } + /* put data in rx buffer and check for errors */ + info->rx_buffer[info->rx_in] = MCF5282_UART_URB(chan); + + /* update buffer values */ + info->rx_in++; + + if ( info->rx_in >= RX_BUFFER_SIZE ) { + info->rx_in = 0; + } + } + + /* Make sure the port has been opened */ + if ( info->ttyp ) { + + /* check to see if task driven */ + if ( info->iomode == TERMIOS_TASK_DRIVEN ) { + /* notify rx task that rx buffer has data */ + rtems_termios_rxirq_occured(info->ttyp); + } else { + /* Push up the received data */ + rtems_termios_enqueue_raw_characters( + info->ttyp, info->rx_buffer, info->rx_in); + info->rx_in = 0; + } + } + } + + /* check to see if data needs to be transmitted */ + if ( ( info->uimr & MCF5282_UART_UIMR_TXRDY ) && + ( MCF5282_UART_UISR(chan) & MCF5282_UART_UISR_TXRDY ) ) + { + + /* disable tx interrupts */ + info->uimr &= ~MCF5282_UART_UIMR_TXRDY; + MCF5282_UART_UIMR(chan) = info->uimr; + + /* tell upper level that character has been sent */ + if ( info->ttyp ) + rtems_termios_dequeue_characters(info->ttyp, 1); + } +} + +/* + * Function : IntUartInitialize + * + * Description : This initialises the internal uart hardware for all + * internal uarts. If the internal uart is to be interrupt driven then the + * interrupt vectors are hooked. + */ +static void IntUartInitialize(void) +{ + unsigned int chan; + struct IntUartInfoStruct *info; + rtems_isr_entry old_handler; + rtems_interrupt_level level; + + for ( chan = 0; chan < MAX_UART_INFO; chan++ ) { + info = &IntUartInfo[chan]; + + info->ttyp = NULL; + info->rx_in = 0; + info->rx_out = 0; + info->baud = -1; + info->databits = -1; + info->parity = -1; + info->stopbits = -1; + info->hwflow = -1; + info->iomode = TERMIOS_POLLED; + + MCF5282_UART_UACR(chan) = 0; + MCF5282_UART_UIMR(chan) = 0; + if ( info->iomode != TERMIOS_POLLED ) { + rtems_interrupt_catch (IntUartInterruptHandler, + UART_INTC0_IRQ_VECTOR(chan), + &old_handler); + } + + /* set uart default values */ + IntUartSetAttributes(chan, NULL); + + /* unmask interrupt */ + rtems_interrupt_disable(level); + switch(chan) { + case 0: + MCF5282_INTC0_ICR13 = MCF5282_INTC_ICR_IL(UART0_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(UART0_IRQ_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT13 | + MCF5282_INTC_IMRL_MASKALL); + break; + + case 1: + MCF5282_INTC0_ICR14 = MCF5282_INTC_ICR_IL(UART1_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(UART1_IRQ_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT14 | + MCF5282_INTC_IMRL_MASKALL); + break; + + case 2: + MCF5282_INTC0_ICR15 = MCF5282_INTC_ICR_IL(UART2_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(UART2_IRQ_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT15 | + MCF5282_INTC_IMRL_MASKALL); + break; + } + rtems_interrupt_enable(level); + + } /* of chan loop */ + + +} /* IntUartInitialise */ + + +/* + * Function : IntUartInterruptWrite + * + * Description : This writes a single character to the appropriate uart + * channel. This is either called during an interrupt or in the user's task + * to initiate a transmit sequence. Calling this routine enables Tx + * interrupts. + */ +static ssize_t IntUartInterruptWrite( + int minor, + const char *buf, + size_t len +) +{ + if (len > 0) { + /* write out character */ + MCF5282_UART_UTB(minor) = *buf; + + /* enable tx interrupt */ + IntUartInfo[minor].uimr |= MCF5282_UART_UIMR_TXRDY; + MCF5282_UART_UIMR(minor) = IntUartInfo[minor].uimr; + } + + return 0; +} + +/* + * Function : IntUartInterruptOpen + * + * Description : This enables interrupts when the tty is opened. + */ +static int IntUartInterruptOpen( + int major, + int minor, + void *arg +) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* enable the uart */ + MCF5282_UART_UCR(minor) = (MCF5282_UART_UCR_TX_ENABLED | + MCF5282_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if ( info->iomode != TERMIOS_POLLED ) { + /* enable rx interrupts */ + info->uimr |= MCF5282_UART_UIMR_FFULL; + MCF5282_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if ( info->hwflow ) { + /* assert the RTS line */ + MCF5282_UART_UOP1(minor) = 1; + } + + return 0; +} + +/* + * Function : IntUartInterruptClose + * + * Description : This disables interrupts when the tty is closed. + */ +static int IntUartInterruptClose( + int major, + int minor, + void *arg +) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* disable the interrupts and the uart */ + MCF5282_UART_UIMR(minor) = 0; + MCF5282_UART_UCR(minor) = + (MCF5282_UART_UCR_TX_DISABLED | MCF5282_UART_UCR_RX_DISABLED); + + /* reset values */ + info->ttyp = NULL; + info->uimr = 0; + info->rx_in = 0; + info->rx_out = 0; + + return 0; +} + +/* + * Function : IntUartTaskRead + * + * Description : This reads all available characters from the internal uart + * and places them into the termios buffer. The rx interrupts will be + * re-enabled after all data has been read. + */ +static int IntUartTaskRead(int minor) +{ + char buffer[RX_BUFFER_SIZE]; + int count; + int rx_in; + int index = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* determine number of values to copy out */ + rx_in = info->rx_in; + if ( info->rx_out <= rx_in ) { + count = rx_in - info->rx_out; + } else { + count = (RX_BUFFER_SIZE - info->rx_out) + rx_in; + } + + /* copy data into local buffer from rx buffer */ + while ( ( index < count ) && ( index < RX_BUFFER_SIZE ) ) { + /* copy data byte */ + buffer[index] = info->rx_buffer[info->rx_out]; + index++; + + /* increment rx buffer values */ + info->rx_out++; + if ( info->rx_out >= RX_BUFFER_SIZE ) { + info->rx_out = 0; + } + } + + /* check to see if buffer is not empty */ + if ( count > 0 ) { + /* set characters into termios buffer */ + rtems_termios_enqueue_raw_characters(info->ttyp, buffer, count); + } + + return EOF; +} + + + +/* + * Function : IntUartPollRead + * + * Description : This reads a character from the internal uart. It returns + * to the caller without blocking if not character is waiting. + */ +static int IntUartPollRead(int minor) +{ + if ( (MCF5282_UART_USR(minor) & MCF5282_UART_USR_RXRDY) == 0 ) + return-1; + + return MCF5282_UART_URB(minor); +} + + +/* + * Function : IntUartPollWrite + * + * Description : This writes out each character in the buffer to the + * appropriate internal uart channel waiting till each one is sucessfully + * transmitted. + */ +static ssize_t IntUartPollWrite( + int minor, + const char *buf, + size_t len +) +{ + size_t retval = len; + /* loop over buffer */ + + while ( len-- ) { + /* block until we can transmit */ + while ( (MCF5282_UART_USR(minor) & MCF5282_UART_USR_TXRDY) == 0 ) + continue; + /* transmit data byte */ + MCF5282_UART_UTB(minor) = *buf++; + } + return retval; +} + +/* + * Function : console_initialize + * + * Description : This initialises termios, both sets of uart hardware before + * registering /dev/tty devices for each channel and the system /dev/console. + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + + /* Set up TERMIOS */ + rtems_termios_initialize (); + + /* set io modes for the different channels and initialize device */ + IntUartInfo[minor].iomode = TERMIOS_IRQ_DRIVEN; + IntUartInitialize(); + + /* Register the console port */ + status = rtems_io_register_name ("/dev/console", major, CONSOLE_PORT); + if ( status != RTEMS_SUCCESSFUL ) { + rtems_fatal_error_occurred (status); + } + + /* Register the other port */ + if ( CONSOLE_PORT != 0 ) { + status = rtems_io_register_name ("/dev/tty00", major, 0); + if ( status != RTEMS_SUCCESSFUL ) { + rtems_fatal_error_occurred (status); + } + } + if ( CONSOLE_PORT != 1 ) { + status = rtems_io_register_name ("/dev/tty01", major, 1); + if ( status != RTEMS_SUCCESSFUL ) { + rtems_fatal_error_occurred (status); + } + } + + return RTEMS_SUCCESSFUL; +} + +/* + * Function : console_open + * + * Description : This actually opens the device depending on the minor + * number set during initialisation. The device specific access routines are + * passed to termios when the devices is opened depending on whether it is + * polled or not. + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_status_code status = RTEMS_INVALID_NUMBER; + rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *)arg; + struct IntUartInfoStruct *info; + + static const rtems_termios_callbacks IntUartPollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + IntUartPollRead, /* pollRead */ + IntUartPollWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* mode */ + }; + static const rtems_termios_callbacks IntUartIntrCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + NULL, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* mode */ + }; + + static const rtems_termios_callbacks IntUartTaskCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + IntUartTaskRead, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_TASK_DRIVEN /* mode */ + }; + + /* open the port depending on the minor device number */ + if ( ( minor >= 0 ) && ( minor < MAX_UART_INFO ) ) { + info = &IntUartInfo[minor]; + switch ( info->iomode ) { + case TERMIOS_POLLED: + status = rtems_termios_open(major, minor, arg, &IntUartPollCallbacks); + break; + case TERMIOS_IRQ_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartIntrCallbacks); + info->ttyp = args->iop->data1; + break; + case TERMIOS_TASK_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartTaskCallbacks); + info->ttyp = args->iop->data1; + break; + } + } + + return status; +} + +/* + * Function : console_close + * + * Description : This closes the device via termios + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_close(arg); +} + +/* + * Function : console_read + * + * Description : Read from the device via termios + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_read(arg); +} + +/* + * Function : console_write + * + * Description : Write to the device via termios + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_write(arg); +} + +/* + * Function : console_ioctl + * + * Description : Pass the IOCtl call to termios + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_ioctl(arg); +} diff --git a/bsps/m68k/csb360/console/console-io.c b/bsps/m68k/csb360/console/console-io.c new file mode 100644 index 0000000000..9b0aeac5bb --- /dev/null +++ b/bsps/m68k/csb360/console/console-io.c @@ -0,0 +1,97 @@ +/* + * This file contains the hardware specific portions of the TTY driver + * for the serial ports on the mcf5272 + */ + +/* + * 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.org/license/LICENSE. + */ + +#include <bsp.h> +#include <bsp/console-polled.h> +#include <rtems/libio.h> +#include <mcf5272/mcf5272.h> + +volatile int g_cnt = 0; + +/* + * console_initialize_hardware + * + * This routine initializes the console hardware. + * + */ + +void console_initialize_hardware(void) +{ +} + + +/* + * console_outbyte_polled + * + * This routine transmits a character using polling. + */ + +void console_outbyte_polled( + int port, + char ch +) +{ + uart_regs_t *uart; + int i; + if (port == 0) { + uart = g_uart0_regs; + } else { + uart = g_uart1_regs; + } + + /* wait for the fifo to make room */ +/* while ((uart->usr & MCF5272_USR_TXRDY) == 0) { */ + while ((uart->ucsr & MCF5272_USR_TXRDY) == 0) { + continue; + } + + uart->udata = ch; + for (i = 0; i < 1000; i++) g_cnt++; +} + +/* + * console_inbyte_nonblocking + * + * This routine polls for a character. + */ + +int console_inbyte_nonblocking( + int port +) +{ + uart_regs_t *uart; + unsigned char c; + + if (port == 0) { + uart = g_uart0_regs; + } else { + uart = g_uart1_regs; + } + +/* if (uart->usr & MCF5272_USR_RXRDY) { */ + if (uart->ucsr & MCF5272_USR_RXRDY) { + c = (char)uart->udata; + return c; + } else { + return -1; + } +} + +#include <rtems/bspIo.h> + +static void mcf5272_output_char(char c) { console_outbyte_polled( 0, c ); } + +BSP_output_char_function_type BSP_output_char = mcf5272_output_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + diff --git a/bsps/m68k/gen68340/console/console.c b/bsps/m68k/gen68340/console/console.c new file mode 100644 index 0000000000..d6634b1079 --- /dev/null +++ b/bsps/m68k/gen68340/console/console.c @@ -0,0 +1,690 @@ +/* + * 68340/68349 console serial I/O. + */ + +/* + * Author: + * Geoffroy Montel + * France Telecom - CNET/DSM/TAM/CAT + * 4, rue du Clos Courtel + * 35512 CESSON-SEVIGNE + * FRANCE + * + * e-mail: g_montel@yahoo.com + * + * COPYRIGHT (c) 1989-1999. + * 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.org/license/LICENSE. + */ + +#include <termios.h> + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include <bsp.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <rtems/console.h> +#include <m68340.h> +#include <m340uart.h> +#include <m340timer.h> + +#define CONSOLE_VECTOR 121 +#define CONSOLE_IRQ_LEVEL 3 +#define CONSOLE_INTERRUPT_ARBITRATION 2 + +static void *ttypA; /* to remember which tty has been opened on channel A + used when interrupts are enabled */ + +static void *ttypB; /* to remember which tty has been opened on channel B + used when interrupts are enabled */ + +unsigned char DUIER_mirror = 0 ; /* reflects the state of IER register, which is Write Only */ +unsigned char Error_Status_A = 0; /* error status on Channel A */ +unsigned char Error_Status_B = 0; /* error status on Channel A */ + +/* + * Device-specific routines + */ + +#define USE_INTERRUPTS_A (m340_uart_config[UART_CHANNEL_A].mode==UART_INTERRUPTS) +#define USE_INTERRUPTS_B (m340_uart_config[UART_CHANNEL_B].mode==UART_INTERRUPTS) +#define CHANNEL_ENABLED_A m340_uart_config[UART_CHANNEL_A].enable +#define CHANNEL_ENABLED_B m340_uart_config[UART_CHANNEL_B].enable + +#define set_DUIER(a) DUIER_mirror |= (a); DUIER = DUIER_mirror +#define unset_DUIER(a) DUIER_mirror &= ~(a); DUIER = DUIER_mirror + +#define Enable_Interrupts_Tx_A if (USE_INTERRUPTS_A) set_DUIER(m340_TxRDYA) +#define Disable_Interrupts_Tx_A if (USE_INTERRUPTS_A) unset_DUIER(m340_TxRDYA) + +#define Enable_Interrupts_Tx_B if (USE_INTERRUPTS_B) set_DUIER(m340_TxRDYB) +#define Disable_Interrupts_Tx_B if (USE_INTERRUPTS_B) unset_DUIER(m340_TxRDYB) + +/****************************************************** + Name: InterruptHandler + Input parameters: vector number + Output parameters: - + Description: UART ISR Routine, called by _RTEMS_ISR + *****************************************************/ +rtems_isr +InterruptHandler (rtems_vector_number v) +{ + char ch; + + /***************************************************************************** + ** CHANNEL A ** + *****************************************************************************/ + + /* check Received Break*/ + if (DUSRA & m340_RB) { + Error_Status_A |= m340_RB; + /* reset error status */ + DUCRA = m340_Reset_Error_Status; + } + + /* buffer received ? */ + if (DUSRA & m340_Rx_RDY) { + do { + /* error encountered? */ + if (DUSRA & (m340_OE | m340_PE | m340_FE | m340_RB)) { + Error_Status_A |= DUSRA; + /* reset error status */ + DUCRA = m340_Reset_Error_Status; + /* all the characters in the queue may not be good */ + while (DUSRA & m340_Rx_RDY) + /* push them in a trash */ + ch = DURBA; + } + else { + /* this is necessary, otherwise it blocks when FIFO is full */ + ch = DURBA; + rtems_termios_enqueue_raw_characters(ttypA,&ch,1); + } + } while (DUSRA & m340_Rx_RDY); + Restart_Fifo_Full_A_Timer(); /* only if necessary (pointer to a fake function if + not in FIFO full mode) */ + } + + else /* if no character has been received */ + Restart_Check_A_Timer(); /* same remark */ + + /* ready to accept a character ? */ + if (DUISR & DUIER_mirror & m340_TxRDYA) { + Disable_Interrupts_Tx_A; + /* one character has been transmitted */ + rtems_termios_dequeue_characters(ttypA,1); + } + + /***************************************************************************** + ** CHANNEL B ** + *****************************************************************************/ + + /* check Received Break*/ + if (DUSRB & m340_RB) { + Error_Status_B |= m340_RB; + /* reset error status */ + DUCRB = m340_Reset_Error_Status; + } + + /* buffer received ? */ + if (DUSRB & m340_Rx_RDY) { + do { + if (DUSRB & (m340_OE | m340_PE | m340_FE | m340_RB)) { + Error_Status_B |= DUSRB; + /* reset error status */ + DUCRB = m340_Reset_Error_Status; + /* all the characters in the queue may not be good */ + while (DUSRB & m340_Rx_RDY) + /* push them in a trash */ + ch = DURBB; + } + else { + ch = DURBB; + rtems_termios_enqueue_raw_characters(ttypB,&ch,1); + } + + } while (DUSRB & m340_Rx_RDY); + Restart_Fifo_Full_B_Timer(); + } + else /* if no character has been received */ + Restart_Check_B_Timer(); + + /* ready to accept a character ? */ + if (DUISR & DUIER_mirror & m340_TxRDYB) { + Disable_Interrupts_Tx_B; + /* one character has been transmitted */ + rtems_termios_dequeue_characters(ttypB,1); + } +} + +/****************************************************** + Name: InterruptWrite + Input parameters: minor = channel, pointer to buffer, + and length of buffer to transmit + Output parameters: - + Description: write the first character of buf only + may be called by either console_write + or rtems_termios_enqueue_raw_characters + *****************************************************/ +static ssize_t +InterruptWrite (int minor, const char *buf, size_t len) +{ + if (minor==UART_CHANNEL_A) { + if (len>0) { + DUTBA=*buf; + Enable_Interrupts_Tx_A; + } + } + else if (minor==UART_CHANNEL_B) { + if (len>0) { + DUTBB=*buf; + Enable_Interrupts_Tx_B; + } + } + return 0; +} + +/****************************************************** + Name: dbug_out_char + Input parameters: channel, character to emit + Output parameters: - + Description: wait for the UART to be ready to emit + a character and send it + *****************************************************/ +void dbug_out_char( int minor, int ch ) +{ + if (minor==UART_CHANNEL_A) { + while (!(DUSRA & m340_Tx_RDY)) continue; + DUTBA=ch; + } + else if (minor==UART_CHANNEL_B) { + while (!(DUSRB & m340_Tx_RDY)) continue; + DUTBB=ch; + } +} + +/****************************************************** + Name: dbug_in_char + Input parameters: - + Output parameters: received character + Description: return the character in the UART + *****************************************************/ +int dbug_in_char( int minor ) +{ + if (minor==UART_CHANNEL_A) { + return DURBA; + } + else if (minor==UART_CHANNEL_B) { + return DURBB; + } + return 0; +} + +/****************************************************** + Name: dbug_char_present + Input parameters: channel # + Output parameters: TRUE or FALSE + Description: return whether there's a character + in the receive buffer + *****************************************************/ +int dbug_char_present( int minor ) +{ + if (minor==UART_CHANNEL_A) { + return (DUSRA & m340_Rx_RDY); + } + else if (minor==UART_CHANNEL_B) { + return (DUSRB & m340_Rx_RDY); + } + return 0; +} + +/****************************************************** + Name: dbugInitialise + Input parameters: - + Output parameters: - + Description: Init the UART + *****************************************************/ +static void +dbugInitialise (void) +{ + t_baud_speed_table uart_config; /* configuration of UARTS */ + + /* + * Reset Receiver + */ + DUCRA = m340_Reset_Receiver; + DUCRB = m340_Reset_Receiver; + + /* + * Reset Transmitter + */ + DUCRA = m340_Reset_Transmitter; + DUCRB = m340_Reset_Transmitter; + + /* + * Enable serial module for normal operation, ignore FREEZE, select the crystal clock, + * supervisor/user serial registers unrestricted + * interrupt arbitration at priority CONSOLE_INTERRUPT_ARBITRATION + * WARNING : 8 bits access only on this UART! + */ + DUMCRH = 0x00; + DUMCRL = CONSOLE_INTERRUPT_ARBITRATION; + + /* + * Interrupt level register + */ + DUILR = CONSOLE_IRQ_LEVEL; + + /* sets the IVR */ + DUIVR = CONSOLE_VECTOR; + + /* search for a correct m340 uart configuration */ + uart_config = Find_Right_m340_UART_Config(m340_uart_config[UART_CHANNEL_A].rx_baudrate, + m340_uart_config[UART_CHANNEL_A].tx_baudrate, + CHANNEL_ENABLED_A, + m340_uart_config[UART_CHANNEL_B].rx_baudrate, + m340_uart_config[UART_CHANNEL_B].tx_baudrate, + CHANNEL_ENABLED_B); + + /***************************************************************************** + ** CHANNEL A ** + *****************************************************************************/ + if (CHANNEL_ENABLED_A) { + + if (USE_INTERRUPTS_A) { + rtems_isr_entry old_handler; + + (void) rtems_interrupt_catch (InterruptHandler, + CONSOLE_VECTOR, + &old_handler); + + /* uncomment this if you want to pass control to your own ISR handler + it may be usefull to do so to check for performances with an oscilloscope */ + /* + { + proc_ptr ignored; + _CPU_ISR_install_raw_handler( CONSOLE_VECTOR, _Debug_ISR_Handler_Console, &ignored ); + } + */ + + /* + * Interrupt Enable Register + * Enable Interrupts on Channel A Receiver Ready + */ + set_DUIER(m340_RxRDYA); + } + else { + /* + * Disable Interrupts on channel A + */ + unset_DUIER(m340_RxRDYA&m340_TxRDYA); + } + + /* + * Change set of baud speeds + * disable input control + */ + /* no good uart configuration ? */ + if (uart_config.nb<1) rtems_fatal_error_occurred (-1); + + if (uart_config.baud_speed_table[UART_CHANNEL_A].set==1) + DUACR = m340_BRG_Set1; + else + DUACR = m340_BRG_Set2; + + /* + * make OPCR an auxiliary function serving the communication channels + */ + DUOPCR = m340_OPCR_Aux; + + /* poll the XTAL_RDY bit until it is cleared to ensure that an unstable crystal + input is not applied to the baud rate generator */ + while (DUISR & m340_XTAL_RDY) continue; + + /* + * Serial Channel Baud Speed + */ + DUCSRA = (uart_config.baud_speed_table[UART_CHANNEL_A].rcs << 4) + | (uart_config.baud_speed_table[UART_CHANNEL_A].tcs); + + /* + * Serial Channel Configuration + */ + DUMR1A = m340_uart_config[UART_CHANNEL_A].parity_mode + | m340_uart_config[UART_CHANNEL_A].bits_per_char + | m340_RxRTS; + + if (m340_uart_config[UART_CHANNEL_A].rx_mode==UART_FIFO_FULL) DUMR1A |= m340_R_F | m340_ERR; + + /* + * Serial Channel Configuration 2 + */ + DUMR2A |= m340_normal; + + /* + * Enable Channel A: transmitter and receiver + */ + DUCRA = m340_Transmitter_Enable | m340_Receiver_Enable; + } /* channel A enabled */ + + /***************************************************************************** + ** CHANNEL B ** + *****************************************************************************/ + if (CHANNEL_ENABLED_B) { + + /* we mustn't set the console vector twice! */ + if ((USE_INTERRUPTS_B && !(CHANNEL_ENABLED_A)) + || (USE_INTERRUPTS_B && CHANNEL_ENABLED_A && !USE_INTERRUPTS_A)) { + rtems_isr_entry old_handler; + + (void) rtems_interrupt_catch (InterruptHandler, + CONSOLE_VECTOR, + &old_handler); + + /* uncomment this if you want to pass control to your own ISR handler + it may be usefull to do so to check for performances with an oscilloscope */ + /* + { + proc_ptr ignored; + _CPU_ISR_install_raw_handler( CONSOLE_VECTOR, _Debug_ISR_Handler_Console, &ignored ); + } + */ + + /* + * Interrupt Enable Register + * Enable Interrupts on Channel A Receiver Ready + */ + set_DUIER(m340_RxRDYB); + } + else { + /* + * Disable Interrupts on channel B + */ + unset_DUIER(m340_RxRDYB&m340_TxRDYB); + } + + /* + * Change set of baud speeds + * disable input control + */ + + /* no good uart configuration ? */ + if (uart_config.nb<2) rtems_fatal_error_occurred (-1); + + /* don't set DUACR twice! */ + if (!CHANNEL_ENABLED_A) { + if (uart_config.baud_speed_table[UART_CHANNEL_B].set==1) + DUACR = m340_BRG_Set1; + else + DUACR = m340_BRG_Set2; + } + + /* + * make OPCR an auxiliary function serving the communication channels + */ + if (!CHANNEL_ENABLED_A) DUOPCR = m340_OPCR_Aux; + + /* poll the XTAL_RDY bit until it is cleared to ensure that an unstable crystal + input is not applied to the baud rate generator */ + while (DUISR & m340_XTAL_RDY) continue; + + /* + * Serial Channel Baud Speed + */ + DUCSRB = (uart_config.baud_speed_table[UART_CHANNEL_B].rcs << 4) + | (uart_config.baud_speed_table[UART_CHANNEL_B].tcs); + + /* + * Serial Channel Configuration + */ + DUMR1B = m340_uart_config[UART_CHANNEL_B].parity_mode + | m340_uart_config[UART_CHANNEL_B].bits_per_char + | m340_RxRTS; + + if (m340_uart_config[UART_CHANNEL_B].rx_mode==UART_FIFO_FULL) DUMR1B |= m340_R_F | m340_ERR; + + /* + * Serial Channel Configuration 2 + */ + DUMR2B |= m340_normal; + + /* + * Enable Channel A: transmitter and receiver + */ + DUCRB = m340_Transmitter_Enable | m340_Receiver_Enable; + } /* channel B enabled */ +} + +/****************************************************** + Name: SetAttributes + Input parameters: termios structure, channel + Output parameters: - + Description: return whether there's a character + in the receive buffer + TO DO: add the channel # to check for!! + *****************************************************/ +static int +SetAttributes (int minor, const struct termios *t) +{ + rtems_interrupt_level level; + float ispeed, ospeed; + + /* convert it */ + ispeed = rtems_termios_baud_to_number(t->c_ispeed); + ospeed = rtems_termios_baud_to_number(t->c_ospeed); + + if (ispeed || ospeed) { + /* update config table */ + m340_uart_config[UART_CHANNEL_A].rx_baudrate = ((minor==UART_CHANNEL_A)&&(ispeed!=0)) ? ispeed : m340_uart_config[UART_CHANNEL_A].rx_baudrate; + m340_uart_config[UART_CHANNEL_A].tx_baudrate = ((minor==UART_CHANNEL_A)&&(ospeed!=0)) ? ospeed : m340_uart_config[UART_CHANNEL_A].tx_baudrate; + m340_uart_config[UART_CHANNEL_B].rx_baudrate = ((minor==UART_CHANNEL_B)&&(ispeed!=0)) ? ispeed : m340_uart_config[UART_CHANNEL_B].rx_baudrate; + m340_uart_config[UART_CHANNEL_B].tx_baudrate = ((minor==UART_CHANNEL_B)&&(ospeed!=0)) ? ospeed : m340_uart_config[UART_CHANNEL_B].tx_baudrate; + } + + /* change parity */ + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) m340_uart_config[minor].parity_mode = m340_Odd_Parity; + else m340_uart_config[minor].parity_mode = m340_Even_Parity; + } + + /* change bits per character */ + if (t->c_cflag & CSIZE) { + switch (t->c_cflag & CSIZE) { + default: break; + case CS5: m340_uart_config[minor].bits_per_char = m340_5bpc; break; + case CS6: m340_uart_config[minor].bits_per_char = m340_6bpc; break; + case CS7: m340_uart_config[minor].bits_per_char = m340_7bpc; break; + case CS8: m340_uart_config[minor].bits_per_char = m340_8bpc; break; + } + } + + /* if serial module configuration has been changed */ + if (t->c_cflag & (CSIZE | PARENB)) { + rtems_interrupt_disable(level); + /* reinit the UART */ + dbugInitialise(); + rtems_interrupt_enable (level); + } + + return 0; +} + +/****************************************************** + Name: console_initialize + Input parameters: MAJOR # of console_driver, + minor is always 0, + args are always NULL + Output parameters: - + Description: Reserve resources consumed by this driver + TODO: We should pass m340_uart_config table in arg + *****************************************************/ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + int i; + + /* + * Set up TERMIOS + */ + rtems_termios_initialize (); + + /* + * Do device-specific initialization + */ + Init_UART_Table(); + dbugInitialise (); + Fifo_Full_benchmark_timer_initialize(); + + /* + * Register the devices + */ + for (i=0; i<UART_NUMBER_OF_CHANNELS; i++) { + if (m340_uart_config[i].enable) { + status = rtems_io_register_name (m340_uart_config[i].name, major, i); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + } + } + + return RTEMS_SUCCESSFUL; +} + +/****************************************************** + Name: console_open + Input parameters: channel #, arg + Output parameters: - + Description: open the device + *****************************************************/ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_status_code sc = 0; + + static const rtems_termios_callbacks intrCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + NULL, /* pollRead */ + InterruptWrite, /* write */ + SetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 1 /* outputUsesInterrupts */ + }; + + static const rtems_termios_callbacks pollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + dbugRead, /* pollRead */ + dbugWrite, /* write */ + SetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 0 /* outputUsesInterrupts */ + }; + + if (minor==UART_CHANNEL_A) { + if (USE_INTERRUPTS_A) { + rtems_libio_open_close_args_t *args = arg; + + sc |= rtems_termios_open (major, minor, arg, &intrCallbacks); + ttypA = args->iop->data1; + } + else { + sc |= rtems_termios_open (major, minor, arg, &pollCallbacks); + } + } + + else if (minor==UART_CHANNEL_B) { + if (USE_INTERRUPTS_B) { + rtems_libio_open_close_args_t *args = arg; + + sc |= rtems_termios_open (major, minor, arg, &intrCallbacks); + ttypB = args->iop->data1; + } + else { + sc |= rtems_termios_open (major, minor, arg, &pollCallbacks); + } + } + + else return RTEMS_INVALID_NUMBER; + + return sc; +} + +/****************************************************** + Name: console_close + Input parameters: channel #, termios args + Output parameters: - + Description: close the device + *****************************************************/ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_close (arg); +} + +/****************************************************** + Name: console_read + Input parameters: channel #, termios args + Output parameters: - + Description: read the device + *****************************************************/ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_read (arg); +} + +/****************************************************** + Name: console_write + Input parameters: channel #, termios args + Output parameters: - + Description: write to the device + *****************************************************/ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_write (arg); +} + +/****************************************************** + Name: console_control + Input parameters: channel #, termios args + Output parameters: - + Description: Handle ioctl request + *****************************************************/ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + + if (args->command == TIOCSETA) + SetAttributes (minor, (struct termios *)args->buffer); + + return rtems_termios_ioctl (arg); +} diff --git a/bsps/m68k/gen68340/console/m340uart.c b/bsps/m68k/gen68340/console/m340uart.c new file mode 100644 index 0000000000..56ad29c256 --- /dev/null +++ b/bsps/m68k/gen68340/console/m340uart.c @@ -0,0 +1,311 @@ +/* + * M68340/349 UART management tools + */ + +/* + * Author: + * Geoffroy Montel + * France Telecom - CNET/DSM/TAM/CAT + * 4, rue du Clos Courtel + * 35512 CESSON-SEVIGNE + * FRANCE + * + * e-mail: g_montel@yahoo.com + * + * COPYRIGHT (c) 1989-1999. + * 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.org/license/LICENSE. + */ + +#include <termios.h> +#include <bsp.h> +#include <rtems/libio.h> +#include <m68340.h> +#include <m340uart.h> +#include <stdarg.h> +#include <string.h> + +/* this table shows compatible speed configurations for the MC68340: + the first row shows baud rates for baud speed set 1 + the second row shows baud rates for baud speed set 2 + look at Motorola's MC68340 Integrated Processor User's Manual + page 7-30 for more infos */ + +float m340_Baud_Rates_Table[16][2] = { + { 50, 75 }, + { 110, 110 }, + { 134.5, 134.5 }, + { 200, 150 }, + { 300, 300 }, + { 600, 600 }, + { 1200, 1200 }, + { 1050, 2000 }, + { 2400, 2400 }, + { 4800, 4800 }, + { 7200, 1800 }, + { 9600, 9600 }, + { 38400, 19200 }, + { 76800, 38400 }, + { SCLK/16, SCLK/16}, + { SCLK, SCLK }, +}; + +/* config on both 340 channels */ +uart_channel_config m340_uart_config[UART_NUMBER_OF_CHANNELS]; + +/* + * Init UART table + */ + +#define NOT_IMPLEMENTED_YET 0 + +/****************************************************** + Name: Init_UART_Table + Input parameters: - + Output parameters: - + Description: Init the m340_uart_config + THIS SHOULD NOT BE HERE! + Its aim was to let the user configure + UARTs for each application. + As we can't pass args to the console + driver initialisation routine at the + moment, this was not done. + ATTENTION: TERMIOS init presupposes that the channel + baud rates is 9600/9600. + -> risks when using IOCTL + *****************************************************/ +void Init_UART_Table(void) +{ + m340_uart_config[UART_CHANNEL_A].enable = TRUE; + strcpy(m340_uart_config[UART_CHANNEL_A].name, UART_CONSOLE_NAME); + m340_uart_config[UART_CHANNEL_A].parity_mode = m340_No_Parity; + m340_uart_config[UART_CHANNEL_A].bits_per_char = m340_8bpc; + m340_uart_config[UART_CHANNEL_A].rx_baudrate = 9600; + m340_uart_config[UART_CHANNEL_A].tx_baudrate = 9600; + m340_uart_config[UART_CHANNEL_A].rx_mode = UART_CRR; + m340_uart_config[UART_CHANNEL_A].mode = UART_POLLING; + + m340_uart_config[UART_CHANNEL_A].termios.enable = TRUE; + m340_uart_config[UART_CHANNEL_A].termios.rx_buffer_size = NOT_IMPLEMENTED_YET; + m340_uart_config[UART_CHANNEL_A].termios.tx_buffer_size = NOT_IMPLEMENTED_YET; + + m340_uart_config[UART_CHANNEL_B].enable = FALSE; + strcpy(m340_uart_config[UART_CHANNEL_B].name, UART_RAW_IO_NAME); + m340_uart_config[UART_CHANNEL_B].parity_mode = m340_No_Parity; + m340_uart_config[UART_CHANNEL_B].bits_per_char = m340_8bpc; + m340_uart_config[UART_CHANNEL_B].rx_baudrate = 38400; + m340_uart_config[UART_CHANNEL_B].tx_baudrate = 38400; + m340_uart_config[UART_CHANNEL_B].rx_mode = UART_CRR; + m340_uart_config[UART_CHANNEL_B].mode = UART_INTERRUPTS; + + m340_uart_config[UART_CHANNEL_B].termios.enable = TRUE; + m340_uart_config[UART_CHANNEL_B].termios.rx_buffer_size = NOT_IMPLEMENTED_YET; + m340_uart_config[UART_CHANNEL_B].termios.tx_buffer_size = NOT_IMPLEMENTED_YET; +} + +/****************************************************** + Name: Find_Right_m340_UART_Channel_Config + Input parameters: Send/Receive baud rates for a + given channel + Output parameters: UART compatible configs for this + channel + Description: returns which uart configurations fit + Receiver Baud Rate and Transmitter Baud + Rate for a given channel + For instance, according to the + m340_Baud_Rates_Table: + - Output Speed = 50, Input Speed = 75 + is not a correct config, because + 50 bauds implies set 1 and 75 bauds + implies set 2 + - Output Speed = 9600, Input Speed = 9600 + two correct configs for this: + RCS=11, TCS=11, Set=1 or 2 + *****************************************************/ +static t_baud_speed_table +Find_Right_m340_UART_Channel_Config( + float ReceiverBaudRate, + float TransmitterBaudRate +) +{ + t_baud_speed_table return_value; + int i,j; + + struct { + int cs; + int set; + } Receiver[2], Transmitter[2]; + + int Receiver_nb_of_config = 0; + int Transmitter_nb_of_config = 0; + + /* Receiver and Transmitter baud rates must be compatible, ie in the + * same set. + */ + + /* search for configurations for ReceiverBaudRate + * there can't be more than two (only two sets). + */ + for (i=0;i<16;i++) { + for (j=0;j<2;j++) { + if (m340_Baud_Rates_Table[i][j]==ReceiverBaudRate) { + Receiver[Receiver_nb_of_config].cs=i; + Receiver[Receiver_nb_of_config].set=j; + Receiver_nb_of_config++; + } + } + } + + /* search for configurations for TransmitterBaudRate + * there can't be more than two (only two sets) + */ + for (i=0;i<16;i++) { + for (j=0;j<2;j++) { + if (m340_Baud_Rates_Table[i][j]==TransmitterBaudRate) { + Transmitter[Transmitter_nb_of_config].cs=i; + Transmitter[Transmitter_nb_of_config].set=j; + Transmitter_nb_of_config++; + } + } + } + + /* now check if there's a compatible config */ + return_value.nb=0; + + for (i=0; i<Receiver_nb_of_config; i++) { + for (j=0;j<Transmitter_nb_of_config;j++) { + if (Receiver[i].set == Transmitter[j].set) { + return_value.baud_speed_table[return_value.nb].set = Receiver[i].set + 1; + /* we want set 1 or set 2, not 0 or 1 */ + return_value.baud_speed_table[return_value.nb].rcs = Receiver[i].cs; + return_value.baud_speed_table[return_value.nb].tcs = Transmitter[j].cs; + return_value.nb++; + } + } + } + + return return_value; +} + +/****************************************************** + Name: Find_Right_m340_UART_Config + Input parameters: Send/Receive baud rates for both + channels + Output parameters: UART compatible configs for + BOTH channels + Description: returns which uart configurations fit + Receiver Baud Rate and Transmitter Baud + Rate for both channels + For instance, if we want 9600/38400 on + channel A and 9600/19200 on channel B, + this is not a good m340 uart config + (channel A needs set 1 and channel B + needs set 2) + *****************************************************/ +t_baud_speed_table +Find_Right_m340_UART_Config( + float ChannelA_ReceiverBaudRate, + float ChannelA_TransmitterBaudRate, + uint8_t enableA, + float ChannelB_ReceiverBaudRate, + float ChannelB_TransmitterBaudRate, + uint8_t enableB +) +{ + t_baud_speed_table tableA, tableB; + t_baud_speed_table return_value, tmp; + int i,j; + + memset( &return_value, '\0', sizeof(return_value) ); + return_value.nb=0; + + if (enableA && enableB) { + tableA = Find_Right_m340_UART_Channel_Config( + ChannelA_ReceiverBaudRate, ChannelA_TransmitterBaudRate); + tableB = Find_Right_m340_UART_Channel_Config( + ChannelB_ReceiverBaudRate, ChannelB_TransmitterBaudRate); + + for (i=0;i<tableA.nb;i++) { + for (j=0;j<tableB.nb;j++) { + if (tableA.baud_speed_table[i].set==tableB.baud_speed_table[j].set) { + return_value.baud_speed_table[UART_CHANNEL_A].set = + tableA.baud_speed_table[i].set; + return_value.baud_speed_table[UART_CHANNEL_A].rcs = + tableA.baud_speed_table[i].rcs; + return_value.baud_speed_table[UART_CHANNEL_A].tcs = + tableA.baud_speed_table[i].tcs; + return_value.baud_speed_table[UART_CHANNEL_B].set = + tableB.baud_speed_table[j].set; + return_value.baud_speed_table[UART_CHANNEL_B].rcs = + tableB.baud_speed_table[j].rcs; + return_value.baud_speed_table[UART_CHANNEL_B].tcs = + tableB.baud_speed_table[j].tcs; + return_value.nb=2; + break; + } + } + } + return return_value; + } + + if (enableA) { + return_value = Find_Right_m340_UART_Channel_Config( + ChannelA_ReceiverBaudRate, ChannelA_TransmitterBaudRate); + return return_value; + } + + if (enableB) { + tmp = Find_Right_m340_UART_Channel_Config( + ChannelB_ReceiverBaudRate, ChannelB_TransmitterBaudRate); + if (tmp.nb!=0) { + return_value.nb = 2; + return_value.baud_speed_table[1].set = tmp.baud_speed_table[0].set; + return_value.baud_speed_table[1].rcs = tmp.baud_speed_table[0].rcs; + return_value.baud_speed_table[1].tcs = tmp.baud_speed_table[0].tcs; + } + } + return return_value; +} + + +/* + * very low level fmted output + */ +extern void dbug_out_char( int minor, int ch ); +extern int dbug_in_char( int minor ); +extern int dbug_char_present( int minor ); + +/****************************************************** + Name: dbugRead + Input parameters: channel + Output parameters: char read + Description: polled read + *****************************************************/ +int dbugRead (int minor) +{ + if (dbug_char_present(minor) == 0) + return -1; + return dbug_in_char(minor); +} + +/****************************************************** + Name: dbugWrite + Input parameters: channel, buffer and its length + Output parameters: always successfull + Description: polled write + *****************************************************/ +ssize_t dbugWrite (int minor, const char *buf, size_t len) +{ + static char txBuf; + size_t retval = len; + + while (len--) { + txBuf = *buf++; + dbug_out_char( minor, (int)txBuf ); + } + return retval; +} + diff --git a/bsps/m68k/gen68360/console/console.c b/bsps/m68k/gen68360/console/console.c new file mode 100644 index 0000000000..36d8470168 --- /dev/null +++ b/bsps/m68k/gen68360/console/console.c @@ -0,0 +1,390 @@ +/* + * SMC1 raw console serial I/O. + * + * This driver is an example of `POLLING' or `INTERRUPT' I/O. + * + * To run with interrupt-driven I/O, ensure m360_smc1_interrupt + * is set before calling the initialization routine. + */ + +/* + * Author: + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + * + * COPYRIGHT (c) 1989-1999. + * 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.org/license/LICENSE. + */ + +#include <termios.h> +#include <bsp.h> +#include <rtems/libio.h> +#include <rtems/console.h> +#include <rtems/termiostypes.h> +#include <rtems/m68k/m68360.h> + +/* + * Declare clock speed -- may be overwritten by downloader or debugger + */ +int m360_clock_rate = 25000000; + +/* + * Interrupt-driven input buffer + * Declare console baud rate -- may also be overwritten + */ +int console_baud_rate = 9600; + +/* + */ +#define RXBUFSIZE 16 + +/* + * Interrupt-driven callback + */ +static int m360_smc1_interrupt = 1; +static void *smc1ttyp; + +/* + * I/O buffers and pointers to buffer descriptors + */ +static volatile char rxBuf[RXBUFSIZE]; +static volatile m360BufferDescriptor_t *smcRxBd, *smcTxBd; + +/* + * Device-specific routines + */ + +/* + * Compute baud-rate-generator configuration register value + */ +static int +smc1BRGC (int baud) +{ + int divisor; + int div16 = 0; + + divisor = ((m360_clock_rate / 16) + (baud / 2)) / baud; + if (divisor > 4096) { + div16 = 1; + divisor = (divisor + 8) / 16; + } + return M360_BRG_EN | M360_BRG_EXTC_BRGCLK | ((divisor - 1) << 1) | div16; +} + +/* + * Hardware-dependent portion of tcsetattr(). + */ +static int +smc1SetAttributes (int minor, const struct termios *t) +{ + int baud; + + baud = rtems_termios_baud_to_number(t->c_ospeed); + if (baud > 0) + m360.brgc1 = smc1BRGC (baud); + return 0; +} + +/* + * Interrupt handler + */ +static rtems_isr +smc1InterruptHandler (rtems_vector_number v) +{ + /* + * Buffer received? + */ + if (m360.smc1.smce & 0x1) { + m360.smc1.smce = 0x1; + while ((smcRxBd->status & M360_BD_EMPTY) == 0) { + rtems_termios_enqueue_raw_characters (smc1ttyp, + (char *)smcRxBd->buffer, + smcRxBd->length); + smcRxBd->status = M360_BD_EMPTY | M360_BD_WRAP | M360_BD_INTERRUPT; + } + } + + /* + * Buffer transmitted? + */ + if (m360.smc1.smce & 0x2) { + m360.smc1.smce = 0x2; + if ((smcTxBd->status & M360_BD_READY) == 0) + rtems_termios_dequeue_characters (smc1ttyp, smcTxBd->length); + } + m360.cisr = 1UL << 4; /* Clear SMC1 interrupt-in-service bit */ +} + +static int +smc1Initialize (int major, int minor, void *arg) +{ + /* + * Allocate buffer descriptors + */ + smcRxBd = M360AllocateBufferDescriptors (1); + smcTxBd = M360AllocateBufferDescriptors (1); + + /* + * Configure port B pins to enable SMTXD1 and SMRXD1 pins + */ + m360.pbpar |= 0xC0; + m360.pbdir &= ~0xC0; + m360.pbodr &= ~0xC0; + + /* + * Set up BRG1 (9,600 baud) + */ + m360.brgc1 = M360_BRG_RST; + m360.brgc1 = smc1BRGC (console_baud_rate); + + /* + * Put SMC1 in NMSI mode, connect SMC1 to BRG1 + */ + m360.simode |= M360_SI_SMC1_BRG1; + + /* + * Set up SMC1 parameter RAM common to all protocols + */ + m360.smc1p.rbase = (char *)smcRxBd - (char *)&m360; + m360.smc1p.tbase = (char *)smcTxBd - (char *)&m360; + m360.smc1p.rfcr = M360_RFCR_MOT | M360_RFCR_DMA_SPACE; + m360.smc1p.tfcr = M360_TFCR_MOT | M360_TFCR_DMA_SPACE; + if (m360_smc1_interrupt) + m360.smc1p.mrblr = RXBUFSIZE; + else + m360.smc1p.mrblr = 1; + + /* + * Set up SMC1 parameter RAM UART-specific parameters + */ + m360.smc1p.un.uart.max_idl = 10; + m360.smc1p.un.uart.brklen = 0; + m360.smc1p.un.uart.brkec = 0; + m360.smc1p.un.uart.brkcr = 0; + + /* + * Set up the Receive Buffer Descriptor + */ + smcRxBd->status = M360_BD_EMPTY | M360_BD_WRAP | M360_BD_INTERRUPT; + smcRxBd->length = 0; + smcRxBd->buffer = rxBuf; + + /* + * Setup the Transmit Buffer Descriptor + */ + smcTxBd->status = M360_BD_WRAP; + + /* + * Set up SMC1 general and protocol-specific mode registers + */ + m360.smc1.smce = ~0; /* Clear any pending events */ + m360.smc1.smcm = 0; /* Mask all interrupt/event sources */ + m360.smc1.smcmr = M360_SMCMR_CLEN(9) | M360_SMCMR_SM_UART; + + /* + * Send "Init parameters" command + */ + M360ExecuteRISC (M360_CR_OP_INIT_RX_TX | M360_CR_CHAN_SMC1); + + /* + * Enable receiver and transmitter + */ + m360.smc1.smcmr |= M360_SMCMR_TEN | M360_SMCMR_REN; + + if (m360_smc1_interrupt) { + rtems_isr_entry old_handler; + + (void) rtems_interrupt_catch (smc1InterruptHandler, + (m360.cicr & 0xE0) | 0x04, + &old_handler); + m360.smc1.smcm = 3; /* Enable SMC1 TX and RX interrupts */ + m360.cimr |= 1UL << 4; /* Enable SMC1 interrupts */ + } + + return 0; +} + +static int +smc1PollRead (int minor) +{ + unsigned char c; + + if (smcRxBd->status & M360_BD_EMPTY) + return -1; + c = rxBuf[0]; + smcRxBd->status = M360_BD_EMPTY | M360_BD_WRAP; + return c; +} + +/* + * Device-dependent write routine + * Interrupt-driven devices: + * Begin transmission of as many characters as possible (minimum is 1). + * Polling devices: + * Transmit all characters. + */ +static ssize_t +smc1InterruptWrite (int minor, const char *buf, size_t len) +{ + if (len > 0) { + smcTxBd->buffer = (char *)buf; + smcTxBd->length = len; + smcTxBd->status = M360_BD_READY | M360_BD_WRAP | M360_BD_INTERRUPT; + } + + return 0; +} + +static ssize_t +smc1PollWrite (int minor, const char *buf, size_t len) +{ + size_t retval = len; + while (len--) { + static char txBuf; + while (smcTxBd->status & M360_BD_READY) + continue; + txBuf = *buf++; + smcTxBd->buffer = &txBuf; + smcTxBd->length = 1; + smcTxBd->status = M360_BD_READY | M360_BD_WRAP; + } + return retval; +} + +/* + *************** + * BOILERPLATE * + *************** + */ + +/* + * Reserve resources consumed by this driver + * + * NOTE: This is in another file to reduce dependencies on the minimum size. + */ + +/* + * Initialize and register the device + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + + /* + * Set up TERMIOS + */ + rtems_termios_initialize (); + + /* + * Register the device + */ + status = rtems_io_register_name ("/dev/console", major, 0); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + return RTEMS_SUCCESSFUL; +} + +/* + * Open the device + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_status_code sc; + static const rtems_termios_callbacks intrCallbacks = { + smc1Initialize, /* firstOpen */ + NULL, /* lastClose */ + NULL, /* pollRead */ + smc1InterruptWrite, /* write */ + smc1SetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 1 /* outputUsesInterrupts */ + }; + static const rtems_termios_callbacks pollCallbacks = { + smc1Initialize, /* firstOpen */ + NULL, /* lastClose */ + smc1PollRead, /* pollRead */ + smc1PollWrite, /* write */ + smc1SetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 0 /* outputUsesInterrupts */ + }; + + /* + * Do generic termios initialization + */ + if (m360_smc1_interrupt) { + rtems_libio_open_close_args_t *args = arg; + + sc = rtems_termios_open (major, minor, arg, &intrCallbacks); + smc1ttyp = args->iop->data1; + } + else { + sc = rtems_termios_open (major, minor, arg, &pollCallbacks); + } + return sc; +} + +/* + * Close the device + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_close (arg); +} + +/* + * Read from the device + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_read (arg); +} + +/* + * Write to the device + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_write (arg); +} + +/* + * Handle ioctl request. + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_ioctl (arg); +} diff --git a/bsps/m68k/genmcf548x/console/console.c b/bsps/m68k/genmcf548x/console/console.c new file mode 100644 index 0000000000..32e5601a17 --- /dev/null +++ b/bsps/m68k/genmcf548x/console/console.c @@ -0,0 +1,843 @@ +/*===============================================================*\ +| Project: RTEMS generic mcf548x BSP | ++-----------------------------------------------------------------+ +| File: console.c | ++-----------------------------------------------------------------+ +| The file contains the console driver code of generic MCF548x | +| BSP. | ++-----------------------------------------------------------------+ +| Copyright (c) 2007 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| | +| Parts of the code has been derived from the "dBUG source code" | +| package Freescale is providing for M548X EVBs. The usage of | +| the modified or unmodified code and it's integration into the | +| generic mcf548x BSP has been done according to the Freescale | +| license terms. | +| | +| The Freescale license terms can be reviewed in the file | +| | +| Freescale_license.txt | +| | ++-----------------------------------------------------------------+ +| | +| The generic mcf548x BSP has been developed on the basic | +| structures and modules of the av5282 BSP. | +| | ++-----------------------------------------------------------------+ +| | +| 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. | +| | ++-----------------------------------------------------------------+ +| | +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 12.11.07 1.0 ras | +| | +\*===============================================================*/ + + /* + * Multi UART console serial I/O. + * + * TO DO: Add DMA input/output + */ + +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> +#include <malloc.h> + +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <rtems/console.h> +#include <rtems/bspIo.h> + +#define UART_INTC0_IRQ_VECTOR(x) (64+35-(x)) + +#define MCF548X_PSC_SR_ERROR ( MCF548X_PSC_SR_RB_NEOF | \ + MCF548X_PSC_SR_FE_PHYERR | \ + MCF548X_PSC_SR_PE_CRCERR | \ + MCF548X_PSC_SR_OE ) + +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len); +static int IntUartPollRead (int minor); +static int IntUartSetAttributes(int minor, const struct termios *t); + +static void +psc_output_char( char c ) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + while (!((MCF548X_PSC_SR(CONSOLE_PORT) & MCF548X_PSC_SR_TXRDY))) + continue; + *((uint8_t *) &MCF548X_PSC_TB(CONSOLE_PORT)) = c; + while (!((MCF548X_PSC_SR(CONSOLE_PORT) & MCF548X_PSC_SR_TXRDY))) + continue; + rtems_interrupt_enable(level); +} + +static void +psc_output_char_init(char c) +{ + IntUartSetAttributes(CONSOLE_PORT, NULL); + BSP_output_char = psc_output_char; + psc_output_char(c); +} + +BSP_output_char_function_type BSP_output_char = psc_output_char_init; + +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +#define MAX_UART_INFO 4 +#define RX_BUFFER_SIZE 248 + +struct IntUartInfoStruct +{ + int iomode; + volatile int imr; + int baud; + int databits; + int parity; + int stopbits; + int hwflow; + int rx_in; + int rx_out; + char rx_buffer[RX_BUFFER_SIZE]; + void *ttyp; +}; + +struct IntUartInfoStruct IntUartInfo[MAX_UART_INFO]; + +static int GetBaud( int baudHandle ) +{ + int baud = BSP_CONSOLE_BAUD; + switch(baudHandle) + { + case B0: + baud = (int)0; + break; + case B1200: + baud = (int)1200; + break; + case B2400: + baud = (int)2400; + break; + case B4800: + baud = (int)4800; + break; + case B9600: + baud = (int)9600; + break; + case B19200: + baud = (int)19200; + break; + case B38400: + baud = (int)38400; + break; + case B57600: + baud = (int)57600; + break; + case B115200: + baud = (int)115200; + break; + } + return baud; +} + +/*************************************************************************** + Function : IntUartSet + + Description : This updates the hardware UART settings. + ***************************************************************************/ +static void +IntUartSet(int minor, int baud, int databits, int parity, int stopbits, int hwflow) +{ + uint8_t psc_mode_1 = 0, psc_mode_2 = 0; + uint16_t divider; + int level; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + rtems_interrupt_disable(level); + + /* disable interrupts, clear RTS line, and disable the UARTS */ + /* Mask all psc interrupts */ + MCF548X_PSC_IMR(minor) = 0x0000; + + /* Clear RTS to send */ + MCF548X_PSC_OPSET(minor) &= ~(MCF548X_PSC_OPSET_RTS); + + /* Disable receiver and transmitter */ + MCF548X_PSC_CR(minor) &= ~(MCF548X_PSC_CR_RX_ENABLED | MCF548X_PSC_CR_TX_ENABLED); + + /* provide gpio settings */ + switch (minor) + { + case 0: + MCF548X_GPIO_PAR_PSC0 = (0 | MCF548X_GPIO_PAR_PSC0_PAR_TXD0 | MCF548X_GPIO_PAR_PSC0_PAR_RXD0); + + if(hwflow) + { + MCF548X_GPIO_PAR_PSC0 |= (0 | MCF548X_GPIO_PAR_PSC0_PAR_CTS0_CTS | MCF548X_GPIO_PAR_PSC0_PAR_RTS0_RTS); + } + break; + case 1: + MCF548X_GPIO_PAR_PSC1 = (0 | MCF548X_GPIO_PAR_PSC1_PAR_TXD1 | MCF548X_GPIO_PAR_PSC1_PAR_RXD1); + + if(hwflow) + { + MCF548X_GPIO_PAR_PSC1 |= (0 | MCF548X_GPIO_PAR_PSC1_PAR_CTS1_CTS | MCF548X_GPIO_PAR_PSC1_PAR_RTS1_RTS); + } + break; + case 2: + MCF548X_GPIO_PAR_PSC2 = (0 | MCF548X_GPIO_PAR_PSC2_PAR_TXD2 | MCF548X_GPIO_PAR_PSC2_PAR_RXD2); + + if(hwflow) + { + MCF548X_GPIO_PAR_PSC2 |= (0 | MCF548X_GPIO_PAR_PSC2_PAR_CTS2_CTS | MCF548X_GPIO_PAR_PSC2_PAR_RTS2_RTS); + } + break; + case 3: + MCF548X_GPIO_PAR_PSC3 = (0 | MCF548X_GPIO_PAR_PSC3_PAR_TXD3 | MCF548X_GPIO_PAR_PSC3_PAR_RXD3); + + if(hwflow) + { + MCF548X_GPIO_PAR_PSC3 |= (0 | MCF548X_GPIO_PAR_PSC3_PAR_CTS3_CTS | MCF548X_GPIO_PAR_PSC3_PAR_RTS3_RTS); + } + break; + default: + break; + } + + /* save the current values */ + info->imr = 0; + info->baud = baud; + info->databits = databits; + info->parity = parity; + info->stopbits = stopbits; + info->hwflow = hwflow; + + /* Put PSC in UART mode */ + MCF548X_PSC_SICR(minor) = MCF548X_PSC_SICR_SIM_UART; + + /* set the baud rate values */ + MCF548X_PSC_CSR(minor) = (0 | MCF548X_PSC_CSR_RCSEL_SYS_CLK | MCF548X_PSC_CSR_TCSEL_SYS_CLK); + + /* Calculate baud settings */ + divider = (uint16_t)((get_CPU_clock_speed())/(baud * 32)); + MCF548X_PSC_CTUR(minor) = (uint8_t) ((divider >> 8) & 0xFF); + MCF548X_PSC_CTLR(minor) = (uint8_t) (divider & 0xFF); + + /* Reset transmitter, receiver, mode register, and error conditions */ + MCF548X_PSC_CR(minor) = MCF548X_PSC_CR_RESET_RX; + MCF548X_PSC_CR(minor) = MCF548X_PSC_CR_RESET_TX; + MCF548X_PSC_CR(minor) = MCF548X_PSC_CR_RESET_ERROR; + MCF548X_PSC_CR(minor) = MCF548X_PSC_CR_BKCHGINT; + MCF548X_PSC_CR(minor) = MCF548X_PSC_CR_RESET_MR; + + /* check to see if doing hardware flow control */ + if ( hwflow ) + { + /* set hardware flow options */ + psc_mode_1 = MCF548X_PSC_MR_RXRTS; + psc_mode_2 = MCF548X_PSC_MR_TXCTS; + } + + /* set mode registers */ + psc_mode_1 |= (uint8_t)(parity | databits); + psc_mode_2 |= (uint8_t)(stopbits); + + /* set mode registers */ + MCF548X_PSC_MR(minor) = psc_mode_1; + MCF548X_PSC_MR(minor) = psc_mode_2; + + /* Setup FIFO Alarms */ + MCF548X_PSC_RFAR(minor) = MCF548X_PSC_RFAR_ALARM(248); + MCF548X_PSC_TFAR(minor) = MCF548X_PSC_TFAR_ALARM(248); + + /* check to see if interrupts need to be enabled */ + if ( info->iomode != TERMIOS_POLLED ) + { + /* enable rx interrupts */ + info->imr |= MCF548X_PSC_IMR_RXRDY_FU; + MCF548X_PSC_IMR(minor) = info->imr; + } + + /* check to see if doing hardware flow control */ + if ( hwflow ) + { + /* assert the RTS line */ + MCF548X_PSC_OPSET(minor) = MCF548X_PSC_OPSET_RTS; + } + + rtems_interrupt_enable(level); + + /* Enable receiver and transmitter */ + MCF548X_PSC_CR(minor) =(0 | MCF548X_PSC_CR_RX_ENABLED | MCF548X_PSC_CR_TX_ENABLED); + + +} + +/*************************************************************************** + Function : IntUartSetAttributes + + Description : This provides the hardware-dependent portion of tcsetattr(). + value and sets it. At the moment this just sets the baud rate. + + Note: The highest baudrate is 115200 as this stays within + an error of +/- 5% at 25MHz processor clock + ***************************************************************************/ +static int +IntUartSetAttributes(int minor, const struct termios *t) +{ +/* set default index values */ +#ifdef HAS_DBUG + int baud = DBUG_SETTINGS.console_baudrate; +#else + int baud = (int)BSP_CONSOLE_BAUD; +#endif + int databits = (int)MCF548X_PSC_MR_BC_8; + int parity = (int)MCF548X_PSC_MR_PM_NONE; + int stopbits = (int)MCF548X_PSC_MR_SB_STOP_BITS_1; + int hwflow = (int)1; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* check to see if input is valid */ + if ( t != (const struct termios *)0 ) + { + /* determine baud rate index */ + baud = GetBaud( t->c_ospeed ); + + /* determine data bits */ + switch ( t->c_cflag & CSIZE ) + { + case CS5: + databits = (int)MCF548X_PSC_MR_BC_5; + break; + case CS6: + databits = (int)MCF548X_PSC_MR_BC_6; + break; + case CS7: + databits = (int)MCF548X_PSC_MR_BC_7; + break; + case CS8: + databits = (int)MCF548X_PSC_MR_BC_8; + break; + } + + /* determine if parity is enabled */ + if ( t->c_cflag & PARENB ) + { + if ( t->c_cflag & PARODD ) + { + /* odd parity */ + parity = (int)MCF548X_PSC_MR_PM_ODD; + } + else + { + /* even parity */ + parity = (int)MCF548X_PSC_MR_PM_EVEN; + } + } + + /* determine stop bits */ + if ( t->c_cflag & CSTOPB ) + { + /* two stop bits */ + stopbits = (int)MCF548X_PSC_MR_SB_STOP_BITS_2; + } + + /* check to see if hardware flow control */ + if ( t->c_cflag & CRTSCTS ) + { + hwflow = 1; + } + } + + /* check to see if values have changed */ + if ( ( baud != info->baud ) || + ( databits != info->databits ) || + ( parity != info->parity ) || + ( stopbits != info->stopbits ) || + ( hwflow != info->hwflow ) ) + { + + /* call function to set values */ + IntUartSet(minor, baud, databits, parity, stopbits, hwflow); + } + +return RTEMS_SUCCESSFUL; + +} + +/*************************************************************************** + Function : IntUartInterruptHandler + + Description : This is the interrupt handler for the internal uart. It + determines which channel caused the interrupt before queueing any received + chars and dequeueing chars waiting for transmission. + ***************************************************************************/ +static rtems_isr +IntUartInterruptHandler(rtems_vector_number v) +{ + unsigned int chan = v - UART_INTC0_IRQ_VECTOR(0); + struct IntUartInfoStruct *info = &IntUartInfo[chan]; + + /* check to see if received data */ + if ( MCF548X_PSC_ISR(chan) & MCF548X_PSC_ISR_RXRDY_FU ) + { + /* read data and put into the receive buffer */ + while ( MCF548X_PSC_SR(chan) & MCF548X_PSC_SR_RXRDY ) + { + + /* put data in rx buffer */ + info->rx_buffer[info->rx_in] = *((volatile uint8_t *)&MCF548X_PSC_RB(chan)); + + /* check for errors */ + if ( MCF548X_PSC_SR(chan) & MCF548X_PSC_SR_ERROR ) + { + /* clear the error */ + MCF548X_PSC_CR(chan) = MCF548X_PSC_CR_RESET_ERROR; + } + + /* update buffer values */ + info->rx_in++; + + if ( info->rx_in >= RX_BUFFER_SIZE ) + { + info->rx_in = 0; + } + } + /* Make sure the port has been opened */ + if ( info->ttyp ) + { + + /* check to see if task driven */ + if ( info->iomode == TERMIOS_TASK_DRIVEN ) + { + /* notify rx task that rx buffer has data */ + rtems_termios_rxirq_occured(info->ttyp); + } + else + { + /* Push up the received data */ + rtems_termios_enqueue_raw_characters(info->ttyp, info->rx_buffer, info->rx_in); + info->rx_in = 0; + } + } + } + + /* check to see if data needs to be transmitted */ + if ( ( info->imr & MCF548X_PSC_IMR_TXRDY ) && + ( MCF548X_PSC_ISR(chan) & MCF548X_PSC_ISR_TXRDY ) ) + { + + /* disable tx interrupts */ + info->imr &= ~MCF548X_PSC_IMR_TXRDY; + MCF548X_PSC_IMR(chan) = info->imr; + + /* tell upper level that character has been sent */ + if ( info->ttyp ) + rtems_termios_dequeue_characters(info->ttyp, 1); + } + +} + +/*************************************************************************** + Function : IntUartInitialize + + Description : This initialises the internal uart hardware for all + internal uarts. If the internal uart is to be interrupt driven then the + interrupt vectors are hooked. + ***************************************************************************/ +static void +IntUartInitialize(void) +{ + unsigned int chan; + struct IntUartInfoStruct *info; + rtems_isr_entry old_handler; + + for ( chan = 0; chan < MAX_UART_INFO; chan++ ) + { + info = &IntUartInfo[chan]; + + info->ttyp = NULL; + info->rx_in = 0; + info->rx_out = 0; + info->baud = -1; + info->databits = -1; + info->parity = -1; + info->stopbits = -1; + info->hwflow = -1; + + MCF548X_PSC_ACR(chan) = 0; + MCF548X_PSC_IMR(chan) = 0; + if ( info->iomode != TERMIOS_POLLED ) + { + rtems_interrupt_catch (IntUartInterruptHandler, + UART_INTC0_IRQ_VECTOR(chan), + &old_handler); + } + + /* set uart default values */ + IntUartSetAttributes(chan, NULL); + + /* unmask interrupt */ + bsp_interrupt_vector_enable(MCF548X_IRQ_PSC(chan)); + } /* of chan loop */ + + BSP_output_char = psc_output_char; +} /* IntUartInitialise */ + +/*************************************************************************** + Function : IntUartInterruptWrite + + Description : This writes a single character to the appropriate uart + channel. This is either called during an interrupt or in the user's task + to initiate a transmit sequence. Calling this routine enables Tx + interrupts. + ***************************************************************************/ +static ssize_t +IntUartInterruptWrite (int minor, const char *buf, size_t len) +{ + if (len > 0) { + /* write out character */ + *(volatile uint8_t *)(&MCF548X_PSC_TB(minor)) = *buf; + + /* enable tx interrupt */ + IntUartInfo[minor].imr |= MCF548X_PSC_IMR_TXRDY; + MCF548X_PSC_IMR(minor) = IntUartInfo[minor].imr; + } + + return 0; +} + +/*************************************************************************** + Function : IntUartInterruptOpen + + Description : This enables interrupts when the tty is opened. + ***************************************************************************/ +static int +IntUartInterruptOpen(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* enable the uart */ + MCF548X_PSC_CR(minor) = (MCF548X_PSC_CR_TX_ENABLED | MCF548X_PSC_CR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if ( info->iomode != TERMIOS_POLLED ) + { + /* enable rx interrupts */ + info->imr |= MCF548X_PSC_IMR_RXRDY_FU; + MCF548X_PSC_IMR(minor) = info->imr; + } + + /* check to see if doing hardware flow control */ + if ( info->hwflow ) + { + /* assert the RTS line */ + MCF548X_PSC_OPSET(minor) = MCF548X_PSC_OPSET_RTS; + } + + return 0; +} + + +/*************************************************************************** + Function : IntUartInterruptClose + + Description : This disables interrupts when the tty is closed. + ***************************************************************************/ +static int +IntUartInterruptClose(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* disable the interrupts and the uart */ + MCF548X_PSC_IMR(minor) = 0; + MCF548X_PSC_CR(minor) = (MCF548X_PSC_CR_TX_ENABLED | MCF548X_PSC_CR_RX_ENABLED); + + /* reset values */ + info->ttyp = NULL; + info->imr = 0; + info->rx_in = 0; + info->rx_out = 0; + + return 0; +} + +/*************************************************************************** + Function : IntUartTaskRead + + Description : This reads all available characters from the internal uart + and places them into the termios buffer. The rx interrupts will be + re-enabled after all data has been read. + ***************************************************************************/ +static int +IntUartTaskRead(int minor) +{ + char buffer[RX_BUFFER_SIZE]; + int count; + int rx_in; + int index = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* determine number of values to copy out */ + rx_in = info->rx_in; + if ( info->rx_out <= rx_in ) + { + count = rx_in - info->rx_out; + } + else + { + count = (RX_BUFFER_SIZE - info->rx_out) + rx_in; + } + + /* copy data into local buffer from rx buffer */ + while ( ( index < count ) && ( index < RX_BUFFER_SIZE ) ) + { + /* copy data byte */ + buffer[index] = info->rx_buffer[info->rx_out]; + index++; + + /* increment rx buffer values */ + info->rx_out++; + if ( info->rx_out >= RX_BUFFER_SIZE ) + { + info->rx_out = 0; + } + } + + /* check to see if buffer is not empty */ + if ( count > 0 ) + { + /* set characters into termios buffer */ + rtems_termios_enqueue_raw_characters(info->ttyp, buffer, count); + } + + return EOF; +} + + +/*************************************************************************** + Function : IntUartPollRead + + Description : This reads a character from the internal uart. It returns + to the caller without blocking if not character is waiting. + ***************************************************************************/ +static int +IntUartPollRead (int minor) +{ +if (!((MCF548X_PSC_SR(minor) & MCF548X_PSC_SR_RXRDY))) + return(-1); + + return *((uint8_t *)&MCF548X_PSC_RB(minor)); +} + + +/*************************************************************************** + Function : IntUartPollWrite + + Description : This writes out each character in the buffer to the + appropriate internal uart channel waiting till each one is sucessfully + transmitted. + ***************************************************************************/ +static ssize_t +IntUartPollWrite (int minor, const char *buf, size_t len) +{ + size_t retval = len; +/* loop over buffer */ + while ( len-- ) + { + /* block until we can transmit */ + while (!((MCF548X_PSC_SR(minor) & MCF548X_PSC_SR_TXRDY))) + continue; + /* transmit data byte */ + *((uint8_t *)&MCF548X_PSC_TB(minor)) = *buf++; + } + return retval; +} + +/*************************************************************************** + Function : console_initialize + + Description : This initialises termios, both sets of uart hardware before + registering /dev/tty devices for each channel and the system /dev/console. + ***************************************************************************/ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg ) +{ + rtems_status_code status; + + + /* Set up TERMIOS */ + rtems_termios_initialize (); + + /* set io modes for the different channels and initialize device */ + IntUartInfo[minor].iomode = TERMIOS_IRQ_DRIVEN; //TERMIOS_POLLED; + IntUartInitialize(); + + /* Register the console port */ + status = rtems_io_register_name ("/dev/console", major, CONSOLE_PORT); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + + /* Register the other port */ + if ( CONSOLE_PORT != 0 ) + { + status = rtems_io_register_name ("/dev/tty00", major, 0); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + } + if ( CONSOLE_PORT != 1 ) + { + status = rtems_io_register_name ("/dev/tty01", major, 1); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + } + + return(RTEMS_SUCCESSFUL); +} + +/*************************************************************************** + Function : console_open + + Description : This actually opens the device depending on the minor + number set during initialisation. The device specific access routines are + passed to termios when the devices is opened depending on whether it is + polled or not. + ***************************************************************************/ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + rtems_status_code status = RTEMS_INVALID_NUMBER; + rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *)arg; + struct IntUartInfoStruct *info; + + static const rtems_termios_callbacks IntUartPollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + IntUartPollRead, /* pollRead */ + IntUartPollWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* mode */ + }; + static const rtems_termios_callbacks IntUartIntrCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + NULL, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* mode */ + }; + + static const rtems_termios_callbacks IntUartTaskCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + IntUartTaskRead, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_TASK_DRIVEN /* mode */ + }; + + /* open the port depending on the minor device number */ + if ( ( minor >= 0 ) && ( minor < MAX_UART_INFO ) ) + { + info = &IntUartInfo[minor]; + switch ( info->iomode ) + { + case TERMIOS_POLLED: + status = rtems_termios_open(major, minor, arg, &IntUartPollCallbacks); + break; + case TERMIOS_IRQ_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartIntrCallbacks); + info->ttyp = args->iop->data1; + break; + case TERMIOS_TASK_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartTaskCallbacks); + info->ttyp = args->iop->data1; + break; + } + } + + return( status ); +} + +/*************************************************************************** + Function : console_close + + Description : This closes the device via termios + ***************************************************************************/ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return(rtems_termios_close (arg)); +} + +/*************************************************************************** + Function : console_read + + Description : Read from the device via termios + ***************************************************************************/ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return(rtems_termios_read (arg)); +} + +/*************************************************************************** + Function : console_write + + Description : Write to the device via termios + ***************************************************************************/ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return(rtems_termios_write (arg)); +} + +/*************************************************************************** + Function : console_ioctl + + Description : Pass the IOCtl call to termios + ***************************************************************************/ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return( rtems_termios_ioctl (arg) ); +} diff --git a/bsps/m68k/mcf5206elite/console/console.c b/bsps/m68k/mcf5206elite/console/console.c new file mode 100644 index 0000000000..bbf343d0f3 --- /dev/null +++ b/bsps/m68k/mcf5206elite/console/console.c @@ -0,0 +1,431 @@ +/* + * Console driver for Motorola MCF5206E UART modules + */ + +/* + * Copyright (C) 2000 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov <vvv@oktet.ru> + * + * COPYRIGHT (c) 1989-1998. + * 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.org/license/LICENSE. + */ + +#include <termios.h> +#include <bsp.h> +#include <rtems/libio.h> +#include <rtems/console.h> +#include "mcf5206/mcf5206e.h" +#include "mcf5206/mcfuart.h" + +/* Descriptor structures for two on-chip UART channels */ +static mcfuart uart[2]; + +/* Console operations mode: + * 0 - raw (non-termios) polled input/output + * 1 - termios-based polled input/output + * 2 - termios-based interrupt-driven input/output + */ +int console_mode = 2; +#define CONSOLE_MODE_RAW (0) +#define CONSOLE_MODE_POLL (1) +#define CONSOLE_MODE_INT (2) + +/* Wrapper functions for MCF UART generic driver */ + +/* console_poll_read -- + * wrapper for poll read function + * + * PARAMETERS: + * minor - minor device number + * + * RETURNS: + * character code readed from UART, or -1 if there is no characters + * available + */ +static int +console_poll_read(int minor) +{ + return mcfuart_poll_read(&uart[minor]); +} + +/* console_interrupt_write -- + * wrapper for interrupt write function + * + * PARAMETERS: + * minor - minor device number + * buf - output buffer + * len - output buffer length + * + * RETURNS: + * result code + */ +static ssize_t +console_interrupt_write(int minor, const char *buf, size_t len) +{ + return mcfuart_interrupt_write(&uart[minor], buf, len); +} + +/* console_poll_write -- + * wrapper for polling mode write function + * + * PARAMETERS: + * minor - minor device number + * buf - output buffer + * len - output buffer length + * + * RETURNS: + * result code + */ +static ssize_t +console_poll_write(int minor, const char *buf, size_t len) +{ + return mcfuart_poll_write(&uart[minor], buf, len); +} + +/* console_set_attributes -- + * wrapper for hardware-dependent termios attributes setting + * + * PARAMETERS: + * minor - minor device number + * t - pointer to the termios structure + * + * RETURNS: + * result code + */ +static int +console_set_attributes(int minor, const struct termios *t) +{ + return mcfuart_set_attributes(&uart[minor], t); +} + +/* console_stop_remote_tx -- + * wrapper for stopping data flow from remote party. + * + * PARAMETERS: + * minor - minor device number + * + * RETURNS: + * result code + */ +static int +console_stop_remote_tx(int minor) +{ + if (minor < sizeof(uart)/sizeof(uart[0])) + return mcfuart_stop_remote_tx(&uart[minor]); + else + return RTEMS_INVALID_NUMBER; +} + +/* console_start_remote_tx -- + * wrapper for resuming data flow from remote party. + * + * PARAMETERS: + * minor - minor device number + * + */ +static int +console_start_remote_tx(int minor) +{ + if (minor < sizeof(uart)/sizeof(uart[0])) + return mcfuart_start_remote_tx(&uart[minor]); + else + return RTEMS_INVALID_NUMBER; +} + +/* console_first_open -- + * wrapper for UART controller initialization functions + * + * PARAMETERS: + * major - major device number + * minor - minor device number + * arg - libio device open argument + * + * RETURNS: + * error code + */ +static int +console_first_open(int major, int minor, void *arg) +{ + rtems_libio_open_close_args_t *args = arg; + rtems_status_code sc; + uint8_t intvec; + + switch (minor) { + case 0: intvec = BSP_INTVEC_UART1; break; + case 1: intvec = BSP_INTVEC_UART2; break; + default: + return RTEMS_INVALID_NUMBER; + } + + if (console_mode != CONSOLE_MODE_INT) { + intvec = 0; + } + + sc = mcfuart_init( + &uart[minor], /* uart */ + args->iop->data1, /* tty */ + intvec, /* interrupt vector number */ + minor+1); + + if (sc == RTEMS_SUCCESSFUL) + sc = mcfuart_reset(&uart[minor]); + + return sc; +} + +/* console_last_close -- + * wrapper for UART controller close function + * + * PARAMETERS: + * major - major device number + * minor - minor device number + * arg - libio device close argument + * + * RETURNS: + * error code + */ +static int +console_last_close(int major, int minor, void *arg) +{ + return mcfuart_disable(&uart[minor]); +} + +/* console_initialize -- + * This routine initializes the console IO drivers and register devices + * in RTEMS I/O system. + * + * PARAMETERS: + * major - major console device number + * minor - minor console device number (not used) + * arg - device initialize argument + * + * RETURNS: + * RTEMS error code (RTEMS_SUCCESSFUL if device initialized successfuly) + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + rtems_status_code status; + + /* + * Set up TERMIOS + */ + if (console_mode != CONSOLE_MODE_RAW) + rtems_termios_initialize (); + + /* + * Register the devices + */ + status = rtems_io_register_name ("/dev/console", major, 0); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/aux", major, 1); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + if (console_mode == CONSOLE_MODE_RAW) { + rtems_status_code sc; + sc = mcfuart_init(&uart[0], /* uart */ + NULL, /* tty */ + 0, /* interrupt vector number */ + 1); /* UART channel number */ + + if (sc == RTEMS_SUCCESSFUL) + sc = mcfuart_reset(&uart[0]); + + sc = mcfuart_init(&uart[1], /* uart */ + NULL, /* tty */ + 0, /* interrupt vector number */ + 2); /* UART channel number */ + + if (sc == RTEMS_SUCCESSFUL) + sc = mcfuart_reset(&uart[1]); + return sc; + } + + return RTEMS_SUCCESSFUL; +} + +/* console_open -- + * Open console device driver. Pass appropriate termios callback + * functions to termios library. + * + * PARAMETERS: + * major - major device number for console devices + * minor - minor device number for console + * arg - device opening argument + * + * RETURNS: + * RTEMS error code + */ +rtems_device_driver +console_open(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + static const rtems_termios_callbacks intr_callbacks = { + console_first_open, /* firstOpen */ + console_last_close, /* lastClose */ + NULL, /* pollRead */ + console_interrupt_write, /* write */ + console_set_attributes, /* setAttributes */ + console_stop_remote_tx, /* stopRemoteTx */ + console_start_remote_tx, /* startRemoteTx */ + 1 /* outputUsesInterrupts */ + }; + static const rtems_termios_callbacks poll_callbacks = { + console_first_open, /* firstOpen */ + console_last_close, /* lastClose */ + console_poll_read, /* pollRead */ + console_poll_write, /* write */ + console_set_attributes, /* setAttributes */ + console_stop_remote_tx, /* stopRemoteTx */ + console_start_remote_tx, /* startRemoteTx */ + 0 /* outputUsesInterrupts */ + }; + + switch (console_mode) { + case CONSOLE_MODE_RAW: + return RTEMS_SUCCESSFUL; + + case CONSOLE_MODE_INT: + return rtems_termios_open(major, minor, arg, &intr_callbacks); + + case CONSOLE_MODE_POLL: + return rtems_termios_open(major, minor, arg, &poll_callbacks); + + default: + rtems_fatal_error_occurred(0xC07A1310); + } + return RTEMS_INTERNAL_ERROR; +} + +/* console_close -- + * Close console device. + * + * PARAMETERS: + * major - major device number for console devices + * minor - minor device number for console + * arg - device close argument + * + * RETURNS: + * RTEMS error code + */ +rtems_device_driver +console_close(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + if (console_mode != CONSOLE_MODE_RAW) + return rtems_termios_close (arg); + else + return RTEMS_SUCCESSFUL; +} + +/* console_read -- + * Read from the console device + * + * PARAMETERS: + * major - major device number for console devices + * minor - minor device number for console + * arg - device read argument + * + * RETURNS: + * RTEMS error code + */ +rtems_device_driver +console_read(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + if (console_mode != CONSOLE_MODE_RAW) { + return rtems_termios_read (arg); + } else { + rtems_libio_rw_args_t *argp = arg; + char *buf = argp->buffer; + int count = argp->count; + int n = 0; + int c; + + while (n < count) { + do { + c = mcfuart_poll_read(&uart[minor]); + } while (c == -1); + if (c == '\r') + c = '\n'; + *(buf++) = c; + n++; + if (c == '\n') + break; + } + argp->bytes_moved = n; + return RTEMS_SUCCESSFUL; + } +} + +/* console_write -- + * Write to the console device + * + * PARAMETERS: + * major - major device number for console devices + * minor - minor device number for console + * arg - device write argument + * + * RETURNS: + * RTEMS error code + */ +rtems_device_driver +console_write(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if (console_mode != CONSOLE_MODE_RAW) { + return rtems_termios_write (arg); + } else { + rtems_libio_rw_args_t *argp = arg; + char cr = '\r'; + char *buf = argp->buffer; + int count = argp->count; + int i; + + for (i = 0; i < count; i++) { + if (*buf == '\n') + mcfuart_poll_write(&uart[minor], &cr, 1); + mcfuart_poll_write(&uart[minor], buf, 1); + buf++; + } + argp->bytes_moved = count; + return RTEMS_SUCCESSFUL; + } +} + +/* console_control -- + * Handle console device I/O control (IOCTL) + * + * PARAMETERS: + * major - major device number for console devices + * minor - minor device number for console + * arg - device ioctl argument + * + * RETURNS: + * RTEMS error code + */ +rtems_device_driver +console_control(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + if (console_mode != CONSOLE_MODE_RAW) { + return rtems_termios_ioctl (arg); + } else { + return RTEMS_SUCCESSFUL; + } +} diff --git a/bsps/m68k/mcf52235/console/console.c b/bsps/m68k/mcf52235/console/console.c new file mode 100644 index 0000000000..c2b6e36bfa --- /dev/null +++ b/bsps/m68k/mcf52235/console/console.c @@ -0,0 +1,656 @@ + /* + * Multi UART console serial I/O. + * + * TO DO: Add DMA input/output + */ + +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> +#include <malloc.h> + +#include <rtems/libio.h> +#include <rtems/console.h> +#include <rtems/termiostypes.h> +#include <bsp.h> + +#include <rtems/bspIo.h> + +#define UART_INTC0_IRQ_VECTOR(x) (64+13+(x)) + +#define MCF_UART_USR_ERROR ( MCF_UART_USR_RB | \ + MCF_UART_USR_FE | \ + MCF_UART_USR_PE | \ + MCF_UART_USR_OE ) + +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len); +static ssize_t IntUartInterruptWrite(int minor, const char *buf, size_t len); + +#define MAX_UART_INFO 3 +#define RX_BUFFER_SIZE 512 + +struct IntUartInfoStruct +{ + int iomode; + volatile int uimr; + int baud; + int databits; + int parity; + int stopbits; + int hwflow; + int rx_in; + int rx_out; + char rx_buffer[RX_BUFFER_SIZE]; + void *ttyp; +}; + +struct IntUartInfoStruct IntUartInfo[MAX_UART_INFO]; + +/*************************************************************************** + Function : IntUartSet + + Description : This updates the hardware UART settings. + ***************************************************************************/ +static void +IntUartSet(int minor, int baud, int databits, int parity, int stopbits, + int hwflow) +{ + int divisor; + uint32_t clock_speed; + uint8_t umr1 = 0; + uint8_t umr2 = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + int level; + + rtems_interrupt_disable(level); + + /* disable interrupts, clear RTS line, and disable the UARTS */ + MCF_UART_UIMR(minor) = 0; + MCF_UART_UOP0(minor) = 1; + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_DISABLED | MCF_UART_UCR_RX_DISABLED); + + /* save the current values */ + info->uimr = 0; + info->baud = baud; + info->databits = databits; + info->parity = parity; + info->stopbits = stopbits; + info->hwflow = hwflow; + + clock_speed = bsp_get_CPU_clock_speed(); + /* determine the baud divisor value */ + divisor = ((clock_speed) / (32 * baud)); + if (divisor < 2) + divisor = 2; + + /* check to see if doing hardware flow control */ + if (hwflow) { + /* set hardware flow options */ + umr1 |= MCF_UART_UMR_RXRTS; + umr2 |= MCF_UART_UMR_TXCTS; + } + + /* determine the new umr values */ + umr1 |= (parity | databits); + umr2 |= (stopbits); + + /* reset the uart */ + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_ERROR; + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_RX; + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_TX; + + /* reset the uart mode register and update values */ + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_MR; + MCF_UART_UMR(minor) = umr1; + MCF_UART_UMR(minor) = umr2; + + /* set the baud rate values */ + MCF_UART_UCSR(minor) = + (MCF_UART_UCSR_RCS_SYS_CLK | MCF_UART_UCSR_TCS_SYS_CLK); + MCF_UART_UBG1(minor) = (divisor & 0xff00) >> 8; + MCF_UART_UBG2(minor) = (divisor & 0x00ff); + + /* enable the uart */ + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_ENABLED | MCF_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if (info->iomode != TERMIOS_POLLED) { + /* enable rx interrupts */ + info->uimr |= MCF_UART_UIMR_RXRDY_FU; + MCF_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if (hwflow) { + /* assert the RTS line */ + MCF_UART_UOP1(minor) = 1; + } + + rtems_interrupt_enable(level); + +} + +/*************************************************************************** + Function : IntUartSetAttributes + + Description : This provides the hardware-dependent portion of tcsetattr(). + value and sets it. At the moment this just sets the baud rate. + + Note: The highest baudrate is 115200 as this stays within + an error of +/- 5% at 25MHz processor clock + ***************************************************************************/ +static int IntUartSetAttributes(int minor, const struct termios *t) +{ + /* set default index values */ + int baud = (int) 19200; + int databits = (int) MCF_UART_UMR_BC_8; + int parity = (int) MCF_UART_UMR_PM_NONE; + int stopbits = (int) MCF_UART_UMR_SB_STOP_BITS_1; + int hwflow = (int) 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* check to see if input is valid */ + if (t != (const struct termios *) 0) { + /* determine baud rate index */ + baud = rtems_termios_baud_to_number(t->c_ospeed); + + /* determine data bits */ + switch (t->c_cflag & CSIZE) { + case CS5: + databits = (int) MCF_UART_UMR_BC_5; + break; + case CS6: + databits = (int) MCF_UART_UMR_BC_6; + break; + case CS7: + databits = (int) MCF_UART_UMR_BC_7; + break; + case CS8: + databits = (int) MCF_UART_UMR_BC_8; + break; + } + + /* determine if parity is enabled */ + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) { + /* odd parity */ + parity = (int) MCF_UART_UMR_PM_ODD; + } else { + /* even parity */ + parity = (int) MCF_UART_UMR_PM_EVEN; + } + } + + /* determine stop bits */ + if (t->c_cflag & CSTOPB) { + /* two stop bits */ + stopbits = (int) MCF_UART_UMR_SB_STOP_BITS_2; + } + + /* check to see if hardware flow control */ + if (t->c_cflag & CRTSCTS) { + hwflow = 1; + } + } + + /* check to see if values have changed */ + if ((baud != info->baud) || + (databits != info->databits) || + (parity != info->parity) || + (stopbits != info->stopbits) || (hwflow != info->hwflow)) { + + /* call function to set values */ + IntUartSet(minor, baud, databits, parity, stopbits, hwflow); + } + + return (RTEMS_SUCCESSFUL); + +} + +/*************************************************************************** + Function : IntUartInterruptHandler + + Description : This is the interrupt handler for the internal uart. It + determines which channel caused the interrupt before queueing any received + chars and dequeueing chars waiting for transmission. + ***************************************************************************/ +static rtems_isr IntUartInterruptHandler(rtems_vector_number v) +{ + unsigned int chan = v - UART_INTC0_IRQ_VECTOR(0); + struct IntUartInfoStruct *info = &IntUartInfo[chan]; + + /* check to see if received data */ + if (MCF_UART_UISR(chan) & MCF_UART_UISR_RXRDY_FU) { + /* read data and put into the receive buffer */ + while (MCF_UART_USR(chan) & MCF_UART_USR_RXRDY) { + + if (MCF_UART_USR(chan) & MCF_UART_USR_ERROR) { + /* clear the error */ + MCF_UART_UCR(chan) = MCF_UART_UCR_RESET_ERROR; + } + /* put data in rx buffer and check for errors */ + info->rx_buffer[info->rx_in] = MCF_UART_URB(chan); + + /* update buffer values */ + info->rx_in++; + + if (info->rx_in >= RX_BUFFER_SIZE) { + info->rx_in = 0; + } + } + /* Make sure the port has been opened */ + if (info->ttyp) { + + /* check to see if task driven */ + if (info->iomode == TERMIOS_TASK_DRIVEN) { + /* notify rx task that rx buffer has data */ + rtems_termios_rxirq_occured(info->ttyp); + } else { + /* Push up the received data */ + rtems_termios_enqueue_raw_characters(info->ttyp, info->rx_buffer, + info->rx_in); + info->rx_in = 0; + } + } + } + + /* check to see if data needs to be transmitted */ + if ((info->uimr & MCF_UART_UIMR_TXRDY) && + (MCF_UART_UISR(chan) & MCF_UART_UISR_TXRDY)) { + + /* disable tx interrupts */ + info->uimr &= ~MCF_UART_UIMR_TXRDY; + MCF_UART_UIMR(chan) = info->uimr; + + /* tell upper level that character has been sent */ + if (info->ttyp) + rtems_termios_dequeue_characters(info->ttyp, 1); + } +} + +/*************************************************************************** + Function : IntUartInitialize + + Description : This initialises the internal uart hardware for all + internal uarts. If the internal uart is to be interrupt driven then the + interrupt vectors are hooked. + ***************************************************************************/ +static void IntUartInitialize(void) +{ + unsigned int chan; + struct IntUartInfoStruct *info; + rtems_isr_entry old_handler; + int level; + + for (chan = 0; chan < MAX_UART_INFO; chan++) { + info = &IntUartInfo[chan]; + + info->ttyp = NULL; + info->rx_in = 0; + info->rx_out = 0; + info->baud = -1; + info->databits = -1; + info->parity = -1; + info->stopbits = -1; + info->hwflow = -1; + info->iomode = TERMIOS_POLLED; /*polled console io */ + + MCF_UART_UACR(chan) = 0; + MCF_UART_UIMR(chan) = 0; + if (info->iomode != TERMIOS_POLLED) { + rtems_interrupt_catch(IntUartInterruptHandler, + UART_INTC0_IRQ_VECTOR(chan), &old_handler); + } + + /* set uart default values */ + IntUartSetAttributes(chan, NULL); + + /* unmask interrupt */ + rtems_interrupt_disable(level); + switch (chan) { + case 0: + MCF_INTC0_ICR13 = MCF_INTC_ICR_IL(UART0_IRQ_LEVEL) | + MCF_INTC_ICR_IP(UART0_IRQ_PRIORITY); + MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_MASK13 | MCF_INTC_IMRL_MASKALL); + break; + + case 1: + MCF_INTC0_ICR14 = MCF_INTC_ICR_IL(UART1_IRQ_LEVEL) | + MCF_INTC_ICR_IP(UART1_IRQ_PRIORITY); + MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_MASK14 | MCF_INTC_IMRL_MASKALL); + break; + + case 2: + MCF_INTC0_ICR15 = MCF_INTC_ICR_IL(UART2_IRQ_LEVEL) | + MCF_INTC_ICR_IP(UART2_IRQ_PRIORITY); + MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_MASK15 | MCF_INTC_IMRL_MASKALL); + break; + } + rtems_interrupt_enable(level); + + } /* of chan loop */ + +} /* IntUartInitialise */ + +/*************************************************************************** + Function : IntUartInterruptWrite + + Description : This writes a single character to the appropriate uart + channel. This is either called during an interrupt or in the user's task + to initiate a transmit sequence. Calling this routine enables Tx + interrupts. + ***************************************************************************/ +static ssize_t IntUartInterruptWrite(int minor, const char *buf, size_t len) +{ + if (len > 0) { + /* write out character */ + MCF_UART_UTB(minor) = *buf; + + /* enable tx interrupt */ + IntUartInfo[minor].uimr |= MCF_UART_UIMR_TXRDY; + MCF_UART_UIMR(minor) = IntUartInfo[minor].uimr; + } + + return (0); +} + +/*************************************************************************** + Function : IntUartInterruptOpen + + Description : This enables interrupts when the tty is opened. + ***************************************************************************/ +static int IntUartInterruptOpen(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* enable the uart */ + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_ENABLED | MCF_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if (info->iomode != TERMIOS_POLLED) { + /* enable rx interrupts */ + info->uimr |= MCF_UART_UIMR_RXRDY_FU; + MCF_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if (info->hwflow) { + /* assert the RTS line */ + MCF_UART_UOP1(minor) = 1; + } + + return (0); +} + +/*************************************************************************** + Function : IntUartInterruptClose + + Description : This disables interrupts when the tty is closed. + ***************************************************************************/ +static int IntUartInterruptClose(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* disable the interrupts and the uart */ + MCF_UART_UIMR(minor) = 0; + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_DISABLED | MCF_UART_UCR_RX_DISABLED); + + /* reset values */ + info->ttyp = NULL; + info->uimr = 0; + info->rx_in = 0; + info->rx_out = 0; + + return (0); +} + +/*************************************************************************** + Function : IntUartTaskRead + + Description : This reads all available characters from the internal uart + and places them into the termios buffer. The rx interrupts will be + re-enabled after all data has been read. + ***************************************************************************/ +static int IntUartTaskRead(int minor) +{ + char buffer[RX_BUFFER_SIZE]; + int count; + int rx_in; + int index = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* determine number of values to copy out */ + rx_in = info->rx_in; + if (info->rx_out <= rx_in) { + count = rx_in - info->rx_out; + } else { + count = (RX_BUFFER_SIZE - info->rx_out) + rx_in; + } + + /* copy data into local buffer from rx buffer */ + while ((index < count) && (index < RX_BUFFER_SIZE)) { + /* copy data byte */ + buffer[index] = info->rx_buffer[info->rx_out]; + index++; + + /* increment rx buffer values */ + info->rx_out++; + if (info->rx_out >= RX_BUFFER_SIZE) { + info->rx_out = 0; + } + } + + /* check to see if buffer is not empty */ + if (count > 0) { + /* set characters into termios buffer */ + rtems_termios_enqueue_raw_characters(info->ttyp, buffer, count); + } + + return (EOF); +} + +/*************************************************************************** + Function : IntUartPollRead + + Description : This reads a character from the internal uart. It returns + to the caller without blocking if not character is waiting. + ***************************************************************************/ +static int IntUartPollRead(int minor) +{ + if ((MCF_UART_USR(minor) & MCF_UART_USR_RXRDY) == 0) + return (-1); + + return (MCF_UART_URB(minor)); +} + +/*************************************************************************** + Function : IntUartPollWrite + + Description : This writes out each character in the buffer to the + appropriate internal uart channel waiting till each one is sucessfully + transmitted. + ***************************************************************************/ +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len) +{ + size_t retval = len; + /* loop over buffer */ + while (len--) { + /* block until we can transmit */ + while ((MCF_UART_USR(minor) & MCF_UART_USR_TXRDY) == 0) + continue; + /* transmit data byte */ + MCF_UART_UTB(minor) = *buf++; + } + return retval; +} + +/*************************************************************************** + Function : console_initialize + + Description : This initialises termios, both sets of uart hardware before + registering /dev/tty devices for each channel and the system /dev/console. + ***************************************************************************/ +rtems_device_driver console_initialize(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + rtems_status_code status; + + /* Set up TERMIOS */ + rtems_termios_initialize(); + + /* set io modes for the different channels and initialize device */ + IntUartInfo[minor].iomode = TERMIOS_IRQ_DRIVEN; + IntUartInitialize(); + + /* Register the console port */ + status = rtems_io_register_name("/dev/console", major, CONSOLE_PORT); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + + /* Register the other port */ + if (CONSOLE_PORT != 0) { + status = rtems_io_register_name("/dev/tty00", major, 0); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + } + if (CONSOLE_PORT != 1) { + status = rtems_io_register_name("/dev/tty01", major, 1); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + } + + return (RTEMS_SUCCESSFUL); +} + +/*************************************************************************** + Function : console_open + + Description : This actually opens the device depending on the minor + number set during initialisation. The device specific access routines are + passed to termios when the devices is opened depending on whether it is + polled or not. + ***************************************************************************/ +rtems_device_driver console_open(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + rtems_status_code status = RTEMS_INVALID_NUMBER; + rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *) arg; + struct IntUartInfoStruct *info; + + static const rtems_termios_callbacks IntUartPollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + IntUartPollRead, /* pollRead */ + IntUartPollWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* mode */ + }; + static const rtems_termios_callbacks IntUartIntrCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + NULL, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* mode */ + }; + + static const rtems_termios_callbacks IntUartTaskCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + IntUartTaskRead, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_TASK_DRIVEN /* mode */ + }; + + /* open the port depending on the minor device number */ + if ((minor >= 0) && (minor < MAX_UART_INFO)) { + info = &IntUartInfo[minor]; + switch (info->iomode) { + case TERMIOS_POLLED: + status = rtems_termios_open(major, minor, arg, &IntUartPollCallbacks); + break; + case TERMIOS_IRQ_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartIntrCallbacks); + info->ttyp = args->iop->data1; + break; + case TERMIOS_TASK_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartTaskCallbacks); + info->ttyp = args->iop->data1; + break; + } + } + + if (status == RTEMS_SUCCESSFUL) { + /* + * Reset the default baudrate. + */ + struct termios term; + + if (tcgetattr(STDIN_FILENO, &term) >= 0) { + term.c_cflag &= ~(CSIZE); + term.c_cflag |= CS8; + term.c_ispeed = B19200; + term.c_ospeed = B19200; + tcsetattr(STDIN_FILENO, TCSANOW, &term); + } + } + + return (status); +} + +/*************************************************************************** + Function : console_close + + Description : This closes the device via termios + ***************************************************************************/ +rtems_device_driver console_close(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + return (rtems_termios_close(arg)); +} + +/****************** +********************************************************* + Function : console_read + + Description : Read from the device via termios + ***************************************************************************/ +rtems_device_driver console_read(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + return (rtems_termios_read(arg)); +} + +/*************************************************************************** + Function : console_write + + Description : Write to the device via termios + ***************************************************************************/ +rtems_device_driver console_write(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + return (rtems_termios_write(arg)); +} + +/*************************************************************************** + Function : console_ioctl + + Description : Pass the IOCtl call to termios + ***************************************************************************/ +rtems_device_driver console_control(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + return (rtems_termios_ioctl(arg)); +} diff --git a/bsps/m68k/mcf52235/console/debugio.c b/bsps/m68k/mcf52235/console/debugio.c new file mode 100644 index 0000000000..1fbf4b09d7 --- /dev/null +++ b/bsps/m68k/mcf52235/console/debugio.c @@ -0,0 +1,32 @@ + /* + * Multi UART console serial I/O. + * + * TO DO: Add DMA input/output + */ + +#include <stdio.h> +#include <fcntl.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <termios.h> +#include <bsp.h> +#include <malloc.h> + +#include <rtems/bspIo.h> + +static void _BSP_null_char(char c) +{ + int level; + + rtems_interrupt_disable(level); + while ((MCF_UART_USR(CONSOLE_PORT) & MCF_UART_USR_TXRDY) == 0) + continue; + MCF_UART_UTB(CONSOLE_PORT) = c; + while ((MCF_UART_USR(CONSOLE_PORT) & MCF_UART_USR_TXRDY) == 0) + continue; + rtems_interrupt_enable(level); +} + +BSP_output_char_function_type BSP_output_char = _BSP_null_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + diff --git a/bsps/m68k/mcf5225x/console/console.c b/bsps/m68k/mcf5225x/console/console.c new file mode 100644 index 0000000000..9e36e3945a --- /dev/null +++ b/bsps/m68k/mcf5225x/console/console.c @@ -0,0 +1,689 @@ +/* + * Multi UART console serial I/O. + * + * TO DO: Add DMA input/output + * + * 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 <stdio.h> +#include <fcntl.h> +#include <termios.h> +#include <malloc.h> + +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <rtems/console.h> +#include <rtems/bspIo.h> +#include <bsp.h> + +#define UART_INTC0_IRQ_VECTOR(x) (64+13+(x)) + +#define MCF_UART_USR_ERROR ( MCF_UART_USR_RB | \ + MCF_UART_USR_FE | \ + MCF_UART_USR_PE | \ + MCF_UART_USR_OE ) + +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len); +static ssize_t IntUartInterruptWrite(int minor, const char *buf, size_t len); + +#define MAX_UART_INFO 3 +#define RX_BUFFER_SIZE 512 + +struct IntUartInfoStruct +{ + int iomode; + volatile int uimr; + int baud; + int databits; + int parity; + int stopbits; + int hwflow; + int rx_in; + int rx_out; + char rx_buffer[RX_BUFFER_SIZE]; + void *ttyp; +}; + +struct IntUartInfoStruct IntUartInfo[MAX_UART_INFO]; + +/*************************************************************************** + Function : IntUartSet + + Description : This updates the hardware UART settings. + ***************************************************************************/ +static void +IntUartSet(int minor, int baud, int databits, int parity, int stopbits, + int hwflow) +{ + int divisor; + uint32_t clock_speed; + uint8_t umr1 = 0; + uint8_t umr2 = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + rtems_interrupt_level level=UART0_IRQ_LEVEL; + + rtems_interrupt_disable(level); + + /* disable interrupts, clear RTS line, and disable the UARTS */ + MCF_UART_UIMR(minor) = 0; + MCF_UART_UOP0(minor) = 1; + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_DISABLED | MCF_UART_UCR_RX_DISABLED); + + /* save the current values */ + info->uimr = 0; + info->baud = baud; + info->databits = databits; + info->parity = parity; + info->stopbits = stopbits; + info->hwflow = hwflow; + + clock_speed = bsp_get_CPU_clock_speed(); + /* determine the baud divisor value */ + divisor = ((clock_speed) / (32 * baud)); + if (divisor < 2) + divisor = 2; + + /* check to see if doing hardware flow control */ + if (hwflow) { + /* set hardware flow options */ + umr1 |= MCF_UART_UMR_RXRTS; + umr2 |= MCF_UART_UMR_TXCTS; + } + + /* determine the new umr values */ + umr1 |= (parity | databits); + +#if 1 /* TZN: maybe needed for santec bus modul handling */ + if (minor==STATIONS_PORT) + umr2 |= (stopbits) | 0x20; /* 0x20 ... set TXRTS just4testing */ + else + umr2 |= (stopbits); +#else + umr2 |= (stopbits); +#endif + + /* reset the uart */ + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_ERROR; + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_RX; + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_TX; + + /* reset the uart mode register and update values */ + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_MR; + MCF_UART_UMR(minor) = umr1; + MCF_UART_UMR(minor) = umr2; + + /* set the baud rate values */ + MCF_UART_UCSR(minor) = + (MCF_UART_UCSR_RCS_SYS_CLK | MCF_UART_UCSR_TCS_SYS_CLK); + MCF_UART_UBG1(minor) = (divisor & 0xff00) >> 8; + MCF_UART_UBG2(minor) = (divisor & 0x00ff); + + /* enable the uart */ + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_ENABLED | MCF_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if (info->iomode != TERMIOS_POLLED) { + /* enable rx interrupts */ + info->uimr |= MCF_UART_UIMR_RXRDY_FU; + MCF_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if (hwflow) { + /* assert the RTS line */ + MCF_UART_UOP1(minor) = 1; + } + + if (minor==STATIONS_PORT) //maybe needed for santec handling + MCF_UART_UOP0(minor) = 1; + + rtems_interrupt_enable(level); + +} + +/*************************************************************************** + Function : IntUartSetAttributes + + Description : This provides the hardware-dependent portion of tcsetattr(). + value and sets it. At the moment this just sets the baud rate. + + Note: The highest baudrate is 115200 as this stays within + an error of +/- 5% at 25MHz processor clock + ***************************************************************************/ +static int IntUartSetAttributes(int minor, const struct termios *t) +{ + /* set default index values */ + int baud = (int) 19200; + int databits = (int) MCF_UART_UMR_BC_8; + int parity = (int) MCF_UART_UMR_PM_NONE; + int stopbits = (int) MCF_UART_UMR_SB_STOP_BITS_1; + int hwflow = (int) 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* check to see if input is valid */ + if (t != (const struct termios *) 0) { + /* determine baud rate index */ + baud = rtems_termios_baud_to_number(t->c_ospeed); + + /* determine data bits */ + switch (t->c_cflag & CSIZE) { + case CS5: + databits = (int) MCF_UART_UMR_BC_5; + break; + case CS6: + databits = (int) MCF_UART_UMR_BC_6; + break; + case CS7: + databits = (int) MCF_UART_UMR_BC_7; + break; + case CS8: + databits = (int) MCF_UART_UMR_BC_8; + break; + } + + /* determine if parity is enabled */ + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) { + /* odd parity */ + parity = (int) MCF_UART_UMR_PM_ODD; + } else { + /* even parity */ + parity = (int) MCF_UART_UMR_PM_EVEN; + } + } + + /* determine stop bits */ + if (t->c_cflag & CSTOPB) { + /* two stop bits */ + stopbits = (int) MCF_UART_UMR_SB_STOP_BITS_2; + } + + /* check to see if hardware flow control */ + if (t->c_cflag & CRTSCTS) { + hwflow = 1; + } + } + + /* check to see if values have changed */ + if ((baud != info->baud) || + (databits != info->databits) || + (parity != info->parity) || + (stopbits != info->stopbits) || (hwflow != info->hwflow)) { + + /* call function to set values */ + IntUartSet(minor, baud, databits, parity, stopbits, hwflow); + } + + return (RTEMS_SUCCESSFUL); + +} + +/*************************************************************************** + Function : IntUartInterruptHandler + + Description : This is the interrupt handler for the internal uart. It + determines which channel caused the interrupt before queueing any received + chars and dequeueing chars waiting for transmission. + ***************************************************************************/ +static rtems_isr IntUartInterruptHandler(rtems_vector_number v) +{ + unsigned int chan = v - UART_INTC0_IRQ_VECTOR(0); + struct IntUartInfoStruct *info = &IntUartInfo[chan]; + + + /* check to see if received data */ + if (MCF_UART_UISR(chan) & MCF_UART_UISR_RXRDY_FU) { + +#if 0 /* TZN ... just4testing */ + if (MCF_GPIO_PORTTC&MCF_GPIO_PORTTC_PORTTC0) + MCF_GPIO_PORTTC &= ~MCF_GPIO_PORTTC_PORTTC0; + else + MCF_GPIO_PORTTC |= MCF_GPIO_PORTTC_PORTTC0; +#endif + + /* read data and put into the receive buffer */ + while (MCF_UART_USR(chan) & MCF_UART_USR_RXRDY) { + + if (MCF_UART_USR(chan) & MCF_UART_USR_ERROR) { + /* clear the error */ + MCF_UART_UCR(chan) = MCF_UART_UCR_RESET_ERROR; + } + /* put data in rx buffer and check for errors */ + info->rx_buffer[info->rx_in] = MCF_UART_URB(chan); + + /* update buffer values */ + info->rx_in++; + + if (info->rx_in >= RX_BUFFER_SIZE) { + info->rx_in = 0; + } + } + /* Make sure the port has been opened */ + if (info->ttyp) { + + /* check to see if task driven */ + if (info->iomode == TERMIOS_TASK_DRIVEN) { + /* notify rx task that rx buffer has data */ + rtems_termios_rxirq_occured(info->ttyp); + } else { + /* Push up the received data */ + rtems_termios_enqueue_raw_characters(info->ttyp, info->rx_buffer, + info->rx_in); + info->rx_in = 0; + } + } + } + + /* check to see if data needs to be transmitted */ + if ((info->uimr & MCF_UART_UIMR_TXRDY) && + (MCF_UART_UISR(chan) & MCF_UART_UISR_TXRDY)) { + + /* disable tx interrupts */ + info->uimr &= ~MCF_UART_UIMR_TXRDY; + MCF_UART_UIMR(chan) = info->uimr; + + /* tell upper level that character has been sent */ + if (info->ttyp) + rtems_termios_dequeue_characters(info->ttyp, 1); + } +} + +/*************************************************************************** + Function : IntUartInitialize + + Description : This initialises the internal uart hardware for all + internal uarts. If the internal uart is to be interrupt driven then the + interrupt vectors are hooked. + ***************************************************************************/ +static void IntUartInitialize(void) +{ + unsigned int chan; + struct IntUartInfoStruct *info; + rtems_isr_entry old_handler; + rtems_interrupt_level level=UART0_IRQ_LEVEL; + + for (chan = 0; chan < MAX_UART_INFO; chan++) { + info = &IntUartInfo[chan]; + + info->ttyp = NULL; + info->rx_in = 0; + info->rx_out = 0; + info->baud = -1; + info->databits = -1; + info->parity = -1; + info->stopbits = -1; + info->hwflow = -1; + info->iomode = TERMIOS_IRQ_DRIVEN; /*TZN, irq driven console io */ + //info->iomode = TERMIOS_POLLED; /*TZN, just4testint, use polling mode for all UARTS */ + + MCF_UART_UACR(chan) = 0; + MCF_UART_UIMR(chan) = 0; + if (info->iomode != TERMIOS_POLLED) { + rtems_interrupt_catch(IntUartInterruptHandler, + UART_INTC0_IRQ_VECTOR(chan), &old_handler); + } + + /* set uart default values */ + IntUartSetAttributes(chan, NULL); + + /* unmask interrupt */ + rtems_interrupt_disable(level); + switch (chan) { + case 0: + MCF_INTC0_ICR13 = MCF_INTC_ICR_IL(UART0_IRQ_LEVEL) | + MCF_INTC_ICR_IP(UART0_IRQ_PRIORITY); + MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_MASK13 | MCF_INTC_IMRL_MASKALL); + break; + + case 1: + MCF_INTC0_ICR14 = MCF_INTC_ICR_IL(UART1_IRQ_LEVEL) | + MCF_INTC_ICR_IP(UART1_IRQ_PRIORITY); + MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_MASK14 | MCF_INTC_IMRL_MASKALL); + break; + + case 2: + MCF_INTC0_ICR15 = MCF_INTC_ICR_IL(UART2_IRQ_LEVEL) | + MCF_INTC_ICR_IP(UART2_IRQ_PRIORITY); + MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_MASK15 | MCF_INTC_IMRL_MASKALL); + break; + } + rtems_interrupt_enable(level); + + } /* of chan loop */ + +} /* IntUartInitialise */ + +/*************************************************************************** + Function : IntUartInterruptWrite + + Description : This writes a single character to the appropriate uart + channel. This is either called during an interrupt or in the user's task + to initiate a transmit sequence. Calling this routine enables Tx + interrupts. + ***************************************************************************/ +static ssize_t IntUartInterruptWrite(int minor, const char *buf, size_t len) +{ + if (len > 0) { + /* write out character */ + MCF_UART_UTB(minor) = *buf; + + /* enable tx interrupt */ + IntUartInfo[minor].uimr |= MCF_UART_UIMR_TXRDY; + MCF_UART_UIMR(minor) = IntUartInfo[minor].uimr; + } + + return (0); +} + +/*************************************************************************** + Function : IntUartInterruptOpen + + Description : This enables interrupts when the tty is opened. + ***************************************************************************/ +static int IntUartInterruptOpen(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* enable the uart */ + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_ENABLED | MCF_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if (info->iomode != TERMIOS_POLLED) { + /* enable rx interrupts */ + info->uimr |= MCF_UART_UIMR_RXRDY_FU; + MCF_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if (info->hwflow) { + /* assert the RTS line */ + MCF_UART_UOP1(minor) = 1; + } + + return (0); +} + +/*************************************************************************** + Function : IntUartInterruptClose + + Description : This disables interrupts when the tty is closed. + ***************************************************************************/ +static int IntUartInterruptClose(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* disable the interrupts and the uart */ + MCF_UART_UIMR(minor) = 0; + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_DISABLED | MCF_UART_UCR_RX_DISABLED); + + /* reset values */ + info->ttyp = NULL; + info->uimr = 0; + info->rx_in = 0; + info->rx_out = 0; + + return (0); +} + +/*************************************************************************** + Function : IntUartTaskRead + + Description : This reads all available characters from the internal uart + and places them into the termios buffer. The rx interrupts will be + re-enabled after all data has been read. + ***************************************************************************/ +static int IntUartTaskRead(int minor) +{ + char buffer[RX_BUFFER_SIZE]; + int count; + int rx_in; + int index = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* determine number of values to copy out */ + rx_in = info->rx_in; + if (info->rx_out <= rx_in) { + count = rx_in - info->rx_out; + } else { + count = (RX_BUFFER_SIZE - info->rx_out) + rx_in; + } + + /* copy data into local buffer from rx buffer */ + while ((index < count) && (index < RX_BUFFER_SIZE)) { + /* copy data byte */ + buffer[index] = info->rx_buffer[info->rx_out]; + index++; + + /* increment rx buffer values */ + info->rx_out++; + if (info->rx_out >= RX_BUFFER_SIZE) { + info->rx_out = 0; + } + } + + /* check to see if buffer is not empty */ + if (count > 0) { + /* set characters into termios buffer */ + rtems_termios_enqueue_raw_characters(info->ttyp, buffer, count); + } + + return (EOF); +} + +/*************************************************************************** + Function : IntUartPollRead + + Description : This reads a character from the internal uart. It returns + to the caller without blocking if not character is waiting. + ***************************************************************************/ +static +int IntUartPollRead(int minor) +{ + if ((MCF_UART_USR(minor) & MCF_UART_USR_RXRDY) == 0) + return (-1); + + return (MCF_UART_URB(minor)); +} + +/*************************************************************************** + Function : IntUartPollWrite + + Description : This writes out each character in the buffer to the + appropriate internal uart channel waiting till each one is sucessfully + transmitted. + ***************************************************************************/ +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len) +{ + /* loop over buffer */ + while (len--) { + /* block until we can transmit */ + while ((MCF_UART_USR(minor) & MCF_UART_USR_TXRDY) == 0) + continue; + /* transmit data byte */ + MCF_UART_UTB(minor) = *buf++; + } + return (0); +} + +/*************************************************************************** + Function : console_initialize + + Description : This initialises termios, both sets of uart hardware before + registering /dev/tty devices for each channel and the system /dev/console. + ***************************************************************************/ +rtems_device_driver console_initialize(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + rtems_status_code status; + + /* Set up TERMIOS */ + rtems_termios_initialize(); + + /* set io modes for the different channels and initialize device */ + IntUartInitialize(); + + /* Register the console port */ + status = rtems_io_register_name("/dev/console", major, CONSOLE_PORT); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + + /* Register the RS485 port to communicate with SANTEC stations */ + if ((STATIONS_PORT!=CONSOLE_PORT) && (STATIONS_PORT!=BLUETOOTH_PORT)) { + status = rtems_io_register_name("/dev/tty00", major,STATIONS_PORT); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + } + else { + status=RTEMS_TOO_MANY; + rtems_fatal_error_occurred(status); + } + + /* Register the Bluetooth port */ + if ((BLUETOOTH_PORT!=CONSOLE_PORT) && (BLUETOOTH_PORT!=STATIONS_PORT)) { + status = rtems_io_register_name("/dev/tty01", major, BLUETOOTH_PORT); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + } + else { + status=RTEMS_TOO_MANY; + rtems_fatal_error_occurred(status); + } + + return (RTEMS_SUCCESSFUL); +} + +/*************************************************************************** + Function : console_open + + Description : This actually opens the device depending on the minor + number set during initialisation. The device specific access routines are + passed to termios when the devices is opened depending on whether it is + polled or not. + ***************************************************************************/ +rtems_device_driver console_open(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + rtems_status_code status = RTEMS_INVALID_NUMBER; + rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *) arg; + struct IntUartInfoStruct *info; + + static const rtems_termios_callbacks IntUartPollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + IntUartPollRead, /* pollRead */ + IntUartPollWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* mode */ + }; + static const rtems_termios_callbacks IntUartIntrCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + NULL, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* mode */ + }; + + static const rtems_termios_callbacks IntUartTaskCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + IntUartTaskRead, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_TASK_DRIVEN /* mode */ + }; + + /* open the port depending on the minor device number */ + if ((minor >= 0) && (minor < MAX_UART_INFO)) { + info = &IntUartInfo[minor]; + switch (info->iomode) { + case TERMIOS_POLLED: + status = rtems_termios_open(major, minor, arg, &IntUartPollCallbacks); + break; + case TERMIOS_IRQ_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartIntrCallbacks); + info->ttyp = args->iop->data1; + break; + case TERMIOS_TASK_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartTaskCallbacks); + info->ttyp = args->iop->data1; + break; + } + } + + if (status == RTEMS_SUCCESSFUL) { + /* + * Reset the default baudrate. + */ + struct termios term; + + if (tcgetattr(STDIN_FILENO, &term) >= 0) { + term.c_cflag &= ~(CSIZE); + term.c_cflag |= CS8; + term.c_ispeed = B115200; + term.c_ospeed = B115200; + tcsetattr(STDIN_FILENO, TCSANOW, &term); + } + } + + return (status); +} + +/*************************************************************************** + Function : console_close + + Description : This closes the device via termios + ***************************************************************************/ +rtems_device_driver console_close(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + return (rtems_termios_close(arg)); +} + +/****************** +********************************************************* + Function : console_read + + Description : Read from the device via termios + ***************************************************************************/ +rtems_device_driver console_read(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + return (rtems_termios_read(arg)); +} + +/*************************************************************************** + Function : console_write + + Description : Write to the device via termios + ***************************************************************************/ +rtems_device_driver console_write(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + return (rtems_termios_write(arg)); +} + +/*************************************************************************** + Function : console_ioctl + + Description : Pass the IOCtl call to termios + ***************************************************************************/ +rtems_device_driver console_control(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + return (rtems_termios_ioctl(arg)); +} diff --git a/bsps/m68k/mcf5225x/console/debugio.c b/bsps/m68k/mcf5225x/console/debugio.c new file mode 100644 index 0000000000..b91048a310 --- /dev/null +++ b/bsps/m68k/mcf5225x/console/debugio.c @@ -0,0 +1,35 @@ +/* + * Multi UART console serial I/O. + * + * TO DO: Add DMA input/output + * + * 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 <stdio.h> +#include <fcntl.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <termios.h> +#include <bsp.h> +#include <malloc.h> + +#include <rtems/bspIo.h> + +static void _BSP_null_char(char c) +{ + rtems_interrupt_level level=UART0_IRQ_LEVEL; + + rtems_interrupt_disable(level); + while ((MCF_UART_USR(CONSOLE_PORT) & MCF_UART_USR_TXRDY) == 0) + continue; + MCF_UART_UTB(CONSOLE_PORT) = c; + while ((MCF_UART_USR(CONSOLE_PORT) & MCF_UART_USR_TXRDY) == 0) + continue; + rtems_interrupt_enable(level); +} + +BSP_output_char_function_type BSP_output_char = _BSP_null_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; diff --git a/bsps/m68k/mcf5235/console/console.c b/bsps/m68k/mcf5235/console/console.c new file mode 100644 index 0000000000..38317130cb --- /dev/null +++ b/bsps/m68k/mcf5235/console/console.c @@ -0,0 +1,745 @@ + /* + * Multi UART console serial I/O. + * + * TO DO: Add DMA input/output + */ + +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> +#include <malloc.h> + +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <rtems/bspIo.h> +#include <rtems/console.h> +#include <bsp.h> + +#define UART_INTC0_IRQ_VECTOR(x) (64+13+(x)) + +#define MCF5235_UART_USR_ERROR ( MCF5235_UART_USR_RB | \ + MCF5235_UART_USR_FE | \ + MCF5235_UART_USR_PE | \ + MCF5235_UART_USR_OE ) + +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len); +static ssize_t IntUartInterruptWrite (int minor, const char *buf, size_t len); + +static void +_BSP_null_char( char c ) +{ + int level; + + rtems_interrupt_disable(level); + while ( (MCF5235_UART_USR(CONSOLE_PORT) & MCF5235_UART_USR_TXRDY) == 0 ) + continue; + MCF5235_UART_UTB(CONSOLE_PORT) = c; + while ( (MCF5235_UART_USR(CONSOLE_PORT) & MCF5235_UART_USR_TXRDY) == 0 ) + continue; + rtems_interrupt_enable(level); +} +BSP_output_char_function_type BSP_output_char = _BSP_null_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +#define MAX_UART_INFO 3 +#define RX_BUFFER_SIZE 512 + +struct IntUartInfoStruct +{ + int iomode; + volatile int uimr; + int baud; + int databits; + int parity; + int stopbits; + int hwflow; + int rx_in; + int rx_out; + char rx_buffer[RX_BUFFER_SIZE]; + void *ttyp; +}; + +struct IntUartInfoStruct IntUartInfo[MAX_UART_INFO]; + +/*************************************************************************** + Function : IntUartSet + + Description : This updates the hardware UART settings. + ***************************************************************************/ +static void +IntUartSet(int minor, int baud, int databits, int parity, int stopbits, int hwflow) +{ + int divisor; + uint32_t clock_speed; + uint8_t umr1 = 0; + uint8_t umr2 = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + int level; + + rtems_interrupt_disable(level); + + + /* disable interrupts, clear RTS line, and disable the UARTS */ + MCF5235_UART_UIMR(minor) = 0; + MCF5235_UART_UOP0(minor) = 1; + MCF5235_UART_UCR(minor) = (MCF5235_UART_UCR_TX_DISABLED | MCF5235_UART_UCR_RX_DISABLED); + + /* save the current values */ + info->uimr = 0; + info->baud = baud; + info->databits = databits; + info->parity = parity; + info->stopbits = stopbits; + info->hwflow = hwflow; + + clock_speed = get_CPU_clock_speed(); + /* determine the baud divisor value */ + divisor = ((clock_speed/2) / ( 32 * baud )); + if ( divisor < 2 ) { + divisor = 2; + } + + /* check to see if doing hardware flow control */ + if ( hwflow ) + { + /* set hardware flow options */ + umr1 |= MCF5235_UART_UMR_RXRTS; + umr2 |= MCF5235_UART_UMR_TXCTS; + } + + /* determine the new umr values */ + umr1 |= (parity | databits); + umr2 |= (stopbits); + + /* reset the uart */ + MCF5235_UART_UCR(minor) = MCF5235_UART_UCR_RESET_ERROR; + MCF5235_UART_UCR(minor) = MCF5235_UART_UCR_RESET_RX; + MCF5235_UART_UCR(minor) = MCF5235_UART_UCR_RESET_TX; + + /* reset the uart mode register and update values */ + MCF5235_UART_UCR(minor) = MCF5235_UART_UCR_RESET_MR; + MCF5235_UART_UMR(minor) = umr1; + MCF5235_UART_UMR(minor) = umr2; + + /* set the baud rate values */ + MCF5235_UART_UCSR(minor) = (MCF5235_UART_UCSR_RCS_SYS_CLK | MCF5235_UART_UCSR_TCS_SYS_CLK); + MCF5235_UART_UBG1(minor) = (divisor & 0xff00) >> 8; + MCF5235_UART_UBG2(minor) = (divisor & 0x00ff); + + /* enable the uart */ + MCF5235_UART_UCR(minor) = (MCF5235_UART_UCR_TX_ENABLED | MCF5235_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if ( info->iomode != TERMIOS_POLLED ) + { + /* enable rx interrupts */ + info->uimr |= MCF5235_UART_UIMR_FFULL; + MCF5235_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if ( hwflow ) + { + /* assert the RTS line */ + MCF5235_UART_UOP1(minor) = 1; + } + + rtems_interrupt_enable(level); + +} + + +/*************************************************************************** + Function : IntUartSetAttributes + + Description : This provides the hardware-dependent portion of tcsetattr(). + value and sets it. At the moment this just sets the baud rate. + + Note: The highest baudrate is 115200 as this stays within + an error of +/- 5% at 25MHz processor clock + ***************************************************************************/ +static int +IntUartSetAttributes(int minor, const struct termios *t) +{ + /* set default index values */ + int baud = (int)19200; + int databits = (int)MCF5235_UART_UMR_BC_8; + int parity = (int)MCF5235_UART_UMR_PM_NONE; + int stopbits = (int)MCF5235_UART_UMR_STOP_BITS_1; + int hwflow = (int)0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* check to see if input is valid */ + if ( t != (const struct termios *)0 ) + { + /* determine baud rate index */ + baud = rtems_termios_baud_to_number(t->c_ospeed); + + /* determine data bits */ + switch ( t->c_cflag & CSIZE ) + { + case CS5: + databits = (int)MCF5235_UART_UMR_BC_5; + break; + case CS6: + databits = (int)MCF5235_UART_UMR_BC_6; + break; + case CS7: + databits = (int)MCF5235_UART_UMR_BC_7; + break; + case CS8: + databits = (int)MCF5235_UART_UMR_BC_8; + break; + } + + /* determine if parity is enabled */ + if ( t->c_cflag & PARENB ) + { + if ( t->c_cflag & PARODD ) + { + /* odd parity */ + parity = (int)MCF5235_UART_UMR_PM_ODD; + } + else + { + /* even parity */ + parity = (int)MCF5235_UART_UMR_PM_EVEN; + } + } + + /* determine stop bits */ + if ( t->c_cflag & CSTOPB ) + { + /* two stop bits */ + stopbits = (int)MCF5235_UART_UMR_STOP_BITS_2; + } + + /* check to see if hardware flow control */ + if ( t->c_cflag & CRTSCTS ) + { + hwflow = 1; + } + } + + /* check to see if values have changed */ + if ( ( baud != info->baud ) || + ( databits != info->databits ) || + ( parity != info->parity ) || + ( stopbits != info->stopbits ) || + ( hwflow != info->hwflow ) ) + { + + /* call function to set values */ + IntUartSet(minor, baud, databits, parity, stopbits, hwflow); + } + + return( RTEMS_SUCCESSFUL ); + +} + +/*************************************************************************** + Function : IntUartInterruptHandler + + Description : This is the interrupt handler for the internal uart. It + determines which channel caused the interrupt before queueing any received + chars and dequeueing chars waiting for transmission. + ***************************************************************************/ +static rtems_isr +IntUartInterruptHandler(rtems_vector_number v) +{ + unsigned int chan = v - UART_INTC0_IRQ_VECTOR(0); + struct IntUartInfoStruct *info = &IntUartInfo[chan]; + + /* check to see if received data */ + if ( MCF5235_UART_UISR(chan) & MCF5235_UART_UISR_RXRDY ) + { + /* read data and put into the receive buffer */ + while ( MCF5235_UART_USR(chan) & MCF5235_UART_USR_RXRDY ) + { + + if ( MCF5235_UART_USR(chan) & MCF5235_UART_USR_ERROR ) + { + /* clear the error */ + MCF5235_UART_UCR(chan) = MCF5235_UART_UCR_RESET_ERROR; + } + /* put data in rx buffer and check for errors */ + info->rx_buffer[info->rx_in] = MCF5235_UART_URB(chan); + + /* update buffer values */ + info->rx_in++; + + if ( info->rx_in >= RX_BUFFER_SIZE ) + { + info->rx_in = 0; + } + } + /* Make sure the port has been opened */ + if ( info->ttyp ) + { + + /* check to see if task driven */ + if ( info->iomode == TERMIOS_TASK_DRIVEN ) + { + /* notify rx task that rx buffer has data */ + rtems_termios_rxirq_occured(info->ttyp); + } + else + { + /* Push up the received data */ + rtems_termios_enqueue_raw_characters(info->ttyp, info->rx_buffer, info->rx_in); + info->rx_in = 0; + } + } + } + + /* check to see if data needs to be transmitted */ + if ( ( info->uimr & MCF5235_UART_UIMR_TXRDY ) && + ( MCF5235_UART_UISR(chan) & MCF5235_UART_UISR_TXRDY ) ) + { + + /* disable tx interrupts */ + info->uimr &= ~MCF5235_UART_UIMR_TXRDY; + MCF5235_UART_UIMR(chan) = info->uimr; + + /* tell upper level that character has been sent */ + if ( info->ttyp ) + rtems_termios_dequeue_characters(info->ttyp, 1); + } +} + + + +/*************************************************************************** + Function : IntUartInitialize + + Description : This initialises the internal uart hardware for all + internal uarts. If the internal uart is to be interrupt driven then the + interrupt vectors are hooked. + ***************************************************************************/ +static void +IntUartInitialize(void) +{ + unsigned int chan; + struct IntUartInfoStruct *info; + rtems_isr_entry old_handler; + int level; + + for ( chan = 0; chan < MAX_UART_INFO; chan++ ) + { + info = &IntUartInfo[chan]; + + info->ttyp = NULL; + info->rx_in = 0; + info->rx_out = 0; + info->baud = -1; + info->databits = -1; + info->parity = -1; + info->stopbits = -1; + info->hwflow = -1; + info->iomode = TERMIOS_POLLED; /*polled console io */ + + MCF5235_UART_UACR(chan) = 0; + MCF5235_UART_UIMR(chan) = 0; + if ( info->iomode != TERMIOS_POLLED ) + { + rtems_interrupt_catch (IntUartInterruptHandler, + UART_INTC0_IRQ_VECTOR(chan), + &old_handler); + } + + /* set uart default values */ + IntUartSetAttributes(chan, NULL); + + /* unmask interrupt */ + rtems_interrupt_disable(level); + switch(chan) { + case 0: + MCF5235_INTC0_ICR13 = MCF5235_INTC_ICR_IL(UART0_IRQ_LEVEL) | + MCF5235_INTC_ICR_IP(UART0_IRQ_PRIORITY); + MCF5235_INTC0_IMRL &= ~(MCF5235_INTC0_IMRL_INT13 | + MCF5235_INTC0_IMRL_MASKALL); + break; + + case 1: + MCF5235_INTC0_ICR14 = MCF5235_INTC_ICR_IL(UART1_IRQ_LEVEL) | + MCF5235_INTC_ICR_IP(UART1_IRQ_PRIORITY); + MCF5235_INTC0_IMRL &= ~(MCF5235_INTC0_IMRL_INT14 | + MCF5235_INTC0_IMRL_MASKALL); + break; + + case 2: + MCF5235_INTC0_ICR15 = MCF5235_INTC_ICR_IL(UART2_IRQ_LEVEL) | + MCF5235_INTC_ICR_IP(UART2_IRQ_PRIORITY); + MCF5235_INTC0_IMRL &= ~(MCF5235_INTC0_IMRL_INT15 | + MCF5235_INTC0_IMRL_MASKALL); + break; + } + rtems_interrupt_enable(level); + + } /* of chan loop */ + + +} /* IntUartInitialise */ + + +/*************************************************************************** + Function : IntUartInterruptWrite + + Description : This writes a single character to the appropriate uart + channel. This is either called during an interrupt or in the user's task + to initiate a transmit sequence. Calling this routine enables Tx + interrupts. + ***************************************************************************/ +static ssize_t +IntUartInterruptWrite (int minor, const char *buf, size_t len) +{ + if (len > 0) { + /* write out character */ + MCF5235_UART_UTB(minor) = *buf; + + /* enable tx interrupt */ + IntUartInfo[minor].uimr |= MCF5235_UART_UIMR_TXRDY; + MCF5235_UART_UIMR(minor) = IntUartInfo[minor].uimr; + } + + return( 0 ); +} + +/*************************************************************************** + Function : IntUartInterruptOpen + + Description : This enables interrupts when the tty is opened. + ***************************************************************************/ +static int +IntUartInterruptOpen(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* enable the uart */ + MCF5235_UART_UCR(minor) = (MCF5235_UART_UCR_TX_ENABLED | MCF5235_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if ( info->iomode != TERMIOS_POLLED ) + { + /* enable rx interrupts */ + info->uimr |= MCF5235_UART_UIMR_FFULL; + MCF5235_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if ( info->hwflow ) + { + /* assert the RTS line */ + MCF5235_UART_UOP1(minor) = 1; + } + + return( 0 ); +} + + + +/*************************************************************************** + Function : IntUartInterruptClose + + Description : This disables interrupts when the tty is closed. + ***************************************************************************/ +static int +IntUartInterruptClose(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* disable the interrupts and the uart */ + MCF5235_UART_UIMR(minor) = 0; + MCF5235_UART_UCR(minor) = (MCF5235_UART_UCR_TX_DISABLED | MCF5235_UART_UCR_RX_DISABLED); + + /* reset values */ + info->ttyp = NULL; + info->uimr = 0; + info->rx_in = 0; + info->rx_out = 0; + + return( 0 ); +} + +/*************************************************************************** + Function : IntUartTaskRead + + Description : This reads all available characters from the internal uart + and places them into the termios buffer. The rx interrupts will be + re-enabled after all data has been read. + ***************************************************************************/ +static int +IntUartTaskRead(int minor) +{ + char buffer[RX_BUFFER_SIZE]; + int count; + int rx_in; + int index = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* determine number of values to copy out */ + rx_in = info->rx_in; + if ( info->rx_out <= rx_in ) + { + count = rx_in - info->rx_out; + } + else + { + count = (RX_BUFFER_SIZE - info->rx_out) + rx_in; + } + + /* copy data into local buffer from rx buffer */ + while ( ( index < count ) && ( index < RX_BUFFER_SIZE ) ) + { + /* copy data byte */ + buffer[index] = info->rx_buffer[info->rx_out]; + index++; + + /* increment rx buffer values */ + info->rx_out++; + if ( info->rx_out >= RX_BUFFER_SIZE ) + { + info->rx_out = 0; + } + } + + /* check to see if buffer is not empty */ + if ( count > 0 ) + { + /* set characters into termios buffer */ + rtems_termios_enqueue_raw_characters(info->ttyp, buffer, count); + } + + return( EOF ); +} + + + +/*************************************************************************** + Function : IntUartPollRead + + Description : This reads a character from the internal uart. It returns + to the caller without blocking if not character is waiting. + ***************************************************************************/ +static int +IntUartPollRead (int minor) +{ + if ( (MCF5235_UART_USR(minor) & MCF5235_UART_USR_RXRDY) == 0 ) + return(-1); + + return(MCF5235_UART_URB(minor)); +} + + +/*************************************************************************** + Function : IntUartPollWrite + + Description : This writes out each character in the buffer to the + appropriate internal uart channel waiting till each one is sucessfully + transmitted. + ***************************************************************************/ +static ssize_t +IntUartPollWrite (int minor, const char *buf, size_t len) +{ + size_t retval = len; + /* loop over buffer */ + while ( len-- ) + { + /* block until we can transmit */ + while ( (MCF5235_UART_USR(minor) & MCF5235_UART_USR_TXRDY) == 0 ) + continue; + /* transmit data byte */ + MCF5235_UART_UTB(minor) = *buf++; + } + return retval; +} + +/*************************************************************************** + Function : console_initialize + + Description : This initialises termios, both sets of uart hardware before + registering /dev/tty devices for each channel and the system /dev/console. + ***************************************************************************/ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg ) +{ + rtems_status_code status; + + + /* Set up TERMIOS */ + rtems_termios_initialize (); + + /* set io modes for the different channels and initialize device */ + IntUartInfo[minor].iomode = TERMIOS_IRQ_DRIVEN; + IntUartInitialize(); + + /* Register the console port */ + status = rtems_io_register_name ("/dev/console", major, CONSOLE_PORT); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + + /* Register the other port */ + if ( CONSOLE_PORT != 0 ) + { + status = rtems_io_register_name ("/dev/tty00", major, 0); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + } + if ( CONSOLE_PORT != 1 ) + { + status = rtems_io_register_name ("/dev/tty01", major, 1); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + } + + return(RTEMS_SUCCESSFUL); +} + +/*************************************************************************** + Function : console_open + + Description : This actually opens the device depending on the minor + number set during initialisation. The device specific access routines are + passed to termios when the devices is opened depending on whether it is + polled or not. + ***************************************************************************/ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + rtems_status_code status = RTEMS_INVALID_NUMBER; + rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *)arg; + struct IntUartInfoStruct *info; + + static const rtems_termios_callbacks IntUartPollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + IntUartPollRead, /* pollRead */ + IntUartPollWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* mode */ + }; + static const rtems_termios_callbacks IntUartIntrCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + NULL, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* mode */ + }; + + static const rtems_termios_callbacks IntUartTaskCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + IntUartTaskRead, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_TASK_DRIVEN /* mode */ + }; + + /* open the port depending on the minor device number */ + if ( ( minor >= 0 ) && ( minor < MAX_UART_INFO ) ) + { + info = &IntUartInfo[minor]; + switch ( info->iomode ) + { + case TERMIOS_POLLED: + status = rtems_termios_open(major, minor, arg, &IntUartPollCallbacks); + break; + case TERMIOS_IRQ_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartIntrCallbacks); + info->ttyp = args->iop->data1; + break; + case TERMIOS_TASK_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartTaskCallbacks); + info->ttyp = args->iop->data1; + break; + } + } + + if (status == RTEMS_SUCCESSFUL) + { + /* + * Reset the default baudrate. + */ + struct termios term; + if (tcgetattr (STDIN_FILENO, &term) >= 0) + { + term.c_cflag &= ~(CSIZE); + term.c_cflag |= CS8; + term.c_ispeed = B19200; + term.c_ospeed = B19200; + tcsetattr (STDIN_FILENO, TCSANOW, &term); + } + } + + return( status ); +} + +/*************************************************************************** + Function : console_close + + Description : This closes the device via termios + ***************************************************************************/ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return(rtems_termios_close (arg)); +} + +/****************** +********************************************************* + Function : console_read + + Description : Read from the device via termios + ***************************************************************************/ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return(rtems_termios_read (arg)); +} + +/*************************************************************************** + Function : console_write + + Description : Write to the device via termios + ***************************************************************************/ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return(rtems_termios_write (arg)); +} + +/*************************************************************************** + Function : console_ioctl + + Description : Pass the IOCtl call to termios + ***************************************************************************/ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return( rtems_termios_ioctl (arg) ); +} diff --git a/bsps/m68k/mcf5329/console/console.c b/bsps/m68k/mcf5329/console/console.c new file mode 100644 index 0000000000..797e5b0606 --- /dev/null +++ b/bsps/m68k/mcf5329/console/console.c @@ -0,0 +1,668 @@ + /* + * Multi UART console serial I/O. + * + * TO DO: Add DMA input/output + */ + +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> +#include <malloc.h> + +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <rtems/console.h> +#include <rtems/bspIo.h> + +#include <bsp.h> + +#define UART_INTC0_IRQ_VECTOR(x) (64+26+(x)) + +#define MCF_UART_USR_ERROR ( MCF_UART_USR_RB | \ + MCF_UART_USR_FE | \ + MCF_UART_USR_PE | \ + MCF_UART_USR_OE ) + +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len); +static ssize_t IntUartInterruptWrite(int minor, const char *buf, size_t len); + +static void _BSP_null_char(char c) +{ + int level; + + rtems_interrupt_disable(level); + while ((MCF_UART_USR(CONSOLE_PORT) & MCF_UART_USR_TXRDY) == 0) + continue; + MCF_UART_UTB(CONSOLE_PORT) = c; + while ((MCF_UART_USR(CONSOLE_PORT) & MCF_UART_USR_TXRDY) == 0) + continue; + rtems_interrupt_enable(level); +} + +BSP_output_char_function_type BSP_output_char = _BSP_null_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +#define MAX_UART_INFO 3 +#define RX_BUFFER_SIZE 512 + +struct IntUartInfoStruct +{ + int iomode; + volatile int uimr; + int baud; + int databits; + int parity; + int stopbits; + int hwflow; + int rx_in; + int rx_out; + char rx_buffer[RX_BUFFER_SIZE]; + void *ttyp; +}; + +struct IntUartInfoStruct IntUartInfo[MAX_UART_INFO]; + +/*************************************************************************** + Function : IntUartSet + + Description : This updates the hardware UART settings. + ***************************************************************************/ +static void +IntUartSet(int minor, int baud, int databits, int parity, int stopbits, + int hwflow) +{ + int divisor; + uint32_t clock_speed; + uint8_t umr1 = 0; + uint8_t umr2 = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + int level; + + rtems_interrupt_disable(level); + + /* disable interrupts, clear RTS line, and disable the UARTS */ + MCF_UART_UIMR(minor) = 0; + MCF_UART_UOP0(minor) = 1; + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_DISABLED | MCF_UART_UCR_RX_DISABLED); + + /* save the current values */ + info->uimr = 0; + info->baud = baud; + info->databits = databits; + info->parity = parity; + info->stopbits = stopbits; + info->hwflow = hwflow; + + clock_speed = bsp_get_BUS_clock_speed(); + /* determine the baud divisor value */ + divisor = ((clock_speed) / (32 * baud)); + if (divisor < 2) + divisor = 2; + + /* check to see if doing hardware flow control */ + if (hwflow) { + /* set hardware flow options */ + umr1 |= MCF_UART_UMR_RXRTS; + umr2 |= MCF_UART_UMR_TXCTS; + } + + /* determine the new umr values */ + umr1 |= (parity | databits); + umr2 |= (stopbits); + + /* reset the uart */ + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_ERROR; + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_RX; + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_TX; + + /* reset the uart mode register and update values */ + MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_MR; + MCF_UART_UMR(minor) = umr1; + MCF_UART_UMR(minor) = umr2; + + /* set the baud rate values */ + MCF_UART_UCSR(minor) = + (MCF_UART_UCSR_RCS_SYS_CLK | MCF_UART_UCSR_TCS_SYS_CLK); + MCF_UART_UBG1(minor) = (divisor & 0xff00) >> 8; + MCF_UART_UBG2(minor) = (divisor & 0x00ff); + + /* enable the uart */ + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_ENABLED | MCF_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if (info->iomode != TERMIOS_POLLED) { + /* enable rx interrupts */ + info->uimr |= MCF_UART_UIMR_RXRDY_FU; + MCF_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if (hwflow) { + /* assert the RTS line */ + MCF_UART_UOP1(minor) = 1; + } + + rtems_interrupt_enable(level); + +} + +/*************************************************************************** + Function : IntUartSetAttributes + + Description : This provides the hardware-dependent portion of tcsetattr(). + value and sets it. At the moment this just sets the baud rate. + + Note: The highest baudrate is 115200 as this stays within + an error of +/- 5% at 25MHz processor clock + ***************************************************************************/ +static int IntUartSetAttributes(int minor, const struct termios *t) +{ + /* set default index values */ + int baud = (int) 19200; + int databits = (int) MCF_UART_UMR_BC_8; + int parity = (int) MCF_UART_UMR_PM_NONE; + int stopbits = (int) MCF_UART_UMR_SB_STOP_BITS_1; + int hwflow = (int) 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* check to see if input is valid */ + if (t != (const struct termios *) 0) { + /* determine baud rate index */ + baud = rtems_termios_baud_to_number(t->c_ospeed); + + /* determine data bits */ + switch (t->c_cflag & CSIZE) { + case CS5: + databits = (int) MCF_UART_UMR_BC_5; + break; + case CS6: + databits = (int) MCF_UART_UMR_BC_6; + break; + case CS7: + databits = (int) MCF_UART_UMR_BC_7; + break; + case CS8: + databits = (int) MCF_UART_UMR_BC_8; + break; + } + + /* determine if parity is enabled */ + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) { + /* odd parity */ + parity = (int) MCF_UART_UMR_PM_ODD; + } else { + /* even parity */ + parity = (int) MCF_UART_UMR_PM_EVEN; + } + } + + /* determine stop bits */ + if (t->c_cflag & CSTOPB) { + /* two stop bits */ + stopbits = (int) MCF_UART_UMR_SB_STOP_BITS_2; + } + + /* check to see if hardware flow control */ + if (t->c_cflag & CRTSCTS) { + hwflow = 1; + } + } + + /* check to see if values have changed */ + if ((baud != info->baud) || + (databits != info->databits) || + (parity != info->parity) || + (stopbits != info->stopbits) || (hwflow != info->hwflow)) { + + /* call function to set values */ + IntUartSet(minor, baud, databits, parity, stopbits, hwflow); + } + + return (RTEMS_SUCCESSFUL); + +} + +/*************************************************************************** + Function : IntUartInterruptHandler + + Description : This is the interrupt handler for the internal uart. It + determines which channel caused the interrupt before queueing any received + chars and dequeueing chars waiting for transmission. + ***************************************************************************/ +static rtems_isr IntUartInterruptHandler(rtems_vector_number v) +{ + unsigned int chan = v - UART_INTC0_IRQ_VECTOR(0); + struct IntUartInfoStruct *info = &IntUartInfo[chan]; + + /* check to see if received data */ + if (MCF_UART_UISR(chan) & MCF_UART_UISR_RXRDY_FU) { + /* read data and put into the receive buffer */ + while (MCF_UART_USR(chan) & MCF_UART_USR_RXRDY) { + + if (MCF_UART_USR(chan) & MCF_UART_USR_ERROR) { + /* clear the error */ + MCF_UART_UCR(chan) = MCF_UART_UCR_RESET_ERROR; + } + /* put data in rx buffer and check for errors */ + info->rx_buffer[info->rx_in] = MCF_UART_URB(chan); + + /* update buffer values */ + info->rx_in++; + + if (info->rx_in >= RX_BUFFER_SIZE) { + info->rx_in = 0; + } + } + /* Make sure the port has been opened */ + if (info->ttyp) { + + /* check to see if task driven */ + if (info->iomode == TERMIOS_TASK_DRIVEN) { + /* notify rx task that rx buffer has data */ + rtems_termios_rxirq_occured(info->ttyp); + } else { + /* Push up the received data */ + rtems_termios_enqueue_raw_characters(info->ttyp, info->rx_buffer, + info->rx_in); + info->rx_in = 0; + } + } + } + + /* check to see if data needs to be transmitted */ + if ((info->uimr & MCF_UART_UIMR_TXRDY) && + (MCF_UART_UISR(chan) & MCF_UART_UISR_TXRDY)) { + + /* disable tx interrupts */ + info->uimr &= ~MCF_UART_UIMR_TXRDY; + MCF_UART_UIMR(chan) = info->uimr; + + /* tell upper level that character has been sent */ + if (info->ttyp) + rtems_termios_dequeue_characters(info->ttyp, 1); + } +} + +/*************************************************************************** + Function : IntUartInitialize + + Description : This initialises the internal uart hardware for all + internal uarts. If the internal uart is to be interrupt driven then the + interrupt vectors are hooked. + ***************************************************************************/ +static void IntUartInitialize(void) +{ + unsigned int chan; + struct IntUartInfoStruct *info; + rtems_isr_entry old_handler; + int level; + + for (chan = 0; chan < MAX_UART_INFO; chan++) { + info = &IntUartInfo[chan]; + + info->ttyp = NULL; + info->rx_in = 0; + info->rx_out = 0; + info->baud = -1; + info->databits = -1; + info->parity = -1; + info->stopbits = -1; + info->hwflow = -1; + info->iomode = TERMIOS_POLLED; /*polled console io */ + + MCF_UART_UACR(chan) = 0; + MCF_UART_UIMR(chan) = 0; + if (info->iomode != TERMIOS_POLLED) { + rtems_interrupt_catch(IntUartInterruptHandler, + UART_INTC0_IRQ_VECTOR(chan), &old_handler); + } + + /* set uart default values */ + IntUartSetAttributes(chan, NULL); + + /* unmask interrupt */ + rtems_interrupt_disable(level); + switch (chan) { + case 0: + MCF_INTC0_ICR26 = MCF_INTC_ICR_IL(UART0_IRQ_LEVEL); + MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_INT_MASK26); + break; + + case 1: + MCF_INTC0_ICR27 = MCF_INTC_ICR_IL(UART1_IRQ_LEVEL); + MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_INT_MASK27); + break; + + case 2: + MCF_INTC0_ICR28 = MCF_INTC_ICR_IL(UART2_IRQ_LEVEL); + MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_INT_MASK28); + break; + } + rtems_interrupt_enable(level); + + } /* of chan loop */ + +} /* IntUartInitialise */ + +/*************************************************************************** + Function : IntUartInterruptWrite + + Description : This writes a single character to the appropriate uart + channel. This is either called during an interrupt or in the user's task + to initiate a transmit sequence. Calling this routine enables Tx + interrupts. + ***************************************************************************/ +static ssize_t IntUartInterruptWrite(int minor, const char *buf, size_t len) +{ + if (len > 0) { + /* write out character */ + MCF_UART_UTB(minor) = *buf; + + /* enable tx interrupt */ + IntUartInfo[minor].uimr |= MCF_UART_UIMR_TXRDY; + MCF_UART_UIMR(minor) = IntUartInfo[minor].uimr; + } + + return (0); +} + +/*************************************************************************** + Function : IntUartInterruptOpen + + Description : This enables interrupts when the tty is opened. + ***************************************************************************/ +static int IntUartInterruptOpen(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* enable the uart */ + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_ENABLED | MCF_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if (info->iomode != TERMIOS_POLLED) { + /* enable rx interrupts */ + info->uimr |= MCF_UART_UIMR_RXRDY_FU; + MCF_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if (info->hwflow) { + /* assert the RTS line */ + MCF_UART_UOP1(minor) = 1; + } + + return (0); +} + +/*************************************************************************** + Function : IntUartInterruptClose + + Description : This disables interrupts when the tty is closed. + ***************************************************************************/ +static int IntUartInterruptClose(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* disable the interrupts and the uart */ + MCF_UART_UIMR(minor) = 0; + MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_DISABLED | MCF_UART_UCR_RX_DISABLED); + + /* reset values */ + info->ttyp = NULL; + info->uimr = 0; + info->rx_in = 0; + info->rx_out = 0; + + return (0); +} + +/*************************************************************************** + Function : IntUartTaskRead + + Description : This reads all available characters from the internal uart + and places them into the termios buffer. The rx interrupts will be + re-enabled after all data has been read. + ***************************************************************************/ +static int IntUartTaskRead(int minor) +{ + char buffer[RX_BUFFER_SIZE]; + int count; + int rx_in; + int index = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* determine number of values to copy out */ + rx_in = info->rx_in; + if (info->rx_out <= rx_in) { + count = rx_in - info->rx_out; + } else { + count = (RX_BUFFER_SIZE - info->rx_out) + rx_in; + } + + /* copy data into local buffer from rx buffer */ + while ((index < count) && (index < RX_BUFFER_SIZE)) { + /* copy data byte */ + buffer[index] = info->rx_buffer[info->rx_out]; + index++; + + /* increment rx buffer values */ + info->rx_out++; + if (info->rx_out >= RX_BUFFER_SIZE) { + info->rx_out = 0; + } + } + + /* check to see if buffer is not empty */ + if (count > 0) { + /* set characters into termios buffer */ + rtems_termios_enqueue_raw_characters(info->ttyp, buffer, count); + } + + return (EOF); +} + +/*************************************************************************** + Function : IntUartPollRead + + Description : This reads a character from the internal uart. It returns + to the caller without blocking if not character is waiting. + ***************************************************************************/ +static int IntUartPollRead(int minor) +{ + if ((MCF_UART_USR(minor) & MCF_UART_USR_RXRDY) == 0) + return (-1); + + return (MCF_UART_URB(minor)); +} + +/*************************************************************************** + Function : IntUartPollWrite + + Description : This writes out each character in the buffer to the + appropriate internal uart channel waiting till each one is sucessfully + transmitted. + ***************************************************************************/ +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len) +{ + size_t retval = len; + /* loop over buffer */ + while (len--) { + /* block until we can transmit */ + while ((MCF_UART_USR(minor) & MCF_UART_USR_TXRDY) == 0) + continue; + /* transmit data byte */ + MCF_UART_UTB(minor) = *buf++; + } + return retval; +} + +/*************************************************************************** + Function : console_initialize + + Description : This initialises termios, both sets of uart hardware before + registering /dev/tty devices for each channel and the system /dev/console. + ***************************************************************************/ +rtems_device_driver console_initialize(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + rtems_status_code status; + + /* Set up TERMIOS */ + rtems_termios_initialize(); + + /* set io modes for the different channels and initialize device */ + IntUartInfo[minor].iomode = TERMIOS_IRQ_DRIVEN; + IntUartInitialize(); + + /* Register the console port */ + status = rtems_io_register_name("/dev/console", major, CONSOLE_PORT); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + + /* Register the other port */ + if (CONSOLE_PORT != 0) { + status = rtems_io_register_name("/dev/tty00", major, 0); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + } + if (CONSOLE_PORT != 1) { + status = rtems_io_register_name("/dev/tty01", major, 1); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + } + + return (RTEMS_SUCCESSFUL); +} + +/*************************************************************************** + Function : console_open + + Description : This actually opens the device depending on the minor + number set during initialisation. The device specific access routines are + passed to termios when the devices is opened depending on whether it is + polled or not. + ***************************************************************************/ +rtems_device_driver console_open(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + rtems_status_code status = RTEMS_INVALID_NUMBER; + rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *) arg; + struct IntUartInfoStruct *info; + + static const rtems_termios_callbacks IntUartPollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + IntUartPollRead, /* pollRead */ + IntUartPollWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* mode */ + }; + static const rtems_termios_callbacks IntUartIntrCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + NULL, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* mode */ + }; + + static const rtems_termios_callbacks IntUartTaskCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + IntUartTaskRead, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_TASK_DRIVEN /* mode */ + }; + + /* open the port depending on the minor device number */ + if ((minor >= 0) && (minor < MAX_UART_INFO)) { + info = &IntUartInfo[minor]; + switch (info->iomode) { + case TERMIOS_POLLED: + status = rtems_termios_open(major, minor, arg, &IntUartPollCallbacks); + break; + case TERMIOS_IRQ_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartIntrCallbacks); + info->ttyp = args->iop->data1; + break; + case TERMIOS_TASK_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartTaskCallbacks); + info->ttyp = args->iop->data1; + break; + } + } + + if (status == RTEMS_SUCCESSFUL) { + /* + * Reset the default baudrate. + */ + struct termios term; + + if (tcgetattr(STDIN_FILENO, &term) >= 0) { + term.c_cflag &= ~(CSIZE); + term.c_cflag |= CS8; + term.c_ispeed = B19200; + term.c_ospeed = B19200; + tcsetattr(STDIN_FILENO, TCSANOW, &term); + } + } + + return (status); +} + +/*************************************************************************** + Function : console_close + + Description : This closes the device via termios + ***************************************************************************/ +rtems_device_driver console_close(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + return (rtems_termios_close(arg)); +} + +/*************************************************************************** + Function : console_read + + Description : Read from the device via termios + ***************************************************************************/ +rtems_device_driver console_read(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + return (rtems_termios_read(arg)); +} + +/*************************************************************************** + Function : console_write + + Description : Write to the device via termios + ***************************************************************************/ +rtems_device_driver console_write(rtems_device_major_number major, + rtems_device_minor_number minor, void *arg) +{ + return (rtems_termios_write(arg)); +} + +/*************************************************************************** + Function : console_ioctl + + Description : Pass the IOCtl call to termios + ***************************************************************************/ +rtems_device_driver console_control(rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + return (rtems_termios_ioctl(arg)); +} diff --git a/bsps/m68k/mrm332/console/console.c b/bsps/m68k/mrm332/console/console.c new file mode 100644 index 0000000000..7b5ae7d51c --- /dev/null +++ b/bsps/m68k/mrm332/console/console.c @@ -0,0 +1,155 @@ +/* + * COPYRIGHT (c) 1989-1997. + * 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.org/license/LICENSE. + */ + +#include <termios.h> + +#include <rtems/console.h> +#include <rtems/libio.h> +#include <bsp.h> +#include "sci.h" + +/* + * console_open + * + * open a port as a termios console. + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_status_code status; + + /* the console is opened three times at startup */ + /* for standard input, output, and error */ + + /* Get correct callback structure for the device */ + + /* argument of FALSE gives us interrupt driven serial io */ + /* argument of TRUE gives us polling based serial io */ + + /* SCI internal uart */ + + status = rtems_termios_open( major, minor, arg, SciGetTermiosHandlers( FALSE ) ); + + return status; +} + +/* + * console_close + * + * This routine closes a port that has been opened as console. + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_close (arg); +} + +/* + * console_read + * + * This routine uses the termios driver to read a character. + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_read (arg); +} + +/* + * console_write + * + * this routine uses the termios driver to write a character. + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_write (arg); +} + +/* + * console_control + * + * this routine uses the termios driver to process io + */ + +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_ioctl (arg); +} + +/* + * console_initialize + * + * Routine called to initialize the console device driver. + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor_arg, + void *arg +) +{ + rtems_status_code status; + + /* + * initialize the termio interface. + */ + rtems_termios_initialize(); + + /* + * register the SCI device name for termios + * do this over in the sci driver init routine? + */ + + status = rtems_io_register_name( "/dev/sci", major, 0 ); + + if (status != RTEMS_SUCCESSFUL) + { + rtems_fatal_error_occurred(status); + } + + /* + * Link the uart device to the console device + */ + +#if 1 + status = rtems_io_register_name( "/dev/console", major, 0 ); + + if (status != RTEMS_SUCCESSFUL) + { + rtems_fatal_error_occurred(status); + } +#else + if ( link( "/dev/sci", "/dev/console") < 0 ) + { + rtems_fatal_error_occurred( RTEMS_IO_ERROR ); + } +#endif + + /* + * Console Initialize Succesful + */ + + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/m68k/mrm332/console/sci.c b/bsps/m68k/mrm332/console/sci.c new file mode 100644 index 0000000000..c6b4933f13 --- /dev/null +++ b/bsps/m68k/mrm332/console/sci.c @@ -0,0 +1,1586 @@ +/***************************************************************************** +* File: sci.c +* +* Desc: This file contains the console IO routines for the SCI port. +* There are two interfaces in this module. One is for the rtems +* termios/console code and the other is a device driver interface. +* This module works together with the termio module which is +* sometimes referred to as the "line disciplines" which implements +* terminal i/o processing like tabs, backspaces, and newlines. +* The rtems printf uses interrupt io and the rtems printk routine +* uses polled io which is better for debugging. +* +* Index: Documentation +* Section A - Include Files +* Section B - Manifest Constants +* Section C - External Data +* Section D - External Functions +* Section E - Local Functions +* Section F - Local Variables +* Section G - A circular data buffer for rcv chars +* Section H - RTEMS termios callbacks for the interrupt api +* Section I - RTEMS termios callbacks for the polled api + +* Section 0 - Miscellaneous routines +* Section 1 - Routines to manipulate the circular buffer +* Section 2 - Interrupt based entry points for the termios module +* Section 3 - Polling based entry points for the termios module +* Section 4 - Device driver public api entry points +* Section 5 - Hardware level routines +* Section 6 - Testing and debugging code +* +* Refer: Motorola QSM Reference Manual - Chapter 5 - SCI sub-module +* +* Note: See bsp.h,confdefs.h,system.h for installing drivers into RTEMS. +* +*****************************************************************************/ + +/***************************************************************************** + Overview of serial port console terminal input/output +*****************************************************************************/ + +/* + +-----------+ +---------+ + | app | | app | + +-----------+ +---------+ + | | + | (printf,scanf,etc.) | + v | + +-----------+ | + | libc | | + +-----------+ | + | | + | | + | (open,close,read,write,ioctl) | + ======|==========================================|======================== + | /dev/console | /dev/sci + | (stdin,stdout,stderr) | + ======|==========================================|======================== + | | + | | + v v + +-----------+ +-----------+ +---------+ + | console | <---> | termios | <---> | sci | + | driver | | module | | driver | + +-----------+ +-----------+ +---------+ + | + | + v + +---------+ + | | + | uart | + | | + +---------+ +*/ + + +/***************************************************************************** + Section A - Include Files +*****************************************************************************/ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/bspIo.h> +#include <stdio.h> +#include <rtems/libio.h> +#include <libchip/serial.h> +#include <libchip/sersupp.h> +#include "sci.h" +#include <rtems/m68k/qsm.h> +#include <inttypes.h> +/*#include "../misc/include/cpu332.h" */ + +/***************************************************************************** + Section B - Manifest Constants +*****************************************************************************/ + +#define SCI_MINOR 0 /* minor device number */ + +/* IMPORTANT - if the device driver api is opened, it means the sci is being + * used for direct hardware access, so other users (like termios) get ignored + */ +#define DRIVER_CLOSED 0 /* the device driver api is closed */ +#define DRIVER_OPENED 1 /* the device driver api is opened */ + +/* system clock definitions, i dont have documentation on this... */ + +#if 0 /* Not needed, this is provided in mrm332.h */ +#define XTAL 32768.0 /* crystal frequency in Hz */ +#define NUMB_W 0 /* system clock parameters */ +#define NUMB_X 1 +#define NUMB_Y 0x38 /* for 14.942 Mhz */ +#define NUMB_Y 0x3F /* for 16.777 Mhz */ + +#define SYS_CLOCK (XTAL * 4.0 * (NUMB_Y+1) * (1 << (2 * NUMB_W + NUMB_X))) + +#endif + + +/***************************************************************************** + Section C - External Data +*****************************************************************************/ + + + +/***************************************************************************** + Section D - External Functions +*****************************************************************************/ + + + +/***************************************************************************** + Section E - Local Functions +*****************************************************************************/ + +void SCI_output_char(char c); + +/*rtems_isr SciIsr( rtems_vector_number vector ); interrupt handler */ + +const rtems_termios_callbacks * SciGetTermiosHandlers( int32_t polled ); + +rtems_device_driver SciInitialize( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciOpen( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciClose( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciRead( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciWrite( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciControl( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciRead ( + rtems_device_major_number, rtems_device_minor_number, void *); + +rtems_isr SciIsr( rtems_vector_number vector ); + +int SciInterruptOpen(int, int, void *); /* termios api */ +int SciInterruptClose(int, int, void *); /* termios api */ +ssize_t SciInterruptWrite(int, const char *, size_t); /* termios api */ + +int SciSetAttributes(int, const struct termios*); /* termios api */ +int SciPolledOpen(int, int, void *); /* termios api */ +int SciPolledClose(int, int, void *); /* termios api */ +int SciPolledRead(int); /* termios api */ +ssize_t SciPolledWrite(int, const char *, size_t); /* termios api */ + +static void SciSetBaud(uint32_t rate); /* hardware routine */ +static void SciSetDataBits(uint16_t bits); /* hardware routine */ +static void SciSetParity(uint16_t parity); /* hardware routine */ + +static void inline SciDisableAllInterrupts( void ); /* hardware routine */ +static void inline SciDisableTransmitInterrupts( void );/* hardware routine */ +static void inline SciDisableReceiveInterrupts( void ); /* hardware routine */ + +static void inline SciEnableTransmitInterrupts( void ); /* hardware routine */ +static void inline SciEnableReceiveInterrupts( void ); /* hardware routine */ + +static void inline SciDisableReceiver( void ); /* hardware routine */ +static void inline SciDisableTransmitter( void ); /* hardware routine */ + +static void inline SciEnableReceiver( void ); /* hardware routine */ +static void inline SciEnableTransmitter( void ); /* hardware routine */ + +void SciWriteCharWait ( uint8_t ); /* hardware routine */ +void SciWriteCharNoWait( uint8_t ); /* hardware routine */ + +uint8_t inline SciCharAvailable( void ); /* hardware routine */ + +static uint8_t inline SciReadCharWait( void ); /* hardware routine */ +static uint8_t inline SciReadCharNoWait( void ); /* hardware routine */ + +void SciSendBreak( void ); /* test routine */ + +static int8_t SciRcvBufGetChar(void); /* circular rcv buf */ +static void SciRcvBufPutChar( uint8_t); /* circular rcv buf */ +#if 0 +static void SciRcvBufFlush( void ); /* unused routine */ +#endif + +void SciUnitTest(void); /* test routine */ +void SciPrintStats(void); /* test routine */ + + +/***************************************************************************** + Section F - Local Variables +*****************************************************************************/ + +static struct rtems_termios_tty *SciTermioTty; + +static uint8_t SciInited = 0; /* has the driver been inited */ + +static uint8_t SciOpened; /* has the driver been opened */ + +static uint8_t SciMajor; /* major device number */ + +static uint16_t SciBaud; /* current value in baud register */ + +static uint32_t SciBytesIn = 0; /* bytes received */ +static uint32_t SciBytesOut = 0; /* bytes transmitted */ + +static uint32_t SciErrorsParity = 0; /* error counter */ +static uint32_t SciErrorsNoise = 0; /* error counter */ +static uint32_t SciErrorsFraming = 0; /* error counter */ +static uint32_t SciErrorsOverrun = 0; /* error counter */ + +#if defined(CONSOLE_SCI) + +/* this is what rtems printk uses to do polling based output */ + +BSP_output_char_function_type BSP_output_char = SCI_output_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +#endif + +/***************************************************************************** + Section G - A circular buffer for rcv chars when the driver interface is used. +*****************************************************************************/ + +/* it is trivial to wrap your buffer pointers when size is a power of two */ + +#define SCI_RCV_BUF_SIZE 256 /* must be a power of 2 !!! */ + +/* if someone opens the sci device using the device driver interface, + * then the receive data interrupt handler will put characters in this buffer + * instead of sending them up to the termios module for the console + */ +static uint8_t SciRcvBuffer[SCI_RCV_BUF_SIZE]; + +static uint8_t SciRcvBufPutIndex = 0; /* array index to put in next char */ + +static uint8_t SciRcvBufGetIndex = 0; /* array index to take out next char */ + +static uint16_t SciRcvBufCount = 0; /* how many bytes are in the buffer */ + + + +/***************************************************************************** + Section H - RTEMS termios callbacks for the interrupt version of the driver +*****************************************************************************/ + +static const rtems_termios_callbacks SciInterruptCallbacks = +{ + SciInterruptOpen, /* first open */ + SciInterruptClose, /* last close */ + NULL, /* polled read (not required) */ + SciInterruptWrite, /* write */ + SciSetAttributes, /* set attributes */ + NULL, /* stop remote xmit */ + NULL, /* start remote xmit */ + TRUE /* output uses interrupts */ +}; + +/***************************************************************************** + Section I - RTEMS termios callbacks for the polled version of the driver +*****************************************************************************/ + +static const rtems_termios_callbacks SciPolledCallbacks = +{ + SciPolledOpen, /* first open */ + SciPolledClose, /* last close */ + SciPolledRead, /* polled read */ + SciPolledWrite, /* write */ + SciSetAttributes, /* set attributes */ + NULL, /* stop remote xmit */ + NULL, /* start remote xmit */ + FALSE /* output uses interrupts */ +}; + + +/* + * SECTION 0 + * MISCELLANEOUS ROUTINES + */ + +/**************************************************************************** + * Func: SCI_output_char + * Desc: used by rtems printk function to send a char to the uart + * Inputs: the character to transmit + * Outputs: none + * Errors: none + * Scope: public + ****************************************************************************/ + +void SCI_output_char(char c) +{ +/* ( minor device number, pointer to the character, length ) */ + + SciPolledWrite( SCI_MINOR, &c, 1); + + return; +} + + +/**************************************************************************** +* Func: SciGetTermiosHandlers +* Desc: returns a pointer to the table of serial io functions +* this is called from console_open with polled set to false +* Inputs: flag indicating whether we want polled or interrupt driven io +* Outputs: pointer to function table +* Errors: none +* Scope: public +****************************************************************************/ + +const rtems_termios_callbacks * SciGetTermiosHandlers( int32_t polled ) +{ + if ( polled ) + { + return &SciPolledCallbacks; /* polling based */ + } + else + { + return &SciInterruptCallbacks; /* interrupt driven */ + } +} + + +/**************************************************************************** +* Func: SciIsr +* Desc: interrupt handler for serial communications interface +* Inputs: vector number - unused +* Outputs: none +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_isr SciIsr( rtems_vector_number vector ) +{ + uint8_t ch; + + if ( (*SCSR) & SCI_ERROR_PARITY ) SciErrorsParity ++; + if ( (*SCSR) & SCI_ERROR_FRAMING ) SciErrorsFraming ++; + if ( (*SCSR) & SCI_ERROR_NOISE ) SciErrorsNoise ++; + if ( (*SCSR) & SCI_ERROR_OVERRUN ) SciErrorsOverrun ++; + + /* see if it was a transmit interrupt */ + /* data reg empty, xmt complete */ + if ( ( *SCCR1 & SCI_ENABLE_INT_TX ) && ( (*SCSR) & SCI_XMTR_AVAILABLE ) ) + { + SciDisableTransmitInterrupts(); + + /* tell termios module that the charcter was sent */ + /* he will call us later to transmit more if there are any */ + + if (rtems_termios_dequeue_characters( SciTermioTty, 1 )) + { + /* there are more bytes to transmit so enable TX interrupt */ + + SciEnableTransmitInterrupts(); + } + } + + /* see if it was a receive interrupt */ + /* on the sci uart we just get one character per interrupt */ + + while ( SciCharAvailable() ) /* char in data register? */ + { + ch = SciReadCharNoWait(); /* get the char from the uart */ + + /* IMPORTANT!!! */ + /* either send it to the termios module or keep it locally */ + + if ( SciOpened == DRIVER_OPENED ) /* the driver is open */ + { + SciRcvBufPutChar(ch); /* keep it locally */ + } + else /* put in termios buffer */ + { + char c = (char) ch; + rtems_termios_enqueue_raw_characters( SciTermioTty, &c, 1 ); + } + + *SCSR &= SCI_CLEAR_RX_INT; /* clear the interrupt */ + } +} + + +/* + * SECTION 1 + * ROUTINES TO MANIPULATE THE CIRCULAR BUFFER + */ + +/**************************************************************************** +* Func: SciRcvBufGetChar +* Desc: read a character from the circular buffer +* make sure there is data before you call this! +* Inputs: none +* Outputs: the character or -1 +* Errors: none +* Scope: private +****************************************************************************/ + +static int8_t SciRcvBufGetChar(void) +{ + rtems_interrupt_level level; + uint8_t ch; + + if ( SciRcvBufCount == 0 ) + { + rtems_fatal_error_occurred(0xDEAD); /* check the count first! */ + } + + rtems_interrupt_disable( level ); /* disable interrupts */ + + ch = SciRcvBuffer[SciRcvBufGetIndex]; /* get next byte */ + + SciRcvBufGetIndex++; /* bump the index */ + + SciRcvBufGetIndex &= SCI_RCV_BUF_SIZE - 1; /* and wrap it */ + + SciRcvBufCount--; /* decrement counter */ + + rtems_interrupt_enable( level ); /* restore interrupts */ + + return ch; /* return the char */ +} + + +/**************************************************************************** +* Func: SciRcvBufPutChar +* Desc: put a character into the rcv data circular buffer +* Inputs: the character +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void SciRcvBufPutChar( uint8_t ch ) +{ + rtems_interrupt_level level; + + if ( SciRcvBufCount == SCI_RCV_BUF_SIZE ) /* is there room? */ + { + return; /* no, throw it away */ + } + + rtems_interrupt_disable( level ); /* disable interrupts */ + + SciRcvBuffer[SciRcvBufPutIndex] = ch; /* put it in the buf */ + + SciRcvBufPutIndex++; /* bump the index */ + + SciRcvBufPutIndex &= SCI_RCV_BUF_SIZE - 1; /* and wrap it */ + + SciRcvBufCount++; /* increment counter */ + + rtems_interrupt_enable( level ); /* restore interrupts */ + + return; /* return */ +} + + +/**************************************************************************** +* Func: SciRcvBufFlush +* Desc: completely reset and clear the rcv buffer +* Inputs: none +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +#if 0 /* prevents compiler warning */ +static void SciRcvBufFlush( void ) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable( level ); /* disable interrupts */ + + memset( SciRcvBuffer, 0, sizeof(SciRcvBuffer) ); + + SciRcvBufPutIndex = 0; /* clear */ + + SciRcvBufGetIndex = 0; /* clear */ + + SciRcvBufCount = 0; /* clear */ + + rtems_interrupt_enable( level ); /* restore interrupts */ + + return; /* return */ +} +#endif + + +/* + * + * SECTION 2 + * INTERRUPT BASED ENTRY POINTS FOR THE TERMIOS MODULE + */ + +/**************************************************************************** +* Func: SciInterruptOpen +* Desc: open routine for the interrupt based device driver +* Default state is 9600 baud, 8 bits, No parity, and 1 stop bit. ?? +**CHANGED** Default baud rate is now 19200, 8N1 +* called from rtems_termios_open which is called from console_open +* Inputs: major - device number +* minor - device number +* args - points to terminal info +* Outputs: success/fail +* Errors: none +* Scope: public API +****************************************************************************/ + +int SciInterruptOpen( + int major, + int minor, + void *arg +) +{ + rtems_libio_open_close_args_t * args = arg; + rtems_isr_entry old_vector; + + if ( minor != SCI_MINOR ) /* check minor device num */ + { + return -1; + } + + if ( !args ) /* must have args */ + { + return -1; + } + + SciTermioTty = args->iop->data1; /* save address of struct */ + + SciDisableAllInterrupts(); /* turn off sci interrupts */ + + /* THIS IS ACTUALLY A BAD THING - SETTING LINE PARAMETERS HERE */ + /* IT SHOULD BE DONE THROUGH TCSETATTR() WHEN THE CONSOLE IS OPENED!!! */ + +/* SciSetBaud(115200); set the baud rate */ +/* SciSetBaud( 57600); set the baud rate */ +/* SciSetBaud( 38400); set the baud rate */ +/* SciSetBaud( 19200); set the baud rate */ + SciSetBaud( 9600); /* set the baud rate */ + + SciSetParity(SCI_PARITY_NONE); /* set parity to none */ + + SciSetDataBits(SCI_8_DATA_BITS); /* set data bits to 8 */ + + /* Install our interrupt handler into RTEMS. */ + /* 68 is an unused user-defined vector. Note that the vector must be */ + /* even - it sets the low bit for SPI interrupts, and clears it for */ + /* SCI interrupts. Also note that vector 66 is used by CPU32bug on */ + /* the mrm332. */ + + rtems_interrupt_catch( SciIsr, 68, &old_vector ); + + *QSMCR = (*QSMCR & ~IARB) | 1; // Is 1 a good value for qsm iarb? + *QIVR = 68; + *QILR &= 0xf8; + *QILR |= 0x06 & 0x07; + + SciEnableTransmitter(); /* enable the transmitter */ + + SciEnableReceiver(); /* enable the receiver */ + + SciEnableReceiveInterrupts(); /* enable rcv interrupts */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciInterruptClose +* Desc: close routine called by the termios module +* Inputs: major - device number +* minor - device number +* args - unused +* Outputs: success/fail +* Errors: none +* Scope: public - termio entry point +****************************************************************************/ + +int SciInterruptClose( + int major, + int minor, + void *arg +) +{ + SciDisableAllInterrupts(); + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciInterruptWrite +* Desc: writes data to the uart using transmit interrupts +* Inputs: minor - device number +* buf - points to the data +* len - number of bytes to send +* Outputs: success/fail +* Errors: none +* Scope: public API +****************************************************************************/ + +ssize_t SciInterruptWrite( + int minor, + const char *buf, + size_t len +) +{ + /* We are using interrupt driven output so termios only sends us */ + /* one character at a time. The sci does not have a fifo. */ + + if ( !len ) /* no data? */ + { + return -1; /* return error */ + } + + if ( minor != SCI_MINOR ) /* check the minor dev num */ + { + return -1; /* return error */ + } + + if ( SciOpened == DRIVER_OPENED ) /* is the driver api open? */ + { + return -1; /* yep, throw this away */ + } + + SciWriteCharNoWait(*buf); /* try to send a char */ + + *SCSR &= SCI_CLEAR_TDRE; /* clear tx data reg empty flag */ + + SciEnableTransmitInterrupts(); /* enable the tx interrupt */ + + return 0; /* return success */ +} + + +/**************************************************************************** +* Func: SciSetAttributes +* Desc: setup the uart based on the termios modules requests +* Inputs: minor - device number +* t - pointer to the termios info struct +* Outputs: none +* Errors: none +* Scope: public API +****************************************************************************/ + +int SciSetAttributes( + int minor, + const struct termios *t +) +{ + uint32_t baud_requested; + uint32_t sci_rate = 0; + uint16_t sci_parity = 0; + uint16_t sci_databits = 0; + + if ( minor != SCI_MINOR ) /* check the minor dev num */ + { + return -1; /* return error */ + } + + /* if you look closely you will see this is the only thing we use */ + /* set the baud rate */ + + baud_requested = t->c_ospeed; /* baud rate */ + + if (!baud_requested) + { + baud_requested = B9600; /* default to 9600 baud */ + /* baud_requested = B19200; default to 19200 baud */ + } + + sci_rate = rtems_termios_baud_to_number( baud_requested ); + + /* parity error detection */ + + if (t->c_cflag & PARENB) /* enable parity detection? */ + { + if (t->c_cflag & PARODD) + { + sci_parity = SCI_PARITY_ODD; /* select odd parity */ + } + else + { + sci_parity = SCI_PARITY_EVEN; /* select even parity */ + } + } + else + { + sci_parity = SCI_PARITY_NONE; /* no parity, most common */ + } + + /* set the number of data bits, 8 is most common */ + + if (t->c_cflag & CSIZE) /* was it specified? */ + { + switch (t->c_cflag & CSIZE) + { + case CS8: sci_databits = SCI_8_DATA_BITS; break; + default : sci_databits = SCI_9_DATA_BITS; break; + } + } + else + { + sci_databits = SCI_8_DATA_BITS; /* default to 8 data bits */ + } + + /* the number of stop bits; always 1 for SCI */ + + if (t->c_cflag & CSTOPB) + { + /* do nothing */ + } + + /* setup the hardware with these serial port parameters */ + + SciSetBaud(sci_rate); /* set the baud rate */ + SciSetParity(sci_parity); /* set the parity type */ + SciSetDataBits(sci_databits); /* set the data bits */ + + return RTEMS_SUCCESSFUL; +} + + +/* + * + * SECTION 3 + * POLLING BASED ENTRY POINTS FOR THE TERMIOS MODULE + */ + +/**************************************************************************** +* Func: SciPolledOpen +* Desc: open routine for the polled i/o version of the driver +* called from rtems_termios_open which is called from console_open +* Inputs: major - device number +* minor - device number +* args - points to terminal info struct +* Outputs: success/fail +* Errors: none +* Scope: public - termios entry point +****************************************************************************/ + +int SciPolledOpen( + int major, + int minor, + void *arg +) +{ + rtems_libio_open_close_args_t * args = arg; + + if ( minor != SCI_MINOR ) /* check minor device num */ + { + return -1; + } + + if ( !args ) /* must have args */ + { + return -1; + } + + SciTermioTty = args->iop->data1; /* Store tty pointer */ + + SciDisableAllInterrupts(); /* don't generate interrupts */ + + /* THIS IS ACTUALLY A BAD THING - SETTING LINE PARAMETERS HERE */ + /* IT SHOULD BE DONE THROUGH TCSETATTR() WHEN THE CONSOLE IS OPENED!!! */ + +/* SciSetBaud(115200); set the baud rate */ +/* SciSetBaud( 57600); set the baud rate */ +/* SciSetBaud( 38400); set the baud rate */ +/* SciSetBaud( 19200); * set the baud rate */ + SciSetBaud( 9600); /* set the baud rate */ + + SciSetParity(SCI_PARITY_NONE); /* set no parity */ + + SciSetDataBits(SCI_8_DATA_BITS); /* set 8 data bits */ + + SciEnableTransmitter(); /* enable the xmitter */ + + SciEnableReceiver(); /* enable the rcvr */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciPolledClose +* Desc: close routine for the device driver, same for both +* Inputs: major - device number +* minor - device number +* args - unused +* Outputs: success/fail +* Errors: none +* Scope: public termios API +****************************************************************************/ + +int SciPolledClose( + int major, + int minor, + void *arg +) +{ + SciDisableAllInterrupts(); + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciPolledRead +* Desc: polling based read routine for the uart +* Inputs: minor - device number +* Outputs: error or the character read +* Errors: none +* Scope: public API +****************************************************************************/ + +int SciPolledRead( + int minor +) +{ + if ( minor != SCI_MINOR ) /* check the type-punned dev num */ + { + return -1; /* return error */ + } + + if ( SciCharAvailable() ) /* if a char is available */ + { + return SciReadCharNoWait(); /* read the rx data register */ + } + + return -1; /* return error */ +} + + +/**************************************************************************** +* Func: SciPolledWrite +* Desc: writes out characters in polled mode, waiting for the uart +* check in console_open, but we only seem to use interrupt mode +* Inputs: minor - device number +* buf - points to the data +* len - how many bytes +* Outputs: error or number of bytes written +* Errors: none +* Scope: public termios API +****************************************************************************/ + +ssize_t SciPolledWrite( + int minor, + const char *buf, + size_t len +) +{ + ssize_t written = 0; + + if ( minor != SCI_MINOR ) /* check minor device num */ + { + return -1; + } + + if ( SciOpened == DRIVER_OPENED ) /* is the driver api open? */ + { + return -1; /* toss the data */ + } + + /* send each byte in the string out the port */ + + while ( written < len ) + { + SciWriteCharWait(*buf++); /* send a byte */ + + written++; /* increment counter */ + } + + return written; /* return count */ +} + + +/* + * + * SECTION 4 + * DEVICE DRIVER PUBLIC API ENTRY POINTS + */ + +/**************************************************************************** +* Func: SciInit +* Desc: Initialize the lasers device driver and hardware +* Inputs: major - the major device number which is assigned by rtems +* minor - the minor device number which is undefined at this point +* arg - ????? +* Outputs: RTEMS_SUCCESSFUL +* Errors: None. +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciInitialize ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ +/* rtems_status_code status; */ + +/*printk("%s\r\n", __FUNCTION__); */ + + /* register the SCI device name for termios console i/o + * this is done over in console.c which doesn't seem exactly right + * but there were problems doing it here... + */ + +/* status = rtems_io_register_name( "/dev/sci", major, 0 ); */ + +/* if (status != RTEMS_SUCCESSFUL) */ +/* rtems_fatal_error_occurred(status); */ + + SciMajor = major; /* save the rtems major number */ + + SciOpened = DRIVER_CLOSED; /* initial state is closed */ + + /* if you have an interrupt handler, install it here */ + + SciInited = 1; /* set the inited flag */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciOpen +* Desc: device driver open routine +* you must open a device before you can anything else +* only one process can have the device opened at a time +* you could look at the task id to restrict access if you want +* Inputs: major - the major device number assigned by rtems +* minor - the minor device number assigned by us +* arg - ????? +* Outputs: see below +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciOpen ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ +/*printk("%s major=%d minor=%d\r\n", __FUNCTION__,major,minor); */ + + if (SciInited == 0) /* must be initialized first! */ + { + return RTEMS_NOT_CONFIGURED; + } + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* verify minor number */ + } + + if (SciOpened == DRIVER_OPENED) + { + return RTEMS_RESOURCE_IN_USE; /* already opened! */ + } + + SciOpened = DRIVER_OPENED; /* set the opened flag */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciClose +* Desc: device driver close routine +* the device must be opened before you can close it +* the device must be closed before someone (else) can open it +* Inputs: major - the major device number +* minor - the minor device number +* arg - ????? +* Outputs: see below +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciClose ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ +/*printk("%s major=%d minor=%d\r\n", __FUNCTION__,major,minor); */ + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* check the minor number */ + } + + if (SciOpened != DRIVER_OPENED) + { + return RTEMS_INCORRECT_STATE; /* must be opened first */ + } + + SciOpened = DRIVER_CLOSED; /* set the flag */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciRead +* Desc: device driver read routine +* this function is not meaningful for the laser devices +* Inputs: major - the major device number +* minor - the minor device number +* arg - read/write arguments +* Outputs: see below +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciRead ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args; /* ptr to argument struct */ + char *buffer; + + rw_args = (rtems_libio_rw_args_t *) arg; /* arguments to read() */ + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* check the minor number */ + } + + if (SciOpened == DRIVER_CLOSED) + { + return RTEMS_INCORRECT_STATE; /* must be opened first */ + } + + buffer = rw_args->buffer; /* points to user's buffer */ + +/* *buffer = SciReadCharWait(); wait for a character */ + + /* if there isn't a character available, wait until one shows up */ + /* or the timeout period expires, which ever happens first */ + + if ( SciRcvBufCount == 0 ) /* no chars */ + { + /* wait for someone to wake me up... */ + /*rtems_task_wake_after(SciReadTimeout); */ + } + + if ( SciRcvBufCount ) /* any characters locally? */ + { + *buffer = SciRcvBufGetChar(); /* get the character */ + + rw_args->bytes_moved = 1; /* how many we actually read */ + } + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciWrite +* Desc: device driver write routine +* this function is not meaningful for the laser devices +* Inputs: major - the major device number +* minor - the minor device number +* arg - read/write arguments +* Outputs: see below +* Errors: non3 +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciWrite ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args; /* ptr to argument struct */ + uint8_t *buffer; + size_t length; + + rw_args = (rtems_libio_rw_args_t *) arg; + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* check the minor number */ + } + + if (SciOpened == DRIVER_CLOSED) + { + return RTEMS_INCORRECT_STATE; /* must be opened first */ + } + + buffer = (uint8_t*)rw_args->buffer; /* points to data */ + + length = rw_args->count; /* how many bytes */ + + while (length--) + { + SciWriteCharWait(*buffer++); /* send the bytes out */ + } + + rw_args->bytes_moved = rw_args->count; /* how many we wrote */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciControl +* Desc: device driver control routine +* see below for an example of how to use the ioctl interface +* Inputs: major - the major device number +* minor - the minor device number +* arg - io control args +* Outputs: see below +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciControl ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_ioctl_args_t *args = arg; /* rtems arg struct */ + uint16_t command; /* the cmd to execute */ + +/*printk("%s major=%d minor=%d\r\n", __FUNCTION__,major,minor); */ + + /* do some sanity checking */ + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* check the minor number */ + } + + if (SciOpened == DRIVER_CLOSED) + { + return RTEMS_INCORRECT_STATE; /* must be open first */ + } + + if (args == 0) + { + return RTEMS_INVALID_ADDRESS; /* must have args */ + } + + args->ioctl_return = -1; /* assume an error */ + + command = args->command; /* get the command */ + + if (command == SCI_SEND_BREAK) /* process the command */ + { + SciSendBreak(); /* send break char */ + } + + args->ioctl_return = 0; /* return status */ + + return RTEMS_SUCCESSFUL; +} + + +/* + * + * SECTION 5 + * HARDWARE LEVEL ROUTINES + */ + +/**************************************************************************** +* Func: SciSetBaud +* Desc: setup the uart based on the termios modules requests +* Inputs: baud rate +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void SciSetBaud(uint32_t rate) +{ + uint16_t value; + uint16_t save_sccr1; + +/* when you open the console you need to set the termio struct baud rate */ +/* it has a default value of 9600, when someone calls tcsetattr it reverts! */ + + SciBaud = rate; /* save the rate */ + + /* calculate the register value as a float and convert to an int */ + /* set baud rate - you must define the system clock constant */ + /* see mrm332.h for an example */ + + value = ( (uint16_t) ( SYS_CLOCK / rate / 32.0 + 0.5 ) & 0x1fff ); + + save_sccr1 = *SCCR1; /* save register */ + + /* also turns off the xmtr and rcvr */ + + *SCCR1 &= SCI_DISABLE_INT_ALL; /* disable interrupts */ + + *SCCR0 = value; /* write the register */ + + *SCCR1 = save_sccr1; /* restore register */ + + return; +} + + +/**************************************************************************** +* Func: SciSetParity +* Desc: setup the uart based on the termios modules requests +* Inputs: parity +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void SciSetParity(uint16_t parity) +{ + uint16_t value; + + value = *SCCR1; /* get the register */ + + if (parity == SCI_PARITY_ODD) + { + value |= SCI_PARITY_ENABLE; /* parity enabled */ + value |= SCI_PARITY_ODD; /* parity odd */ + } + + else if (parity == SCI_PARITY_EVEN) + { + value |= SCI_PARITY_ENABLE; /* parity enabled */ + value &= ~SCI_PARITY_ODD; /* parity even */ + } + + else if (parity == SCI_PARITY_NONE) + { + value &= ~SCI_PARITY_ENABLE; /* disabled, most common */ + } + + /* else no changes */ + + *SCCR1 = value; /* write the register */ + + return; +} + + +/**************************************************************************** +* Func: SciSetDataBits +* Desc: setup the uart based on the termios modules requests +* Inputs: data bits +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void SciSetDataBits(uint16_t bits) +{ + uint16_t value; + + value = *SCCR1; /* get the register */ + + /* note - the parity setting affects the number of data bits */ + + if (bits == SCI_9_DATA_BITS) + { + value |= SCI_9_DATA_BITS; /* 9 data bits */ + } + + else if (bits == SCI_8_DATA_BITS) + { + value &= SCI_8_DATA_BITS; /* 8 data bits */ + } + + /* else no changes */ + + *SCCR1 = value; /* write the register */ + + return; +} + + +/**************************************************************************** +* Func: SciDisableAllInterrupts +* Func: SciEnableTransmitInterrupts +* Func: SciEnableReceiveInterrupts +* Desc: handles generation of interrupts by the sci module +* Inputs: none +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void inline SciDisableAllInterrupts( void ) +{ + /* this also turns off the xmtr and rcvr */ + + *SCCR1 &= SCI_DISABLE_INT_ALL; +} + +static void inline SciEnableReceiveInterrupts( void ) +{ + *SCCR1 |= SCI_ENABLE_INT_RX; +} + +static void inline SciDisableReceiveInterrupts( void ) +{ + *SCCR1 &= SCI_DISABLE_INT_RX; +} + +static void inline SciEnableTransmitInterrupts( void ) +{ + *SCCR1 |= SCI_ENABLE_INT_TX; +} + +static void inline SciDisableTransmitInterrupts( void ) +{ + *SCCR1 &= SCI_DISABLE_INT_TX; +} + + +/**************************************************************************** +* Func: SciEnableTransmitter, SciDisableTransmitter +* Func: SciEnableReceiver, SciDisableReceiver +* Desc: turns the transmitter and receiver on and off +* Inputs: none +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void inline SciEnableTransmitter( void ) +{ + *SCCR1 |= SCI_ENABLE_XMTR; +} + +static void inline SciDisableTransmitter( void ) +{ + *SCCR1 &= SCI_DISABLE_XMTR; +} + +static void inline SciEnableReceiver( void ) +{ + *SCCR1 |= SCI_ENABLE_RCVR; +} + +static void inline SciDisableReceiver( void ) +{ + *SCCR1 &= SCI_DISABLE_RCVR; +} + + +/**************************************************************************** +* Func: SciWriteCharWait +* Desc: wait for room in the fifo and then put a char in +* Inputs: a byte to send +* Outputs: none +* Errors: none +* Scope: public +****************************************************************************/ + +void SciWriteCharWait(uint8_t c) +{ + /* poll the fifo, waiting for room for another character */ + + while ( ( *SCSR & SCI_XMTR_AVAILABLE ) != SCI_XMTR_AVAILABLE ) + { + /* Either we are writing to the fifo faster than + * the uart can clock bytes out onto the cable, + * or we are in flow control (actually no, we + * are ignoring flow control from the other end). + * In the first case, higher baud rates will help. + */ + /* relinquish processor while waiting */ + rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); + } + + *SCDR = c; /* send the charcter */ + + SciBytesOut++; /* increment the counter */ + + return; +} + +/**************************************************************************** +* Func: SciWriteCharNoWait +* Desc: if no room in the fifo throw the char on the floor +* Inputs: a byte to send +* Outputs: none +* Errors: none +* Scope: public +****************************************************************************/ + +void SciWriteCharNoWait(uint8_t c) +{ + if ( ( *SCSR & SCI_XMTR_AVAILABLE ) == 0 ) + { + return; /* no room, throw it away */ + } + + *SCDR = c; /* put the char in the fifo */ + + SciBytesOut++; /* increment the counter */ + + return; +} + + +/**************************************************************************** +* Func: SciReadCharWait +* Desc: read a character, waiting for one to show up, if need be +* Inputs: none +* Outputs: a character +* Errors: none +* Scope: public +****************************************************************************/ + +static uint8_t inline SciReadCharWait( void ) +{ + uint8_t ch; + + while ( SciCharAvailable() == 0 ) /* anything there? */ + { + /* relinquish processor while waiting */ + rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); + } + + /* if you have rcv ints enabled, then the isr will probably */ + /* get the character before you will unless you turn off ints */ + /* ie polling and ints don't mix that well */ + + ch = *SCDR; /* get the charcter */ + + SciBytesIn++; /* increment the counter */ + + return ch; /* return the char */ +} + +/**************************************************************************** +* Func: SciReadCharNoWait +* Desc: try to get a char but dont wait for one +* Inputs: none +* Outputs: a character or -1 if none +* Errors: none +* Scope: public +****************************************************************************/ + +static uint8_t inline SciReadCharNoWait( void ) +{ + uint8_t ch; + + if ( SciCharAvailable() == 0 ) /* anything there? */ + return -1; + + ch = *SCDR; /* get the character */ + + SciBytesIn++; /* increment the count */ + + return ch; /* return the char */ +} + + +/**************************************************************************** +* Func: SciCharAvailable +* Desc: is there a receive character in the data register +* Inputs: none +* Outputs: false if no char available, else true +* Errors: none +* Scope: public +****************************************************************************/ + +uint8_t inline SciCharAvailable( void ) +{ + return ( *SCSR & SCI_RCVR_READY ); /* char in data register? */ +} + + +/**************************************************************************** +* Func: SciSendBreak +* Desc: send 1 or tow breaks (all zero bits) +* Inputs: none +* Outputs: none +* Errors: none +* Scope: public +****************************************************************************/ + +void SciSendBreak( void ) +{ + /* From the Motorola QSM reference manual - */ + + /* "if SBK is toggled by writing it first to a one and then immediately */ + /* to a zero (in less than one serial frame interval), the transmitter */ + /* sends only one or two break frames before reverting to mark (idle) */ + /* or before commencing to send more data" */ + + *SCCR1 |= SCI_SEND_BREAK; /* set the bit */ + + *SCCR1 &= ~SCI_SEND_BREAK; /* clear the bit */ + + return; +} + + +/* + * + * SECTION 6 + * TEST CODE + */ + +/**************************************************************************** +* Func: SciUnitTest +* Desc: test the device driver +* Inputs: nothing +* Outputs: nothing +* Scope: public +****************************************************************************/ + +#if 0 +void SciUnitTest() +{ + uint8_t byte; /* a character */ + uint16_t fd; /* file descriptor for device */ + uint16_t result; /* result of ioctl */ + + fd = open("/dev/sci",O_RDWR); /* open the device */ + +printk("SCI open fd=%d\r\n",fd); + + result = write(fd, "abcd\r\n", 6); /* send a string */ + +printk("SCI write result=%d\r\n",result); + + result = read(fd, &byte, 1); /* read a byte */ + +printk("SCI read result=%d,byte=%x\r\n",result,byte); + + return; +} +#endif + + +/**************************************************************************** +* Func: SciPrintStats +* Desc: print out some driver information +* Inputs: nothing +* Outputs: nothing +* Scope: public +****************************************************************************/ + +void SciPrintStats ( void ) +{ + printk("\r\n"); + + printk( "SYS_CLOCK is %2.6f Mhz\r\n\n", SYS_CLOCK / 1000000.0 ); + + printk( "Current baud rate is %d bps or %d cps\r\n\n", SciBaud, SciBaud / 10 ); + + printk( "SCI Uart chars in %8" PRIu32 "\r\n", SciBytesIn ); + printk( "SCI Uart chars out %8" PRIu32 "\r\n", SciBytesOut ); + printk( "SCI Uart framing errors %8" PRIu32 "\r\n", SciErrorsFraming ); + printk( "SCI Uart parity errors %8" PRIu32 "\r\n", SciErrorsParity ); + printk( "SCI Uart overrun errors %8" PRIu32 "\r\n", SciErrorsOverrun ); + printk( "SCI Uart noise errors %8" PRIu32 "\r\n", SciErrorsNoise ); + + return; +} diff --git a/bsps/m68k/mrm332/console/sci.h b/bsps/m68k/mrm332/console/sci.h new file mode 100644 index 0000000000..93893ecbfc --- /dev/null +++ b/bsps/m68k/mrm332/console/sci.h @@ -0,0 +1,233 @@ +/**************************************************************************** +* File: sci.h +* +* Desc: This is the include file for the serial communications interface. +* +* Note: See bsp.h,confdefs.h,system.h for installing drivers into RTEMS. +* +****************************************************************************/ + +#ifndef _sci_h_ +#define _sci_h_ + +/******************************************************************************* + IOCTL commands for the sci driver. + I'm still working on these... +*******************************************************************************/ + +#define SCI_IOCTL_PARITY_NONE 0x00 /* no parity bit after the data bits */ +#define SCI_IOCTL_PARITY_ODD 0x01 /* parity bit added after data bits */ +#define SCI_IOCTL_PARITY_EVEN 0x02 /* parity bit added after data bits */ +#define SCI_IOCTL_PARITY_MARK 0x03 /* parity bit is lo, -12 volts, logical 1 */ +#define SCI_IOCTL_PARITY_SPACE 0x04 /* parity bit is hi, +12 volts, logical 0 */ +#define SCI_IOCTL_PARITY_FORCED_ON 0x03 /* parity bit is forced hi or lo */ +#define SCI_IOCTL_PARITY_FORCED_OFF 0x04 /* parity bit is forced hi or lo */ + +#define SCI_IOCTL_BAUD_RATE 0x20 /* set the baud rate, arg is baud */ + +#define SCI_IOCTL_DATA_BITS 0x30 /* set the data bits, arg is # bits */ + +#define SCI_IOCTL_STOP_BITS_1 0x40 /* 1 stop bit after char frame */ +#define SCI_IOCTL_STOP_BITS_2 0x41 /* 2 stop bit after char frame */ + +#define SCI_IOCTL_MODE_NORMAL 0x50 /* normal operating mode */ +#define SCI_IOCTL_MODE_LOOP 0x51 /* internal loopback mode */ + +#define SCI_IOCTL_FLOW_NONE 0x60 /* no flow control */ +#define SCI_IOCTL_FLOW_RTS_CTS 0x61 /* hardware flow control */ + +#define SCI_IOCTL_SEND_BREAK 0x70 /* send an rs-232 break */ + +#define SCI_IOCTL_MODE_1200 0x80 /* 1200,n,8,1 download mode */ +#define SCI_IOCTL_MODE_9600 0x81 /* 9600,n,8,1 download mode */ +#define SCI_IOCTL_MODE_9_BIT 0x82 /* 9600,forced,8,1 command mode */ + + +/******************************************************************************* + SCI Registers +*******************************************************************************/ + +/* SCI Control Register 0 (SCCR0) $FFFC08 + + 8 4 2 1 - 8 4 2 1 - 8 4 2 1 - 8 4 2 1 + ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ + | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | +----- 0 baud rate divisor + | | | | | | | | | | | | | | +------- 1 baud rate divisor + | | | | | | | | | | | | | +--------- 2 baud rate divisor + | | | | | | | | | | | | +----------- 3 baud rate divisor + | | | | | | | | | | | | + | | | | | | | | | | | +--------------- 4 baud rate divisor + | | | | | | | | | | +----------------- 5 baud rate divisor + | | | | | | | | | +------------------- 6 baud rate divisor + | | | | | | | | +--------------------- 7 baud rate divisor + | | | | | | | | + | | | | | | | +------------------------- 8 baud rate divisor + | | | | | | +--------------------------- 9 baud rate divisor + | | | | | +----------------------------- 10 baud rate divisor + | | | | +------------------------------- 11 baud rate divisor + | | | | + | | | +----------------------------------- 12 baud rate divisor + | | +------------------------------------- 13 unused + | +--------------------------------------- 14 unused + +----------------------------------------- 15 unused + + 0 0 0 0 - 0 0 0 0 - 0 0 0 0 - 0 1 0 0 reset value - (64k baud?) + */ + +#define SCI_BAUD_57_6K 9 +#define SCI_BAUD_38_4K 14 +#define SCI_BAUD_19_2K 27 +#define SCI_BAUD_9600 55 +#define SCI_BAUD_4800 109 +#define SCI_BAUD_2400 218 +#define SCI_BAUD_1200 437 + + +/* SCI Control Register 1 (SCCR1) $FFFC0A + + 8 4 2 1 - 8 4 2 1 - 8 4 2 1 - 8 4 2 1 + ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ + | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | +----- 0 send a break + | | | | | | | | | | | | | | +------- 1 rcvr wakeup mode + | | | | | | | | | | | | | +--------- 2 rcvr enable + | | | | | | | | | | | | +----------- 3 xmtr enable + | | | | | | | | | | | | + | | | | | | | | | | | +--------------- 4 idle line intr enable + | | | | | | | | | | +----------------- 5 rcvr intr enable + | | | | | | | | | +------------------- 6 xmit complete intr enable + | | | | | | | | +--------------------- 7 xmtr intr enable + | | | | | | | | + | | | | | | | +------------------------- 8 wakeup on address mark + | | | | | | +--------------------------- 9 mode 1=9 bits, 0=8 bits + | | | | | +----------------------------- 10 parity enable 1=on, 0=off + | | | | +------------------------------- 11 parity type 1=odd, 0=even + | | | | + | | | +----------------------------------- 12 idle line select + | | +------------------------------------- 13 wired-or mode + | +--------------------------------------- 14 loop mode + +----------------------------------------- 15 unused + + 0 0 0 0 - 0 0 0 0 - 0 0 0 0 - 0 0 0 0 reset value +*/ + +#define SCI_SEND_BREAK 0x0001 /* 0000-0000-0000-0001 */ +#define SCI_RCVR_WAKEUP 0x0002 /* 0000-0000-0000-0010 */ +#define SCI_ENABLE_RCVR 0x0004 /* 0000-0000-0000-0100 */ +#define SCI_ENABLE_XMTR 0x0008 /* 0000-0000-0000-1000 */ + +#define SCI_DISABLE_RCVR 0xFFFB /* 1111-1111-1111-1011 */ +#define SCI_DISABLE_XMTR 0xFFF7 /* 1111-1111-1111-0111 */ + +#define SCI_ENABLE_INT_IDLE 0x0010 /* 0000-0000-0001-0000 */ +#define SCI_ENABLE_INT_RX 0x0020 /* 0000-0000-0010-0000 */ +#define SCI_ENABLE_INT_TX_DONE 0x0040 /* 0000-0000-0100-0000 */ +#define SCI_ENABLE_INT_TX 0x0080 /* 0000-0000-1000-0000 */ + +#define SCI_DISABLE_INT_ALL 0xFF00 /* 1111-1111-0000-0000 ??? */ + +#define SCI_DISABLE_INT_RX 0xFFDF /* 1111-1111-1101-1111 */ +#define SCI_CLEAR_RX_INT 0xFFBF /* 1111-1111-1011-1111 */ +#define SCI_DISABLE_INT_TX 0xFF7F /* 1111-1111-0111-1111 */ +#define SCI_CLEAR_TDRE 0xFEFF /* 1111-1110-1111-1111 */ + +#define SCI_RCVR_WAKE_ON_MARK 0x0100 /* 0000-0001-0000-0000 */ +#define SCI_9_DATA_BITS 0x0200 /* 0000-0010-0000-0000 */ +#define SCI_PARITY_ENABLE 0x0400 /* 0000-0100-0000-0000 */ +#define SCI_PARITY_ODD 0x0800 /* 0000-1000-0000-0000 */ + +#define SCI_RCVR_WAKE_ON_IDLE 0xFEFF /* 1111-1110-1111-1111 */ +#define SCI_8_DATA_BITS 0xFDFF /* 1111-1101-1111-1111 */ +#define SCI_PARITY_DISABLE 0xFBFF /* 1111-1011-1111-1111 */ +#define SCI_PARITY_EVEN 0xF7FF /* 1111-0111-1111-1111 */ + +#define SCI_PARITY_NONE 0xF3FF /* 1111-0011-1111-1111 */ + +#define SCI_IDLE_LINE_LONG 0x1000 /* 0001-0000-0000-0000 */ +#define SCI_TXD_OPEN_DRAIN 0x2000 /* 0010-0000-0000-0000 */ +#define SCI_LOOPBACK_MODE 0x4000 /* 0100-0000-0000-0000 */ +#define SCI_SCCR1_UNUSED 0x8000 /* 1000-0000-0000-0000 */ + + +/* SCI Status Register (SCSR) $FFFC0C + + 8 4 2 1 - 8 4 2 1 - 8 4 2 1 - 8 4 2 1 + ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ + | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | +----- 0 PF - parity error + | | | | | | | | | | | | | | +------- 1 FE - framing error + | | | | | | | | | | | | | +--------- 2 NF - noise flag + | | | | | | | | | | | | +----------- 3 OR - overrun flag + | | | | | | | | | | | | + | | | | | | | | | | | +--------------- 4 IDLE - idle line detected + | | | | | | | | | | +----------------- 5 RAF - rcvr active flag + | | | | | | | | | +------------------- 6 RDRF - rcv data reg full + | | | | | | | | +--------------------- 7 TC - xmt complete flag + | | | | | | | | + | | | | | | | +------------------------- 8 TDRE - xmt data reg empty + | | | | | | +--------------------------- 9 always zero + | | | | | +----------------------------- 10 always zero + | | | | +------------------------------- 11 always zero + | | | | + | | | +----------------------------------- 12 always zero + | | +------------------------------------- 13 always zero + | +--------------------------------------- 14 always zero + +----------------------------------------- 15 always zero + + 0 0 0 0 - 0 0 0 1 - 1 0 0 0 - 0 0 0 0 reset value +*/ + +#define SCI_ERROR_PARITY 0x0001 /* 0000-0000-0000-0001 */ +#define SCI_ERROR_FRAMING 0x0002 /* 0000-0000-0000-0010 */ +#define SCI_ERROR_NOISE 0x0004 /* 0000-0000-0000-0100 */ +#define SCI_ERROR_OVERRUN 0x0008 /* 0000-0000-0000-1000 */ + +#define SCI_IDLE_LINE 0x0010 /* 0000-0000-0001-0000 */ +#define SCI_RCVR_ACTIVE 0x0020 /* 0000-0000-0010-0000 */ +#define SCI_RCVR_READY 0x0040 /* 0000-0000-0100-0000 */ +#define SCI_XMTR_IDLE 0x0080 /* 0000-0000-1000-0000 */ + +#define SCI_CLEAR_RX_INT 0xFFBF /* 1111-1111-1011-1111 */ + +#define SCI_XMTR_READY 0x0100 /* 0000-0001-0000-0000 */ + +#define SCI_CLEAR_TDRE 0xFEFF /* 1111-1110-1111-1111 */ + +#define SCI_XMTR_AVAILABLE 0x0180 /* 0000-0001-1000-0000 */ + + + +/******************************************************************************* + Function prototypes +*******************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* look at console_open to see how this is called */ + +const rtems_termios_callbacks * SciGetTermiosHandlers( int32_t polled ); + +/* SCI interrupt */ + +/*rtems_isr SciIsr( rtems_vector_number vector ); */ + +/*int32_t SciOpenPolled ( int32_t major, int32_t minor, void *arg ); */ +/*int32_t SciOpenInterrupt ( int32_t major, int32_t minor, void *arg ); */ + +/*int32_t SciClose ( int32_t major, int32_t minor, void *arg ); */ + +/*int32_t SciWritePolled ( int32_t minor, const char *buf, int32_t len ); */ +/*int32_t SciWriteInterrupt( int32_t minor, const char *buf, int32_t len ); */ + +/*int32_t SciReadPolled ( int32_t minor ); */ + +/*int32_t SciSetAttributes ( int32_t minor, const struct termios *t ); */ + +#ifdef __cplusplus +} +#endif + +#endif /* _sci_h_ */ diff --git a/bsps/m68k/mvme147/console/console.c b/bsps/m68k/mvme147/console/console.c new file mode 100644 index 0000000000..f26ca6485a --- /dev/null +++ b/bsps/m68k/mvme147/console/console.c @@ -0,0 +1,203 @@ +/* + * This file contains the MVME147 console IO package. + */ + +/* + * COPYRIGHT (c) 1989-1999. + * 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.org/license/LICENSE. + * + * MVME147 port for TNI - Telecom Bretagne + * by Dominique LE CAMPION (Dominique.LECAMPION@enst-bretagne.fr) + * May 1996 + * + * This file was taken from the DMV152 bsp + */ + +#define M147_INIT + +#include <rtems/console.h> +#include <rtems/libio.h> +#include <rtems/zilog/z8530.h> +#include <rtems/iosupp.h> +#include <bsp.h> + +/* console_initialize + * + * This routine initializes the console IO driver. + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + + status = rtems_io_register_name( + "/dev/console", + major, + (rtems_device_minor_number) 0 + ); + + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred(status); + + return RTEMS_SUCCESSFUL; +} + +/* inbyte + * + * This routine reads a character from the SCC. + */ +static char inbyte( void ) +{ + uint8_t rr_0; + char ch; + + for ( ; ; ) { + Z8x30_READ_CONTROL( CONSOLE_CONTROL, RR_0, rr_0 ); + if ( (rr_0 & RR_0_RX_DATA_AVAILABLE) != 0 ) + break; + } + + Z8x30_READ_DATA( CONSOLE_DATA, ch ); + return ( ch ); +} + +/* outbyte + * + * This routine transmits a character out the SCC. It supports + * XON/XOFF flow control. + */ +static void outbyte( + char ch +) +{ + uint8_t rr_0; + char flow_control; + + for ( ; ; ) { + Z8x30_READ_CONTROL( CONSOLE_CONTROL, RR_0, rr_0 ); + if ( (rr_0 & RR_0_TX_BUFFER_EMPTY) != 0 ) + break; + } + + for ( ; ; ) { + Z8x30_READ_CONTROL( CONSOLE_CONTROL, RR_0, rr_0 ); + if ( (rr_0 & RR_0_RX_DATA_AVAILABLE) == 0 ) + break; + + Z8x30_READ_DATA( CONSOLE_DATA, flow_control ); + + if ( flow_control == XOFF ) + do { + do { + Z8x30_READ_CONTROL( CONSOLE_CONTROL, RR_0, rr_0 ); + } while ( (rr_0 & RR_0_RX_DATA_AVAILABLE) == 0 ); + Z8x30_READ_DATA( CONSOLE_DATA, flow_control ); + } while ( flow_control != XON ); + } + + Z8x30_WRITE_DATA( CONSOLE_DATA, ch ); +} + +/* + * Open entry point + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return RTEMS_SUCCESSFUL; +} + +/* + * Close entry point + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return RTEMS_SUCCESSFUL; +} + +/* + * read bytes from the serial port. We only have stdin. + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args; + char *buffer; + int maximum; + int count = 0; + + rw_args = (rtems_libio_rw_args_t *) arg; + + buffer = rw_args->buffer; + maximum = rw_args->count; + + for (count = 0; count < maximum; count++) { + buffer[ count ] = inbyte(); + if (buffer[ count ] == '\n' || buffer[ count ] == '\r') { + buffer[ count++ ] = '\n'; + break; + } + } + + rw_args->bytes_moved = count; + return (count >= 0) ? RTEMS_SUCCESSFUL : RTEMS_UNSATISFIED; +} + +/* + * write bytes to the serial port. Stdout and stderr are the same. + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + int count; + int maximum; + rtems_libio_rw_args_t *rw_args; + char *buffer; + + rw_args = (rtems_libio_rw_args_t *) arg; + + buffer = rw_args->buffer; + maximum = rw_args->count; + + for (count = 0; count < maximum; count++) { + if ( buffer[ count ] == '\n') { + outbyte('\r'); + } + outbyte( buffer[ count ] ); + } + + rw_args->bytes_moved = maximum; + return 0; +} + +/* + * IO Control entry point + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/m68k/mvme162/console/console.c b/bsps/m68k/mvme162/console/console.c new file mode 100644 index 0000000000..985254d483 --- /dev/null +++ b/bsps/m68k/mvme162/console/console.c @@ -0,0 +1,277 @@ +/* + * This file contains the MVME162 console IO package. + */ + +/* + * COPYRIGHT (c) 1989-2013. + * 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.org/license/LICENSE. + * + * Modifications of respective RTEMS file: COPYRIGHT (c) 1994. + * EISCAT Scientific Association. M.Savitski + * + * This material is a part of the MVME162 Board Support Package + * for the RTEMS executive. Its licensing policies are those of the + * RTEMS above. + */ + +#define M162_INIT + +#include <rtems/bspIo.h> +#include <rtems/console.h> +#include <rtems/libio.h> +#include <rtems/ringbuf.h> +#include <bsp.h> + +Ring_buffer_t Console_Buffer[2]; + +/* + * Interrupt handler for receiver interrupts + */ +static rtems_isr C_Receive_ISR(rtems_vector_number vector) +{ + register int ipend, port; + + ZWRITE0(1, 0x38); /* reset highest IUS */ + + ipend = ZREAD(1, 3); /* read int pending from A side */ + + if (ipend == 0x04) port = 0; /* channel B intr pending */ + else if (ipend == 0x20) port = 1; /* channel A intr pending */ + else return; + + Ring_buffer_Add_character(&Console_Buffer[port], ZREADD(port)); + + if (ZREAD(port, 1) & 0x70) { /* check error stat */ + ZWRITE0(port, 0x30); /* reset error */ + } +} + +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + int i; + rtems_status_code status; + + /* + * Initialise receiver interrupts on both ports + */ + for (i = 0; i <= 1; i++) { + Ring_buffer_Initialize( &Console_Buffer[i] ); + ZWRITE(i, 2, SCC_VECTOR); + ZWRITE(i, 10, 0); + ZWRITE(i, 1, 0x10); /* int on all Rx chars or special condition */ + ZWRITE(i, 9, 8); /* master interrupt enable */ + } + + set_vector(C_Receive_ISR, SCC_VECTOR, 1); /* install ISR for ports A and B */ + + mcchip->vector_base = 0; + mcchip->gen_control = 2; /* MIEN */ + mcchip->SCC_int_ctl = 0x13; /* SCC IEN, IPL3 */ + + status = rtems_io_register_name( + "/dev/console", + major, + (rtems_device_minor_number) 1 + ); + + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred(status); + + status = rtems_io_register_name( + "/dev/tty00", + major, + (rtems_device_minor_number) 0 + ); + + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred(status); + + status = rtems_io_register_name( + "/dev/tty01", + major, + (rtems_device_minor_number) 1 + ); + + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred(status); + + return RTEMS_SUCCESSFUL; +} + +/* + * Non-blocking char input + */ +bool char_ready(int port, char *ch) +{ + if ( Ring_buffer_Is_empty( &Console_Buffer[port] ) ) + return false; + + Ring_buffer_Remove_character( &Console_Buffer[port], *ch ); + + return true; +} + +/* + * Block on char input + */ +static char inbyte(int port) +{ + char tmp_char; + + while ( !char_ready(port, &tmp_char) ); + return tmp_char; +} + +/* + * This routine transmits a character out the SCC. It no longer supports + * XON/XOFF flow control. + */ +static void outbyte(char ch, int port) +{ + while (1) { + if (ZREAD0(port) & TX_BUFFER_EMPTY) break; + } + ZWRITED(port, ch); +} + +/* + * Open entry point + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return RTEMS_SUCCESSFUL; +} + +/* + * Close entry point + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return RTEMS_SUCCESSFUL; +} + +/* + * read bytes from the serial port. We only have stdin. + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args; + char *buffer; + int maximum; + int count = 0; + + rw_args = (rtems_libio_rw_args_t *) arg; + + buffer = rw_args->buffer; + maximum = rw_args->count; + + if ( minor > 1 ) + return RTEMS_INVALID_NUMBER; + + for (count = 0; count < maximum; count++) { + buffer[ count ] = inbyte( minor ); + if (buffer[ count ] == '\n' || buffer[ count ] == '\r') { + buffer[ count++ ] = '\n'; + break; + } + } + + rw_args->bytes_moved = count; + return (count >= 0) ? RTEMS_SUCCESSFUL : RTEMS_UNSATISFIED; +} + +/* + * write bytes to the serial port. Stdout and stderr are the same. + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + int count; + int maximum; + rtems_libio_rw_args_t *rw_args; + char *buffer; + + rw_args = (rtems_libio_rw_args_t *) arg; + + buffer = rw_args->buffer; + maximum = rw_args->count; + + if ( minor > 1 ) + return RTEMS_INVALID_NUMBER; + + for (count = 0; count < maximum; count++) { + if ( buffer[ count ] == '\n') { + outbyte('\r', minor ); + } + outbyte( buffer[ count ], minor ); + } + + rw_args->bytes_moved = maximum; + return 0; +} + +/* + * IO Control entry point + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return RTEMS_SUCCESSFUL; +} + +/* + * _162Bug_output_char + * + * Output a single character using the 162Bug functions. The character + * will be written to the default output port. + */ +static void _162Bug_output_char( char c ) +{ + asm volatile( "moveb %0, -(%%sp)\n\t" /* char to output */ + "trap #15\n\t" /* Trap to 162Bug */ + ".short 0x20" /* Code for .OUTCHR */ + :: "d" (c) ); +} + +/* + * _BSP_output_char + * + * printk() function prototyped in bspIo.h. Does not use termios. + * + * If we have initialized the console device then use it, otherwise + * use the 162Bug routines to send it to the default output port. + */ +static void _BSP_output_char(char c) +{ + _162Bug_output_char(c); +} + +/* Printk function */ +BSP_output_char_function_type BSP_output_char = _BSP_output_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; diff --git a/bsps/m68k/mvme167/console/console-recording.h b/bsps/m68k/mvme167/console/console-recording.h new file mode 100644 index 0000000000..4d8d3fc66c --- /dev/null +++ b/bsps/m68k/mvme167/console/console-recording.h @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2000, National Research Council of Canada + * + * 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. + */ + +/* CD2401 CONSOLE DRIVER DEBUG INFO RECORDING */ + +#ifdef CD2401_RECORD_DEBUG_INFO + +/* Control individual recording here. That way, we don't clutter console.c */ +#define CD2401_RECORD_WRITE +#define CD2401_RECORD_TX_ISR +#define CD2401_RECORD_RX_ISR +#define CD2401_RECORD_RE_ISR +#define CD2401_RECORD_MODEM_ISR +#define CD2401_RECORD_SET_ATTRIBUTE +#define CD2401_RECORD_FIRST_OPEN +#define CD2401_RECORD_LAST_CLOSE +#define CD2401_RECORD_START_REMOTE_TX +#define CD2401_RECORD_STOP_REMOTE_TX +#define CD2401_RECORD_DRAIN_OUTPUT +#define CD2401_RECORD_DELAY + +/* Call the data recording functions */ +#ifdef CD2401_RECORD_WRITE +#define CD2401_RECORD_WRITE_INFO( args ) cd2401_record_write_info args +#else +#define CD2401_RECORD_WRITE_INFO( args ) +#endif + +#ifdef CD2401_RECORD_TX_ISR +#define CD2401_RECORD_TX_ISR_INFO( args ) cd2401_record_tx_isr_info args +#define CD2401_RECORD_TX_ISR_SPURIOUS_INFO( args ) cd2401_record_tx_isr_spurious_info args +#define CD2401_RECORD_TX_ISR_BUSERR_INFO( args ) cd2401_record_tx_isr_buserr_info args +#else +#define CD2401_RECORD_TX_ISR_INFO( args ) +#define CD2401_RECORD_TX_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_TX_ISR_BUSERR_INFO( args ) +#endif + +#ifdef CD2401_RECORD_RX_ISR +#define CD2401_RECORD_RX_ISR_INFO( args ) cd2401_record_rx_isr_info args +#define CD2401_RECORD_RX_ISR_SPURIOUS_INFO( args ) cd2401_record_rx_isr_spurious_info args +#else +#define CD2401_RECORD_RX_ISR_INFO( args ) +#define CD2401_RECORD_RX_ISR_SPURIOUS_INFO( args ) +#endif + +#ifdef CD2401_RECORD_RE_ISR +#define CD2401_RECORD_RE_ISR_SPURIOUS_INFO( args ) cd2401_record_re_isr_spurious_info args +#else +#define CD2401_RECORD_RE_ISR_SPURIOUS_INFO( args ) +#endif + +#ifdef CD2401_RECORD_MODEM_ISR +#define CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO( args ) cd2401_record_modem_isr_spurious_info args +#else +#define CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO( args ) +#endif + +#ifdef CD2401_RECORD_SET_ATTRIBUTES +#define CD2401_RECORD_SET_ATTRIBUTES_INFO( args ) cd2401_record_set_attributes_info args +#else +#define CD2401_RECORD_SET_ATTRIBUTES_INFO( args ) +#endif + +#ifdef CD2401_RECORD_FIRST_OPEN +#define CD2401_RECORD_FIRST_OPEN_INFO( args ) cd2401_record_first_open_info args +#else +#define CD2401_RECORD_FIRST_OPEN_INFO( args ) +#endif + +#ifdef CD2401_RECORD_LAST_CLOSE +#define CD2401_RECORD_LAST_CLOSE_INFO( args ) cd2401_record_last_close_info args +#else +#define CD2401_RECORD_LAST_CLOSE_INFO( args ) +#endif + +#ifdef CD2401_RECORD_START_REMOTE_TX +#define CD2401_RECORD_START_REMOTE_TX_INFO( args ) cd2401_record_start_remote_tx_info args +#else +#define CD2401_RECORD_START_REMOTE_TX_INFO( args ) +#endif + +#ifdef CD2401_RECORD_STOP_REMOTE_TX +#define CD2401_RECORD_STOP_REMOTE_TX_INFO( args ) cd2401_record_stop_remote_tx_info args +#else +#define CD2401_RECORD_STOP_REMOTE_TX_INFO( args ) +#endif + +#ifdef CD2401_RECORD_DRAIN_OUTPUT +#define CD2401_RECORD_DRAIN_OUTPUT_INFO( args ) cd2401_record_drain_output_info args +#else +#define CD2401_RECORD_DRAIN_OUTPUT_INFO( args ) +#endif + +#ifdef CD2401_RECORD_DELAY +#define CD2401_RECORD_DELAY_INFO( args ) cd2401_record_delay_info args +#else +#define CD2401_RECORD_DELAY_INFO( args ) +#endif + +/* Define the data and the recording functions */ +#define CD2401_DEBUG_BUFFER_SIZE 256 +#define CD2401_DEBUG_CHAR_BUFSIZE 64 +#define CD2401_WRITE_INFO 1 +#define CD2401_TX_ISR_INFO 2 +#define CD2401_TX_ISR_SPURIOUS_INFO 3 +#define CD2401_TX_ISR_BUSERR_INFO 4 +#define CD2401_RX_ISR_INFO 5 +#define CD2401_RX_ISR_SPURIOUS_INFO 6 +#define CD2401_RE_ISR_SPURIOUS_INFO 7 +#define CD2401_MODEM_ISR_SPURIOUS_INFO 8 +#define CD2401_FIRST_OPEN_INFO 9 +#define CD2401_LAST_CLOSE_INFO 10 +#define CD2401_START_REMOTE_TX_INFO 11 +#define CD2401_STOP_REMOTE_TX_INFO 12 +#define CD2401_SET_ATTRIBUTE_INFO 13 +#define CD2401_DRAIN_OUTPUT_INFO 14 +#define CD2401_DELAY_INFO 15 + +struct cd2401_debug_info { + short discriminant; + short record_size; + union { + struct cd2401_write_info { + int length; + char buffer[CD2401_DEBUG_CHAR_BUFSIZE]; + char dmabuf; + } write_info; + struct cd2401_tx_isr_info { + unsigned char channel; + unsigned char status; + unsigned char initial_ier; + unsigned char final_ier; + uint8_t txEmpty; + } tx_isr_info; + struct cd2401_tx_isr_spurious_info { + unsigned char channel; + unsigned char status; + unsigned char initial_ier; + unsigned char final_ier; + unsigned long spurdev; + unsigned long spurcount; + } tx_isr_spurious_info; + struct cd2401_tx_isr_buserr_info { + unsigned char channel; + unsigned char status; + unsigned char initial_ier; + unsigned char buserr; + unsigned long type; + unsigned long addr; + } tx_isr_buserr_info; + struct cd2401_rx_isr_info { + unsigned char channel; + int length; + char buffer[CD2401_DEBUG_CHAR_BUFSIZE]; + } rx_isr_info; + struct cd2401_rx_isr_spurious_info { + unsigned char channel; + unsigned char status; + unsigned long spurdev; + unsigned long spurcount; + } rx_isr_spurious_info; + struct cd2401_re_isr_spurious_info { + unsigned char channel; + unsigned long spurdev; + unsigned long spurcount; + } re_isr_spurious_info; + struct cd2401_modem_isr_spurious_info { + unsigned char channel; + unsigned long spurdev; + unsigned long spurcount; + } modem_isr_spurious_info; + struct cd2401_first_open_info { + unsigned char channel; + uint8_t init_count; + } first_open_info; + struct cd2401_last_close_info { + unsigned char channel; + uint8_t init_count; + } last_close_info; + struct cd2401_start_remote_tx_info { + unsigned char channel; + } start_remote_tx_info; + struct cd2401_stop_remote_tx_info { + unsigned char channel; + } stop_remote_tx_info; + struct cd2401_set_attribute_info { + int minor; + uint8_t need_reinit; + uint8_t txEmpty; + uint8_t csize; + uint8_t cstopb; + uint8_t parodd; + uint8_t parenb; + uint8_t ignpar; + uint8_t inpck; + uint8_t hw_flow_ctl; + uint8_t sw_flow_ctl; + uint8_t extra_flow_ctl; + uint8_t icrnl; + uint8_t igncr; + uint8_t inlcr; + uint8_t brkint; + uint8_t ignbrk; + uint8_t parmrk; + uint8_t istrip; + uint16_t tx_period; + uint16_t rx_period; + uint32_t out_baud; + uint32_t in_baud; + } set_attribute_info; + struct cd2401_drain_output_info { + uint8_t txEmpty; + uint8_t own_buf_A; + uint8_t own_buf_B; + } drain_output_info; + struct cd2401_delay_info { + rtems_interval start; + rtems_interval end; + rtems_interval current; + unsigned long loop_count; + } delay_info; + } u; +}; + +struct cd2401_debug_info cd2401_debug_buffer[CD2401_DEBUG_BUFFER_SIZE]; +int cd2401_debug_index = 0; + +#include <string.h> + +int cd2401_get_record_size( + int size +) +{ + /* Not the best way to do this */ + return size + 4; +} + +void cd2401_record_write_info( + int len, + const char * buf, + char dmabuf +) +{ + int max_length; + + max_length = (len < CD2401_DEBUG_CHAR_BUFSIZE ) ? len : CD2401_DEBUG_CHAR_BUFSIZE; + + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_WRITE_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_write_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.write_info.length = len; + memcpy ( &(cd2401_debug_buffer[cd2401_debug_index].u.write_info.buffer), buf, max_length ); + cd2401_debug_buffer[cd2401_debug_index].u.write_info.dmabuf = dmabuf; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_tx_isr_info( + unsigned char ch, + unsigned char status, + unsigned char initial_ier, + unsigned char final_ier, + uint8_t txEmpty +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_TX_ISR_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_tx_isr_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.status = status; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.initial_ier = initial_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.final_ier = final_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.txEmpty = txEmpty; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_tx_isr_spurious_info( + unsigned char ch, + unsigned char status, + unsigned char initial_ier, + unsigned char final_ier, + unsigned char spur_dev, + unsigned char spur_cnt +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_TX_ISR_SPURIOUS_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_tx_isr_spurious_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.status = status; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.initial_ier = initial_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.final_ier = final_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.spurdev = spur_dev; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.spurcount = spur_cnt; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_tx_isr_buserr_info( + unsigned char ch, + unsigned char status, + unsigned char initial_ier, + unsigned char buserr, + unsigned long buserr_type, + unsigned long buserr_addr +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_TX_ISR_BUSERR_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_tx_isr_buserr_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.status = status; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.initial_ier = initial_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.buserr = buserr; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.type = buserr_type; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.addr = buserr_addr; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_rx_isr_info( + unsigned char ch, + unsigned char total, + char * buffer +) +{ + int max_length; + + max_length = (total < CD2401_DEBUG_CHAR_BUFSIZE ) ? total : CD2401_DEBUG_CHAR_BUFSIZE; + + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_RX_ISR_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_rx_isr_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_info.length = max_length; + memcpy ( &(cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_info.buffer), buffer, max_length ); + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_rx_isr_spurious_info( + unsigned char ch, + unsigned char status, + uint32_t spur_dev, + uint32_t spur_cnt +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_RX_ISR_SPURIOUS_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_rx_isr_spurious_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_spurious_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_spurious_info.status = status; + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_spurious_info.spurdev = spur_dev; + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_spurious_info.spurcount = spur_cnt; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_re_isr_spurious_info( + unsigned char ch, + uint32_t spur_dev, + uint32_t spur_cnt +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_RE_ISR_SPURIOUS_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_re_isr_spurious_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.re_isr_spurious_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.re_isr_spurious_info.spurdev = spur_dev; + cd2401_debug_buffer[cd2401_debug_index].u.re_isr_spurious_info.spurcount = spur_cnt; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_modem_isr_spurious_info( + unsigned char ch, + uint32_t spur_dev, + uint32_t spur_cnt +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_MODEM_ISR_SPURIOUS_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_modem_isr_spurious_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.modem_isr_spurious_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.modem_isr_spurious_info.spurdev = spur_dev; + cd2401_debug_buffer[cd2401_debug_index].u.modem_isr_spurious_info.spurcount = spur_cnt; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_first_open_info( + unsigned char ch, + uint8_t init_count +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_FIRST_OPEN_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_first_open_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.first_open_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.first_open_info.init_count = init_count; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_last_close_info( + unsigned char ch, + uint8_t init_count +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_LAST_CLOSE_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_last_close_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.last_close_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.last_close_info.init_count = init_count; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_start_remote_tx_info( + unsigned char ch +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_START_REMOTE_TX_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_start_remote_tx_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.start_remote_tx_info.channel = ch; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_stop_remote_tx_info( + unsigned char ch +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_STOP_REMOTE_TX_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_stop_remote_tx_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.stop_remote_tx_info.channel = ch; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_set_attributes_info( + int minor, + uint8_t need_reinit, + uint8_t csize, + uint8_t cstopb, + uint8_t parodd, + uint8_t parenb, + uint8_t ignpar, + uint8_t inpck, + uint8_t hw_flow_ctl, + uint8_t sw_flow_ctl, + uint8_t extra_flow_ctl, + uint8_t icrnl, + uint8_t igncr, + uint8_t inlcr, + uint8_t brkint, + uint8_t ignbrk, + uint8_t parmrk, + uint8_t istrip, + uint16_t tx_period, + uint16_t rx_period, + uint32_t out_baud, + uint32_t in_baud +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_SET_ATTRIBUTE_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_set_attribute_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.minor = minor; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.need_reinit = need_reinit; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.txEmpty = CD2401_Channel_Info[minor].txEmpty; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.csize = csize; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.cstopb = cstopb; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.parodd = parodd; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.parenb = parenb; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.ignpar = ignpar; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.inpck = inpck; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.hw_flow_ctl = hw_flow_ctl; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.sw_flow_ctl = sw_flow_ctl; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.extra_flow_ctl = extra_flow_ctl; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.icrnl = icrnl; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.igncr = igncr; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.inlcr = inlcr; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.brkint = brkint; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.ignbrk = ignbrk; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.parmrk = parmrk; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.istrip = istrip; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.tx_period = tx_period; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.rx_period = rx_period; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.out_baud = out_baud; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.in_baud = in_baud; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_drain_output_info( + uint8_t txEmpty, + uint8_t own_buf_A, + uint8_t own_buf_B +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_DRAIN_OUTPUT_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_drain_output_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.drain_output_info.txEmpty = txEmpty; + cd2401_debug_buffer[cd2401_debug_index].u.drain_output_info.own_buf_A = own_buf_A; + cd2401_debug_buffer[cd2401_debug_index].u.drain_output_info.own_buf_B = own_buf_B; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_delay_info( + rtems_interval start, + rtems_interval end, + rtems_interval current, + unsigned long loop_count +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_DELAY_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_delay_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.delay_info.start = start; + cd2401_debug_buffer[cd2401_debug_index].u.delay_info.end = end; + cd2401_debug_buffer[cd2401_debug_index].u.delay_info.current = current; + cd2401_debug_buffer[cd2401_debug_index].u.delay_info.loop_count = loop_count; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +#else + +/* Do not call the data recording functions */ +#define CD2401_RECORD_WRITE_INFO( args ) +#define CD2401_RECORD_TX_ISR_INFO( args ) +#define CD2401_RECORD_TX_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_TX_ISR_BUSERR_INFO( args ) +#define CD2401_RECORD_RX_ISR_INFO( args ) +#define CD2401_RECORD_RX_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_RE_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_FIRST_OPEN_INFO( args ) +#define CD2401_RECORD_LAST_CLOSE_INFO( args ) +#define CD2401_RECORD_START_REMOTE_TX_INFO( args ) +#define CD2401_RECORD_STOP_REMOTE_TX_INFO( args ) +#define CD2401_RECORD_SET_ATTRIBUTES_INFO( args ) +#define CD2401_RECORD_DRAIN_OUTPUT_INFO( args ) +#define CD2401_RECORD_DELAY_INFO( args ) + +#endif diff --git a/bsps/m68k/mvme167/console/console.c b/bsps/m68k/mvme167/console/console.c new file mode 100644 index 0000000000..0499ac46b3 --- /dev/null +++ b/bsps/m68k/mvme167/console/console.c @@ -0,0 +1,1676 @@ +/* + * This file contains the MVME167 termios console package. Only asynchronous + * I/O is supported. + * + * /dev/tty0 is channel 0, Serial Port 1/Console on the MVME712M. + * /dev/tty1 is channel 1, Serial Port 2/TTY01 on the MVME712M. + * /dev/tty2 is channel 2, Serial Port 3 on the MVME712M. + * /dev/tty3 is channel 3, Serial Port 4 on the MVME712M. + * + * Normal I/O uses DMA for output, interrupts for input. /dev/console is + * fixed to be /dev/tty01, Serial Port 2. Very limited support is provided + * for polled I/O. Polled I/O is intended only for running the RTEMS test + * suites. In all cases, Serial Port 1/Console is allocated to 167Bug and + * is the dedicated debugger port. We configure GDB to use 167Bug for + * debugging. When debugging with GDB or 167Bug, do not open /dev/tty00. + * + * Modern I/O chips often contain a number of I/O devices that can operate + * almost independently of each other. Typically, in RTEMS, all devices in + * an I/O chip are handled by a single device driver, but that need not be + * always the case. Each device driver must supply six entry points in the + * Device Driver Table: a device initialization function, as well as an open, + * close, read, write and a control function. RTEMS assigns a device major + * number to each device driver. This major device number is the index of the + * device driver entries in the Device Driver Table, and it used to identify + * a particular device driver. To distinguish multiple I/O sub-devices within + * an I/O chip, RTEMS supports device minor numbers. When a I/O device is + * initialized, the major number is supplied to the initialization function. + * That function must register each sub-device with a separate name and minor + * number (as well as the supplied major number). When an application opens a + * device by name, the corresponding major and minor numbers are returned to + * the caller to be used in subsequent I/O operations (although these details + * are typically hidden within the library functions). + * + * Such a scheme recognizes that the initialization of the individual + * sub-devices is generally not completely independent. For example, the + * four serial ports of the CD2401 can be configured almost independently + * from each other. One port could be configured to operate in asynchronous + * mode with interrupt-driven I/O, while another port could be configured to + * operate in HDLC mode with DMA I/O. However, a device reset command will + * reset all four channels, and the width of DMA transfers and the number of + * retries following bus errors selected applies to all four channels. + * Consequently, when initializing one channel, one must be careful not to + * destroy the configuration of other channels that are already configured. + * + * One problem with the RTEMS I/O initialization model is that no information + * other than a device major number is passed to the initialization function. + * Consequently, the sub-devices must be initialized with some pre-determined + * configuration. To change the configuration of a sub-device, it is + * necessary to either rewrite the initialization function, or to make a + * series of rtems_io_control() calls after initialization. The first + * approach is not very elegant. The second approach is acceptable if an + * application is simply changing baud rates, parity or other such + * asynchronous parameters (as supplied by the termios package). But what if + * an application requires one channel to run in HDLC or Bisync mode and + * another in async mode? With a single driver per I/O chip approach, the + * device driver must support multiple protocols. This is feasible, but it + * often means that an application that only does asynchronous I/O now links + * in code for other unused protocols, thus wasting precious ROM space. + * Worse, it requires that the sub-devices be initialized in some + * configuration, and that configuration then changed through a series of + * device driver control calls. There is no standard API in RTEMS to switch + * a serial line to some synchronous protocol. + * + * A better approach is to treat each channel as a separate device, each with + * its own device device driver. The application then supplies its own device + * driver table with only the required protocols (drivers) on each line. The + * problem with this approach is that the device drivers are not really + * independent, given that the I/O sub-devices within a common chip are not + * independent themselves. Consequently, the related device drivers must + * share some information. In RTEMS, there is no standard location in which + * to share information. + * + * This driver handles all four channels, i.e. it distinguishes the + * sub-devices using minor device numbers. Only asynchronous I/O is + * supported. The console is currently fixed to be channel 1 on the CD2401, + * which corresponds to the TTY01 port (Serial Port 2) on the MVME712M + * Transition Module. + * + * The CD2401 does either interrupt-driven or DMA I/O; it does not support + * polling. In interrupt-driven or DMA I/O modes, interrupts from the CD2401 + * are routed to the MC68040, and the processor generates an interrupt + * acknowledge cycle directly to the CD2401 to obtain an interrupt vector. + * The PCCchip2 supports a pseudo-polling mode in which interrupts from the + * CD2401 are not routed to the MC68040, but can be detected by the processor + * by reading the appropriate CD2401 registers. In this mode, interrupt + * acknowledge cycles must be generated to the CD2401 by reading the + * appropriate PCCchip2 registers. + * + * Interrupts from the four channels cannot be routed independently; either + * all channels are used in the pseudo-polling mode, or all channels are used + * in interrupt-driven/DMA mode. There is no advantage in using the speudo- + * polling mode. Consenquently, this driver performs DMA input and output. + * Output is performed directly from the termios raw output buffer, while + * input is accumulated into a separate buffer. + * + * THIS MODULE IS NOT RE-ENTRANT! Simultaneous access to a device from + * multiple tasks is likely to cause significant problems! Concurrency + * control is implemented in the termios package. + * + * THE INTERRUPT LEVEL IS SET TO 1 FOR ALL CHANNELS. + * If the CD2401 is to be used for high speed synchronous serial I/O, the + * interrupt priority might need to be increased. + * + * ALL INTERRUPT HANDLERS ARE SHARED. + * When adding extra device drivers, either rewrite the interrupt handlers + * to demultiplex the interrupts, or install separate vectors. Common vectors + * are currently used to catch spurious interrupts. We could already have + * installed separate vectors for each channel and used the spurious + * interrupt handler defined in some other BSPs, but handling spurious + * interrupts from the CD2401 in this device driver allows us to record more + * information on the source of the interrupts. Furthermore, we have observed + * the occasional spurious interrupt from channel 0. We definitely do not + * to call a debugger for those. + * + * All page references are to the MVME166/MVME167/MVME187 Single Board + * Computer Programmer's Reference Guide (MVME187PG/D2) with the April + * 1993 supplements/addenda (MVME187PG/D2A1). + */ + +/* + * Copyright (c) 1998, National Research Council of Canada + * + * 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. + */ + +#define M167_INIT + +#include <stdarg.h> +#include <stdio.h> +#include <termios.h> + +#include <rtems/console.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <bsp.h> /* Must be before libio.h */ + +/* Utility functions */ +void cd2401_udelay( unsigned long delay ); +void cd2401_chan_cmd( uint8_t channel, uint8_t cmd, uint8_t wait ); +uint16_t cd2401_bitrate_divisor( uint32_t clkrate, uint32_t * bitrate ); +void cd2401_initialize( void ); +void cd2401_interrupts_initialize( bool enable ); + +/* ISRs */ +rtems_isr cd2401_modem_isr( rtems_vector_number vector ); +rtems_isr cd2401_re_isr( rtems_vector_number vector ); +rtems_isr cd2401_rx_isr( rtems_vector_number vector ); +rtems_isr cd2401_tx_isr( rtems_vector_number vector ); + +/* Termios callbacks */ +int cd2401_firstOpen( int major, int minor, void *arg ); +int cd2401_lastClose( int major, int minor, void *arg ); +int cd2401_setAttributes( int minor, const struct termios *t ); +int cd2401_startRemoteTx( int minor ); +int cd2401_stopRemoteTx( int minor ); +ssize_t cd2401_write( int minor, const char *buf, size_t len ); +int cd2401_drainOutput( int minor ); +int _167Bug_pollRead( int minor ); +ssize_t _167Bug_pollWrite( int minor, const char *buf, size_t len ); + +/* Printk function */ +static void _BSP_output_char( char c ); +BSP_output_char_function_type BSP_output_char = _BSP_output_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +/* '\r' character in memory. This used to live on + * the stack but storing the '\r' character is + * optimized away by gcc-4.3.2 (since it seems to + * be unused [only referenced from inline assembly + * code in _167Bug_pollWrite()]). + * Hence we make it a global constant. + */ +static const char cr_char = '\r'; + +/* Channel info */ +/* static */ volatile struct { + void *tty; /* Really a struct rtems_termios_tty * */ + int len; /* Record nb of chars being TX'ed */ + const char *buf; /* Record where DMA is coming from */ + uint32_t spur_cnt; /* Nb of spurious ints so far */ + uint32_t spur_dev; /* Indo on last spurious int */ + uint32_t buserr_addr; /* Faulting address */ + uint32_t buserr_type; /* Reason of bus error during DMA */ + uint8_t own_buf_A; /* If true, buffer A belongs to the driver */ + uint8_t own_buf_B; /* If true, buffer B belongs to the driver */ + uint8_t txEmpty; /* If true, the output FIFO should be empty */ +} CD2401_Channel_Info[4]; + +/* + * The number of channels already opened. If zero, enable the interrupts. The + * initial value must be 0. If initialized explicitly, the variable ends up + * in the .data section. Its value is not re-initialized on system restart. + * Furthermore, because the variable is changed, the .data section would not + * be ROMable. We thus leave the variable uninitialized, which causes it to + * be allocated in the .bss section, and rely on RTEMS to zero the .bss + * section on every startup. + */ +uint8_t Init_count; + +/* Record previous handlers */ +rtems_isr_entry Prev_re_isr; /* Previous rx exception isr */ +rtems_isr_entry Prev_rx_isr; /* Previous rx isr */ +rtems_isr_entry Prev_tx_isr; /* Previous tx isr */ +rtems_isr_entry Prev_modem_isr; /* Previous modem/timer isr */ + +/* Define the following symbol to trace the calls to this driver */ +/* #define CD2401_RECORD_DEBUG_INFO */ +#include "console-recording.h" + +/* + * Utility functions. + */ + +/* + * Assumes that clock ticks 1 million times per second. + * + * MAXIMUM DELAY IS ABOUT 20 ms + * + * Input parameters: + * delay: Number of microseconds to delay. + * + * Output parameters: NONE + * + * Return values: NONE + */ + void cd2401_udelay +( + unsigned long delay +) +{ + unsigned long i = 20000; /* In case clock is off */ + rtems_interval start_ticks, end_ticks, current_ticks; + + start_ticks = rtems_clock_get_ticks_since_boot(); + end_ticks = start_ticks + delay; + + do { + current_ticks = rtems_clock_get_ticks_since_boot(); + } while ( --i && (current_ticks <= end_ticks) ); + + CD2401_RECORD_DELAY_INFO(( start_ticks, end_ticks, current_ticks, i )); +} + +/* + * cd2401_chan_cmd + * + * Sends a CCR command to the specified channel. Waits for any unfinished + * previous command to complete, then sends the specified command. Optionally + * wait for the current command to finish before returning. + * + * Input parameters: + * channel - CD2401 channel number + * cmd - command byte + * wait - if non-zero, wait for specified command to complete before + * returning. + * + * Output parameters: NONE + * + * Return values: NONE + */ +void cd2401_chan_cmd( + uint8_t channel, + uint8_t cmd, + uint8_t wait +) +{ + if ( channel < 4 ) { + cd2401->car = channel; /* Select channel */ + + while ( cd2401->ccr != 0 ); /* Wait for completion of previous command */ + cd2401->ccr = cmd; /* Send command */ + if ( wait ) + while( cd2401->ccr != 0 );/* Wait for completion */ + } + else { + /* This may not be the best error message */ + rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER ); + } +} + +/* + * cd2401_bitrate_divisor + * + * Compute the divisor and clock source to use to obtain the desired bitrate. + * + * Input parameters: + * clkrate - system clock rate (CLK input frequency) + * bitrate - the desired bitrate + * + * Output parameters: + * bitrate - The actual bitrate achievable, to the nearest bps. + * + * Return values: + * Returns divisor in lower byte and clock source in upper byte for the + * specified bitrate. + */ +uint16_t cd2401_bitrate_divisor( + uint32_t clkrate, + uint32_t * bitrate +) +{ + uint32_t divisor; + uint16_t clksource; + + divisor = *bitrate << 3; /* temporary; multiply by 8 for CLK/8 */ + divisor = (clkrate + (divisor>>1)) / divisor; /* divisor for clk0 (CLK/8) */ + + /* Use highest speed clock source for best precision - try clk0 to clk4 */ + for( clksource = 0; clksource < 0x0400 && divisor > 0x100; clksource += 0x0100 ) + divisor >>= 2; + divisor--; /* adjustment, see specs */ + if( divisor < 1 ) + divisor = 1; + else if( divisor > 0xFF ) + divisor = 0xFF; + *bitrate = clkrate / (1 << ((clksource >> 7)+3)) / (divisor+1); + return( clksource | divisor ); +} + +/* + * cd2401_initialize + * + * Initializes the CD2401 device. Individual channels on the chip are left in + * their default reset state, and should be subsequently configured. + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * Return values: NONE + */ +void cd2401_initialize( void ) +{ + int i; + + for ( i = 3; i >= 0; i-- ) { + CD2401_Channel_Info[i].tty = NULL; + CD2401_Channel_Info[i].len = 0; + CD2401_Channel_Info[i].buf = NULL; + CD2401_Channel_Info[i].spur_cnt = 0; + CD2401_Channel_Info[i].spur_dev = 0; + CD2401_Channel_Info[i].buserr_type = 0; + CD2401_Channel_Info[i].buserr_addr = 0; + CD2401_Channel_Info[i].own_buf_A = TRUE; + CD2401_Channel_Info[i].own_buf_B = TRUE; + CD2401_Channel_Info[i].txEmpty = TRUE; + } + + /* + * Normally, do a device reset here. If we do it, we will most likely clober + * the port settings for 167Bug on channel 0. So we just shut up all the + * ports by disabling their interrupts. + */ +#if 0 + cd2401->gfrcr = 0; /* So we can detect that device init is done */ + cd2401_chan_cmd( 0x10, 0); /* Reset all */ + while(cd2401->gfrcr == 0); /* Wait for reset all */ +#endif + + /* + * The CL-CD2400/2401 manual (part no 542400-003) states on page 87 that + * the LICR "contains the number of the interrupting channel being served. + * The channel number is always that of the current acknowledged interrupt." + * THE USER MUST PROGRAM CHANNEL NUMBER IN LICR! It is not set automatically + * by the hardware, as suggested by the manual. + * + * The updated manual (part no 542400-007) has the story straight. The + * CD2401 automatically initializes the LICR to contain the channel number + * in bits 2 and 3. However, these bits are not preserved when the user + * defined bits are written. + * + * The same vector number is used for all four channels. Different vector + * numbers could be programmed for each channel, thus avoiding the need to + * demultiplex the interrupts in the ISR. + */ + for ( i = 0; i < 4; i++ ) { + cd2401->car = i; /* Select channel */ + cd2401->livr = 0x5C; /* Motorola suggested value p. 3-15 */ + cd2401->licr = i << 2; /* Don't rely on reset value */ + cd2401->ier = 0; /* Disable all interrupts */ + } + + /* + * The content of the CD2401 xpilr registers must match the A7-A0 addresses + * generated by the PCCchip2 during interrupt acknowledge cycles in order + * for the CD2401 to recognize the IACK cycle and clear its interrupt + * request. + */ + cd2401->mpilr = 0x01; /* Match pccchip2->modem_piack p. 3-27 */ + cd2401->tpilr = 0x02; /* Match pccchip2->tx_piack p. 3-28 */ + cd2401->rpilr = 0x03; /* Match pccchip2->rx_piack p. 3-29 */ + + /* Global CD2401 registers */ + cd2401->dmr = 0; /* 16-bit DMA transfers when possible */ + cd2401->bercnt = 0; /* Do not retry DMA upon bus errors */ + + /* + * Setup timer prescaler period, which clocks timers 1 and 2 (or rx timeout + * and tx delay). The prescaler is clocked by the system clock) / 2048. The + * register must be in the range 0x0A..0xFF, ie. a rescaler period range of + * about 1ms..26ms for a nominal system clock rate of 20MHz. + */ + cd2401->tpr = 0x0A; /* Same value as 167Bug */ +} + +/* + * cd2401_interrupts_initialize + * + * This routine enables or disables the CD2401 interrupts to the MC68040. + * Interrupts cannot be enabled/disabled on a per-channel basis. + * + * Input parameters: + * enable - if true, enable the interrupts, else disable them. + * + * Output parameters: NONE + * + * Return values: NONE + * + * THE FIRST CD2401 CHANNEL OPENED SHOULD ENABLE INTERRUPTS. + * THE LAST CD2401 CHANNEL CLOSED SHOULD DISABLE INTERRUPTS. + */ +void cd2401_interrupts_initialize( + bool enable +) +{ + if ( enable ) { + /* + * Enable interrupts from the CD2401 in the PCCchip2. + * During DMA transfers, the MC68040 supplies dirty data during read cycles + * from the CD2401 and leaves the data dirty in its data cache if there is + * a cache hit. The MC68040 updates the data cache during write cycles from + * the CD2401 if there is a cache hit. + */ + pccchip2->SCC_error = 0x01; + pccchip2->SCC_modem_int_ctl = 0x10 | CD2401_INT_LEVEL; + pccchip2->SCC_tx_int_ctl = 0x10 | CD2401_INT_LEVEL; + pccchip2->SCC_rx_int_ctl = 0x50 | CD2401_INT_LEVEL; + + pccchip2->gen_control |= 0x02; /* Enable pccchip2 interrupts */ + } + else { + /* Disable interrupts */ + pccchip2->SCC_modem_int_ctl &= 0xEF; + pccchip2->SCC_tx_int_ctl &= 0xEF; + pccchip2->SCC_rx_int_ctl &= 0xEF; + } +} + +/* ISRs */ + +/* + * cd2401_modem_isr + * + * Modem/timer interrupt (group 1) from CD2401. These are not used, and not + * expected. Record as spurious and clear. + * + * Input parameters: + * vector - vector number + * + * Output parameters: NONE + * + * Return values: NONE + */ +rtems_isr cd2401_modem_isr( + rtems_vector_number vector +) +{ + uint8_t ch; + + /* Get interrupting channel ID */ + ch = cd2401->licr >> 2; + + /* Record interrupt info for debugging */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->mir << 8) | cd2401->misr; + CD2401_Channel_Info[ch].spur_cnt++; + + cd2401->meoir = 0; /* EOI */ + CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO(( ch, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); +} + +/* + * cd2401_re_isr + * + * RX exception interrupt (group 3, receiver exception) from CD2401. These are + * not used, and not expected. Record as spurious and clear. + * + * FIX THIS ISR TO DETECT BREAK CONDITIONS AND RAISE SIGINT + * + * Input parameters: + * vector - vector number + * + * Output parameters: NONE + * + * Return values: NONE + */ +rtems_isr cd2401_re_isr( + rtems_vector_number vector +) +{ + uint8_t ch; + + /* Get interrupting channel ID */ + ch = cd2401->licr >> 2; + + /* Record interrupt info for debugging */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->rir << 8) | cd2401->u5.b.risrl; + CD2401_Channel_Info[ch].spur_cnt++; + + if ( cd2401->u5.b.risrl & 0x80 ) /* Timeout interrupt? */ + cd2401->ier &= 0xDF; /* Disable rx timeout interrupt */ + cd2401->reoir = 0x08; /* EOI; exception char not read */ + CD2401_RECORD_RE_ISR_SPURIOUS_INFO(( ch, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); +} + +/* + * cd2401_rx_isr + * + * RX interrupt (group 3, receiver data) from CD2401. + * + * Input parameters: + * vector - vector number + * + * Output parameters: NONE + * + * Return values: NONE + */ +rtems_isr cd2401_rx_isr( + rtems_vector_number vector +) +{ + char c; + uint8_t ch, status, nchars, total; + #ifdef CD2401_RECORD_DEBUG_INFO + uint8_t i = 0; + char buffer[256]; + #endif + + (void) total; /* avoid set but not used warnings when not recording info */ + + status = cd2401->u5.b.risrl; + ch = cd2401->licr >> 2; + + /* Has this channel been initialized or is it a condition we ignore? */ + if ( CD2401_Channel_Info[ch].tty && !status ) { + /* Normal Rx Int, read chars, enqueue them, and issue EOI */ + total = nchars = cd2401->rfoc; /* Nb of chars to retrieve from rx FIFO */ + while ( nchars-- > 0 ) { + c = (char)cd2401->dr; /* Next char in rx FIFO */ + rtems_termios_enqueue_raw_characters( CD2401_Channel_Info[ch].tty ,&c, 1 ); + #ifdef CD2401_RECORD_DEBUG_INFO + buffer[i++] = c; + #endif + } + cd2401->reoir = 0; /* EOI */ + CD2401_RECORD_RX_ISR_INFO(( ch, total, buffer )); + } else { + /* No, record as spurious interrupt */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->rir << 8) | cd2401->u5.b.risrl; + CD2401_Channel_Info[ch].spur_cnt++; + cd2401->reoir = 0x04; /* EOI - character not read */ + CD2401_RECORD_RX_ISR_SPURIOUS_INFO(( ch, status, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); + } +} + +/* + * cd2401_tx_isr + * + * TX interrupt (group 2) from CD2401. + * + * Input parameters: + * vector - vector number + * + * Output parameters: NONE + * + * Return values: NONE + */ +rtems_isr cd2401_tx_isr( + rtems_vector_number vector +) +{ + uint8_t ch, status, buserr, initial_ier, final_ier; + + status = cd2401->tisr; + ch = cd2401->licr >> 2; + initial_ier = cd2401->ier; + + #ifndef CD2401_RECORD_DEBUG_INFO + /* + * When the debug is disabled, these variables are really not read. + * But when debug is enabled, they are. + */ + (void) initial_ier; /* avoid set but not used warning */ + (void) final_ier; /* avoid set but not used warning */ + #endif + + /* Has this channel been initialized? */ + if ( !CD2401_Channel_Info[ch].tty ) { + /* No, record as spurious interrupt */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->tir << 8) | cd2401->tisr; + CD2401_Channel_Info[ch].spur_cnt++; + final_ier = cd2401->ier &= 0xFC;/* Shut up, whoever you are */ + + cd2401->teoir = 0x88; /* EOI - Terminate buffer and no transfer */ + CD2401_RECORD_TX_ISR_SPURIOUS_INFO(( ch, status, initial_ier, final_ier, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); + return; + } + + if ( status & 0x80 ) { + /* + * Bus error occurred during DMA transfer. For now, just record. + * Get reason for DMA bus error and clear the report for the next + * occurrence + */ + buserr = pccchip2->SCC_error; + pccchip2->SCC_error = 0x01; + CD2401_Channel_Info[ch].buserr_type = + (vector << 24) | (buserr << 16) | (cd2401->tir << 8) | cd2401->tisr; + CD2401_Channel_Info[ch].buserr_addr = + (((uint32_t)cd2401->tcbadru) << 16) | cd2401->tcbadrl; + + cd2401->teoir = 0x80; /* EOI - terminate bad buffer */ + CD2401_RECORD_TX_ISR_BUSERR_INFO(( ch, status, initial_ier, buserr, + CD2401_Channel_Info[ch].buserr_type, + CD2401_Channel_Info[ch].buserr_addr )); + return; + } + + if ( status & 0x20 ) { + /* DMA done -- Turn off TxD int, turn on TxMpty */ + final_ier = cd2401->ier = (cd2401->ier & 0xFE) | 0x02; + if( status & 0x08 ) { + /* Transmit buffer B was released */ + CD2401_Channel_Info[ch].own_buf_B = TRUE; + } + else { + /* Transmit buffer A was released */ + CD2401_Channel_Info[ch].own_buf_A = TRUE; + } + CD2401_RECORD_TX_ISR_INFO(( ch, status, initial_ier, final_ier, + CD2401_Channel_Info[ch].txEmpty )); + + /* This call can result in a call to cd2401_write() */ + rtems_termios_dequeue_characters ( + CD2401_Channel_Info[ch].tty, + CD2401_Channel_Info[ch].len ); + cd2401->teoir = 0x08; /* EOI - no data transfered */ + } + else if ( status & 0x02 ) { + /* TxEmpty */ + CD2401_Channel_Info[ch].txEmpty = TRUE; + final_ier = cd2401->ier &= 0xFD;/* Shut up the interrupts */ + cd2401->teoir = 0x08; /* EOI - no data transfered */ + CD2401_RECORD_TX_ISR_INFO(( ch, status, initial_ier, final_ier, + CD2401_Channel_Info[ch].txEmpty )); + } + else { + /* Why did we get a Tx interrupt? */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->tir << 8) | cd2401->tisr; + CD2401_Channel_Info[ch].spur_cnt++; + cd2401->teoir = 0x08; /* EOI - no data transfered */ + CD2401_RECORD_TX_ISR_SPURIOUS_INFO(( ch, status, initial_ier, 0xFF, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); + } +} + +/* + * termios callbacks + */ + +/* + * cd2401_firstOpen + * + * This is the first time that this minor device (channel) is opened. + * Complete the asynchronous initialization. + * + * Input parameters: + * major - device major number + * minor - channel number + * arg - pointer to a struct rtems_libio_open_close_args_t + * + * Output parameters: NONE + * + * Return value: IGNORED + */ +int cd2401_firstOpen( + int major, + int minor, + void *arg +) +{ + rtems_libio_open_close_args_t *args = arg; + rtems_libio_ioctl_args_t newarg; + struct termios termios; + rtems_status_code sc; + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + /* + * Set up the line with the specified parameters. The difficulty is that + * the line parameters are stored in the struct termios field of a + * struct rtems_termios_tty that is not defined in a public header file. + * Therefore, we do not have direct access to the termios passed in with + * arg. So we make a rtems_termios_ioctl() call to get a pointer to the + * termios structure. + * + * THIS KLUDGE MAY BREAK IN THE FUTURE! + * + * We could have made a tcgetattr() call if we had our fd. + */ + newarg.iop = args->iop; + newarg.command = TIOCGETA; + newarg.buffer = &termios; + sc = rtems_termios_ioctl (&newarg); + if (sc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (sc); + + /* + * Turn off hardware flow control. It is a pain with 3-wire cables. + * The rtems_termios_ioctl() call below results in a call to + * cd2401_setAttributes to initialize the line. The caller will "wait" + * on the ttyMutex that it already owns; this is safe in RTEMS. + */ + termios.c_cflag |= CLOCAL; /* Ignore modem status lines */ + newarg.command = TIOCGETA; + sc = rtems_termios_ioctl (&newarg); + if (sc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (sc); + + /* Mark that the channel as initialized */ + CD2401_Channel_Info[minor].tty = args->iop->data1; + + /* If the first of the four channels to open, set up the interrupts */ + if ( !Init_count++ ) { + /* Install the interrupt handlers */ + Prev_re_isr = (rtems_isr_entry) set_vector( cd2401_re_isr, 0x5C, 1 ); + Prev_modem_isr = (rtems_isr_entry) set_vector( cd2401_modem_isr, 0x5D, 1 ); + Prev_tx_isr = (rtems_isr_entry) set_vector( cd2401_tx_isr, 0x5E, 1 ); + Prev_rx_isr = (rtems_isr_entry) set_vector( cd2401_rx_isr, 0x5F, 1 ); + + cd2401_interrupts_initialize( TRUE ); + } + + CD2401_RECORD_FIRST_OPEN_INFO(( minor, Init_count )); + + rtems_interrupt_enable (level); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_lastClose + * + * There are no more opened file descriptors to this device. Close it down. + * + * Input parameters: + * major - device major number + * minor - channel number + * arg - pointer to a struct rtems_libio_open_close_args_t + */ +int cd2401_lastClose( + int major, + int minor, + void *arg +) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + /* Mark that the channel is no longer is use */ + CD2401_Channel_Info[minor].tty = NULL; + + /* If the last of the four channels to close, disable the interrupts */ + if ( !--Init_count ) { + cd2401_interrupts_initialize( FALSE ); + + /* De-install the interrupt handlers */ + set_vector( Prev_re_isr, 0x5C, 1 ); + set_vector( Prev_modem_isr, 0x5D, 1 ); + set_vector( Prev_tx_isr, 0x5E, 1 ); + set_vector( Prev_rx_isr, 0x5F, 1 ); + } + + CD2401_RECORD_LAST_CLOSE_INFO(( minor, Init_count )); + + rtems_interrupt_enable (level); + + /* return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_setAttributes + * + * Set up the selected channel of the CD2401 chip for doing asynchronous + * I/O with DMA. + * + * The chip must already have been initialized by cd2401_initialize(). + * + * This code was written for clarity. The code space it occupies could be + * reduced. The code could also be compiled with aggressive optimization + * turned on. + * + * Input parameters: + * minor - the selected channel + * t - the termios parameters + * + * Output parameters: NONE + * + * Return value: IGNORED + */ +int cd2401_setAttributes( + int minor, + const struct termios *t +) +{ + uint8_t csize, cstopb, parodd, parenb, ignpar, inpck; + uint8_t hw_flow_ctl, sw_flow_ctl, extra_flow_ctl; + uint8_t icrnl, igncr, inlcr, brkint, ignbrk, parmrk, istrip; + uint8_t need_reinitialization = FALSE; + uint8_t read_enabled; + uint16_t tx_period, rx_period; + uint32_t out_baud, in_baud; + rtems_interrupt_level level; + + /* Determine what the line parameters should be */ + + /* baud rates */ + out_baud = rtems_termios_baud_to_number(t->c_ospeed); + in_baud = rtems_termios_baud_to_number(t->c_ispeed); + + /* Number of bits per char */ + csize = 0x07; /* to avoid a warning */ + switch ( t->c_cflag & CSIZE ) { + case CS5: csize = 0x04; break; + case CS6: csize = 0x05; break; + case CS7: csize = 0x06; break; + case CS8: csize = 0x07; break; + } + + /* Parity */ + if ( t->c_cflag & PARODD ) + parodd = 0x80; /* Odd parity */ + else + parodd = 0; + + if ( t->c_cflag & PARENB ) + parenb = 0x40; /* Parity enabled on Tx and Rx */ + else + parenb = 0x00; /* No parity on Tx and Rx */ + + /* CD2401 IGNPAR and INPCK bits are inverted wrt POSIX standard? */ + if ( t->c_iflag & INPCK ) + ignpar = 0; /* Check parity on input */ + else + ignpar = 0x10; /* Do not check parity on input */ + if ( t->c_iflag & IGNPAR ) { + inpck = 0x03; /* Discard error character */ + parmrk = 0; + } else { + if ( t->c_iflag & PARMRK ) { + inpck = 0x01; /* Translate to 0xFF 0x00 <char> */ + parmrk = 0x04; + } else { + inpck = 0x01; /* Translate to 0x00 */ + parmrk = 0; + } + } + + /* Stop bits */ + if ( t->c_cflag & CSTOPB ) + cstopb = 0x04; /* Two stop bits */ + else + cstopb = 0x02; /* One stop bit */ + + /* Modem flow control */ + if ( t->c_cflag & CLOCAL ) + hw_flow_ctl = 0x04; /* Always assert RTS before Tx */ + else + hw_flow_ctl = 0x07; /* Always assert RTS before Tx, + wait for CTS and DSR */ + + /* XON/XOFF Tx flow control */ + if ( t->c_iflag & IXON ) { + sw_flow_ctl = 0x40; /* Tx in-band flow ctl enabled, wait for XON */ + extra_flow_ctl = 0x30; /* Eat XON/XOFF, XON/XOFF in SCHR1, SCHR2 */ + } + else { + sw_flow_ctl = 0; /* Tx in-band flow ctl disabled */ + extra_flow_ctl = 0; /* Pass on XON/XOFF */ + } + + /* CL/LF translation */ + if ( t->c_iflag & ICRNL ) + icrnl = 0x40; /* Map CR to NL on input */ + else + icrnl = 0; /* Pass on CR */ + if ( t->c_iflag & INLCR ) + inlcr = 0x20; /* Map NL to CR on input */ + else + inlcr = 0; /* Pass on NL */ + if ( t->c_iflag & IGNCR ) + igncr = 0x80; /* CR discarded on input */ + else + igncr = 0; + + /* Break handling */ + if ( t->c_iflag & IGNBRK ) { + ignbrk = 0x10; /* Ignore break on input */ + brkint = 0x08; + } else { + if ( t->c_iflag & BRKINT ) { + ignbrk = 0; /* Generate SIGINT (interrupt ) */ + brkint = 0; + } else { + ignbrk = 0; /* Convert to 0x00 */ + brkint = 0x08; + } + } + + /* Stripping */ + if ( t->c_iflag & ISTRIP ) + istrip = 0x80; /* Strip to 7 bits */ + else + istrip = 0; /* Leave as 8 bits */ + + rx_period = cd2401_bitrate_divisor( 20000000Ul, &in_baud ); + tx_period = cd2401_bitrate_divisor( 20000000Ul, &out_baud ); + + /* + * If this is the first time that the line characteristics are set up, then + * the device must be re-initialized. + * Also check if we need to change anything. It is preferable to not touch + * the device if nothing changes. As soon as we touch it, it tends to + * glitch. If anything changes, we reprogram all registers. This is + * harmless. + */ + if ( ( CD2401_Channel_Info[minor].tty == 0 ) || + ( cd2401->cor1 != (parodd | parenb | ignpar | csize) ) || + ( cd2401->cor2 != (sw_flow_ctl | hw_flow_ctl) ) || + ( cd2401->cor3 != (extra_flow_ctl | cstopb) ) || + ( cd2401->cor6 != (igncr | icrnl | inlcr | ignbrk | brkint | parmrk | inpck) ) || + ( cd2401->cor7 != istrip ) || + ( cd2401->u1.async.schr1 != t->c_cc[VSTART] ) || + ( cd2401->u1.async.schr2 != t->c_cc[VSTOP] ) || + ( cd2401->rbpr != (unsigned char)rx_period ) || + ( cd2401->rcor != (unsigned char)(rx_period >> 8) ) || + ( cd2401->tbpr != (unsigned char)tx_period ) || + ( cd2401->tcor != ( (tx_period >> 3) & 0xE0 ) ) ) + need_reinitialization = TRUE; + + /* Write to the ports */ + rtems_interrupt_disable (level); + + cd2401->car = minor; /* Select channel */ + read_enabled = cd2401->csr & 0x80 ? TRUE : FALSE; + + if ( (t->c_cflag & CREAD ? TRUE : FALSE ) != read_enabled ) { + /* Read enable status is changing */ + need_reinitialization = TRUE; + } + + if ( need_reinitialization ) { + /* + * Could not find a way to test whether the CD2401 was done transmitting. + * The TxEmpty interrupt does not seem to indicate that the FIFO is empty + * in DMA mode. So, just wait a while for output to drain. May not be + * enough, but it will have to do (should be long enough for 1 char at + * 9600 bsp)... + */ + cd2401_udelay( 2000L ); + + /* Clear channel */ + cd2401_chan_cmd (minor, 0x40, 1); + + cd2401->car = minor; /* Select channel */ + cd2401->cmr = 0x42; /* Interrupt Rx, DMA Tx, async mode */ + cd2401->cor1 = parodd | parenb | ignpar | csize; + cd2401->cor2 = sw_flow_ctl | hw_flow_ctl; + cd2401->cor3 = extra_flow_ctl | cstopb; + cd2401->cor4 = 0x0A; /* No DSR/DCD/CTS detect; FIFO threshold of 10 */ + cd2401->cor5 = 0x0A; /* No DSR/DCD/CTS detect; DTR threshold of 10 */ + cd2401->cor6 = igncr | icrnl | inlcr | ignbrk | brkint | parmrk | inpck; + cd2401->cor7 = istrip; /* No LNext; ignore XON/XOFF if frame error; no tx translations */ + /* Special char 1: XON character */ + cd2401->u1.async.schr1 = t->c_cc[VSTART]; + /* special char 2: XOFF character */ + cd2401->u1.async.schr2 = t->c_cc[VSTOP]; + + /* + * Special chars 3 and 4, char range, LNext, RFAR[1..4] and CRC + * are unused, left as is. + */ + + /* Set baudrates for receiver and transmitter */ + cd2401->rbpr = (unsigned char)rx_period; + cd2401->rcor = (unsigned char)(rx_period >> 8); /* no DPLL */ + cd2401->tbpr = (unsigned char)tx_period; + cd2401->tcor = (tx_period >> 3) & 0xE0; /* no x1 ext clk, no loopback */ + + /* Timeout for 4 chars at 9600, 8 bits per char, 1 stop bit */ + cd2401->u2.w.rtpr = 0x04; /* NEED TO LOOK AT THIS LINE! */ + + if ( t->c_cflag & CREAD ) { + /* Re-initialize channel, enable rx and tx */ + cd2401_chan_cmd (minor, 0x2A, 1); + /* Enable rx data ints */ + cd2401->ier = 0x08; + } else { + /* Re-initialize channel, enable tx, disable rx */ + cd2401_chan_cmd (minor, 0x29, 1); + } + } + + CD2401_RECORD_SET_ATTRIBUTES_INFO(( minor, need_reinitialization, csize, + cstopb, parodd, parenb, ignpar, inpck, + hw_flow_ctl, sw_flow_ctl, extra_flow_ctl, + icrnl, igncr, inlcr, brkint, ignbrk, + parmrk, istrip, tx_period, rx_period, + out_baud, in_baud )); + + rtems_interrupt_enable (level); + + /* + * Looks like the CD2401 needs time to settle after initialization. Give it + * 10 ms. I don't really believe it, but if output resumes to quickly after + * this call, the first few characters are not right. + */ + if ( need_reinitialization ) + cd2401_udelay( 10000L ); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_startRemoreTx + * + * Defined as a callback, but it would appear that it is never called. The + * POSIX standard states that when the tcflow() function is called with the + * TCION action, the system wall transmit a START character. Presumably, + * tcflow() is called internally when IXOFF is set in the termios c_iflag + * field when the input buffer can accomodate enough characters. It should + * probably be called from fillBufferQueue(). Clearly, the function is also + * explicitly callable by user code. The action is clearly to send the START + * character, regardless of whether START/STOP flow control is in effect. + * + * Input parameters: + * minor - selected channel + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * PROPER START CHARACTER MUST BE PROGRAMMED IN SCHR1. + */ +int cd2401_startRemoteTx( + int minor +) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + cd2401->car = minor; /* Select channel */ + cd2401->stcr = 0x01; /* Send SCHR1 ahead of chars in FIFO */ + + CD2401_RECORD_START_REMOTE_TX_INFO(( minor )); + + rtems_interrupt_enable (level); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_stopRemoteTx + * + * Defined as a callback, but it would appear that it is never called. The + * POSIX standard states that when the tcflow() function is called with the + * TCIOFF function, the system wall transmit a STOP character. Presumably, + * tcflow() is called internally when IXOFF is set in the termios c_iflag + * field as the input buffer is about to overflow. It should probably be + * called from rtems_termios_enqueue_raw_characters(). Clearly, the function + * is also explicitly callable by user code. The action is clearly to send + * the STOP character, regardless of whether START/STOP flow control is in + * effect. + * + * Input parameters: + * minor - selected channel + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * PROPER STOP CHARACTER MUST BE PROGRAMMED IN SCHR2. + */ +int cd2401_stopRemoteTx( + int minor +) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + cd2401->car = minor; /* Select channel */ + cd2401->stcr = 0x02; /* Send SCHR2 ahead of chars in FIFO */ + + CD2401_RECORD_STOP_REMOTE_TX_INFO(( minor )); + + rtems_interrupt_enable (level); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_write + * + * Initiate DMA output. Termios guarantees that the buffer does not wrap + * around, so we can do DMA strait from the supplied buffer. + * + * Input parameters: + * minor - selected channel + * buf - output buffer + * len - number of chars to output + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * MUST BE EXECUTED WITH THE CD2401 INTERRUPTS DISABLED! + * The processor is placed at interrupt level CD2401_INT_LEVEL explicitly in + * console_write(). The processor is necessarily at interrupt level 1 in + * cd2401_tx_isr(). + */ +ssize_t cd2401_write( + int minor, + const char *buf, + size_t len +) +{ + if (len > 0) { + cd2401->car = minor; /* Select channel */ + + if ( (cd2401->dmabsts & 0x08) == 0 ) { + /* Next buffer is A. Wait for it to be ours. */ + while ( cd2401->atbsts & 0x01 ); + + CD2401_Channel_Info[minor].own_buf_A = FALSE; + CD2401_Channel_Info[minor].len = len; + CD2401_Channel_Info[minor].buf = buf; + cd2401->atbadru = (uint16_t)( ( (uint32_t) buf ) >> 16 ); + cd2401->atbadrl = (uint16_t)( (uint32_t) buf ); + cd2401->atbcnt = len; + CD2401_RECORD_WRITE_INFO(( len, buf, 'A' )); + cd2401->atbsts = 0x03; /* CD2401 owns buffer, int when empty */ + } + else { + /* Next buffer is B. Wait for it to be ours. */ + while ( cd2401->btbsts & 0x01 ); + + CD2401_Channel_Info[minor].own_buf_B = FALSE; + CD2401_Channel_Info[minor].len = len; + CD2401_Channel_Info[minor].buf = buf; + cd2401->btbadru = (uint16_t)( ( (uint32_t) buf ) >> 16 ); + cd2401->btbadrl = (uint16_t)( (uint32_t) buf ); + cd2401->btbcnt = len; + CD2401_RECORD_WRITE_INFO(( len, buf, 'B' )); + cd2401->btbsts = 0x03; /* CD2401 owns buffer, int when empty */ + } + /* Nuts -- Need TxD ints */ + CD2401_Channel_Info[minor].txEmpty = FALSE; + cd2401->ier |= 0x01; + } + + /* Return something */ + return len; +} + +#if 0 +/* + * cd2401_drainOutput + * + * Wait for the txEmpty indication on the specified channel. + * + * Input parameters: + * minor - selected channel + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * MUST NOT BE EXECUTED WITH THE CD2401 INTERRUPTS DISABLED! + * The txEmpty flag is set by the tx ISR. + * + * DOES NOT WORK! DO NOT ENABLE THIS CODE. THE CD2401 DOES NOT COOPERATE! + * The code is here to document that the output FIFO is NOT empty when + * the CD2401 reports that the Tx buffer is empty. + */ +int cd2401_drainOutput( + int minor +) +{ + CD2401_RECORD_DRAIN_OUTPUT_INFO(( CD2401_Channel_Info[minor].txEmpty, + CD2401_Channel_Info[minor].own_buf_A, + CD2401_Channel_Info[minor].own_buf_B )); + + while( ! (CD2401_Channel_Info[minor].txEmpty && + CD2401_Channel_Info[minor].own_buf_A && + CD2401_Channel_Info[minor].own_buf_B) ); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} +#endif + +/* + * _167Bug_pollRead + * + * Read a character from the 167Bug console, and return it. Return -1 + * if there is no character in the input FIFO. + * + * Input parameters: + * minor - selected channel + * + * Output parameters: NONE + * + * Return value: char returned as positive signed int + * -1 if no character is present in the input FIFO. + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +int _167Bug_pollRead( + int minor +) +{ + int char_not_available; + unsigned char c; + rtems_interrupt_level previous_level; + + /* + * Redirection of .INSTAT does not work: 167-Bug crashes. + * Switch the input stream to the specified port. + * Make sure this is atomic code. + */ + rtems_interrupt_disable( previous_level ); + + __asm__ volatile( "movew %1, -(%%sp)\n\t"/* Channel */ + "trap #15\n\t" /* Trap to 167Bug */ + ".short 0x61\n\t" /* Code for .REDIR_I */ + "trap #15\n\t" /* Trap to 167Bug */ + ".short 0x01\n\t" /* Code for .INSTAT */ + "move %%cc, %0\n\t" /* Get condition codes */ + "andil #4, %0" /* Keep the Zero bit */ + : "=d" (char_not_available) : "d" (minor): "%%cc" ); + + if (char_not_available) { + rtems_interrupt_enable( previous_level ); + return -1; + } + + /* Read the char and return it */ + __asm__ volatile( "subq.l #2,%%a7\n\t" /* Space for result */ + "trap #15\n\t" /* Trap to 167 Bug */ + ".short 0x00\n\t" /* Code for .INCHR */ + "moveb (%%a7)+, %0" /* Pop char into c */ + : "=d" (c) : ); + + rtems_interrupt_enable( previous_level ); + + return (int)c; +} + +/* + * _167Bug_pollWrite + * + * Output buffer through 167Bug. Returns only once every character has been + * sent (polled output). + * + * Input parameters: + * minor - selected channel + * buf - output buffer + * len - number of chars to output + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +ssize_t _167Bug_pollWrite( + int minor, + const char *buf, + size_t len +) +{ + const char *endbuf = buf + len; + + __asm__ volatile( "pea (%0)\n\t" /* endbuf */ + "pea (%1)\n\t" /* buf */ + "movew #0x21, -(%%sp)\n\t" /* Code for .OUTSTR */ + "movew %2, -(%%sp)\n\t" /* Channel */ + "trap #15\n\t" /* Trap to 167Bug */ + ".short 0x60" /* Code for .REDIR */ + :: "a" (endbuf), "a" (buf), "d" (minor) ); + + /* Return something */ + return len; +} + +/* + * do_poll_read + * + * Input characters through 167Bug. Returns has soon as a character has been + * received. Otherwise, if we wait for the number of requested characters, we + * could be here forever! + * + * CR is converted to LF on input. The terminal should not send a CR/LF pair + * when the return or enter key is pressed. + * + * Input parameters: + * major - ignored. Should be the major number for this driver. + * minor - selected channel. + * arg->buffer - where to put the received characters. + * arg->count - number of characters to receive before returning--Ignored. + * + * Output parameters: + * arg->bytes_moved - the number of characters read. Always 1. + * + * Return value: RTEMS_SUCCESSFUL + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +static rtems_status_code do_poll_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args = arg; + int c; + + while( (c = _167Bug_pollRead (minor)) == -1 ); + rw_args->buffer[0] = (uint8_t)c; + if( rw_args->buffer[0] == '\r' ) + rw_args->buffer[0] = '\n'; + rw_args->bytes_moved = 1; + return RTEMS_SUCCESSFUL; +} + +/* + * do_poll_write + * + * Output characters through 167Bug. Returns only once every character has + * been sent. + * + * CR is transmitted AFTER a LF on output. + * + * Input parameters: + * major - ignored. Should be the major number for this driver. + * minor - selected channel + * arg->buffer - where to get the characters to transmit. + * arg->count - the number of characters to transmit before returning. + * + * Output parameters: + * arg->bytes_moved - the number of characters read + * + * Return value: RTEMS_SUCCESSFUL + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +static rtems_status_code do_poll_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args = arg; + uint32_t i; + + for( i = 0; i < rw_args->count; i++ ) { + _167Bug_pollWrite(minor, &(rw_args->buffer[i]), 1); + if ( rw_args->buffer[i] == '\n' ) + _167Bug_pollWrite(minor, &cr_char, 1); + } + rw_args->bytes_moved = i; + return RTEMS_SUCCESSFUL; +} + +/* + * _BSP_output_char + * + * printk() function prototyped in bspIo.h. Does not use termios. + */ +void _BSP_output_char(char c) +{ + rtems_device_minor_number printk_minor; + + /* + * Can't rely on console_initialize having been called before this function + * is used. + */ + if ( NVRAM_CONFIGURE ) + /* J1-4 is on, use NVRAM info for configuration */ + printk_minor = (nvram->console_printk_port & 0x30) >> 4; + else + printk_minor = PRINTK_MINOR; + + _167Bug_pollWrite(printk_minor, &c, 1); +} + +/* + *************** + * BOILERPLATE * + *************** + * + * All these functions are prototyped in rtems/c/src/lib/include/console.h. + */ + +/* + * Initialize and register the device + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + rtems_device_minor_number console_minor; + + /* + * Set up TERMIOS if needed + */ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + console_minor = nvram->console_printk_port & 0x03; + + if ( nvram->console_mode & 0x01 ) + /* termios */ + rtems_termios_initialize (); + } + else { + console_minor = CONSOLE_MINOR; +#if CD2401_USE_TERMIOS == 1 + rtems_termios_initialize (); +#endif + } + + /* + * Do device-specific initialization + * Does not affect 167-Bug. + */ + cd2401_initialize (); + + /* + * Register the devices + */ + status = rtems_io_register_name ("/dev/tty0", major, 0); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/tty1", major, 1); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/console", major, console_minor); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/tty2", major, 2); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/tty3", major, 3); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + return RTEMS_SUCCESSFUL; +} + +/* + * Open the device + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + static const rtems_termios_callbacks pollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + _167Bug_pollRead, /* pollRead */ + _167Bug_pollWrite, /* write */ + NULL, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 0 /* outputUsesInterrupts */ + }; + + static const rtems_termios_callbacks intrCallbacks = { + cd2401_firstOpen, /* firstOpen */ + cd2401_lastClose, /* lastClose */ + NULL, /* pollRead */ + cd2401_write, /* write */ + cd2401_setAttributes, /* setAttributes */ + cd2401_stopRemoteTx, /* stopRemoteTx */ + cd2401_startRemoteTx, /* startRemoteTx */ + 1 /* outputUsesInterrupts */ + }; + + if ( NVRAM_CONFIGURE ) + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + if ( nvram->console_mode & 0x02 ) + /* interrupt-driven I/O */ + return rtems_termios_open (major, minor, arg, &intrCallbacks); + else + /* polled I/O */ + return rtems_termios_open (major, minor, arg, &pollCallbacks); + else + /* no termios -- default to polled I/O */ + return RTEMS_SUCCESSFUL; +#if CD2401_USE_TERMIOS == 1 +#if CD2401_IO_MODE != 1 + else + /* termios & polled I/O*/ + return rtems_termios_open (major, minor, arg, &pollCallbacks); +#else + else + /* termios & interrupt-driven I/O*/ + return rtems_termios_open (major, minor, arg, &intrCallbacks); +#endif +#else + else + /* no termios -- default to polled I/O */ + return RTEMS_SUCCESSFUL; +#endif +} + +/* + * Close the device + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + return rtems_termios_close (arg); + else + /* no termios */ + return RTEMS_SUCCESSFUL; + } +#if CD2401_USE_TERMIOS == 1 + else + /* termios */ + return rtems_termios_close (arg); +#else + else + /* no termios */ + return RTEMS_SUCCESSFUL; +#endif +} + +/* + * Read from the device + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + return rtems_termios_read (arg); + else + /* no termios -- default to polled */ + return do_poll_read (major, minor, arg); + } +#if CD2401_USE_TERMIOS == 1 + else + /* termios */ + return rtems_termios_read (arg); +#else + else + /* no termios -- default to polled */ + return do_poll_read (major, minor, arg); +#endif +} + +/* + * Write to the device + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + return rtems_termios_write (arg); + else + /* no termios -- default to polled */ + return do_poll_write (major, minor, arg); + } +#if CD2401_USE_TERMIOS == 1 + else + /* termios */ + return rtems_termios_write (arg); +#else + else + /* no termios -- default to polled */ + return do_poll_write (major, minor, arg); +#endif +} + +/* + * Handle ioctl request. + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + return rtems_termios_ioctl (arg); + else + /* no termios -- default to polled */ + return RTEMS_SUCCESSFUL; + } +#if CD2401_USE_TERMIOS == 1 + else + /* termios */ + return rtems_termios_ioctl (arg); +#else + else + /* no termios -- default to polled */ + return RTEMS_SUCCESSFUL; +#endif +} diff --git a/bsps/m68k/uC5282/console/console.c b/bsps/m68k/uC5282/console/console.c new file mode 100644 index 0000000000..276d0c6f18 --- /dev/null +++ b/bsps/m68k/uC5282/console/console.c @@ -0,0 +1,774 @@ +/* + * Multi UART console serial I/O. + * + * TO DO: Add DMA input/output + * + * Author: W. Eric Norum <norume@aps.anl.gov> + * + * COPYRIGHT (c) 2005. + * 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.org/license/LICENSE. + * + */ + +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> +#include <malloc.h> + +#include <rtems/console.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <bsp.h> + +#include <rtems/bspIo.h> + +#define UART_INTC0_IRQ_VECTOR(x) (64+13+(x)) + +#define MCF5282_UART_USR_ERROR ( MCF5282_UART_USR_RB | \ + MCF5282_UART_USR_FE | \ + MCF5282_UART_USR_PE | \ + MCF5282_UART_USR_OE ) + +static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len); +static ssize_t IntUartInterruptWrite (int minor, const char *buf, size_t len); + +static void +_BSP_null_char( char c ) +{ + int level; + + rtems_interrupt_disable(level); + while ( (MCF5282_UART_USR(CONSOLE_PORT) & MCF5282_UART_USR_TXRDY) == 0 ) + continue; + MCF5282_UART_UTB(CONSOLE_PORT) = c; + while ( (MCF5282_UART_USR(CONSOLE_PORT) & MCF5282_UART_USR_TXRDY) == 0 ) + continue; + rtems_interrupt_enable(level); +} + +BSP_polling_getchar_function_type BSP_poll_char = NULL; +BSP_output_char_function_type BSP_output_char = _BSP_null_char; + +/* + * The MCF5282 has three UARTs. Enable all them here. I/O pin selection + * is assumed to have been done elsewher. + */ +#define MAX_UART_INFO 3 +#define RX_BUFFER_SIZE 512 + +struct IntUartInfoStruct +{ + int iomode; + volatile int uimr; + int baud; + int databits; + int parity; + int stopbits; + int hwflow; + int rx_in; + int rx_out; + char rx_buffer[RX_BUFFER_SIZE]; + void *ttyp; +}; + +struct IntUartInfoStruct IntUartInfo[MAX_UART_INFO]; + +/*************************************************************************** + Function : IntUartSet + + Description : This updates the hardware UART settings. + ***************************************************************************/ +static void +IntUartSet(int minor, int baud, int databits, int parity, int stopbits, int hwflow) +{ + int divisor; + uint32_t clock_speed; + uint8_t umr1 = 0; + uint8_t umr2 = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + int level; + + rtems_interrupt_disable(level); + + + /* disable interrupts, clear RTS line, and disable the UARTS */ + MCF5282_UART_UIMR(minor) = 0; + MCF5282_UART_UOP0(minor) = 1; + MCF5282_UART_UCR(minor) = (MCF5282_UART_UCR_TX_DISABLED | MCF5282_UART_UCR_RX_DISABLED); + + /* save the current values */ + info->uimr = 0; + info->baud = baud; + info->databits = databits; + info->parity = parity; + info->stopbits = stopbits; + info->hwflow = hwflow; + + clock_speed = bsp_get_CPU_clock_speed(); + /* determine the baud divisor value */ + divisor = (clock_speed / ( 32 * baud )); + if ( divisor < 2 ) { + divisor = 2; + } + + /* check to see if doing hardware flow control */ + if ( hwflow ) + { + /* set hardware flow options */ + umr1 |= MCF5282_UART_UMR1_RXRTS; + umr2 |= MCF5282_UART_UMR2_TXCTS; + } + + /* determine the new umr values */ + umr1 |= (parity | databits); + umr2 |= (stopbits); + + /* reset the uart */ + MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_ERROR; + MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_RX; + MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_TX; + + /* reset the uart mode register and update values */ + MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_MR; + MCF5282_UART_UMR(minor) = umr1; + MCF5282_UART_UMR(minor) = umr2; + + /* set the baud rate values */ + MCF5282_UART_UCSR(minor) = (MCF5282_UART_UCSR_RCS_SYS_CLK | MCF5282_UART_UCSR_TCS_SYS_CLK); + MCF5282_UART_UBG1(minor) = (divisor & 0xff00) >> 8; + MCF5282_UART_UBG2(minor) = (divisor & 0x00ff); + + /* enable the uart */ + MCF5282_UART_UCR(minor) = (MCF5282_UART_UCR_TX_ENABLED | MCF5282_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if ( info->iomode != TERMIOS_POLLED ) + { + /* enable rx interrupts */ + info->uimr |= MCF5282_UART_UIMR_FFULL; + MCF5282_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if ( hwflow ) + { + /* assert the RTS line */ + MCF5282_UART_UOP1(minor) = 1; + } + + rtems_interrupt_enable(level); + +} + + +/*************************************************************************** + Function : IntUartSetAttributes + + Description : This provides the hardware-dependent portion of tcsetattr(). + value and sets it. At the moment this just sets the baud rate. + + Note: The highest baudrate is 115200 as this stays within + an error of +/- 5% at 25MHz processor clock + ***************************************************************************/ +static int +IntUartSetAttributes(int minor, const struct termios *t) +{ + /* set default index values */ + int baud = (int)9600; + int databits = (int)MCF5282_UART_UMR1_BC_8; + int parity = (int)MCF5282_UART_UMR1_PM_NONE; + int stopbits = (int)MCF5282_UART_UMR2_STOP_BITS_1; + int hwflow = (int)0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* check to see if input is valid */ + if ( t != (const struct termios *)0 ) + { + /* determine baud rate index */ + baud = rtems_termios_baud_to_number(t->c_ospeed); + + /* determine data bits */ + switch ( t->c_cflag & CSIZE ) + { + case CS5: + databits = (int)MCF5282_UART_UMR1_BC_5; + break; + case CS6: + databits = (int)MCF5282_UART_UMR1_BC_6; + break; + case CS7: + databits = (int)MCF5282_UART_UMR1_BC_7; + break; + case CS8: + databits = (int)MCF5282_UART_UMR1_BC_8; + break; + } + + /* determine if parity is enabled */ + if ( t->c_cflag & PARENB ) + { + if ( t->c_cflag & PARODD ) + { + /* odd parity */ + parity = (int)MCF5282_UART_UMR1_PM_ODD; + } + else + { + /* even parity */ + parity = (int)MCF5282_UART_UMR1_PM_EVEN; + } + } + + /* determine stop bits */ + if ( t->c_cflag & CSTOPB ) + { + /* two stop bits */ + stopbits = (int)MCF5282_UART_UMR2_STOP_BITS_2; + } + + /* check to see if hardware flow control */ + if ( t->c_cflag & CRTSCTS ) + { + hwflow = 1; + } + } + + /* check to see if values have changed */ + if ( ( baud != info->baud ) || + ( databits != info->databits ) || + ( parity != info->parity ) || + ( stopbits != info->stopbits ) || + ( hwflow != info->hwflow ) ) + { + + /* call function to set values */ + IntUartSet(minor, baud, databits, parity, stopbits, hwflow); + } + + return( RTEMS_SUCCESSFUL ); + +} + +/*************************************************************************** + Function : IntUartInterruptHandler + + Description : This is the interrupt handler for the internal uart. It + determines which channel caused the interrupt before queueing any received + chars and dequeueing chars waiting for transmission. + ***************************************************************************/ +static rtems_isr +IntUartInterruptHandler(rtems_vector_number v) +{ + unsigned int chan = v - UART_INTC0_IRQ_VECTOR(0); + struct IntUartInfoStruct *info = &IntUartInfo[chan]; + + /* check to see if received data */ + if ( MCF5282_UART_UISR(chan) & MCF5282_UART_UISR_RXRDY ) + { + /* read data and put into the receive buffer */ + while ( MCF5282_UART_USR(chan) & MCF5282_UART_USR_RXRDY ) + { + + if ( MCF5282_UART_USR(chan) & MCF5282_UART_USR_ERROR ) + { + /* clear the error */ + MCF5282_UART_UCR(chan) = MCF5282_UART_UCR_RESET_ERROR; + } + /* put data in rx buffer and check for errors */ + info->rx_buffer[info->rx_in] = MCF5282_UART_URB(chan); + + /* update buffer values */ + info->rx_in++; + + if ( info->rx_in >= RX_BUFFER_SIZE ) + { + info->rx_in = 0; + } + } + /* Make sure the port has been opened */ + if ( info->ttyp ) + { + + /* check to see if task driven */ + if ( info->iomode == TERMIOS_TASK_DRIVEN ) + { + /* notify rx task that rx buffer has data */ + rtems_termios_rxirq_occured(info->ttyp); + } + else + { + /* Push up the received data */ + rtems_termios_enqueue_raw_characters(info->ttyp, info->rx_buffer, info->rx_in); + info->rx_in = 0; + } + } + } + + /* check to see if data needs to be transmitted */ + if ( ( info->uimr & MCF5282_UART_UIMR_TXRDY ) && + ( MCF5282_UART_UISR(chan) & MCF5282_UART_UISR_TXRDY ) ) + { + + /* disable tx interrupts */ + info->uimr &= ~MCF5282_UART_UIMR_TXRDY; + MCF5282_UART_UIMR(chan) = info->uimr; + + /* tell upper level that character has been sent */ + if ( info->ttyp ) + rtems_termios_dequeue_characters(info->ttyp, 1); + } +} + + + +/*************************************************************************** + Function : IntUartInitialize + + Description : This initialises the internal uart hardware for all + internal uarts. If the internal uart is to be interrupt driven then the + interrupt vectors are hooked. + ***************************************************************************/ +static void +IntUartInitialize(void) +{ + unsigned int chan; + struct IntUartInfoStruct *info; + rtems_isr_entry old_handler; + int level; + + for ( chan = 0; chan < MAX_UART_INFO; chan++ ) + { + info = &IntUartInfo[chan]; + + info->ttyp = NULL; + info->rx_in = 0; + info->rx_out = 0; + info->baud = -1; + info->databits = -1; + info->parity = -1; + info->stopbits = -1; + info->hwflow = -1; + + MCF5282_UART_UACR(chan) = 0; + MCF5282_UART_UIMR(chan) = 0; + if ( info->iomode != TERMIOS_POLLED ) + { + rtems_interrupt_catch (IntUartInterruptHandler, + UART_INTC0_IRQ_VECTOR(chan), + &old_handler); + } + + /* set uart default values */ + IntUartSetAttributes(chan, NULL); + + /* unmask interrupt */ + rtems_interrupt_disable(level); + switch(chan) { + case 0: + bsp_allocate_interrupt(UART0_IRQ_LEVEL, UART0_IRQ_PRIORITY); + MCF5282_INTC0_ICR13 = MCF5282_INTC_ICR_IL(UART0_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(UART0_IRQ_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT13 | + MCF5282_INTC_IMRL_MASKALL); + break; + + case 1: + bsp_allocate_interrupt(UART1_IRQ_LEVEL, UART1_IRQ_PRIORITY); + MCF5282_INTC0_ICR14 = MCF5282_INTC_ICR_IL(UART1_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(UART1_IRQ_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT14 | + MCF5282_INTC_IMRL_MASKALL); + break; + + case 2: + bsp_allocate_interrupt(UART2_IRQ_LEVEL, UART2_IRQ_PRIORITY); + MCF5282_INTC0_ICR15 = MCF5282_INTC_ICR_IL(UART2_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(UART2_IRQ_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT15 | + MCF5282_INTC_IMRL_MASKALL); + break; + } + rtems_interrupt_enable(level); + + } /* of chan loop */ + + +} /* IntUartInitialise */ + + +/*************************************************************************** + Function : IntUartInterruptWrite + + Description : This writes a single character to the appropriate uart + channel. This is either called during an interrupt or in the user's task + to initiate a transmit sequence. Calling this routine enables Tx + interrupts. + ***************************************************************************/ +static ssize_t +IntUartInterruptWrite (int minor, const char *buf, size_t len) +{ + if (len > 0) { + /* write out character */ + MCF5282_UART_UTB(minor) = *buf; + + /* enable tx interrupt */ + IntUartInfo[minor].uimr |= MCF5282_UART_UIMR_TXRDY; + MCF5282_UART_UIMR(minor) = IntUartInfo[minor].uimr; + } + + return 0; +} + +/*************************************************************************** + Function : IntUartInterruptOpen + + Description : This enables interrupts when the tty is opened. + ***************************************************************************/ +static int +IntUartInterruptOpen(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + int level; + + /* + * Enable serial I/O pin assignments + */ + rtems_interrupt_disable(level); + switch(minor) { + case 0: + MCF5282_GPIO_PUAPAR |= MCF5282_GPIO_PUAPAR_PUAPA1|MCF5282_GPIO_PUAPAR_PUAPA0; + break; + case 1: + MCF5282_GPIO_PUAPAR |= MCF5282_GPIO_PUAPAR_PUAPA3|MCF5282_GPIO_PUAPAR_PUAPA2; + break; + case 2: + MCF5282_GPIO_PASPAR = + (MCF5282_GPIO_PASPAR + & ~(MCF5282_GPIO_PASPAR_PASPA3(3)|MCF5282_GPIO_PASPAR_PASPA2(3))) + | (MCF5282_GPIO_PASPAR_PASPA3(2)|MCF5282_GPIO_PASPAR_PASPA2(2)); + break; + } + rtems_interrupt_enable(level); + /* enable the uart */ + MCF5282_UART_UCR(minor) = (MCF5282_UART_UCR_TX_ENABLED | MCF5282_UART_UCR_RX_ENABLED); + + /* check to see if interrupts need to be enabled */ + if ( info->iomode != TERMIOS_POLLED ) + { + /* enable rx interrupts */ + info->uimr |= MCF5282_UART_UIMR_FFULL; + MCF5282_UART_UIMR(minor) = info->uimr; + } + + /* check to see if doing hardware flow control */ + if ( info->hwflow ) + { + /* assert the RTS line */ + MCF5282_UART_UOP1(minor) = 1; + } + + return( 0 ); +} + + + +/*************************************************************************** + Function : IntUartInterruptClose + + Description : This disables interrupts when the tty is closed. + ***************************************************************************/ +static int +IntUartInterruptClose(int major, int minor, void *arg) +{ + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* disable the interrupts and the uart */ + MCF5282_UART_UIMR(minor) = 0; + MCF5282_UART_UCR(minor) = (MCF5282_UART_UCR_TX_DISABLED | MCF5282_UART_UCR_RX_DISABLED); + + /* reset values */ + info->ttyp = NULL; + info->uimr = 0; + info->rx_in = 0; + info->rx_out = 0; + + return( 0 ); +} + +/*************************************************************************** + Function : IntUartTaskRead + + Description : This reads all available characters from the internal uart + and places them into the termios buffer. The rx interrupts will be + re-enabled after all data has been read. + ***************************************************************************/ +static int +IntUartTaskRead(int minor) +{ + char buffer[RX_BUFFER_SIZE]; + int count; + int rx_in; + int index = 0; + struct IntUartInfoStruct *info = &IntUartInfo[minor]; + + /* determine number of values to copy out */ + rx_in = info->rx_in; + if ( info->rx_out <= rx_in ) + { + count = rx_in - info->rx_out; + } + else + { + count = (RX_BUFFER_SIZE - info->rx_out) + rx_in; + } + + /* copy data into local buffer from rx buffer */ + while ( ( index < count ) && ( index < RX_BUFFER_SIZE ) ) + { + /* copy data byte */ + buffer[index] = info->rx_buffer[info->rx_out]; + index++; + + /* increment rx buffer values */ + info->rx_out++; + if ( info->rx_out >= RX_BUFFER_SIZE ) + { + info->rx_out = 0; + } + } + + /* check to see if buffer is not empty */ + if ( count > 0 ) + { + /* set characters into termios buffer */ + rtems_termios_enqueue_raw_characters(info->ttyp, buffer, count); + } + + return( EOF ); +} + + + +/*************************************************************************** + Function : IntUartPollRead + + Description : This reads a character from the internal uart. It returns + to the caller without blocking if not character is waiting. + ***************************************************************************/ +static int +IntUartPollRead (int minor) +{ + if ( (MCF5282_UART_USR(minor) & MCF5282_UART_USR_RXRDY) == 0 ) + return(-1); + + return(MCF5282_UART_URB(minor)); +} + + +/*************************************************************************** + Function : IntUartPollWrite + + Description : This writes out each character in the buffer to the + appropriate internal uart channel waiting till each one is sucessfully + transmitted. + ***************************************************************************/ +static ssize_t +IntUartPollWrite (int minor, const char *buf, size_t len) +{ + size_t retval = len; + /* loop over buffer */ + while ( len-- ) + { + /* block until we can transmit */ + while ( (MCF5282_UART_USR(minor) & MCF5282_UART_USR_TXRDY) == 0 ) + continue; + /* transmit data byte */ + MCF5282_UART_UTB(minor) = *buf++; + } + return retval; +} + +/*************************************************************************** + Function : console_initialize + + Description : This initialises termios, all uart hardware before + registering /dev/tty devices for each channel and the system /dev/console. + ***************************************************************************/ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg ) +{ + rtems_status_code status; + int chan; + + /* Set up TERMIOS */ + rtems_termios_initialize (); + + /* set io modes for the different channels and initialize device */ + for ( chan = 0; chan < MAX_UART_INFO; chan++ ) + IntUartInfo[chan].iomode = TERMIOS_IRQ_DRIVEN; + IntUartInitialize(); + + /* Register the console port */ + status = rtems_io_register_name ("/dev/console", major, CONSOLE_PORT); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + + /* Register the other ports */ + if ( CONSOLE_PORT != 0 ) + { + status = rtems_io_register_name ("/dev/tty00", major, 0); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + } + if ( CONSOLE_PORT != 1 ) + { + status = rtems_io_register_name ("/dev/tty01", major, 1); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + } + status = rtems_io_register_name ("/dev/tty02", major, 2); + if ( status != RTEMS_SUCCESSFUL ) + { + rtems_fatal_error_occurred (status); + } + + return(RTEMS_SUCCESSFUL); +} + +/*************************************************************************** + Function : console_open + + Description : This actually opens the device depending on the minor + number set during initialisation. The device specific access routines are + passed to termios when the devices is opened depending on whether it is + polled or not. + ***************************************************************************/ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + rtems_status_code status = RTEMS_INVALID_NUMBER; + rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *)arg; + struct IntUartInfoStruct *info; + + static const rtems_termios_callbacks IntUartPollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + IntUartPollRead, /* pollRead */ + IntUartPollWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* mode */ + }; + static const rtems_termios_callbacks IntUartIntrCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + NULL, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* mode */ + }; + + static const rtems_termios_callbacks IntUartTaskCallbacks = { + IntUartInterruptOpen, /* firstOpen */ + IntUartInterruptClose, /* lastClose */ + IntUartTaskRead, /* pollRead */ + IntUartInterruptWrite, /* write */ + IntUartSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_TASK_DRIVEN /* mode */ + }; + + /* open the port depending on the minor device number */ + if ( ( minor >= 0 ) && ( minor < MAX_UART_INFO ) ) + { + info = &IntUartInfo[minor]; + switch ( info->iomode ) + { + case TERMIOS_POLLED: + status = rtems_termios_open(major, minor, arg, &IntUartPollCallbacks); + break; + case TERMIOS_IRQ_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartIntrCallbacks); + info->ttyp = args->iop->data1; + break; + case TERMIOS_TASK_DRIVEN: + status = rtems_termios_open(major, minor, arg, &IntUartTaskCallbacks); + info->ttyp = args->iop->data1; + break; + } + } + + return( status ); +} + +/*************************************************************************** + Function : console_close + + Description : This closes the device via termios + ***************************************************************************/ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return(rtems_termios_close (arg)); +} + +/****************** +********************************************************* + Function : console_read + + Description : Read from the device via termios + ***************************************************************************/ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return(rtems_termios_read (arg)); +} + +/*************************************************************************** + Function : console_write + + Description : Write to the device via termios + ***************************************************************************/ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return(rtems_termios_write (arg)); +} + +/*************************************************************************** + Function : console_ioctl + + Description : Pass the IOCtl call to termios + ***************************************************************************/ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg) +{ + return( rtems_termios_ioctl (arg) ); +} + |