/*-----------------------------------------------------------------------*/
/* MMC/SDC (in SPI mode) control module  (C)ChaN, 2007                   */
/*-----------------------------------------------------------------------*/
/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros         */
/* are platform dependent.                                               */
/*-----------------------------------------------------------------------*/

/*
 * This file was modified from a sample available from the FatFs
 * web site. It was modified to work with an DK-TM4C129X development
 * board.
 */

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/rom.h"
#include "driverlib/ssi.h"
#include "driverlib/sysctl.h"
#include "third/fatfs/diskio.h"

/* Definitions for MMC/SDC command */
#define CMD0    (0x40+0)    /* GO_IDLE_STATE */
#define CMD1    (0x40+1)    /* SEND_OP_COND */
#define CMD8    (0x40+8)    /* SEND_IF_COND */
#define CMD9    (0x40+9)    /* SEND_CSD */
#define CMD10    (0x40+10)    /* SEND_CID */
#define CMD12    (0x40+12)    /* STOP_TRANSMISSION */
#define CMD16    (0x40+16)    /* SET_BLOCKLEN */
#define CMD17    (0x40+17)    /* READ_SINGLE_BLOCK */
#define CMD18    (0x40+18)    /* READ_MULTIPLE_BLOCK */
#define CMD23    (0x40+23)    /* SET_BLOCK_COUNT */
#define CMD24    (0x40+24)    /* WRITE_BLOCK */
#define CMD25    (0x40+25)    /* WRITE_MULTIPLE_BLOCK */
#define CMD41    (0x40+41)    /* SEND_OP_COND (ACMD) */
#define CMD55    (0x40+55)    /* APP_CMD */
#define CMD58    (0x40+58)    /* READ_OCR */

/* Peripheral definitions for DK-TM4C129X board */
/* SSI port */
#define SDC_SSI_BASE            SSI1_BASE
#define SDC_SSI_SYSCTL_PERIPH   SYSCTL_PERIPH_SSI1

/* GPIO for SSI pins */
/* CLK pin */
#define SDC_SSI_CLK_GPIO_PORT_BASE   GPIO_PORTB_BASE
#define SDC_SSI_CLK             GPIO_PIN_5
/* TX pin */
#define SDC_SSI_TX_GPIO_PORT_BASE   GPIO_PORTE_BASE
#define SDC_SSI_TX              GPIO_PIN_4
/* RX pin */
#define SDC_SSI_RX_GPIO_PORT_BASE   GPIO_PORTE_BASE
#define SDC_SSI_RX              GPIO_PIN_5
/* CS pin */
#define SDC_SSI_FSS_GPIO_PORT_BASE   GPIO_PORTP_BASE
#define SDC_SSI_FSS             GPIO_PIN_6

#define	CLOCK_12M5HZ			(12500000)				// SDカードの限界？
#define	SD_CLOCK					(CLOCK_12M5HZ >> 1)

/* must be supplied by the application */
extern uint32_t g_ui32SysClock;

//extern	void	Wait1usec(uint32_t SleepTime);

// SDRAM FatFs用4kバッファー用関数
extern	bool	SDRAM_FatBufClr(void);													// FatFs用バッファーのクリア
extern	bool	SDRAM_FatBufSave(uint8_t* pbDat);								// 4kバッファーへのセーブ
extern	bool	SDRAM_FatBufLoad(uint8_t* pbDat);								// 4kバッファーからのロード
extern	bool	SDRAM_FatBufByteSave(uint16_t wStart, uint16_t wLength, uint8_t* pbDat);	// 指定バイト数のセーブ
extern	uint16_t*	GetSDRAMFatBufAddress(void);

// Flash部分＋SDRAM用関数
// 2016/01/06 コメントアウト
//extern	void SPIFlashPageProgramSdRam(uint32_t ui32Base,uint32_t ui32Addr,const uint16_t *pui16Data,uint32_t ui32Count);
//extern	void SPIFlashQuadReadSdRam(uint32_t ui32Base,uint32_t ui32Addr,uint16_t *pui16Data,uint32_t ui32Count);


#define	EXFLASH_CSA_LOW		(ROM_GPIOPinWrite(GPIO_PORTE_BASE,GPIO_PIN_6,(uint8_t)~(GPIO_PIN_6)))
#define	EXFLASH_CSA_HIGH	(ROM_GPIOPinWrite(GPIO_PORTE_BASE,GPIO_PIN_6,GPIO_PIN_6))
#define	EXFLASH_CSB_LOW		(ROM_GPIOPinWrite(GPIO_PORTE_BASE,GPIO_PIN_7,(uint8_t)~(GPIO_PIN_7)))
#define	EXFLASH_CSB_HIGH	(ROM_GPIOPinWrite(GPIO_PORTE_BASE,GPIO_PIN_7,GPIO_PIN_7))

#define	SDCARD_CS_LOW			(ROM_GPIOPinWrite(GPIO_PORTP_BASE,GPIO_PIN_6,(uint8_t)~(GPIO_PIN_6)))
#define	SDCARD_CS_HIGH		(ROM_GPIOPinWrite(GPIO_PORTP_BASE,GPIO_PIN_6,GPIO_PIN_6))

#define	EXFLASH_SIZE			(512 * 1024 * 1024 / 8)						// フラッシュのサイズ半分

