/*=================================< card.c >================================*/
// 
//    Title:      FLASH CARD (MMC) MODULE
//    Saved date: 05 May 2003 11:41:32
//    Written by: JAG
//    Project:    RMS101 SBC
//    Target:     LH79520
//    Compiler:   GNU GCC for ARM
//		Copyright:	Subject to the Revely Open Source License (see readme.txt)
//
// The card module contains driver functions for accessing data on a flash
// card in the multimedia card slot.  SPI Mode is used to simplify interface
// operations. 
//
// The status LED is employed as a BUSY indicator for Flash operations.
//
// Currently there are no timeouts on SPI operations ! Use at your own risk !
// Also, this is a minimal set of functions. More features coming soon...
//
// History:
//	
// 1.00	04-22-03	JAG	First release
//
/*===========================================================================*/

#include <stddefs.h>
#include <regs.h>
#include <card.h>
#include <string.h>
#include <uart1.h>

/*---------------------< Local Function Prototypes >-------------------------*/
void CardPowerOn(void);
void CardPowerOff(void);
void CardInitSPI(void);
void SPIAssertCS(void);
void SPIReleaseCS(void);
void CardSendCommand(BYTE bCommand, WORD wArg);
WORD CardGetR1Response(void);
void SPISendByte(BYTE bDat);
BYTE SPIGetByte(void);
void CardReset(void);
void CardDelay(BYTE bNum);

/*==========================< Global Functions >=============================*/

/*----------------------------< CardSlotInit >-------------------------------*/
void CardSlotInit(void)
{
	// Prepare Slot for Card
	CardInitSPI();
	CardPowerOn();
	// Status LED
	PORTF_DDR = 0x02;
	PORTF_DATA |= 0x02;	// LED is Off
}

/*-----------------------------< CardDetect >--------------------------------*/
BYTE CardDetect(void) 
{
	BYTE bTries, bResponse, bBytes;
	
	// Send 80 dummy clocks (no CS)
	CardDelay(10);
	
	// Set to SPI Mode
	SPIAssertCS();
	
	CardSendCommand(GO_IDLE_STATE, 0);
		
	bBytes = 20;
	CardDelay(1);
	while (--bBytes)
	{
		if ((SPIGetByte() & 0xff) == 0x01)
			break;
		CardDelay(1);
	}
	if (bBytes)
		return TRUE; 
	else
		return FALSE;
}

/*-------------------------------< CardInit >--------------------------------*/
BYTE CardInit(void)
{
	BYTE bI;
	
	// Activate cards initialization process
	SPIReleaseCS();
	CardDelay(1);
	do
	{ 
		SPIAssertCS();
		CardSendCommand(SEND_OP_COND,0);
		bI = CardGetR1Response();
		SPIReleaseCS();
		CardDelay(1);
	} while (bI != 0x00);

	return TRUE;
}

/*------------------------------< CardReadBlock >-----------------------------*/
BYTE CardReadBlock(WORD wAddr, PBYTE pDat)
{
	BYTE bI;
	WORD wI;
	
	// Address must be alligned to 512 byte boundary
	wAddr &= 0xfffffe00;
	// Send Command
	SPIAssertCS();
	CardSendCommand(READ_SINGLE_BLOCK,wAddr);
	SPIGetByte();
	if (CardGetR1Response() == 0)
	{
		do
		{
			CardDelay(1);
			bI = SPIGetByte();
		} while (bI != 0xfe);
		for (wI=0; wI < 512; wI++)
		{
			CardDelay(1);
			*pDat++ = SPIGetByte();
		} 		
		SPIReleaseCS();
		CardDelay(1);
		return TRUE;
	}
	else
	{
		SPIReleaseCS();
		CardDelay(1);
		return FALSE;
	}
}

/*-----------------------------< CardWriteByte >-----------------------------*/
BYTE CardWriteBlock(WORD wAddr, PBYTE pDat)
{
	volatile BYTE bI;
	WORD wI;
	
	// Address must be alligned to 512 byte boundary
	wAddr &= 0xfffffe00;
	// Send Command
	SPIAssertCS();
	CardSendCommand(WRITE_BLOCK,wAddr);
	if (CardGetR1Response() == 0)
	{
		CardDelay(1);
		SPISendByte(0xfe);	// Data token
		
		for (wI=0; wI < 512; wI++)
		{
			SPISendByte(*pDat++);
			bI = SPIGetByte();
		} 		
		SPISendByte(0xff);		// CRC
		SPISendByte(0xff);		// CRC
		SPISendByte(0xff);
		SPIGetByte();
		SPIGetByte();
		SPIGetByte();
		SPIGetByte();
		SPIGetByte();
		SPIGetByte();
		SPIGetByte();
		SPIGetByte();
		wI = SPIGetByte();		// Data response token
		do
		{
			CardDelay(1);
			bI = SPIGetByte();
		}
		while (bI == 0);
				
		SPIReleaseCS();
		CardDelay(1);
		SPIAssertCS();
		CardDelay(1);
		SPIGetByte();
		SPIGetByte();
		do
		{
			CardDelay(1);
			bI = SPIGetByte();
			
		}
		while (bI == 0x00);
		SPIReleaseCS();
		if (wI == 0xe5)	// data token OK ?
			return TRUE;
		else
			return FALSE;
	}
	else
	{
		SPIReleaseCS();
		CardDelay(1);
		return FALSE;
	}
}


