/*------------------------------------------------------------------------*/
/* FRK-RX62N: MMCv3/SDv1/SDv2 (SPIモード) 制御モジュール                   */
/*------------------------------------------------------------------------*/
/*
/  Copyright (C) 2011, ChaN, all right reserved.
/
/ * This software is a free software and there is NO WARRANTY.
/ * No restriction on use. You can use, modify and redistribute it for
/   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
/ * Redistributions of source code must retain the above copyright notice.
/
/-------------------------------------------------------------------------*/

#define RSPI_CH	0	/* 使用するSPIポートの選択: 0:RSPIA-A, 1:RSPIB-A, 10:RSPIA-B, 11:RSPIB-B */	

#define	CS_LOW()	{PORTC.DR.BIT.B4 = 0;}	/* CS#をLにする */
#define	CS_HIGH()	{PORTC.DR.BIT.B4 = 1;}	/* CS#をHにする */
#define	INS			(!PORT0.PORT.BIT.B7)	/* カード検出(真:あり, 偽:なし) */
#define	WP			0 //(PORT0.PORT.BIT.B5)	/* 書き込み禁止(真:禁止, 偽:許可) */
#define CS_INIT()	{	/* CS#,INS#,WP 各端子の初期化 */\
	PORTC.DR.BIT.B4 = 1; /* CS#=OUT */	\
	PORTC.DDR.BIT.B4 = 1; /* CS#=H */	\
	PORT0.ICR.BIT.B7 = 1; /* INS#=IN */	\
	PORT0.ICR.BIT.B5 = 1; /* WP=IN */	\
}

#define PCLK		48000000UL	/* PCLKの周波数[Hz] */
#define CLK_FAST	24000000UL	/* SCLKの周波数[Hz] (動作中) */
#define	CLK_SLOW	400000UL	/* SCLKの周波数[Hz] (初期化中) */

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


#if RSPI_CH == 0	/* RSPIA-A */
#define RSPI	RSPI0
#define	SPI_ATTACH() {			\
	PORTC.ICR.BIT.B7 = 1;		\
	IOPORT.PFGSPI.BYTE = 0x0E;	\
	MSTP_RSPI0 = 0;				\
}

#elif RSPI_CH == 10	/* RSPIA-B */
#define RSPI	RSPI0
#define	SPI_ATTACH() {			\
	PORTA.ICR.BIT.B7 = 1;		\
	IOPORT.PFGSPI.BYTE = 0x0F;	\
	MSTP_RSPI0 = 0;				\
}

#elif RSPI_CH == 1	/* RSPIB-A */
#define RSPI	RSPI1
#define	SPI_ATTACH() {			\
	PORT3.ICR.BIT.B0 = 1;		\
	IOPORT.PFHSPI.BYTE = 0x0E;	\
	MSTP_RSPI1 = 0;				\
}

#elif RSPI_CH == 11	/* RSPIB-B */
#define RSPI	RSPI1
#define	SPI_ATTACH() {			\
	PORTE.ICR.BIT.B7 = 1;		\
	IOPORT.PFHSPI.BYTE = 0x0F;	\
	MSTP_RSPI1 = 0;				\
}

#endif

#define FCLK_FAST() {					\
	RSPI.SPCR.BIT.SPE = 0;				\
	RSPI.SPBR.BYTE = PCLK/2/CLK_FAST-1;	\
	RSPI.SPCR.BIT.SPE = 1;				\
}

#ifdef __LIT
#define LDDW(x) revl(x)	/* リトルエンディアンのときは32ビットフレームのバイトスワップする */
#else
#define LDDW(x) x		/* ビッグエンディアンのときは32ビットフレームのバイトスワップをしない */
#endif



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

   プライベート関数・ワークエリア

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

#include <machine.h>
#include "iodefine.h"
#include "diskio.h"