// Flash全体で128MB、AとBの半分で64MB
// 512Byte が 131072 セクター数分
// 256KByte 消去用セクターが 256 セクターある
// 今から書き込むセクターが、この消去用セクターのどこに含まれるのか？
// 256KByte には 512Byte × 512 含まれる
// 0xFF = 255 / 0x1FF = 511
/* 1セクター 512 Byte の場合 */
#define	EXFLASH_SECTOR_SIZE			(512)						// 1セクターのバイト数
#define	EXFLASH_SECTOR_COUNT		(131072)				// 64MBの中には n 個のセクターがある
#define	EXFLASH_BLOCK_SIZE			(512)						// フラッシュのブロックサイズ / セクターサイズ すなわち 256 * 1024 / 512 = 512
#define	EXFLASH_BLOCK_SIZE_EXP	(9)							// 512 = 2^9
#define	EXFLASH_PAGE_SIZE				(256 * 1024)		// 消去時のサイズ
/**/
/* 1セクター 8192 Byte の場合
#define	EXFLASH_SECTOR_SIZE			(8192)					// 1セクターのバイト数
#define	EXFLASH_SECTOR_COUNT		(8192)					// 64MBの中には n 個のセクターがある
#define	EXFLASH_BLOCK_SIZE			(32)						// フラッシュのブロックサイズ / セクターサイズ すなわち 256 * 1024 / 8192 = 32
#define	EXFLASH_BLOCK_SIZE_EXP	(5)							// 32 = 2^5
#define	EXFLASH_PAGE_SIZE				(256 * 1024)		// 消去時のサイズ
*/
/* 1セクター 4096 Byte の場合
#define	EXFLASH_SECTOR_SIZE			(4096)					// 1セクターのバイト数
#define	EXFLASH_SECTOR_COUNT		(16384)					// 64MBの中には n 個のセクターがある
#define	EXFLASH_BLOCK_SIZE			(64)						// フラッシュのブロックサイズ / セクターサイズ すなわち 256 * 1024 / 8192 = 32
#define	EXFLASH_BLOCK_SIZE_EXP	(6)							// 64 = 2^6
#define	EXFLASH_PAGE_SIZE				(256 * 1024)		// 消去時のサイズ
*/


/* asserts the CS pin to the card */
static	void	SELECT(uint8_t Drive){
	switch(Drive){
	case DRV_SDRAM:
		SDCARD_CS_HIGH;
		EXFLASH_CSA_HIGH;
		EXFLASH_CSB_HIGH;
		break;
	case DRV_FLS_A:				// チップセレクト（Flash）
		SDCARD_CS_HIGH;
		EXFLASH_CSA_LOW;
		EXFLASH_CSB_HIGH;
		// 40nsecのWaitが必要 120MHz -> 8nsec
		asm("nop;nop;nop;nop;nop");
		break;
	case DRV_FLS_B:				// チップセレクト（Flash）
		SDCARD_CS_HIGH;
		EXFLASH_CSA_HIGH;
		EXFLASH_CSB_LOW;
		// 40nsecのWaitが必要 120MHz -> 8nsec
		asm("nop;nop;nop;nop;nop");
		break;
	case DRV_SDCARD:					// チップセレクト（SD）
		SDCARD_CS_LOW;
		EXFLASH_CSA_HIGH;
		EXFLASH_CSB_HIGH;
		break;
	}
	return;
}

/* de-asserts the CS pin to the card */
static	void	DESELECT(void){
	SDCARD_CS_HIGH;
	EXFLASH_CSA_HIGH;
	EXFLASH_CSB_HIGH;
	// 40nsecのWaitが必要
}

/*--------------------------------------------------------------------------

   Module Private Functions

---------------------------------------------------------------------------*/

static volatile
DSTATUS Stat = STA_NOINIT;    /* Disk status */

static volatile
FS_BYTE Timer1, Timer2;    /* 100Hz decrement timer */

static
FS_BYTE CardType;            /* b0:MMC, b1:SDC, b2:Block addressing */

static
FS_BYTE PowerFlag = 0;     /* indicates if "power" is on */

/*-----------------------------------------------------------------------*/
/* Transmit a byte to MMC via SPI  (Platform dependent)                  */
/*-----------------------------------------------------------------------*/

static
void xmit_spi(FS_BYTE dat)
{
    uint32_t ui32RcvDat;

    ROM_SSIDataPut(SDC_SSI_BASE, dat); /* Write the data to the tx fifo */

    ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* flush data read during the write */
}


/*-----------------------------------------------------------------------*/
/* Receive a byte from MMC via SPI  (Platform dependent)                 */
/*-----------------------------------------------------------------------*/

static
FS_BYTE rcvr_spi (void)
{
    uint32_t ui32RcvDat;

    ROM_SSIDataPut(SDC_SSI_BASE, 0xFF); /* write dummy data */

    ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* read data frm rx fifo */

    return (FS_BYTE)ui32RcvDat;
}


static
void rcvr_spi_m (FS_BYTE *dst)
{
    *dst = rcvr_spi();
}

/*-----------------------------------------------------------------------*/
/* Wait for card ready                                                   */
/*-----------------------------------------------------------------------*/

static
FS_BYTE wait_ready (void)
{
    FS_BYTE res;


    Timer2 = 50;    /* Wait for ready in timeout of 500ms */
    rcvr_spi();
    do
        res = rcvr_spi();
    while ((res != 0xFF) && Timer2);

    return res;
}

/*-----------------------------------------------------------------------*/
/* Send 80 or so clock transitions with CS and DI held high. This is     */
/* required after card power up to get it into SPI mode                  */
/*-----------------------------------------------------------------------*/
static
void send_initial_clock_train(void)
{
    unsigned int i;
    uint32_t ui32Dat;

    /* Ensure CS is held high. */
    DESELECT();

    /* Switch the SSI TX line to a GPIO and drive it high too. */
    ROM_GPIOPinTypeGPIOOutput(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX);
    ROM_GPIOPinWrite(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX, SDC_SSI_TX);

    /* Send 10 bytes over the SSI. This causes the clock to wiggle the */
    /* required number of times. */
    for(i = 0 ; i < 10 ; i++)
    {
        /* Write DUMMY data. SSIDataPut() waits until there is room in the */
        /* FIFO. */
        ROM_SSIDataPut(SDC_SSI_BASE, 0xFF);

        /* Flush data read during data write. */
        ROM_SSIDataGet(SDC_SSI_BASE, &ui32Dat);
    }

    /* Revert to hardware control of the SSI TX line. */
    ROM_GPIOPinTypeSSI(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX);
}

