/* ************************************************************
 *
 * 2368_Serial:
 * Exemplo de utilização da porta Serial, a baudrate para
 * conexão deve ser alterada em BAUDRATE presente no Makefile.
 * Retorna o valor correspondente ao ASCII dos caracteres
 * enviados.
 *
 **************************************************************	*/

#include <arch/nxp/lpc23xx.h>
#include <util/comandos.h>
#include "interrupcao.h"
#include "timer.h"
#include "serial.h"
#include "rtc.h"
#include "type.h"
#include "mci.h"
#include "lcd.h"
#include "irq.h"


/* Frequencia efetiva do clock 12MHz */
#define SYSCLK 12000000

volatile  DWORD MCI_ReadFifo(DWORD * dst);
volatile  DWORD MCI_WriteFifo(DWORD * src);

extern volatile DWORD MCI_CardType;
extern volatile DWORD MCI_Block_End_Flag;
extern volatile DWORD mci_fifo;
extern volatile BYTE *WriteBlock, *ReadBlock;
//void FIQ_Routine(void) __attribute__ ((interrupt("FIQ")));

int cpuclock;
int tempo;

void inicializa(void)
{
	/* Seta bits 0 e 5 do GPIOM
	 * 0 -> GPIOM - High Speed IO habilitados na porta 0 e 1
	 * 5 -> OSCEN - Habilita e inicia Oscilador principal 		*/
	SCS = 0x21;
	/* Aguarda o Oscilador principal ficar pronto para uso
	 * 6 -> OSCSTAT - Status do Oscilador principal				*/
	while(SCS & 0x40);
	/* Seta o Oscilador principal como fonte PLL				*/
	CLKSRCSEL = 0x01;
	/* Define o divisor para criar o clock da CPU
	 * Obs: o valor deve ser 0 ou números ímpares				*/
	CCLKCFG = 0;
	/* PCLK = CCLK quando valor da seleção do clock para o
	 * dispositivo for 01.
	 * Para PCLKSEL0:
	 * Obs: Os pinos 18 e 19 pertencem ao RTC e não devem ser
	 * alterados. Os pinos 10 e 11 não são utilizados.
	 * Para PCLKSEL1:
	 * Obs: Os pinos 26 e 27, e 30 e 31 não são utilizados.		*/
	PCLKSEL0 = 0x55515155;
	PCLKSEL1 = 0x11555555;
	/* Funções do Módulo Acelerador de Memória desabilitadas 	*/
	MAMCR = 0x00;
	/* Para um sistema com o clock maior que 40MHz 3 CCLK são
	 * recomendados, logo MAMTIM = 3;							*/
	//MAMTIM = 3;
	/* Funções do Módulo Acelerador de Memória habilitadas
	 * Com as configurações ajustadas pode-se iniciar o Módulo
	 * Acelerador de Memória.*/
	//MAMCR=2;
}

void PLL_inicializa(int pllmulti, int cpudiv)
{
	int k;
	cpudiv &= -2;	/* cpudiv deve ser par */
	if(PLLSTAT & (1 << 25)){	/* Se PLL nao estava configurado */
		PLLCON = 1;	/* Habilita PLL, desconectado */
		PLLFEED=0xAA;PLLFEED=0x55;
		}
	PLLCON=0;	/* Desconecta e desabilita o PLL */
	PLLFEED=0xAA;PLLFEED=0x55;
	/* Depois deste comando deve-se usar FIO1PIN para acessar a porta P1 */
	SCS = 0x21;	/* Liga o oscilador externo (12MHz) e o GPIO */
	while( !(SCS & 0x40) );	/* Espera estabilizar o oscilador */
	CLKSRCSEL = 0x1;	/* Usa o 12MHz como oscilador principal */
	PLLCFG = (pllmulti/2 - 1);
	PLLFEED=0xAA;PLLFEED=0x55;
	CCLKCFG=0;	/* Divisor do clock da CPU = 1 */
	PLLCON=1;	/* Habilita o PLL	*/
	PLLFEED=0xAA;PLLFEED=0x55;
	k=80000;	/* No maximo 80000 voltas esperando o PLL lock */
	while((!(PLLSTAT & (1<<26))) && k) k--;	/* espera PLL Lock */
	/* Se conseguiu PLL Lock, conecta CPU clock ao PLL */
	CCLKCFG=cpudiv-1;	/* Divisor por cpudiv na CPU */
	if(k)	{
		cpuclock= pllmulti*SYSCLK/cpudiv;
		PLLCON=3;	/* Conecta o PLL ao clock da CPU */
		}
	else	cpuclock=SYSCLK/cpudiv;
	PLLFEED=0xAA;PLLFEED=0x55;
}

