////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.  All rights reserved.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <tinyhal.h>
#include "SD_BL.h"

#include "..\..\iodefine.h"

//--//


#define TIME_OUT 0x400

extern struct SD_BL_CONFIGURATION g_SD_BL_Configuration;
extern struct SD_BLOCK_CONFIG g_SD_BL_Config;

SH7262_SD_BS_Driver g_SH7262_SD_BS_Driver;

#define SD_PHYISCAL_BASE_ADDRESS 0

extern FileSystemVolume g_SH7262_SD_FS;

static BYTE s_sectorBuff[SD_DATA_SIZE];
static BYTE s_cmdBuff[SD_CMD_SIZE];

BYTE SDversion;
BYTE isCardInitialized; 

//--//

static UINT8 CRC7Encode(UINT8 crc, UINT8 *input, int length)
{
    for(int j = 0; j < length; j++)
    {
        UINT8 byte = input[j];

        for(int i = 8; i > 0; i--)
        {
            crc = (crc << 1) | ((byte &0x80) ? 1 : 0);
            if(crc &0x80)
            {
                crc ^= 9; // polynomial
            } 
            byte <<= 1;
        }
        crc &= 0x7f;
    }

    for(int i = 7; i > 0; i--)
    {
        crc = (crc << 1);
        if(crc &0x80)
        {
            crc ^= 9; // polynomial
        }
    }

    crc &= 0x7f;

    return crc;
}

int SH7262_SD_BS_Driver::CARD_GetCardDetect(void)
{
	if (PORT.PJPR0.WORD & 2)
		return 0;	
	return -1;	
}

BYTE SH7262_SD_BS_Driver::SPI_Send1Byte(BYTE data)
{
	RSPI0.SPDR.BYTE = data;	/* Start transmission */
	while ((RSPI0.SPSR.BYTE & 0x80) == 0) ;	/* Wait for end of transmission */
	
	return RSPI0.SPDR.BYTE;	/* Discard received data */
}

void SH7262_SD_BS_Driver::SPI_SendXByte(BYTE *buffer, int len)
{
	int i;
	unsigned char data;
	for(i=0;i<len;i++){
		data=*(buffer++);
		SPI_Send1Byte(data);
	}
}

BYTE SH7262_SD_BS_Driver::SPI_Recv1Byte(void)
{
	RSPI0.SPDR.BYTE = 0xFF;	/* Trigger reception */
	while ((RSPI0.SPSR.BYTE & 0x80) == 0) ;	/* Wait for a byte */
	
	return RSPI0.SPDR.BYTE;	/* Get received byte and return */
}

void SH7262_SD_BS_Driver::SPI_RecvXByte(unsigned char *buffer, int len)
{
	int i;
	unsigned char data;
	for(i=0;i<len;i++){
		data=SPI_Recv1Byte();
		*(buffer++)=data;
	}
}

void SH7262_SD_BS_Driver::SD_ChipSelectCtrl(int f)
{
	if (f)
		PORT.PGDR1.WORD &= 0xFFEF;
	else
		PORT.PGDR1.WORD |= 0x0010;
}

void SH7262_SD_BS_Driver::SPI_Initialize()
{
	//*STBCR5 &= ~0x02;	/* SPIチャネル0有効 */
	CPG.STBCR5.BYTE  =  ~0x02;
	
	/* SPIチャネル0選択 */
	PORT.PFCR3.WORD = 0X0003;
	PORT.PFCR2.WORD = (PORT.PFCR2.WORD & 0x0F0F) | 0x3030;

	//PG20 is output
	PORT.PGIOR1.WORD = PORT.PGIOR1.WORD | (1<<4);

	if (!(RSPI0.SPCR.BIT.SPE))
	{
		/* RSPI0初期化 */
		RSPI0.SPPCR.BYTE  = 0;
		RSPI0.SPSCR.BYTE  = 0;
		RSPI0.SPDCR.BYTE  = 0x20;	/* データレジスタはバイトアクセス */
		RSPI0.SPSR.BYTE  &= 0xFA;	/* ステータスクリア */
		RSPI0.SPBFCR.BYTE = 0xF0;	/* 送受信バッファクリア */
		RSPI0.SPBFCR.BYTE = 0x30;	/* 送信バッファトリガ0 */

		RSPI0.SPCMD0.WORD = 0x070C;	/* MSBファースト,8ビット,BRDV=3 */
		RSPI0.SPBR.BYTE   = 0x05;	/* SPIクロック500kHz(Bφ=24MHz時) */
	//	*SPCMD00 = 0x0700;	/* MSBファースト,8ビット, */
	//	*SPBR0   = 0x01;	/* SPIクロック12.0MHz(Bφ=24MHz時) */

		RSPI0.SPCR.BYTE   = 0x48;	/* SPIイネーブル＆マスタ */
	}
}

