/* * Multi UART console serial I/O. * * TO DO: Add DMA input/output * * $Id$ */ #include #include #include #include #include #include #include #include #include #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; if (c == '\n') _BSP_null_char('\r'); 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_cflag & CBAUD); /* 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 ) { rtems_interrupt_level level; rtems_interrupt_disable(level); /* 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; rtems_interrupt_enable(level); 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); } int DEBUG_OUTCHAR(int c) { if (c == '\n') DEBUG_OUTCHAR('\r'); _BSP_null_char(c); return c; } void DEBUG_OUTSTR(const char *msg) { while (*msg) DEBUG_OUTCHAR(*msg++); } void DEBUG_OUTNUM(int i) { int n; static const char map[] = "0123456789ABCDEF"; DEBUG_OUTCHAR(' '); for (n = 28 ; n >= 0 ; n -= 4) DEBUG_OUTCHAR(map[(i >> n) & 0xF]); }