/** * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) #include "SdioTeensy.h" #include "SdCardInfo.h" #include "SdioCard.h" //============================================================================== // limit of K66 due to errata KINETIS_K_0N65N. const uint32_t MAX_BLKCNT = 0XFFFF; //============================================================================== #define SDHC_PROCTL_DTW_4BIT 0x01 const uint32_t FIFO_WML = 16; const uint32_t CMD8_RETRIES = 3; const uint32_t BUSY_TIMEOUT_MICROS = 1000000; //============================================================================== const uint32_t SDHC_IRQSTATEN_MASK = SDHC_IRQSTATEN_DMAESEN | SDHC_IRQSTATEN_AC12ESEN | SDHC_IRQSTATEN_DEBESEN | SDHC_IRQSTATEN_DCESEN | SDHC_IRQSTATEN_DTOESEN | SDHC_IRQSTATEN_CIESEN | SDHC_IRQSTATEN_CEBESEN | SDHC_IRQSTATEN_CCESEN | SDHC_IRQSTATEN_CTOESEN | SDHC_IRQSTATEN_DINTSEN | SDHC_IRQSTATEN_TCSEN | SDHC_IRQSTATEN_CCSEN; const uint32_t SDHC_IRQSTAT_CMD_ERROR = SDHC_IRQSTAT_CIE | SDHC_IRQSTAT_CEBE | SDHC_IRQSTAT_CCE | SDHC_IRQSTAT_CTOE; const uint32_t SDHC_IRQSTAT_DATA_ERROR = SDHC_IRQSTAT_AC12E | SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE; const uint32_t SDHC_IRQSTAT_ERROR = SDHC_IRQSTAT_DMAE | SDHC_IRQSTAT_CMD_ERROR | SDHC_IRQSTAT_DATA_ERROR; const uint32_t SDHC_IRQSIGEN_MASK = SDHC_IRQSIGEN_DMAEIEN | SDHC_IRQSIGEN_AC12EIEN | SDHC_IRQSIGEN_DEBEIEN | SDHC_IRQSIGEN_DCEIEN | SDHC_IRQSIGEN_DTOEIEN | SDHC_IRQSIGEN_CIEIEN | SDHC_IRQSIGEN_CEBEIEN | SDHC_IRQSIGEN_CCEIEN | SDHC_IRQSIGEN_CTOEIEN | SDHC_IRQSIGEN_TCIEN; //============================================================================== const uint32_t CMD_RESP_NONE = SDHC_XFERTYP_RSPTYP(0); const uint32_t CMD_RESP_R1 = SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(2); const uint32_t CMD_RESP_R1b = SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(3); const uint32_t CMD_RESP_R2 = SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(1); const uint32_t CMD_RESP_R3 = SDHC_XFERTYP_RSPTYP(2); const uint32_t CMD_RESP_R6 = CMD_RESP_R1; const uint32_t CMD_RESP_R7 = CMD_RESP_R1; #if defined(__MK64FX512__) || defined(__MK66FX1M0__) const uint32_t DATA_READ = SDHC_XFERTYP_DTDSEL | SDHC_XFERTYP_DPSEL; const uint32_t DATA_READ_DMA = DATA_READ | SDHC_XFERTYP_DMAEN; const uint32_t DATA_READ_MULTI_DMA = DATA_READ_DMA | SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_AC12EN | SDHC_XFERTYP_BCEN; const uint32_t DATA_READ_MULTI_PGM = DATA_READ | SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_BCEN; const uint32_t DATA_WRITE_DMA = SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DMAEN; const uint32_t DATA_WRITE_MULTI_DMA = DATA_WRITE_DMA | SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_AC12EN | SDHC_XFERTYP_BCEN; const uint32_t DATA_WRITE_MULTI_PGM = SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_BCEN; #elif defined(__IMXRT1062__) // Use low bits for SDHC_MIX_CTRL since bits 15-0 of SDHC_XFERTYP are reserved. const uint32_t SDHC_MIX_CTRL_MASK = SDHC_MIX_CTRL_DMAEN | SDHC_MIX_CTRL_BCEN | SDHC_MIX_CTRL_AC12EN | SDHC_MIX_CTRL_DDR_EN | SDHC_MIX_CTRL_DTDSEL | SDHC_MIX_CTRL_MSBSEL | SDHC_MIX_CTRL_NIBBLE_POS | SDHC_MIX_CTRL_AC23EN; const uint32_t DATA_READ = SDHC_MIX_CTRL_DTDSEL | SDHC_XFERTYP_DPSEL; const uint32_t DATA_READ_DMA = DATA_READ | SDHC_MIX_CTRL_DMAEN; const uint32_t DATA_READ_MULTI_DMA = DATA_READ_DMA | SDHC_MIX_CTRL_MSBSEL | SDHC_MIX_CTRL_AC12EN | SDHC_MIX_CTRL_BCEN; const uint32_t DATA_READ_MULTI_PGM = DATA_READ | SDHC_MIX_CTRL_MSBSEL; const uint32_t DATA_WRITE_DMA = SDHC_XFERTYP_DPSEL | SDHC_MIX_CTRL_DMAEN; const uint32_t DATA_WRITE_MULTI_DMA = DATA_WRITE_DMA | SDHC_MIX_CTRL_MSBSEL | SDHC_MIX_CTRL_AC12EN | SDHC_MIX_CTRL_BCEN; const uint32_t DATA_WRITE_MULTI_PGM = SDHC_XFERTYP_DPSEL | SDHC_MIX_CTRL_MSBSEL; #endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) const uint32_t ACMD6_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD6) | CMD_RESP_R1; const uint32_t ACMD13_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD13) | CMD_RESP_R1 | DATA_READ_DMA; const uint32_t ACMD41_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD41) | CMD_RESP_R3; const uint32_t ACMD51_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD51) | CMD_RESP_R1 | DATA_READ_DMA; const uint32_t CMD0_XFERTYP = SDHC_XFERTYP_CMDINX(CMD0) | CMD_RESP_NONE; const uint32_t CMD2_XFERTYP = SDHC_XFERTYP_CMDINX(CMD2) | CMD_RESP_R2; const uint32_t CMD3_XFERTYP = SDHC_XFERTYP_CMDINX(CMD3) | CMD_RESP_R6; const uint32_t CMD6_XFERTYP = SDHC_XFERTYP_CMDINX(CMD6) | CMD_RESP_R1 | DATA_READ_DMA; const uint32_t CMD7_XFERTYP = SDHC_XFERTYP_CMDINX(CMD7) | CMD_RESP_R1b; const uint32_t CMD8_XFERTYP = SDHC_XFERTYP_CMDINX(CMD8) | CMD_RESP_R7; const uint32_t CMD9_XFERTYP = SDHC_XFERTYP_CMDINX(CMD9) | CMD_RESP_R2; const uint32_t CMD10_XFERTYP = SDHC_XFERTYP_CMDINX(CMD10) | CMD_RESP_R2; const uint32_t CMD11_XFERTYP = SDHC_XFERTYP_CMDINX(CMD11) | CMD_RESP_R1; const uint32_t CMD12_XFERTYP = SDHC_XFERTYP_CMDINX(CMD12) | CMD_RESP_R1b | SDHC_XFERTYP_CMDTYP(3); const uint32_t CMD13_XFERTYP = SDHC_XFERTYP_CMDINX(CMD13) | CMD_RESP_R1; const uint32_t CMD17_DMA_XFERTYP = SDHC_XFERTYP_CMDINX(CMD17) | CMD_RESP_R1 | DATA_READ_DMA; const uint32_t CMD18_DMA_XFERTYP = SDHC_XFERTYP_CMDINX(CMD18) | CMD_RESP_R1 | DATA_READ_MULTI_DMA; const uint32_t CMD18_PGM_XFERTYP = SDHC_XFERTYP_CMDINX(CMD18) | CMD_RESP_R1 | DATA_READ_MULTI_PGM; const uint32_t CMD24_DMA_XFERTYP = SDHC_XFERTYP_CMDINX(CMD24) | CMD_RESP_R1 | DATA_WRITE_DMA; const uint32_t CMD25_DMA_XFERTYP = SDHC_XFERTYP_CMDINX(CMD25) | CMD_RESP_R1 | DATA_WRITE_MULTI_DMA; const uint32_t CMD25_PGM_XFERTYP = SDHC_XFERTYP_CMDINX(CMD25) | CMD_RESP_R1 | DATA_WRITE_MULTI_PGM; const uint32_t CMD32_XFERTYP = SDHC_XFERTYP_CMDINX(CMD32) | CMD_RESP_R1; const uint32_t CMD33_XFERTYP = SDHC_XFERTYP_CMDINX(CMD33) | CMD_RESP_R1; const uint32_t CMD38_XFERTYP = SDHC_XFERTYP_CMDINX(CMD38) | CMD_RESP_R1b; const uint32_t CMD55_XFERTYP = SDHC_XFERTYP_CMDINX(CMD55) | CMD_RESP_R1; //============================================================================== static bool cardCommand(uint32_t xfertyp, uint32_t arg); static void enableGPIO(bool enable); static void enableDmaIrs(); static void initSDHC(); static bool isBusyCMD13(); static bool isBusyCommandComplete(); static bool isBusyCommandInhibit(); static bool readReg16(uint32_t xfertyp, void* data); static void setSdclk(uint32_t kHzMax); static bool yieldTimeout(bool (*fcn)()); static bool waitDmaStatus(); static bool waitTimeout(bool (*fcn)()); //------------------------------------------------------------------------------ static bool (*m_busyFcn)() = 0; static bool m_initDone = false; static bool m_version2; static bool m_highCapacity; static bool m_transferActive = false; static uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED; static uint32_t m_errorLine = 0; static uint32_t m_rca; static volatile bool m_dmaBusy = false; static volatile uint32_t m_irqstat; static uint32_t m_sdClkKhz = 0; static uint32_t m_ocr; static cid_t m_cid; static csd_t m_csd; static scr_t m_scr; static sds_t m_sds; //============================================================================== #define DBG_TRACE \ Serial.print("TRACE."); \ Serial.println(__LINE__); \ delay(200); #define USE_DEBUG_MODE 0 #if USE_DEBUG_MODE #define DBG_IRQSTAT() \ if (SDHC_IRQSTAT) { \ Serial.print(__LINE__); \ Serial.print(" IRQSTAT "); \ Serial.println(SDHC_IRQSTAT, HEX); \ } static void printRegs(uint32_t line) { uint32_t blkattr = SDHC_BLKATTR; uint32_t xfertyp = SDHC_XFERTYP; uint32_t prsstat = SDHC_PRSSTAT; uint32_t proctl = SDHC_PROCTL; uint32_t irqstat = SDHC_IRQSTAT; Serial.print("\nLINE: "); Serial.println(line); Serial.print("BLKATTR "); Serial.println(blkattr, HEX); Serial.print("XFERTYP "); Serial.print(xfertyp, HEX); Serial.print(" CMD"); Serial.print(xfertyp >> 24); Serial.print(" TYP"); Serial.print((xfertyp >> 2) & 3); if (xfertyp & SDHC_XFERTYP_DPSEL) { Serial.print(" DPSEL"); } Serial.println(); Serial.print("PRSSTAT "); Serial.print(prsstat, HEX); if (prsstat & SDHC_PRSSTAT_BREN) { Serial.print(" BREN"); } if (prsstat & SDHC_PRSSTAT_BWEN) { Serial.print(" BWEN"); } if (prsstat & SDHC_PRSSTAT_RTA) { Serial.print(" RTA"); } if (prsstat & SDHC_PRSSTAT_WTA) { Serial.print(" WTA"); } if (prsstat & SDHC_PRSSTAT_SDOFF) { Serial.print(" SDOFF"); } if (prsstat & SDHC_PRSSTAT_PEROFF) { Serial.print(" PEROFF"); } if (prsstat & SDHC_PRSSTAT_HCKOFF) { Serial.print(" HCKOFF"); } if (prsstat & SDHC_PRSSTAT_IPGOFF) { Serial.print(" IPGOFF"); } if (prsstat & SDHC_PRSSTAT_SDSTB) { Serial.print(" SDSTB"); } if (prsstat & SDHC_PRSSTAT_DLA) { Serial.print(" DLA"); } if (prsstat & SDHC_PRSSTAT_CDIHB) { Serial.print(" CDIHB"); } if (prsstat & SDHC_PRSSTAT_CIHB) { Serial.print(" CIHB"); } Serial.println(); Serial.print("PROCTL "); Serial.print(proctl, HEX); if (proctl & SDHC_PROCTL_SABGREQ) Serial.print(" SABGREQ"); Serial.print(" EMODE"); Serial.print((proctl >> 4) & 3); Serial.print(" DWT"); Serial.print((proctl >> 1) & 3); Serial.println(); Serial.print("IRQSTAT "); Serial.print(irqstat, HEX); if (irqstat & SDHC_IRQSTAT_BGE) { Serial.print(" BGE"); } if (irqstat & SDHC_IRQSTAT_TC) { Serial.print(" TC"); } if (irqstat & SDHC_IRQSTAT_CC) { Serial.print(" CC"); } Serial.print("\nm_irqstat "); Serial.println(m_irqstat, HEX); } #else // USE_DEBUG_MODE #define DBG_IRQSTAT() #endif // USE_DEBUG_MODE //============================================================================== // Error function and macro. #define sdError(code) setSdErrorCode(code, __LINE__) inline bool setSdErrorCode(uint8_t code, uint32_t line) { m_errorCode = code; m_errorLine = line; #if USE_DEBUG_MODE printRegs(line); #endif // USE_DEBUG_MODE return false; } //============================================================================== // ISR static void sdIrs() { SDHC_IRQSIGEN = 0; m_irqstat = SDHC_IRQSTAT; SDHC_IRQSTAT = m_irqstat; #if defined(__IMXRT1062__) SDHC_MIX_CTRL &= ~(SDHC_MIX_CTRL_AC23EN | SDHC_MIX_CTRL_DMAEN); #endif m_dmaBusy = false; } //============================================================================== // GPIO and clock functions. #if defined(__MK64FX512__) || defined(__MK66FX1M0__) //------------------------------------------------------------------------------ static void enableGPIO(bool enable) { const uint32_t PORT_CLK = PORT_PCR_MUX(4) | PORT_PCR_DSE; const uint32_t PORT_CMD_DATA = PORT_CLK | PORT_PCR_PE | PORT_PCR_PS; const uint32_t PORT_PUP = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; PORTE_PCR0 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_D1 PORTE_PCR1 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_D0 PORTE_PCR2 = enable ? PORT_CLK : PORT_PUP; // SDHC_CLK PORTE_PCR3 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_CMD PORTE_PCR4 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_D3 PORTE_PCR5 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_D2 } //------------------------------------------------------------------------------ static void initClock() { #ifdef HAS_KINETIS_MPU // Allow SDHC Bus Master access. MPU_RGDAAC0 |= 0x0C000000; #endif // HAS_KINETIS_MPU // Enable SDHC clock. SIM_SCGC3 |= SIM_SCGC3_SDHC; } static uint32_t baseClock() { return F_CPU; } #elif defined(__IMXRT1062__) //------------------------------------------------------------------------------ static void gpioMux(uint8_t mode) { IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_04 = mode; // DAT2 IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_05 = mode; // DAT3 IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_00 = mode; // CMD IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_01 = mode; // CLK IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_02 = mode; // DAT0 IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_03 = mode; // DAT1 } //------------------------------------------------------------------------------ // add speed strength args? static void enableGPIO(bool enable) { const uint32_t CLOCK_MASK = IOMUXC_SW_PAD_CTL_PAD_PKE | #if defined(ARDUINO_TEENSY41) IOMUXC_SW_PAD_CTL_PAD_DSE(7) | #else // defined(ARDUINO_TEENSY41) IOMUXC_SW_PAD_CTL_PAD_DSE(4) | ///// WHG #endif // defined(ARDUINO_TEENSY41) IOMUXC_SW_PAD_CTL_PAD_SPEED(2); const uint32_t DATA_MASK = CLOCK_MASK | IOMUXC_SW_PAD_CTL_PAD_PUE | IOMUXC_SW_PAD_CTL_PAD_PUS(1); if (enable) { gpioMux(0); IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_04 = DATA_MASK; // DAT2 IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_05 = DATA_MASK; // DAT3 IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_00 = DATA_MASK; // CMD IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_01 = CLOCK_MASK; // CLK IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_02 = DATA_MASK; // DAT0 IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_03 = DATA_MASK; // DAT1 } else { gpioMux(5); } } //------------------------------------------------------------------------------ static void initClock() { /* set PDF_528 PLL2PFD0 */ CCM_ANALOG_PFD_528 |= (1 << 7); CCM_ANALOG_PFD_528 &= ~(0x3F << 0); CCM_ANALOG_PFD_528 |= ((24) & 0x3F << 0); // 12 - 35 CCM_ANALOG_PFD_528 &= ~(1 << 7); /* Enable USDHC clock. */ CCM_CCGR6 |= CCM_CCGR6_USDHC1(CCM_CCGR_ON); CCM_CSCDR1 &= ~(CCM_CSCDR1_USDHC1_CLK_PODF_MASK); CCM_CSCMR1 |= CCM_CSCMR1_USDHC1_CLK_SEL; // PLL2PFD0 // CCM_CSCDR1 |= CCM_CSCDR1_USDHC1_CLK_PODF((7)); / &0x7 WHG CCM_CSCDR1 |= CCM_CSCDR1_USDHC1_CLK_PODF((1)); } //------------------------------------------------------------------------------ static uint32_t baseClock() { uint32_t divider = ((CCM_CSCDR1 >> 11) & 0x7) + 1; return (528000000U * 3) / ((CCM_ANALOG_PFD_528 & 0x3F) / 6) / divider; } #endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) //============================================================================== // Static functions. static bool cardAcmd(uint32_t rca, uint32_t xfertyp, uint32_t arg) { return cardCommand(CMD55_XFERTYP, rca) && cardCommand(xfertyp, arg); } //------------------------------------------------------------------------------ static bool cardCommand(uint32_t xfertyp, uint32_t arg) { DBG_IRQSTAT(); if (waitTimeout(isBusyCommandInhibit)) { return false; // Caller will set errorCode. } SDHC_CMDARG = arg; #if defined(__IMXRT1062__) // Set MIX_CTRL if data transfer. if (xfertyp & SDHC_XFERTYP_DPSEL) { SDHC_MIX_CTRL &= ~SDHC_MIX_CTRL_MASK; SDHC_MIX_CTRL |= xfertyp & SDHC_MIX_CTRL_MASK; } xfertyp &= ~SDHC_MIX_CTRL_MASK; #endif // defined(__IMXRT1062__) SDHC_XFERTYP = xfertyp; if (waitTimeout(isBusyCommandComplete)) { return false; // Caller will set errorCode. } m_irqstat = SDHC_IRQSTAT; SDHC_IRQSTAT = m_irqstat; return (m_irqstat & SDHC_IRQSTAT_CC) && !(m_irqstat & SDHC_IRQSTAT_CMD_ERROR); } //------------------------------------------------------------------------------ static bool cardACMD13(sds_t* scr) { // ACMD13 returns 64 bytes. if (waitTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_CMD13); } enableDmaIrs(); SDHC_DSADDR = (uint32_t)scr; SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(64); SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; if (!cardAcmd(m_rca, ACMD13_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_ACMD13); } if (!waitDmaStatus()) { return sdError(SD_CARD_ERROR_DMA); } return true; } //------------------------------------------------------------------------------ static bool cardACMD51(scr_t* scr) { // ACMD51 returns 8 bytes. if (waitTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_CMD13); } enableDmaIrs(); SDHC_DSADDR = (uint32_t)scr; SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(8); SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; if (!cardAcmd(m_rca, ACMD51_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_ACMD51); } if (!waitDmaStatus()) { return sdError(SD_CARD_ERROR_DMA); } return true; } //------------------------------------------------------------------------------ static void enableDmaIrs() { m_dmaBusy = true; m_irqstat = 0; } //------------------------------------------------------------------------------ static void initSDHC() { initClock(); // Disable GPIO clock. enableGPIO(false); #if defined(__IMXRT1062__) SDHC_MIX_CTRL |= 0x80000000; #endif // (__IMXRT1062__) // Reset SDHC. Use default Water Mark Level of 16. SDHC_SYSCTL |= SDHC_SYSCTL_RSTA | SDHC_SYSCTL_SDCLKFS(0x80); while (SDHC_SYSCTL & SDHC_SYSCTL_RSTA) { } // Set initial SCK rate. setSdclk(SD_MAX_INIT_RATE_KHZ); enableGPIO(true); // Enable desired IRQSTAT bits. SDHC_IRQSTATEN = SDHC_IRQSTATEN_MASK; attachInterruptVector(IRQ_SDHC, sdIrs); NVIC_SET_PRIORITY(IRQ_SDHC, 6 * 16); NVIC_ENABLE_IRQ(IRQ_SDHC); // Send 80 clocks to card. SDHC_SYSCTL |= SDHC_SYSCTL_INITA; while (SDHC_SYSCTL & SDHC_SYSCTL_INITA) { } } //------------------------------------------------------------------------------ static uint32_t statusCMD13() { return cardCommand(CMD13_XFERTYP, m_rca) ? SDHC_CMDRSP0 : 0; } //------------------------------------------------------------------------------ static bool isBusyCMD13() { return !(statusCMD13() & CARD_STATUS_READY_FOR_DATA); } //------------------------------------------------------------------------------ static bool isBusyCommandComplete() { return !(SDHC_IRQSTAT & (SDHC_IRQSTAT_CC | SDHC_IRQSTAT_CMD_ERROR)); } //------------------------------------------------------------------------------ static bool isBusyCommandInhibit() { return SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB; } //------------------------------------------------------------------------------ static bool isBusyDat() { return SDHC_PRSSTAT & (1 << 24) ? false : true; } //------------------------------------------------------------------------------ static bool isBusyDMA() { return m_dmaBusy; } //------------------------------------------------------------------------------ static bool isBusyFifoRead() { return !(SDHC_PRSSTAT & SDHC_PRSSTAT_BREN); } //------------------------------------------------------------------------------ static bool isBusyFifoWrite() { return !(SDHC_PRSSTAT & SDHC_PRSSTAT_BWEN); } //------------------------------------------------------------------------------ static bool isBusyTransferComplete() { return !(SDHC_IRQSTAT & (SDHC_IRQSTAT_TC | SDHC_IRQSTAT_ERROR)); } //------------------------------------------------------------------------------ static bool rdWrSectors(uint32_t xfertyp, uint32_t sector, uint8_t* buf, size_t n) { if ((3 & (uint32_t)buf) || n == 0) { return sdError(SD_CARD_ERROR_DMA); } if (yieldTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_CMD13); } enableDmaIrs(); SDHC_DSADDR = (uint32_t)buf; SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(n) | SDHC_BLKATTR_BLKSIZE(512); SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; if (!cardCommand(xfertyp, m_highCapacity ? sector : 512 * sector)) { return false; } return waitDmaStatus(); } //------------------------------------------------------------------------------ // Read 16 byte CID or CSD register. static bool readReg16(uint32_t xfertyp, void* data) { uint8_t* d = reinterpret_cast(data); if (!cardCommand(xfertyp, m_rca)) { return false; // Caller will set errorCode. } uint32_t sr[] = {SDHC_CMDRSP0, SDHC_CMDRSP1, SDHC_CMDRSP2, SDHC_CMDRSP3}; for (int i = 0; i < 15; i++) { d[14 - i] = sr[i / 4] >> 8 * (i % 4); } d[15] = 0; return true; } //------------------------------------------------------------------------------ static void setSdclk(uint32_t kHzMax) { const uint32_t DVS_LIMIT = 0X10; const uint32_t SDCLKFS_LIMIT = 0X100; uint32_t dvs = 1; uint32_t sdclkfs = 1; uint32_t maxSdclk = 1000 * kHzMax; uint32_t base = baseClock(); while ((base / (sdclkfs * DVS_LIMIT) > maxSdclk) && (sdclkfs < SDCLKFS_LIMIT)) { sdclkfs <<= 1; } while ((base / (sdclkfs * dvs) > maxSdclk) && (dvs < DVS_LIMIT)) { dvs++; } m_sdClkKhz = base / (1000 * sdclkfs * dvs); sdclkfs >>= 1; dvs--; #if defined(__MK64FX512__) || defined(__MK66FX1M0__) // Disable SDHC clock. SDHC_SYSCTL &= ~SDHC_SYSCTL_SDCLKEN; #endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) // Change dividers. uint32_t sysctl = SDHC_SYSCTL & ~(SDHC_SYSCTL_DTOCV_MASK | SDHC_SYSCTL_DVS_MASK | SDHC_SYSCTL_SDCLKFS_MASK); SDHC_SYSCTL = sysctl | SDHC_SYSCTL_DTOCV(0x0E) | SDHC_SYSCTL_DVS(dvs) | SDHC_SYSCTL_SDCLKFS(sdclkfs); // Wait until the SDHC clock is stable. while (!(SDHC_PRSSTAT & SDHC_PRSSTAT_SDSTB)) { } #if defined(__MK64FX512__) || defined(__MK66FX1M0__) // Enable the SDHC clock. SDHC_SYSCTL |= SDHC_SYSCTL_SDCLKEN; #endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) } //------------------------------------------------------------------------------ static bool transferStop() { // This fix allows CDIHB to be cleared in Tennsy 3.x without a reset. SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; if (!cardCommand(CMD12_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_CMD12); } if (yieldTimeout(isBusyDat)) { return sdError(SD_CARD_ERROR_CMD13); } if (SDHC_PRSSTAT & SDHC_PRSSTAT_CDIHB) { // This should not happen after above fix. // Save registers before reset DAT lines. uint32_t irqsststen = SDHC_IRQSTATEN; uint32_t proctl = SDHC_PROCTL & ~SDHC_PROCTL_SABGREQ; // Do reset to clear CDIHB. Should be a better way! SDHC_SYSCTL |= SDHC_SYSCTL_RSTD; // Restore registers. SDHC_IRQSTATEN = irqsststen; SDHC_PROCTL = proctl; } return true; } //------------------------------------------------------------------------------ // Return true if timeout occurs. static bool yieldTimeout(bool (*fcn)()) { m_busyFcn = fcn; uint32_t m = micros(); while (fcn()) { if ((micros() - m) > BUSY_TIMEOUT_MICROS) { m_busyFcn = 0; return true; } yield(); } m_busyFcn = 0; return false; // Caller will set errorCode. } //------------------------------------------------------------------------------ static bool waitDmaStatus() { if (yieldTimeout(isBusyDMA)) { return false; // Caller will set errorCode. } return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); } //------------------------------------------------------------------------------ // Return true if timeout occurs. static bool waitTimeout(bool (*fcn)()) { uint32_t m = micros(); while (fcn()) { if ((micros() - m) > BUSY_TIMEOUT_MICROS) { return true; } } return false; // Caller will set errorCode. } //------------------------------------------------------------------------------ static bool waitTransferComplete() { if (!m_transferActive) { return true; } bool timeOut = waitTimeout(isBusyTransferComplete); m_transferActive = false; m_irqstat = SDHC_IRQSTAT; SDHC_IRQSTAT = m_irqstat; if (timeOut || (m_irqstat & SDHC_IRQSTAT_ERROR)) { return sdError(SD_CARD_ERROR_TRANSFER_COMPLETE); } return true; } //============================================================================== // Start of SdioCard member functions. //============================================================================== bool SdioCard::begin(SdioConfig sdioConfig) { uint32_t kHzSdClk; uint32_t arg; m_sdioConfig = sdioConfig; m_curState = IDLE_STATE; m_initDone = false; m_errorCode = SD_CARD_ERROR_NONE; m_highCapacity = false; m_version2 = false; // initialize controller. initSDHC(); if (!cardCommand(CMD0_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_CMD0); } // Try several times for case of reset delay. for (uint32_t i = 0; i < CMD8_RETRIES; i++) { if (cardCommand(CMD8_XFERTYP, 0X1AA)) { if (SDHC_CMDRSP0 != 0X1AA) { return sdError(SD_CARD_ERROR_CMD8); } m_version2 = true; break; } SDHC_SYSCTL |= SDHC_SYSCTL_RSTA; while (SDHC_SYSCTL & SDHC_SYSCTL_RSTA) { } } // Must support 3.2-3.4 Volts arg = m_version2 ? 0X40300000 : 0x00300000; int m = micros(); do { if (!cardAcmd(0, ACMD41_XFERTYP, arg) || ((micros() - m) > BUSY_TIMEOUT_MICROS)) { return sdError(SD_CARD_ERROR_ACMD41); } } while ((SDHC_CMDRSP0 & 0x80000000) == 0); m_ocr = SDHC_CMDRSP0; if (SDHC_CMDRSP0 & 0x40000000) { // Is high capacity. m_highCapacity = true; } if (!cardCommand(CMD2_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_CMD2); } if (!cardCommand(CMD3_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_CMD3); } m_rca = SDHC_CMDRSP0 & 0xFFFF0000; if (!readReg16(CMD9_XFERTYP, &m_csd)) { return sdError(SD_CARD_ERROR_CMD9); } if (!readReg16(CMD10_XFERTYP, &m_cid)) { return sdError(SD_CARD_ERROR_CMD10); } if (!cardCommand(CMD7_XFERTYP, m_rca)) { return sdError(SD_CARD_ERROR_CMD7); } // Set card to bus width four. if (!cardAcmd(m_rca, ACMD6_XFERTYP, 2)) { return sdError(SD_CARD_ERROR_ACMD6); } // Set SDHC to bus width four. SDHC_PROCTL &= ~SDHC_PROCTL_DTW_MASK; SDHC_PROCTL |= SDHC_PROCTL_DTW(SDHC_PROCTL_DTW_4BIT); SDHC_WML = SDHC_WML_RDWML(FIFO_WML) | SDHC_WML_WRWML(FIFO_WML); if (!cardACMD51(&m_scr)) { return false; } if (!cardACMD13(&m_sds)) { return false; } // Determine if High Speed mode is supported and set frequency. // Check status[16] for error 0XF or status[16] for new mode 0X1. uint8_t status[64]; if (m_scr.sdSpec() > 0 && cardCMD6(0X00FFFFFF, status) && (2 & status[13]) && cardCMD6(0X80FFFFF1, status) && (status[16] & 0XF) == 1) { kHzSdClk = 50000; } else { kHzSdClk = 25000; } // Disable GPIO. enableGPIO(false); // Set the SDHC SCK frequency. setSdclk(kHzSdClk); // Enable GPIO. enableGPIO(true); m_initDone = true; return true; } //------------------------------------------------------------------------------ bool SdioCard::cardCMD6(uint32_t arg, uint8_t* status) { // CMD6 returns 64 bytes. if (waitTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_CMD13); } enableDmaIrs(); SDHC_DSADDR = (uint32_t)status; SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(64); SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; if (!cardCommand(CMD6_XFERTYP, arg)) { return sdError(SD_CARD_ERROR_CMD6); } if (!waitDmaStatus()) { return sdError(SD_CARD_ERROR_DMA); } return true; } //------------------------------------------------------------------------------ bool SdioCard::erase(uint32_t firstSector, uint32_t lastSector) { if (m_curState != IDLE_STATE && !syncDevice()) { return false; } // check for single sector erase if (!m_csd.eraseSingleBlock()) { // erase size mask uint8_t m = m_csd.eraseSize() - 1; if ((firstSector & m) != 0 || ((lastSector + 1) & m) != 0) { // error card can't erase specified area return sdError(SD_CARD_ERROR_ERASE_SINGLE_SECTOR); } } if (!m_highCapacity) { firstSector <<= 9; lastSector <<= 9; } if (!cardCommand(CMD32_XFERTYP, firstSector)) { return sdError(SD_CARD_ERROR_CMD32); } if (!cardCommand(CMD33_XFERTYP, lastSector)) { return sdError(SD_CARD_ERROR_CMD33); } if (!cardCommand(CMD38_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_CMD38); } if (waitTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_ERASE_TIMEOUT); } return true; } //------------------------------------------------------------------------------ uint8_t SdioCard::errorCode() const { return m_errorCode; } //------------------------------------------------------------------------------ uint32_t SdioCard::errorData() const { return m_irqstat; } //------------------------------------------------------------------------------ uint32_t SdioCard::errorLine() const { return m_errorLine; } //------------------------------------------------------------------------------ bool SdioCard::isBusy() { if (m_sdioConfig.useDma()) { return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13(); } else { if (m_transferActive) { if (isBusyTransferComplete()) { return true; } #if defined(__MK64FX512__) || defined(__MK66FX1M0__) if ((SDHC_BLKATTR & 0XFFFF0000) != 0) { return false; } m_transferActive = false; stopTransmission(false); return true; #else // defined(__MK64FX512__) || defined(__MK66FX1M0__) return false; #endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) } // Use DAT0 low as busy. return SDHC_PRSSTAT & (1 << 24) ? false : true; } } //------------------------------------------------------------------------------ uint32_t SdioCard::kHzSdClk() { return m_sdClkKhz; } //------------------------------------------------------------------------------ bool SdioCard::readCID(cid_t* cid) { memcpy(cid, &m_cid, sizeof(cid_t)); return true; } //------------------------------------------------------------------------------ bool SdioCard::readCSD(csd_t* csd) { memcpy(csd, &m_csd, sizeof(csd_t)); return true; } //------------------------------------------------------------------------------ bool SdioCard::readData(uint8_t* dst) { DBG_IRQSTAT(); uint32_t* p32 = reinterpret_cast(dst); if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_RTA)) { SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; noInterrupts(); SDHC_PROCTL |= SDHC_PROCTL_CREQ; SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; interrupts(); } if (waitTimeout(isBusyFifoRead)) { return sdError(SD_CARD_ERROR_READ_FIFO); } for (uint32_t iw = 0; iw < 512 / (4 * FIFO_WML); iw++) { while (0 == (SDHC_PRSSTAT & SDHC_PRSSTAT_BREN)) { } for (uint32_t i = 0; i < FIFO_WML; i++) { p32[i] = SDHC_DATPORT; } p32 += FIFO_WML; } if (waitTimeout(isBusyTransferComplete)) { return sdError(SD_CARD_ERROR_READ_TIMEOUT); } m_irqstat = SDHC_IRQSTAT; SDHC_IRQSTAT = m_irqstat; return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); } //------------------------------------------------------------------------------ bool SdioCard::readOCR(uint32_t* ocr) { *ocr = m_ocr; return true; } //------------------------------------------------------------------------------ bool SdioCard::readSCR(scr_t* scr) { memcpy(scr, &m_scr, sizeof(scr_t)); return true; } //------------------------------------------------------------------------------ bool SdioCard::readSDS(sds_t* sds) { memcpy(sds, &m_sds, sizeof(sds_t)); return true; } //------------------------------------------------------------------------------ bool SdioCard::readSector(uint32_t sector, uint8_t* dst) { if (m_sdioConfig.useDma()) { uint8_t aligned[512]; uint8_t* ptr = (uint32_t)dst & 3 ? aligned : dst; if (!rdWrSectors(CMD17_DMA_XFERTYP, sector, ptr, 1)) { return sdError(SD_CARD_ERROR_CMD17); } if (ptr != dst) { memcpy(dst, aligned, 512); } } else { if (!waitTransferComplete()) { return false; } if (m_curState != READ_STATE || sector != m_curSector) { if (!syncDevice()) { return false; } if (!readStart(sector)) { return false; } m_curSector = sector; m_curState = READ_STATE; } if (!readData(dst)) { return false; } #if defined(__MK64FX512__) || defined(__MK66FX1M0__) if ((SDHC_BLKATTR & 0XFFFF0000) == 0) { if (!syncDevice()) { return false; } } #endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) m_curSector++; } return true; } //------------------------------------------------------------------------------ bool SdioCard::readSectors(uint32_t sector, uint8_t* dst, size_t n) { if (m_sdioConfig.useDma()) { if ((uint32_t)dst & 3) { for (size_t i = 0; i < n; i++, sector++, dst += 512) { if (!readSector(sector, dst)) { return false; // readSector will set errorCode. } } return true; } if (!rdWrSectors(CMD18_DMA_XFERTYP, sector, dst, n)) { return sdError(SD_CARD_ERROR_CMD18); } } else { for (size_t i = 0; i < n; i++) { if (!readSector(sector + i, dst + i * 512UL)) { return false; } } } return true; } //------------------------------------------------------------------------------ // SDHC will do Auto CMD12 after count sectors. bool SdioCard::readStart(uint32_t sector) { DBG_IRQSTAT(); if (yieldTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_CMD13); } SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; #if defined(__IMXRT1062__) // Infinite transfer. SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512); #else // defined(__IMXRT1062__) // Errata - can't do infinite transfer. SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(MAX_BLKCNT) | SDHC_BLKATTR_BLKSIZE(512); #endif // defined(__IMXRT1062__) if (!cardCommand(CMD18_PGM_XFERTYP, m_highCapacity ? sector : 512 * sector)) { return sdError(SD_CARD_ERROR_CMD18); } return true; } //------------------------------------------------------------------------------ bool SdioCard::readStop() { return transferStop(); } //------------------------------------------------------------------------------ uint32_t SdioCard::sectorCount() { return m_csd.capacity(); } //------------------------------------------------------------------------------ uint32_t SdioCard::status() { return statusCMD13(); } //------------------------------------------------------------------------------ bool SdioCard::stopTransmission(bool blocking) { m_curState = IDLE_STATE; // This fix allows CDIHB to be cleared in Tennsy 3.x without a reset. SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; if (!cardCommand(CMD12_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_CMD12); } if (blocking) { if (yieldTimeout(isBusyDat)) { return sdError(SD_CARD_ERROR_CMD13); } } return true; } //------------------------------------------------------------------------------ bool SdioCard::syncDevice() { if (!waitTransferComplete()) { return false; } if (m_curState != IDLE_STATE) { return stopTransmission(true); } return true; } //------------------------------------------------------------------------------ uint8_t SdioCard::type() const { return m_version2 ? m_highCapacity ? SD_CARD_TYPE_SDHC : SD_CARD_TYPE_SD2 : SD_CARD_TYPE_SD1; } //------------------------------------------------------------------------------ bool SdioCard::writeData(const uint8_t* src) { DBG_IRQSTAT(); if (!waitTransferComplete()) { return false; } const uint32_t* p32 = reinterpret_cast(src); if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_WTA)) { SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; SDHC_PROCTL |= SDHC_PROCTL_CREQ; } SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; if (waitTimeout(isBusyFifoWrite)) { return sdError(SD_CARD_ERROR_WRITE_FIFO); } for (uint32_t iw = 0; iw < 512 / (4 * FIFO_WML); iw++) { while (0 == (SDHC_PRSSTAT & SDHC_PRSSTAT_BWEN)) { } for (uint32_t i = 0; i < FIFO_WML; i++) { SDHC_DATPORT = p32[i]; } p32 += FIFO_WML; } m_transferActive = true; return true; } //------------------------------------------------------------------------------ bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) { if (m_sdioConfig.useDma()) { uint8_t* ptr; uint8_t aligned[512]; if (3 & (uint32_t)src) { ptr = aligned; memcpy(aligned, src, 512); } else { ptr = const_cast(src); } if (!rdWrSectors(CMD24_DMA_XFERTYP, sector, ptr, 1)) { return sdError(SD_CARD_ERROR_CMD24); } } else { if (!waitTransferComplete()) { return false; } #if defined(__MK64FX512__) || defined(__MK66FX1M0__) // End transfer with CMD12 if required. if ((SDHC_BLKATTR & 0XFFFF0000) == 0) { if (!syncDevice()) { return false; } } #endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) if (m_curState != WRITE_STATE || m_curSector != sector) { if (!syncDevice()) { return false; } if (!writeStart(sector)) { return false; } m_curSector = sector; m_curState = WRITE_STATE; } if (!writeData(src)) { return false; } m_curSector++; } return true; } //------------------------------------------------------------------------------ bool SdioCard::writeSectors(uint32_t sector, const uint8_t* src, size_t n) { if (m_sdioConfig.useDma()) { uint8_t* ptr = const_cast(src); if (3 & (uint32_t)ptr) { for (size_t i = 0; i < n; i++, sector++, ptr += 512) { if (!writeSector(sector, ptr)) { return false; // writeSector will set errorCode. } } return true; } if (!rdWrSectors(CMD25_DMA_XFERTYP, sector, ptr, n)) { return sdError(SD_CARD_ERROR_CMD25); } } else { for (size_t i = 0; i < n; i++) { if (!writeSector(sector + i, src + i * 512UL)) { return false; } } } return true; } //------------------------------------------------------------------------------ bool SdioCard::writeStart(uint32_t sector) { if (yieldTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_CMD13); } SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; #if defined(__IMXRT1062__) // Infinite transfer. SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512); #else // defined(__IMXRT1062__) // Errata - can't do infinite transfer. SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(MAX_BLKCNT) | SDHC_BLKATTR_BLKSIZE(512); #endif // defined(__IMXRT1062__) if (!cardCommand(CMD25_PGM_XFERTYP, m_highCapacity ? sector : 512 * sector)) { return sdError(SD_CARD_ERROR_CMD25); } return true; } //------------------------------------------------------------------------------ bool SdioCard::writeStop() { return transferStop(); } #endif // defined(__MK64FX512__) defined(__MK66FX1M0__) defined(__IMXRT1062__)