void SH7262_SD_BS_Driver::SPI_GPIOInitialize(void)
{
	/* MMC_CD[PJ1] & MMC_WP[PJ3]入力方向 */
	PORT.PJCR0.WORD = PORT.PJCR0.WORD & 0x0F0F;
	PORT.PJIOR0.WORD = PORT.PJIOR0.WORD & 0xFFF5;

	/* MMC_CS[PG20] 出力方向 */
	PORT.PGCR5.WORD = PORT.PGCR5.WORD & 0xFFF0;
	PORT.PGDR1.WORD = PORT.PGDR1.WORD | 0x0010; 
	PORT.PGIOR1.WORD = PORT.PGIOR1.WORD | 0x0010;
}

void SH7262_SD_BS_Driver::SD_CsSetHigh()
{
	SD_ChipSelectCtrl(0);
}

void SH7262_SD_BS_Driver::SD_CsSetLow()
{
	SD_ChipSelectCtrl(1);
}

BYTE SH7262_SD_BS_Driver::SD_CheckBusy(void)
{
    BYTE response;
    BYTE rvalue = 0xFF;

    for(int i=10000; i!=0; i--)
    {
        response = SPI_Send1Byte(0xff);

        if(response != 0xFF && rvalue == 0xFF)
        {
            response &= 0x1f;
            switch(response) /* 7 6 5 4 3    1 0  */
            {
                /* data response  x x x 0 status 1 */
                case 0x05:
                    rvalue = SD_SUCCESS;
                    break;

                case 0x0b:
                    return (SD_CRC_ERROR);

                case 0x0d:
                    return (SD_WRITE_ERROR);

                default:
                    rvalue = SD_OTHER_ERROR;
                    break;
            }
        }
        else if(response != 0x00 && rvalue != 0xFF)
        {
            break;
        }

        HAL_Time_Sleep_MicroSeconds(10);
    }

    return rvalue;
}

//return Card status in R1 response
BYTE SH7262_SD_BS_Driver::SD_SendCmdWithR1Resp(BYTE cmd, UINT32 arg, BYTE crc, BYTE expectedToken, INT32 iterations)
{
    BYTE response;
    BYTE retVal = 0xFF;
	int i; 
	
    s_cmdBuff[0] = (0x40 | cmd); // command
    s_cmdBuff[1] = ((BYTE)((arg >> 24) &0xff)); // parameter
    s_cmdBuff[2] = ((BYTE)((arg >> 16) &0xff));
    s_cmdBuff[3] = ((BYTE)((arg >> 8) &0xff));
    s_cmdBuff[4] = ((BYTE)((arg >> 0) &0xff));
    s_cmdBuff[5] = (crc); // CRC check code

    SPI_Send1Byte(DUMMY);

	for (i=0 ; i < 6; i++)
		SPI_Send1Byte(s_cmdBuff[i]);
		
    for(int i = 0; i < 0x400; i++)
    {
        response = SPI_Send1Byte(DUMMY);

        if(response == expectedToken)
        {
            return expectedToken;
        }
        else if(response != 0xFF && retVal == 0xFF)
        {
            retVal = response;
        }
    }

    return retVal;
}

//return support voltage range in R7response
BOOL SH7262_SD_BS_Driver::SD_SendCmdWithR7Resp(BYTE cmd, UINT32 arg, BYTE *outVoltage)
{
    BYTE response;
    BYTE i, echo_back;

    ASSERT(outVoltage != NULL);

    *outVoltage = 0xFF;

    s_cmdBuff[0] = (0x40 | cmd); // tansmition bit | command
    s_cmdBuff[1] = ((BYTE)((arg >> 24) &0xff)); // parameter
    s_cmdBuff[2] = ((BYTE)((arg >> 16) &0xff));
    s_cmdBuff[3] = ((BYTE)((arg >> 8) &0xff));
    s_cmdBuff[4] = ((BYTE)((arg >> 0) &0xff));
    s_cmdBuff[5] = (CRC7Encode(0, s_cmdBuff, 5) << 1) | 1; // CRC | ENDBIT

    SPI_Send1Byte(DUMMY);

	for (i=0 ; i < 6; i++)
		SPI_Send1Byte(s_cmdBuff[i]);

    for(i = 0; i < 100; i++)
    {
		response = SPI_Recv1Byte();
        if(response != 0xFF)
        // begin token of R7 response
        break;
    }

    if(i == 100)
        return FALSE;

    //recieve voltage content in R7 response
	SPI_Recv1Byte();
	SPI_Recv1Byte();
    *outVoltage = SPI_Recv1Byte();;
    echo_back = SPI_Recv1Byte();;

    if(echo_back != s_cmdBuff[4])
    {
        return FALSE;
    }

    return TRUE;
}

