//******************************************************************************
//*	File		: unzip.cpp
//*	Company		: VTech	Info-Tech Ltd.
//*	By			: Kam Ching
//*	Revision	: 1.0
//*	Description	: Decompress system from ROM to RAM
//* NOTE:		  "minilzo.c" file must be compiled with "-O1" optimization or
//*				  below. If "-O2" (or higher) is used, GNU may over optimize
//*				  the code and generate some wrong code. This will make the
//*				  "lzo_init ()" return error (more accuratly, it error on
//*				  "schedule_insns_bug()")
//*	............................................................................
//*	Credit		: The compression and decompression functions are devised from
//*				  LZO's "minilzo". Please refer to file "minilzo.c" for details.
//*	----------------------------------------------------------------------------
//*	History:
//*	Jul.27,2000	Kam		- Initial design
//******************************************************************************

// Include files ---------------------------------------------------------------
#include "pr31700c.h"
#include "pr31700s.h"
#include "datatype.h"
#include "minilzo.h"

// Configuration define --------------------------------------------------------
//#define	PRINTF_DEBUG				// define to enable printf debug

// Constant defines ------------------------------------------------------------

// self-designed LZO file header
#define	LZO_NAME_SZ			4		// 4 byte lzo recgnization char (include NULL)
typedef struct _LZO_HDR {
	UBYTE	ZipName[LZO_NAME_SZ];	//
	UWORD	UnzipSz;				// uncompressed buffer size in byte
	UWORD	ZipSz;					// compressed buffer size in byte
} LZO_HDR;
#define	LZO_HDR_SZ			sizeof(LZO_HDR)		// store compressed buffer size (4 bytes) in the head of compressed file
#define	LZO_ZIPNAME			"LZO"				// file header name to recognize if the following buffer is LZO compressed format

// External variables ----------------------------------------------------------
extern eatext;
extern _fadata;
extern eadata;
extern _fbtext;

// Macro -----------------------------------------------------------------------
//#define	PIN_DEBUG		(1 << 13)		// define this to enable pin debug

#ifdef	PIN_DEBUG	////////////////////////////////////////////////////////////

#define	DebugPinInit()	\
	CPU->REG_MFIO_SEL |= PIN_DEBUG;	\
	CPU->REG_MFIO_DATA_OUT &= ~(PIN_DEBUG);	\
CPU->REG_MFIO_DIRECTN |= PIN_DEBUG
#define	DebugSetPin()		CPU->REG_MFIO_DATA_OUT |= PIN_DEBUG
#define	DebugClearPin()		CPU->REG_MFIO_DATA_OUT &= ~PIN_DEBUG
#define	DebugTogglePin()	CPU->REG_MFIO_DATA_OUT ^= PIN_DEBUG

#else	////////////////////////////////////////////////////////////////////////

#define	DebugPinInit()
#define	DebugSetPin()
#define	DebugClearPin()
#define	DebugTogglePin()

#endif	PIN_DEBUG	////////////////////////////////////////////////////////////

// UART printf prototype -------------------------------------------------------
#ifdef	PRINTF_DEBUG	////////////////////////////////////////////////////////

static void UartIOInit (void);
static void UartDrvInit (void);
static void UartDrvSleep (void);
static void UartDrvWakeup (void);
static void UartDrvSetComm (WORD Baud, WORD Bits, WORD Parity, WORD Stops);
void BootPrintf (BYTE *pBuffer);
void BootPutc (UBYTE Char);
void BootPrintw (UWORD Word);

#else	////////////////////////////////////////////////////////////////////////

#define	UartDrvInit()
#define	BootPrintf
#define	BootPutc
#define	BootPrintw

#endif	PRINTF_DEBUG	////////////////////////////////////////////////////////

//******************************************************************************
// unzip functions
//******************************************************************************

