/* * This file contains the console driver chip level routines for the * z85c30 chip. * * COPYRIGHT (c) 1998 by Radstone Technology * * * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU. * * You are hereby granted permission to use, copy, modify, and distribute * this file, provided that this notice, plus the above copyright notice * and disclaimer, appears in all copies. Radstone Technology will provide * no support for this code. * * COPYRIGHT (c) 1989-1997. * 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 "console.h" #include "z85c30_p.h" /* * Flow control is only supported when using interrupts */ console_flow z85c30_flow_RTSCTS = { z85c30_negate_RTS, /* deviceStopRemoteTx */ z85c30_assert_RTS /* deviceStartRemoteTx */ }; console_flow z85c30_flow_DTRCTS = { z85c30_negate_DTR, /* deviceStopRemoteTx */ z85c30_assert_DTR /* deviceStartRemoteTx */ }; /* * Exported driver function table */ console_fns z85c30_fns = { z85c30_probe, /* deviceProbe */ z85c30_open, /* deviceFirstOpen */ z85c30_flush, /* deviceLastClose */ NULL, /* deviceRead */ z85c30_write_support_int, /* deviceWrite */ z85c30_initialize_interrupts, /* deviceInitialize */ z85c30_write_polled, /* deviceWritePolled */ FALSE, /* deviceOutputUsesInterrupts */ }; console_fns z85c30_fns_polled = { z85c30_probe, /* deviceProbe */ z85c30_open, /* deviceFirstOpen */ z85c30_close, /* deviceLastClose */ z85c30_inbyte_nonblocking_polled, /* deviceRead */ z85c30_write_support_polled, /* deviceWrite */ z85c30_init, /* deviceInitialize */ z85c30_write_polled, /* deviceWritePolled */ FALSE, /* deviceOutputUsesInterrupts */ }; /* * Read_85c30_register * * Read a Z85c30 register */ static unsigned8 Read_85c30_register( unsigned32 ulCtrlPort, unsigned8 ucRegNum ) { unsigned8 ucData; outport_byte(ulCtrlPort, ucRegNum); inport_byte(ulCtrlPort, ucData); return ucData; } /* * Write_85c30_register * * Write a Z85c30 register */ static void Write_85c30_register( unsigned32 ulCtrlPort, unsigned8 ucRegNum, unsigned8 ucData ) { if(ucRegNum!=SCC_WR0_SEL_WR0) { outport_byte(ulCtrlPort, ucRegNum); } outport_byte(ulCtrlPort, ucData); } /* * Read_85c30_data * * Read a Z85c30 data register */ static unsigned8 Read_85c30_data( unsigned32 ulDataPort ) { unsigned8 ucData; inport_byte(ulDataPort, ucData); return ucData; } /* * Write_85c30_data * * Write a Z85c30 data register */ static void Write_85c30_data( unsigned32 ulDataPort, unsigned8 ucData ) { outport_byte(ulDataPort, ucData); } /* * z85c30_initialize_port * * initialize a z85c30 Port */ static void z85c30_initialize_port( int minor ) { unsigned32 ulCtrlPort; unsigned32 ulBaudDivisor; ulCtrlPort=Console_Port_Tbl[minor].ulCtrlPort1; /* * Using register 4 * Set up the clock rate is 16 times the data * rate, 8 bit sync char, 1 stop bit, no parity */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR4, SCC_WR4_1_STOP | SCC_WR4_16_CLOCK); /* * Set up for 8 bits/character on receive with * receiver disable via register 3 */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR3, SCC_WR3_RX_8_BITS); /* * Set up for 8 bits/character on transmit * with transmitter disable via register 5 */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR5, SCC_WR5_TX_8_BITS); /* * Clear misc control bits */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR10, 0x00); /* * Setup the source of the receive and xmit * clock as BRG output and the transmit clock * as the output source for TRxC pin via register 11 */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR11, SCC_WR11_OUT_BR_GEN | SCC_WR11_TRXC_OI | SCC_WR11_TX_BR_GEN | SCC_WR11_RX_BR_GEN); ulBaudDivisor=Z85C30_Baud( (unsigned32)Console_Port_Tbl[minor].pDeviceParams); /* * Setup the lower 8 bits time constants=1E. * If the time constans=1E, then the desire * baud rate will be equilvalent to 9600, via register 12. */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR12, ulBaudDivisor&0xff); /* * using register 13 * Setup the upper 8 bits time constant */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR13, (ulBaudDivisor>>8)&0xff); /* * Enable the baud rate generator enable with clock from the * SCC's PCLK input via register 14. */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR14, SCC_WR14_BR_EN | SCC_WR14_BR_SRC | SCC_WR14_NULL); /* * We are only interested in CTS state changes */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR15, SCC_WR15_CTS_IE); /* * Reset errors */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT); Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_ERR_RST); /* * Enable the receiver via register 3 */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR3, SCC_WR3_RX_8_BITS | SCC_WR3_RX_EN); /* * Enable the transmitter pins set via register 5. */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR5, SCC_WR5_TX_8_BITS | SCC_WR5_TX_EN); /* * Disable interrupts */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR1, 0); /* * Reset TX CRC */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_CRC); /* * Reset interrupts */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT); } static int z85c30_open( int major, int minor, void * arg ) { /* * Assert DTR */ if(Console_Port_Tbl[minor].pDeviceFlow !=&z85c30_flow_DTRCTS) { z85c30_assert_DTR(minor); } return(RTEMS_SUCCESSFUL); } static int z85c30_close( int major, int minor, void * arg ) { /* * Negate DTR */ if(Console_Port_Tbl[minor].pDeviceFlow !=&z85c30_flow_DTRCTS) { z85c30_negate_DTR(minor); } return(RTEMS_SUCCESSFUL); } /* * z85c30_write_polled * * This routine transmits a character using polling. */ static void z85c30_write_polled( int minor, char cChar ) { volatile unsigned8 z85c30_status; unsigned32 ulCtrlPort; ulCtrlPort=Console_Port_Tbl[minor].ulCtrlPort1; /* * Wait for the Transmit buffer to indicate that it is empty. */ z85c30_status=Read_85c30_register(ulCtrlPort, SCC_WR0_SEL_RD0); while(!Z85C30_Status_Is_TX_buffer_empty(z85c30_status)) { /* * Yield while we wait */ if(_System_state_Is_up(_System_state_Get())) { rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); } z85c30_status=Read_85c30_register(ulCtrlPort, SCC_WR0_SEL_RD0); } /* * Write the character. */ Write_85c30_data(Console_Port_Tbl[minor].ulDataPort, cChar); } /* * Console Device Driver Entry Points */ static boolean z85c30_probe(int minor) { /* * If the configuration dependant probe has located the device then * assume it is there */ return(TRUE); } static void z85c30_init(int minor) { unsigned32 ulCtrlPort; unsigned8 dummy; z85c30_context *pz85c30Context; pz85c30Context=(z85c30_context *)malloc(sizeof(z85c30_context)); Console_Port_Data[minor].pDeviceContext=(void *)pz85c30Context; pz85c30Context->ucModemCtrl=SCC_WR5_TX_8_BITS | SCC_WR5_TX_EN; ulCtrlPort=Console_Port_Tbl[minor].ulCtrlPort1; if(ulCtrlPort==Console_Port_Tbl[minor].ulCtrlPort2) { /* * This is channel A */ /* * Ensure port state machine is reset */ inport_byte(ulCtrlPort, dummy); Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR9, SCC_WR9_CH_A_RST); } else { /* * This is channel B */ /* * Ensure port state machine is reset */ inport_byte(ulCtrlPort, dummy); Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR9, SCC_WR9_CH_B_RST); } z85c30_initialize_port(minor); } /* * These routines provide control of the RTS and DTR lines */ /* * z85c30_assert_RTS */ static void z85c30_assert_RTS(int minor) { unsigned32 Irql; z85c30_context *pz85c30Context; pz85c30Context=(z85c30_context *) Console_Port_Data[minor].pDeviceContext; /* * Assert RTS */ rtems_interrupt_disable(Irql); pz85c30Context->ucModemCtrl|=SCC_WR5_RTS; Write_85c30_register( Console_Port_Tbl[minor].ulCtrlPort1, SCC_WR0_SEL_WR5, pz85c30Context->ucModemCtrl); rtems_interrupt_enable(Irql); } /* * z85c30_negate_RTS */ static void z85c30_negate_RTS(int minor) { unsigned32 Irql; z85c30_context *pz85c30Context; pz85c30Context=(z85c30_context *) Console_Port_Data[minor].pDeviceContext; /* * Negate RTS */ rtems_interrupt_disable(Irql); pz85c30Context->ucModemCtrl&=~SCC_WR5_RTS; Write_85c30_register( Console_Port_Tbl[minor].ulCtrlPort1, SCC_WR0_SEL_WR5, pz85c30Context->ucModemCtrl); rtems_interrupt_enable(Irql); } /* * These flow control routines utilise a connection from the local DTR * line to the remote CTS line */ /* * z85c30_assert_DTR */ static void z85c30_assert_DTR(int minor) { unsigned32 Irql; z85c30_context *pz85c30Context; pz85c30Context=(z85c30_context *) Console_Port_Data[minor].pDeviceContext; /* * Assert DTR */ rtems_interrupt_disable(Irql); pz85c30Context->ucModemCtrl|=SCC_WR5_DTR; Write_85c30_register( Console_Port_Tbl[minor].ulCtrlPort1, SCC_WR0_SEL_WR5, pz85c30Context->ucModemCtrl); rtems_interrupt_enable(Irql); } /* * z85c30_negate_DTR */ static void z85c30_negate_DTR(int minor) { unsigned32 Irql; z85c30_context *pz85c30Context; pz85c30Context=(z85c30_context *) Console_Port_Data[minor].pDeviceContext; /* * Negate DTR */ rtems_interrupt_disable(Irql); pz85c30Context->ucModemCtrl&=~SCC_WR5_DTR; Write_85c30_register( Console_Port_Tbl[minor].ulCtrlPort1, SCC_WR0_SEL_WR5, pz85c30Context->ucModemCtrl); rtems_interrupt_enable(Irql); } /* * z85c30_isr * * This routine is the console interrupt handler for COM3 and COM4 * * Input parameters: * vector - vector number * * Output parameters: NONE * * Return values: NONE */ static void z85c30_process( int minor, unsigned8 ucIntPend ) { unsigned32 ulCtrlPort, ulDataPort; volatile unsigned8 z85c30_status; char cChar; ulCtrlPort=Console_Port_Tbl[minor].ulCtrlPort1; ulDataPort=Console_Port_Tbl[minor].ulDataPort; /* * Deal with any received characters */ while(ucIntPend&SCC_RR3_B_RX_IP) { z85c30_status=Read_85c30_register(ulCtrlPort, SCC_WR0_SEL_RD0); if(!Z85C30_Status_Is_RX_character_available(z85c30_status)) { break; } /* * Return the character read. */ cChar=Read_85c30_data(ulDataPort); rtems_termios_enqueue_raw_characters( Console_Port_Data[minor].termios_data, &cChar, 1); } while(TRUE) { z85c30_status=Read_85c30_register(ulCtrlPort, SCC_WR0_SEL_RD0); if(!Z85C30_Status_Is_TX_buffer_empty(z85c30_status)) { /* * We'll get another interrupt when * the transmitter holding reg. becomes * free again and we are clear to send */ break; } if(!Z85C30_Status_Is_CTS_asserted(z85c30_status)) { /* * We can't transmit yet */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_INT); /* * The next state change of CTS will wake us up */ break; } if(Ring_buffer_Is_empty(&Console_Port_Data[minor].TxBuffer)) { Console_Port_Data[minor].bActive=FALSE; if(Console_Port_Tbl[minor].pDeviceFlow !=&z85c30_flow_RTSCTS) { z85c30_negate_RTS(minor); } /* * There is no data to transmit */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_INT); break; } Ring_buffer_Remove_character( &Console_Port_Data[minor].TxBuffer, cChar); /* * transmit character */ Write_85c30_data(ulDataPort, cChar); /* * Interrupt once FIFO has room */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_INT); break; } if(ucIntPend&SCC_RR3_B_EXT_IP) { /* * Clear the external status interrupt */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT); z85c30_status=Read_85c30_register(ulCtrlPort, SCC_WR0_SEL_RD0); } /* * Reset interrupts */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_HI_IUS); } static rtems_isr z85c30_isr( rtems_vector_number vector ) { int minor; unsigned32 ulCtrlPort; volatile unsigned8 ucIntPend; volatile unsigned8 ucIntPendPort; for(minor=0;minor>3; ucIntPendPort=ucIntPendPort&=7; } else { ucIntPendPort=ucIntPend&=7; } if(ucIntPendPort) { z85c30_process(minor, ucIntPendPort); } } while(ucIntPendPort); } } } /* * z85c30_flush */ static int z85c30_flush(int major, int minor, void *arg) { while(!Ring_buffer_Is_empty(&Console_Port_Data[minor].TxBuffer)) { /* * Yield while we wait */ if(_System_state_Is_up(_System_state_Get())) { rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); } } z85c30_close(major, minor, arg); return(RTEMS_SUCCESSFUL); } /* * z85c30_initialize_interrupts * * This routine initializes the console's receive and transmit * ring buffers and loads the appropriate vectors to handle the interrupts. * * Input parameters: NONE * * Output parameters: NONE * * Return values: NONE */ static void z85c30_enable_interrupts( int minor ) { unsigned32 ulCtrlPort; ulCtrlPort=Console_Port_Tbl[minor].ulCtrlPort1; /* * Enable interrupts */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR1, SCC_WR1_EXT_INT_EN | SCC_WR1_TX_INT_EN | SCC_WR1_INT_ALL_RX); Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR2, 0); Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR9, SCC_WR9_MIE); /* * Reset interrupts */ Write_85c30_register(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT); } static void z85c30_initialize_interrupts( int minor ) { z85c30_init(minor); Ring_buffer_Initialize(&Console_Port_Data[minor].TxBuffer); Console_Port_Data[minor].bActive=FALSE; if(Console_Port_Tbl[minor].pDeviceFlow !=&z85c30_flow_RTSCTS) { z85c30_negate_RTS(minor); } if(Console_Port_Tbl[minor].ulCtrlPort1== Console_Port_Tbl[minor].ulCtrlPort2) { /* * Only do this for Channel A */ set_vector(z85c30_isr, Console_Port_Tbl[minor].ulIntVector, 1); } z85c30_enable_interrupts(minor); } /* * z85c30_write_support_int * * Console Termios output entry point. * */ static int z85c30_write_support_int( int minor, const char *buf, int len) { int i; unsigned32 Irql; for(i=0; i