/* MMC/SD command */
#define CMD0	(0)			/* GO_IDLE_STATE */
#define CMD1	(1)			/* SEND_OP_COND (MMC) */
#define	ACMD41	(0x80+41)	/* SEND_OP_COND (SDC) */
#define CMD8	(8)			/* SEND_IF_COND */
#define CMD9	(9)			/* SEND_CSD */
#define CMD10	(10)		/* SEND_CID */
#define CMD12	(12)		/* STOP_TRANSMISSION */
#define ACMD13	(0x80+13)	/* SD_STATUS (SDC) */
#define CMD16	(16)		/* SET_BLOCKLEN */
#define CMD17	(17)		/* READ_SINGLE_BLOCK */
#define CMD18	(18)		/* READ_MULTIPLE_BLOCK */
#define CMD23	(23)		/* SET_BLOCK_COUNT (MMC) */
#define	ACMD23	(0x80+23)	/* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24	(24)		/* WRITE_BLOCK */
#define CMD25	(25)		/* WRITE_MULTIPLE_BLOCK */
#define CMD32	(32)		/* ERASE_ER_BLK_START */
#define CMD33	(33)		/* ERASE_ER_BLK_END */
#define CMD38	(38)		/* ERASE */
#define CMD55	(55)		/* APP_CMD */
#define CMD58	(58)		/* READ_OCR */



static volatile
DSTATUS Stat = STA_NOINIT;	/* Physical drive status */

static volatile
WORD Timer1, Timer2;	/* 100Hz decrement timer stopped at zero (disk_timerproc()) */

static
BYTE CardType;			/* Card type flags */



/*-----------------------------------------------------------------------*/
/* MMC/SDCに1バイト送信 (プラットフォーム依存)                           */
/*-----------------------------------------------------------------------*/

/* 1バイト送信 */
static
void xmit_spi (
	BYTE dat	/* 送信データ */
)
{
	RSPI.SPDR.LONG = dat;			/* 送信開始 */
	while (!RSPI.SPSR.BIT.SPRF) ;	/* 転送完了待ち */
	RSPI.SPDR.LONG;					/* 受信データは棄てる */
}


/* 複数バイトの送信 (RTOS環境ではDMA化改造が推奨される) */
static
void xmit_spi_multi (
	const BYTE *buff,	/* データバッファへのポインタ */
	UINT btx			/* 送信するバイト数 (4の倍数) */
)
{
	const DWORD *lp = (const DWORD*)buff;


	RSPI.SPCMD0.BIT.SPB = 3;	/* フレーム長を32-bitに設定 */

	do {
		RSPI.SPDR.LONG = LDDW(*lp++);	/* 4バイト送信開始 */
		while (!RSPI.SPSR.BIT.SPRF) ;	/* 転送完了待ち */
		RSPI.SPDR.LONG;					/* 受信データは棄てる */
	} while (btx -= 4);					/* 必要なだけ繰り返し */

	RSPI.SPCMD0.BIT.SPB = 7;	/* フレーム長を8-bitに戻す */
}



/*-----------------------------------------------------------------------*/
/* MMCからデータを受信する  (プラットフォーム依存)                       */
/*-----------------------------------------------------------------------*/

/* 1バイト受信 */
static
BYTE rcvr_spi (void)
{
	RSPI.SPDR.LONG = 0xFF;			/* 0xFFを送信 */
	while (!RSPI.SPSR.BIT.SPRF) ;	/* 転送終了待ち */
	return RSPI.SPDR.LONG;			/* 交換されたバイトを返す */
}

/* 複数バイトの受信 (RTOS環境ではDMA化改造が推奨される) */
static
void rcvr_spi_multi (
	BYTE *buff,		/* データバッファへのポインタ */
	UINT btr		/* 受信するバイト数 (4の倍数) */
)
{
	DWORD *lp = (DWORD*)buff;


	RSPI.SPCMD0.BIT.SPB = 3;	/* フレーム長を32-bitに設定 */

	do {
		RSPI.SPDR.LONG = 0xFFFFFFFF;	/* 受信時はDOをHに保つ */
		while (!RSPI.SPSR.BIT.SPRF) ;	/* 転送完了待ち */
		*lp++ = LDDW(RSPI.SPDR.LONG);	/* 受信した4バイトをストア */
	} while (btr -= 4);					/* 必要なだけ繰り返し */

	RSPI.SPCMD0.BIT.SPB = 7;	/* フレーム長を8-bitに戻す */
}