//==============================================================================
#define	PrintLzoInfo()	\
{	\
	UWORD *p_addr;	\
	\
	BootPrintf ("p_lzo_hdr=");	\
	BootPrintw ((UWORD)p_lzo_hdr);	\
	BootPrintf ("p_in_buff=");	\
	BootPrintw ((UWORD)p_in_buff);	\
	BootPrintf ("in_len=");	\
	BootPrintw ((UWORD)in_len);	\
	BootPrintf ("out_len=");	\
	BootPrintw ((UWORD)out_len);	\
	BootPrintf ("p_out_buff=");	\
	BootPrintw ((UWORD)p_out_buff);	\
	BootPutc ('\n');	\
	\
	p_addr = (UWORD *)p_in_buff;	\
	BootPrintw ((UWORD)p_addr);	\
	BootPutc (':');	\
	BootPrintw (*p_addr++);	\
	BootPrintw (*p_addr++);	\
	BootPrintw (*p_addr++);	\
	BootPrintw (*p_addr++);	\
	BootPutc ('\n');	\
}

void DecompressSys (void)
{
	UBYTE *p_in_buff, *p_out_buff;
	UWORD in_len, out_len, rtn;
	LZO_HDR *p_lzo_hdr;
	
	UartDrvInit ();
	if (lzo_init () != LZO_E_OK)
		while (1)
			;
		
		p_lzo_hdr = (LZO_HDR*)((UWORD)&eatext + ((UWORD)&eadata - (UWORD)&_fadata));
		p_in_buff = (UBYTE*)((UWORD)p_lzo_hdr + LZO_HDR_SZ);
		in_len = p_lzo_hdr->ZipSz;
		out_len = p_lzo_hdr->UnzipSz;
		p_out_buff = (UBYTE*)&_fbtext;
		PrintLzoInfo ();
		rtn = lzo1x_decompress_safe (p_in_buff, in_len, p_out_buff, &out_len, NULL);
		BootPrintf ("return = ");
		BootPrintw (rtn);
		BootPrintw (out_len);
		BootPutc ('\n');
		if (rtn != LZO_E_OK)
			while (1)
				;
}

//******************************************************************************
// uart function for debug purpose
//******************************************************************************

#ifdef	PRINTF_DEBUG	////////////////////////////////////////////////////////

#include "pr3910.h"
#include "pr31700s.h"
#include "pr31700c.h"
#include "dev_pwr.h"

// Constant	defines ------------------------------------------------------------
#define	UART_PARITY_N	0				// no parity bit
#define	UART_PARITY_E	1				// even parity bit
#define	UART_PARITY_O	-1				// odd parity bit

#define DEFAULT_BPS		115200					// choice of 11520, 57600, 38400, 19200, 9600, etc
#define	DEFAULT_DATABIT	8						// choice of 8 or 7
#define	DEFAULT_STOPBIT	1						// choice of 1 or 2
#define	DEFAULT_PARITY	UART_PARITY_N			// choice of UART_PARITY_N, _E or _O

// Macro -----------------------------------------------------------------------
#define BAUD(a)         (230400/a-1)

#define	CFG_RFMASK				((1 << 10) | (1 << 11))
#define	CP0_ReadConfigReg()		\
	({	\
	register UWORD rtn;	\
	__asm__ volatile ("mfc0 %0, $3" : "=r"(rtn));	\
	rtn;	\
})
#define	CP0_WriteConfigReg(word)	\
__asm__ volatile ("mtc0 %0, $3" : : "r"((UWORD)(word)));
#define	CPUSetSpeed(speed)		CP0_WriteConfigReg ((CP0_ReadConfigReg () & ~CFG_RFMASK) | speed)
#define	CPUGetSpeed()			(CP0_ReadConfigReg () & CFG_RFMASK)
#define	CPUSpeedMin()			CPUSetSpeed(3)