/*===========================< Local Functions >=============================*/

/*-----------------------------< CardPowerOn >-------------------------------*/
void CardPowerOn(void)
{
	volatile int i; 
		
	// Uses Port H.7
	PORTH_DDR |= 0x80;
	PORTH_DATA &= 0x7f;
	
	// Delay to allow +3.3V supply to come up
	for (i=0; i<1000; i++);
}

/*----------------------------< CardPowerOff >-------------------------------*/
void CardPowerOff(void)
{
	PORTH_DDR |= 0x80;
	PORTH_DATA |= 0x80;
}



/*-----------------------------< CardInitSPI >-------------------------------*/
void CardInitSPI(void)
{
	#define SCR 		0x100		// Clock prescalar
	#define SPO			0x40		// Clock phase
	#define SPH 		0x80
	#define MOTSPI		0			// Motorola SPI
	#define DATABITS8	7			// 8 data bits

	// Set up Chip Select - Port A.2 is CS
	SPIReleaseCS();
	PORTA_DDR |= 0x00000004;
	SSPMUX = 0x0008;		// Assign I/O pins 
	
	
	// Set clock and prescalar
	SSPCPSR = 0xfe;			// 5 Mhz
	SSPPSVAL = 0x04;
	PERCLKCTRL2 &= 0xfffd;

	// Set up SSP	- 8 data bits, -ve edge clock
	SSPCR0 = (SCR << 8) + SPO + SPH + MOTSPI + DATABITS8;
	//SSPCR0 = (SCR << 8) + MOTSPI + DATABITS8;
	SSPCR1 = 0x10;		// enable SPI
}

/*-----------------------------< SPIAssertCS >-------------------------------*/
void SPIAssertCS(void)
{
	// Signal Flash Card is busy
	PORTF_DATA &= 0xfd;	// LED is ON
	
	// Wait for any action to finish by checking SSP Busy Flag
	while (SSPSR & 0x10);
	
	// Port A.2 is CS
	PORTA_DATA &= 0xfffffffb;
}

/*----------------------------< SPIReleaseCS >-------------------------------*/
void SPIReleaseCS(void)
{
	// Wait for any action to finish by checking SSP Busy Flag
	while (SSPSR & 0x10);
	
	PORTA_DATA |= 0x00000004;
	
	// Signal Flash Card is OK to remove
	PORTF_DATA |= 0x02;	// LED is Off
} 

/*---------------------------< CardSendCommand >-----------------------------*/
void CardSendCommand(BYTE bCommand, WORD wArg)
{
	BYTE bCRC;

	// Flush receive buffer
	while (SSPSR & 0x04)
		bCRC = SSPDR;
	// Send 5 byte long command and argument
	SPISendByte(bCommand + 0x40);
	SPISendByte(wArg >> 24);
	SPISendByte((wArg >> 16) & 0xff);
	SPISendByte((wArg >> 8) & 0xff);
	SPISendByte(wArg & 0xff);
	// No need to calculate CRC - it is disabled for SPI mode by default
	if (bCommand)
		SPISendByte(0x00);
	else
		SPISendByte(0x95);	// 95h is CRC for CMD0 by default
}

/*----------------------------< CardGetR1Response >----------------------------*/
// Get R1 type response
WORD CardGetR1Response(void)
{
	WORD wDat=0xff;
	WORD wResponse;
	BYTE bI;

	// Flush 0xFF bytes from receive 
	do
	{
		wDat = SPIGetByte();
		CardDelay(1);
	}
	while (wDat & 0x80);		// MSB Set
	wResponse = wDat;

	return wResponse;
}

/*-----------------------------< SPISendByte >-------------------------------*/
void SPISendByte(BYTE bDat)
{
	// Wait for any action to finish by checking SSP Busy Flag
	while (SSPSR & 0x10);
	
	SSPDR = bDat;
}
 
/*-----------------------------< SPIGetByte >--------------------------------*/
BYTE SPIGetByte(void)
{
	volatile BYTE bI;
	
	// Wait for a byte
	while (!(SSPSR & 0x04));
	bI = SSPDR;
	
	return bI;
}

/*-----------------------------< CardReset >---------------------------------*/
void CardReset(void)
{
	SPIAssertCS();
	CardSendCommand(GO_IDLE_STATE, 0);
	SPIReleaseCS();
}

/*-----------------------------< CardError >---------------------------------*/
void CardError(BYTE bCode)
{


}

/*------------------------------< CardDelay >--------------------------------*/
void CardDelay(BYTE bNum)
{
	BYTE bI;
	
	for (bI=0; bI<bNum; bI++)
			SPISendByte(0xff);
}

// end of card.c