/*-----------------------------------------------------------------------*/
/* Power Control  (Platform dependent)                                   */
/*-----------------------------------------------------------------------*/
/* When the target system does not support socket power control, there   */
/* is nothing to do in these functions and chk_power always returns 1.   */

static
void power_on (void)
{
    /*
     * This doesn't really turn the power on, but initializes the
     * SSI port and pins needed to talk to the card.
     */
#if 0		//[[[ 2015/12/02 comment out
    /* Enable the peripherals used to drive the SDC on SSI */
    ROM_SysCtlPeripheralEnable(SDC_SSI_SYSCTL_PERIPH);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);

    /*
     * Configure the appropriate pins to be SSI instead of GPIO. The FSS (CS)
     * signal is directly driven to ensure that we can hold it low through a
     * complete transaction with the SD card.
     */
    ROM_GPIOPinTypeSSI(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX);
    ROM_GPIOPinTypeSSI(SDC_SSI_RX_GPIO_PORT_BASE, SDC_SSI_RX);
    ROM_GPIOPinTypeSSI(SDC_SSI_CLK_GPIO_PORT_BASE, SDC_SSI_CLK);
    ROM_GPIOPinTypeGPIOOutput(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS);
#endif		//]]]

    /*
     * Set the SSI output pins to 4MA drive strength and engage the
     * pull-up on the receive line.
     */
    ROM_GPIOPadConfigSet(SDC_SSI_RX_GPIO_PORT_BASE, SDC_SSI_RX,
                         GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU);
    ROM_GPIOPadConfigSet(SDC_SSI_CLK_GPIO_PORT_BASE, SDC_SSI_CLK,
                         GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    ROM_GPIOPadConfigSet(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX,
                         GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    ROM_GPIOPadConfigSet(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS,
                         GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);

#if 0		//[[[
    /* Configure the SSI3 port */
    ROM_SSIConfigSetExpClk(SDC_SSI_BASE, g_ui32SysClock,
                           SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8);
    ROM_SSIEnable(SDC_SSI_BASE);
#endif		//]]]
    /* Set DI and CS high and apply more than 74 pulses to SCLK for the card */
    /* to be able to accept a native command. */
    send_initial_clock_train();

    PowerFlag = 1;
}

// set the SSI speed to the max setting
static
void set_max_speed(void)
{
    unsigned long i;

    /* Disable the SSI */
    ROM_SSIDisable(SDC_SSI_BASE);

    /* Set the maximum speed as half the system clock, with a max of 12.5 MHz. */
    i = g_ui32SysClock / 2;
    if(i > (SD_CLOCK))
    {
        i = (SD_CLOCK);
    }

    /* Configure the SSI0 port to run at 12.5MHz */
    ROM_SSIConfigSetExpClk(SDC_SSI_BASE, g_ui32SysClock,
                           SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, i, 8);

    /* Enable the SSI */
    ROM_SSIEnable(SDC_SSI_BASE);
}

static
void power_off (void)
{
    PowerFlag = 0;
}

static
int chk_power(void)        /* Socket power state: 0=off, 1=on */
{
    return PowerFlag;
}



/*-----------------------------------------------------------------------*/
/* Receive a data packet from MMC                                        */
/*-----------------------------------------------------------------------*/

static
FS_BOOL rcvr_datablock (
    FS_BYTE *buff,            /* Data buffer to store received data */
    FS_UINT btr            /* Byte count (must be even number) */
)
{
    FS_BYTE token;


    Timer1 = 100;
    do {                            /* Wait for data packet in timeout of 100ms */
        token = rcvr_spi();
    } while ((token == 0xFF) && Timer1);
    if(token != 0xFE) return FS_FALSE;    /* If not valid data token, retutn with error */

    do {                            /* Receive the data block into buffer */
        rcvr_spi_m(buff++);
        rcvr_spi_m(buff++);
    } while (btr -= 2);
    rcvr_spi();                        /* Discard CRC */
    rcvr_spi();

    return FS_TRUE;                    /* Return with success */
}



/*-----------------------------------------------------------------------*/
/* Send a data packet to MMC                                             */
/*-----------------------------------------------------------------------*/

#if _READONLY == 0
static
FS_BOOL xmit_datablock (
    const FS_BYTE *buff,    /* 512 byte data block to be transmitted */
    FS_BYTE token            /* Data/Stop token */
)
{
    FS_BYTE resp, wc;


    if (wait_ready() != 0xFF) return FS_FALSE;

    xmit_spi(token);                    /* Xmit data token */
    if (token != 0xFD) {    /* Is data token */
        wc = 0;
        do {                            /* Xmit the 512 byte data block to MMC */
            xmit_spi(*buff++);
            xmit_spi(*buff++);
        } while (--wc);
        xmit_spi(0xFF);                    /* CRC (Dummy) */
        xmit_spi(0xFF);
        resp = rcvr_spi();                /* Reveive data response */
        if ((resp & 0x1F) != 0x05)        /* If not accepted, return with error */
            return FS_FALSE;
    }

    return FS_TRUE;
}
#endif /* _READONLY */



/*-----------------------------------------------------------------------*/
/* Send a command packet to MMC                                          */
/*-----------------------------------------------------------------------*/

static
FS_BYTE send_cmd (
    FS_BYTE cmd,        /* Command byte */
    FS_DWORD arg        /* Argument */
)
{
    FS_BYTE n, res;


    if (wait_ready() != 0xFF) return 0xFF;

    /* Send command packet */
    xmit_spi(cmd);                        /* Command */
    xmit_spi((FS_BYTE)(arg >> 24));        /* Argument[31..24] */
    xmit_spi((FS_BYTE)(arg >> 16));        /* Argument[23..16] */
    xmit_spi((FS_BYTE)(arg >> 8));            /* Argument[15..8] */
    xmit_spi((FS_BYTE)arg);                /* Argument[7..0] */
    n = 0xff;
    if (cmd == CMD0) n = 0x95;            /* CRC for CMD0(0) */
    if (cmd == CMD8) n = 0x87;            /* CRC for CMD8(0x1AA) */
    xmit_spi(n);

    /* Receive command response */
    if (cmd == CMD12) rcvr_spi();        /* Skip a stuff byte when stop reading */
    n = 10;                                /* Wait for a valid response in timeout of 10 attempts */
    do
        res = rcvr_spi();
    while ((res & 0x80) && --n);

    return res;            /* Return with the response value */
}

/*-----------------------------------------------------------------------*
 * Send the special command used to terminate a multi-sector read.
 *
 * This is the only command which can be sent while the SDCard is sending
 * data. The SDCard spec indicates that the data transfer will stop 2 bytes
 * after the 6 byte CMD12 command is sent and that the card will then send
 * 0xFF for between 2 and 6 more bytes before the R1 response byte.  This
 * response will be followed by another 0xFF byte.  In testing, however, it
 * seems that some cards don't send the 2 to 6 0xFF bytes between the end of
 * data transmission and the response code.  This function, therefore, merely
 * reads 10 bytes and, if the last one read is 0xFF, returns the value of the
 * latest non-0xFF byte as the response code.
 *
 *-----------------------------------------------------------------------*/

static
FS_BYTE send_cmd12 (void)
{
    FS_BYTE n, res, val;

    /* For CMD12, we don't wait for the card to be idle before we send
     * the new command.
     */

    /* Send command packet - the argument for CMD12 is ignored. */
    xmit_spi(CMD12);
    xmit_spi(0);
    xmit_spi(0);
    xmit_spi(0);
    xmit_spi(0);
    xmit_spi(0);

    /* Read up to 10 bytes from the card, remembering the value read if it's
       not 0xFF */
    for(n = 0; n < 10; n++)
    {
        val = rcvr_spi();
        if(val != 0xFF)
        {
            res = val;
        }
    }

    return res;            /* Return with the response value */
}

/*--------------------------------------------------------------------------

   Public Functions

---------------------------------------------------------------------------*/


/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive (SD)                                            */
/*-----------------------------------------------------------------------*/
DSTATUS	disk_init_sd(void){
    FS_BYTE n, ty, ocr[4];

    if (Stat & STA_NODISK) return Stat;    /* No card in the socket */

    power_on();                            /* Force socket power on */
    send_initial_clock_train();            /* Ensure the card is in SPI mode */

    SELECT(DRV_SDCARD);                /* CS = L */
    ty = 0;
    if (send_cmd(CMD0, 0) == 1) {            /* Enter Idle state */
        Timer1 = 100;                        /* Initialization timeout of 1000 msec */
        if (send_cmd(CMD8, 0x1AA) == 1) {    /* SDC Ver2+ */
            for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
            if (ocr[2] == 0x01 && ocr[3] == 0xAA) {    /* The card can work at vdd range of 2.7-3.6V */
                do {
                    if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0)    break;    /* ACMD41 with HCS bit */
                } while (Timer1);
                if (Timer1 && send_cmd(CMD58, 0) == 0) {    /* Check CCS bit */
                    for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
                    ty = (ocr[0] & 0x40) ? 6 : 2;
                }
            }
        } else {                            /* SDC Ver1 or MMC */
            ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1;    /* SDC : MMC */
            do {
                if (ty == 2) {
                    if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break;    /* ACMD41 */
                } else {
                    if (send_cmd(CMD1, 0) == 0) break;                                /* CMD1 */
                }
            } while (Timer1);
            if (!Timer1 || send_cmd(CMD16, 512) != 0)    /* Select R/W block length */
                ty = 0;
        }
    }
    CardType = ty;
    DESELECT();            /* CS = H */
    rcvr_spi();            /* Idle (Release DO) */

    if (ty) {            /* Initialization succeded */
        Stat &= ~STA_NOINIT;        /* Clear STA_NOINIT */
        set_max_speed();
    } else {            /* Initialization failed */
        power_off();
    }
		return	Stat;
}

