/*************************************************************************** * 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_stm32f2_f4.h" #include "interfaces/bsp.h" #include "interfaces/arch_registers.h" #include "core/cache_cortexMx.h" #include "kernel/scheduler/scheduler.h" #include "interfaces/delays.h" #include "kernel/kernel.h" #include "board_settings.h" //For sdVoltage and SD_ONE_BIT_DATABUS definitions #include #include #include //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) /* * The SDMMC1 peripheral in the STM32F7 is basically the old SDIO with the * registers renamed and a few bits changed. Let's map the old names in the new */ #if defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7) #define SDIO SDMMC1 #define RCC_APB2ENR_SDIOEN RCC_APB2ENR_SDMMC1EN #define SDIO_IRQn SDMMC1_IRQn #define SDIO_STA_STBITERR 0 //This bit has been removed #define SDIO_STA_RXOVERR SDMMC_STA_RXOVERR #define SDIO_STA_TXUNDERR SDMMC_STA_TXUNDERR #define SDIO_STA_DTIMEOUT SDMMC_STA_DTIMEOUT #define SDIO_STA_DCRCFAIL SDMMC_STA_DCRCFAIL #define SDIO_STA_CMDSENT SDMMC_STA_CMDSENT #define SDIO_STA_CMDREND SDMMC_STA_CMDREND #define SDIO_STA_CCRCFAIL SDMMC_STA_CCRCFAIL #define SDIO_STA_CTIMEOUT SDMMC_STA_CTIMEOUT #define SDIO_CMD_CPSMEN SDMMC_CMD_CPSMEN #define SDIO_CMD_WAITRESP_0 SDMMC_CMD_WAITRESP_0 #define SDIO_CMD_WAITRESP_1 SDMMC_CMD_WAITRESP_1 #define SDIO_ICR_CTIMEOUTC SDMMC_ICR_CTIMEOUTC #define SDIO_ICR_CCRCFAILC SDMMC_ICR_CCRCFAILC #define SDIO_CLKCR_CLKEN SDMMC_CLKCR_CLKEN #define SDIO_CLKCR_PWRSAV SDMMC_CLKCR_PWRSAV #define SDIO_CLKCR_PWRSAV SDMMC_CLKCR_PWRSAV #define SDIO_MASK_STBITERRIE 0 //This bit has been removed #define SDIO_MASK_RXOVERRIE SDMMC_MASK_RXOVERRIE #define SDIO_MASK_TXUNDERRIE SDMMC_MASK_TXUNDERRIE #define SDIO_MASK_DCRCFAILIE SDMMC_MASK_DCRCFAILIE #define SDIO_MASK_DTIMEOUTIE SDMMC_MASK_DTIMEOUTIE #define SDIO_MASK_DATAENDIE SDMMC_MASK_DATAENDIE #define SDIO_DCTRL_DMAEN SDMMC_DCTRL_DMAEN #define SDIO_DCTRL_DTDIR SDMMC_DCTRL_DTDIR #define SDIO_DCTRL_DTEN SDMMC_DCTRL_DTEN #define SDIO_POWER_PWRCTRL_1 SDMMC_POWER_PWRCTRL_1 #define SDIO_POWER_PWRCTRL_0 SDMMC_POWER_PWRCTRL_0 #endif //defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7) /** * \internal * DMA2 Stream3 interrupt handler */ void __attribute__((naked)) DMA2_Stream3_IRQHandler() { saveContext(); asm volatile("bl _ZN6miosix18DMA2stream3irqImplEv"); restoreContext(); } /** * \internal * SDIO interrupt handler */ #if defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7) void __attribute__((naked)) SDMMC1_IRQHandler() #else //stm32f2 and stm32f4 void __attribute__((naked)) SDIO_IRQHandler() #endif { saveContext(); asm volatile("bl _ZN6miosix11SDIOirqImplEv"); restoreContext(); } namespace miosix { 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 Stream3 interrupt handler actual implementation */ void __attribute__((used)) DMA2stream3irqImpl() { dmaFlags=DMA2->LISR; if(dmaFlags & (DMA_LISR_TEIF3 | DMA_LISR_DMEIF3 | DMA_LISR_FEIF3)) transferError=true; DMA2->LIFCR=DMA_LIFCR_CTCIF3 | DMA_LIFCR_CTEIF3 | DMA_LIFCR_CDMEIF3 | DMA_LIFCR_CFEIF3; if(!waiting) return; waiting->IRQwakeup(); if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority()) Scheduler::IRQfindNextThread(); waiting=0; } /** * \internal * DMA2 Stream3 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; } /* * 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 */ //static const unsigned char sdVoltage=33; //Is defined in board_settings.h static const unsigned int sdVoltageMask=1<<(sdVoltage-13); //See OCR reg 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 * After fixing the FSMC bug in the stm32f1, ST decided to willingly introduce * another quirk in the stm32f4. They introduced a core coupled memory that is * not accessible by the DMA. While from an hardware perspective it may make * sense, it is a bad design decision when viewed from the software side. * This is because if application code allocates a buffer in the core coupled * memory and passes that to an fread() or fwrite() call, that buffer is * forwarded here, and this driver is DMA-based... Now, in an OS such as Miosix * that tries to shield the application developer from such quirks, it is * unacceptable to fail to work in such an use case, so this class exists to * try and work around this. * In essence, the first "bad buffer" that is passed to a readBlock() or * writeBlock() causes the allocation on the heap (which Miosix guarantees * is not allocated in the core coupled memory) of a 512 byte buffer which is * then never deallocated and always reused to deal with these bad buffers. * While this works, performance suffers for two reasons: first, when dealing * with those bad buffers, the filesystem code is no longer zero copy, and * second because multiple block read/writes between bad buffers and the SD * card are implemented as a sequence of single block read/writes. * If you're an application developer and care about speed, try to allocate * your buffers in the heap if you're coding for the STM32F4. */ 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 not inside the CCM */ static bool isGoodBuffer(const void *x) { unsigned int ptr=reinterpret_cast(x); return (ptr<0x10000000) || (ptr>=(0x10000000+64*1024)); } /** * \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 char *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 char *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 char *wordAlignedBuffer; }; const unsigned char *BufferConverter::toWordAligned(const unsigned char *buffer) { originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do if(isGoodBuffer(buffer)) { return buffer; } else { if(wordAlignedBuffer==0) wordAlignedBuffer=new unsigned char[BUFFER_SIZE]; std::memcpy(wordAlignedBuffer,buffer,BUFFER_SIZE); return wordAlignedBuffer; } } unsigned char *BufferConverter::toWordAlignedWithoutCopy( unsigned char *buffer) { if(isGoodBuffer(buffer)) { originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do return buffer; } else { originalBuffer=buffer; //Save original pointer for toOriginalBuffer() if(wordAlignedBuffer==0) wordAlignedBuffer=new unsigned char[BUFFER_SIZE]; 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 char *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); /** * \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::send(CommandType cmd, unsigned int arg) { unsigned char cc=static_cast(cmd); //Handle ACMDxx as CMD55, CMDxx if(cc & 0x80) { DBG("ACMD%d\n",cc & 0x3f); CmdResult r=send(CMD55,(static_cast(rca))<<16); if(r.validateR1Response()==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); } else DBG("CMD%d\n",cc & 0x3f); //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 ClockController // /** * \internal * This class controls the clock speed of the SDIO peripheral. It originated * from a previous version of this driver, where the SDIO was used in polled * mode instead of DMA mode, but has been retained to improve the robustness * of the driver. */ 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 SDIO->CLKCR=CLOCK_400KHz | SDIO_CLKCR_CLKEN; SDIO->DTIMER=240000; //Timeout 600ms expressed in SD_CK cycles } /** * \internal * Automatically select the data speed. 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 binary 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(); /** * \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); static const unsigned int SDIOCLK=48000000; //On stm32f2 SDIOCLK is always 48MHz static const unsigned int CLOCK_400KHz=118; //48MHz/(118+2)=400KHz #ifdef OVERRIDE_SD_CLOCK_DIVIDER_MAX //Some boards using SDRAM cause SDIO TX Underrun occasionally static const unsigned int CLOCK_MAX=OVERRIDE_SD_CLOCK_DIVIDER_MAX; #else //OVERRIDE_SD_CLOCK_DIVIDER_MAX static const unsigned int CLOCK_MAX=0; //48MHz/(0+2) =24MHz #endif //OVERRIDE_SD_CLOCK_DIVIDER_MAX #ifdef SD_ONE_BIT_DATABUS ///\internal Clock enabled, bus width 1bit, clock powersave enabled. static const unsigned int CLKCR_FLAGS=SDIO_CLKCR_CLKEN | SDIO_CLKCR_PWRSAV; #else //SD_ONE_BIT_DATABUS ///\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; #endif //SD_ONE_BIT_DATABUS ///\internal Maximum number of calls to IRQreduceClockSpeed() allowed static const unsigned char MAX_ALLOWED_REDUCTIONS=1; ///\internal value returned by getRetryCount() while *not* calibrating clock. static const unsigned char MAX_RETRY=10; ///\internal Used to allow only one call to reduceClockSpeed() static unsigned char clockReductionAvailable; ///\internal value returned by getRetryCount() 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; unsigned int maxFreq=CLOCK_MAX; 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::reduceClockSpeed() { DBGERR("clock speed reduction requested\n"); //Ensure this function can be called only a few times 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*SDIOCLK)/((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;iICR=0x7ff; DMA2->LIFCR=DMA_LIFCR_CTCIF3 | DMA_LIFCR_CTEIF3 | DMA_LIFCR_CDMEIF3 | DMA_LIFCR_CFEIF3; transferError=false; dmaFlags=sdioFlags=0; waiting=Thread::getCurrentThread(); //Select DMA transfer size based on buffer alignment. Best performance //is achieved when the buffer is aligned on a 4 byte boundary switch(reinterpret_cast(buffer) & 0x3) { case 0: return DMA_SxCR_MSIZE_1; //DMA reads 32bit at a time case 2: return DMA_SxCR_MSIZE_0; //DMA reads 16bit at a time default: return 0; //DMA reads 8bit at a time } } /** * \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 * \param nblk number of blocks to read. * \param lba logical block address of the first block to read. */ static bool multipleBlockRead(unsigned char *buffer, unsigned int nblk, unsigned int lba) { if(nblk==0) return true; while(nblk>32767) { if(multipleBlockRead(buffer,32767,lba)==false) return false; buffer+=32767*512; nblk-=32767; lba+=32767; } if(waitForCardReady()==false) return false; if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC unsigned int memoryTransferSize=dmaTransferCommonSetup(buffer); //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_Stream3->PAR=reinterpret_cast(&SDIO->FIFO); DMA2_Stream3->M0AR=reinterpret_cast(buffer); //Note: DMA2_Stream3->NDTR is don't care in peripheral flow control mode DMA2_Stream3->FCR=DMA_SxFCR_FEIE | //Interrupt on fifo error DMA_SxFCR_DMDIS | //Fifo enabled DMA_SxFCR_FTH_0; //Take action if fifo half full DMA2_Stream3->CR=DMA_SxCR_CHSEL_2 | //Channel 4 (SDIO) DMA_SxCR_PBURST_0 | //4-beat bursts read from SDIO DMA_SxCR_PL_0 | //Medium priority DMA stream memoryTransferSize | //RAM data size depends on alignment DMA_SxCR_PSIZE_1 | //Read 32bit at a time from SDIO DMA_SxCR_MINC | //Increment RAM pointer 0 | //Peripheral to memory direction DMA_SxCR_PFCTRL | //Peripheral is flow controller DMA_SxCR_TCIE | //Interrupt on transfer complete DMA_SxCR_TEIE | //Interrupt on transfer error DMA_SxCR_DMEIE | //Interrupt on direct mode error DMA_SxCR_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_Stream3->CR=0; while(DMA2_Stream3->CR & DMA_SxCR_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; } //Read ok, deal with cache coherence markBufferAfterDmaRead(buffer,nblk*512); 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 * \param nblk number of blocks to write. * \param lba logical block address of the first block to write. */ static bool multipleBlockWrite(const unsigned char *buffer, unsigned int nblk, unsigned int lba) { if(nblk==0) return true; while(nblk>32767) { if(multipleBlockWrite(buffer,32767,lba)==false) return false; buffer+=32767*512; nblk-=32767; lba+=32767; } //Deal with cache coherence markBufferBeforeDmaWrite(buffer,nblk*512); 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; } unsigned int memoryTransferSize=dmaTransferCommonSetup(buffer); //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_Stream3->PAR=reinterpret_cast(&SDIO->FIFO); DMA2_Stream3->M0AR=reinterpret_cast(buffer); //Note: DMA2_Stream3->NDTR is don't care in peripheral flow control mode //Quirk: not enabling DMA_SxFCR_FEIE because the SDIO seems to generate //a spurious fifo error. The code was tested and the transfer completes //successfully even in the presence of this fifo error DMA2_Stream3->FCR=DMA_SxFCR_DMDIS | //Fifo enabled DMA_SxFCR_FTH_1 | //Take action if fifo full DMA_SxFCR_FTH_0; DMA2_Stream3->CR=DMA_SxCR_CHSEL_2 | //Channel 4 (SDIO) DMA_SxCR_PBURST_0 | //4-beat bursts write to SDIO DMA_SxCR_PL_0 | //Medium priority DMA stream memoryTransferSize | //RAM data size depends on alignment DMA_SxCR_PSIZE_1 | //Write 32bit at a time to SDIO DMA_SxCR_MINC | //Increment RAM pointer DMA_SxCR_DIR_0 | //Memory to peripheral direction DMA_SxCR_PFCTRL | //Peripheral is flow controller DMA_SxCR_TEIE | //Interrupt on transfer error DMA_SxCR_DMEIE | //Interrupt on direct mode error DMA_SxCR_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_Stream3->CR=0; while(DMA2_Stream3->CR & DMA_SxCR_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; } // // 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->AHB1ENR |= RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN | RCC_AHB1ENR_DMA2EN; RCC_SYNC(); RCC->APB2ENR |= RCC_APB2ENR_SDIOEN; RCC_SYNC(); sdD0::mode(Mode::ALTERNATE); sdD0::alternateFunction(12); #ifndef SD_ONE_BIT_DATABUS sdD1::mode(Mode::ALTERNATE); sdD1::alternateFunction(12); sdD2::mode(Mode::ALTERNATE); sdD2::alternateFunction(12); sdD3::mode(Mode::ALTERNATE); sdD3::alternateFunction(12); #endif //SD_ONE_BIT_DATABUS sdCLK::mode(Mode::ALTERNATE); sdCLK::alternateFunction(12); sdCMD::mode(Mode::ALTERNATE); sdCMD::alternateFunction(12); } NVIC_SetPriority(DMA2_Stream3_IRQn,15);//Low priority for DMA NVIC_EnableIRQ(DMA2_Stream3_IRQn); NVIC_SetPriority(SDIO_IRQn,15);//Low priority for SDIO NVIC_EnableIRQ(SDIO_IRQn); 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 goodBuffer=BufferConverter::isGoodBuffer(buffer); if(goodBuffer==false) DBG("Buffer inside CCM\n"); for(int i=0;i(buffer), nSectors,lba)==false) error=true; } else { //Fallback code to work around CCM 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 goodBuffer=BufferConverter::isGoodBuffer(buffer); if(goodBuffer==false) DBG("Buffer inside CCM\n"); for(int i=0;i(buffer), nSectors,lba)==false) error=true; } else { //Fallback code to work around CCM 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; } #ifndef SD_ONE_BIT_DATABUS r=Command::send(Command::ACMD6,2); //Set 4 bit bus width if(r.validateR1Response()==false) return; #endif //SD_ONE_BIT_DATABUS 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