//==============================================================================
//=Prototype:	void UartIOInit (void);
//=Purpose:		Initialize UART communication ports IO
//=Scope:		UART Comm port driver
//=Param:		none
//=Return:		none
//==============================================================================
static void UartIOInit (void)
{
	CPU->REG_MFIO_SEL |= UART_TX_ENABLE | UART_RX_DISABLE;		// set as IO pins
	CPU->REG_MFIO_DATA_OUT |= UART_TX_ENABLE;					// power on UART driver
	CPU->REG_MFIO_DATA_OUT &= ~(UART_RX_DISABLE);				// power off UART driver
	CPU->REG_MFIO_DIRECTN |= UART_TX_ENABLE | UART_RX_DISABLE;// set pins as output
	CPU->REG_IO_POWER_DOWN |= UART_TX_ENABLE | UART_RX_DISABLE;	// power down pins
}

//==============================================================================
//=Prototype:	void UartDrvInit (void);
//=Purpose:		Initialize UART communication ports.
//=Scope:		UART Comm port driver
//=Param:		none
//=Return:		none
//==============================================================================
static void UartDrvInit (void)
{
	UartDrvSetComm (DEFAULT_BPS, DEFAULT_DATABIT, UART_PARITY_N, DEFAULT_STOPBIT);
	UartDrvWakeup ();
}

//==============================================================================
//=Prototype:	void UartDrvSleep (void);
//=Purpose:		Put UART port A into sleep mode to save power
//=Scope:		UART Comm port driver
//=Param:		none
//=Return:		none
//=NOTE:		This sub-routine will not shut down the clock module (which is
//=				shared by system wide)
//==============================================================================
static void UartDrvSleep (void)
{
	CPU->REG_MFIO_DATA_OUT &= ~UART_TX_ENABLE;	// power on UART driver
	CPU->REG_MFIO_DATA_OUT |= UART_RX_DISABLE;	// power off UART driver
	CPU->REG_UARTA_CTRL_1 &= ~ENUART;
}

//==============================================================================
//=Prototype:	void UartDrvWakeup (void);
//=Purpose:		Wake up UART port A from sleep mode
//=Scope:		UART Comm port driver
//=Param:		none
//=Return:		none
//=NOTE:		Assume the UART clock is already enable by clock module
//==============================================================================
static void UartDrvWakeup (void)
{
	CPU->REG_MFIO_DATA_OUT |= UART_TX_ENABLE;					// power on UART driver
	CPU->REG_MFIO_DATA_OUT &= ~(UART_RX_DISABLE);				// power off UART driver
	CPU->REG_UARTA_CTRL_1 &= ~DISTXD;			// enable tx
	CPU->REG_UARTA_CTRL_1 |= ENUART;
}

//==============================================================================
//=Prototype:	void UartDrvSetComm (WORD Baud, WORD Bits, WORD Parity, WORD Stops);
//=Purpose:		Set the communication settings for UART A port
//=Scope:		UART Comm port driver
//=Param:		Baud = 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600 or
//=					   115200 bits-per-second
//=				Bit = 7 or 8 bit data
//=				Parity = parity type (either none = 0, even = +ve or odd = -ve)
//=				Stop = number of stop bit (either 1 or 2)
//=Return:		none
//==============================================================================
static void UartDrvSetComm (WORD Baud, WORD Bits, WORD Parity, WORD Stops)
{
	/* config SIB clock, for all serial comm. in side CPU */
	CPU->REG_CLK_CTRL &=  ~(SIBMCLKDIV(7));
	CPU->REG_CLK_CTRL |= SIBMCLKDIR | ENSIBMCLK | SIBMCLKDIV(2)| CSERSEL;
	CPU->REG_CLK_CTRL |= CSERDIV(3) | ENCSERCLK | ENUARTACLK;
	
	// set number of data bits (8 or 7)
	if (Bits == 7)
		CPU->REG_UARTA_CTRL_1 |=  BIT_7 ;
	else
		CPU->REG_UARTA_CTRL_1 &= ~BIT_7 ;
	
	// set parity bit (none, even or odd parity)
	if (Parity) {
		if (Parity < 0) {
			CPU->REG_UARTA_CTRL_1 |= ENPARITY;
			CPU->REG_UARTA_CTRL_1 &= ~EVENPARITY;
		} else
			CPU->REG_UARTA_CTRL_1 |= EVENPARITY | ENPARITY;
	} else
		CPU->REG_UARTA_CTRL_1 &= ~ENPARITY;
	
	// set number of stop bits (1 or 2)
	if (Stops == 2)
		CPU->REG_UARTA_CTRL_1 |= TWOSTOP;
	else
		CPU->REG_UARTA_CTRL_1 &= ~TWOSTOP;
	
	// set baud rate
	CPU->REG_UARTA_CTRL_2 = BAUD (Baud);
}