/*-----------------------------------------------------------------------*/
/* MMC/SDCの内部処理完了を待つ                                           */
/*-----------------------------------------------------------------------*/

static
int wait_ready (	/* 1:OK, 0:タイムアウト */
	UINT wt			/* タイムアウト時間 [ms] */
)
{
	Timer2 = (WORD)wt;

	rcvr_spi();		/* 1バイト空読み (DO出力を確定させる) */
	do {
		if (rcvr_spi() == 0xFF) return 1;	/* レディ状態になった */
		/* このループはある程度時間を要する. RTOS環境では適宜rot_rdq()等を挿入する. */
	} while (Timer2);	/* タイムアウトするまでループ */

	return 0;	/* タイムアウト */
}



/*-----------------------------------------------------------------------*/
/* MMC/SDCをディセレクトしSPIを解放                                      */
/*-----------------------------------------------------------------------*/

static
void deselect (void)
{
	CS_HIGH();		/* CS = H */
	rcvr_spi();		/* ダミークロックを送り, DO出力をHi-Zにする */
}



/*-----------------------------------------------------------------------*/
/* カードを選択しレディ状態を確認                                        */
/*-----------------------------------------------------------------------*/

static
int select (void)	/* 1:OK, 0:タイムアウト */
{
	CS_LOW();		/* CS = L */
	if (!wait_ready(500)) {
		deselect();
		return 0;	/* カードがビジー状態でタイムアウト */
	}
	return 1;	/* OK */
}



/*-----------------------------------------------------------------------*/
/* SPIモジュールの制御 (プラットフォーム依存)                            */
/*-----------------------------------------------------------------------*/

static
void power_on (void)	/* SPIポートの初期化 */
{
	CS_INIT();	/* CS制御端子等の有効化 */

	/* RSPIモジュールをI/Oパッドに接続 */
	SPI_ATTACH();

	/* RSPIモジュールの初期化 */
	RSPI.SPCR.BYTE = 0;		/* Stop SPI */
	RSPI.SPPCR.BYTE = 0;	/* Fixed idle value, disable loop-back mode */
	RSPI.SPSCR.BYTE = 0;	/* Disable sequence control */
	RSPI.SPDCR.BYTE = 0x20;	/* SPLW=1 */
	RSPI.SPCMD0.WORD = 0x0700;	/* LSBF=0, SPB=7, BRDV=0, CPOL=0, CPHA=0 */
	RSPI.SPBR.BYTE = PCLK / 2 / CLK_SLOW - 1;	/* Bit rate */
	RSPI.SPCR.BYTE = 0x49;	/* Start SPI in master mode */
}


static
void power_off (void)	/* SPIポートの開放 */
{
	select();				/* MMC/SDCの内部処理完了待ち */
	deselect();

	RSPI.SPCR.BYTE = 0;		/* RSPIモジュールの停止 */
}



/*-----------------------------------------------------------------------*/
/* MMC/SDCからデータ・パケットを受信する                                 */
/*-----------------------------------------------------------------------*/

static
int rcvr_datablock (	/* 1:成功, 0:失敗 */
	BYTE *buff,			/* データバッファへのポインタ */
	UINT btr			/* データブロック長 [byte] */
)
{
	BYTE token;


	Timer1 = 200;
	do {							/* DataStartトークンを待つ(最大200ms) */
		token = rcvr_spi();
		/* このループはある程度時間を要する. RTOS環境では適宜rot_rdq()等を挿入する. */
	} while ((token == 0xFF) && Timer1);
	if(token != 0xFE) return 0;		/* DataStartトークン以外の受信またはタイムアウト発生 */

	rcvr_spi_multi(buff, btr);		/* トークンに続くデータブロックをストア */
	rcvr_spi(); rcvr_spi();			/* データCRCは棄てる */

	return 1;						/* OK */
}