BOOL SH7262_SD_BS_Driver::ChipInitialize(void *context)
{
    SD_BLOCK_CONFIG *config = (SD_BLOCK_CONFIG*)context;

    if(!config || !config->BlockDeviceInformation)
    {
        return FALSE;
    }

	PORT.PCDR0.WORD = PORT.PCDR0.WORD | 0x0100;
	PORT.PCIOR0.WORD = PORT.PCIOR0.WORD | 0x100;
	PORT.PCCR2.WORD = PORT.PCCR2.WORD | 0xFFF0;
	
	SPI_GPIOInitialize();
	
	SPI_Initialize();
	
	if (CARD_GetCardDetect())
		InsertISR();
		
    return TRUE;
}

BOOL SH7262_SD_BS_Driver::DetectInsertEject()
{
	int i = 0, j = 0x8000;
	
	if (isCardInitialized == 1)
	{	
		//Detect card remove
		while(j--) {
			if (CARD_GetCardDetect()) i++;
			else i = 0;
		}
		if (i < 0x8000)
		{
			return TRUE; //Changed from insert to remove
		}
		else
		{
			return FALSE;
		}
	}
	else
	{	
		//Detect card insert
		while(j--) {
			if (CARD_GetCardDetect()) i++;
			else i = 0;
		}
		if (i >= 0x8000)
		{
			return TRUE; //Card inserted
		}
		else
		{
			return FALSE;
		}
	}
}

BOOL SH7262_SD_BS_Driver::ISR(GPIO_PIN Pin, BOOL PinState, void *Param)
{
	if (CARD_GetCardDetect())
		return (InsertISR());
	else
		return (EjectISR());
}