//==============================================================================
//=Prototype:	void BootPrintf (BYTE *pBuffer);
//=Purpose:		Keep tx the buffer content through UART A till NULL
//=Scope:		UART Comm port driver
//=Param:		pBuffer = pointer of the NULL terminated string
//=Return:		none
//==============================================================================
void BootPrintf (BYTE *pBuffer)
{
	UWORD speed, i;
	
	if (*pBuffer == 0)
		return;
	
	speed = CPUGetSpeed ();				// get the current CPU running speed
	CPUSpeedMin ();						// set the CPU speed to lowest to preserve power
	do {
		while (!(CPU->REG_UARTA_CTRL_1 & EMPTY))
			;
		CPU->REG_UARTA_HLDG = *pBuffer++;
		for (i = 0; i < 100; i++)		// delay
			;							//   |
	} while (*pBuffer);
	CPUSetSpeed (speed);				// restore the CPU speed before exit
}

//==============================================================================
//=Prototype:	VOID BootPutc (CHAR Char);
//=Purpose:		Tx the Char through UART A
//=Scope:		UART Comm port driver
//=Param:		Char = the character that will be tx through UART A
//=Return:		none
//==============================================================================
void BootPutc (UBYTE Char)
{
	UWORD i;
	
	while (!(CPU->REG_UARTA_CTRL_1 & EMPTY))
		;
	CPU->REG_UARTA_HLDG = Char;
	for (i = 0; i < 100; i++)			// delay
		;								//   |
}

//==============================================================================
void BootPrintw (UWORD Word)
{
	BYTE sz_word[10];
	UBYTE tmp;
	
	tmp = 0x0F & (Word >> 28);
	sz_word[0] = (tmp <= 9) ? tmp + '0' : tmp + 'A' - 0x0A;
	tmp = 0x0F & (Word >> 24);
	sz_word[1] = (tmp <= 9) ? tmp + '0' : tmp + 'A' - 0x0A;
	tmp = 0x0F & (Word >> 20);
	sz_word[2] = (tmp <= 9) ? tmp + '0' : tmp + 'A' - 0x0A;
	tmp = 0x0F & (Word >> 16);
	sz_word[3] = (tmp <= 9) ? tmp + '0' : tmp + 'A' - 0x0A;
	tmp = 0x0F & (Word >> 12);
	sz_word[4] = (tmp <= 9) ? tmp + '0' : tmp + 'A' - 0x0A;
	tmp = 0x0F & (Word >> 8);
	sz_word[5] = (tmp <= 9) ? tmp + '0' : tmp + 'A' - 0x0A;
	tmp = 0x0F & (Word >> 4);
	sz_word[6] = (tmp <= 9) ? tmp + '0' : tmp + 'A' - 0x0A;
	tmp = 0x0F & (Word >> 0);
	sz_word[7] = (tmp <= 9) ? tmp + '0' : tmp + 'A' - 0x0A;
	sz_word[8] = ' ';
	sz_word[9] = 0;
	BootPrintf (sz_word);
}

#endif	PRINTF_DEBUG	////////////////////////////////////////////////////////

