/*************************************************************************** * Copyright (C) 2010, 2011, 2012, 2013, 2014 by Terraneo Federico * * * * 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 2 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. * * * * As a special exception, if other files instantiate templates or use * * macros or inline functions from this file, or you compile this file * * and link it with other works to produce a work based on this file, * * this file does not by itself cause the resulting work to be covered * * by the GNU General Public License. However the source code for this * * file must still be made available in accordance with the GNU General * * Public License. This exception does not invalidate any other reasons * * why a work based on this file might be covered by the GNU General * * Public License. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see * ***************************************************************************/ #include "sd_stm32f1.h" #include "interfaces/bsp.h" #include "interfaces/arch_registers.h" #include "interfaces/delays.h" #include "kernel/kernel.h" #include "kernel/scheduler/scheduler.h" #include "board_settings.h" //For sdVoltage #include #include #include /* * This driver is quite a bit complicated, due to a silicon errata in the * STM32F1 microcontrollers, that prevents concurrent access to the FSMC * (i.e., the external memory controller) by both the CPU and DMA. * Therefore, if __ENABLE_XRAM is defined, the SDIO peripheral is used in * polled mode, otherwise in DMA mode. The use in polled mode is further * complicated by the fact that the SDIO peripheral does not halt the clock * to the SD card if its internal fifo is full. Therefore, when using the * SDIO in polled mode the only solution is to disable interrupts during * the data transfer. To optimize reading and writing speed this code * automatically chooses the best transfer speed using a binary search during * card initialization. Also, other sources of mess are the requirement for * word alignment of pointers when doing DMA transfers or writing to the SDIO * peripheral. Because of that, tryng to fwrite() large bloks of data is faster * if they are word aligned. An easy way to do so is to allocate them on the * heap (and not doing any pointer arithmetic on the value returned by * malloc/new) */ //Note: enabling debugging might cause deadlock when using sleep() or reboot() //The bug won't be fixed because debugging is only useful for driver development ///\internal Debug macro, for normal conditions //#define DBG iprintf #define DBG(x,...) do {} while(0) ///\internal Debug macro, for errors only //#define DBGERR iprintf #define DBGERR(x,...) do {} while(0) #ifndef __ENABLE_XRAM /** * \internal * DMA2 Channel4 interrupt handler */ void __attribute__((naked)) DMA2_Channel4_5_IRQHandler() { saveContext(); asm volatile("bl _ZN6miosix19DMA2channel4irqImplEv"); restoreContext(); } /** * \internal * SDIO interrupt handler */ void __attribute__((naked)) SDIO_IRQHandler() { saveContext(); asm volatile("bl _ZN6miosix11SDIOirqImplEv"); restoreContext(); } #endif //__ENABLE_XRAM namespace miosix { #ifndef __ENABLE_XRAM static volatile bool transferError; ///< \internal DMA or SDIO transfer error static Thread *waiting; ///< \internal Thread waiting for transfer static unsigned int dmaFlags; ///< \internal DMA status flags static unsigned int sdioFlags; ///< \internal SDIO status flags /** * \internal * DMA2 Channel4 interrupt handler actual implementation */ void __attribute__((used)) DMA2channel4irqImpl() { dmaFlags=DMA2->ISR; if(dmaFlags & DMA_ISR_TEIF4) transferError=true; DMA2->IFCR=DMA_IFCR_CGIF4; if(!waiting) return; waiting->IRQwakeup(); if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority()) Scheduler::IRQfindNextThread(); waiting=0; } /** * \internal * DMA2 Channel4 interrupt handler actual implementation */ void __attribute__((used)) SDIOirqImpl() { sdioFlags=SDIO->STA; if(sdioFlags & (SDIO_STA_STBITERR | SDIO_STA_RXOVERR | SDIO_STA_TXUNDERR | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL)) transferError=true; SDIO->ICR=0x7ff;//Clear flags if(!waiting) return; waiting->IRQwakeup(); if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority()) Scheduler::IRQfindNextThread(); waiting=0; } #endif //__ENABLE_XRAM /* * Operating voltage of device. It is sent to the SD card to check if it can * work at this voltage. Range *must* be within 28..36 * Example 33=3.3v */ //const unsigned char sdVoltage=33; //Is defined in board_settings.h const unsigned int sdVoltageMask=1<<(sdVoltage-13); //See OCR register in SD spec /** * \internal * Possible state of the cardType variable. */ enum CardType { Invalid=0, ///<\internal Invalid card type MMC=1<<0, ///<\internal if(cardType==MMC) card is an MMC SDv1=1<<1, ///<\internal if(cardType==SDv1) card is an SDv1 SDv2=1<<2, ///<\internal if(cardType==SDv2) card is an SDv2 SDHC=1<<3 ///<\internal if(cardType==SDHC) card is an SDHC }; ///\internal Type of card. static CardType cardType=Invalid; //SD card GPIOs typedef Gpio sdD0; typedef Gpio sdD1; typedef Gpio sdD2; typedef Gpio sdD3; typedef Gpio sdCLK; typedef Gpio sdCMD; // // Class BufferConverter // /** * \internal * Convert a single buffer of *fixed* and predetermined size to and from * word-aligned. To do so, if the buffer is already word aligned a cast is made, * otherwise a new buffer is allocated. * Note that this class allocates at most ONE buffer at any given time. * Therefore any call to toWordAligned(), toWordAlignedWithoutCopy(), * toOriginalBuffer() or deallocateBuffer() invalidates the buffer previousy * returned by toWordAligned() and toWordAlignedWithoutCopy() */ class BufferConverter { public: /** * \internal * The buffer will be of this size only. */ static const int BUFFER_SIZE=512; /** * \internal * \return true if the pointer is word aligned */ static bool isWordAligned(const void *x) { return (reinterpret_cast(x) & 0x3)==0; } /** * \internal * Convert from a constunsigned char* buffer of size BUFFER_SIZE to a * const unsigned int* word aligned buffer. * If the original buffer is already word aligned it only does a cast, * otherwise it copies the data on the original buffer to a word aligned * buffer. Useful if subseqent code will read from the buffer. * \param a buffer of size BUFFER_SIZE. Can be word aligned or not. * \return a word aligned buffer with the same data of the given buffer */ static const unsigned int *toWordAligned(const unsigned char *buffer); /** * \internal * Convert from an unsigned char* buffer of size BUFFER_SIZE to an * unsigned int* word aligned buffer. * If the original buffer is already word aligned it only does a cast, * otherwise it returns a new buffer which *does not* contain the data * on the original buffer. Useful if subseqent code will write to the * buffer. To move the written data to the original buffer, use * toOriginalBuffer() * \param a buffer of size BUFFER_SIZE. Can be word aligned or not. * \return a word aligned buffer with undefined content. */ static unsigned int *toWordAlignedWithoutCopy(unsigned char *buffer); /** * \internal * Convert the buffer got through toWordAlignedWithoutCopy() to the * original buffer. If the original buffer was word aligned, nothing * happens, otherwise a memcpy is done. * Note that this function does not work on buffers got through * toWordAligned(). */ static void toOriginalBuffer(); /** * \internal * Can be called to deallocate the buffer */ static void deallocateBuffer(); private: static unsigned char *originalBuffer; static unsigned int *wordAlignedBuffer; }; const unsigned int *BufferConverter::toWordAligned(const unsigned char *buffer) { originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do if(isWordAligned(buffer)) { return reinterpret_cast(buffer); } else { if(wordAlignedBuffer==0) wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)]; std::memcpy(wordAlignedBuffer,buffer,BUFFER_SIZE); return wordAlignedBuffer; } } unsigned int *BufferConverter::toWordAlignedWithoutCopy( unsigned char *buffer) { if(isWordAligned(buffer)) { originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do return reinterpret_cast(buffer); } else { originalBuffer=buffer; //Save original pointer for toOriginalBuffer() if(wordAlignedBuffer==0) wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)]; return wordAlignedBuffer; } } void BufferConverter::toOriginalBuffer() { if(originalBuffer==0) return; std::memcpy(originalBuffer,wordAlignedBuffer,BUFFER_SIZE); originalBuffer=0; } void BufferConverter::deallocateBuffer() { originalBuffer=0; //Invalidate also original buffer if(wordAlignedBuffer!=0) { delete[] wordAlignedBuffer; wordAlignedBuffer=0; } } unsigned char *BufferConverter::originalBuffer=0; unsigned int *BufferConverter::wordAlignedBuffer=0; // // Class CmdResult // /** * \internal * Contains the result of an SD/MMC command */ class CmdResult { public: /** * \internal * Possible outcomes of sending a command */ enum Error { Ok=0, /// No errors Timeout, /// Timeout while waiting command reply CRCFail, /// CRC check failed in command reply RespNotMatch,/// Response index does not match command index ACMDFail /// Sending CMD55 failed }; /** * \internal * Default constructor */ CmdResult(): cmd(0), error(Ok), response(0) {} /** * \internal * Constructor, set the response data * \param cmd command index of command that was sent * \param result result of command */ CmdResult(unsigned char cmd, Error error): cmd(cmd), error(error), response(SDIO->RESP1) {} /** * \internal * \return the 32 bit of the response. * May not be valid if getError()!=Ok or the command does not send a * response, such as CMD0 */ unsigned int getResponse() { return response; } /** * \internal * \return command index */ unsigned char getCmdIndex() { return cmd; } /** * \internal * \return the error flags of the response */ Error getError() { return error; } /** * \internal * Checks if errors occurred while sending the command. * \return true if no errors, false otherwise */ bool validateError(); /** * \internal * interprets this->getResponse() as an R1 response, and checks if there are * errors, or everything is ok * \return true on success, false on failure */ bool validateR1Response(); /** * \internal * Same as validateR1Response, but can be called with interrupts disabled. * \return true on success, false on failure */ bool IRQvalidateR1Response(); /** * \internal * interprets this->getResponse() as an R6 response, and checks if there are * errors, or everything is ok * \return true on success, false on failure */ bool validateR6Response(); /** * \internal * \return the card state from an R1 or R6 resonse */ unsigned char getState(); private: unsigned char cmd; ///<\internal Command index that was sent Error error; ///<\internal possible error that occurred unsigned int response; ///<\internal 32bit response }; bool CmdResult::validateError() { switch(error) { case Ok: return true; case Timeout: DBGERR("CMD%d: Timeout\n",cmd); break; case CRCFail: DBGERR("CMD%d: CRC Fail\n",cmd); break; case RespNotMatch: DBGERR("CMD%d: Response does not match\n",cmd); break; case ACMDFail: DBGERR("CMD%d: ACMD Fail\n",cmd); break; } return false; } bool CmdResult::validateR1Response() { if(error!=Ok) return validateError(); //Note: this number is obtained with all the flags of R1 which are errors //(flagged as E in the SD specification), plus CARD_IS_LOCKED because //locked card are not supported by this software driver if((response & 0xfff98008)==0) return true; DBGERR("CMD%d: R1 response error(s):\n",cmd); if(response & (1<<31)) DBGERR("Out of range\n"); if(response & (1<<30)) DBGERR("ADDR error\n"); if(response & (1<<29)) DBGERR("BLOCKLEN error\n"); if(response & (1<<28)) DBGERR("ERASE SEQ error\n"); if(response & (1<<27)) DBGERR("ERASE param\n"); if(response & (1<<26)) DBGERR("WP violation\n"); if(response & (1<<25)) DBGERR("card locked\n"); if(response & (1<<24)) DBGERR("LOCK_UNLOCK failed\n"); if(response & (1<<23)) DBGERR("command CRC failed\n"); if(response & (1<<22)) DBGERR("illegal command\n"); if(response & (1<<21)) DBGERR("ECC fail\n"); if(response & (1<<20)) DBGERR("card controller error\n"); if(response & (1<<19)) DBGERR("unknown error\n"); if(response & (1<<16)) DBGERR("CSD overwrite\n"); if(response & (1<<15)) DBGERR("WP ERASE skip\n"); if(response & (1<<3)) DBGERR("AKE_SEQ error\n"); return false; } bool CmdResult::IRQvalidateR1Response() { if(error!=Ok) return false; if(response & 0xfff98008) return false; return true; } bool CmdResult::validateR6Response() { if(error!=Ok) return validateError(); if((response & 0xe008)==0) return true; DBGERR("CMD%d: R6 response error(s):\n",cmd); if(response & (1<<15)) DBGERR("command CRC failed\n"); if(response & (1<<14)) DBGERR("illegal command\n"); if(response & (1<<13)) DBGERR("unknown error\n"); if(response & (1<<3)) DBGERR("AKE_SEQ error\n"); return false; } unsigned char CmdResult::getState() { unsigned char result=(response>>9) & 0xf; DBG("CMD%d: State: ",cmd); switch(result) { case 0: DBG("Idle\n"); break; case 1: DBG("Ready\n"); break; case 2: DBG("Ident\n"); break; case 3: DBG("Stby\n"); break; case 4: DBG("Tran\n"); break; case 5: DBG("Data\n"); break; case 6: DBG("Rcv\n"); break; case 7: DBG("Prg\n"); break; case 8: DBG("Dis\n"); break; case 9: DBG("Btst\n"); break; default: DBG("Unknown\n"); break; } return result; } // // Class Command // /** * \internal * This class allows sending commands to an SD or MMC */ class Command { public: /** * \internal * SD/MMC commands * - bit #7 is @ 1 if a command is an ACMDxx. send() will send the * sequence CMD55, CMDxx * - bit from #0 to #5 indicate command index (CMD0..CMD63) * - bit #6 is don't care */ enum CommandType { CMD0=0, //GO_IDLE_STATE CMD2=2, //ALL_SEND_CID CMD3=3, //SEND_RELATIVE_ADDR ACMD6=0x80 | 6, //SET_BUS_WIDTH CMD7=7, //SELECT_DESELECT_CARD ACMD41=0x80 | 41, //SEND_OP_COND (SD) CMD8=8, //SEND_IF_COND CMD9=9, //SEND_CSD CMD12=12, //STOP_TRANSMISSION CMD13=13, //SEND_STATUS CMD16=16, //SET_BLOCKLEN CMD17=17, //READ_SINGLE_BLOCK CMD18=18, //READ_MULTIPLE_BLOCK ACMD23=0x80 | 23, //SET_WR_BLK_ERASE_COUNT (SD) CMD24=24, //WRITE_BLOCK CMD25=25, //WRITE_MULTIPLE_BLOCK CMD55=55 //APP_CMD }; /** * \internal * Send a command. * \param cmd command index (CMD0..CMD63) or ACMDxx command * \param arg the 32 bit argument to the command * \return a CmdResult object */ static CmdResult send(CommandType cmd, unsigned int arg) { if(static_cast(cmd) & 0x80) { DBG("ACMD%d\n",static_cast(cmd) & 0x3f); } else { DBG("CMD%d\n",static_cast(cmd) & 0x3f); } return IRQsend(cmd,arg); } /** * \internal * Send a command. Can be called with interrupts disabled as it does not * print any debug information. * \param cmd command index (CMD0..CMD63) or ACMDxx command * \param arg the 32 bit argument to the command * \return a CmdResult object */ static CmdResult IRQsend(CommandType cmd, unsigned int arg); /** * \internal * Set the relative card address, obtained during initialization. * \param r the card's rca */ static void setRca(unsigned short r) { rca=r; } /** * \internal * \return the card's rca, as set by setRca */ static unsigned int getRca() { return static_cast(rca); } private: static unsigned short rca;///<\internal Card's relative address }; CmdResult Command::IRQsend(CommandType cmd, unsigned int arg) { unsigned char cc=static_cast(cmd); //Handle ACMDxx as CMD55, CMDxx if(cc & 0x80) { CmdResult r=IRQsend(CMD55,(static_cast(rca))<<16); if(r.IRQvalidateR1Response()==false) return CmdResult(cc & 0x3f,CmdResult::ACMDFail); //Bit 5 @ 1 = next command will be interpreted as ACMD if((r.getResponse() & (1<<5))==0) return CmdResult(cc & 0x3f,CmdResult::ACMDFail); } //Send command cc &= 0x3f; unsigned int command=SDIO_CMD_CPSMEN | static_cast(cc); if(cc!=CMD0) command |= SDIO_CMD_WAITRESP_0; //CMD0 has no response if(cc==CMD2) command |= SDIO_CMD_WAITRESP_1; //CMD2 has long response if(cc==CMD9) command |= SDIO_CMD_WAITRESP_1; //CMD9 has long response SDIO->ARG=arg; SDIO->CMD=command; //CMD0 has no response, so wait until it is sent if(cc==CMD0) { for(int i=0;i<500;i++) { if(SDIO->STA & SDIO_STA_CMDSENT) { SDIO->ICR=0x7ff;//Clear flags return CmdResult(cc,CmdResult::Ok); } delayUs(1); } SDIO->ICR=0x7ff;//Clear flags return CmdResult(cc,CmdResult::Timeout); } //Command is not CMD0, so wait a reply for(int i=0;i<500;i++) { unsigned int status=SDIO->STA; if(status & SDIO_STA_CMDREND) { SDIO->ICR=0x7ff;//Clear flags if(SDIO->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok); else return CmdResult(cc,CmdResult::RespNotMatch); } if(status & SDIO_STA_CCRCFAIL) { SDIO->ICR=SDIO_ICR_CCRCFAILC; return CmdResult(cc,CmdResult::CRCFail); } if(status & SDIO_STA_CTIMEOUT) break; delayUs(1); } SDIO->ICR=SDIO_ICR_CTIMEOUTC; return CmdResult(cc,CmdResult::Timeout); } unsigned short Command::rca=0; // // Class DataResult // /** * \internal * Contains the result of sending/receiving a data block */ class DataResult { public: /** * \internal * Possible outcomes of sending or receiving data */ enum Error { Ok=0, Timeout, CRCFail, RXOverrun, TXUnderrun, StartBitFail }; /** * \internal * Default constructor */ DataResult(): error(Ok) {} /** * \internal * Constructor, set the result. * \param error error type */ DataResult(Error error): error(error) {} /** * \internal * \return the error flags */ Error getError() { return error; } /** * \internal * Checks if errors occurred while sending/receiving data. * \return true if no errors, false otherwise */ bool validateError(); private: Error error; }; bool DataResult::validateError() { switch(error) { case Ok: return true; case Timeout: DBGERR("Data Timeout\n"); break; case CRCFail: DBGERR("Data CRC Fail\n"); break; case RXOverrun: DBGERR("Data overrun\n"); break; case TXUnderrun: DBGERR("Data underrun\n"); break; case StartBitFail: DBGERR("Data start bit Fail\n"); break; } return false; } // // Class ClockController // /** * \internal * This class controls the clock speed of the SDIO peripheral. The SDIO * peripheral, when used in polled mode, requires two timing critical pieces of * code: the one to send and the one to receive a data block. This because * the peripheral has a 128 byte fifo while the block size is 512 byte, and * if fifo underrun/overrun occurs the peripheral does not pause communcation, * instead it simply aborts the data transfer. Since the speed of the code to * read/write a data block depends on too many factors, such as compiler * optimizations, code running from internal flash or external ram, and the * cpu clock speed, a dynamic clocking approach was chosen. */ class ClockController { public: /** * \internal. Set a low clock speed of 400KHz or less, used for * detecting SD/MMC cards. This function as a side effect enables 1bit bus * width, and disables clock powersave, since it is not allowed by SD spec. */ static void setLowSpeedClock() { clockReductionAvailable=0; // No hardware flow control, SDIO_CK generated on rising edge, 1bit bus // width, no clock bypass, no powersave. // Set low clock speed 400KHz, 72MHz/400KHz-2=178 SDIO->CLKCR=CLOCK_400KHz | SDIO_CLKCR_CLKEN; SDIO->DTIMER=240000; //Timeout 600ms expressed in SD_CK cycles } /** * \internal * Automatically select the data speed. * Since the maximum speed depends on many factors, such as code running in * internal or external RAM, compiler optimizations etc. this routine * selects the highest sustainable data transfer speed. * This is done by binary search until the highest clock speed that causes * no errors is found. * This function as a side effect enables 4bit bus width, and clock * powersave. */ static void calibrateClockSpeed(SDIODriver *sdio); /** * \internal * Since clock speed is set dynamically by bynary search at runtime, a * corner case might be that of a clock speed which results in unreliable * data transfer, that sometimes succeeds, and sometimes fail. * For maximum robustness, this function is provided to reduce the clock * speed slightly in case a data transfer should fail after clock * calibration. To avoid inadvertently considering other kind of issues as * clock issues, this function can be called only MAX_ALLOWED_REDUCTIONS * times after clock calibration, subsequent calls will fail. This will * avoid other issues causing an ever decreasing clock speed. * \return true on success, false on failure */ static bool reduceClockSpeed() { return IRQreduceClockSpeed(); } /** * \internal * Same as reduceClockSpeed(), can be called with interrupts disabled. * \return true on success, false on failure */ static bool IRQreduceClockSpeed(); /** * \internal * Read and write operation do retry during normal use for robustness, but * during clock claibration they must not retry for speed reasons. This * member function returns 1 during clock claibration and MAX_RETRY during * normal use. */ static unsigned char getRetryCount() { return retries; } private: /** * Set SDIO clock speed * \param clkdiv speed is SDIOCLK/(clkdiv+2) */ static void setClockSpeed(unsigned int clkdiv); /** * \internal * Value of SDIO->CLKCR that will give a 400KHz clock, depending on cpu * clock speed. */ #ifdef SYSCLK_FREQ_72MHz static const unsigned int CLOCK_400KHz=178; #elif SYSCLK_FREQ_56MHz static const unsigned int CLOCK_400KHz=138; #elif SYSCLK_FREQ_48MHz static const unsigned int CLOCK_400KHz=118; #elif SYSCLK_FREQ_36MHz static const unsigned int CLOCK_400KHz=88; #elif SYSCLK_FREQ_24MHz static const unsigned int CLOCK_400KHz=58; #else static const unsigned int CLOCK_400KHz=18; #endif ///\internal Clock enabled, bus width 4bit, clock powersave enabled. static const unsigned int CLKCR_FLAGS=SDIO_CLKCR_CLKEN | SDIO_CLKCR_WIDBUS_0 | SDIO_CLKCR_PWRSAV; ///\internal Maximum number of calls to IRQreduceClockSpeed() allowed ///When using polled mode this is a critical parameter, if SDIO driver ///starts to fail, it might be a good idea to increase this static const unsigned char MAX_ALLOWED_REDUCTIONS=7; ///\internal value returned by getRetryCount() while *not* calibrating clock. ///When using polled mode this is a critical parameter, if SDIO driver ///starts to fail, it might be a good idea to increase this static const unsigned char MAX_RETRY=10; ///\internal Used to allow only one call to reduceClockSpeed() static unsigned char clockReductionAvailable; static unsigned char retries; }; void ClockController::calibrateClockSpeed(SDIODriver *sdio) { //During calibration we call readBlock which will call reduceClockSpeed() //so not to invalidate calibration clock reduction must not be available clockReductionAvailable=0; retries=1; DBG("Automatic speed calibration\n"); unsigned int buffer[512/sizeof(unsigned int)]; unsigned int minFreq=CLOCK_400KHz; //400KHz, independent of CPU clock unsigned int maxFreq=1; //24MHz with CPU running @ 72MHz unsigned int selected; while(minFreq-maxFreq>1) { selected=(minFreq+maxFreq)/2; DBG("Trying CLKCR=%d\n",selected); setClockSpeed(selected); if(sdio->readBlock(reinterpret_cast(buffer),512,0)==512) minFreq=selected; else maxFreq=selected; } //Last round of algorithm setClockSpeed(maxFreq); if(sdio->readBlock(reinterpret_cast(buffer),512,0)==512) { DBG("Optimal CLKCR=%d\n",maxFreq); } else { setClockSpeed(minFreq); DBG("Optimal CLKCR=%d\n",minFreq); } //Make clock reduction available clockReductionAvailable=MAX_ALLOWED_REDUCTIONS; retries=MAX_RETRY; } bool ClockController::IRQreduceClockSpeed() { //Ensure this function can be called only twice per calibration if(clockReductionAvailable==0) return false; clockReductionAvailable--; unsigned int currentClkcr=SDIO->CLKCR & 0xff; if(currentClkcr==CLOCK_400KHz) return false; //No lower than this value //If the value of clockcr is low, increasing it by one is enough since //frequency changes a lot, otherwise increase by 2. if(currentClkcr<10) currentClkcr++; else currentClkcr+=2; setClockSpeed(currentClkcr); return true; } void ClockController::setClockSpeed(unsigned int clkdiv) { SDIO->CLKCR=clkdiv | CLKCR_FLAGS; //Timeout 600ms expressed in SD_CK cycles SDIO->DTIMER=(6*SystemCoreClock)/((clkdiv+2)*10); } unsigned char ClockController::clockReductionAvailable=false; unsigned char ClockController::retries=ClockController::MAX_RETRY; // // Data send/receive functions // /** * \internal * Wait until the card is ready for data transfer. * Can be called independently of the card being selected. * \return true on success, false on failure */ static bool waitForCardReady() { const int timeout=1500; //Timeout 1.5 second const int sleepTime=2; for(int i=0;iDLEN and must match the size parameter given to this * function. * \param buffer buffer where to store received data. Its size must be >=size * \param buffer size, which *must* be multiple of 8 words (32bytes) * Note that the size parameter must be expressed in word (4bytes), while * the value in SDIO->DLEN is expressed in bytes. * \return a DataResult object */ static DataResult IRQreceiveDataBlock(unsigned int *buffer, unsigned int size) { // A note on speed. // Due to the auto calibration of SDIO clock speed being done with // IRQreceiveDataBlock(), the speed of this function must be comparable // with the speed of IRQsendDataBlock(), otherwise IRQsendDataBlock() // will fail because of data underrun. const unsigned int *bufend=buffer+size; unsigned int status; for(;;) { status=SDIO->STA; if(status & (SDIO_STA_RXOVERR | SDIO_STA_DCRCFAIL | SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break; if((status & SDIO_STA_RXFIFOHF) && (buffer!=bufend)) { //Read 8 words from the fifo, loop entirely unrolled for speed *buffer=SDIO->FIFO; buffer++; *buffer=SDIO->FIFO; buffer++; *buffer=SDIO->FIFO; buffer++; *buffer=SDIO->FIFO; buffer++; *buffer=SDIO->FIFO; buffer++; *buffer=SDIO->FIFO; buffer++; *buffer=SDIO->FIFO; buffer++; *buffer=SDIO->FIFO; buffer++; } } SDIO->ICR=0x7ff;//Clear flags if(status & SDIO_STA_RXOVERR) return DataResult(DataResult::RXOverrun); if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail); if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout); if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail); //Read eventual data left in the FIFO for(;;) { if((SDIO->STA & SDIO_STA_RXDAVL)==0) break; *buffer=SDIO->FIFO; buffer++; } return DataResult(DataResult::Ok); } /** * \internal * Send a data block. The end of the data block must be told to the SDIO * peripheral in SDIO->DLEN and must match the size parameter given to this * function. * \param buffer buffer where to store received data. Its size must be >=size * \param buffer size, which *must* be multiple of 8 words (32bytes). * Note that the size parameter must be expressed in word (4bytes), while * the value in SDIO->DLEN is expressed in bytes. * \return a DataResult object */ static DataResult IRQsendDataBlock(const unsigned int *buffer, unsigned int size) { // A note on speed. // Due to the auto calibration of SDIO clock speed being done with // IRQreceiveDataBlock(), the speed of this function must be comparable // with the speed of IRQreceiveDataBlock(), otherwise this function // will fail because of data underrun. const unsigned int *bufend=buffer+size; unsigned int status; for(;;) { status=SDIO->STA; if(status & (SDIO_STA_TXUNDERR | SDIO_STA_DCRCFAIL | SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break; if((status & SDIO_STA_TXFIFOHE) && (buffer!=bufend)) { //Write 8 words to the fifo, loop entirely unrolled for speed SDIO->FIFO=*buffer; buffer++; SDIO->FIFO=*buffer; buffer++; SDIO->FIFO=*buffer; buffer++; SDIO->FIFO=*buffer; buffer++; SDIO->FIFO=*buffer; buffer++; SDIO->FIFO=*buffer; buffer++; SDIO->FIFO=*buffer; buffer++; SDIO->FIFO=*buffer; buffer++; } } SDIO->ICR=0x7ff;//Clear flags if(status & SDIO_STA_TXUNDERR) return DataResult(DataResult::TXUnderrun); if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail); if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout); if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail); return DataResult(DataResult::Ok); } /** * \internal * Read a single block of 512 bytes from an SD/MMC card. * Card must be selected prior to caling this function. * \param buffer, a buffer whose size is >=512 bytes, word aligned * \param lba logical block address of the block to read. */ static bool singleBlockRead(unsigned int *buffer, unsigned int lba) { if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC if(waitForCardReady()==false) return false; CmdResult cr; DataResult dr; bool failed=true; for(;;) { // Since we read with polling, a context switch or interrupt here // would cause a fifo overrun, so we disable interrupts. FastInterruptDisableLock dLock; SDIO->DLEN=512; //Block size 512 bytes, block data xfer, from card to controller SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN; cr=Command::IRQsend(Command::CMD17,lba); if(cr.IRQvalidateR1Response()) { dr=IRQreceiveDataBlock(buffer,512/sizeof(unsigned int)); SDIO->DCTRL=0; //Disable data path state machine //If failed because too slow check if it is possible to reduce speed if(dr.getError()==DataResult::RXOverrun) { if(ClockController::IRQreduceClockSpeed()) { //Disabling interrupts for too long is bad FastInterruptEnableLock eLock(dLock); //After an error during data xfer the card might be a little //confused. So send STOP_TRANSMISSION command to reassure it cr=Command::send(Command::CMD12,0); if(cr.validateR1Response()) continue; } } if(dr.getError()==DataResult::Ok) failed=false; } break; } if(failed) { cr.validateR1Response(); dr.validateError(); //After an error during data xfer the card might be a little //confused. So send STOP_TRANSMISSION command to reassure it cr=Command::send(Command::CMD12,0); cr.validateR1Response(); return false; } return true; } /** * \internal * Write a single block of 512 bytes to an SD/MMC card * Card must be selected prior to caling this function. * \param buffer, a buffer whose size is >=512 bytes * \param lba logical block address of the block to write. */ static bool singleBlockWrite(const unsigned int *buffer, unsigned int lba) { if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC if(waitForCardReady()==false) return false; bool failed=true; CmdResult cr; DataResult dr; for(;;) { // Since we write with polling, a context switch or interrupt here // would cause a fifo overrun, so we disable interrupts. FastInterruptDisableLock dLock; cr=Command::IRQsend(Command::CMD24,lba); if(cr.IRQvalidateR1Response()) { SDIO->DLEN=512; //Block size 512 bytes, block data xfer, from controller to card SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTEN; dr=IRQsendDataBlock(buffer,512/sizeof(unsigned int)); SDIO->DCTRL=0; //Disable data path state machine //If failed because too slow check if it is possible to reduce speed if(dr.getError()==DataResult::TXUnderrun) { if(ClockController::IRQreduceClockSpeed()) { //Disabling interrupts for too long is bad FastInterruptEnableLock eLock(dLock); //After an error during data xfer the card might be a little //confused. So send STOP_TRANSMISSION command to reassure it cr=Command::send(Command::CMD12,0); if(cr.validateR1Response()) continue; } } if(dr.getError()==DataResult::Ok) failed=false; } break; } if(failed) { cr.validateR1Response(); dr.validateError(); //After an error during data xfer the card might be a little //confused. So send STOP_TRANSMISSION command to reassure it cr=Command::send(Command::CMD12,0); cr.validateR1Response(); return false; } return true; } #else //__ENABLE_XRAM /** * \internal * Prints the errors that may occur during a DMA transfer */ static void displayBlockTransferError() { DBGERR("Block transfer error\n"); if(dmaFlags & DMA_ISR_TEIF4) DBGERR("* DMA Transfer error\n"); if(sdioFlags & SDIO_STA_STBITERR) DBGERR("* SDIO Start bit error\n"); if(sdioFlags & SDIO_STA_RXOVERR) DBGERR("* SDIO RX Overrun\n"); if(sdioFlags & SDIO_STA_TXUNDERR) DBGERR("* SDIO TX Underrun error\n"); if(sdioFlags & SDIO_STA_DCRCFAIL) DBGERR("* SDIO Data CRC fail\n"); if(sdioFlags & SDIO_STA_DTIMEOUT) DBGERR("* SDIO Data timeout\n"); } /** * \internal * Read a given number of contiguous 512 byte blocks from an SD/MMC card. * Card must be selected prior to calling this function. * \param buffer, a buffer whose size is 512*nblk bytes, word aligned * \param nblk number of blocks to read. * \param lba logical block address of the first block to read. */ static bool multipleBlockRead(unsigned int *buffer, unsigned int nblk, unsigned int lba) { if(nblk==0) return true; while(nblk>511) { if(multipleBlockRead(buffer,511,lba)==false) return false; buffer+=511*512; nblk-=511; lba+=511; } if(waitForCardReady()==false) return false; if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC //Clear both SDIO and DMA interrupt flags SDIO->ICR=0x7ff; DMA2->IFCR=DMA_IFCR_CGIF4; transferError=false; dmaFlags=sdioFlags=0; waiting=Thread::getCurrentThread(); //Data transfer is considered complete once the DMA transfer complete //interrupt occurs, that happens when the last data was written in the //buffer. Both SDIO and DMA error interrupts are active to catch errors SDIO->MASK=SDIO_MASK_STBITERRIE | //Interrupt on start bit error SDIO_MASK_RXOVERRIE | //Interrupt on rx underrun SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout DMA2_Channel4->CPAR=reinterpret_cast(&SDIO->FIFO); DMA2_Channel4->CMAR=reinterpret_cast(buffer); DMA2_Channel4->CNDTR=nblk*512/sizeof(unsigned int); DMA2_Channel4->CCR=DMA_CCR4_PL_1 | //High priority DMA stream DMA_CCR4_MSIZE_1 | //Write 32bit at a time to RAM DMA_CCR4_PSIZE_1 | //Read 32bit at a time from SDIO DMA_CCR4_MINC | //Increment RAM pointer 0 | //Peripheral to memory direction DMA_CCR4_TCIE | //Interrupt on transfer complete DMA_CCR4_TEIE | //Interrupt on transfer error DMA_CCR4_EN; //Start the DMA SDIO->DLEN=nblk*512; if(waiting==0) { DBGERR("Premature wakeup\n"); transferError=true; } CmdResult cr=Command::send(nblk>1 ? Command::CMD18 : Command::CMD17,lba); if(cr.validateR1Response()) { //Block size 512 bytes, block data xfer, from card to controller SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN; FastInterruptDisableLock dLock; while(waiting) { Thread::IRQwait(); { FastInterruptEnableLock eLock(dLock); Thread::yield(); } } } else transferError=true; DMA2_Channel4->CCR=0; while(DMA2_Channel4->CCR & DMA_CCR4_EN) ; //DMA may take time to stop SDIO->DCTRL=0; //Disable data path state machine SDIO->MASK=0; // CMD12 is sent to end CMD18 (multiple block read), or to abort an // unfinished read in case of errors if(nblk>1 || transferError) cr=Command::send(Command::CMD12,0); if(transferError || cr.validateR1Response()==false) { displayBlockTransferError(); ClockController::reduceClockSpeed(); return false; } return true; } /** * \internal * Write a given number of contiguous 512 byte blocks to an SD/MMC card. * Card must be selected prior to calling this function. * \param buffer, a buffer whose size is 512*nblk bytes, word aligned * \param nblk number of blocks to write. * \param lba logical block address of the first block to write. */ static bool multipleBlockWrite(const unsigned int *buffer, unsigned int nblk, unsigned int lba) { if(nblk==0) return true; while(nblk>511) { if(multipleBlockWrite(buffer,511,lba)==false) return false; buffer+=511*512; nblk-=511; lba+=511; } if(waitForCardReady()==false) return false; if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC if(nblk>1) { CmdResult cr=Command::send(Command::ACMD23,nblk); if(cr.validateR1Response()==false) return false; } //Clear both SDIO and DMA interrupt flags SDIO->ICR=0x7ff; DMA2->IFCR=DMA_IFCR_CGIF4; transferError=false; dmaFlags=sdioFlags=0; waiting=Thread::getCurrentThread(); //Data transfer is considered complete once the SDIO transfer complete //interrupt occurs, that happens when the last data was written to the SDIO //Both SDIO and DMA error interrupts are active to catch errors SDIO->MASK=SDIO_MASK_DATAENDIE | //Interrupt on data end SDIO_MASK_STBITERRIE | //Interrupt on start bit error SDIO_MASK_RXOVERRIE | //Interrupt on rx underrun SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout DMA2_Channel4->CPAR=reinterpret_cast(&SDIO->FIFO); DMA2_Channel4->CMAR=reinterpret_cast(buffer); DMA2_Channel4->CNDTR=nblk*512/sizeof(unsigned int); DMA2_Channel4->CCR=DMA_CCR4_PL_1 | //High priority DMA stream DMA_CCR4_MSIZE_1 | //Read 32bit at a time from RAM DMA_CCR4_PSIZE_1 | //Write 32bit at a time to SDIO DMA_CCR4_MINC | //Increment RAM pointer DMA_CCR4_DIR | //Memory to peripheral direction DMA_CCR4_TEIE | //Interrupt on transfer error DMA_CCR4_EN; //Start the DMA SDIO->DLEN=nblk*512; if(waiting==0) { DBGERR("Premature wakeup\n"); transferError=true; } CmdResult cr=Command::send(nblk>1 ? Command::CMD25 : Command::CMD24,lba); if(cr.validateR1Response()) { //Block size 512 bytes, block data xfer, from card to controller SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN; FastInterruptDisableLock dLock; while(waiting) { Thread::IRQwait(); { FastInterruptEnableLock eLock(dLock); Thread::yield(); } } } else transferError=true; DMA2_Channel4->CCR=0; while(DMA2_Channel4->CCR & DMA_CCR4_EN) ; //DMA may take time to stop SDIO->DCTRL=0; //Disable data path state machine SDIO->MASK=0; // CMD12 is sent to end CMD25 (multiple block write), or to abort an // unfinished write in case of errors if(nblk>1 || transferError) cr=Command::send(Command::CMD12,0); if(transferError || cr.validateR1Response()==false) { displayBlockTransferError(); ClockController::reduceClockSpeed(); return false; } return true; } #endif //__ENABLE_XRAM // // Class CardSelector // /** * \internal * Simple RAII class for selecting an SD/MMC card an automatically deselect it * at the end of the scope. */ class CardSelector { public: /** * \internal * Constructor. Selects the card. * The result of the select operation is available through its succeded() * member function */ explicit CardSelector() { success=Command::send( Command::CMD7,Command::getRca()<<16).validateR1Response(); } /** * \internal * \return true if the card was selected, false on error */ bool succeded() { return success; } /** * \internal * Destructor, ensures that the card is deselected */ ~CardSelector() { Command::send(Command::CMD7,0); //Deselect card. This will timeout } private: bool success; }; // // Initialization helper functions // /** * \internal * Initialzes the SDIO peripheral in the STM32 */ static void initSDIOPeripheral() { { //Doing read-modify-write on RCC->APBENR2 and gpios, better be safe FastInterruptDisableLock lock; RCC->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN; RCC_SYNC(); #ifdef __ENABLE_XRAM RCC->AHBENR |= RCC_AHBENR_SDIOEN; #else //__ENABLE_XRAM RCC->AHBENR |= RCC_AHBENR_SDIOEN | RCC_AHBENR_DMA2EN; #endif //__ENABLE_XRAM RCC_SYNC(); sdD0::mode(Mode::ALTERNATE); sdD1::mode(Mode::ALTERNATE); sdD2::mode(Mode::ALTERNATE); sdD3::mode(Mode::ALTERNATE); sdCLK::mode(Mode::ALTERNATE); sdCMD::mode(Mode::ALTERNATE); } #ifndef __ENABLE_XRAM NVIC_SetPriority(DMA2_Channel4_5_IRQn,15);//Low priority for DMA NVIC_EnableIRQ(DMA2_Channel4_5_IRQn); NVIC_SetPriority(SDIO_IRQn,15);//Low priority for SDIO NVIC_EnableIRQ(SDIO_IRQn); #endif //__ENABLE_XRAM SDIO->POWER=0; //Power off state delayUs(1); SDIO->CLKCR=0; SDIO->CMD=0; SDIO->DCTRL=0; SDIO->ICR=0xc007ff; SDIO->POWER=SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0; //Power on state //This delay is particularly important: when setting the POWER register a //glitch on the CMD pin happens. This glitch has a fast fall time and a slow //rise time resembling an RC charge with a ~6us rise time. If the clock is //started too soon, the card sees a clock pulse while CMD is low, and //interprets it as a start bit. No, setting POWER to powerup does not //eliminate the glitch. delayUs(10); ClockController::setLowSpeedClock(); } /** * \internal * Detect if the card is an SDHC, SDv2, SDv1, MMC * \return Type of card: (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC * or Invalid if card detect failed. */ static CardType detectCardType() { const int INIT_TIMEOUT=200; //200*10ms= 2 seconds CmdResult r=Command::send(Command::CMD8,0x1aa); if(r.validateError()) { //We have an SDv2 card connected if(r.getResponse()!=0x1aa) { DBGERR("CMD8 validation: voltage range fail\n"); return Invalid; } for(int i=0;i SDIODriver::instance() { static FastMutex m; static intrusive_ref_ptr instance; Lock l(m); if(!instance) instance=new SDIODriver(); return instance; } ssize_t SDIODriver::readBlock(void* buffer, size_t size, off_t where) { if(where % 512 || size % 512) return -EFAULT; unsigned int lba=where/512; unsigned int nSectors=size/512; Lock l(mutex); DBG("SDIODriver::readBlock(): nSectors=%d\n",nSectors); bool aligned=BufferConverter::isWordAligned(buffer); if(aligned==false) DBG("Buffer misaligned\n"); for(int i=0;i(buffer); unsigned int tempLba=lba; for(unsigned int j=0;j(buffer), nSectors,lba)==false) error=true; } else { unsigned char *tempBuffer=reinterpret_cast(buffer); unsigned int tempLba=lba; for(unsigned int j=0;j0) DBGERR("Read: required %d retries\n",i); return size; } } return -EBADF; } ssize_t SDIODriver::writeBlock(const void* buffer, size_t size, off_t where) { if(where % 512 || size % 512) return -EFAULT; unsigned int lba=where/512; unsigned int nSectors=size/512; Lock l(mutex); DBG("SDIODriver::writeBlock(): nSectors=%d\n",nSectors); bool aligned=BufferConverter::isWordAligned(buffer); if(aligned==false) DBG("Buffer misaligned\n"); for(int i=0;i(buffer); unsigned int tempLba=lba; for(unsigned int j=0;j(buffer), nSectors,lba)==false) error=true; } else { const unsigned char *tempBuffer= reinterpret_cast(buffer); unsigned int tempLba=lba; for(unsigned int j=0;j0) DBGERR("Write: required %d retries\n",i); return size; } } return -EBADF; } int SDIODriver::ioctl(int cmd, void* arg) { DBG("SDIODriver::ioctl()\n"); if(cmd!=IOCTL_SYNC) return -ENOTTY; Lock l(mutex); //Note: no need to select card, since status can be queried even with card //not selected. return waitForCardReady() ? 0 : -EFAULT; } SDIODriver::SDIODriver() : Device(Device::BLOCK) { initSDIOPeripheral(); // This is more important than it seems, since CMD55 requires the card's RCA // as argument. During initalization, after CMD0 the card has an RCA of zero // so without this line ACMD41 will fail and the card won't be initialized. Command::setRca(0); //Send card reset command CmdResult r=Command::send(Command::CMD0,0); if(r.validateError()==false) return; cardType=detectCardType(); if(cardType==Invalid) return; //Card detect failed if(cardType==MMC) return; //MMC cards currently unsupported // Now give an RCA to the card. In theory we should loop and enumerate all // the cards but this driver supports only one card. r=Command::send(Command::CMD2,0); //CMD2 sends R2 response, whose CMDINDEX field is wrong if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::RespNotMatch) { r.validateError(); return; } r=Command::send(Command::CMD3,0); if(r.validateR6Response()==false) return; Command::setRca(r.getResponse()>>16); DBG("Got RCA=%u\n",Command::getRca()); if(Command::getRca()==0) { //RCA=0 can't be accepted, since it is used to deselect cards DBGERR("RCA=0 is invalid\n"); return; } //Lastly, try selecting the card and configure the latest bits { CardSelector selector; if(selector.succeded()==false) return; r=Command::send(Command::CMD13,Command::getRca()<<16);//Get status if(r.validateR1Response()==false) return; if(r.getState()!=4) //4=Tran state { DBGERR("CMD7 was not able to select card\n"); return; } r=Command::send(Command::ACMD6,2); //Set 4 bit bus width if(r.validateR1Response()==false) return; if(cardType!=SDHC) { r=Command::send(Command::CMD16,512); //Set 512Byte block length if(r.validateR1Response()==false) return; } } // Now that card is initialized, perform self calibration of maximum // possible read/write speed. This as a side effect enables 4bit bus width. ClockController::calibrateClockSpeed(this); DBG("SDIO init: Success\n"); } } //namespace miosix