BOOL SH7262_SD_BS_Driver::InsertISR()
{
	BOOL foundDBR = FALSE, foundMBR = FALSE;
    BYTE response;
    int i = 0;
	
    unsigned char regCID[16];
    unsigned char ManufacturerCode;
    unsigned short OEMID;
    unsigned char ProductName[5];
	unsigned int ocr;
	unsigned int size;
	unsigned int baseAddress, offset, BytesPerSector;
		
	if(!(&g_SD_BL_Config) || !g_SD_BL_Config.BlockDeviceInformation)
	{
		return FALSE;
	}

	BlockDeviceInfo* pDevInfo = g_SD_BL_Config.BlockDeviceInformation;

    SD_CsSetHigh();

    //need 74 clock for initialize
    for(i = 0; i < 100; i++)
    {
        SPI_Send1Byte(DUMMY);
    }

    SD_CsSetLow();

    // send CMD0, turn to IDLE state
    for(i = 0; i < 10; i++)
    {
        response = SD_SendCmdWithR1Resp(SD_GO_IDLE_STATE, 0, 0x95, R1_IN_IDLE_STATUS, 0x400);

        if(response == R1_IN_IDLE_STATUS)
            break;
    }

    if(response != R1_IN_IDLE_STATUS)
    {
        SD_CsSetHigh();
        return FALSE;
    }

    //send CMD8, check voltage range support 
    UINT32 CMD8_Arg = CMD8_CHECK_PATTERN;
    BYTE support_voltage = 0;
    UINT32 supply_voltage;

	SDversion = 1;

    supply_voltage = 1;
	
    CMD8_Arg |= (supply_voltage << 8);
    if(SD_SendCmdWithR7Resp(SD_SEND_IF_COND, CMD8_Arg, &support_voltage))
    {
        if(support_voltage != supply_voltage) return FALSE;
		else SDversion = 2;
    }
	else
	{
		support_voltage = 0;
	}

    //send CMD55 + ACMD41 until return 0x00
    for(i=0; i<0x7fff; i++)
    {
        SD_SendCmdWithR1Resp(SD_APP_CMD, 0, 0xFF, R1_IN_IDLE_STATUS, 0x400);

		// Format and send cmd: Volt. window is usually 0x00300000 (3.4-3.2v)
		if (i > 0)
			ocr = 0x00ff8000;			
		else
			ocr = 0;

		ocr |= support_voltage << 30;

        response = SD_SendCmdWithR1Resp(SD_SEND_OP_COND, ocr, 0xFF, R1_IN_READY_STATUS, 0x400);
        if(response == R1_IN_READY_STATUS)
        {
            break;
        }
    }

    if(response != R1_IN_READY_STATUS)
    {
        SD_CsSetHigh();
        return FALSE;
    }

    // send CMD16, set block length to 512
    response = SD_SendCmdWithR1Resp(SD_SET_BLOCKLEN, 512, 0xFF, R1_IN_READY_STATUS, 1);

    if(response != R1_IN_READY_STATUS)
    {
        SD_CsSetHigh();
        return FALSE;
    }    

    //send CMD9  to get CSD
    BYTE regCSD[CSD_CID_LENGTH];
    BYTE C_SIZE_MULT = 0;
    BYTE TAAC, NSAC, MAX_RAN_SPEED, READ_BL_LEN, SECTOR_SIZE;
    BOOL ERASE_BL_EN;
    UINT32 C_SIZE;
    UINT32 MemCapacity = 0; //total memory size, in unit of byte
    UINT32 Max_Trans_Speed = 0; //in unit of Hz
	
    response = SD_SendCmdWithR1Resp(SD_SEND_CSD, 0, 0xFF, SD_START_DATA_BLOCK_TOKEN);

    if(response != SD_START_DATA_BLOCK_TOKEN)
    {
        SD_CsSetHigh();
        return FALSE;
    }
    
	//Receive CSD data
    SPI_RecvXByte(regCSD, CSD_CID_LENGTH);

    // receive 16 bit CRC
//        SPI_Send1Byte(DUMMY);
//        SPI_Send1Byte(DUMMY);

	TAAC = regCSD[1];
	NSAC = regCSD[2];
	MAX_RAN_SPEED = regCSD[3];
	READ_BL_LEN = regCSD[5] &0x0F;

	if(MAX_RAN_SPEED == 0x32)
		Max_Trans_Speed = 25000000;       //normal mode
	else if(MAX_RAN_SPEED == 0x5A)
		Max_Trans_Speed = 50000000;        //High-Speed mode

	if(regCSD[0] == 0x00)        //SD spec version1.0
	{
		C_SIZE = ((regCSD[6] &0x3) << 10) | (regCSD[7] << 2) | ((regCSD[8] &0xC0) >> 6);

		C_SIZE_MULT = ((regCSD[9] &0x03) << 1) | ((regCSD[10] &0x80) >> 7);

		ERASE_BL_EN = ((regCSD[10] &0x40) == 0x00) ? FALSE : TRUE;

		SECTOR_SIZE = ((regCSD[10] &0x3F) << 1) | ((regCSD[11] &0x80) >> 7);

		MemCapacity = (C_SIZE + 1)*(0x1 << (C_SIZE_MULT + 2))*(0x1 << READ_BL_LEN);
	}
	else        //SD spec version2.0
	{
		C_SIZE = ((regCSD[7] &0x3F) << 16) | (regCSD[8] << 8) | regCSD[9];

		ERASE_BL_EN = ((regCSD[10] &0x40) == 0x00) ? FALSE : TRUE;

		SECTOR_SIZE = ((regCSD[10] &0x3F) << 1) | ((regCSD[11] &0x80) >> 7);

		MemCapacity = (C_SIZE + 1) *512 * 1024;
	}

#if 0
	UINT8 crc = (CRC7Encode(0, regCSD, 15) << 1) | 1;

	if(crc != regCSD[15])
	{
		lcd_printf("Wrong CRC for CSD register!\r\n");
		debug_printf("Wrong CRC for CSD register!\r\n");
	}
#endif

        //Update SD config according to CSD register
	UINT32 SectorsPerBlock    = (ERASE_BL_EN == TRUE) ? 1 : (SECTOR_SIZE + 1);
	BytesPerSector      = 512; // data bytes per sector is always 512
	
    //CMD55+ACMD51 to get SCR register    
	SD_SendCmdWithR1Resp(SD_APP_CMD, 0, 0xFF, R1_IN_READY_STATUS, 1);

    response = SD_SendCmdWithR1Resp(SD_SEND_SCR, 0, 0xFF, SD_START_DATA_BLOCK_TOKEN, 1);

    if(response != SD_START_DATA_BLOCK_TOKEN)
    {
        SD_CsSetHigh();
        return FALSE;
    }

    //CMD55+ACMD51 to get SCR register
    BYTE regSCR[SCR_LENGTH];

    //Receive SCR data
    SPI_RecvXByte(regSCR, SCR_LENGTH);

    //CMD10 to get CID
    response = SD_SendCmdWithR1Resp(SD_SEND_CID, 0, 0xFF, SD_START_DATA_BLOCK_TOKEN, 1);

    if(response != SD_START_DATA_BLOCK_TOKEN)
    {
        SD_CsSetHigh();
        return FALSE;
    }

	//Receive CID  data
	SPI_RecvXByte(regCID, CSD_CID_LENGTH);

	ManufacturerCode = regCID[0];

	memcpy(&OEMID, &regCID[1], 2);

	memcpy(&ProductName, &regCID[3], 5);

	//Read MBR
	baseAddress = 0;
	offset = 0;
	//MemCapacity = 0x800000000;//32G
	while(TRUE)
	{
		// Read single block data
		ReadSector(baseAddress, 0, 512, s_sectorBuff, 512);
		
		if ( ((s_sectorBuff[0] == 0xEB)||(s_sectorBuff[0] == 0xE9)) && (s_sectorBuff[2] == 0x90)&&(s_sectorBuff[510] == 0x55)&&(s_sectorBuff[511] == 0xaa) )
		{
			foundDBR = TRUE;
			break;
		}
		offset = (s_sectorBuff[454] | s_sectorBuff[455] << 8 | s_sectorBuff[456] << 16 | s_sectorBuff[457] << 24);
		size = (s_sectorBuff[458] | s_sectorBuff[459] << 8 | s_sectorBuff[460] << 16 | s_sectorBuff[461] << 24);
		size *= 512;

		if (((s_sectorBuff[510]==0x55)&&(s_sectorBuff[511]==0xaa)) &&
				  ((s_sectorBuff[446]==0x00)||(s_sectorBuff[446]==0x80)) &&
				  (offset > 0) && (size >0))
				 
		{   
			if (foundMBR)
			{
				break;
			}
			if( (offset*512) + size > MemCapacity )
			{
				hal_printf("SDCard: Invalid boot record\r\n");   
				break;
			}
			
			baseAddress += offset;
				
			foundMBR = TRUE;
		}
		else
		{
			hal_printf("SDCard: Invalid boot record\r\n");   
			break;
		}               
	}
	if (foundDBR)
	{
		SectorsPerBlock     = s_sectorBuff[0xD];//sertor per clustor
		BytesPerSector      = s_sectorBuff[0xB] | s_sectorBuff[0xC]<< 8;
		if (foundMBR) {
			MemCapacity = (offset * BytesPerSector) + size;
		}
		else {
			MemCapacity =   (s_sectorBuff[0x13] | s_sectorBuff[0x14] << 8)* BytesPerSector;;
			if (MemCapacity == 0)
			{
				MemCapacity         = (s_sectorBuff[0x20] | s_sectorBuff[0x21] << 8 | s_sectorBuff[0x22] << 16 | s_sectorBuff[0x23] << 24) //Sector per 1 partition
									  * BytesPerSector;
			}   
		}
	}
		
	pDevInfo->BytesPerSector = BytesPerSector;
	pDevInfo->Size           = MemCapacity;

	BlockRegionInfo* pRegions = (BlockRegionInfo*)&pDevInfo->Regions[0];

	pRegions[0].BytesPerBlock = SectorsPerBlock * pDevInfo->BytesPerSector;
	pRegions[0].NumBlocks     = (MemCapacity) / pRegions[0].BytesPerBlock;    
	if ((MemCapacity % pRegions[0].BytesPerBlock) > 0) pRegions[0].NumBlocks++;
	pRegions->Start = 0;

	BlockRange* pRanges   = (BlockRange*)&pRegions[0].BlockRanges[0];

	pRanges[0].StartBlock = 0;
	pRanges[0].EndBlock   = pRegions[0].NumBlocks-1;
		
	FS_Initialize();
	FileSystemVolumeList::Initialize();
	FS_AddVolumes();
	FileSystemVolumeList::InitializeVolumes();

	isCardInitialized  = 1;
	
    SD_CsSetHigh();

    return TRUE;
}

