mirror of
https://github.com/mist-devel/mist-firmware.git
synced 2026-01-13 15:17:43 +00:00
568 lines
16 KiB
C
568 lines
16 KiB
C
/*
|
||
Copyright 2005, 2006, 2007 Dennis van Weeren
|
||
Copyright 2008, 2009 Jakub Bednarski
|
||
|
||
This file is part of Minimig
|
||
|
||
Minimig 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.
|
||
|
||
Minimig 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, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
// --== based on the work by Dennis van Weeren and Jan Derogee ==--
|
||
// 2008-10-03 - adaptation for ARM controller
|
||
// 2009-07-23 - clean-up and some optimizations
|
||
// 2009-11-22 - multiple sector read implemented
|
||
|
||
|
||
// FIXME - get capacity from SD card
|
||
|
||
//1GB:
|
||
//CSD:
|
||
//0000: 00 7f 00 32 5b 59 83 bc f6 db ff 9f 96 40 00 93 ...2[Y.<2E><>.<2E>.@.<2E>
|
||
//CID:
|
||
//0000: 3e 00 00 34 38 32 44 00 00 73 2f 6f 93 00 c7 cd >..482D..s/o<>...
|
||
|
||
|
||
#include "stdio.h"
|
||
#include "string.h"
|
||
#include "spi.h"
|
||
|
||
#include "mmc.h"
|
||
#include "fat.h"
|
||
|
||
// variables
|
||
static unsigned char crc;
|
||
static unsigned long timeout;
|
||
static unsigned char response;
|
||
static unsigned char CardType;
|
||
|
||
// internal functions
|
||
static void MMC_CRC(unsigned char c) RAMFUNC;
|
||
static unsigned char MMC_Command(unsigned char cmd, unsigned long arg) RAMFUNC;
|
||
static unsigned char MMC_CMD12(void);
|
||
|
||
unsigned char MMC_CheckCard() {
|
||
// check for removal of card
|
||
if((CardType != CARDTYPE_NONE) && !mmc_inserted()) {
|
||
CardType = CARDTYPE_NONE;
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static RAMFUNC char check_card() {
|
||
// check of card has been removed and try to re-initialize it
|
||
if(CardType == CARDTYPE_NONE) {
|
||
iprintf("Card was removed, try to init it\n");
|
||
|
||
if(!mmc_inserted())
|
||
return 0;
|
||
|
||
if(!MMC_Init())
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
// init memory card
|
||
unsigned char MMC_Init(void)
|
||
{
|
||
unsigned char n;
|
||
unsigned char ocr[4];
|
||
|
||
if(!mmc_inserted()) {
|
||
iprintf("No card inserted\r");
|
||
return(CARDTYPE_NONE);
|
||
}
|
||
|
||
spi_slow(); // set slow clock
|
||
DisableCard(); // CS = 1
|
||
SPI(0xff); // DI = 1
|
||
TIMER_wait(20); // 20ms delay
|
||
for (n=0; n<10; n++) SPI(0xff); // 80 dummy clocks, DI = 1
|
||
TIMER_wait(20); // 20ms delay
|
||
EnableCard();
|
||
|
||
CardType = CARDTYPE_NONE;
|
||
|
||
for(n=0; n<16; n++) {
|
||
TIMER_wait(1);
|
||
if (MMC_Command(CMD0, 0) == 0x01) break; // try to send CMD0 multiple times
|
||
}
|
||
if (n<16) // got CMD0 IDLE response
|
||
{ // idle state
|
||
timeout = GetTimer(1000); // initialization timeout 1s, 4s doesn't work with the original arm timer
|
||
if (MMC_Command(CMD8, 0x1AA) == 0x01) // check if the card can operate with 2.7-3.6V power
|
||
{ // SDHC card
|
||
for (n = 0; n < 4; n++)
|
||
ocr[n] = SPI(0xFF); // get the rest of R7 response
|
||
if (ocr[2] == 0x01 && ocr[3] == 0xAA)
|
||
{ // the card can work at 2.7-3.6V
|
||
iprintf("SDHC card detected\r");
|
||
while (!CheckTimer(timeout))
|
||
{ // now we must wait until CMD41 returns 0 (or timeout elapses)
|
||
if (MMC_Command(CMD55, 0) == 0x01)
|
||
{ // CMD55 must precede any ACMD command
|
||
if (MMC_Command(CMD41, 1 << 30) == 0x00) // ACMD41 with HCS bit
|
||
{ // initialization completed
|
||
if (MMC_Command(CMD58, 0) == 0x00)
|
||
{ // check CCS (Card Capacity Status) bit in the OCR
|
||
for (n = 0; n < 4; n++)
|
||
ocr[n] = SPI(0xFF);
|
||
CardType = (ocr[0] & 0x40) ? CARDTYPE_SDHC : CARDTYPE_SD; // if CCS set then the card is SDHC compatible
|
||
}
|
||
else
|
||
iprintf("CMD58 (READ_OCR) failed!\r");
|
||
DisableCard();
|
||
|
||
// set appropriate SPI speed
|
||
spi_fast();
|
||
|
||
return(CardType);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
iprintf("CMD55 (APP_CMD) failed!\r");
|
||
DisableCard();
|
||
return(CARDTYPE_NONE);
|
||
}
|
||
}
|
||
iprintf("SDHC card initialization timed out!\r");
|
||
DisableCard();
|
||
return(CARDTYPE_NONE);
|
||
}
|
||
}
|
||
|
||
// it's not an SDHC card
|
||
if (MMC_Command(CMD55, 0) == 0x01)
|
||
{ // CMD55 accepted so it's an SD card (or Kingston 128 MB MMC)
|
||
if (MMC_Command(CMD41, 0) <= 0x01)
|
||
{ // SD card detected - wait for the end of initialization
|
||
iprintf("SD card detected\r");
|
||
while (!CheckTimer(timeout))
|
||
{ // now we must wait until CMD41 returns 0 (or timeout elapses)
|
||
if (MMC_Command(CMD55, 0) == 0x01)
|
||
{ // CMD55 must precede any ACMD command
|
||
if (MMC_Command(CMD41, 0) == 0x00)
|
||
{ // initialization completed
|
||
|
||
if (MMC_Command(CMD16, 512) != 0x00) //set block length
|
||
iprintf("CMD16 (SET_BLOCKLEN) failed!\r");
|
||
DisableCard();
|
||
|
||
// set appropriate SPI speed
|
||
spi_fast();
|
||
CardType = CARDTYPE_SD;
|
||
|
||
return(CardType);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
iprintf("CMD55 (APP_CMD) failed!\r");
|
||
DisableCard();
|
||
return(CARDTYPE_NONE);
|
||
}
|
||
}
|
||
iprintf("SD card initialization timed out!\r");
|
||
DisableCard();
|
||
return(CARDTYPE_NONE);
|
||
}
|
||
}
|
||
|
||
// it's not an SD card
|
||
iprintf("MMC card detected\r");
|
||
while (!CheckTimer(timeout))
|
||
{ // now we must wait until CMD1 returns 0 (or timeout elapses)
|
||
if (MMC_Command(CMD1, 0) == 0x00)
|
||
{ // initialization completed
|
||
|
||
if (MMC_Command(CMD16, 512) != 0x00) // set block length
|
||
iprintf("CMD16 (SET_BLOCKLEN) failed!\r");
|
||
|
||
DisableCard();
|
||
|
||
// set appropriate SPI speed
|
||
spi_fast_mmc();
|
||
CardType = CARDTYPE_MMC;
|
||
|
||
return(CardType);
|
||
}
|
||
}
|
||
|
||
iprintf("MMC card initialization timed out!\r");
|
||
DisableCard();
|
||
return(CARDTYPE_NONE);
|
||
}
|
||
|
||
DisableCard();
|
||
iprintf("No memory card detected!\r");
|
||
return(CARDTYPE_NONE);
|
||
}
|
||
|
||
|
||
// Read single 512-byte block
|
||
RAMFUNC unsigned char MMC_Read(unsigned long lba, unsigned char *pReadBuffer)
|
||
{
|
||
// if pReadBuffer is NULL then use direct to the FPGA transfer mode (FPGA2 asserted)
|
||
|
||
// check of card has been removed and try to re-initialize it
|
||
if(!check_card()) return 0;
|
||
|
||
unsigned long i;
|
||
unsigned char *p;
|
||
|
||
if (CardType != CARDTYPE_SDHC) // SDHC cards are addressed in sectors not bytes
|
||
lba = lba << 9; // otherwise convert sector adddress to byte address
|
||
|
||
EnableCard();
|
||
|
||
if (MMC_Command(CMD17, lba))
|
||
{
|
||
// iprintf("CMD17 (READ_BLOCK): invalid response 0x%02X (lba=%lu)\r", response, lba);
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
|
||
// now we are waiting for data token, it takes around 300us
|
||
timeout = 0;
|
||
while ((SPI(0xFF)) != 0xFE)
|
||
{
|
||
if (timeout++ >= 1000000) // we can't wait forever
|
||
{
|
||
// iprintf("CMD17 (READ_BLOCK): no data token! (lba=%lu)\r", lba);
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
}
|
||
|
||
if (pReadBuffer == 0)
|
||
{ // in this mode we do not receive data, instead the FPGA captures directly the data stream transmitted by the SD/MMC card
|
||
EnableDMode();
|
||
spi_block(511);
|
||
SPI(0xff); // dummy write for 4096 clocks
|
||
SPI(0xff);
|
||
DisableDMode();
|
||
}
|
||
else
|
||
spi_block_read(pReadBuffer);
|
||
|
||
SPI(0xFF); // read CRC lo byte
|
||
SPI(0xFF); // read CRC hi byte
|
||
|
||
DisableCard();
|
||
return(1);
|
||
}
|
||
|
||
static unsigned char MMC_GetCXD(unsigned char cmd, unsigned char *ptr) {
|
||
int i;
|
||
EnableCard();
|
||
|
||
if (MMC_Command(cmd,0)) {
|
||
iprintf("CMD%d (GET_C%cD): invalid response 0x%02X \r",
|
||
(cmd==CMD9)?9:10, (cmd==CMD9)?'S':'I', response);
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
|
||
// now we are waiting for data token, it takes around 300us
|
||
timeout = 0;
|
||
while ((SPI(0xFF)) != 0xFE) {
|
||
if (timeout++ >= 1000000) { // we can't wait forever
|
||
iprintf("CMD%d (GET_C%cD): no data token!\r",
|
||
(cmd==CMD9)?9:10, (cmd==CMD9)?'S':'I');
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
}
|
||
|
||
for (i = 0; i < 16; i++)
|
||
ptr[i]=SPI(0xFF);
|
||
|
||
DisableCard();
|
||
|
||
return(1);
|
||
}
|
||
|
||
// Read CSD register
|
||
unsigned char MMC_GetCSD(unsigned char *csd) {
|
||
return MMC_GetCXD(CMD9, csd);
|
||
}
|
||
|
||
// Read CID register
|
||
unsigned char MMC_GetCID(unsigned char *cid) {
|
||
return MMC_GetCXD(CMD10, cid);
|
||
}
|
||
|
||
// MMC get capacity
|
||
unsigned long MMC_GetCapacity()
|
||
{
|
||
unsigned long result=0;
|
||
unsigned char CSDData[16];
|
||
|
||
MMC_GetCSD(CSDData);
|
||
|
||
if ((CSDData[0] & 0xC0)==0x40) //CSD Version 2.0 - SDHC
|
||
{
|
||
result=(CSDData[7]&0x3f)<<26;
|
||
result|=CSDData[8]<<18;
|
||
result|=CSDData[9]<<10;
|
||
result+=1024;
|
||
return(result);
|
||
}
|
||
else
|
||
{
|
||
int blocksize=CSDData[5]&15; // READ_BL_LEN
|
||
blocksize=1<<(blocksize-9); // Now a scalar: physical block size / 512.
|
||
result=(CSDData[6]&3)<<10;
|
||
result|=CSDData[7]<<2;
|
||
result|=(CSDData[8]>>6)&3; // result now contains C_SIZE
|
||
int cmult=(CSDData[9]&3)<<1;
|
||
cmult|=(CSDData[10]>>7) & 1;
|
||
++result;
|
||
result<<=cmult+2;
|
||
result*=blocksize; // Scale by the number of 512-byte chunks per block.
|
||
return(result);
|
||
}
|
||
}
|
||
|
||
// read multiple 512-byte blocks
|
||
unsigned char MMC_ReadMultiple(unsigned long lba, unsigned char *pReadBuffer, unsigned long nBlockCount)
|
||
{
|
||
// if pReadBuffer is NULL then use direct to the FPGA transfer mode (FPGA2 asserted)
|
||
|
||
// check of card has been removed and try to re-initialize it
|
||
if(!check_card()) return 0;
|
||
|
||
unsigned long i;
|
||
unsigned char *p;
|
||
|
||
if (CardType != CARDTYPE_SDHC) // SDHC cards are addressed in sectors not bytes
|
||
lba = lba << 9; // otherwise convert sector adddress to byte address
|
||
EnableCard();
|
||
|
||
if (MMC_Command(CMD18, lba))
|
||
{
|
||
iprintf("CMD18 (READ_MULTIPLE_BLOCK): invalid response 0x%02X (lba=%u)\r", response, lba);
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
|
||
while (nBlockCount--)
|
||
{
|
||
// now we are waiting for data token, it takes around 300us
|
||
timeout = 0;
|
||
while ((SPI(0xFF)) != 0xFE)
|
||
{
|
||
if (timeout++ >= 1000000) // we can't wait forever
|
||
{
|
||
iprintf("CMD18 (READ_MULTIPLE_BLOCK): no data token! (lba=%u)\r", lba);
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
}
|
||
|
||
if (pReadBuffer == 0)
|
||
{ // in this mode we do not receive data, instead the FPGA captures directly the data stream transmitted by the SD/MMC card
|
||
EnableDMode();
|
||
spi_block(511);
|
||
SPI(0xff); // dummy write for 4096 clocks
|
||
SPI(0xff);
|
||
DisableDMode();
|
||
}
|
||
else
|
||
{
|
||
spi_block_read(pReadBuffer);
|
||
pReadBuffer += 512; // point to next sector
|
||
}
|
||
|
||
SPI(0xFF); // read CRC lo byte
|
||
SPI(0xFF); // read CRC hi byte
|
||
}
|
||
|
||
MMC_CMD12(); // stop multi block transmission
|
||
|
||
DisableCard();
|
||
return(1);
|
||
}
|
||
|
||
// write 512-byte block
|
||
unsigned char MMC_Write(unsigned long lba, unsigned char *pWriteBuffer)
|
||
{
|
||
unsigned long i;
|
||
|
||
// check of card has been removed and try to re-initialize it
|
||
if(!check_card()) return 0;
|
||
|
||
if (CardType != CARDTYPE_SDHC) // SDHC cards are addressed in sectors not bytes
|
||
lba = lba << 9; // otherwise convert sector adddress to byte address
|
||
|
||
EnableCard();
|
||
|
||
if (MMC_Command(CMD24, lba))
|
||
{
|
||
iprintf("CMD24 (WRITE_BLOCK): invalid response 0x%02X (lba=%lu)\r", response, lba);
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
|
||
SPI(0xFF); // one byte gap
|
||
SPI(0xFE); // send Data Token
|
||
|
||
// send sector bytes
|
||
spi_block_write(pWriteBuffer);
|
||
spi_wait4xfer_end();
|
||
|
||
SPI(0xFF); // send CRC lo byte
|
||
SPI(0xFF); // send CRC hi byte
|
||
|
||
response = SPI(0xFF); // read packet response
|
||
|
||
// Status codes
|
||
// 010 = Data accepted
|
||
// 101 = Data rejected due to CRC error
|
||
// 110 = Data rejected due to write error
|
||
response &= 0x1F;
|
||
if (response != 0x05)
|
||
{
|
||
iprintf("CMD24 (WRITE_BLOCK): invalid status 0x%02X (lba=%lu)\r", response, lba);
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
|
||
// TODO: Move this to the beginning of MMC_Command to interleave Core and SD card better
|
||
|
||
timeout = 0;
|
||
while (spi_in() == 0x00) // wait until the card is not busy
|
||
{
|
||
if (timeout++ >= 1000000)
|
||
{
|
||
iprintf("CMD24 (WRITE_BLOCK): busy wait timeout! (lba=%lu)\r", lba);
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
}
|
||
|
||
// real life values here are ~500 until card becomes not busy again
|
||
// iprintf("W:%d\n", timeout);
|
||
|
||
DisableCard();
|
||
return(1);
|
||
}
|
||
|
||
|
||
// MMC command
|
||
static RAMFUNC unsigned char MMC_Command(unsigned char cmd, unsigned long arg)
|
||
{
|
||
unsigned char c,b;
|
||
|
||
crc = 0;
|
||
|
||
// flush spi, give card a moment to wake up (needed for old 2GB Panasonic card)
|
||
// spi_n(0xff, 8); // this is not flash save if not in ram
|
||
for(b=0;b<8;b++) SPI(0xff);
|
||
|
||
SPI(cmd);
|
||
MMC_CRC(cmd);
|
||
|
||
#if 1
|
||
// code 100 bytes smaller than below
|
||
for(b=0;b<4;b++) {
|
||
c = ((unsigned char*)&arg)[3];
|
||
SPI(c);
|
||
MMC_CRC(c);
|
||
arg <<= 8;
|
||
}
|
||
#else
|
||
c = (unsigned char)(arg >> 24);
|
||
SPI(c);
|
||
MMC_CRC(c);
|
||
|
||
c = (unsigned char)(arg >> 16);
|
||
SPI(c);
|
||
MMC_CRC(c);
|
||
|
||
c = (unsigned char)(arg >> 8);
|
||
SPI(c);
|
||
MMC_CRC(c);
|
||
|
||
c = (unsigned char)(arg);
|
||
SPI(c);
|
||
MMC_CRC(c);
|
||
#endif
|
||
|
||
crc <<= 1;
|
||
crc++;
|
||
SPI(crc);
|
||
|
||
unsigned char Ncr = 100; // Ncr = 0..8 (SD) / 1..8 (MMC)
|
||
do
|
||
response = SPI(0xFF); // get response
|
||
while (response == 0xFF && Ncr--);
|
||
|
||
return response;
|
||
}
|
||
|
||
|
||
// stop multi block data transmission
|
||
static unsigned char MMC_CMD12(void)
|
||
{
|
||
SPI(CMD12); // command
|
||
SPI(0x00);
|
||
SPI(0x00);
|
||
SPI(0x00);
|
||
SPI(0x00);
|
||
SPI(0x00); // dummy CRC7
|
||
SPI(0xFF); // skip stuff byte
|
||
|
||
unsigned char Ncr = 100; // Ncr = 0..8 (SD) / 1..8 (MMC)
|
||
do
|
||
{ response = SPI(0xFF); // get response
|
||
// RS232(response);
|
||
} while (response == 0xFF && Ncr--);
|
||
|
||
timeout = 0;
|
||
while ((SPI(0xFF)) == 0x00) // wait until the card is not busy
|
||
{ // RS232('+');
|
||
if (timeout++ >= 1000000)
|
||
{
|
||
// iprintf("CMD12 (STOP_TRANSMISSION): busy wait timeout!\r");
|
||
DisableCard();
|
||
return(0);
|
||
}
|
||
}
|
||
return response;
|
||
}
|
||
|
||
|
||
// MMC CRC calc
|
||
static RAMFUNC void MMC_CRC(unsigned char c)
|
||
{
|
||
unsigned char i;
|
||
|
||
for (i = 0; i < 8; i++)
|
||
{
|
||
crc <<= 1;
|
||
if (c & 0x80)
|
||
crc ^= 0x09;
|
||
if (crc & 0x80)
|
||
crc ^= 0x09;
|
||
c <<= 1;
|
||
}
|
||
}
|
||
|
||
unsigned char MMC_IsSDHC(void) {
|
||
return(CardType == CARDTYPE_SDHC);
|
||
}
|