//-----------------------------------------------------------------------
// Initialize Disk Drive (Flash)
//-----------------------------------------------------------------------
DSTATUS	disk_init_flash(void){
	SELECT(DRV_SDRAM);
	SDRAM_FatBufClr();																				// 使用するバッファー領域のクリア
	DESELECT();

	// フラッシュ関連の初期化は他で済ませておくこと！（ポートの初期化等）

	return 0x00;
}

/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive                                                 */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
    FS_BYTE drv        /* Physical drive nmuber (0) */
)
{
	DSTATUS	sTmp;

	switch(drv){
	case DRV_SDRAM:
		break;
	case DRV_FLS_A:
	case DRV_FLS_B:
		sTmp = disk_init_flash();
		break;
	case DRV_SDCARD:
		sTmp = disk_init_sd();
		break;
	default:
		sTmp = STA_NOINIT;
		break;
	}
	return sTmp;
}



/*-----------------------------------------------------------------------*/
/* Get Disk Status                                                       */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
    FS_BYTE drv        /* Physical drive nmuber (0) */
)
{
	DSTATUS	sTmp;

	switch(drv){
	case DRV_SDRAM:
		sTmp = 0;																								// 初期化されていない場合、どうするか？
		break;
	case DRV_FLS_A:
	case DRV_FLS_B:
		sTmp = 0;																								// 初期化されていない場合、どうするか？
		break;
	case DRV_SDCARD:
		sTmp = Stat;
		break;
	default:
		sTmp = STA_NOINIT;
		break;
	}
	return sTmp;
}


