/* * This file contains the console driver for the xilinx uart lite. * * Author: Keith Robertson * COPYRIGHT (c) 2005 by Linn Products Ltd, Scotland. * * Derived from libbsp/no_cpu/no_bsp/console.c and therefore also: * * 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.com/license/LICENSE. * */ #include #include #include #include #include #include #include #include RTEMS_XPARAMETERS_H /* Status Register Masks */ #define PARITY_ERROR 0x80 /* Parity Error */ #define FRAME_ERROR 0x40 /* Frame Error */ #define OVERRUN_ERROR 0x20 /* Overrun Error */ #define STATUS_REG_ERROR_MASK ( PARITY_ERROR | FRAME_ERROR | OVERRUN_ERROR ) #define INTR_ENABLED 0x10 /* Interrupts are enabled */ #define TX_FIFO_FULL 0x08 /* Transmit FIFO is full */ #define TX_FIFO_EMPTY 0x04 /* Transmit FIFO is empty */ #define RX_FIFO_FULL 0x02 /* Receive FIFO is full */ #define RX_FIFO_VALID_DATA 0x01 /* Receive FIFO has valid data */ /* Control Register Masks*/ #define ENABLE_INTR 0x10 /* Enable interrupts */ #define RST_RX_FIFO 0x02 /* Reset and clear RX FIFO */ #define RST_TX_FIFO 0x01 /* Reset and clear TX FIFO */ /* General Defines */ #define TX_FIFO_SIZE 16 #define RX_FIFO_SIZE 16 #define RECV_REG 0 #define TRAN_REG 4 #define STAT_REG 8 #define CTRL_REG 12 RTEMS_INLINE_ROUTINE uint32_t xlite_uart_control(uint32_t base) { uint32_t c = *((volatile uint32_t*)(base+CTRL_REG)); return c; } RTEMS_INLINE_ROUTINE uint32_t xlite_uart_status(uint32_t base) { uint32_t c = *((volatile uint32_t*)(base+STAT_REG)); return c; } RTEMS_INLINE_ROUTINE uint32_t xlite_uart_read(uint32_t base) { uint32_t c = *((volatile uint32_t*)(base+RECV_REG)); return c; } RTEMS_INLINE_ROUTINE void xlite_uart_write(uint32_t base, char ch) { *(volatile uint32_t*)(base+TRAN_REG) = (uint32_t)ch; return; } static int xlite_write_char(uint32_t base, char ch) { uint32_t retrycount= 0, idler, status; while( ((status = xlite_uart_status(base)) & TX_FIFO_FULL) != 0 ) { ++retrycount; /* uart tx is busy */ if( retrycount == 0x4000 ) { /* retrycount is arbitrary- just make it big enough so the uart is sure to be timed out before it trips */ return -1; } /* spin for a bit so we can sample the register rather than * continually reading it */ for( idler= 0; idler < 0x2000; idler++); } xlite_uart_write(base, ch); return 1; } static void xlite_init(int minor ) { /* Nothing to do */ } #if VIRTEX_CONSOLE_USE_INTERRUPTS static void xlite_interrupt_handler(void *arg) { int minor = (int) arg; const console_tbl *ct = Console_Port_Tbl[minor]; console_data *cd = &Console_Port_Data[minor]; uint32_t base = ct->ulCtrlPort1; uint32_t status = xlite_uart_status(base); while ((status & RX_FIFO_VALID_DATA) != 0) { char c = (char) xlite_uart_read(base); rtems_termios_enqueue_raw_characters(cd->termios_data, &c, 1); status = xlite_uart_status(base); } if (cd->bActive) { rtems_termios_dequeue_characters(cd->termios_data, 1); } } #endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ static int xlite_open( int major, int minor, void *arg ) { const console_tbl *ct = Console_Port_Tbl[minor]; uint32_t base = ct->ulCtrlPort1; #if VIRTEX_CONSOLE_USE_INTERRUPTS rtems_status_code sc; #endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ /* clear status register */ *((volatile uint32_t*)(base+STAT_REG)) = 0; /* clear control register; reset fifos */ *((volatile uint32_t*)(base+CTRL_REG)) = RST_RX_FIFO | RST_TX_FIFO; #if VIRTEX_CONSOLE_USE_INTERRUPTS *((volatile uint32_t*)(base+CTRL_REG)) = ENABLE_INTR; sc = rtems_interrupt_handler_install( ct->ulIntVector, "xlite", RTEMS_INTERRUPT_UNIQUE, xlite_interrupt_handler, (void *) minor ); assert(sc == RTEMS_SUCCESSFUL); #endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ return 0; } static int xlite_close( int major, int minor, void *arg ) { const console_tbl *ct = Console_Port_Tbl[minor]; uint32_t base = ct->ulCtrlPort1; #if VIRTEX_CONSOLE_USE_INTERRUPTS rtems_status_code sc; #endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ *((volatile uint32_t*)(base+CTRL_REG)) = 0; #if VIRTEX_CONSOLE_USE_INTERRUPTS sc = rtems_interrupt_handler_remove( ct->ulIntVector, xlite_interrupt_handler, (void *) minor ); assert(sc == RTEMS_SUCCESSFUL); #endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ return 0; } static int xlite_read_polled (int minor ) { uint32_t base = Console_Port_Tbl[minor]->ulCtrlPort1; unsigned int status = xlite_uart_status(base); if(status & RX_FIFO_VALID_DATA) return (int)xlite_uart_read(base); else return -1; } #if VIRTEX_CONSOLE_USE_INTERRUPTS static ssize_t xlite_write_interrupt_driven( int minor, const char *buf, size_t len ) { console_data *cd = &Console_Port_Data[minor]; if (len > 0) { const console_tbl *ct = Console_Port_Tbl[minor]; uint32_t base = ct->ulCtrlPort1; xlite_uart_write(base, buf[0]); cd->bActive = true; } else { cd->bActive = false; } return 0; } #else /* VIRTEX_CONSOLE_USE_INTERRUPTS */ static ssize_t xlite_write_buffer_polled( int minor, const char *buf, size_t len ) { uint32_t base = Console_Port_Tbl[minor]->ulCtrlPort1; int nwrite = 0; /* * poll each byte in the string out of the port. */ while (nwrite < len) { if( xlite_write_char(base, *buf++) < 0 ) break; nwrite++; } /* * return the number of bytes written. */ return nwrite; } #endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ static void xlite_write_char_polled( int minor, char c ) { uint32_t base = Console_Port_Tbl[minor]->ulCtrlPort1; xlite_write_char(base, c); return; } static int xlite_set_attributes(int minor, const struct termios *t) { return RTEMS_SUCCESSFUL; } static const console_fns xlite_fns_polled = { .deviceProbe = libchip_serial_default_probe, .deviceFirstOpen = xlite_open, .deviceLastClose = xlite_close, .deviceRead = xlite_read_polled, .deviceInitialize = xlite_init, .deviceWritePolled = xlite_write_char_polled, .deviceSetAttributes = xlite_set_attributes, #if VIRTEX_CONSOLE_USE_INTERRUPTS .deviceWrite = xlite_write_interrupt_driven, .deviceOutputUsesInterrupts = true #else /* VIRTEX_CONSOLE_USE_INTERRUPTS */ .deviceWrite = xlite_write_buffer_polled, .deviceOutputUsesInterrupts = false #endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ }; /* ** Set ulCtrlPort1 to the base address of each UART Lite instance. Set in vhdl model. */ console_tbl Console_Configuration_Ports[] = { { "/dev/ttyS0", /* sDeviceName */ SERIAL_CUSTOM, /* deviceType */ &xlite_fns_polled, /* pDeviceFns */ NULL, /* deviceProbe, assume it is there */ NULL, /* pDeviceFlow */ 16, /* ulMargin */ 8, /* ulHysteresis */ (void *) NULL, /* NULL */ /* pDeviceParams */ STDIN_BASEADDRESS, /* ulCtrlPort1 */ 0, /* ulCtrlPort2 */ 0, /* ulDataPort */ NULL, /* getRegister */ NULL, /* setRegister */ NULL, /* unused */ /* getData */ NULL, /* unused */ /* setData */ 0, /* ulClock */ #ifdef XPAR_XPS_INTC_0_RS232_UART_INTERRUPT_INTR .ulIntVector = XPAR_XPS_INTC_0_RS232_UART_INTERRUPT_INTR #else .ulIntVector = 0 #endif }, #ifdef XPAR_UARTLITE_1_BASEADDR { "/dev/ttyS1", /* sDeviceName */ SERIAL_CUSTOM, /* deviceType */ &xlite_fns_polled, /* pDeviceFns */ NULL, /* deviceProbe, assume it is there */ NULL, /* pDeviceFlow */ 16, /* ulMargin */ 8, /* ulHysteresis */ (void *) NULL, /* NULL */ /* pDeviceParams */ XPAR_UARTLITE_1_BASEADDR, /* ulCtrlPort1 */ 0, /* ulCtrlPort2 */ 0, /* ulDataPort */ NULL, /* getRegister */ NULL, /* setRegister */ NULL, /* unused */ /* getData */ NULL, /* unused */ /* setData */ 0, /* ulClock */ 0 /* ulIntVector -- base for port */ }, #endif #ifdef XPAR_UARTLITE_2_BASEADDR { "/dev/ttyS2", /* sDeviceName */ SERIAL_CUSTOM, /* deviceType */ &xlite_fns_polled, /* pDeviceFns */ NULL, /* deviceProbe, assume it is there */ NULL, /* pDeviceFlow */ 16, /* ulMargin */ 8, /* ulHysteresis */ (void *) NULL, /* NULL */ /* pDeviceParams */ XPAR_UARTLITE_2_BASEADDR, /* ulCtrlPort1 */ 0, /* ulCtrlPort2 */ 0, /* ulDataPort */ NULL, /* getRegister */ NULL, /* setRegister */ NULL, /* unused */ /* getData */ NULL, /* unused */ /* setData */ 0, /* ulClock */ 0 /* ulIntVector -- base for port */ }, #endif #ifdef XPAR_UARTLITE_2_BASEADDR { "/dev/ttyS3", /* sDeviceName */ SERIAL_CUSTOM, /* deviceType */ &xlite_fns_polled, /* pDeviceFns */ NULL, /* deviceProbe, assume it is there */ NULL, /* pDeviceFlow */ 16, /* ulMargin */ 8, /* ulHysteresis */ (void *) NULL, /* NULL */ /* pDeviceParams */ XPAR_UARTLITE_3_BASEADDR, /* ulCtrlPort1 */ 0, /* ulCtrlPort2 */ 0, /* ulDataPort */ NULL, /* getRegister */ NULL, /* setRegister */ NULL, /* unused */ /* getData */ NULL, /* unused */ /* setData */ 0, /* ulClock */ 0 /* ulIntVector -- base for port */ } #endif }; unsigned long Console_Configuration_Count = RTEMS_ARRAY_SIZE(Console_Configuration_Ports); #include static void outputChar(char ch) { if (ch == '\n') { xlite_write_char_polled( 0, '\r' ); } xlite_write_char_polled( 0, ch ); } static int inputChar(void) { return xlite_read_polled(0); } BSP_output_char_function_type BSP_output_char = outputChar; BSP_polling_getchar_function_type BSP_poll_char = inputChar;