/*-----------------------------------------------------------------------*/
/* MMC/SDCにデータパケットを送信する                                     */
/*-----------------------------------------------------------------------*/

#if _READONLY == 0
static
int xmit_datablock (	/* 1:成功, 0:失敗 */
	const BYTE *buff,	/* 送信するデータ(512バイト)へのポインタ */
	BYTE token			/* 使用するトークン */
)
{
	BYTE resp;


	if (!wait_ready(500)) return 0;		/* カードの内部処理完了待ち */

	xmit_spi(token);					/* トークンの送信 */
	if (token != 0xFD) {				/* StopTranトークン以外の時は続いてデータを送信  */
		xmit_spi_multi(buff, 512);		/* データ */
		xmit_spi(0xFF); xmit_spi(0xFF);	/* ダミーCRC */

		resp = rcvr_spi();				/* データレスポンスの受信 */
		if ((resp & 0x1F) != 0x05)		/* 正常に受信されたか? */
			return 0;	/* 失敗 */
	}
	return 1;	/* 成功 */
}
#endif /* _READONLY */



/*-----------------------------------------------------------------------*/
/* MMC/SDCにコマンドパケットを送信                                       */
/*-----------------------------------------------------------------------*/

static
BYTE send_cmd (		/* 戻り値: R1レスポンス (bit7が1のときは送信失敗) */
	BYTE cmd,		/* コマンドインデックス(0〜63, bit7:ACMDフラグ) */
	DWORD arg		/* 引数 */
)
{
	BYTE n, res;


	if (cmd & 0x80) {	/* ACMDのときは先にCMD55を送る */
		cmd &= 0x7F;
		res = send_cmd(CMD55, 0);
		if (res > 1) return res;
	}

	/* カードの選択 */
	deselect();
	if (!select()) return 0xFF;

	/* コマンドパケットを送信 */
	xmit_spi(0x40 | cmd);				/* スタートビット(01) + コマンドインデックス */
	xmit_spi((BYTE)(arg >> 24));		/* 引数[31..24] */
	xmit_spi((BYTE)(arg >> 16));		/* 引数[23..16] */
	xmit_spi((BYTE)(arg >> 8));			/* 引数[15..8] */
	xmit_spi((BYTE)arg);				/* 引数[7..0] */
	n = 0x01;							/* ダミーCRC + Stop */
	if (cmd == CMD0) n = 0x95;			/* 有効なCRC (CMD0(0)) */
	if (cmd == CMD8) n = 0x87;			/* 有効なCRC (CMD8(0x1AA)) */
	xmit_spi(n);						/* CRC + ストップビット(1) */

	/* コマンドレスポンスの受信 */
	if (cmd == CMD12) rcvr_spi();		/* CMD12のときは直後の1バイトを棄てる */
	n = 10;								/* 有効なレスポンスを待つ(最大10バイト) */
	do
		res = rcvr_spi();
	while ((res & 0x80) && --n);

	return res;							/* レスポンス値を返す */
}



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

   パブリック関数

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


