/* File name: UART.c
   File name: UART2support.c

   Buffered interrupt support for DSP Global UART board 
   on the TI C5510 DSK.
   
   UART channel 2 only.

    28Mar2004 .. initial version .. KM
    29Mar2004 .. UART 2 int sup evolved from test code .. KM
    24Nov2005 .. renamed fns, improved buffering, added hardware
              .. threshold-based flow control .. eric williams
    20Feb2006 .. documentation .. eric williams
   
*/

/* THRESHOLD_OFF determines when to tell the sender to
   stop sending (at this many spots left in buffer ) */
#define THRESHOLD_OFF 200
/* THRESHOLD_ON determines when to tell the sender to
   start sending (at this many spots left in buffer ) */
#define THRESHOLD_ON  400


/* UART.c prototypes */
/* void uartSetup(short *inbuf, int incount, short *oubuf, int outcount, int freqdiv);
 * short uartRecvCount();
 * void uartRecv(short *buffer, short count, short stride);
 * short uartSendCount();
 * void uartSend(short *buffer, short count, short stride);
 */



#define FOREVER 1

#define UART 0x500200
#define RBR (UART+0x00)
#define THR (UART+0x00)
#define DLL (UART+0x00)
#define DLM (UART+0x02)
#define IER (UART+0x02)
#define ISR (UART+0x04)
#define FCR (UART+0x04)
#define LCR (UART+0x06)
#define MCR (UART+0x08)
#define LSR (UART+0x0A)
#define MSR (UART+0x0C)
#define SPR (UART+0x0E)


#define IER0 ((unsigned long)0x00)
#define IFR0 ((unsigned long)0x01)
#define IVPD ((unsigned long)0x49)
#define IVPH ((unsigned long)0x4A)

#define INT0 0x0008
#define INT0_BIT 0x0004

interrupt void UART2int(void);
void resetv();

volatile int *U2RxAdr, U2RxSize, U2RxAOff, U2RxIOff, U2RxCount;
volatile int *U2TxAdr, U2TxSize, U2TxAOff, U2TxIOff, U2TxCount, U2TxStopped;

#define far_poke FarPoke
#define far_peek FarPeek

//----------------------------------------------------------------


/* Function to set up the UART channel buffered Rx and Tx support.

   Call prior to globally enabling the interrupt system.
   
   The arguments are:
   
   *intbuf    A pointer to the receive (Rx) buffer.
   nin        The number of ints in the Rx buffer.
   *outbuf    A pointer to the transmit (Tx) buffer.
   nout       The number of ints in the Tx buffer.
   RateDiv    The rate divisor value baud rate is
              230,400/RateDiv.  For 38400 baud use a
              value of 6.
*/

void uartSetup(int *inbuf, int nin, int *outbuf, int nout, int RateDiv)
{
    unsigned long resetloc;
    
    // set up buffering support
    
    U2RxAdr = inbuf;    // address of Rx buffer
    U2RxSize = nin;     // size of Rx buffer
    U2RxAOff = 0;       // application level Rx address offset
    U2RxIOff = 0;       // interrupt level Rx address offset
    U2RxCount = 0;      // count of characters in Rx buffer
    U2TxAdr = outbuf;   // address of Tx buffer
    U2TxSize = nout;    // size of Tx buffer
    U2TxAOff = 0;       // application level TX address offset
    U2TxIOff = 0;       // interrupt level Tx address offset
    U2TxCount = 0;      // count of characters in Tx buffer
    U2TxStopped = 1;    // Tx waiting for a character to be sent
    
    // set up interrupt vector and interrupt registers
    
    resetloc = (long)resetv;
    far_poke(IVPD, (unsigned)(resetloc>>8));
    far_poke(IVPH, (unsigned)(resetloc>>8));
    far_poke((resetloc>>1)+INT0, (unsigned)((unsigned long)UART2int>>16));
    far_poke((resetloc>>1)+INT0+1, (unsigned)((unsigned long)UART2int));
    far_poke(IER0, far_peek(IER0)|INT0_BIT);
    far_poke(IFR0, INT0_BIT);
    
    while (!(far_peek(ISR) & 0x1))
        ;
    
#if 1
    // configure the UART channel 2
    
    far_poke(LCR, 0x80); 		// access baud rate registers
    far_poke(DLM, RateDiv>>8);	// set baud rate divisor high byte
    far_poke(DLL, RateDiv);		// set baud rate divison low byte

    far_poke(LCR, 0x03);		// use 8 data and 1 stop bit
    
    far_poke(FCR, 0x0F);		// ensure FIFOs are on
    far_poke(LSR, 0x60);		// initialize line status register
    far_poke(IER, 0x0F);		// have UART generate Rx and Tx interrupts
    far_poke(MCR, 0x0B);		// make UART int req outputs active, set flow control
    far_peek(RBR);				// clear receiver buffer
#else
    // configure the UART channel 2
    
    far_poke(LCR, 0x80); // access baud rate registers
    far_poke(DLM, RateDiv>>8); // set baud rate divisor high byte
    far_poke(DLL, RateDiv);    // set baud rate divison low byte
    far_poke(LCR, 0x07); // use 8 data and 2 stop bits
    
    far_poke(FCR, 0x00); // insure FIFOs are off
    far_poke(LSR, 0x60); // initialize line status register
    far_poke(IER, 0x03); // have UART generate Rx and Tx interrupts
    far_poke(MCR, 0x08); // make UART int req outputs active
    far_peek(RBR);       // clear receiver buffer
#endif    

    
    return;
}

