//#define SDDEBUG

//////////// SPI routines ////////////

// changes the SPI interface to a fast clock
#define SPI_CLK_fast() {SSP0CR0=7|(0<<8);}	// 24MHz
#define SPI_CLK_slow() {SSP0CR0=7|(59<<8);}	// 400kHz

// CS control via PIO0.1
#define SD_CS_L()   {GPIO0MASK[1<<1]=0;} 
#define SD_CS_H()   {GPIO0MASK[1<<1]=1<<1;} 

extern inline unsigned int SPI_byte(unsigned int d) // 8-bit data transfer
{
	SSP0DR=d; 
	while(!(SSP0SR&0x4));		// wait for data in RX FIFO
	return SSP0DR;
}

extern inline void SPI_rdblock512(unsigned char *pd) // 512-byte read
{
	int i;
	SSP0DR=0xff; 			// to TX FIFO
	for (i=511;i;i--) {
		SSP0DR=0xff;
		while(!(SSP0SR&0x4)); // wait for data in RX FIFO
		*pd++=SSP0DR;
	}
	while(!(SSP0SR&0x4));	// wait for last data
	*pd++=SSP0DR;
}

//////////////////////// SD ROUTINES //////////////////////////////

//SD commands, many of these are not used here
#define GO_IDLE_STATE            0
#define SEND_OP_COND             1
#define SEND_IF_COND			 8
#define SEND_CSD                 9
#define STOP_TRANSMISSION        12
#define SEND_STATUS              13
#define SET_BLOCK_LEN            16
#define READ_SINGLE_BLOCK        17
#define READ_MULTIPLE_BLOCKS     18
#define WRITE_SINGLE_BLOCK       24
#define WRITE_MULTIPLE_BLOCKS    25
#define ERASE_BLOCK_START_ADDR   32
#define ERASE_BLOCK_END_ADDR     33
#define ERASE_SELECTED_BLOCKS    38
#define SD_SEND_OP_COND			 41   //ACMD
#define APP_CMD					 55
#define READ_OCR				 58
#define CRC_ON_OFF               59

static struct {
	unsigned int  res32;			// 32-bit response to commands
	unsigned char SDHC_flag;		// false: byte addresses, true: sector addresses
	unsigned char waitresp;			// switch task on delays if true
} SDvars;

#define WAITRESP	2400		// max number of bytes waiting for cmd response
#define WAITDATA	300000		// max number of bytes waiting for read data (100ms @ 24MHz)
#define RETRIES		2400		// max number of cmd retries during init

//******************************************************************
//Function	: to send a command to SD card
//Arguments	: cmd: (8-bit command value)
// 			  arg: (32-bit command argument)
//return	: response byte
//notes     : CS is left low (for read/write commands)
//******************************************************************
unsigned int SD_cmd(unsigned int cmd, unsigned int arg)
{
	unsigned int i, R1, status;

	if(SDvars.SDHC_flag == 0)		
		if(cmd == READ_SINGLE_BLOCK ||
		cmd == WRITE_SINGLE_BLOCK )
//     ||
//		   cmd == READ_MULTIPLE_BLOCKS  ||
//		   cmd == WRITE_SINGLE_BLOCK    ||
//		   cmd == WRITE_MULTIPLE_BLOCKS ||
//		   cmd == ERASE_BLOCK_START_ADDR|| 
//		   cmd == ERASE_BLOCK_END_ADDR ) 
		{
			arg <<= 9;
		}
	SD_CS_L();

	SPI_byte(cmd | 0x40);	//send command, first two bits always are '01'
	SPI_byte(arg>>24);		//only 8 LSB bits are actually sent
	SPI_byte(arg>>16);
	SPI_byte(arg>>8);
	SPI_byte(arg);

	//it is compulsory to send correct CRC for CMD8 (CRC=0x87) & CMD0 (CRC=0x95)
	//for remaining commands, CRC is ignored in SPI mode
	SPI_byte((cmd)?0x87:0x95);

	i=SDvars.waitresp;
	while((R1 = SPI_byte(0xFF)) == 0xff) { //wait response
		if(!(--i)) break; //time out error
	}

	// commands with additional 32-bit response
	if( cmd == READ_OCR || cmd == SEND_IF_COND) 
	{
		i=SPI_byte(0xff);
		i=(i<<8)|SPI_byte(0xff); 
		i=(i<<8)|SPI_byte(0xff); 
		i=(i<<8)|SPI_byte(0xff);
		SDvars.res32=i;
	}

	SPI_byte(0xff); //extra 8 CLK

	return R1; //return state
}

