/* * This file contains the PowerPC 403GA console IO package. * * Author: Thomas Doerfler * IMD Ingenieurbuero fuer Microcomputertechnik * * COPYRIGHT (c) 1998 by IMD * * Changes from IMD are covered by the original distributions terms. * changes include interrupt support and termios support * for backward compatibility, the original polled driver has been * renamed to console.c.polled * * This file has been initially created (polled version) by * * Author: Andrew Bray * * COPYRIGHT (c) 1995 by i-cubed ltd. * * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this file * for any purpose is hereby granted without fee, provided that * the above copyright notice and this notice appears in all * copies, and that the name of i-cubed limited not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * i-cubed limited makes no representations about the suitability * of this software for any purpose. * * Modifications for spooling (interrupt driven) console driver * by Thomas Doerfler * for these modifications: * COPYRIGHT (c) 1997 by IMD, Puchheim, Germany. * * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this file * for any purpose is hereby granted without fee, provided that * the above copyright notice and this notice appears in all * copies. IMD makes no representations about the suitability * of this software for any purpose. * * Derived from c/src/lib/libbsp/no_cpu/no_bsp/console/console.c: * * COPYRIGHT (c) 1989, 1990, 1991, 1992, 1993, 1994. * On-Line Applications Research Corporation (OAR). * * $Id$ */ #define NO_BSP_INIT #include #include #include "../irq/ictrl.h" #include /* for atexit() */ struct async { /*---------------------------------------------------------------------------+ | Line Status Register. +---------------------------------------------------------------------------*/ unsigned char SPLS; unsigned char SPLSset; #define LSRDataReady 0x80 #define LSRFramingError 0x40 #define LSROverrunError 0x20 #define LSRParityError 0x10 #define LSRBreakInterrupt 0x08 #define LSRTxHoldEmpty 0x04 #define LSRTxShiftEmpty 0x02 /*---------------------------------------------------------------------------+ | Handshake Status Register. +---------------------------------------------------------------------------*/ unsigned char SPHS; unsigned char SPHSset; #define HSRDsr 0x80 #define HSRCts 0x40 /*---------------------------------------------------------------------------+ | Baud rate divisor registers +---------------------------------------------------------------------------*/ unsigned char BRDH; unsigned char BRDL; /*---------------------------------------------------------------------------+ | Control Register. +---------------------------------------------------------------------------*/ unsigned char SPCTL; #define CRNormal 0x00 #define CRLoopback 0x40 #define CRAutoEcho 0x80 #define CRDtr 0x20 #define CRRts 0x10 #define CRWordLength7 0x00 #define CRWordLength8 0x08 #define CRParityDisable 0x00 #define CRParityEnable 0x04 #define CREvenParity 0x00 #define CROddParity 0x02 #define CRStopBitsOne 0x00 #define CRStopBitsTwo 0x01 #define CRDisableDtrRts 0x00 /*--------------------------------------------------------------------------+ | Receiver Command Register. +--------------------------------------------------------------------------*/ unsigned char SPRC; #define RCRDisable 0x00 #define RCREnable 0x80 #define RCRIntDisable 0x00 #define RCRIntEnabled 0x20 #define RCRDMACh2 0x40 #define RCRDMACh3 0x60 #define RCRErrorInt 0x10 #define RCRPauseEnable 0x08 /*--------------------------------------------------------------------------+ | Transmitter Command Register. +--------------------------------------------------------------------------*/ unsigned char SPTC; #define TCRDisable 0x00 #define TCREnable 0x80 #define TCRIntDisable 0x00 #define TCRIntEnabled 0x20 #define TCRDMACh2 0x40 #define TCRDMACh3 0x60 #define TCRTxEmpty 0x10 #define TCRErrorInt 0x08 #define TCRStopPause 0x04 #define TCRBreakGen 0x02 /*--------------------------------------------------------------------------+ | Miscellanies defines. +--------------------------------------------------------------------------*/ unsigned char SPTB; #define SPRB SPTB }; typedef volatile struct async *pasync; static const pasync port = (pasync)0x40000000; static void *spittyp; /* handle for termios */ int ppc403_spi_interrupt = 1; /* use interrupts... */ /* * Rx Interrupt handler */ static rtems_isr spiRxInterruptHandler (rtems_vector_number v) { char ch; /* clear any receive errors (errors are ignored now) */ port->SPLS = (LSRFramingError | LSROverrunError | LSRParityError | LSRBreakInterrupt); /* * Buffer received? */ if (port->SPLS & LSRDataReady) { ch = port->SPRB; /* read receive buffer */ rtems_termios_enqueue_raw_characters (spittyp,&ch,1); } } /* * Tx Interrupt handler */ static rtems_isr spiTxInterruptHandler (rtems_vector_number v) { /* * char transmitted? */ if (0 != (port->SPLS & LSRTxHoldEmpty)) { /* must always be true!! */ port->SPTC &= ~TCRIntEnabled; /* stop irqs for now... */ /* and call termios... */ rtems_termios_dequeue_characters (spittyp,1); } } /* * enable/disable RTS line to start/stop remote transmitter */ static int spiStartRemoteTx (int minor) { rtems_interrupt_level level; rtems_interrupt_disable (level); port->SPCTL |= CRRts; /* activate RTS */ rtems_interrupt_enable (level); return 0; } static int spiStopRemoteTx (int minor) { rtems_interrupt_level level; rtems_interrupt_disable (level); port->SPCTL &= ~CRRts; /* deactivate RTS */ rtems_interrupt_enable (level); return 0; } void spiBaudSet(uint32_t baudrate) { uint32_t tmp; extern uint32_t bsp_serial_per_sec; tmp = bsp_serial_per_sec / baudrate; tmp = ((tmp) >> 4) - 1; port->BRDL = tmp & 0xff; port->BRDH = tmp >> 8; } /* * Hardware-dependent portion of tcsetattr(). */ static int spiSetAttributes (int minor, const struct termios *t) { int baud; /* FIXME: check c_cflag & CRTSCTS for hardware flowcontrol */ /* FIXME: check and IMPLEMENT XON/XOFF */ switch (t->c_cflag & CBAUD) { default: baud = -1; break; case B50: baud = 50; break; case B75: baud = 75; break; case B110: baud = 110; break; case B134: baud = 134; break; case B150: baud = 150; break; case B200: baud = 200; break; case B300: baud = 300; break; case B600: baud = 600; break; case B1200: baud = 1200; break; case B1800: baud = 1800; break; case B2400: baud = 2400; break; case B4800: baud = 4800; break; case B9600: baud = 9600; break; case B19200: baud = 19200; break; case B38400: baud = 38400; break; case B57600: baud = 57600; break; case B115200: baud = 115200; break; case B230400: baud = 230400; break; case B460800: baud = 460800; break; } if (baud > 0) { spiBaudSet(baud); } return 0; } static int spiPollRead (int minor) { unsigned char status; while (0 == ((status = port->SPLS) & LSRDataReady)) { /* Clean any dodgy status */ if ((status & (LSRFramingError | LSROverrunError | LSRParityError | LSRBreakInterrupt)) != 0) { port->SPLS = (LSRFramingError | LSROverrunError | LSRParityError | LSRBreakInterrupt); } } return port->SPRB; } static int spiInterruptWrite (int minor, const char *buf, int len) { port->SPTB = *buf; /* write char to send */ port->SPTC |= TCRIntEnabled; /* always enable tx interrupt */ return 0; } static int spiPollWrite(int minor,const char *buf,int len) { unsigned char status; while (len-- > 0) { do { if (port->SPHS) { port->SPHS = (HSRDsr | HSRCts); } status = port->SPLS; } while (0 == (status & LSRTxHoldEmpty)); port->SPTB = *buf++; } return 0; } /* * * deinit SPI * */ void spiDeInit(void) { extern uint32_t bsp_serial_rate; /* * disable interrupts for serial port * set it to state to work with polling boot monitor, if any... */ /* set up baud rate to original state */ spiBaudSet(bsp_serial_rate); /* clear any receive (error) status */ port->SPLS = (LSRDataReady | LSRFramingError | LSROverrunError | LSRParityError | LSRBreakInterrupt); /* set up port control: DTR/RTS active,8 bit,1 stop,no parity */ port->SPCTL = (CRNormal | CRDtr | CRRts | CRWordLength8 | CRParityDisable | CRStopBitsOne); /* clear handshake status bits */ port->SPHS = (HSRDsr | HSRCts); /* enable receiver/transmitter, no interrupts */ port->SPRC = (RCREnable | RCRIntDisable); port->SPTC = (TCREnable | TCRIntDisable); } /* * * init SPI * */ rtems_status_code spiInitialize(void) { register unsigned tmp; rtems_isr_entry previous_isr; /* this is a dummy */ extern bool bsp_serial_external_clock; extern uint32_t bsp_serial_rate; /* * Initialise the serial port */ /* * select RTS/CTS hardware handshake lines, * select clock source */ asm volatile ("mfdcr %0, 0xa0" : "=r" (tmp)); /* IOCR */ tmp &= ~3; tmp |= (bsp_serial_external_clock ? 2 : 0) | 1; asm volatile ("mtdcr 0xa0, %0" : "=r" (tmp) : "0" (tmp)); /* IOCR */ /* clear any receive (error) status */ port->SPLS = (LSRDataReady | LSRFramingError | LSROverrunError | LSRParityError | LSRBreakInterrupt); /* set up baud rate */ spiBaudSet(bsp_serial_rate); /* set up port control: DTR/RTS active,8 bit,1 stop,no parity */ port->SPCTL = (CRNormal | CRDtr | CRRts | CRWordLength8 | CRParityDisable | CRStopBitsOne); /* clear handshake status bits */ port->SPHS = (HSRDsr | HSRCts); if (ppc403_spi_interrupt) { /* add rx/tx isr to vector table */ ictrl_set_vector(spiRxInterruptHandler, PPC_IRQ_EXT_SPIR, &previous_isr); ictrl_set_vector(spiTxInterruptHandler, PPC_IRQ_EXT_SPIT, &previous_isr); port->SPRC = (RCREnable | RCRIntEnabled | RCRErrorInt); port->SPTC = (TCREnable | TCRIntDisable); /* don't enable TxInt yet */ } else { /* enable receiver/transmitter, no interrupts */ port->SPRC = (RCREnable | RCRIntDisable); port->SPTC = (TCREnable | TCRIntDisable); } atexit(spiDeInit); return RTEMS_SUCCESSFUL; } /* *************** * BOILERPLATE * *************** */ /* console_initialize * * This routine initializes the console IO driver. * * Input parameters: NONE * * Output parameters: NONE * * Return values: */ rtems_device_driver console_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { rtems_status_code status; /* * Set up TERMIOS */ rtems_termios_initialize (); /* * Do device-specific initialization */ spiInitialize (); /* * Register the device */ status = rtems_io_register_name ("/dev/console", major, 0); if (status != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (status); return RTEMS_SUCCESSFUL; } /* * Open entry point */ rtems_device_driver console_open( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { rtems_status_code sc; static const rtems_termios_callbacks intrCallbacks = { NULL, /* firstOpen */ NULL, /* lastClose */ NULL, /* pollRead */ spiInterruptWrite, /* write */ spiSetAttributes, /* setAttributes */ spiStopRemoteTx, /* stopRemoteTx */ spiStartRemoteTx, /* startRemoteTx */ 1 /* outputUsesInterrupts */ }; static const rtems_termios_callbacks pollCallbacks = { NULL, /* firstOpen */ NULL, /* lastClose */ spiPollRead, /* pollRead */ spiPollWrite, /* write */ spiSetAttributes, /* setAttributes */ spiStopRemoteTx, /* stopRemoteTx */ spiStartRemoteTx, /* startRemoteTx */ 0 /* outputUsesInterrupts */ }; if (ppc403_spi_interrupt) { rtems_libio_open_close_args_t *args = arg; sc = rtems_termios_open (major, minor, arg, &intrCallbacks); spittyp = args->iop->data1; } else { sc = rtems_termios_open (major, minor, arg, &pollCallbacks); } return sc; } /* * Close entry point */ rtems_device_driver console_close( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { return rtems_termios_close (arg); } /* * read bytes from the serial port. We only have stdin. */ rtems_device_driver console_read( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { return rtems_termios_read (arg); } /* * write bytes to the serial port. Stdout and stderr are the same. */ rtems_device_driver console_write( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { return rtems_termios_write (arg); } /* * IO Control entry point */ rtems_device_driver console_control( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { return rtems_termios_ioctl (arg); }