/*-----------------------------------------------------------------------*/
/* Read Sector(s) (SD)                                                   */
/*-----------------------------------------------------------------------*/
DRESULT disk_read_sd (
    FS_BYTE *buff,            /* Pointer to the data buffer to store read data */
    FS_DWORD sector,        /* Start sector number (LBA) */
    FS_BYTE count            /* Sector count (1..255) */
){
	FS_BYTE	bCountTmp = count;
    if (Stat & STA_NOINIT) return RES_NOTRDY;

    if (!(CardType & 4)) sector *= 512;    /* Convert to byte address if needed */

    SELECT(DRV_SDCARD);            /* CS = L */

    if (bCountTmp == 1) {    /* Single block read */
        if ((send_cmd(CMD17, sector) == 0)    /* READ_SINGLE_BLOCK */
            && rcvr_datablock(buff, 512))
            bCountTmp = 0;
    }
    else {                /* Multiple block read */
        if (send_cmd(CMD18, sector) == 0) {    /* READ_MULTIPLE_BLOCK */
            do {
                if (!rcvr_datablock(buff, 512)) break;
                buff += 512;
            } while (--bCountTmp);
            send_cmd12();                /* STOP_TRANSMISSION */
        }
    }

    DESELECT();            /* CS = H */
    rcvr_spi();            /* Idle (Release DO) */

   return bCountTmp ? RES_ERROR : RES_OK;
}

//-----------------------------------------------------------------------
// Read Sector(s) (Flash)
//-----------------------------------------------------------------------
DRESULT disk_read_flash (
	FS_BYTE *buff,							// Pointer to the data buffer to store read data
	FS_DWORD sector,						// Start sector number (LBA)
	FS_BYTE count,							// Sector count (1..255)
	FS_BYTE drive								// DRV_FLS_A or DRV_FLS_B
){
	
	// サイズ超過
	if( (sector * EXFLASH_SECTOR_SIZE) >= EXFLASH_SIZE ){	return RES_PARERR;	}
	if( ( (sector + count) * EXFLASH_SECTOR_SIZE) > EXFLASH_SIZE ){	return RES_PARERR;	}

	SELECT(drive);
	//SPIFlashQuadReadSdRam(SDC_SSI_BASE,sector * 512,(FS_WORD*)buff,count * 512);
	ROM_SPIFlashQuadRead(SDC_SSI_BASE,sector * 512,(uint8_t*)buff,count * 512);
	DESELECT();

	return RES_OK;
}

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
    FS_BYTE drv,            /* Physical drive nmuber (0) */
    FS_BYTE *buff,            /* Pointer to the data buffer to store read data */
    FS_DWORD sector,        /* Start sector number (LBA) */
    FS_BYTE count            /* Sector count (1..255) */
){
	DRESULT dTmp;

	if (!count) return RES_PARERR;

	switch(drv){
	case DRV_SDRAM:
		break;
	case DRV_FLS_A:
	case DRV_FLS_B:
		dTmp = disk_read_flash(buff,sector,count,drv);
		break;
	case DRV_SDCARD:
		dTmp = disk_read_sd(buff,sector,count);
		break;
	default:
		dTmp = RES_PARERR;
		break;
	}
	return dTmp;
}



/*-----------------------------------------------------------------------*/
/* Write Sector(s) (SD)                                                  */
/*-----------------------------------------------------------------------*/
#if _READONLY == 0
DRESULT disk_write_sd (
    const FS_BYTE *buff,    /* Pointer to the data to be written */
    FS_DWORD sector,        /* Start sector number (LBA) */
    FS_BYTE count            /* Sector count (1..255) */
)
{
	FS_BYTE	bCountTmp = count;

    if (Stat & STA_NOINIT) return RES_NOTRDY;
    if (Stat & STA_PROTECT) return RES_WRPRT;

    if (!(CardType & 4)) sector *= 512;    /* Convert to byte address if needed */

    SELECT(DRV_SDCARD);            /* CS = L */

    if (bCountTmp == 1) {    /* Single block write */
        if ((send_cmd(CMD24, sector) == 0)    /* WRITE_BLOCK */
            && xmit_datablock(buff, 0xFE))
            bCountTmp = 0;
    }
    else {                /* Multiple block write */
        if (CardType & 2) {
            send_cmd(CMD55, 0); send_cmd(CMD23, bCountTmp);    /* ACMD23 */
        }
        if (send_cmd(CMD25, sector) == 0) {    /* WRITE_MULTIPLE_BLOCK */
            do {
                if (!xmit_datablock(buff, 0xFC)) break;
                buff += 512;
            } while (--bCountTmp);
            if (!xmit_datablock(0, 0xFD))    /* STOP_TRAN token */
                bCountTmp = 1;
        }
    }

    DESELECT();            /* CS = H */
    rcvr_spi();            /* Idle (Release DO) */

    return bCountTmp ? RES_ERROR : RES_OK;
}
#endif /* _READONLY */