/*-----------------------------------------------------------------------*/
/* ディスク装置(MMC/SDC)の初期化                                         */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE drv		/* 物理ドライブ番号(0〜9) */
)
{
	BYTE n, cmd, ty, ocr[4];


	if (drv) return STA_NOINIT;			/* 本ドライバでは1台のドライブのみサポート */
	if (Stat & STA_NODISK) return Stat;	/* ソケットにカードはあるか? */

	power_on();							/* SPIポートの初期化 */
	for (n = 10; n; n--) rcvr_spi();	/* CS=Hのまま80ビットのダミークロックを送る */

	ty = 0;
	if (send_cmd(CMD0, 0) == 1) {			/* MMC/SDCをSPIモードに移行させる */
		Timer1 = 1000;						/* 処理タイムアウト = 1秒 */
		if (send_cmd(CMD8, 0x1AA) == 1) {	/* SDv2か?(CMD8を受け付けたか) */
			for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();		/* R7レスポンスの戻り値を得る */
			if (ocr[2] == 0x01 && ocr[3] == 0xAA) {				/* Vcc=2.7-3.6Vをサポートするか? */
				while (Timer1 && send_cmd(ACMD41, 1UL << 30))	/* ACMD41(HCS)で初期化完了待ち */
					;	/* このループはある程度時間を要する. RTOS環境では適宜rot_rdq()等を挿入する. */
				if (Timer1 && send_cmd(CMD58, 0) == 0) {		/* OCRを読み出す */
					for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
					ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;	/* CCSビットに従いアドレッシングモードの保存 */
				}
			}
		} else {	/* SDv2カード以外(SDv1かMMCv3) */
			if (send_cmd(ACMD41, 0) <= 1) 	{	/* SDv1 or MMC? */
				ty = CT_SD1; cmd = ACMD41;	/* SDv1用初期化コマンド (ACMD41(0)) */
			} else {
				ty = CT_MMC; cmd = CMD1;	/* MMCv3用初期化コマンド (CMD1(0)) */
			}
			while (Timer1 && send_cmd(cmd, 0))			/* 初期化完了待ち */
				;	/* このループはある程度時間を要する. RTOS環境では適宜rot_rdq()等を挿入する. */
			if (!Timer1 || send_cmd(CMD16, 512) != 0)	/* Set block length: 512 */
				ty = 0;
		}
	}
	CardType = ty;	/* Cardタイプの保存 */
	deselect();

	if (ty) {			/* 初期化成功 */
		FCLK_FAST();			/* クロック周波数を高速に設定 */
		Stat &= ~STA_NOINIT;	/* STA_NOINITフラグをクリア(初期化完了状態) */
	} else {			/* 初期化失敗 */
		power_off();			/* カード電源をOFF */
		Stat = STA_NOINIT;		/* STA_NOINITフラグをセット(未初期化状態) */
	}

	return Stat;	/* 現在のディスクステータスを返す */
}



/*-----------------------------------------------------------------------*/
/* ディスクステータスの取得                                              */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE drv		/* 物理ドライブ番号(0〜9) */
)
{
	if (drv) return STA_NOINIT;		/* 本ドライバでは1ドライブのみサポート */

	return Stat;	/* 現在のディスクステータスを返す */
}



/*-----------------------------------------------------------------------*/
/* セクタの読み出し                                                      */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
	BYTE drv,		/* 物理ドライブ番号(0〜9) */
	BYTE *buff,		/* データバッファへのポインタ */
	DWORD sector,	/* 開始セクタ番号(LBA) */
	BYTE count		/* 読み出すセクタ数(1..128) */
)
{
	if (drv || !count) return RES_PARERR;		/* パラメータのチェック */
	if (Stat & STA_NOINIT) return RES_NOTRDY;	/* カードは動作可能か? */

	if (!(CardType & CT_BLOCK)) sector *= 512;	/* カードのアドレッシングモードで必要ならアドレス変換 */

	if (count == 1) {	/* 1セクタの読み出し */
		if ((send_cmd(CMD17, sector) == 0)	/* 1セクタ読み出し(READ_SINGLE_BLOCK) */
			&& rcvr_datablock(buff, 512))
			count = 0;
	}
	else {				/* 複数セクタの読み出し */
		if (send_cmd(CMD18, sector) == 0) {	/* 複数セクタ読み出し開始(READ_MULTIPLE_BLOCK) */
			do {	/* 必要なだけデータブロックを受信 */
				if (!rcvr_datablock(buff, 512)) break;
				buff += 512;
			} while (--count);
			send_cmd(CMD12, 0);				/* 読み出し停止(STOP_TRANSMISSION) */
		}
	}
	deselect();

	return count ? RES_ERROR : RES_OK;	/* 結果を返す */
}



