
#include <common.h>
#include <asm/arch/regaccess.h>
#include <stb225_gpio.h>

extern int stb225_secure_boot (void);

static int baudrate_reg (int br)
{
	if (br == 115200)
		return 1;
	if (br == 57600)
		return 3;
	if (br == 38400)
		return 5;
	if (br == 19200)
		return 11;

	/* default is 9600 */
	return 23;
}

static int usec_per_data_byte (void)
{
	DECLARE_GLOBAL_DATA_PTR;

	unsigned int baudrate = gd->baudrate;
	unsigned int bits_per_data_byte = 10;	/* 8 data + 1 start + 1 stop == 10 bits total */

	return ((1000000 * bits_per_data_byte) + (baudrate - 1)) / baudrate;
}

/*
   Warning: serial_init() is called before relocation.
*/
int serial_init (void)
{
	serial_setbrg();
	return 0;
}

void serial_setbrg (void)
{
	DECLARE_GLOBAL_DATA_PTR;

	/* Set up the port mode - selected baudrate, 8N1 */
	PNX833X_UART_LCR_FCR = PNX833X_UART_LCR_FCR_BIT_8;
	PNX833X_UART_BAUDRATE = baudrate_reg (gd->baudrate);

	/* Clear and disable all interrupts, and reset FIFOs */
	PNX833X_UART_INT_ENABLE = 0;
	PNX833X_UART_INT_CLEAR = 0x3FF;
	PNX833X_UART_LCR_FCR |= (PNX833X_UART_LCR_FCR_TX_FIFO_RST | PNX833X_UART_LCR_FCR_RX_FIFO_RST);
}

int serial_enabled = 0;
int serial_gpio_read = 0;
int IsSerialOutputEnabled(void)
{
	DECLARE_GLOBAL_DATA_PTR;

	if(gd->flags & GD_FLG_ENV)
	{
		if(!serial_gpio_read)
		{
			char tmp[64];
			int i = getenv_r ("loglevel", tmp, sizeof (tmp));
			if(i > 0)
			{
				// If there is an environment variable called loglevel, then enable serial
				serial_enabled = 1;
			}			
			else
			{
				serial_enabled = (gpio_get_value(GPIO_23) == 0);
				
				// Loglevel hasn't been over-ridden, so make up the value based on whether 
				// the switch is being pressed.
				if(serial_enabled)
					setenv("loglevel", "9");
				else
					setenv("loglevel", "4");
			}
			serial_gpio_read = 1;
		}
		return serial_enabled;
	}
	else
	{
		return (gpio_get_value(GPIO_23) == 0);
	}
}

void serial_putc (const char c)
{
	if(!IsSerialOutputEnabled())
		return;
		
	if (c == '\n')
		serial_putc ('\r');

	/*
	   UART HW is buggy. Tx fifo level is decremented as soon as
	   transmission of a data byte has started, not when it's space in the
	   Tx fifo has been freed. It's not safe to write to the Tx fifo until
	   the fifo level reports at least 2 spaces free.
	*/
	while (PNX833X_REGFIELD(UART_RBR_THR, TXFIFO_STA) > 14);

	/* write the character */
	PNX833X_UART_RBR_THR = ((unsigned long) c & 0xFF);
}

void serial_puthex32 (unsigned int v)
{
	if(!IsSerialOutputEnabled())
		return;
		
	int i;

	for (i = 0; i < 8; i++) {
		int ch = (v >> 28);
		ch += (ch < 10) ? '0' : 'A' - 10;
		serial_putc (ch);
		v <<= 4;
	}
	serial_putc ('\n');
}

void serial_puts (const char *s)
{
	while (*s)
		serial_putc (*s++);
}

int serial_tstc (void)
{
	if (stb225_secure_boot())	/* ignore all serial input if secure boot is enabled */
		return 0;

	if(!IsSerialOutputEnabled())
		return 0;

	/* check if there is data in rx fifo */
	return (PNX833X_REGFIELD(UART_RBR_THR, RXFIFO_STA) > 0);
}

int serial_getc (void)
{
	int result;
	unsigned int byte_usec = usec_per_data_byte();
	unsigned long timeout, timeout_start;
	unsigned long hz = get_tbclk();

	if (hz > 1000000)
		timeout = ((hz + 999999) / 1000000) * byte_usec;
	else
		timeout = ((hz * byte_usec) + 999999) / 1000000;

	/* block while waiting for data... */
	while (!serial_tstc());

	/*
	   UART HW is buggy. Rx fifo level is incremented as soon as reception
	   of a data byte has started. It's not safe to read from the Rx fifo
	   until either fifo level reports at least 2, or fifo level has
	   reported 1 for at least the time taken to receive a complete byte
	   at the current baud rate...
	*/
	timeout_start = get_timer (0);

	while (1) {
		if (PNX833X_REGFIELD(UART_RBR_THR, RXFIFO_STA) >= 2)
			break;
		if (get_timer (timeout_start) > timeout)
			break;
	}

	/* get character */
	result = PNX833X_UART_RBR_THR & 0xFF;

	/* advance rx fifo pointer */
	PNX833X_UART_LCR_FCR |= PNX833X_UART_LCR_FCR_RX_FIFO_PT_ADV;

	return result;
}