//-----------------------------------------------------------------------
// Write Sector(s) (Flash)
//-----------------------------------------------------------------------
#if _READONLY == 0
DRESULT disk_write_flash (
	const FS_BYTE *buff,				// Pointer to the data to be written
	FS_DWORD sector,						// Start sector number (LBA)
	FS_BYTE count,							// Sector count (1..255)
	FS_BYTE drive								// DRV_FLS_A or DRV_FLS_B
){
	FS_DWORD	dwFromSector = sector;				// 開始セクター
	FS_DWORD	dwToSector = sector + count;	// 終了セクター
	FS_DWORD	dwStartPage = dwFromSector >> EXFLASH_BLOCK_SIZE_EXP;		// 開始ページ（消去対象の開始)
	FS_DWORD	dwLastPage = dwToSector >> EXFLASH_BLOCK_SIZE_EXP;			// 終了ページ（消去対象の終了）
	FS_DWORD	dwSectorTmp = dwFromSector;
	FS_DWORD	dwSectorCount;
	FS_DWORD	dwBufCount;
	
	FS_DWORD	dwPageTmp;
	FS_DWORD	dwReadSize;
	FS_DWORD	dwPosit = 0;
	uint16_t	wTmp;

	uint16_t*	pwRamTmp = (uint16_t*)GetSDRAMFatBufAddress();					// Fat用SDRAMの先頭アドレス

	// サイズ超過
	if( (sector * EXFLASH_SECTOR_SIZE) >= EXFLASH_SIZE ){	return RES_PARERR;	}
	if( ( (sector + count) * EXFLASH_SECTOR_SIZE) > EXFLASH_SIZE ){	return RES_PARERR;	}

	dwSectorCount = 0;
	dwBufCount = 0;
	for(dwPageTmp = dwStartPage;dwPageTmp <= dwLastPage;dwPageTmp++){
		// 複数ページにまたがる場合も含め、ページ数分、ループを行う
		dwSectorTmp = dwSectorTmp - dwPageTmp * EXFLASH_BLOCK_SIZE;		// 読み込んだデータの書き換えを開始するセクター
		
		// 該当ページのデータ前半を読み込む
		dwReadSize = dwSectorTmp * EXFLASH_SECTOR_SIZE;
		if(dwReadSize > 0){
			SELECT(drive);
			//SPIFlashQuadReadSdRam(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE),pwRamTmp,dwReadSize);
			ROM_SPIFlashQuadRead(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE),(uint8_t*)pwRamTmp,dwReadSize);
			DESELECT();
			//asm("nop;nop;nop;nop;nop");		// 後の処理で十分時間が経過するので省略
		}
		
		// 書き込みデータをSDRAM領域にコピー
		for(dwPosit = dwReadSize;dwPosit < EXFLASH_PAGE_SIZE;dwPosit+=2){
			wTmp = ((uint16_t)(((uint16_t)(*buff) << 8) | (uint16_t)(*(buff+1))));
			*(pwRamTmp + (dwPosit >> 1)) = wTmp;
			buff += 2;
			dwBufCount += 2;
			if( (dwBufCount & (EXFLASH_SECTOR_SIZE - 1) ) == 0){
				dwSectorCount++;
			}
			if(dwSectorCount >= count){
				break;
			}
		}
		
		// 該当ページのデータ後半を読み込む
		dwReadSize = EXFLASH_PAGE_SIZE - dwPosit;
		if(dwReadSize > 0){
			SELECT(drive);
			//SPIFlashQuadReadSdRam(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE) + dwPosit,pwRamTmp + dwPosit,dwReadSize);
			ROM_SPIFlashQuadRead(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE) + dwPosit,(uint8_t*)(pwRamTmp + dwPosit),dwReadSize);
			DESELECT();
			asm("nop;nop;nop;nop;nop");
		}

		// 該当ページの消去
		SELECT(drive);
		ROM_SPIFlashWriteEnable(SDC_SSI_BASE);
		DESELECT();
		asm("nop;nop;nop;nop;nop");
		SELECT(drive);
		ROM_SPIFlashBlockErase64(SDC_SSI_BASE,dwPageTmp * EXFLASH_SECTOR_SIZE);
		DESELECT();
		asm("nop;nop;nop;nop;nop");
		SELECT(drive);
		ROM_SPIFlashWriteDisable(SDC_SSI_BASE);
		DESELECT();
		asm("nop;nop;nop;nop;nop");

		// 該当ページの書き込み
		SELECT(drive);
		ROM_SPIFlashWriteEnable(SDC_SSI_BASE);
		DESELECT();
		asm("nop;nop;nop;nop;nop");
		SELECT(drive);
		//SPIFlashPageProgramSdRam(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE),pwRamTmp,EXFLASH_PAGE_SIZE);
		ROM_SPIFlashPageProgram(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE),(uint8_t*)pwRamTmp,EXFLASH_PAGE_SIZE);
		DESELECT();
		asm("nop;nop;nop;nop;nop");
		SELECT(drive);
		ROM_SPIFlashWriteDisable(SDC_SSI_BASE);
		DESELECT();
		//asm("nop;nop;nop;nop;nop");		// 次の処理で十分にウェイトが発生する
	}
	return RES_OK;
}
#endif /* _READONLY */

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
#if _READONLY == 0
DRESULT disk_write (
    FS_BYTE drv,            /* Physical drive nmuber (0) */
    const FS_BYTE *buff,    /* Pointer to the data to be written */
    FS_DWORD sector,        /* Start sector number (LBA) */
    FS_BYTE count            /* Sector count (1..255) */
){
	DRESULT dTmp;

	if (!count) return RES_PARERR;

	switch(drv){
	case DRV_SDRAM:
		break;
	case DRV_FLS_A:
	case DRV_FLS_B:
		dTmp = disk_write_flash(buff,sector,count,drv);
		break;
	case DRV_SDCARD:
		dTmp = disk_write_sd(buff,sector,count);
		break;
	default:
		dTmp = RES_PARERR;
		break;
	}
	return dTmp;
}
#endif /* _READONLY */