BOOL SH7262_SD_BS_Driver::EjectISR()
{
	FileSystemVolumeList::RemoveVolume( &g_SH7262_SD_FS, FALSE );
	
	isCardInitialized = 0;
	
	return TRUE;
}

BOOL SH7262_SD_BS_Driver::ChipUnInitialize(void *context)
{    
    return TRUE;
}

BOOL SH7262_SD_BS_Driver::ReadProductID(void *context, BYTE *ManufacturerCode, BYTE *OEMID, BYTE *ProductName)
{
    BYTE regCID[16];
    BYTE response;

    // enable SD card
    SD_CsSetLow();

    //CMD10 to CID
    response = SD_SendCmdWithR1Resp(SD_SEND_CID, 0, 0xFF, SD_START_DATA_BLOCK_TOKEN, 1);

    if(response != SD_START_DATA_BLOCK_TOKEN)
    {
        SD_CsSetHigh();
        return FALSE;
    }
    else
    {
        // receive one sector data
        SPI_RecvXByte(regCID, CSD_CID_LENGTH);

        // receive 16 bit CRC
        SPI_Send1Byte(DUMMY);
        SPI_Send1Byte(DUMMY);
    }

    *ManufacturerCode = regCID[0];

    memcpy(OEMID, &regCID[1], 2);

    memcpy(ProductName, &regCID[3], 5);

    // disable SD card
    SD_CsSetHigh();

    return TRUE;
}

