##// END OF EJS Templates
sync
sync

File last commit:

r49:58c317327c7e dev_alexis
r49:58c317327c7e dev_alexis
Show More
sdcard.c
494 lines | 15.7 KiB | text/x-c | CLexer
/*------------------------------------------------------------------------------
-- This file is a part of the libuc, microcontroler library
-- Copyright (C) 2012, Alexis Jeandet
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-------------------------------------------------------------------------------
-- Author : Alexis Jeandet
-- Mail : alexis.jeandet@gmail.com
-------------------------------------------------------------------------------*/
#include <sdcard.h>
#include <sdcard-spi.h>
int sdcardselect (blkdeviceptr _this);
void sdcarddeselect (blkdeviceptr _this);
int sdcardwait_ready (sdcardDev* sdcard);
int sdcardxmit_datablock (sdcardDev* sdcard,const char *buff,char token);
int sdcardrcvr_datablock (sdcardDev* sdcard,char *buff,uint32_t btr);
char sdcardsend_cmd (blkdeviceptr _this,char cmd,uint32_t arg);
DSTATUS sdcarddisk_status (blkdeviceptr _this);
DSTATUS sdcarddisk_initialize (blkdeviceptr _this);
DRESULT sdcarddisk_read (blkdeviceptr _this,char *buff,uint32_t sector,char count);
DRESULT sdcarddisk_write (blkdeviceptr _this,const char *buff,uint32_t sector,char count);
DRESULT sdcarddisk_ioctl (blkdeviceptr _this,char ctrl,void *buff);
static volatile
unsigned int Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */
void sdcarddeselect (blkdeviceptr _this)
{
char d;
_this->select(0);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,&d, 1); /* Dummy clock (force DO hi-z for multiple slave SPI) */
}
int sdcardselect (blkdeviceptr _this) /* 1:OK, 0:Timeout */
{
char d;
_this->select(1);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,&d, 1); /* Dummy clock (force DO enabled) */
if (sdcardwait_ready(((sdcardDev*)_this->phy))) return 1; /* OK */
_this->select(0);
return 0; /* Timeout */
}
int sdcardwait_ready (sdcardDev* sdcard) /* 1:OK, 0:Timeout */
{
char d;
unsigned int tmr;
volatile unsigned int i=0;
for (tmr = 100000; tmr; tmr--) { /* Wait for ready in timeout of 500ms */
sdcard->rcvr_mmc(sdcard->phy,&d, 1);
if (d == 0xFF) break;
for(i=0;i<128;i++);
}
return tmr ? 1 : 0;
}
int sdcardrcvr_datablock (sdcardDev* sdcard,char *buff,uint32_t btr)
{
char d[2];
unsigned int tmr;
volatile unsigned int i=0;
for (tmr = 50000; tmr; tmr--) { /* Wait for data packet in timeout of 100ms */
sdcard->rcvr_mmc(sdcard->phy,d, 1);
if (d[0] != 0xFF) break;
for(i=0;i<64;i++);
}
if (d[0] != 0xFE) return 0; /* If not valid data token, return with error */
sdcard->rcvr_mmc(sdcard->phy,buff, btr); /* Receive the data block into buffer */
sdcard->rcvr_mmc(sdcard->phy,d, 2); /* Discard CRC */
return 1; /* Return with success */
}
int sdcardxmit_datablock (sdcardDev* sdcard,const char *buff,char token)
{
char d[2];
if (!sdcardwait_ready(sdcard)) return 0;
d[0] = token;
sdcard->xmit_mmc(sdcard->phy,d, 1); /* Xmit a token */
if (token != 0xFD) { /* Is it data token? */
sdcard->xmit_mmc(sdcard->phy,buff, 512); /* Xmit the 512 byte data block to MMC */
sdcard->rcvr_mmc(sdcard->phy,d, 2); /* Xmit dummy CRC (0xFF,0xFF) */
sdcard->rcvr_mmc(sdcard->phy,d, 1); /* Receive data response */
if ((d[0] & 0x1F) != 0x05) /* If not accepted, return with error */
return 0;
}
return 1;
}
char sdcardsend_cmd (blkdeviceptr _this,char cmd,uint32_t arg)
{
char n, d, buf[6];
if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
cmd &= 0x7F;
n = sdcardsend_cmd(_this,CMD55, 0);
if (n > 1) return n;
}
/* Select the card and wait for ready */
sdcarddeselect(_this);
if (!sdcardselect(_this)) return 0xFF;
/* Send a command packet */
buf[0] = 0x40 | cmd; /* Start + Command index */
buf[1] = (char)(arg >> 24); /* Argument[31..24] */
buf[2] = (char)(arg >> 16); /* Argument[23..16] */
buf[3] = (char)(arg >> 8); /* Argument[15..8] */
buf[4] = (char)arg; /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0) n = 0x95; /* (valid CRC for CMD0(0)) */
if (cmd == CMD8) n = 0x87; /* (valid CRC for CMD8(0x1AA)) */
if (cmd == CMD55) n = 0x63;
buf[5] = n;
((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,buf, 6);
/* Receive command response */
if (cmd == CMD12) {((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,&d, 1);} /* Skip a stuff byte when stop reading */
n = 10; /* Wait for a valid response in timeout of 10 attempts */
do
{
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,&d, 1);
//libucprintf("resp=%d\n\r",d);
}
while ((d & 0x80) && --n);
return d; /* Return with the response value */
}
DSTATUS sdcarddisk_status (blkdeviceptr _this)
{
DSTATUS s = ((sdcardDev*)_this->phy)->Stat;
char ocr[4];
if ((_this==0) || !_this->detect()) {
s = STA_NODISK | STA_NOINIT;
} else {
s &= ~STA_NODISK;
if (_this->writeprotected()) /* Check card write protection */
s |= STA_PROTECT;
else
s &= ~STA_PROTECT;
if (!(s & STA_NOINIT)) {
if (sdcardsend_cmd(_this,CMD58, 0)) /* Check if the card is kept initialized */
s |= STA_NOINIT;
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr, 4);
_this->select(0);
}
}
((sdcardDev*)_this->phy)->Stat = s;
return s;
}
DSTATUS sdcarddisk_initialize (blkdeviceptr _this)
{
char cmd, ty, ocr[4],ocr2[4];
int n;
const char dummy=0xff;
uint32_t speed = 1000000;
((sdcardDev*)_this->phy)->setspeed(((sdcardDev*)_this->phy)->phy,350000);
if (_this==0) return STA_NOINIT; /* Supports only drive 0 */
if (((sdcardDev*)_this->phy)->Stat & STA_NODISK) return ((sdcardDev*)_this->phy)->Stat; /* Is card existing in the soket? */
for (n = 10; n; n--) ((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1); /* Send 80 dummy clocks */
ty = 0;
if (sdcardsend_cmd(_this,CMD0, 0) == 1) { /* Put the card SPI/Idle state */
Timer1 = 1000; /* Initialization timeout = 1 sec */
if (sdcardsend_cmd(_this,CMD8, 0x1AA) == 1) { /* SDv2? */
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr, 4);
//for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF); /* Get 32 bit return value of R7 resp */
if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* Is the card supports vcc of 2.7-3.6V? */
while (Timer1 && sdcardsend_cmd(_this,ACMD41, 1UL << 30)) ; /* Wait for end of initialization with ACMD41(HCS) */
if (Timer1 && sdcardsend_cmd(_this,CMD58, 0) == 0) { /* Check CCS bit in the OCR */
//for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr, 4);
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* Card id SDv2 */
}
}
} else { /* Not SDv2 card */
if (sdcardsend_cmd(_this,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 && sdcardsend_cmd(_this,cmd, 0)) ; /* Wait for end of initialization */
if (!Timer1 || sdcardsend_cmd(_this,CMD16, 512) != 0) /* Set block length: 512 */
ty = 0;
}
}
((sdcardDev*)_this->phy)->CardType = ty; /* Card type */
if (ty) { /* OK */
((sdcardDev*)_this->phy)->Stat &= ~STA_NOINIT; /* Clear STA_NOINIT flag */
sdcardsend_cmd(_this,CMD58, 0);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr, 4);
do
{
speed+=1000000;
((sdcardDev*)_this->phy)->setspeed(((sdcardDev*)_this->phy)->phy,speed);
sdcardsend_cmd(_this,CMD58, 0);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr2, 4);
for(n=0;n<4;n++)
{
if(ocr[n]!=ocr2[n])
{
n=0;
break;
}
}
if(speed>(50*1000*1000))
{
break;
}
}while(n);
if(!n)
{
speed-=1000000;
((sdcardDev*)_this->phy)->setspeed(((sdcardDev*)_this->phy)->phy,speed);
}
} else { /* Failed */
//power_off();
((sdcardDev*)_this->phy)->Stat = STA_NOINIT;
}
sdcarddeselect (_this);
return ((sdcardDev*)_this->phy)->Stat;
}
DRESULT sdcarddisk_read (blkdeviceptr _this,char *buff,uint32_t sector,char count)
{
DSTATUS s;
s = sdcarddisk_status(_this);
if (s & STA_NOINIT) return RES_NOTRDY;
if (!count) return RES_PARERR;
if (!(((sdcardDev*)_this->phy)->CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block read */
if ((sdcardsend_cmd(_this,CMD17, sector) == 0) && sdcardrcvr_datablock((sdcardDev*)(_this->phy),buff, 512))
count = 0;
}
else { /* Multiple block read */
if (sdcardsend_cmd(_this,CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
do {
if (!sdcardrcvr_datablock (((sdcardDev*)_this->phy),buff, 512)) break;
buff += 512;
} while (--count);
sdcardsend_cmd(_this,CMD12, 0); /* STOP_TRANSMISSION */
}
}
sdcarddeselect (_this);
return count ? RES_ERROR : RES_OK;
}
DRESULT sdcarddisk_write (blkdeviceptr _this,const char *buff,uint32_t sector,char count)
{
DSTATUS s;
s = sdcarddisk_status(_this);
if (s & STA_NOINIT) return RES_NOTRDY;
if (s & STA_PROTECT) return RES_WRPRT;
if (!count) return RES_PARERR;
if (!(((sdcardDev*)_this->phy)->CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block write */
if ((sdcardsend_cmd(_this,CMD24, sector) == 0) /* WRITE_BLOCK */
&& sdcardxmit_datablock (((sdcardDev*)_this->phy),buff, 0xFE))
count = 0;
}
else { /* Multiple block write */
if (((sdcardDev*)_this->phy)->CardType & CT_SDC) sdcardsend_cmd(_this,ACMD23, count);
if (sdcardsend_cmd(_this,CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!sdcardxmit_datablock (((sdcardDev*)_this->phy),buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!sdcardxmit_datablock (((sdcardDev*)_this->phy),0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
sdcarddeselect (_this);
return count ? RES_ERROR : RES_OK;
}
DRESULT sdcarddisk_ioctl (blkdeviceptr _this,char ctrl,void *buff)
{
DRESULT res;
const char dummy=0xff;
char n, csd[16], *ptr = buff;
uint16_t csize;
uint32_t *dp, st, ed;
if (_this==0) return RES_PARERR; /* Check parameter */
if (((sdcardDev*)_this->phy)->Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
res = RES_ERROR;
switch (ctrl) {
case CTRL_SYNC : /* Wait for end of internal write process of the drive */
if (sdcardselect (_this)) {
sdcarddeselect (_this);
res = RES_OK;
}
break;
case GET_SECTOR_COUNT : /* Get drive capacity in unit of sector (DWORD) */
if ((sdcardsend_cmd(_this,CMD9, 0) == 0) && sdcardrcvr_datablock (((sdcardDev*)_this->phy),csd, 16)) {
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
*(uint32_t*)buff = (uint32_t)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) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
*(uint32_t*)buff = (uint32_t)csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_SECTOR_SIZE : /* Get sector size in unit of byte (WORD) */
*(uint16_t*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
if (((sdcardDev*)_this->phy)->CardType & CT_SD2) { /* SDC ver 2.00 */
if (sdcardsend_cmd(_this,ACMD13, 0) == 0) { /* Read SD status */
((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1);
if (sdcardrcvr_datablock (((sdcardDev*)_this->phy),csd, 16)) { /* Read partial block */
for (n = 64 - 16; n; n--) ((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1); /* Purge trailing data */
*(uint32_t*)buff = 16UL << (csd[10] >> 4);
res = RES_OK;
((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1);
}
}
} else { /* SDC ver 1.XX or MMC */
if ((sdcardsend_cmd(_this,CMD9, 0) == 0) && sdcardrcvr_datablock (((sdcardDev*)_this->phy),csd, 16)) { /* Read CSD */
if (((sdcardDev*)_this->phy)->CardType & CT_SD1) { /* SDC ver 1.XX */
*(uint32_t*)buff = (((csd[10] & 63) << 1) + ((uint16_t)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
} else { /* MMC */
*(uint32_t*)buff = ((uint16_t)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
}
res = RES_OK;
}
}
break;
case CTRL_ERASE_SECTOR : /* Erase a block of sectors (used when _USE_ERASE == 1) */
if (!(((sdcardDev*)_this->phy)->CardType & CT_SDC)) break; /* Check if the card is SDC */
if (sdcarddisk_ioctl(_this, MMC_GET_CSD, csd)) break; /* Get CSD */
if (!(csd[0] >> 6) && !(csd[10] & 0x40)) break; /* Check if sector erase can be applied to the card */
dp = buff; st = dp[0]; ed = dp[1]; /* Load sector block */
if (!(((sdcardDev*)_this->phy)->CardType & CT_BLOCK)) {
st *= 512; ed *= 512;
}
if (sdcardsend_cmd(_this,CMD32, st) == 0 && sdcardsend_cmd(_this,CMD33, ed) == 0 && sdcardsend_cmd(_this,CMD38, 0) == 0 && sdcardwait_ready(((sdcardDev*)_this->phy))) /* Erase sector block */
res = RES_OK; /* FatFs does not check result of this command */
break;
/* Following command are not used by FatFs module */
case MMC_GET_TYPE : /* Get MMC/SDC type (BYTE) */
*ptr = ((sdcardDev*)_this->phy)->CardType;
res = RES_OK;
break;
case MMC_GET_CSD : /* Read CSD (16 bytes) */
if (sdcardsend_cmd(_this,CMD9, 0) == 0 /* READ_CSD */
&& sdcardrcvr_datablock (((sdcardDev*)_this->phy),ptr, 16))
res = RES_OK;
break;
case MMC_GET_CID : /* Read CID (16 bytes) */
if (sdcardsend_cmd(_this,CMD10, 0) == 0 /* READ_CID */
&& sdcardrcvr_datablock (((sdcardDev*)_this->phy),ptr, 16))
res = RES_OK;
break;
case MMC_GET_OCR : /* Read OCR (4 bytes) */
if (sdcardsend_cmd(_this,CMD58, 0) == 0) { /* READ_OCR */
//for (n = 4; n; n--) *ptr++ =
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ptr, 4);
res = RES_OK;
}
break;
case MMC_GET_SDSTAT : /* Read SD status (64 bytes) */
if (sdcardsend_cmd(_this,ACMD13, 0) == 0) { /* SD_STATUS */
((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1);
if (sdcardrcvr_datablock (((sdcardDev*)_this->phy),ptr, 64))
res = RES_OK;
}
break;
default:
res = RES_PARERR;
}
sdcarddeselect (_this);
return res;
}
void sdcardmake(sdcardDev* sdcard,UHANDLE phy,void (*rcvr_mmc) (UHANDLE,char *,uint32_t),void (*xmit_mmc) (UHANDLE,const char *,uint32_t ),void (*setspeed) (UHANDLE phy,uint32_t speed),uint32_t (*getspeed) (UHANDLE phy))
{
sdcard->phy = phy;
sdcard->rcvr_mmc = rcvr_mmc;
sdcard->xmit_mmc = xmit_mmc;
sdcard->setspeed = setspeed;
sdcard->getspeed = getspeed;
}
void sdcardmakeblkdev(blkdevice* dev,sdcardDev* sdcard, blkdevselect_t select,blkdevpower_t power,blkdevdetect_t detect,blkdevwriteprotected_t writeprotected)
{
dev->phy=sdcard;
dev->select=select;
dev->power = power;
dev->detect = detect;
dev->writeprotected = writeprotected;
dev->write = sdcarddisk_write;
dev->read = sdcarddisk_read;
dev->ioctl = sdcarddisk_ioctl;
dev->initialize = sdcarddisk_initialize;
dev->status = sdcarddisk_status;
}