/* Escreve os caracteres lidos entre [ e ] */
int main (void)
{
	int c,n;
	DWORD i, j;
	inicializa();
	PLL_inicializa(16,4);
	Timer0_inicializa_ms(60000000);
	//U0init(10800000,19200);
	LCD_inicializa();
	//RTC_inicializa();
	LCD_escreveMsg("\fCPU: ");
	//LCD_escreveInt(12,0);
	LCD_escreveInt(cpuclock/1000000,0);
	LCD_escreveMsg(" MHz");

	for ( i = 0; i < BLOCK_LENGTH; i++ )
	  {
		WriteBlock[i] = i;
		ReadBlock[i] = 0;
	}

	LCD_limpaLinha(2,16);
	LCD_escreveMsgLC(2,1,"Buffer limpo.");
	Timer0_espera(2000);

	if ( MCI_Init() != TRUE )
	{
		while( 1 );			/* fatal error */
	}
	LCD_limpaLinha(2,16);
	LCD_escreveMsgLC(2,1,"MCI init.");
	Timer0_espera(2000);

	MCI_CardType = MCI_CardInit();

	if ( MCI_CardType == CARD_UNKNOWN )
	{
	LCD_limpaLinha(2,16);
	LCD_escreveMsgLC(2,1,"Cartao Desc.");
	Timer0_espera(2000);
	while ( 1 );		/* fatal error */
	}

	LCD_limpaLinha(2,16);
	LCD_escreveMsgLC(2,1,"Cartao Aceito.");
	Timer0_espera(2000);

	if (MCI_Check_CID() != TRUE)
	{
	while ( 1 );		/* fatal error */
	}
	LCD_limpaLinha(2,16);
	LCD_escreveMsgLC(2,1,"Check CID pass.");
	Timer0_espera(2000);

	if ( MCI_Set_Address() != TRUE )
	{
	while ( 1 );		/* fatal error */
	}
	LCD_limpaLinha(2,16);
	LCD_escreveMsgLC(2,1,"SetAddress pass.");
	Timer0_espera(2000);

	if ( MCI_Send_CSD() != TRUE )
	{
	while ( 1 );		/* fatal error */
	}
	LCD_limpaLinha(2,16);
	LCD_escreveMsgLC(2,1,"Send CSD pass.");
	Timer0_espera(2000);

	if ( MCI_Select_Card() != TRUE )
	{
	while ( 1 );		/* fatal error */
	}

	LCD_limpaLinha(2,16);
	LCD_escreveMsgLC(2,1,"SelectCard pass.");
	Timer0_espera(2000);

	if ( MCI_CardType == SD_CARD )
	{
	MCI_Set_MCIClock( NORMAL_RATE );
	if (SD_Set_BusWidth( SD_4_BIT ) != TRUE )
	{
	  while ( 1 );	/* fatal error */
	}
	}

	if ( MCI_Set_BlockLen( BLOCK_LENGTH ) != TRUE )
	{
	while ( 1 );		/* fatal error */
	}

	LCD_limpaLinha(2,16);
	LCD_escreveMsgLC(2,1,"CartaoSD. ");
	LCD_escreveInt(BLOCK_LENGTH,0);
	Timer0_espera(2000);
	/***************************************************************/
	/* TEST - Write and Read number of blocks defined by BLOCK_NUM */
	/***************************************************************/
	for ( i = 0; i < BLOCK_NUM; i++ )
	{
		if ( MCI_Write_Block( i ) != TRUE )
		{
		  while ( 1 );		/* Fatal error */
		}
		/* When MCI_Block_End_Flag is clear, it indicates
		Write_Block is complete, next task, Read_Block to check write */

		if (test_bit(MCI_STATUS,10)) {
			LCD_limpaLinha(2,16);
			LCD_escreveMsgLC(2,1,"Flag 10.");
		}
		else {
			LCD_limpaLinha(2,16);
			LCD_escreveMsgLC(2,1,"Travou.");
		}

		while ( MCI_Block_End_Flag == TRUE);

		if ( MCI_Read_Block( i ) != TRUE )
		{
		  while ( 1 );		/* Fatal error */
		}

		/* When MCI_Block_End_Flag is clear, it indicates RX is done
		with Read_Block,  validation of RX and TX buffers next. */
		while ( MCI_Block_End_Flag == TRUE );

		for ( j = 0; j < (BLOCK_LENGTH); j++ )
		{
		  if ( WriteBlock[j] != ReadBlock[j] )
		  {
			while ( 1 );	/* Data comparison failure, fatal error */
		  }
		}
		/*  clear read buffer for next block comparison */
		for ( j = 0; j < (BLOCK_LENGTH); j++ )
		{
		  ReadBlock[j] = 0;
		}
	}
	LCD_limpaTela();
	LCD_escreveMsgLC(2,1,"Escreveu SD.");
	Timer0_espera(2000);
	n=0;
	tempo=T0TC;
		do	{
			if (T0TC==tempo+1000) {
				LCD_escreveMsgL(2,"Tempo:  ");
				LCD_escreveInt(RTC_HOUR,2);
				LCD_escreveChar(':');
				LCD_escreveInt(RTC_MIN,2);
				LCD_escreveChar(':');
				LCD_escreveInt(RTC_SEC,2);
				tempo=tempo+1000;
			}
			else {
				if (T0TC>=tempo+1000)
				{
					tempo=T0TC;
				}
			}
			} while(c!=1);	/* Ctrl-A sai */
		return 0;
}

/* Estas rotinas sao chamados pelo crt.S. 
Devem existir, mas ainda nao estao sendo usadas */
void UNDEF_Routine(){}
void SWI_Routine(){}
void FIQ_Routine() {}