const BlockDeviceInfo *SH7262_SD_BS_Driver::GetDeviceInfo(void *context)
{
    SD_BLOCK_CONFIG *config = (SD_BLOCK_CONFIG*)context;

    return config->BlockDeviceInformation;
}

BOOL SH7262_SD_BS_Driver::ReadSector(SectorAddress sectorAddress, UINT32 Offset, UINT32 NumBytes, BYTE* pSectorBuff, UINT32 BytesPerSector)
{
    BYTE response;
    UINT32 leftOver;
    bool   fResponse = false;

    NumBytes = (NumBytes + Offset > BytesPerSector ? BytesPerSector - Offset : NumBytes);
    leftOver = BytesPerSector - (Offset + NumBytes);

    for(int i=0; i<100; i++)
    {
        // enable SD card
        SD_CsSetLow();

        // send CMD17 and wait for DATA_BLOCK_TOKEN
		if (SDversion == 2)
			response = SD_SendCmdWithR1Resp(SD_READ_SINGLE_BLOCK, sectorAddress, 0xff, SD_START_DATA_BLOCK_TOKEN, 10000);
		else
			response = SD_SendCmdWithR1Resp(SD_READ_SINGLE_BLOCK, sectorAddress << 9, 0xff, SD_START_DATA_BLOCK_TOKEN, 10000);
		

        if(response == SD_START_DATA_BLOCK_TOKEN)
        {
            fResponse = true;
            break;
        }

        SD_CsSetHigh();
    }

    if(fResponse)
    {
        // receive one sector data
        SPI_RecvXByte(s_sectorBuff, 512);

		memcpy(pSectorBuff, s_sectorBuff + Offset, NumBytes);

        while(leftOver--)
        {
            SPI_Send1Byte(DUMMY);
        }

        // receive 16 bit CRC
        SPI_Send1Byte(DUMMY);
        SPI_Send1Byte(DUMMY);
		//SD_CheckBusy();
    }
    else
    {
        SD_CsSetHigh();
        return FALSE;
    }

    //disable select SD card
    SD_CsSetHigh();
    return TRUE;
}