/*-----------------------------------------------------------------------*/
/* セクタの書き込み                                                      */
/*-----------------------------------------------------------------------*/

#if _READONLY == 0
DRESULT disk_write (
	BYTE drv,			/* 物理ドライブ番号(0〜9) */
	const BYTE *buff,	/* 書き込むデータへのポインタ */
	DWORD sector,		/* 開始セクタ番号 (LBA) */
	BYTE count			/* 書き込むセクタ数 (1..128) */
)
{
	if (drv || !count) return RES_PARERR;		/* パラメータのチェック */
	if (Stat & STA_NOINIT) return RES_NOTRDY;	/* カードは動作可能か? */
	if (Stat & STA_PROTECT) return RES_WRPRT;	/* 書き込み禁止か? */

	if (!(CardType & CT_BLOCK)) sector *= 512;	/* カードのアドレッシングモードで必要ならアドレス変換 */

	if (count == 1) {	/* 1セクタの書き込み */
		if ((send_cmd(CMD24, sector) == 0)	/* 1セクタ書き込み(WRITE_BLOCK) */
			&& xmit_datablock(buff, 0xFE))
			count = 0;
	}
	else {				/* 複数セクタの書き込み */
		if (CardType & CT_SDC) send_cmd(ACMD23, count);	/* SDCの場合はセクタ数を先に設定する */
		if (send_cmd(CMD25, sector) == 0) {	/* 複数セクタ書き込み開始(WRITE_MULTIPLE_BLOCK) */
			do {	/* 必要なだけデータブロックを送信 */
				if (!xmit_datablock(buff, 0xFC)) break;
				buff += 512;
			} while (--count);
			if (!xmit_datablock(0, 0xFD))	/* 書き込み終了(STOP_TRANトークン) */
				count = 1;
		}
	}
	deselect();

	return count ? RES_ERROR : RES_OK;	/* 結果を返す */
}
#endif /* _READONLY == 0 */



/*-----------------------------------------------------------------------*/
/* データ転送以外のドライブ制御                                          */
/*-----------------------------------------------------------------------*/

