//=============================================================================
//
//      cycduart.c - Cyclone UART Diagnostics
//
//=============================================================================
//####COPYRIGHTBEGIN####
//                                                                          
// -------------------------------------------                              
// The contents of this file are subject to the Red Hat eCos Public License 
// Version 1.1 (the "License"); you may not use this file except in         
// compliance with the License.  You may obtain a copy of the License at    
// http://www.redhat.com/                                                   
//                                                                          
// Software distributed under the License is distributed on an "AS IS"      
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
// License for the specific language governing rights and limitations under 
// the License.                                                             
//                                                                          
// The Original Code is eCos - Embedded Configurable Operating System,      
// released September 30, 1998.                                             
//                                                                          
// The Initial Developer of the Original Code is Red Hat.                   
// Portions created by Red Hat are                                          
// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc.                             
// All Rights Reserved.                                                     
// -------------------------------------------                              
//                                                                          
//####COPYRIGHTEND####
//=============================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):   Scott Coulter, Jeff Frazier, Eric Breeden
// Contributors:
// Date:        2001-01-25
// Purpose:     
// Description: 
//
//####DESCRIPTIONEND####
//
//===========================================================================*/

#include "cycduart.h"
#include "iq80310.h"

#define DFLTLOOPERMS 500

extern int printf(char*,...);
extern long hexIn();

int break_flag = 0;
unsigned long baud_rate = 0;

static int duart_already_init = FALSE;
static unsigned int uart_unit = DFLTPORT;
static int looperms;

static int calc_looperms(void);
void serial_init(void);
int inreg(int);
void outreg(int, unsigned char);
void serial_set(unsigned long);
void serial_loopback(int);
int serial_getc();
void serial_putc(int);
int serial_write(int, const unsigned char *, int);
int serial_read(int, unsigned char *, int, int);

extern int enable_external_interrupt (int int_id);
extern int disable_external_interrupt (int int_id);

extern int isr_connect(int int_num, void (*handler)(int), int arg);
extern int isr_disconnect(int int_num);


void duart_initialize()
{

	if (duart_already_init == FALSE)
	{
		/* Calculate the time constant for timeouts on serial_read. */
		if ((looperms = calc_looperms()) <= 0)
			looperms = DFLTLOOPERMS;

	}

	/* Initialize the serial port and set the baud rate.
	 * The baud rate is set here for sanity only; the autobaud
	 * mechanism will change it as required when the host connects.
	 */

	serial_init();
	serial_set(baud_rate?baud_rate:9600L);  

	duart_already_init = TRUE;

}						


/* Establish the loop/time constant to be used in the timing loop in
 * serial_read.  This is done by putting the UART into loopback mode.
 * After transmitting a character at 300 baud, we wait for the character
 * to be received.  Then divide the number of loops waited by the number
 * of milliseconds it takes to transmit 10 bits at 300 baud.
 * If your transmitter doesn't have a loopback mode, this value can be
 * calculated using a timer or some other facility, or an approximate
 * constant can be used.
 */

#define TESTBAUD 300L
#define NBYTES	 10
#define BITS_PER_BYTE 10	/* 1 start bit, 8 data bits, 1 stop bit */
#define TOTAL_MS (NBYTES*BITS_PER_BYTE*1000/TESTBAUD)

static int
calc_looperms(void)
{
	int	i, count, c;
	int	totalloops = 0;

	serial_init();
	serial_set(TESTBAUD);		/* set 300 baud */
	serial_loopback(1);		/* enable loop back mode */

	for (i=0; i < NBYTES; i++)
	{
   		count = 1;
		serial_putc(0xaa);	/* xmit character */

		/*
		 * The timing loop is the same as the loops in serial_read.
		 * Any changes to the loops in serial_read should be reflected
		 * here.
		 */
		do  
		{
			c = serial_getc();
		} while (c < 0 && count++ > 0);

		totalloops += count;
	}

	serial_loopback(0);

	return(totalloops/TOTAL_MS);
}

/*
 * Initialize the device driver.
 */