#ifdef SDDEBUG
//******************************************************************
//Function	: to read CSD / CID registers
//arguments : cmd (9: CSD, 10:CID),
//			  pointer to 16-byte buffer
//return	: 0 if no error,
// 			  otherwise the response byte will be sent
//******************************************************************
unsigned int SD_read_reg16(unsigned int cmd, unsigned char *buffer)
{
	unsigned int i;
 
	SD_CS_L();
	
	SPI_byte(cmd | 0x40);	//send command, first two bits always are '01'
	SPI_byte(0); // Address (dummy)
	SPI_byte(0);
	SPI_byte(0);
	SPI_byte(0);
	SPI_byte(0); // CRC

	i = WAITDATA;
	while(SPI_byte(0xff) != 0xfe) //wait for start block token 0xfe (0x11111110)
		if(!(--i)){SD_CS_H(); return 1;} //return if time-out

	//read 16 bytes of data
	for(i=16; i; i--) *buffer++ = SPI_byte(0xff);

	// 3 extra bytes ignored: 2 for CRC, 1 for extra clocks
	for (i=3;i;i--) SPI_byte(0xff);

	SD_CS_H();

	return 0;
}
#endif

//******************************************************************
//Function	: to initialize the SD/SDHC card in SPI mode
//Arguments	: flagnt (0: espera activa, 1: llama a siguiente tarea)
//return	: 0 if no error,
// 			  otherwise the response byte will be sent
//******************************************************************
unsigned int SD_init()
{
	unsigned int i, response, SD_version;

	SDvars.waitresp=WAITRESP/10;

	SPI_CLK_slow();
	SD_CS_H();
	for(i=10;i;i--) SPI_byte(0xff);   //80 clock pulses before sending the first command

	i=32;
	do {
		response = SD_cmd(GO_IDLE_STATE, 0); //send 'reset & go idle' command
		SD_CS_H();
		if(!(--i)) return 1;   //time out, card not detected
	} while(response != 0x01);

	SPI_byte(0xff);
	SPI_byte(0xff);

	SD_version = 2; //default set to SD compliance with ver2.x; 
					//this may change after checking the next command
	i=RETRIES;
	do {
		response = SD_cmd(SEND_IF_COND,0x000001AA); //Check power supply status, mendatory for SDHC card
		SD_CS_H();
		if(!(--i)) {
			SD_version = 1;
			break;
		} //time out
	}while(response != 0x01);
#ifdef SDDEBUG
	_printf("CMD8.RES7=%08x\n",SDvars.res32);
	_printf("SD version: %d\n",SD_version);
#endif
	i = RETRIES;
	do {
		response = SD_cmd(APP_CMD,0); //CMD55, must be sent before sending any ACMD command
		SD_CS_H();
		response = SD_cmd(SD_SEND_OP_COND,0x40000000); //ACMD41
		SD_CS_H();
		if(!(--i))  return 2;  //time out, card initialization failed
	}while(response != 0x00);

	i = RETRIES;
	SDvars.SDHC_flag = 0;
	if (SD_version == 2) { 
		do {
			response = SD_cmd(READ_OCR,0);
			SD_CS_H();
			if(!(--i)) break;	//time out
		}while(response != 0x00);
#ifdef SDDEBUG
		_printf("OCR=%08x\n",SDvars.res32);
#endif
		SDvars.SDHC_flag = (SDvars.res32>>24) & 0x40;
	}

	SPI_CLK_fast();	// Set a fast SPI clock
	SDvars.waitresp=WAITRESP;

#ifdef SDDEBUG
	static unsigned char buf[16];
	i=SD_read_reg16(9,buf);
	_printf("CSD: ");
	if (i) _printf("Error=%d",i);
	else {
		for (i=0;i<16;i++) _printf("%02x ",buf[i]);
		for (i=0;i<16;i++) _printf("%c",(buf[i]>' ' && buf[i]<128)?buf[i]:'.');
	}
	_printf("\nCID: ");
	i=SD_read_reg16(10,buf);
	if (i) _printf("Error=%d",i);
	else {
		for (i=0;i<16;i++) _printf("%02x ",buf[i]);
		for (i=0;i<16;i++) _printf("%c",(buf[i]>' ' && buf[i]<128)?buf[i]:'.');
	}
	_printf("\n");
#endif
	return 0; //successful return
}