//#if _USE_ERASE		//[[[
//-----------------------------------------------------------------------
// Erase Sector (flash)		A〜Bセクターを消す
//-----------------------------------------------------------------------
void erase_sector_flash(
	FS_DWORD	dwFromSector,			// 開始セクター
	FS_DWORD	dwToSector,				// 終了セクター
	FS_BYTE		drive						// ドライブ番号
){
	FS_DWORD	dwStartPage = dwFromSector >> EXFLASH_BLOCK_SIZE_EXP;		// 開始ページ（消去対象の開始)
	FS_DWORD	dwLastPage = dwToSector >> EXFLASH_BLOCK_SIZE_EXP;			// 終了ページ（消去対象の終了）
	FS_DWORD	dwSectorTmp = dwFromSector;
	
	FS_DWORD	dwPageTmp;
	FS_DWORD	dwSize[2];
	FS_DWORD	dwAddSize;

	uint16_t*	pwRamTmp = (uint16_t*)GetSDRAMFatBufAddress();					// Fat用SDRAMの先頭アドレス

	for(dwPageTmp = dwStartPage;dwPageTmp <= dwLastPage;dwPageTmp++){
		// 複数ページにまたがる場合も含め、ページ数分、ループを行う
		dwSectorTmp = dwSectorTmp - dwPageTmp * EXFLASH_BLOCK_SIZE;		// 読み込んだデータの書き換えを開始するセクター
		
		dwSize[0] = 0;
		dwSize[1] = 0;
		if(dwPageTmp == dwStartPage){
			// 該当ページのデータ前半を読み込む
			dwSize[0] = dwSectorTmp * EXFLASH_SECTOR_SIZE;
			if(dwSize[0] > 0){
				SELECT(drive);
				//SPIFlashQuadReadSdRam(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE),pwRamTmp,dwSize[0]);
				ROM_SPIFlashQuadRead(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE),(uint8_t*)pwRamTmp,dwSize[0]);
				DESELECT();
				asm("nop;nop;nop;nop;nop");
			}
		}

		dwAddSize = 0;
		if(dwPageTmp == dwLastPage){
			// 該当ページのデータ後半を読み込む
			dwAddSize = (dwToSector - (dwPageTmp * EXFLASH_BLOCK_SIZE) + 1) * EXFLASH_SECTOR_SIZE;
			dwSize[1] = EXFLASH_PAGE_SIZE - dwAddSize;
			if(dwSize[1] > 0){
				SELECT(drive);
				//SPIFlashQuadReadSdRam(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE) + dwAddSize,pwRamTmp + dwAddSize,dwSize[1]);
				ROM_SPIFlashQuadRead(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE) + dwAddSize,(uint8_t*)(pwRamTmp + dwAddSize),dwSize[1]);
				DESELECT();
				asm("nop;nop;nop;nop;nop");
			}
		}

		// 該当ページの消去
		SELECT(drive);
		ROM_SPIFlashWriteEnable(SDC_SSI_BASE);
		DESELECT();
		asm("nop;nop;nop;nop;nop");
		SELECT(drive);
		ROM_SPIFlashBlockErase64(SDC_SSI_BASE,dwPageTmp * EXFLASH_SECTOR_SIZE);
		DESELECT();
		asm("nop;nop;nop;nop;nop");
		SELECT(drive);
		ROM_SPIFlashWriteDisable(SDC_SSI_BASE);
		DESELECT();
		asm("nop;nop;nop;nop;nop");

		// 該当ページの書き込み
		if(dwSize[0] > 0){
			SELECT(drive);
			ROM_SPIFlashWriteEnable(SDC_SSI_BASE);
			DESELECT();
			asm("nop;nop;nop;nop;nop");
			SELECT(drive);
			//SPIFlashPageProgramSdRam(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE),pwRamTmp,dwSize[0]);
			ROM_SPIFlashPageProgram(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE),(uint8_t*)pwRamTmp,dwSize[0]);
			DESELECT();
			asm("nop;nop;nop;nop;nop");
			SELECT(drive);
			ROM_SPIFlashWriteDisable(SDC_SSI_BASE);
			DESELECT();
			asm("nop;nop;nop;nop;nop");
		}
		if(dwSize[1] > 0){
			SELECT(drive);
			ROM_SPIFlashWriteEnable(SDC_SSI_BASE);
			DESELECT();
			asm("nop;nop;nop;nop;nop");
			SELECT(drive);
			//SPIFlashPageProgramSdRam(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE) + dwAddSize,pwRamTmp + dwAddSize,dwSize[1]);
			ROM_SPIFlashPageProgram(SDC_SSI_BASE,(dwPageTmp * EXFLASH_SECTOR_SIZE) + dwAddSize,(uint8_t*)(pwRamTmp + dwAddSize),dwSize[1]);
			DESELECT();
			asm("nop;nop;nop;nop;nop");
			SELECT(drive);
			ROM_SPIFlashWriteDisable(SDC_SSI_BASE);
			DESELECT();
			asm("nop;nop;nop;nop;nop");
		}
	}
	return;
}
//#endif		//]]]