void serial_init(void)
{
	/* If the serial port has been init'd before, there may be data in it  */
	/* Wait for the transmit FIFO to empty out before resetting anything   */  
	if (duart_already_init == TRUE)
	{
		while (!(inreg(LSR) & LSR_TSRE));
	}

	/*
	* Configure active port, (uart_unit already set.)
	*
	* Set 8 bits, 1 stop bit, no parity.
	*
	* LCR<7>       0       divisor latch access bit
	* LCR<6>       0       break control (1=send break)
	* LCR<5>       0       stick parity (0=space, 1=mark)
	* LCR<4>       0       parity even (0=odd, 1=even)
	* LCR<3>       0       parity enable (1=enabled)
	* LCR<2>       0       # stop bits (0=1, 1=1.5)
	* LCR<1:0>     11      bits per character(00=5, 01=6, 10=7, 11=8)
	*/

	outreg(LCR, 0x3);
   
	/* Assert DTR and RTS to prevent hardware handshake problems with
       serial terminals, etc. which can be connected to the serial port */
    outreg(MCR, MCR_DTR | MCR_RTS);

	outreg(FCR, FIFO_ENABLE);   /* Enable the FIFO                 */
	outreg(IER, INT_ENABLE);    /* Enable appropriate interrupts   */

}

/* Read a received character if one is available.  Return -1 otherwise. */
int serial_getc()
{
    if (inreg(LSR) & LSR_DR)
    {
         return inreg(DataIn);
    }
    return -1;
}

/* Transmit a character. */
void serial_putc(int c)
{
    while ((inreg(LSR) & LSR_THRE) == 0)
        ;
    outreg(DataOut, c);
}

/*
 * Set the baud rate.
 */
void serial_set(unsigned long baud)
{
        unsigned char sav_lcr;

        if(baud == 0)
           baud = 9600L;

        /*
         * Enable access to the divisor latches by setting DLAB in LCR.
         *
         */
         sav_lcr = inreg(LCR);
         outreg(LCR, LCR_DLAB | sav_lcr);

        /*
         * Set divisor latches.
         */
        outreg(BaudLsb, XTAL/(16*baud));
        outreg(BaudMsb, (XTAL/(16*baud)) >> 8);

        /*
         * Restore line control register
         */
        outreg(LCR, sav_lcr);
}

/*
 * This routine is used by calc_looperms to put the UART in loopback mode.
 */

void serial_loopback(int flag)
{
    if (flag)
		outreg(MCR, inreg(MCR) | MCR_LOOP);		/* enable loop back mode */
    else
		outreg(MCR, inreg(MCR) & ~MCR_LOOP);	/* disable loop back mode */
}

/*
 * These routines are used to read and write to the registers of the
 * 16552.  The delay routine guarantees the required recovery time between
 * cycles to the 16552.
 * DUART is the base address of the 16552.
 * DUART_DELTA gives the spacing between adjacent registers of the 16552.
 * For example, if A0,A1,A2 of the 16552 are connected to A2,A3,A4 of
 * the processor, DUART_DELTA must be 4.
 */

int inreg(int reg)
{
    int val;
    val = *((volatile unsigned char *)TERMINAL + (uart_unit * SCALE + reg));

    return val;
}

void outreg(int reg, unsigned char val)
{
    *((volatile unsigned char *)TERMINAL + (uart_unit * SCALE + reg)) = val;
}



/****************************************************************/
/* The following functions are all part of the Breeze UART test */
/****************************************************************/
  

static volatile int uart_int;


/************************************************/
/* BUS_TEST										*/
/* This routine performs a walking ones test	*/
/* on the given uart chip to test it's bus		*/
/* interface.  It writes to the scratchpad reg.	*/
/* then reads it back.  During					*/
/* this test all 8 data lines from the chip		*/
/* get written with both 1 and 0.				*/
/************************************************/
static int bus_test ()
{
    unsigned char	out, in;
    int			bitpos;
    volatile int 	junk;

    junk = (int) &junk;	/* Don't let compiler optimize or "registerize" */

    outreg(SCR,0);		/* Clear scratchpad register */

    for (bitpos = 0; bitpos < 8; bitpos++)
    {
		out = 1 << bitpos;

		outreg(SCR,out);	/* Write data to scratchpad reg. */

		junk = ~0;			/* Force data lines high */

		in = inreg(SCR);	/* Read data */

		printf ("%02X ", in);

		/* make sure it's what we wrote */
		if (in != out)
			return (0);
    }
    outreg(SCR,0);	/* Clear scratchpad register */
    printf ("\n");

    return (1);
}

/************************************************/
/* DISABLE_UART_INTS							*/
/* This routine disables uart interrupts		*/
/************************************************/
static void disable_uart_ints ()
{
    outreg(IER,0);		/* Make the uart shut up */
}