#if _USE_IOCTL != 0
DRESULT disk_ioctl (
	BYTE drv,		/* 物理ドライブ番号 (0〜9) */
	BYTE ctrl,		/* 制御コマンド番号 */
	void *buff		/* 制御データへのポインタ */
)
{
	DRESULT res;
	BYTE n, csd[16], *ptr = buff;
	WORD csize;
	DWORD *dp, st, ed;


	if (drv) return RES_PARERR;					/* 本ドライバでは1ドライブのみサポート */
	if (Stat & STA_NOINIT) return RES_NOTRDY;	/* ドライブは動作可能か? */

	res = RES_ERROR;

	switch (ctrl) {
	case CTRL_SYNC :		/* ドライブの内部動作の完了を待つ */
		if (select()) {
			deselect();
			res = RES_OK;
		}
		break;

	case GET_SECTOR_COUNT :	/* ドライブ容量(セクタ数)を返す (DOWRD) */
		if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
			if ((csd[0] >> 6) == 1) {	/* SDC ver 2.00 */
				csize = csd[9] + ((WORD)csd[8] << 8) + 1;
				*(DWORD*)buff = (DWORD)csize << 10;
			} else {					/* SDC ver 1.XX or MMC ver 3 */
				n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
				csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
				*(DWORD*)buff = (DWORD)csize << (n - 9);
			}
			res = RES_OK;
		}
		break;

	case GET_SECTOR_SIZE :	/* セクタサイズを返す (WORD)*/
		*(WORD*)buff = 512;	/* 全てのMMC/SDCは512バイトで動作する */
		res = RES_OK;
		break;

	case GET_BLOCK_SIZE :	/* セクタ単位の消去ブロックサイズを返す (DWORD) */
		if (CardType & CT_SD2) {	/* SDC ver 2.00 */
			if (send_cmd(ACMD13, 0) == 0) {	/* Read SD status */
				rcvr_spi();
				if (rcvr_datablock(csd, 16)) {				/* Read partial block */
					for (n = 64 - 16; n; n--) rcvr_spi();	/* Purge trailing data */
					*(DWORD*)buff = 16UL << (csd[10] >> 4);
					res = RES_OK;
				}
			}
		} else {					/* SDC ver 1.XX or MMC */
			if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {	/* Read CSD */
				if (CardType & CT_SD1) {	/* SDC ver 1.XX */
					*(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
				} else {					/* MMC */
					*(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
				}
				res = RES_OK;
			}
		}
		break;

	case CTRL_ERASE_SECTOR :	/* 指定された範囲のセクタの消去 (_USE_ERASE == 1のとき呼ばれる) */
		if (!(CardType & CT_SDC)) break;				/* SDCか? */
		if (disk_ioctl(drv, MMC_GET_CSD, csd)) break;	/* CSDの読み出し */
		if (!(csd[0] >> 6) && !(csd[10] & 0x40)) break;	/* セクタ単位のセクタ消去が可能なカードか? */
		dp = buff; st = dp[0]; ed = dp[1];				/* セクタ範囲データの準備 */
		if (!(CardType & CT_BLOCK)) {
			st *= 512; ed *= 512;
		}
		if (send_cmd(CMD32, st) == 0 && send_cmd(CMD33, ed) == 0 && send_cmd(CMD38, 0) == 0 && wait_ready(30000))	/* 消去の実行 */
			res = RES_OK;	/* ※FatFsはこのコマンドの結果をチェックしない */
		break;

	/* FatFsは以下のコマンドはサポートしない(不要) */

	case MMC_GET_TYPE :		/* カードタイプの取得 (BYTE) */
		*ptr = CardType;
		res = RES_OK;
		break;

	case MMC_GET_CSD :		/* CSDの読み出し (16 bytes) */
		if (send_cmd(CMD9, 0) == 0		/* READ_CSD */
			&& rcvr_datablock(ptr, 16))
			res = RES_OK;
		break;

	case MMC_GET_CID :		/* CIDの読み出し (16 bytes) */
		if (send_cmd(CMD10, 0) == 0		/* READ_CID */
			&& rcvr_datablock(ptr, 16))
			res = RES_OK;
		break;

	case MMC_GET_OCR :		/* OCRの読み出し (4 bytes) */
		if (send_cmd(CMD58, 0) == 0) {	/* READ_OCR */
			for (n = 4; n; n--) *ptr++ = rcvr_spi();
			res = RES_OK;
		}
		break;

	case MMC_GET_SDSTAT :	/* SD statusの読み出し (64 bytes) */
		if (send_cmd(ACMD13, 0) == 0) {	/* SD_STATUS */
			rcvr_spi();
			if (rcvr_datablock(ptr, 64))
				res = RES_OK;
		}
		break;

	default:				/* 未定義コマンド */
		res = RES_PARERR;
	}

	deselect();

	return res;
}
#endif /* _USE_IOCTL != 0 */



/*-----------------------------------------------------------------------*/
/* タイマサービス                                                        */
/*-----------------------------------------------------------------------*/
/* この関数は, 制御タイミングの生成やソケットの監視のため, タイマ割り込み
/  で1ms毎に呼び出されなければならない.
*/

void disk_timerproc (void)
{
	WORD n;
	BYTE s;


	n = Timer1;						/* 1kHz decrement timer stopped at 0 */
	if (n) Timer1 = --n;
	n = Timer2;
	if (n) Timer2 = --n;

	s = Stat;
	if (WP)		/* Write protected */
		s |= STA_PROTECT;
	else		/* Write enabled */
		s &= ~STA_PROTECT;
	if (INS)	/* Card is in socket */
		s &= ~STA_NODISK;
	else		/* Socket empty */
		s |= (STA_NODISK | STA_NOINIT);
	Stat = s;
}