// 

int uartRecvCount()
{
return U2RxCount;
}

// Function to fetch characters from the Rx buffer

void uartRecv(short *buf, short count, short stride)
{
    int i;
    short *ptr = buf;
    
    while (U2RxCount < count);    // wait if not enough characters present
   
    for (i = 0; i < count; i++)
        {
        *ptr = *(U2RxAdr+U2RxAOff);              // fetch character
        if (++U2RxAOff >= U2RxSize) U2RxAOff = 0; // adv pointer cyclicly
		ptr += stride;
        }
        
    _disable_interrupts();     // enter a critical section
    U2RxCount -= count;        // reduce the number present
    _enable_interrupts();      // exit the critical section
    
	if (U2RxCount < U2RxSize-THRESHOLD_ON)
   		far_poke(MCR, far_peek(MCR)|0x02);
}


short uartSendCount()
{
return (U2TxSize - U2TxCount);
}


// Function to put characters into the Tx buffer

void uartSend(short *buf, short count, short stride)
{
    int i;
    short *ptr = buf;

    while ((U2TxSize-U2TxCount) < count)
    	i;  // wait if no room in the buffer
    
    _disable_interrupts();          // enter a critical section
    
    for (i = 0; i < count; i++)
        {
        if (U2TxStopped != 0) {                  // if Tx not running
            far_poke(THR, *ptr);                // load character directly
            far_poke(IER, far_peek(IER)|0x0002); // reenable interrupt
            U2TxStopped = 0;                     // and note that is expected
            }
       else {                                   // if Tx is running
           *(U2TxAdr+U2TxAOff) = *ptr;         // put character into buffer
            if (++U2TxAOff >= U2TxSize) U2TxAOff = 0; // adv pointer cyclicly
            U2TxCount++;                              // and increase the count
           }
       ptr += stride;
       }
    
    _enable_interrupts();          // exit the critical section
    
    return;
}
    
//-----------------------------------------------------------------

// Interrupt handler for UART channel 2

int volatile U2Flag;

interrupt void UART2int(void)
{
	while (1)
	{
	U2Flag = far_peek(ISR);          // get the interrupt status value
	if ((U2Flag&0x0C) != 0) {        // true if Rx interrupt
	while (far_peek(LSR) & 0x1)
	{
       if (U2RxCount < U2RxSize-THRESHOLD_OFF) {
           *(U2RxAdr+U2RxIOff) = far_peek(RBR);	     // put character in Rx buffer
           if (++U2RxIOff >= U2RxSize) U2RxIOff = 0; // adv pointer cyclicly
           U2RxCount++;                              // count the character
           }
       else {                                        // if past threshold in the Rx buffer we
           far_poke(MCR, far_peek(MCR)&(~0x02));     // set flow control
           *(U2RxAdr+U2RxIOff) = far_peek(RBR);      // put character in Rx buffer
           if (++U2RxIOff >= U2RxSize) U2RxIOff = 0; // adv pointer cyclicly
           U2RxCount++;                              // count the character
           }
       }
   } 
   else if ((U2Flag&0x02)!=0) {     // true if Tx interrupt
       if (U2TxCount == 0) {        // true if no characters in Tx buffer
           U2TxStopped = 1;         // so note that no future interrupt
           ///////far_poke(IER, far_peek(IER)&0xFFFD); // and disable Tx interrupt requests
       }
       else {                       // otherwise have a character to send
       if (!U2TxStopped)
               {
               far_poke(THR, *(U2TxAdr+U2TxIOff));       // so put into the Tx buffer
               if (++U2TxIOff >= U2TxSize) U2TxIOff = 0; // adv pointer cyclicly
               U2TxCount--;                              // reduce count present
               }
       }
   }
   else if (U2Flag&0x1)
   {
	   break;
   }
   else if (!(U2Flag&0x3F))
   {		/* MSR change */
	   U2Flag = far_peek(MSR);
   }
   else
   {
	   short i = 0;  // we don't expect to get here
   }
   }

   return;
}