BOOL SH7262_SD_BS_Driver::Read(void *context, ByteAddress phyAddress, UINT32 NumBytes, BYTE *pSectorBuff)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();

    UINT32 RangeIndex;
    UINT32 RegionIndex;
    UINT32 BytesPerSector;

    BLOCK_CONFIG* pConfig = (BLOCK_CONFIG*)context;
    
    if(pConfig->BlockDeviceInformation->FindRegionFromAddress(phyAddress, RegionIndex, RangeIndex))
    {
        ByteAddress StartSector = pConfig->BlockDeviceInformation->PhysicalToSectorAddress( &pConfig->BlockDeviceInformation->Regions[RegionIndex], phyAddress);

        BytesPerSector = pConfig->BlockDeviceInformation->BytesPerSector;

        CHIP_WORD *pBuf = (CHIP_WORD*)pSectorBuff;

        UINT32 offset = phyAddress - (StartSector * pConfig->BlockDeviceInformation->BytesPerSector);

        UINT32 bytes  = (NumBytes + offset > BytesPerSector ? BytesPerSector - offset : NumBytes);


        while(NumBytes > 0)
        {
            if(!ReadSector(StartSector, offset, bytes, pBuf, BytesPerSector))
            {
                return FALSE;
            }
            
            offset    = 0;
            pBuf      = (CHIP_WORD*)((UINT32)pBuf + bytes);
            NumBytes -= bytes;
            StartSector++;

            bytes = __min(BytesPerSector, NumBytes);
        }

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

BOOL SH7262_SD_BS_Driver::Write(void *context, ByteAddress phyAddr, UINT32 NumBytes, BYTE *pSectorBuff, BOOL ReadModifyWrite )
{
    NATIVE_PROFILE_PAL_FLASH();

    return WriteX( context, phyAddr, NumBytes, pSectorBuff, ReadModifyWrite, TRUE );
}

BOOL SH7262_SD_BS_Driver::Memset(void *context, ByteAddress phyAddr, UINT8 Data, UINT32 NumBytes )
{
    NATIVE_PROFILE_PAL_FLASH();

    return WriteX( context, phyAddr, NumBytes, &Data, TRUE, FALSE );
}

BOOL SH7262_SD_BS_Driver::WriteX(void *context, ByteAddress phyAddr, UINT32 NumBytes, BYTE *pSectorBuff, BOOL ReadModifyWrite, BOOL fIncrementDataPtr )
{
    NATIVE_PROFILE_PAL_FLASH();

    UINT32 RangeIndex;
    UINT32 RegionIndex;
    UINT32 BytesPerSector;
    UINT32 offset;
    UINT32 Bytes;
    BYTE response;
    UINT32 StartSector;

    CHIP_WORD *pData, *pWrite;
	
	BLOCK_CONFIG* pConfig = (BLOCK_CONFIG*)context;
	
    // find the corresponding region     
    if(!pConfig->BlockDeviceInformation->FindRegionFromAddress(phyAddr, RegionIndex, RangeIndex))
        return FALSE;

    StartSector = pConfig->BlockDeviceInformation->PhysicalToSectorAddress( &pConfig->BlockDeviceInformation->Regions[RegionIndex], phyAddr);

    pData = (CHIP_WORD*)pSectorBuff;
    BytesPerSector = pConfig->BlockDeviceInformation->BytesPerSector;
    offset = phyAddr - (StartSector * BytesPerSector);
    Bytes = (NumBytes + offset > BytesPerSector ? BytesPerSector - offset : NumBytes);

    while(NumBytes > 0)
    {
        // if we are using memset, or if the bytes written are less than the BytesPerSector then do read/modify/write
        if(!fIncrementDataPtr || (Bytes != BytesPerSector))
        {   
            if(Bytes != BytesPerSector)
            {
                if(!ReadSector(StartSector, 0, BytesPerSector, s_sectorBuff, BytesPerSector))
                {
                    return FALSE;
                }

            }
            
            pWrite = (CHIP_WORD*)&s_sectorBuff[0];

            if(fIncrementDataPtr)
            {
                memcpy(&pWrite[offset], pData, Bytes);
            }
            else
            {
                memset(&pWrite[offset], *pData, Bytes);
            }
        }
        else
        {
            pWrite = pData;
        }

        // select SD CS
        SD_CsSetLow();
        
        // send CMD24 --read single block data
		if (SDversion == 2)
			response = SD_SendCmdWithR1Resp(SD_WRITE_SINGLE_BLOCK, StartSector, 0xff, R1_IN_READY_STATUS, 1);
		else
			response = SD_SendCmdWithR1Resp(SD_WRITE_SINGLE_BLOCK, StartSector << 9, 0xff, R1_IN_READY_STATUS, 1);

        if(response == R1_IN_READY_STATUS)
        {
            SPI_Send1Byte(SD_START_DATA_BLOCK_TOKEN); // send DATA_BLOCK_TOKEN

            // send data
            SPI_SendXByte(pWrite, BytesPerSector);

            // wait for end of write busy
            response = SD_CheckBusy();
        }
		else
		{
			//disable SD card CS
			SD_CsSetHigh();
			return FALSE;
		}

        if(fIncrementDataPtr) pData = (CHIP_WORD*)((UINT32)pData + Bytes);

        NumBytes   -= Bytes;
        offset      = 0;
        StartSector++;
        Bytes = __min(BytesPerSector, NumBytes);        
    }

    return TRUE;
}

BOOL SH7262_SD_BS_Driver::GetSectorMetadata(void* context, ByteAddress SectorStart, SectorMetadata* pSectorMetadata)
{
    return TRUE;
}

BOOL SH7262_SD_BS_Driver::SetSectorMetadata(void* context, ByteAddress SectorStart, SectorMetadata* pSectorMetadata)
{
    return TRUE;
}

BOOL SH7262_SD_BS_Driver::IsBlockErased(void *context, ByteAddress phyAddress, UINT32 BlockLength)
{

    NATIVE_PROFILE_HAL_DRIVERS_FLASH();

    UINT32 RegionIndex;
    UINT32 RangeIndex;
    UINT32 SectorsPerBlock;
    UINT32 BytesPerSector;

    BLOCK_CONFIG* pConfig = (BLOCK_CONFIG*)context;

    // this is static buffer, as the driver is current tailor for SD, a page size is 2048 bytes.
    BYTE *pSectorBuff = s_sectorBuff;


    BYTE state_After_Erase = 0xFF;

    if(!pConfig->BlockDeviceInformation->FindRegionFromAddress(phyAddress, RegionIndex, RangeIndex))
        return FALSE;

    ByteAddress StartSector = pConfig->BlockDeviceInformation->PhysicalToSectorAddress( &pConfig->BlockDeviceInformation->Regions[RegionIndex], phyAddress);

    const BlockRegionInfo* pRegion = &pConfig->BlockDeviceInformation->Regions[RegionIndex];

    // as the input arg Sector may not be the startSector address of a block,
    // we need to recalculate it.
    BytesPerSector  = pConfig->BlockDeviceInformation->BytesPerSector;
    SectorsPerBlock = (pRegion->BytesPerBlock / BytesPerSector);

    StartSector = (StartSector / SectorsPerBlock) * SectorsPerBlock;
    
    for(UINT32 i = 0; i < SectorsPerBlock; i++)
    {
        SH7262_SD_BS_Driver::Read(context, StartSector, BytesPerSector, pSectorBuff);
        for(UINT32 j = 0; j < BytesPerSector; j++)
        {
            if(pSectorBuff[j] != state_After_Erase)
            {
                return FALSE;
            }
        }
    }
    return TRUE;
}


BOOL SH7262_SD_BS_Driver::EraseSectors(SectorAddress Address, INT32 SectorCount)
{
    BYTE response;

    SD_CsSetLow(); // cs low

    //send ERASE_WR_BLK_START command
	if (SDversion == 2)
		response = SD_SendCmdWithR1Resp(SD_ERASE_WR_BLK_START, Address, 0xff, R1_IN_READY_STATUS, 1);
	else
		response = SD_SendCmdWithR1Resp(SD_ERASE_WR_BLK_START, Address << 9, 0xff, R1_IN_READY_STATUS, 1);

    if(response != R1_IN_READY_STATUS)
    {
        SD_CsSetHigh();
        return FALSE;
    }

    //send ERASE_WR_BLK_END command
	if (SDversion == 2)
		response = SD_SendCmdWithR1Resp(SD_ERASE_WR_BLK_END, (Address + SectorCount - 1), 0xff, R1_IN_READY_STATUS, 1);
	else
		response = SD_SendCmdWithR1Resp(SD_ERASE_WR_BLK_END, (Address + SectorCount - 1) << 9, 0xff, R1_IN_READY_STATUS, 1);

    if(response != R1_IN_READY_STATUS)
    {
        SD_CsSetHigh();
        return FALSE;
    }

    // send erase command
    response = SD_SendCmdWithR1Resp(SD_ERASE, 0xffffffff, 0xff, R1_IN_READY_STATUS, 1);

    if(response != R1_IN_READY_STATUS)
    {
        SD_CsSetHigh();
        return FALSE;
    }

    // wait for IDLE
    SD_CheckBusy();

    SD_CsSetHigh();

    return TRUE;
}

BOOL SH7262_SD_BS_Driver::EraseBlock(void *context, ByteAddress phyAddr)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();

    UINT32 RangeIndex;
    UINT32 RegionIndex;

    BLOCK_CONFIG* pConfig = (BLOCK_CONFIG*)context;

    if(!pConfig->BlockDeviceInformation->FindRegionFromAddress(phyAddr, RegionIndex, RangeIndex))
        return FALSE;

    const BlockRegionInfo* pRegion = &pConfig->BlockDeviceInformation->Regions[RegionIndex];

    ByteAddress StartSector = pConfig->BlockDeviceInformation->PhysicalToSectorAddress( pRegion, phyAddr );

    UINT32 SectorsPerBlock = pRegion->BytesPerBlock / pConfig->BlockDeviceInformation->BytesPerSector;

    SectorAddress SectorAddress = (StartSector / SectorsPerBlock) * SectorsPerBlock;

    EraseSectors(SectorAddress, SectorsPerBlock);

    return TRUE;

}

