diff options
Diffstat (limited to 'bsps/powerpc/shared/console/uart.c')
-rw-r--r-- | bsps/powerpc/shared/console/uart.c | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/bsps/powerpc/shared/console/uart.c b/bsps/powerpc/shared/console/uart.c new file mode 100644 index 0000000000..62212b98db --- /dev/null +++ b/bsps/powerpc/shared/console/uart.c @@ -0,0 +1,781 @@ +/* + * This software is Copyright (C) 1998 by T.sqware - all rights limited + * It is provided in to the public domain "as is", can be freely modified + * as far as this copyight notice is kept unchanged, but does not imply + * an endorsement by T.sqware of the product in which it is included. + */ + +#include <stdint.h> +#include <stdio.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/uart.h> +#include <rtems/libio.h> +#include <rtems/bspIo.h> +#include <rtems/termiostypes.h> +#include <termios.h> +#include <assert.h> + +/* + * Basic 16552 driver + */ + +struct uart_data +{ + unsigned long ioBase; + int irq; + int hwFlow; + int baud; + BSP_UartBreakCbRec breakCallback; + int ioMode; +}; + +/* + * Initialization of BSP specific data. + * The constants are pulled in from a BSP + * specific file, whereas all of the code + * in this file is generic and makes no + * assumptions about addresses, irq vectors + * etc... + */ + +#define UART_UNSUPP ((unsigned long)(-1)) + +static struct uart_data uart_data[2] = { + { +#ifdef BSP_UART_IOBASE_COM1 + BSP_UART_IOBASE_COM1, + BSP_UART_COM1_IRQ, +#else + UART_UNSUPP, + -1, +#endif + }, + { +#ifdef BSP_UART_IOBASE_COM2 + BSP_UART_IOBASE_COM2, + BSP_UART_COM2_IRQ, +#else + UART_UNSUPP, + -1, +#endif + }, +}; + +#define MAX_UARTS (sizeof(uart_data)/sizeof(uart_data[0])) +#define SANITY_CHECK(uart) \ + assert( MAX_UARTS > (unsigned)(uart) && uart_data[(uart)].ioBase != UART_UNSUPP ) +/* + * Macros to read/write register of uart, if configuration is + * different just rewrite these macros + */ + +static inline unsigned char +uread(int uart, unsigned int reg) +{ + return in_8((uint8_t*)(uart_data[uart].ioBase + reg)); +} + +static inline void +uwrite(int uart, int reg, unsigned int val) +{ + out_8((uint8_t*)(uart_data[uart].ioBase + reg), val); +} + + +static void +uartError(int uart, void *termiosPrivate) +{ + unsigned char uartStatus, dummy; + BSP_UartBreakCbProc h; + + uartStatus = uread(uart, LSR); + dummy = uread(uart, RBR); + +#ifdef UARTDEBUG + if (uartStatus & OE) + printk("********* Over run Error **********\n"); + if (uartStatus & PE) + printk("********* Parity Error **********\n"); + if (uartStatus & FE) + printk("********* Framing Error **********\n"); + if (uartStatus & BI) { + printk("********* BREAK INTERRUPT *********\n"); +#endif + if ((h=uart_data[uart].breakCallback.handler)) { + h(uart, + (dummy<<8)|uartStatus, + termiosPrivate, + uart_data[uart].breakCallback.private); + } +#ifdef UARTDEBUG + if (uartStatus & ERFIFO) + printk("********* Error receive Fifo **********\n"); +#endif +} + +/* + * Uart initialization, it is hardcoded to 8 bit, no parity, + * one stop bit, FIFO, things to be changed + * are baud rate and nad hw flow control, + * and longest rx fifo setting + */ +void +BSP_uart_init(int uart, int baud, int hwFlow) +{ + unsigned char tmp; + + /* Sanity check */ + SANITY_CHECK(uart); + + /* Make sure any printk activity drains before + * re-initializing. + */ + while ( ! (uread(uart, LSR) & TEMT) ) + ; + + switch(baud) + { + case 50: + case 75: + case 110: + case 134: + case 300: + case 600: + case 1200: + case 2400: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + break; + default: + assert(0); + return; + } + + /* Set DLAB bit to 1 */ + uwrite(uart, LCR, DLAB); + + if ( (int)BSPBaseBaud <= 0 ) { + /* Use current divisor assuming BSPBaseBaud gives us the current speed */ + BSPBaseBaud = BSPBaseBaud ? -BSPBaseBaud : 9600; + BSPBaseBaud *= ((uread(uart, DLM) << 8) | uread(uart, DLL)); + } + + /* Set baud rate */ + uwrite(uart, DLL, (BSPBaseBaud/baud) & 0xff); + uwrite(uart, DLM, ((BSPBaseBaud/baud) >> 8) & 0xff); + + /* 8-bit, no parity , 1 stop */ + uwrite(uart, LCR, CHR_8_BITS); + + /* Set DTR, RTS and OUT2 high */ + uwrite(uart, MCR, DTR | RTS | OUT_2); + + /* Enable FIFO */ + uwrite(uart, FCR, FIFO_EN | XMIT_RESET | RCV_RESET | RECEIVE_FIFO_TRIGGER12); + + /* Disable Interrupts */ + uwrite(uart, IER, 0); + + /* Read status to clear them */ + tmp = uread(uart, LSR); + tmp = uread(uart, RBR); + tmp = uread(uart, MSR); + (void) tmp; /* avoid set but not used warning */ + + /* Remember state */ + uart_data[uart].hwFlow = hwFlow; + uart_data[uart].baud = baud; + return; +} + +/* + * Set baud + */ +void +BSP_uart_set_baud(int uart, int baud) +{ + unsigned char mcr, ier; + + /* Sanity check */ + SANITY_CHECK(uart); + + /* + * This function may be called whenever TERMIOS parameters + * are changed, so we have to make sure that baud change is + * indeed required. + */ + + if(baud == uart_data[uart].baud) + { + return; + } + + mcr = uread(uart, MCR); + ier = uread(uart, IER); + + BSP_uart_init(uart, baud, uart_data[uart].hwFlow); + + uwrite(uart, MCR, mcr); + uwrite(uart, IER, ier); + + return; +} + +/* + * Enable/disable interrupts + */ +void +BSP_uart_intr_ctrl(int uart, int cmd) +{ + + SANITY_CHECK(uart); + + switch(cmd) + { + case BSP_UART_INTR_CTRL_DISABLE: + uwrite(uart, IER, INTERRUPT_DISABLE); + break; + case BSP_UART_INTR_CTRL_ENABLE: + if(uart_data[uart].hwFlow) + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + TRANSMIT_ENABLE | + RECEIVER_LINE_ST_ENABLE | + MODEM_ENABLE + ) + ); + } + else + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + TRANSMIT_ENABLE | + RECEIVER_LINE_ST_ENABLE + ) + ); + } + break; + case BSP_UART_INTR_CTRL_TERMIOS: + if(uart_data[uart].hwFlow) + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + RECEIVER_LINE_ST_ENABLE | + MODEM_ENABLE + ) + ); + } + else + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + RECEIVER_LINE_ST_ENABLE + ) + ); + } + break; + case BSP_UART_INTR_CTRL_GDB: + uwrite(uart, IER, RECEIVE_ENABLE); + break; + default: + assert(0); + break; + } + + return; +} + +void +BSP_uart_throttle(int uart) +{ + unsigned int mcr; + + SANITY_CHECK(uart); + + if(!uart_data[uart].hwFlow) + { + /* Should not happen */ + assert(0); + return; + } + mcr = uread (uart, MCR); + /* RTS down */ + mcr &= ~RTS; + uwrite(uart, MCR, mcr); + + return; +} + +void +BSP_uart_unthrottle(int uart) +{ + unsigned int mcr; + + SANITY_CHECK(uart); + + if(!uart_data[uart].hwFlow) + { + /* Should not happen */ + assert(0); + return; + } + mcr = uread (uart, MCR); + /* RTS up */ + mcr |= RTS; + uwrite(uart, MCR, mcr); + + return; +} + +/* + * Status function, -1 if error + * detected, 0 if no received chars available, + * 1 if received char available, 2 if break + * is detected, it will eat break and error + * chars. It ignores overruns - we cannot do + * anything about - it execpt count statistics + * and we are not counting it. + */ +int +BSP_uart_polled_status(int uart) +{ + unsigned char val; + + SANITY_CHECK(uart); + + val = uread(uart, LSR); + + if(val & BI) + { + /* BREAK found, eat character */ + uread(uart, RBR); + return BSP_UART_STATUS_BREAK; + } + + if((val & (DR | OE | FE)) == 1) + { + /* No error, character present */ + return BSP_UART_STATUS_CHAR; + } + + if((val & (DR | OE | FE)) == 0) + { + /* Nothing */ + return BSP_UART_STATUS_NOCHAR; + } + + /* + * Framing or parity error + * eat character + */ + uread(uart, RBR); + + return BSP_UART_STATUS_ERROR; +} + +/* + * Polled mode write function + */ +void +BSP_uart_polled_write(int uart, int val) +{ + unsigned char val1; + + /* Sanity check */ + SANITY_CHECK(uart); + + for(;;) + { + if((val1=uread(uart, LSR)) & THRE) + { + break; + } + } + + if(uart_data[uart].hwFlow) + { + for(;;) + { + if(uread(uart, MSR) & CTS) + { + break; + } + } + } + + uwrite(uart, THR, val & 0xff); + + return; +} + +void +BSP_output_char_via_serial(const char val) +{ + BSP_uart_polled_write(BSPConsolePort, val); +} + +/* + * Polled mode read function + */ +int +BSP_uart_polled_read(int uart) +{ + unsigned char val; + + SANITY_CHECK(uart); + + for(;;) + { + if(uread(uart, LSR) & DR) + { + break; + } + } + + val = uread(uart, RBR); + + return (int)(val & 0xff); +} + +unsigned +BSP_poll_char_via_serial() +{ + return BSP_uart_polled_read(BSPConsolePort); +} + +static void +uart_noop(const rtems_irq_connect_data *unused) +{ + return; +} + +/* note that the IRQ names contain _ISA_ for legacy + * reasons. They can be any interrupt, depending + * on the particular BSP... + */ + +static int +uart_isr_is_on(const rtems_irq_connect_data *irq) +{ + int uart; + + uart = (irq->name == BSP_UART_COM1_IRQ) ? + BSP_UART_COM1 : BSP_UART_COM2; + + return uread(uart,IER); +} + +static int +doit(int uart, rtems_irq_hdl handler, int (*p)(const rtems_irq_connect_data*)) +{ + rtems_irq_connect_data d={0}; + d.name = uart_data[uart].irq; + d.off = d.on = uart_noop; + d.isOn = uart_isr_is_on; + d.hdl = handler; + return p(&d); +} + +int +BSP_uart_install_isr(int uart, rtems_irq_hdl handler) +{ +/* Using shared interrupts by default might break things.. the + * shared IRQ installer uses malloc() and if a BSP had called this + * during early init it might not work... + */ +#ifdef BSP_UART_USE_SHARED_IRQS + return doit(uart, handler, BSP_install_rtems_shared_irq_handler); +#else + return doit(uart, handler, BSP_install_rtems_irq_handler); +#endif +} + +int +BSP_uart_remove_isr(int uart, rtems_irq_hdl handler) +{ + return doit(uart, handler, BSP_remove_rtems_irq_handler); +} + +/* ================ Termios support =================*/ + +static volatile int termios_stopped_com[2] = {0,0}; +static volatile int termios_tx_active_com[2] = {0,0}; +static void* termios_ttyp_com[2] = {NULL,NULL}; +static char termios_tx_hold_com[2] = {0,0}; +static volatile char termios_tx_hold_valid_com[2] = {0,0}; + +/* + * Set channel parameters + */ +void +BSP_uart_termios_set(int uart, void *p) +{ + struct rtems_termios_tty *ttyp = p; + unsigned char val; + SANITY_CHECK(uart); + + if(uart_data[uart].hwFlow) + { + val = uread(uart, MSR); + + termios_stopped_com[uart] = (val & CTS) ? 0 : 1; + } + else + { + termios_stopped_com[uart] = 0; + } + termios_tx_active_com[uart] = 0; + termios_ttyp_com[uart] = ttyp; + termios_tx_hold_com[uart] = 0; + termios_tx_hold_valid_com[uart] = 0; + + uart_data[uart].ioMode = ttyp->device.outputUsesInterrupts; + + return; +} + +ssize_t +BSP_uart_termios_write_polled(int minor, const char *buf, size_t len) +{ + int uart=minor; /* could differ, theoretically */ + int nwrite; + const char *b = buf; + + for (nwrite=0 ; nwrite < len ; nwrite++) { + BSP_uart_polled_write(uart, *b++); + } + return nwrite; +} + +ssize_t +BSP_uart_termios_write_com(int minor, const char *buf, size_t len) +{ + int uart=minor; /* could differ, theoretically */ + + if(len <= 0) + { + return 0; + } + + /* If the TX buffer is busy - something is royally screwed up */ + /* assert((uread(BSP_UART_COM1, LSR) & THRE) != 0); */ + + if(termios_stopped_com[uart]) + { + /* CTS low */ + termios_tx_hold_com[uart] = *buf; + termios_tx_hold_valid_com[uart] = 1; + return 0; + } + + /* Write character */ + uwrite(uart, THR, *buf & 0xff); + + /* Enable interrupts if necessary */ + if(!termios_tx_active_com[uart] && uart_data[uart].hwFlow) + { + termios_tx_active_com[uart] = 1; + uwrite(uart, IER, + (RECEIVE_ENABLE | + TRANSMIT_ENABLE | + RECEIVER_LINE_ST_ENABLE | + MODEM_ENABLE + ) + ); + } + else if(!termios_tx_active_com[uart]) + { + termios_tx_active_com[uart] = 1; + uwrite(uart, IER, + (RECEIVE_ENABLE | + TRANSMIT_ENABLE | + RECEIVER_LINE_ST_ENABLE + ) + ); + } + + return 0; +} + +int +BSP_uart_termios_read_com(int uart) +{ + int off = (int)0; + char buf[40]; + rtems_interrupt_level l; + + /* read bytes */ + while (( off < sizeof(buf) ) && ( uread(uart, LSR) & DR )) { + buf[off++] = uread(uart, RBR); + } + + /* write out data */ + if ( off > 0 ) { + rtems_termios_enqueue_raw_characters(termios_ttyp_com[uart], buf, off); + } + + /* enable receive interrupts */ + rtems_interrupt_disable(l); + uwrite(uart, IER, uread(uart, IER) | (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE)); + rtems_interrupt_enable(l); + + return ( EOF ); +} + +static void +BSP_uart_termios_isr_com(int uart) +{ + unsigned char buf[40]; + unsigned char val, ier; + int off, ret, vect; + + off = 0; + + for(;;) + { + vect = uread(uart, IIR) & 0xf; + + switch(vect) + { + case MODEM_STATUS : + val = uread(uart, MSR); + if(uart_data[uart].hwFlow) + { + if(val & CTS) + { + /* CTS high */ + termios_stopped_com[uart] = 0; + if(termios_tx_hold_valid_com[uart]) + { + termios_tx_hold_valid_com[uart] = 0; + BSP_uart_termios_write_com(uart, &termios_tx_hold_com[uart], + 1); + } + } + else + { + /* CTS low */ + termios_stopped_com[uart] = 1; + } + } + break; + case NO_MORE_INTR : + /* No more interrupts */ + if(off != 0) + { + /* Update rx buffer */ + rtems_termios_enqueue_raw_characters(termios_ttyp_com[uart], + (char *)buf, + off); + } + return; + case TRANSMITTER_HODING_REGISTER_EMPTY : + /* + * TX holding empty: we have to disable these interrupts + * if there is nothing more to send. + */ + + ret = rtems_termios_dequeue_characters(termios_ttyp_com[uart], 1); + + /* If nothing else to send disable interrupts */ + if(ret == 0 && uart_data[uart].hwFlow) + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + RECEIVER_LINE_ST_ENABLE | + MODEM_ENABLE + ) + ); + termios_tx_active_com[uart] = 0; + } + else if(ret == 0) + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + RECEIVER_LINE_ST_ENABLE + ) + ); + termios_tx_active_com[uart] = 0; + } + break; + case RECEIVER_DATA_AVAIL : + case CHARACTER_TIMEOUT_INDICATION: + if ( uart_data[uart].ioMode == TERMIOS_TASK_DRIVEN ) + { + /* ensure interrupts are enabled */ + if ( (ier = uread(uart,IER)) & RECEIVE_ENABLE ) + { + /* disable interrupts and notify termios */ + ier &= ~(RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE); + uwrite(uart, IER, ier); + rtems_termios_rxirq_occured(termios_ttyp_com[uart]); + } + } + else + { + /* RX data ready */ + assert(off < sizeof(buf)); + while ( off < sizeof(buf) && ( DR & uread(uart, LSR) ) ) + buf[off++] = uread(uart, RBR); + } + break; + case RECEIVER_ERROR: + /* RX error: eat character */ + uartError(uart, termios_ttyp_com[uart]); + break; + default: + /* Should not happen */ + assert(0); + return; + } + } +} + +/* + * XXX - Note that this can now be one isr with the uart + * passed as the parameter. + */ +void +BSP_uart_termios_isr_com1(void *unused) +{ + BSP_uart_termios_isr_com(BSP_UART_COM1); +} + +void +BSP_uart_termios_isr_com2(void *unused) +{ + BSP_uart_termios_isr_com(BSP_UART_COM2); +} + +/* retrieve 'break' handler info */ +int +BSP_uart_get_break_cb(int uart, rtems_libio_ioctl_args_t *arg) +{ +BSP_UartBreakCb cb=arg->buffer; +unsigned long flags; + SANITY_CHECK(uart); + rtems_interrupt_disable(flags); + *cb = uart_data[uart].breakCallback; + rtems_interrupt_enable(flags); + arg->ioctl_return=0; + return RTEMS_SUCCESSFUL; +} + +/* install 'break' handler */ +int +BSP_uart_set_break_cb(int uart, rtems_libio_ioctl_args_t *arg) +{ +BSP_UartBreakCb cb=arg->buffer; +unsigned long flags; + SANITY_CHECK(uart); + rtems_interrupt_disable(flags); + uart_data[uart].breakCallback = *cb; + rtems_interrupt_enable(flags); + arg->ioctl_return=0; + return RTEMS_SUCCESSFUL; +} |