diff options
Diffstat (limited to 'bsps/m68k/mrm332/console/sci.c')
-rw-r--r-- | bsps/m68k/mrm332/console/sci.c | 1586 |
1 files changed, 1586 insertions, 0 deletions
diff --git a/bsps/m68k/mrm332/console/sci.c b/bsps/m68k/mrm332/console/sci.c new file mode 100644 index 0000000000..c6b4933f13 --- /dev/null +++ b/bsps/m68k/mrm332/console/sci.c @@ -0,0 +1,1586 @@ +/***************************************************************************** +* File: sci.c +* +* Desc: This file contains the console IO routines for the SCI port. +* There are two interfaces in this module. One is for the rtems +* termios/console code and the other is a device driver interface. +* This module works together with the termio module which is +* sometimes referred to as the "line disciplines" which implements +* terminal i/o processing like tabs, backspaces, and newlines. +* The rtems printf uses interrupt io and the rtems printk routine +* uses polled io which is better for debugging. +* +* Index: Documentation +* Section A - Include Files +* Section B - Manifest Constants +* Section C - External Data +* Section D - External Functions +* Section E - Local Functions +* Section F - Local Variables +* Section G - A circular data buffer for rcv chars +* Section H - RTEMS termios callbacks for the interrupt api +* Section I - RTEMS termios callbacks for the polled api + +* Section 0 - Miscellaneous routines +* Section 1 - Routines to manipulate the circular buffer +* Section 2 - Interrupt based entry points for the termios module +* Section 3 - Polling based entry points for the termios module +* Section 4 - Device driver public api entry points +* Section 5 - Hardware level routines +* Section 6 - Testing and debugging code +* +* Refer: Motorola QSM Reference Manual - Chapter 5 - SCI sub-module +* +* Note: See bsp.h,confdefs.h,system.h for installing drivers into RTEMS. +* +*****************************************************************************/ + +/***************************************************************************** + Overview of serial port console terminal input/output +*****************************************************************************/ + +/* + +-----------+ +---------+ + | app | | app | + +-----------+ +---------+ + | | + | (printf,scanf,etc.) | + v | + +-----------+ | + | libc | | + +-----------+ | + | | + | | + | (open,close,read,write,ioctl) | + ======|==========================================|======================== + | /dev/console | /dev/sci + | (stdin,stdout,stderr) | + ======|==========================================|======================== + | | + | | + v v + +-----------+ +-----------+ +---------+ + | console | <---> | termios | <---> | sci | + | driver | | module | | driver | + +-----------+ +-----------+ +---------+ + | + | + v + +---------+ + | | + | uart | + | | + +---------+ +*/ + + +/***************************************************************************** + Section A - Include Files +*****************************************************************************/ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/bspIo.h> +#include <stdio.h> +#include <rtems/libio.h> +#include <libchip/serial.h> +#include <libchip/sersupp.h> +#include "sci.h" +#include <rtems/m68k/qsm.h> +#include <inttypes.h> +/*#include "../misc/include/cpu332.h" */ + +/***************************************************************************** + Section B - Manifest Constants +*****************************************************************************/ + +#define SCI_MINOR 0 /* minor device number */ + +/* IMPORTANT - if the device driver api is opened, it means the sci is being + * used for direct hardware access, so other users (like termios) get ignored + */ +#define DRIVER_CLOSED 0 /* the device driver api is closed */ +#define DRIVER_OPENED 1 /* the device driver api is opened */ + +/* system clock definitions, i dont have documentation on this... */ + +#if 0 /* Not needed, this is provided in mrm332.h */ +#define XTAL 32768.0 /* crystal frequency in Hz */ +#define NUMB_W 0 /* system clock parameters */ +#define NUMB_X 1 +#define NUMB_Y 0x38 /* for 14.942 Mhz */ +#define NUMB_Y 0x3F /* for 16.777 Mhz */ + +#define SYS_CLOCK (XTAL * 4.0 * (NUMB_Y+1) * (1 << (2 * NUMB_W + NUMB_X))) + +#endif + + +/***************************************************************************** + Section C - External Data +*****************************************************************************/ + + + +/***************************************************************************** + Section D - External Functions +*****************************************************************************/ + + + +/***************************************************************************** + Section E - Local Functions +*****************************************************************************/ + +void SCI_output_char(char c); + +/*rtems_isr SciIsr( rtems_vector_number vector ); interrupt handler */ + +const rtems_termios_callbacks * SciGetTermiosHandlers( int32_t polled ); + +rtems_device_driver SciInitialize( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciOpen( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciClose( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciRead( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciWrite( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciControl( /* device driver api */ + rtems_device_major_number, rtems_device_minor_number, void *); +rtems_device_driver SciRead ( + rtems_device_major_number, rtems_device_minor_number, void *); + +rtems_isr SciIsr( rtems_vector_number vector ); + +int SciInterruptOpen(int, int, void *); /* termios api */ +int SciInterruptClose(int, int, void *); /* termios api */ +ssize_t SciInterruptWrite(int, const char *, size_t); /* termios api */ + +int SciSetAttributes(int, const struct termios*); /* termios api */ +int SciPolledOpen(int, int, void *); /* termios api */ +int SciPolledClose(int, int, void *); /* termios api */ +int SciPolledRead(int); /* termios api */ +ssize_t SciPolledWrite(int, const char *, size_t); /* termios api */ + +static void SciSetBaud(uint32_t rate); /* hardware routine */ +static void SciSetDataBits(uint16_t bits); /* hardware routine */ +static void SciSetParity(uint16_t parity); /* hardware routine */ + +static void inline SciDisableAllInterrupts( void ); /* hardware routine */ +static void inline SciDisableTransmitInterrupts( void );/* hardware routine */ +static void inline SciDisableReceiveInterrupts( void ); /* hardware routine */ + +static void inline SciEnableTransmitInterrupts( void ); /* hardware routine */ +static void inline SciEnableReceiveInterrupts( void ); /* hardware routine */ + +static void inline SciDisableReceiver( void ); /* hardware routine */ +static void inline SciDisableTransmitter( void ); /* hardware routine */ + +static void inline SciEnableReceiver( void ); /* hardware routine */ +static void inline SciEnableTransmitter( void ); /* hardware routine */ + +void SciWriteCharWait ( uint8_t ); /* hardware routine */ +void SciWriteCharNoWait( uint8_t ); /* hardware routine */ + +uint8_t inline SciCharAvailable( void ); /* hardware routine */ + +static uint8_t inline SciReadCharWait( void ); /* hardware routine */ +static uint8_t inline SciReadCharNoWait( void ); /* hardware routine */ + +void SciSendBreak( void ); /* test routine */ + +static int8_t SciRcvBufGetChar(void); /* circular rcv buf */ +static void SciRcvBufPutChar( uint8_t); /* circular rcv buf */ +#if 0 +static void SciRcvBufFlush( void ); /* unused routine */ +#endif + +void SciUnitTest(void); /* test routine */ +void SciPrintStats(void); /* test routine */ + + +/***************************************************************************** + Section F - Local Variables +*****************************************************************************/ + +static struct rtems_termios_tty *SciTermioTty; + +static uint8_t SciInited = 0; /* has the driver been inited */ + +static uint8_t SciOpened; /* has the driver been opened */ + +static uint8_t SciMajor; /* major device number */ + +static uint16_t SciBaud; /* current value in baud register */ + +static uint32_t SciBytesIn = 0; /* bytes received */ +static uint32_t SciBytesOut = 0; /* bytes transmitted */ + +static uint32_t SciErrorsParity = 0; /* error counter */ +static uint32_t SciErrorsNoise = 0; /* error counter */ +static uint32_t SciErrorsFraming = 0; /* error counter */ +static uint32_t SciErrorsOverrun = 0; /* error counter */ + +#if defined(CONSOLE_SCI) + +/* this is what rtems printk uses to do polling based output */ + +BSP_output_char_function_type BSP_output_char = SCI_output_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +#endif + +/***************************************************************************** + Section G - A circular buffer for rcv chars when the driver interface is used. +*****************************************************************************/ + +/* it is trivial to wrap your buffer pointers when size is a power of two */ + +#define SCI_RCV_BUF_SIZE 256 /* must be a power of 2 !!! */ + +/* if someone opens the sci device using the device driver interface, + * then the receive data interrupt handler will put characters in this buffer + * instead of sending them up to the termios module for the console + */ +static uint8_t SciRcvBuffer[SCI_RCV_BUF_SIZE]; + +static uint8_t SciRcvBufPutIndex = 0; /* array index to put in next char */ + +static uint8_t SciRcvBufGetIndex = 0; /* array index to take out next char */ + +static uint16_t SciRcvBufCount = 0; /* how many bytes are in the buffer */ + + + +/***************************************************************************** + Section H - RTEMS termios callbacks for the interrupt version of the driver +*****************************************************************************/ + +static const rtems_termios_callbacks SciInterruptCallbacks = +{ + SciInterruptOpen, /* first open */ + SciInterruptClose, /* last close */ + NULL, /* polled read (not required) */ + SciInterruptWrite, /* write */ + SciSetAttributes, /* set attributes */ + NULL, /* stop remote xmit */ + NULL, /* start remote xmit */ + TRUE /* output uses interrupts */ +}; + +/***************************************************************************** + Section I - RTEMS termios callbacks for the polled version of the driver +*****************************************************************************/ + +static const rtems_termios_callbacks SciPolledCallbacks = +{ + SciPolledOpen, /* first open */ + SciPolledClose, /* last close */ + SciPolledRead, /* polled read */ + SciPolledWrite, /* write */ + SciSetAttributes, /* set attributes */ + NULL, /* stop remote xmit */ + NULL, /* start remote xmit */ + FALSE /* output uses interrupts */ +}; + + +/* + * SECTION 0 + * MISCELLANEOUS ROUTINES + */ + +/**************************************************************************** + * Func: SCI_output_char + * Desc: used by rtems printk function to send a char to the uart + * Inputs: the character to transmit + * Outputs: none + * Errors: none + * Scope: public + ****************************************************************************/ + +void SCI_output_char(char c) +{ +/* ( minor device number, pointer to the character, length ) */ + + SciPolledWrite( SCI_MINOR, &c, 1); + + return; +} + + +/**************************************************************************** +* Func: SciGetTermiosHandlers +* Desc: returns a pointer to the table of serial io functions +* this is called from console_open with polled set to false +* Inputs: flag indicating whether we want polled or interrupt driven io +* Outputs: pointer to function table +* Errors: none +* Scope: public +****************************************************************************/ + +const rtems_termios_callbacks * SciGetTermiosHandlers( int32_t polled ) +{ + if ( polled ) + { + return &SciPolledCallbacks; /* polling based */ + } + else + { + return &SciInterruptCallbacks; /* interrupt driven */ + } +} + + +/**************************************************************************** +* Func: SciIsr +* Desc: interrupt handler for serial communications interface +* Inputs: vector number - unused +* Outputs: none +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_isr SciIsr( rtems_vector_number vector ) +{ + uint8_t ch; + + if ( (*SCSR) & SCI_ERROR_PARITY ) SciErrorsParity ++; + if ( (*SCSR) & SCI_ERROR_FRAMING ) SciErrorsFraming ++; + if ( (*SCSR) & SCI_ERROR_NOISE ) SciErrorsNoise ++; + if ( (*SCSR) & SCI_ERROR_OVERRUN ) SciErrorsOverrun ++; + + /* see if it was a transmit interrupt */ + /* data reg empty, xmt complete */ + if ( ( *SCCR1 & SCI_ENABLE_INT_TX ) && ( (*SCSR) & SCI_XMTR_AVAILABLE ) ) + { + SciDisableTransmitInterrupts(); + + /* tell termios module that the charcter was sent */ + /* he will call us later to transmit more if there are any */ + + if (rtems_termios_dequeue_characters( SciTermioTty, 1 )) + { + /* there are more bytes to transmit so enable TX interrupt */ + + SciEnableTransmitInterrupts(); + } + } + + /* see if it was a receive interrupt */ + /* on the sci uart we just get one character per interrupt */ + + while ( SciCharAvailable() ) /* char in data register? */ + { + ch = SciReadCharNoWait(); /* get the char from the uart */ + + /* IMPORTANT!!! */ + /* either send it to the termios module or keep it locally */ + + if ( SciOpened == DRIVER_OPENED ) /* the driver is open */ + { + SciRcvBufPutChar(ch); /* keep it locally */ + } + else /* put in termios buffer */ + { + char c = (char) ch; + rtems_termios_enqueue_raw_characters( SciTermioTty, &c, 1 ); + } + + *SCSR &= SCI_CLEAR_RX_INT; /* clear the interrupt */ + } +} + + +/* + * SECTION 1 + * ROUTINES TO MANIPULATE THE CIRCULAR BUFFER + */ + +/**************************************************************************** +* Func: SciRcvBufGetChar +* Desc: read a character from the circular buffer +* make sure there is data before you call this! +* Inputs: none +* Outputs: the character or -1 +* Errors: none +* Scope: private +****************************************************************************/ + +static int8_t SciRcvBufGetChar(void) +{ + rtems_interrupt_level level; + uint8_t ch; + + if ( SciRcvBufCount == 0 ) + { + rtems_fatal_error_occurred(0xDEAD); /* check the count first! */ + } + + rtems_interrupt_disable( level ); /* disable interrupts */ + + ch = SciRcvBuffer[SciRcvBufGetIndex]; /* get next byte */ + + SciRcvBufGetIndex++; /* bump the index */ + + SciRcvBufGetIndex &= SCI_RCV_BUF_SIZE - 1; /* and wrap it */ + + SciRcvBufCount--; /* decrement counter */ + + rtems_interrupt_enable( level ); /* restore interrupts */ + + return ch; /* return the char */ +} + + +/**************************************************************************** +* Func: SciRcvBufPutChar +* Desc: put a character into the rcv data circular buffer +* Inputs: the character +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void SciRcvBufPutChar( uint8_t ch ) +{ + rtems_interrupt_level level; + + if ( SciRcvBufCount == SCI_RCV_BUF_SIZE ) /* is there room? */ + { + return; /* no, throw it away */ + } + + rtems_interrupt_disable( level ); /* disable interrupts */ + + SciRcvBuffer[SciRcvBufPutIndex] = ch; /* put it in the buf */ + + SciRcvBufPutIndex++; /* bump the index */ + + SciRcvBufPutIndex &= SCI_RCV_BUF_SIZE - 1; /* and wrap it */ + + SciRcvBufCount++; /* increment counter */ + + rtems_interrupt_enable( level ); /* restore interrupts */ + + return; /* return */ +} + + +/**************************************************************************** +* Func: SciRcvBufFlush +* Desc: completely reset and clear the rcv buffer +* Inputs: none +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +#if 0 /* prevents compiler warning */ +static void SciRcvBufFlush( void ) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable( level ); /* disable interrupts */ + + memset( SciRcvBuffer, 0, sizeof(SciRcvBuffer) ); + + SciRcvBufPutIndex = 0; /* clear */ + + SciRcvBufGetIndex = 0; /* clear */ + + SciRcvBufCount = 0; /* clear */ + + rtems_interrupt_enable( level ); /* restore interrupts */ + + return; /* return */ +} +#endif + + +/* + * + * SECTION 2 + * INTERRUPT BASED ENTRY POINTS FOR THE TERMIOS MODULE + */ + +/**************************************************************************** +* Func: SciInterruptOpen +* Desc: open routine for the interrupt based device driver +* Default state is 9600 baud, 8 bits, No parity, and 1 stop bit. ?? +**CHANGED** Default baud rate is now 19200, 8N1 +* called from rtems_termios_open which is called from console_open +* Inputs: major - device number +* minor - device number +* args - points to terminal info +* Outputs: success/fail +* Errors: none +* Scope: public API +****************************************************************************/ + +int SciInterruptOpen( + int major, + int minor, + void *arg +) +{ + rtems_libio_open_close_args_t * args = arg; + rtems_isr_entry old_vector; + + if ( minor != SCI_MINOR ) /* check minor device num */ + { + return -1; + } + + if ( !args ) /* must have args */ + { + return -1; + } + + SciTermioTty = args->iop->data1; /* save address of struct */ + + SciDisableAllInterrupts(); /* turn off sci interrupts */ + + /* THIS IS ACTUALLY A BAD THING - SETTING LINE PARAMETERS HERE */ + /* IT SHOULD BE DONE THROUGH TCSETATTR() WHEN THE CONSOLE IS OPENED!!! */ + +/* SciSetBaud(115200); set the baud rate */ +/* SciSetBaud( 57600); set the baud rate */ +/* SciSetBaud( 38400); set the baud rate */ +/* SciSetBaud( 19200); set the baud rate */ + SciSetBaud( 9600); /* set the baud rate */ + + SciSetParity(SCI_PARITY_NONE); /* set parity to none */ + + SciSetDataBits(SCI_8_DATA_BITS); /* set data bits to 8 */ + + /* Install our interrupt handler into RTEMS. */ + /* 68 is an unused user-defined vector. Note that the vector must be */ + /* even - it sets the low bit for SPI interrupts, and clears it for */ + /* SCI interrupts. Also note that vector 66 is used by CPU32bug on */ + /* the mrm332. */ + + rtems_interrupt_catch( SciIsr, 68, &old_vector ); + + *QSMCR = (*QSMCR & ~IARB) | 1; // Is 1 a good value for qsm iarb? + *QIVR = 68; + *QILR &= 0xf8; + *QILR |= 0x06 & 0x07; + + SciEnableTransmitter(); /* enable the transmitter */ + + SciEnableReceiver(); /* enable the receiver */ + + SciEnableReceiveInterrupts(); /* enable rcv interrupts */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciInterruptClose +* Desc: close routine called by the termios module +* Inputs: major - device number +* minor - device number +* args - unused +* Outputs: success/fail +* Errors: none +* Scope: public - termio entry point +****************************************************************************/ + +int SciInterruptClose( + int major, + int minor, + void *arg +) +{ + SciDisableAllInterrupts(); + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciInterruptWrite +* Desc: writes data to the uart using transmit interrupts +* Inputs: minor - device number +* buf - points to the data +* len - number of bytes to send +* Outputs: success/fail +* Errors: none +* Scope: public API +****************************************************************************/ + +ssize_t SciInterruptWrite( + int minor, + const char *buf, + size_t len +) +{ + /* We are using interrupt driven output so termios only sends us */ + /* one character at a time. The sci does not have a fifo. */ + + if ( !len ) /* no data? */ + { + return -1; /* return error */ + } + + if ( minor != SCI_MINOR ) /* check the minor dev num */ + { + return -1; /* return error */ + } + + if ( SciOpened == DRIVER_OPENED ) /* is the driver api open? */ + { + return -1; /* yep, throw this away */ + } + + SciWriteCharNoWait(*buf); /* try to send a char */ + + *SCSR &= SCI_CLEAR_TDRE; /* clear tx data reg empty flag */ + + SciEnableTransmitInterrupts(); /* enable the tx interrupt */ + + return 0; /* return success */ +} + + +/**************************************************************************** +* Func: SciSetAttributes +* Desc: setup the uart based on the termios modules requests +* Inputs: minor - device number +* t - pointer to the termios info struct +* Outputs: none +* Errors: none +* Scope: public API +****************************************************************************/ + +int SciSetAttributes( + int minor, + const struct termios *t +) +{ + uint32_t baud_requested; + uint32_t sci_rate = 0; + uint16_t sci_parity = 0; + uint16_t sci_databits = 0; + + if ( minor != SCI_MINOR ) /* check the minor dev num */ + { + return -1; /* return error */ + } + + /* if you look closely you will see this is the only thing we use */ + /* set the baud rate */ + + baud_requested = t->c_ospeed; /* baud rate */ + + if (!baud_requested) + { + baud_requested = B9600; /* default to 9600 baud */ + /* baud_requested = B19200; default to 19200 baud */ + } + + sci_rate = rtems_termios_baud_to_number( baud_requested ); + + /* parity error detection */ + + if (t->c_cflag & PARENB) /* enable parity detection? */ + { + if (t->c_cflag & PARODD) + { + sci_parity = SCI_PARITY_ODD; /* select odd parity */ + } + else + { + sci_parity = SCI_PARITY_EVEN; /* select even parity */ + } + } + else + { + sci_parity = SCI_PARITY_NONE; /* no parity, most common */ + } + + /* set the number of data bits, 8 is most common */ + + if (t->c_cflag & CSIZE) /* was it specified? */ + { + switch (t->c_cflag & CSIZE) + { + case CS8: sci_databits = SCI_8_DATA_BITS; break; + default : sci_databits = SCI_9_DATA_BITS; break; + } + } + else + { + sci_databits = SCI_8_DATA_BITS; /* default to 8 data bits */ + } + + /* the number of stop bits; always 1 for SCI */ + + if (t->c_cflag & CSTOPB) + { + /* do nothing */ + } + + /* setup the hardware with these serial port parameters */ + + SciSetBaud(sci_rate); /* set the baud rate */ + SciSetParity(sci_parity); /* set the parity type */ + SciSetDataBits(sci_databits); /* set the data bits */ + + return RTEMS_SUCCESSFUL; +} + + +/* + * + * SECTION 3 + * POLLING BASED ENTRY POINTS FOR THE TERMIOS MODULE + */ + +/**************************************************************************** +* Func: SciPolledOpen +* Desc: open routine for the polled i/o version of the driver +* called from rtems_termios_open which is called from console_open +* Inputs: major - device number +* minor - device number +* args - points to terminal info struct +* Outputs: success/fail +* Errors: none +* Scope: public - termios entry point +****************************************************************************/ + +int SciPolledOpen( + int major, + int minor, + void *arg +) +{ + rtems_libio_open_close_args_t * args = arg; + + if ( minor != SCI_MINOR ) /* check minor device num */ + { + return -1; + } + + if ( !args ) /* must have args */ + { + return -1; + } + + SciTermioTty = args->iop->data1; /* Store tty pointer */ + + SciDisableAllInterrupts(); /* don't generate interrupts */ + + /* THIS IS ACTUALLY A BAD THING - SETTING LINE PARAMETERS HERE */ + /* IT SHOULD BE DONE THROUGH TCSETATTR() WHEN THE CONSOLE IS OPENED!!! */ + +/* SciSetBaud(115200); set the baud rate */ +/* SciSetBaud( 57600); set the baud rate */ +/* SciSetBaud( 38400); set the baud rate */ +/* SciSetBaud( 19200); * set the baud rate */ + SciSetBaud( 9600); /* set the baud rate */ + + SciSetParity(SCI_PARITY_NONE); /* set no parity */ + + SciSetDataBits(SCI_8_DATA_BITS); /* set 8 data bits */ + + SciEnableTransmitter(); /* enable the xmitter */ + + SciEnableReceiver(); /* enable the rcvr */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciPolledClose +* Desc: close routine for the device driver, same for both +* Inputs: major - device number +* minor - device number +* args - unused +* Outputs: success/fail +* Errors: none +* Scope: public termios API +****************************************************************************/ + +int SciPolledClose( + int major, + int minor, + void *arg +) +{ + SciDisableAllInterrupts(); + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciPolledRead +* Desc: polling based read routine for the uart +* Inputs: minor - device number +* Outputs: error or the character read +* Errors: none +* Scope: public API +****************************************************************************/ + +int SciPolledRead( + int minor +) +{ + if ( minor != SCI_MINOR ) /* check the type-punned dev num */ + { + return -1; /* return error */ + } + + if ( SciCharAvailable() ) /* if a char is available */ + { + return SciReadCharNoWait(); /* read the rx data register */ + } + + return -1; /* return error */ +} + + +/**************************************************************************** +* Func: SciPolledWrite +* Desc: writes out characters in polled mode, waiting for the uart +* check in console_open, but we only seem to use interrupt mode +* Inputs: minor - device number +* buf - points to the data +* len - how many bytes +* Outputs: error or number of bytes written +* Errors: none +* Scope: public termios API +****************************************************************************/ + +ssize_t SciPolledWrite( + int minor, + const char *buf, + size_t len +) +{ + ssize_t written = 0; + + if ( minor != SCI_MINOR ) /* check minor device num */ + { + return -1; + } + + if ( SciOpened == DRIVER_OPENED ) /* is the driver api open? */ + { + return -1; /* toss the data */ + } + + /* send each byte in the string out the port */ + + while ( written < len ) + { + SciWriteCharWait(*buf++); /* send a byte */ + + written++; /* increment counter */ + } + + return written; /* return count */ +} + + +/* + * + * SECTION 4 + * DEVICE DRIVER PUBLIC API ENTRY POINTS + */ + +/**************************************************************************** +* Func: SciInit +* Desc: Initialize the lasers device driver and hardware +* Inputs: major - the major device number which is assigned by rtems +* minor - the minor device number which is undefined at this point +* arg - ????? +* Outputs: RTEMS_SUCCESSFUL +* Errors: None. +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciInitialize ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ +/* rtems_status_code status; */ + +/*printk("%s\r\n", __FUNCTION__); */ + + /* register the SCI device name for termios console i/o + * this is done over in console.c which doesn't seem exactly right + * but there were problems doing it here... + */ + +/* status = rtems_io_register_name( "/dev/sci", major, 0 ); */ + +/* if (status != RTEMS_SUCCESSFUL) */ +/* rtems_fatal_error_occurred(status); */ + + SciMajor = major; /* save the rtems major number */ + + SciOpened = DRIVER_CLOSED; /* initial state is closed */ + + /* if you have an interrupt handler, install it here */ + + SciInited = 1; /* set the inited flag */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciOpen +* Desc: device driver open routine +* you must open a device before you can anything else +* only one process can have the device opened at a time +* you could look at the task id to restrict access if you want +* Inputs: major - the major device number assigned by rtems +* minor - the minor device number assigned by us +* arg - ????? +* Outputs: see below +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciOpen ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ +/*printk("%s major=%d minor=%d\r\n", __FUNCTION__,major,minor); */ + + if (SciInited == 0) /* must be initialized first! */ + { + return RTEMS_NOT_CONFIGURED; + } + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* verify minor number */ + } + + if (SciOpened == DRIVER_OPENED) + { + return RTEMS_RESOURCE_IN_USE; /* already opened! */ + } + + SciOpened = DRIVER_OPENED; /* set the opened flag */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciClose +* Desc: device driver close routine +* the device must be opened before you can close it +* the device must be closed before someone (else) can open it +* Inputs: major - the major device number +* minor - the minor device number +* arg - ????? +* Outputs: see below +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciClose ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ +/*printk("%s major=%d minor=%d\r\n", __FUNCTION__,major,minor); */ + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* check the minor number */ + } + + if (SciOpened != DRIVER_OPENED) + { + return RTEMS_INCORRECT_STATE; /* must be opened first */ + } + + SciOpened = DRIVER_CLOSED; /* set the flag */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciRead +* Desc: device driver read routine +* this function is not meaningful for the laser devices +* Inputs: major - the major device number +* minor - the minor device number +* arg - read/write arguments +* Outputs: see below +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciRead ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args; /* ptr to argument struct */ + char *buffer; + + rw_args = (rtems_libio_rw_args_t *) arg; /* arguments to read() */ + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* check the minor number */ + } + + if (SciOpened == DRIVER_CLOSED) + { + return RTEMS_INCORRECT_STATE; /* must be opened first */ + } + + buffer = rw_args->buffer; /* points to user's buffer */ + +/* *buffer = SciReadCharWait(); wait for a character */ + + /* if there isn't a character available, wait until one shows up */ + /* or the timeout period expires, which ever happens first */ + + if ( SciRcvBufCount == 0 ) /* no chars */ + { + /* wait for someone to wake me up... */ + /*rtems_task_wake_after(SciReadTimeout); */ + } + + if ( SciRcvBufCount ) /* any characters locally? */ + { + *buffer = SciRcvBufGetChar(); /* get the character */ + + rw_args->bytes_moved = 1; /* how many we actually read */ + } + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciWrite +* Desc: device driver write routine +* this function is not meaningful for the laser devices +* Inputs: major - the major device number +* minor - the minor device number +* arg - read/write arguments +* Outputs: see below +* Errors: non3 +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciWrite ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args; /* ptr to argument struct */ + uint8_t *buffer; + size_t length; + + rw_args = (rtems_libio_rw_args_t *) arg; + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* check the minor number */ + } + + if (SciOpened == DRIVER_CLOSED) + { + return RTEMS_INCORRECT_STATE; /* must be opened first */ + } + + buffer = (uint8_t*)rw_args->buffer; /* points to data */ + + length = rw_args->count; /* how many bytes */ + + while (length--) + { + SciWriteCharWait(*buffer++); /* send the bytes out */ + } + + rw_args->bytes_moved = rw_args->count; /* how many we wrote */ + + return RTEMS_SUCCESSFUL; +} + + +/**************************************************************************** +* Func: SciControl +* Desc: device driver control routine +* see below for an example of how to use the ioctl interface +* Inputs: major - the major device number +* minor - the minor device number +* arg - io control args +* Outputs: see below +* Errors: none +* Scope: public API +****************************************************************************/ + +rtems_device_driver SciControl ( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_ioctl_args_t *args = arg; /* rtems arg struct */ + uint16_t command; /* the cmd to execute */ + +/*printk("%s major=%d minor=%d\r\n", __FUNCTION__,major,minor); */ + + /* do some sanity checking */ + + if (minor != SCI_MINOR) + { + return RTEMS_INVALID_NAME; /* check the minor number */ + } + + if (SciOpened == DRIVER_CLOSED) + { + return RTEMS_INCORRECT_STATE; /* must be open first */ + } + + if (args == 0) + { + return RTEMS_INVALID_ADDRESS; /* must have args */ + } + + args->ioctl_return = -1; /* assume an error */ + + command = args->command; /* get the command */ + + if (command == SCI_SEND_BREAK) /* process the command */ + { + SciSendBreak(); /* send break char */ + } + + args->ioctl_return = 0; /* return status */ + + return RTEMS_SUCCESSFUL; +} + + +/* + * + * SECTION 5 + * HARDWARE LEVEL ROUTINES + */ + +/**************************************************************************** +* Func: SciSetBaud +* Desc: setup the uart based on the termios modules requests +* Inputs: baud rate +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void SciSetBaud(uint32_t rate) +{ + uint16_t value; + uint16_t save_sccr1; + +/* when you open the console you need to set the termio struct baud rate */ +/* it has a default value of 9600, when someone calls tcsetattr it reverts! */ + + SciBaud = rate; /* save the rate */ + + /* calculate the register value as a float and convert to an int */ + /* set baud rate - you must define the system clock constant */ + /* see mrm332.h for an example */ + + value = ( (uint16_t) ( SYS_CLOCK / rate / 32.0 + 0.5 ) & 0x1fff ); + + save_sccr1 = *SCCR1; /* save register */ + + /* also turns off the xmtr and rcvr */ + + *SCCR1 &= SCI_DISABLE_INT_ALL; /* disable interrupts */ + + *SCCR0 = value; /* write the register */ + + *SCCR1 = save_sccr1; /* restore register */ + + return; +} + + +/**************************************************************************** +* Func: SciSetParity +* Desc: setup the uart based on the termios modules requests +* Inputs: parity +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void SciSetParity(uint16_t parity) +{ + uint16_t value; + + value = *SCCR1; /* get the register */ + + if (parity == SCI_PARITY_ODD) + { + value |= SCI_PARITY_ENABLE; /* parity enabled */ + value |= SCI_PARITY_ODD; /* parity odd */ + } + + else if (parity == SCI_PARITY_EVEN) + { + value |= SCI_PARITY_ENABLE; /* parity enabled */ + value &= ~SCI_PARITY_ODD; /* parity even */ + } + + else if (parity == SCI_PARITY_NONE) + { + value &= ~SCI_PARITY_ENABLE; /* disabled, most common */ + } + + /* else no changes */ + + *SCCR1 = value; /* write the register */ + + return; +} + + +/**************************************************************************** +* Func: SciSetDataBits +* Desc: setup the uart based on the termios modules requests +* Inputs: data bits +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void SciSetDataBits(uint16_t bits) +{ + uint16_t value; + + value = *SCCR1; /* get the register */ + + /* note - the parity setting affects the number of data bits */ + + if (bits == SCI_9_DATA_BITS) + { + value |= SCI_9_DATA_BITS; /* 9 data bits */ + } + + else if (bits == SCI_8_DATA_BITS) + { + value &= SCI_8_DATA_BITS; /* 8 data bits */ + } + + /* else no changes */ + + *SCCR1 = value; /* write the register */ + + return; +} + + +/**************************************************************************** +* Func: SciDisableAllInterrupts +* Func: SciEnableTransmitInterrupts +* Func: SciEnableReceiveInterrupts +* Desc: handles generation of interrupts by the sci module +* Inputs: none +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void inline SciDisableAllInterrupts( void ) +{ + /* this also turns off the xmtr and rcvr */ + + *SCCR1 &= SCI_DISABLE_INT_ALL; +} + +static void inline SciEnableReceiveInterrupts( void ) +{ + *SCCR1 |= SCI_ENABLE_INT_RX; +} + +static void inline SciDisableReceiveInterrupts( void ) +{ + *SCCR1 &= SCI_DISABLE_INT_RX; +} + +static void inline SciEnableTransmitInterrupts( void ) +{ + *SCCR1 |= SCI_ENABLE_INT_TX; +} + +static void inline SciDisableTransmitInterrupts( void ) +{ + *SCCR1 &= SCI_DISABLE_INT_TX; +} + + +/**************************************************************************** +* Func: SciEnableTransmitter, SciDisableTransmitter +* Func: SciEnableReceiver, SciDisableReceiver +* Desc: turns the transmitter and receiver on and off +* Inputs: none +* Outputs: none +* Errors: none +* Scope: private +****************************************************************************/ + +static void inline SciEnableTransmitter( void ) +{ + *SCCR1 |= SCI_ENABLE_XMTR; +} + +static void inline SciDisableTransmitter( void ) +{ + *SCCR1 &= SCI_DISABLE_XMTR; +} + +static void inline SciEnableReceiver( void ) +{ + *SCCR1 |= SCI_ENABLE_RCVR; +} + +static void inline SciDisableReceiver( void ) +{ + *SCCR1 &= SCI_DISABLE_RCVR; +} + + +/**************************************************************************** +* Func: SciWriteCharWait +* Desc: wait for room in the fifo and then put a char in +* Inputs: a byte to send +* Outputs: none +* Errors: none +* Scope: public +****************************************************************************/ + +void SciWriteCharWait(uint8_t c) +{ + /* poll the fifo, waiting for room for another character */ + + while ( ( *SCSR & SCI_XMTR_AVAILABLE ) != SCI_XMTR_AVAILABLE ) + { + /* Either we are writing to the fifo faster than + * the uart can clock bytes out onto the cable, + * or we are in flow control (actually no, we + * are ignoring flow control from the other end). + * In the first case, higher baud rates will help. + */ + /* relinquish processor while waiting */ + rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); + } + + *SCDR = c; /* send the charcter */ + + SciBytesOut++; /* increment the counter */ + + return; +} + +/**************************************************************************** +* Func: SciWriteCharNoWait +* Desc: if no room in the fifo throw the char on the floor +* Inputs: a byte to send +* Outputs: none +* Errors: none +* Scope: public +****************************************************************************/ + +void SciWriteCharNoWait(uint8_t c) +{ + if ( ( *SCSR & SCI_XMTR_AVAILABLE ) == 0 ) + { + return; /* no room, throw it away */ + } + + *SCDR = c; /* put the char in the fifo */ + + SciBytesOut++; /* increment the counter */ + + return; +} + + +/**************************************************************************** +* Func: SciReadCharWait +* Desc: read a character, waiting for one to show up, if need be +* Inputs: none +* Outputs: a character +* Errors: none +* Scope: public +****************************************************************************/ + +static uint8_t inline SciReadCharWait( void ) +{ + uint8_t ch; + + while ( SciCharAvailable() == 0 ) /* anything there? */ + { + /* relinquish processor while waiting */ + rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); + } + + /* if you have rcv ints enabled, then the isr will probably */ + /* get the character before you will unless you turn off ints */ + /* ie polling and ints don't mix that well */ + + ch = *SCDR; /* get the charcter */ + + SciBytesIn++; /* increment the counter */ + + return ch; /* return the char */ +} + +/**************************************************************************** +* Func: SciReadCharNoWait +* Desc: try to get a char but dont wait for one +* Inputs: none +* Outputs: a character or -1 if none +* Errors: none +* Scope: public +****************************************************************************/ + +static uint8_t inline SciReadCharNoWait( void ) +{ + uint8_t ch; + + if ( SciCharAvailable() == 0 ) /* anything there? */ + return -1; + + ch = *SCDR; /* get the character */ + + SciBytesIn++; /* increment the count */ + + return ch; /* return the char */ +} + + +/**************************************************************************** +* Func: SciCharAvailable +* Desc: is there a receive character in the data register +* Inputs: none +* Outputs: false if no char available, else true +* Errors: none +* Scope: public +****************************************************************************/ + +uint8_t inline SciCharAvailable( void ) +{ + return ( *SCSR & SCI_RCVR_READY ); /* char in data register? */ +} + + +/**************************************************************************** +* Func: SciSendBreak +* Desc: send 1 or tow breaks (all zero bits) +* Inputs: none +* Outputs: none +* Errors: none +* Scope: public +****************************************************************************/ + +void SciSendBreak( void ) +{ + /* From the Motorola QSM reference manual - */ + + /* "if SBK is toggled by writing it first to a one and then immediately */ + /* to a zero (in less than one serial frame interval), the transmitter */ + /* sends only one or two break frames before reverting to mark (idle) */ + /* or before commencing to send more data" */ + + *SCCR1 |= SCI_SEND_BREAK; /* set the bit */ + + *SCCR1 &= ~SCI_SEND_BREAK; /* clear the bit */ + + return; +} + + +/* + * + * SECTION 6 + * TEST CODE + */ + +/**************************************************************************** +* Func: SciUnitTest +* Desc: test the device driver +* Inputs: nothing +* Outputs: nothing +* Scope: public +****************************************************************************/ + +#if 0 +void SciUnitTest() +{ + uint8_t byte; /* a character */ + uint16_t fd; /* file descriptor for device */ + uint16_t result; /* result of ioctl */ + + fd = open("/dev/sci",O_RDWR); /* open the device */ + +printk("SCI open fd=%d\r\n",fd); + + result = write(fd, "abcd\r\n", 6); /* send a string */ + +printk("SCI write result=%d\r\n",result); + + result = read(fd, &byte, 1); /* read a byte */ + +printk("SCI read result=%d,byte=%x\r\n",result,byte); + + return; +} +#endif + + +/**************************************************************************** +* Func: SciPrintStats +* Desc: print out some driver information +* Inputs: nothing +* Outputs: nothing +* Scope: public +****************************************************************************/ + +void SciPrintStats ( void ) +{ + printk("\r\n"); + + printk( "SYS_CLOCK is %2.6f Mhz\r\n\n", SYS_CLOCK / 1000000.0 ); + + printk( "Current baud rate is %d bps or %d cps\r\n\n", SciBaud, SciBaud / 10 ); + + printk( "SCI Uart chars in %8" PRIu32 "\r\n", SciBytesIn ); + printk( "SCI Uart chars out %8" PRIu32 "\r\n", SciBytesOut ); + printk( "SCI Uart framing errors %8" PRIu32 "\r\n", SciErrorsFraming ); + printk( "SCI Uart parity errors %8" PRIu32 "\r\n", SciErrorsParity ); + printk( "SCI Uart overrun errors %8" PRIu32 "\r\n", SciErrorsOverrun ); + printk( "SCI Uart noise errors %8" PRIu32 "\r\n", SciErrorsNoise ); + + return; +} |