void SH7262_SD_BS_Driver::SetPowerState(void *context, UINT32 State)
{
    // our flash driver is always Power ON
    return ;
}

UINT32 SH7262_SD_BS_Driver::MaxSectorWrite_uSec(void *context)
{
    NATIVE_PROFILE_PAL_FLASH();

    SD_BLOCK_CONFIG *config = (SD_BLOCK_CONFIG*)context;

    return config->BlockDeviceInformation->MaxSectorWrite_uSec;
}

UINT32 SH7262_SD_BS_Driver::MaxBlockErase_uSec(void *context)
{
    NATIVE_PROFILE_PAL_FLASH();

    SD_BLOCK_CONFIG *config = (SD_BLOCK_CONFIG*)context;

    return config->BlockDeviceInformation->MaxBlockErase_uSec;
}

struct IBlockStorageDevice g_SD_BL_DeviceTable = 
{                          
	&SH7262_SD_BS_Driver::ChipInitialize,
	&SH7262_SD_BS_Driver::ChipUnInitialize,
	&SH7262_SD_BS_Driver::GetDeviceInfo,
	&SH7262_SD_BS_Driver::Read,
	&SH7262_SD_BS_Driver::Write,
	&SH7262_SD_BS_Driver::Memset,
	&SH7262_SD_BS_Driver::GetSectorMetadata,
	&SH7262_SD_BS_Driver::SetSectorMetadata,
	&SH7262_SD_BS_Driver::IsBlockErased,
	&SH7262_SD_BS_Driver::EraseBlock,
	&SH7262_SD_BS_Driver::SetPowerState,
	&SH7262_SD_BS_Driver::MaxSectorWrite_uSec,
	&SH7262_SD_BS_Driver::MaxBlockErase_uSec,
};