/* * This file contains the termios TTY driver for the Motorola MC68681. * * This part is available from a number of secondary sources. * In particular, we know about the following: * * + Exar 88c681 and 68c681 * * COPYRIGHT (c) 1989-1998. * On-Line Applications Research Corporation (OAR). * Copyright assigned to U.S. Government, 1994. * * 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 "sersupp.h" #include "mc68681_p.h" #include "mc68681.h" /* * Flow control is only supported when using interrupts */ console_fns mc68681_fns = { libchip_serial_default_probe, /* deviceProbe */ mc68681_open, /* deviceFirstOpen */ NULL, /* deviceLastClose */ NULL, /* deviceRead */ mc68681_write_support_int, /* deviceWrite */ mc68681_initialize_interrupts, /* deviceInitialize */ mc68681_write_polled, /* deviceWritePolled */ mc68681_set_attributes, /* deviceSetAttributes */ TRUE /* deviceOutputUsesInterrupts */ }; console_fns mc68681_fns_polled = { libchip_serial_default_probe, /* deviceProbe */ mc68681_open, /* deviceFirstOpen */ mc68681_close, /* deviceLastClose */ mc68681_inbyte_nonblocking_polled, /* deviceRead */ mc68681_write_support_polled, /* deviceWrite */ mc68681_init, /* deviceInitialize */ mc68681_write_polled, /* deviceWritePolled */ mc68681_set_attributes, /* deviceSetAttributes */ FALSE, /* deviceOutputUsesInterrupts */ }; extern void set_vector( rtems_isr_entry, rtems_vector_number, int ); /* * Console Device Driver Entry Points */ /* * mc68681_baud_rate * * This routine returns the proper ACR bit and baud rate field values * based on the requested baud rate. The baud rate set to be used * must be configured by the user. */ /* major index of 0 : ACR[7] = 0, X = 0 -- 68c681 only has these */ /* major index of 1 : ACR[7] = 1, X = 0 -- 68c681 only has these */ /* major index of 2 : ACR[7] = 0, X = 1 */ /* major index of 3 : ACR[7] = 1, X = 1 */ /* mc68681_baud_table_t mc68681_baud_rate_table[4] = { */ mc68681_baud_t mc68681_baud_rate_table[4][RTEMS_TERMIOS_NUMBER_BAUD_RATES] = { { /* ACR[7] = 0, X = 0 */ MC68681_BAUD_NOT_VALID, /* B0 */ 0x00, /* B50 */ MC68681_BAUD_NOT_VALID, /* B75 */ 0x01, /* B110 */ 0x02, /* B134 */ MC68681_BAUD_NOT_VALID, /* B150 */ 0x03, /* B200 */ 0x04, /* B300 */ 0x05, /* B600 */ 0x06, /* B1200 */ MC68681_BAUD_NOT_VALID, /* B1800 */ 0x08, /* B2400 */ 0x09, /* B4800 */ 0x0B, /* B9600 */ MC68681_BAUD_NOT_VALID, /* B19200 */ 0x0C, /* B38400 */ MC68681_BAUD_NOT_VALID, /* B57600 */ MC68681_BAUD_NOT_VALID, /* B115200 */ MC68681_BAUD_NOT_VALID, /* B230400 */ MC68681_BAUD_NOT_VALID /* B460800 */ }, { /* ACR[7] = 1, X = 0 */ MC68681_BAUD_NOT_VALID, /* B0 */ MC68681_BAUD_NOT_VALID, /* B50 */ 0x00, /* B75 */ 0x01, /* B110 */ 0x02, /* B134 */ 0x03, /* B150 */ MC68681_BAUD_NOT_VALID, /* B200 */ 0x04, /* B300 */ 0x05, /* B600 */ 0x06, /* B1200 */ 0x0A, /* B1800 */ 0x08, /* B2400 */ 0x09, /* B4800 */ 0x0B, /* B9600 */ 0x0C, /* B19200 */ MC68681_BAUD_NOT_VALID, /* B38400 */ MC68681_BAUD_NOT_VALID, /* B57600 */ MC68681_BAUD_NOT_VALID, /* B115200 */ MC68681_BAUD_NOT_VALID, /* B230400 */ MC68681_BAUD_NOT_VALID /* B460800 */ }, { /* ACR[7] = 0, X = 1 */ MC68681_BAUD_NOT_VALID, /* B0 */ MC68681_BAUD_NOT_VALID, /* B50 */ 0x00, /* B75 */ 0x01, /* B110 */ 0x02, /* B134 */ 0x03, /* B150 */ MC68681_BAUD_NOT_VALID, /* B200 */ MC68681_BAUD_NOT_VALID, /* B300 */ MC68681_BAUD_NOT_VALID, /* B600 */ MC68681_BAUD_NOT_VALID, /* B1200 */ 0x0A, /* B1800 */ MC68681_BAUD_NOT_VALID, /* B2400 */ 0x08, /* B4800 */ 0x0B, /* B9600 */ 0x0C, /* B19200 */ MC68681_BAUD_NOT_VALID, /* B38400 */ 0x07, /* B57600 */ 0x08, /* B115200 */ MC68681_BAUD_NOT_VALID, /* B230400 */ MC68681_BAUD_NOT_VALID /* B460800 */ }, { /* ACR[7] = 1, X = 1 */ MC68681_BAUD_NOT_VALID, /* B0 */ 0x00, /* B50 */ MC68681_BAUD_NOT_VALID, /* B75 */ 0x01, /* B110 */ 0x02, /* B134 */ MC68681_BAUD_NOT_VALID, /* B150 */ 0x03, /* B200 */ MC68681_BAUD_NOT_VALID, /* B300 */ MC68681_BAUD_NOT_VALID, /* B600 */ MC68681_BAUD_NOT_VALID, /* B1200 */ MC68681_BAUD_NOT_VALID, /* B1800 */ MC68681_BAUD_NOT_VALID, /* B2400 */ 0x09, /* B4800 */ 0x0B, /* B9600 */ MC68681_BAUD_NOT_VALID, /* B19200 */ 0x0C, /* B38400 */ 0x07, /* B57600 */ 0x08, /* B115200 */ MC68681_BAUD_NOT_VALID, /* B230400 */ MC68681_BAUD_NOT_VALID /* B460800 */ }, }; MC68681_STATIC int mc68681_baud_rate( int minor, int baud, unsigned int *baud_mask_p, unsigned int *acr_bit_p, unsigned int *command ); /* * mc68681_set_attributes * * This function sets the DUART channel to reflect the requested termios * port settings. */ MC68681_STATIC int mc68681_set_attributes( int minor, const struct termios *t ) { unsigned32 pMC68681_port; unsigned32 pMC68681; unsigned int mode1; unsigned int mode2; unsigned int baud_mask; unsigned int acr_bit; unsigned int cmd; setRegister_f setReg; rtems_interrupt_level Irql; pMC68681 = Console_Port_Tbl[minor].ulCtrlPort1; pMC68681_port = Console_Port_Tbl[minor].ulCtrlPort2; setReg = Console_Port_Tbl[minor].setRegister; /* * Set the baud rate */ if (mc68681_baud_rate( minor, t->c_cflag, &baud_mask, &acr_bit, &cmd ) == -1) return -1; baud_mask |= baud_mask << 4; acr_bit <<= 7; /* * Parity */ mode1 = 0; mode2 = 0; if (t->c_cflag & PARENB) { if (t->c_cflag & PARODD) mode1 |= 0x04; else mode1 |= 0x04; } else { mode1 |= 0x10; } /* * Character Size */ if (t->c_cflag & CSIZE) { switch (t->c_cflag & CSIZE) { case CS5: break; case CS6: mode1 |= 0x01; break; case CS7: mode1 |= 0x02; break; case CS8: mode1 |= 0x03; break; } } else { mode1 |= 0x03; /* default to 9600,8,N,1 */ } /* * Stop Bits */ if (t->c_cflag & CSTOPB) { mode2 |= 0x07; /* 2 stop bits */ } else { if ((t->c_cflag & CSIZE) == CS5) /* CS5 and 2 stop bits not supported */ return -1; mode2 |= 0x0F; /* 1 stop bit */ } rtems_interrupt_disable(Irql); (*setReg)( pMC68681, MC68681_AUX_CTRL_REG, acr_bit ); (*setReg)( pMC68681_port, MC68681_CLOCK_SELECT, baud_mask ); if ( cmd ) { (*setReg)( pMC68681_port, MC68681_COMMAND, cmd ); /* RX */ (*setReg)( pMC68681_port, MC68681_COMMAND, cmd | 0x20 ); /* TX */ } (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_MR_PTR ); (*setReg)( pMC68681_port, MC68681_MODE, mode1 ); (*setReg)( pMC68681_port, MC68681_MODE, mode2 ); rtems_interrupt_enable(Irql); return 0; } /* * mc68681_initialize_context * * This function sets the default values of the per port context structure. */ MC68681_STATIC void mc68681_initialize_context( int minor, mc68681_context *pmc68681Context ) { int port; unsigned int pMC68681; unsigned int pMC68681_port; pMC68681 = Console_Port_Tbl[minor].ulCtrlPort1; pMC68681_port = Console_Port_Tbl[minor].ulCtrlPort2; pmc68681Context->mate = -1; for (port=0 ; portmate = port; pmc68681Context->imr = 0; break; } } } /* * mc68681_init * * This function initializes the DUART to a quiecsent state. */ MC68681_STATIC void mc68681_init(int minor) { unsigned32 pMC68681_port; unsigned32 pMC68681; mc68681_context *pmc68681Context; setRegister_f setReg; getRegister_f getReg; pmc68681Context = (mc68681_context *) malloc(sizeof(mc68681_context)); Console_Port_Data[minor].pDeviceContext = (void *)pmc68681Context; mc68681_initialize_context( minor, pmc68681Context ); pMC68681 = Console_Port_Tbl[minor].ulCtrlPort1; pMC68681_port = Console_Port_Tbl[minor].ulCtrlPort2; setReg = Console_Port_Tbl[minor].setRegister; getReg = Console_Port_Tbl[minor].getRegister; /* * Reset everything and leave this port disabled. */ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_RX ); (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_TX ); (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_ERROR ); (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_BREAK ); (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_STOP_BREAK ); (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_TX ); (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_RX ); (*setReg)( pMC68681_port, MC68681_MODE_REG_1A, 0x00 ); (*setReg)( pMC68681_port, MC68681_MODE_REG_2A, 0x02 ); /* * Disable interrupts on RX and TX for this port */ mc68681_enable_interrupts( minor, MC68681_IMR_DISABLE_ALL ); } /* * mc68681_open * * This function opens a port for communication. * * Default state is 9600 baud, 8 bits, No parity, and 1 stop bit. */ MC68681_STATIC int mc68681_open( int major, int minor, void *arg ) { unsigned32 pMC68681; unsigned32 pMC68681_port; unsigned int baud; unsigned int acr; unsigned int vector; unsigned int command; rtems_interrupt_level Irql; setRegister_f setReg; pMC68681 = Console_Port_Tbl[minor].ulCtrlPort1; pMC68681_port = Console_Port_Tbl[minor].ulCtrlPort2; setReg = Console_Port_Tbl[minor].setRegister; vector = Console_Port_Tbl[minor].ulIntVector; /* XXX default baud rate should be from configuration table */ (void) mc68681_baud_rate( minor, B9600, &baud, &acr, &command ); /* * Set the DUART channel to a default useable state */ rtems_interrupt_disable(Irql); (*setReg)( pMC68681, MC68681_AUX_CTRL_REG, acr ); (*setReg)( pMC68681_port, MC68681_CLOCK_SELECT, baud ); if ( command ) { (*setReg)( pMC68681_port, MC68681_COMMAND, command ); /* RX */ (*setReg)( pMC68681_port, MC68681_COMMAND, command | 0x20 ); /* TX */ } (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_MR_PTR ); (*setReg)( pMC68681_port, MC68681_MODE, 0x13 ); (*setReg)( pMC68681_port, MC68681_MODE, 0x07 ); rtems_interrupt_enable(Irql); (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_ENABLE_TX ); (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_ENABLE_RX ); (*setReg)( pMC68681, MC68681_INTERRUPT_VECTOR_REG, vector ); return RTEMS_SUCCESSFUL; } /* * mc68681_close * * This function shuts down the requested port. */ MC68681_STATIC int mc68681_close( int major, int minor, void *arg ) { unsigned32 pMC68681; unsigned32 pMC68681_port; setRegister_f setReg; pMC68681 = Console_Port_Tbl[minor].ulCtrlPort1; pMC68681_port = Console_Port_Tbl[minor].ulCtrlPort2; setReg = Console_Port_Tbl[minor].setRegister; /* * Disable interrupts from this channel and then disable it totally. */ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_TX ); (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_RX ); return(RTEMS_SUCCESSFUL); } /* * mc68681_write_polled * * This routine polls out the requested character. */ MC68681_STATIC void mc68681_write_polled( int minor, char cChar ) { unsigned32 pMC68681_port; unsigned char ucLineStatus; int iTimeout; getRegister_f getReg; setRegister_f setReg; pMC68681_port = Console_Port_Tbl[minor].ulCtrlPort2; getReg = Console_Port_Tbl[minor].getRegister; setReg = Console_Port_Tbl[minor].setRegister; /* * wait for transmitter holding register to be empty */ iTimeout = 1000; ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS); while ((ucLineStatus & (MC68681_TX_READY|MC68681_TX_EMPTY)) == 0) { if ((ucLineStatus & 0xF0)) (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_ERROR ); /* * Yield while we wait */ if(_System_state_Is_up(_System_state_Get())) { rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); } ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS); if(!--iTimeout) { break; } } /* * transmit character */ (*setReg)(pMC68681_port, MC68681_TX_BUFFER, cChar); } /* * mc68681_isr * * This is the single interrupt entry point which parcels interrupts * out to the various ports. */ MC68681_STATIC rtems_isr mc68681_isr( rtems_vector_number vector ) { int minor; for(minor=0 ; minor>= 4; if(ucLineStatus & MC68681_IR_TX_READY) { mc68681_enable_interrupts(minor, MC68681_IMR_ENABLE_ALL_EXCEPT_TX); rtems_termios_dequeue_characters(Console_Port_Data[minor].termios_data, 1); } } /* * mc68681_build_imr * * This function returns the value for the interrupt mask register for this * DUART. Since this is a shared register, we must look at the other port * on this chip to determine whether or not it is using interrupts. */ MC68681_STATIC unsigned int mc68681_build_imr( int minor, int enable_flag ) { int mate; int is_a; unsigned int mask; unsigned int mate_mask; unsigned int pMC68681; unsigned int pMC68681_port; mc68681_context *pmc68681Context; mc68681_context *mateContext; pMC68681 = Console_Port_Tbl[minor].ulCtrlPort1; pMC68681_port = Console_Port_Tbl[minor].ulCtrlPort2; pmc68681Context = (mc68681_context *) Console_Port_Data[minor].pDeviceContext; mate = pmc68681Context->mate; mask = 0; mate_mask = 0; is_a = (pMC68681 == pMC68681_port); /* * If there is a mate for this port, get its IMR mask. */ if ( mate != -1 ) { mateContext = Console_Port_Data[mate].pDeviceContext; if (mateContext) mate_mask = mateContext->imr; } /* * Calculate this port's IMR mask and save it in the context area. */ if ( Console_Port_Tbl[minor].pDeviceFns->deviceOutputUsesInterrupts ) mask = enable_flag; pmc68681Context->imr = mask; /* * Now return the full IMR value */ if (is_a) return (mate_mask << 4) | mask; return (mask << 4) | mate_mask; } /* * mc68681_enable_interrupts * * This function initializes the hardware for this port to use interrupts. */ MC68681_STATIC void mc68681_enable_interrupts( int minor, int imr_mask ) { unsigned32 pMC68681; setRegister_f setReg; pMC68681 = Console_Port_Tbl[minor].ulCtrlPort1; setReg = Console_Port_Tbl[minor].setRegister; /* * Enable interrupts on RX and TX -- not break */ (*setReg)( pMC68681, MC68681_INTERRUPT_MASK_REG, mc68681_build_imr(minor, imr_mask) ); }