//-----------------------------------------------------------------------
// Miscellaneous Functions (flash)
//-----------------------------------------------------------------------
DRESULT disk_ioctl_flash (
	FS_BYTE ctrl,					// Control code
	void *buff,						// Buffer to send/receive control data
	FS_BYTE drive								// DRV_FLS_A or DRV_FLS_B
){
	DRESULT res = RES_OK;

	switch(ctrl){
	case CTRL_SYNC:
		break;
	case GET_SECTOR_SIZE:
		*((FS_WORD*)buff) = (FS_WORD)EXFLASH_SECTOR_SIZE;
		break;

	case GET_SECTOR_COUNT:
		*((FS_DWORD*)buff) = (FS_DWORD)EXFLASH_SECTOR_COUNT;
		break;

	case GET_BLOCK_SIZE:
		*((FS_DWORD*)buff) = (FS_DWORD)EXFLASH_BLOCK_SIZE;
		break;

	case CTRL_ERASE_SECTOR:
		erase_sector_flash(((FS_DWORD*)buff)[0], ((FS_DWORD*)buff)[1] , drive);		// A〜Bセクターを消す
		break;

	default:
		res = RES_PARERR;
		break;
	}

	return res;
}

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions (SD)                                          */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl_sd (
    FS_BYTE ctrl,        /* Control code */
    void *buff        /* Buffer to send/receive control data */
)
{
    DRESULT res;
    FS_BYTE n, csd[16], *ptr = buff;
    FS_WORD csize;

    res = RES_ERROR;

    if (ctrl == CTRL_POWER) {
        switch (*ptr) {
        case 0:        /* Sub control code == 0 (POWER_OFF) */
            if (chk_power())
                power_off();        /* Power off */
            res = RES_OK;
            break;
        case 1:        /* Sub control code == 1 (POWER_ON) */
            power_on();                /* Power on */
            res = RES_OK;
            break;
        case 2:        /* Sub control code == 2 (POWER_GET) */
            *(ptr+1) = (FS_BYTE)chk_power();
            res = RES_OK;
            break;
        default :
            res = RES_PARERR;
        }
    }
    else {
        if (Stat & STA_NOINIT) return RES_NOTRDY;

        SELECT(DRV_SDCARD);        /* CS = L */

        switch (ctrl) {
        case GET_SECTOR_COUNT :    /* Get number of sectors on the disk (FS_DWORD) */
            if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
                if ((csd[0] >> 6) == 1) {    /* SDC ver 2.00 */
                    csize = csd[9] + ((FS_WORD)csd[8] << 8) + 1;
                    *(FS_DWORD*)buff = (FS_DWORD)csize << 10;
                } else {                    /* MMC or SDC ver 1.XX */
                    n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
                    csize = (csd[8] >> 6) + ((FS_WORD)csd[7] << 2) + ((FS_WORD)(csd[6] & 3) << 10) + 1;
                    *(FS_DWORD*)buff = (FS_DWORD)csize << (n - 9);
                }
                res = RES_OK;
            }
            break;

        case GET_SECTOR_SIZE :    /* Get sectors on the disk (FS_WORD) */
            *(FS_WORD*)buff = 512;
            res = RES_OK;
            break;

        case CTRL_SYNC :    /* Make sure that data has been written */
            if (wait_ready() == 0xFF)
                res = RES_OK;
            break;

        case MMC_GET_CSD :    /* Receive CSD as a data block (16 bytes) */
            if (send_cmd(CMD9, 0) == 0        /* READ_CSD */
                && rcvr_datablock(ptr, 16))
                res = RES_OK;
            break;

        case MMC_GET_CID :    /* Receive CID as a data block (16 bytes) */
            if (send_cmd(CMD10, 0) == 0        /* READ_CID */
                && rcvr_datablock(ptr, 16))
                res = RES_OK;
            break;

        case MMC_GET_OCR :    /* Receive OCR as an R3 resp (4 bytes) */
            if (send_cmd(CMD58, 0) == 0) {    /* READ_OCR */
                for (n = 0; n < 4; n++)
                    *ptr++ = rcvr_spi();
                res = RES_OK;
            }

//        case MMC_GET_TYPE :    /* Get card type flags (1 byte) */
//            *ptr = CardType;
//            res = RES_OK;
//            break;

        default:
            res = RES_PARERR;
        }

        DESELECT();            /* CS = H */
        rcvr_spi();            /* Idle (Release DO) */
    }

    return res;
}

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
    FS_BYTE drv,        /* Physical drive nmuber (0) */
    FS_BYTE ctrl,        /* Control code */
    void *buff        /* Buffer to send/receive control data */
){
	DRESULT res;

	switch(drv){
	case DRV_SDRAM:
		break;
	case DRV_FLS_A:
	case DRV_FLS_B:
		res = disk_ioctl_flash(ctrl,buff,drv);
		break;
	case DRV_SDCARD:
		res = disk_ioctl_sd(ctrl,buff);
		break;
	default:
		res = RES_PARERR;
		break;
	}
	return res;
}



/*-----------------------------------------------------------------------*/
/* Device Timer Interrupt Procedure  (Platform dependent)                */
/*-----------------------------------------------------------------------*/
/* This function must be called in period of 10ms                        */

void disk_timerproc (void)
{
//    FS_BYTE n, s;
    FS_BYTE n;


    n = Timer1;                        /* 100Hz decrement timer */
    if (n) Timer1 = --n;
    n = Timer2;
    if (n) Timer2 = --n;

}

/*---------------------------------------------------------*/
/* User Provided Timer Function for FatFs module           */
/*---------------------------------------------------------*/
/* This is a real time clock service to be called from     */
/* FatFs module. Any valid time must be returned even if   */
/* the system does not support a real time clock.          */

FS_DWORD get_fattime (void)
{

    return    ((2007UL-1980) << 25)    // Year = 2007
            | (6UL << 21)            // Month = June
            | (5UL << 16)            // Day = 5
            | (11U << 11)            // Hour = 11
            | (38U << 5)            // Min = 38
            | (0U >> 1)                // Sec = 0
            ;

}