//******************************************************************
//Function	: to read a single block from SD card
//Arguments	: Block address (LBA),
//			: destination buffer address (pointer)
//return	: 0 if no error,
// 			  otherwise the response byte will be sent
//******************************************************************
unsigned int SD_read_Block(unsigned int addr, unsigned char *buffer)
{
	unsigned int i;

	i = SD_cmd(READ_SINGLE_BLOCK, addr); //read a Block command
 
	//_printf("addr=%x stat=%02x\n",addr,i);
	if(i) {SD_CS_H(); return i;}	//check for SD status: 0x00 - OK (No flags set)

	i = WAITDATA;
	while(SPI_byte(0xff) != 0xfe) { //wait for start block token 0xfe (0x11111110)
		if(!(--i)){SD_CS_H(); return 1;} //return if time-out
	}
	//read 512 bytes of data
	//for(i=512; i; i--) *buffer++ = SPI_byte(0xff);
	SPI_rdblock512(buffer);

	// 3 extra bytes ignored: 2 for CRC, 1 for extra clocks
	for (i=3;i;i--) SPI_byte(0xff);

	SD_CS_H();

	return 0;
}

/*
//******************************************************************
//Function	: to write to a single block of SD card
//Arguments	: (uint) block address, (uchar *) data buffer pointer
//return	: will be 0 if no error,
// 			  otherwise the reponse byte will be sent
//******************************************************************
unsigned int SD_write_Block(unsigned int addr, unsigned char *buffer)
{
	unsigned int res,i,retry=0;

	res = SD_cmd(WRITE_SINGLE_BLOCK, addr); //write a Block command
	if(res != 0x00) { return res; }	//check for SD status: 0x00 - OK (No flags set)

	SD_CS_L();
	SPI_byte(0xFE);     			//Send start block token 0xfe (0x11111110)

	for(i=0; i<512; i++)  { SPI_byte(buffer[i]); }   //send 512 bytes of data

	SPI_byte(0xFF);     			//transmit dummy CRC (16-bit), CRC is ignored here
	SPI_byte(0xFF);

	res = SPI_byte(0xFF);
	//reponse= 0xXXX0AAA1
	//	AAA='010' - data accepted ;
	//	AAA='101'-data rejected due to CRC error;
	//	AAA='110'-data rejected due to write error
	if( (res & 0x1F) != 0x05)  { SD_CS_H(); return res; } 

	while(!SPI_byte(0xFF)) {
		if(retry++ > 0xFFFE) {SD_CS_H(); return 1;}
	} //wait for SD card to complete writing and get idle
	
	SD_CS_H();
	SPI_byte(0xFF);	//just spend 8 clock cycle delay before reasserting the CS line
	SD_CS_L();		//re-asserting the CS line to verify if card is still busy

	while(!SPI_byte(0xff)) {
		if(retry++ > 0xFFFE) {SD_CS_H(); return 1;}
	} //wait for SD card to complete writing and get idle
		 
	SD_CS_H();
	
	return 0;
}
*/
//////////////////////////////////////////////////////////////////////

