/* * This file contains the termios TTY driver for the UART found * on the Synova Mongoose-V. * * COPYRIGHT (c) 1989-2001. * 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.OARcorp.com/rtems/license.html. * * $Id$ */ #include #include #include #include #include #include #include extern void set_vector( rtems_isr_entry, rtems_vector_number, int ); /* * Indices of registers */ /* * Per chip context control */ typedef struct _mg5uart_context { int mate; } mg5uart_context; /* * Define MG5UART_STATIC to nothing while debugging so the entry points * will show up in the symbol table. */ #define MG5UART_STATIC /* #define MG5UART_STATIC static */ #define MG5UART_SETREG( _base, _register, _value ) \ MONGOOSEV_WRITE_REGISTER( _base, _register, _value ) #define MG5UART_GETREG( _base, _register ) \ MONGOOSEV_READ_REGISTER( _base, _register ) /* * Console Device Driver Support Functions */ MG5UART_STATIC int mg5uart_baud_rate( int minor, int baud, unsigned int *code ); MG5UART_STATIC void mg5uart_enable_interrupts( int minor, int mask ); /* * mg5uart_set_attributes * * This function sets the UART channel to reflect the requested termios * port settings. */ MG5UART_STATIC int mg5uart_set_attributes( int minor, const struct termios *t ) { unsigned32 pMG5UART_port; unsigned32 pMG5UART; unsigned int cmd; unsigned int baudcmd; unsigned int portshift; rtems_interrupt_level Irql; pMG5UART = Console_Port_Tbl[minor].ulCtrlPort1; pMG5UART_port = Console_Port_Tbl[minor].ulCtrlPort2; /* * Set the baud rate */ if (mg5uart_baud_rate( minor, t->c_cflag, &baudcmd ) == -1) return -1; /* * Base settings */ /* * Base settings */ cmd = MONGOOSEV_UART_CMD_TX_ENABLE | MONGOOSEV_UART_CMD_TX_ENABLE; /* * Parity */ if (t->c_cflag & PARENB) { cmd |= MONGOOSEV_UART_CMD_PARITY_ENABLE; if (t->c_cflag & PARODD) cmd |= MONGOOSEV_UART_CMD_PARITY_ODD; else cmd |= MONGOOSEV_UART_CMD_PARITY_EVEN; } else { cmd |= MONGOOSEV_UART_CMD_PARITY_DISABLE; } /* * Character Size */ if (t->c_cflag & CSIZE) { switch (t->c_cflag & CSIZE) { case CS5: case CS6: case CS7: return -1; break; case CS8: /* Mongoose-V only supports CS8 */ break; } } /* else default to CS8 */ /* * Stop Bits */ #if 0 if (t->c_cflag & CSTOPB) { /* 2 stop bits not supported by Mongoose-V uart */ return -1; } #endif /* * XXX what about CTS/RTS */ /* XXX */ /* * Now write the registers */ if ( Console_Port_Tbl[minor].ulDataPort == MG5UART_UART0 ) portshift = MONGOOSEV_UART0_CMD_SHIFT; else portshift = MONGOOSEV_UART1_CMD_SHIFT; rtems_interrupt_disable(Irql); MG5UART_SETREG( pMG5UART, MG5UART_COMMAND_REGISTER, cmd << portshift ); MG5UART_SETREG( pMG5UART_port, MG5UART_BAUD_RATE, baudcmd ); rtems_interrupt_enable(Irql); return 0; } /* * mg5uart_initialize_context * * This function sets the default values of the per port context structure. */ MG5UART_STATIC void mg5uart_initialize_context( int minor, mg5uart_context *pmg5uartContext ) { int port; unsigned int pMG5UART; unsigned int pMG5UART_port; pMG5UART = Console_Port_Tbl[minor].ulCtrlPort1; pMG5UART_port = Console_Port_Tbl[minor].ulCtrlPort2; pmg5uartContext->mate = -1; for (port=0 ; portmate = port; break; } } } /* * mg5uart_init * * This function initializes the DUART to a quiecsent state. */ MG5UART_STATIC void mg5uart_init(int minor) { unsigned32 pMG5UART_port; unsigned32 pMG5UART; mg5uart_context *pmg5uartContext; pmg5uartContext = (mg5uart_context *) malloc(sizeof(mg5uart_context)); Console_Port_Data[minor].pDeviceContext = (void *)pmg5uartContext; mg5uart_initialize_context( minor, pmg5uartContext ); pMG5UART = Console_Port_Tbl[minor].ulCtrlPort1; pMG5UART_port = Console_Port_Tbl[minor].ulCtrlPort2; /* * Reset everything and leave this port disabled. */ MG5UART_SETREG( pMG5UART, 0, MONGOOSEV_UART_CMD_RESET_BOTH_PORTS ); /* * Disable interrupts on RX and TX for this port */ mg5uart_enable_interrupts( minor, MG5UART_DISABLE_ALL ); } /* * mg5uart_open * * This function opens a port for communication. * * Default state is 9600 baud, 8 bits, No parity, and 1 stop bit. */ MG5UART_STATIC int mg5uart_open( int major, int minor, void *arg ) { unsigned32 pMG5UART; unsigned32 pMG5UART_port; unsigned int vector; unsigned int cmd; unsigned int baudcmd; unsigned int portshift; rtems_interrupt_level Irql; pMG5UART = Console_Port_Tbl[minor].ulCtrlPort1; pMG5UART_port = Console_Port_Tbl[minor].ulCtrlPort2; vector = Console_Port_Tbl[minor].ulIntVector; /* XXX default baud rate could be from configuration table */ (void) mg5uart_baud_rate( minor, B9600, &baudcmd ); /* * Set the DUART channel to a default useable state * B9600, 8Nx since there is no stop bit control. */ cmd = MONGOOSEV_UART_CMD_TX_ENABLE | MONGOOSEV_UART_CMD_RX_ENABLE; if ( Console_Port_Tbl[minor].ulDataPort == MG5UART_UART0 ) portshift = MONGOOSEV_UART0_CMD_SHIFT; else portshift = MONGOOSEV_UART1_CMD_SHIFT; rtems_interrupt_disable(Irql); MG5UART_SETREG( pMG5UART, MG5UART_COMMAND_REGISTER, cmd << portshift ); MG5UART_SETREG( pMG5UART_port, MG5UART_BAUD_RATE, baudcmd ); rtems_interrupt_enable(Irql); return RTEMS_SUCCESSFUL; } /* * mg5uart_close * * This function shuts down the requested port. */ MG5UART_STATIC int mg5uart_close( int major, int minor, void *arg ) { unsigned32 pMG5UART; unsigned32 pMG5UART_port; unsigned int cmd; unsigned int portshift; pMG5UART = Console_Port_Tbl[minor].ulCtrlPort1; pMG5UART_port = Console_Port_Tbl[minor].ulCtrlPort2; /* * Disable interrupts from this channel and then disable it totally. */ /* XXX interrupts */ cmd = MONGOOSEV_UART_CMD_TX_DISABLE | MONGOOSEV_UART_CMD_RX_DISABLE; if ( Console_Port_Tbl[minor].ulDataPort == MG5UART_UART0 ) portshift = MONGOOSEV_UART0_CMD_SHIFT; else portshift = MONGOOSEV_UART1_CMD_SHIFT; MG5UART_SETREG( pMG5UART, MG5UART_COMMAND_REGISTER, cmd << portshift ); return(RTEMS_SUCCESSFUL); } /* * mg5uart_write_polled * * This routine polls out the requested character. */ MG5UART_STATIC void mg5uart_write_polled( int minor, char c ) { unsigned32 pMG5UART; unsigned32 pMG5UART_port; unsigned32 status; int shift; int timeout; pMG5UART = Console_Port_Tbl[minor].ulCtrlPort1; pMG5UART_port = Console_Port_Tbl[minor].ulCtrlPort2; if ( Console_Port_Tbl[minor].ulDataPort == MG5UART_UART0 ) shift = MONGOOSEV_UART0_IRQ_SHIFT; else shift = MONGOOSEV_UART1_IRQ_SHIFT; /* * wait for transmitter holding register to be empty */ timeout = 1000; status = MG5UART_GETREG(pMG5UART, MG5UART_STATUS_REGISTER); while ( 1 ) { status = MG5UART_GETREG(pMG5UART, MG5UART_STATUS_REGISTER) >> shift; if ( (status & (MONGOOSEV_UART_TX_READY|MONGOOSEV_UART_TX_EMPTY_0)) == (MONGOOSEV_UART_TX_READY|MONGOOSEV_UART_TX_EMPTY_0) ) break; /* * Yield while we wait */ #if 0 if(_System_state_Is_up(_System_state_Get())) { rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); } #endif if(!--timeout) { break; } } /* * transmit character */ MG5UART_SETREG(pMG5UART_port, MG5UART_TX_BUFFER, c); } /* * mg5uart_isr_XXX * * This is the single interrupt entry point which parcels interrupts * out to the handlers for specific sources and makes sure that the * shared handler gets the right arguments. * * NOTE: Yes .. this is ugly but it provides 5 interrupt source * wrappers which are nearly functionally identical. */ #define __ISR(_TYPE, _OFFSET) \ MG5UART_STATIC void mg5uart_process_isr_ ## _TYPE ( \ int minor \ ); \ \ MG5UART_STATIC rtems_isr mg5uart_isr_ ## _TYPE ( \ rtems_vector_number vector \ ) \ { \ int minor; \ \ for(minor=0 ; minor> shift; if ( status & MONGOOSEV_UART_RX_READY ) { return (int) MG5UART_GETREG(pMG5UART_port, MG5UART_RX_BUFFER); } else { return -1; } } /* * mg5uart_baud_rate */ MG5UART_STATIC int mg5uart_baud_rate( int minor, int baud, unsigned int *code ) { rtems_unsigned32 clock; rtems_unsigned32 tmp_code; rtems_unsigned32 baud_requested; baud_requested = baud & CBAUD; if (!baud_requested) baud_requested = B9600; /* default to 9600 baud */ baud_requested = termios_baud_to_number( B9600 ); clock = (rtems_unsigned32) Console_Port_Tbl[minor].ulClock; if (!clock) rtems_fatal_error_occurred(RTEMS_INVALID_NUMBER); /* * Formula is Code = round(ClockFrequency / Baud - 1). * * Since this is integer math, we will divide by twice the baud and * check the remaining odd bit. */ tmp_code = (clock / (baud_requested * 2)); if ( tmp_code & 0x01 ) tmp_code = (tmp_code >> 1) + 1; else tmp_code = (tmp_code >> 1); /* * From section 12.7, "Keep C>100 for best receiver operation." * That is 100 cycles which is not a lot of instructions. It is * reasonable to think that the Mongoose-V could not keep * up with C < 200. */ if ( tmp_code < 100 ) return RTEMS_INVALID_NUMBER; /* * upper word is receiver baud and lower word is transmitter baud */ *code = (tmp_code << 16) | tmp_code; return 0; } /* * mg5uart_enable_interrupts * * This function enables specific interrupt sources on the DUART. */ MG5UART_STATIC void mg5uart_enable_interrupts( int minor, int mask ) { unsigned32 pMG5UART; unsigned int shift; pMG5UART = Console_Port_Tbl[minor].ulCtrlPort1; /* * Enable interrupts on RX and TX -- not break */ if ( Console_Port_Tbl[minor].ulDataPort == MG5UART_UART0 ) shift = MONGOOSEV_UART0_IRQ_SHIFT; else shift = MONGOOSEV_UART1_IRQ_SHIFT; MG5UART_SETREG( pMG5UART, MG5UART_INTERRUPT_MASK_REGISTER, mask << shift ); } /* * Flow control is only supported when using interrupts */ console_fns mg5uart_fns = { libchip_serial_default_probe, /* deviceProbe */ mg5uart_open, /* deviceFirstOpen */ NULL, /* deviceLastClose */ NULL, /* deviceRead */ mg5uart_write_support_int, /* deviceWrite */ mg5uart_initialize_interrupts, /* deviceInitialize */ mg5uart_write_polled, /* deviceWritePolled */ mg5uart_set_attributes, /* deviceSetAttributes */ TRUE /* deviceOutputUsesInterrupts */ }; console_fns mg5uart_fns_polled = { libchip_serial_default_probe, /* deviceProbe */ mg5uart_open, /* deviceFirstOpen */ mg5uart_close, /* deviceLastClose */ mg5uart_inbyte_nonblocking_polled, /* deviceRead */ mg5uart_write_support_polled, /* deviceWrite */ mg5uart_init, /* deviceInitialize */ mg5uart_write_polled, /* deviceWritePolled */ mg5uart_set_attributes, /* deviceSetAttributes */ FALSE, /* deviceOutputUsesInterrupts */ };