#include <common.h>

/*
   Legacy PNX8330 serial init was written to access GPIO
   control regs directly... allow it to continue to do so.
*/
#define PNX8330_HACK__ALLOW_DIRECT_GPIO_REGISTER_ACCESS

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

DECLARE_GLOBAL_DATA_PTR;

extern int stb225_secure_boot (void);

static inline 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 inline int byteusecs (void)
{
	return 4 * (PNX833X_UART_BAUDRATE + 1);
}

/*
void serial_put_hex(unsigned int value)
{
	static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	int i;
	for(i = 0; i < 8; ++i)
	{
		serial_putc(hex[(value >> 28) & 0xf]);
		value <<= 4;
	}
}
*/
int serial_init (void)
{
	unsigned long val;

	if (CPU_IS_PNX8330)
	{
		/* Ensure UART is not in reset */
		val = PNX833X_RESET_CONTROL;
		if (!(val & PNX833X_RESET_CONTROL_UART_RESET_CTRL))
			PNX833X_RESET_CONTROL = val | PNX833X_RESET_CONTROL_UART_RESET_CTRL;

		/* Ensure UART pins are UART not GPIO */
		PNX833X_PIO_SEL = PNX833X_PIO_SEL | 0x000000F0ul;
		PNX833X_PIO_DIR = (PNX833X_PIO_DIR | 0x00000090ul) & 0xFFFFFF9Ful;
	}
	/* else... not needed for PNX8335, its UART0 pins are enabled by default */

	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)
{
	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;
		
	/* want pretty newlines :) */
	if (c == '\n')
	       	serial_putc ('\r');

	/* wait for tx fifo to have at least 2 free bytes (one slot is not
	   reliable against overruns than waiting for a single slot) */
	while (PNX833X_REGFIELD(UART_RBR_THR, TXFIFO_STA) > 14);

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

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 res;

	/* wait for data */
	while (!serial_tstc());

	/* looks if there is only one byte, a bit of waiting makes things
	   more reliable */
	if (PNX833X_REGFIELD(UART_RBR_THR, RXFIFO_STA) == 1)
		udelay (byteusecs());

	/* get a character */
	res = PNX833X_UART_RBR_THR & 0xFF;

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

	return res;
}