/************************************************/
/* UART_ISR										*/
/* This routine responds to uart interrupts		*/
/* must return 1 to indicate that an interrupt  */
/* was serviced.								*/
/************************************************/
static void uart_isr (int unused)
{
	unsigned char iir;

    disable_uart_ints ();
    uart_int = 1;

	/* read the IIR to clear the interrupt */
	iir = inreg(IIR);

    return ;
}

/************************************************/
/* INIT_UART									*/
/* This routine initializes the 16550 interrupt */
/* and uart registers and initializes the uart  */
/* count.										*/
/************************************************/
static void init_uart ()
{
      outreg(IER,0x02);		/* Enable Tx Empty interrupt -
					   	       should generate an interrupt since Tx is
				               empty to begin with */
}


/****************************************/
/* UART DIAGNOSTIC TEST					*/
/****************************************/
void uart_test ()
{
volatile int loop;
int	looplim;
int int_id;
unsigned long* reg_ptr;
int i, baud;

/*11/01/00 */
char info[] = {"Move Console Cable back to Connector J9 and hit <CR> to exit test"};
int index;

    looplim = 400000;

	/* perform tests on both UARTs */
	for (uart_unit = 0; uart_unit < 2; uart_unit++)
	{

		if (uart_unit == 0)
			int_id = UART1_INT_ID;
		else
			int_id = UART2_INT_ID;
				
		if (!bus_test ())
			printf ("\nERROR:  bus_test for UART Unit %d failed\n", uart_unit);
		else
		{
			printf ("\nbus_test for UART Unit %d passed\n", uart_unit);
		
			uart_int = 0;   

			isr_connect (int_id, uart_isr, 0);

			if (enable_external_interrupt(int_id) != OK)
				printf("ERROR enabling UART UINT %d interrupt!\n", uart_unit);

			init_uart ();
		
			loop = 0;

			while (!uart_int && (loop < looplim))
				loop++;
			if (!uart_int)
				printf ("UART Unit %d INTERRUPT test failed %X\n", uart_unit, loop) ;
			else
				printf ("UART Unit %d INTERRUPT test passed\n", uart_unit);

			serial_putc(' ');
		}

		/* disable UART interrupt */
		if (disable_external_interrupt(int_id)!= OK)
			printf("ERROR disabling UART UNIT %d interrupt!\n", uart_unit);

		/* disconnect test handler */
		isr_disconnect (int_id);

	}

/* 11/01/00 */
/* #if 0 */	 /* writing to port 2 doesnt work yet... */
#if 1 /* writing to port 2 doesnt work yet... */

/*
	printf ("\nMove the Console Cable to the 2nd Serial Port,\n");
	printf ("Connector J10,\n");
    printf ("and Hit <CR> when the cable is connected.\n\n");
	printf ("After alphabet prints, move Console Cable back to 1st Serial Port,\n");
	printf ("Connector J9,\n");
	printf ("and hit <CR> to exit test\n");
*/  

/* 10/30/00 */
	uart_unit = DFLTPORT;	/* test J10, the PCI-700 GDB port */

	printf ("\nMove the Console Cable to the 2nd Serial Port, Connector J10,\n");
    printf ("and Hit <CR> when the cable is connected.\n");
	printf ("The alphabet should print on the screen.\n\n");

/* 11/01/00 */
/*
	printf ("After alphabet prints, move Console Cable back to 1st Serial Port,\n");
	printf ("Connector J9,\n");
	printf ("and hit <CR> to exit test\n");
*/
	baud = 115200;
	serial_init();
    serial_set(baud?baud:115200L);

/*	while (serial_getc() == -1); */
	while (serial_getc() != 0x0d);	/* wait for a carriage return character to start test */

/*
	while (1)
	{
		for ( i = 65; i <= 90; i++ )
			serial_putc(i);
	}
*/
	for ( i = 65; i <= 90; i++ )	/* transmit the alphabet */
		serial_putc(i);

	serial_putc(10);	/* transmit a New Line */
	serial_putc(13);	/* transmit a Carriage Return */
	serial_putc(10);	/* transmit a New Line */

	for (index=0; info[index] != '\0'; index++)		/* transmit some instructions to the user */
	{
		serial_putc(info[index]);
	}


	/* point at default port before returning */
/*	uart_unit = DFLTPORT; */

	(void)hexIn();
  
#endif
	
	printf ("\n\nUART tests done.\n");

    printf ("Press return to continue.\n");
    (void) hexIn();
}




