|
|
@@ -20,21 +20,17 @@ |
|
|
|
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) |
|
|
|
#include "SdioCard.h" |
|
|
|
//============================================================================== |
|
|
|
#ifndef SDHC_WML_RDWML_MASK |
|
|
|
#define SDHC_WML_RDWML_MASK (0XFFU) |
|
|
|
#endif |
|
|
|
#ifndef SDHC_WML_RDWML_SHIFT |
|
|
|
#define SDHC_WML_RDWML_SHIFT (0U) |
|
|
|
#endif |
|
|
|
#define SDHC_PROCTL_DTW_4BIT 0x01 |
|
|
|
#define SDHC_PROCTL_DTW_4BIT 0x01 |
|
|
|
const uint32_t FIFO_WML = 16; |
|
|
|
const uint32_t CMD8_RETRIES = 10; |
|
|
|
const uint32_t BUSY_TIMEOUT_MICROS = 500000; |
|
|
|
//============================================================================== |
|
|
|
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_BRRSEN | |
|
|
|
SDHC_IRQSTATEN_BWRSEN | SDHC_IRQSTATEN_DINTSEN | |
|
|
|
SDHC_IRQSTATEN_CTOESEN | SDHC_IRQSTATEN_DINTSEN | |
|
|
|
SDHC_IRQSTATEN_TCSEN | SDHC_IRQSTATEN_CCSEN; |
|
|
|
|
|
|
|
const uint32_t SDHC_IRQSTAT_CMD_ERROR = |
|
|
@@ -79,11 +75,18 @@ 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 | SDHC_XFERTYP_AC12EN; |
|
|
|
|
|
|
|
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 | SDHC_XFERTYP_AC12EN; |
|
|
|
|
|
|
|
const uint32_t ACMD6_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD6) | CMD_RESP_R1; |
|
|
|
|
|
|
|
const uint32_t ACMD41_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD41) | CMD_RESP_R3; |
|
|
@@ -105,19 +108,28 @@ 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 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_XFERTYP = SDHC_XFERTYP_CMDINX(CMD17) | CMD_RESP_R1 | |
|
|
|
DATA_READ_DMA; |
|
|
|
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 CMD18_XFERTYP = SDHC_XFERTYP_CMDINX(CMD18) | CMD_RESP_R1 | |
|
|
|
DATA_READ_MULTI_DMA; |
|
|
|
const uint32_t CMD24_DMA_XFERTYP = SDHC_XFERTYP_CMDINX(CMD24) | CMD_RESP_R1 | |
|
|
|
DATA_WRITE_DMA; |
|
|
|
|
|
|
|
const uint32_t CMD24_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_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; |
|
|
|
|
|
|
@@ -126,49 +138,109 @@ 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 isBusy(); |
|
|
|
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 uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED; |
|
|
|
static uint32_t m_errorLine = 0; |
|
|
|
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 uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED; |
|
|
|
static uint32_t m_errorLine = 0; |
|
|
|
static uint32_t m_rca; |
|
|
|
bool m_waitCmd13 = false; |
|
|
|
static volatile bool m_dmaDone = true; |
|
|
|
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 uint32_t m_ocr; |
|
|
|
//============================================================================= |
|
|
|
// Debug and error macros. |
|
|
|
#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) { |
|
|
|
Serial.print(line); |
|
|
|
Serial.print(" PRSSTAT "); |
|
|
|
Serial.print(SDHC_PRSSTAT, HEX); |
|
|
|
Serial.print(" PROCTL "); |
|
|
|
Serial.print(SDHC_PROCTL, HEX); |
|
|
|
Serial.print(" IRQSTAT "); |
|
|
|
Serial.print(SDHC_IRQSTAT, HEX); |
|
|
|
Serial.print(" m_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; |
|
|
|
return false; |
|
|
|
return false; // setSdErrorCode |
|
|
|
} |
|
|
|
//============================================================================= |
|
|
|
// ISR |
|
|
|
void sdhc_isr() { |
|
|
|
SDHC_IRQSIGEN = 0; |
|
|
|
m_irqstat = SDHC_IRQSTAT; |
|
|
|
SDHC_IRQSTAT = m_irqstat; |
|
|
|
m_dmaDone = true; |
|
|
|
m_dmaBusy = false; |
|
|
|
} |
|
|
|
//============================================================================= |
|
|
|
// Static functions. |
|
|
|
static bool cardAcmd(uint32_t rca, uint32_t xfertyp, uint32_t arg) { |
|
|
|
return cardCommand(CMD55_XFERTYP, rca) && cardCommand (xfertyp, arg); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
static void enableDmaIrs() { |
|
|
|
m_dmaDone = false; |
|
|
|
m_irqstat = 0; |
|
|
|
static bool cardCommand(uint32_t xfertyp, uint32_t arg) { |
|
|
|
DBG_IRQSTAT(); |
|
|
|
if (waitTimeout(isBusyCommandInhibit)) { |
|
|
|
return false; // Caller will set errorCode. |
|
|
|
} |
|
|
|
SDHC_CMDARG = arg; |
|
|
|
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 waitDmaStatus() { |
|
|
|
while (!m_dmaDone) { |
|
|
|
yield(); |
|
|
|
static bool cardCMD6(uint32_t arg, uint8_t* status) { |
|
|
|
// CMD6 returns 64 bytes. |
|
|
|
if (waitTimeout(isBusyCMD13)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD13); |
|
|
|
} |
|
|
|
return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); |
|
|
|
enableDmaIrs(); |
|
|
|
SDHC_DSADDR = (uint32_t)status; |
|
|
|
SDHC_CMDARG = arg; |
|
|
|
SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(64); |
|
|
|
SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; |
|
|
|
SDHC_XFERTYP = CMD6_XFERTYP; |
|
|
|
|
|
|
|
if (!waitDmaStatus()) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD6); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
static void enableGPIO(bool enable) { |
|
|
@@ -183,7 +255,12 @@ static void enableGPIO(bool enable) { |
|
|
|
PORTE_PCR5 = enable ? PORT_CMD_DATA : 0; // SDHC_D2 |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
static void SDHC_Init() { |
|
|
|
static void enableDmaIrs() { |
|
|
|
m_dmaBusy = true; |
|
|
|
m_irqstat = 0; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
static void initSDHC() { |
|
|
|
#ifdef HAS_KINETIS_MPU |
|
|
|
// Allow SDHC Bus Master access. |
|
|
|
MPU_RGDAAC0 |= 0x0C000000; |
|
|
@@ -215,16 +292,46 @@ static void SDHC_Init() { |
|
|
|
} |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
static bool isBusyCMD13() { |
|
|
|
if (!cardCommand(CMD13_XFERTYP, m_rca)) { |
|
|
|
// Caller will timeout. |
|
|
|
return true; |
|
|
|
} |
|
|
|
return !(SDHC_CMDRSP0 & 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 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 rdWrBlocks(uint32_t xfertyp, |
|
|
|
uint32_t lba, uint8_t* buf, size_t n) { |
|
|
|
if ((3 & (uint32_t)buf) || n == 0) { |
|
|
|
return sdError(SD_CARD_ERROR_DMA); |
|
|
|
} |
|
|
|
m_waitCmd13 = true; |
|
|
|
while (isBusy()) { |
|
|
|
yield(); |
|
|
|
if (yieldTimeout(isBusyCMD13)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD13); |
|
|
|
} |
|
|
|
m_waitCmd13 = false; |
|
|
|
enableDmaIrs(); |
|
|
|
SDHC_DSADDR = (uint32_t)buf; |
|
|
|
SDHC_CMDARG = m_highCapacity ? lba : 512*lba; |
|
|
@@ -235,30 +342,18 @@ static bool rdWrBlocks(uint32_t xfertyp, |
|
|
|
return waitDmaStatus(); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
static bool cardCommand(uint32_t xfertyp, uint32_t arg) { |
|
|
|
SDHC_CMDARG = arg; |
|
|
|
SDHC_XFERTYP = xfertyp; |
|
|
|
|
|
|
|
do { |
|
|
|
m_irqstat = SDHC_IRQSTAT; |
|
|
|
} while (!(m_irqstat & (SDHC_IRQSTAT_CC | SDHC_IRQSTAT_CMD_ERROR))); |
|
|
|
|
|
|
|
SDHC_IRQSTAT = m_irqstat; |
|
|
|
|
|
|
|
return (m_irqstat & SDHC_IRQSTAT_CC) && |
|
|
|
!(m_irqstat & SDHC_IRQSTAT_CMD_ERROR); |
|
|
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
static bool cardAcmd(uint32_t rca, uint32_t xfertyp, uint32_t arg) { |
|
|
|
return cardCommand(CMD55_XFERTYP, rca) && cardCommand (xfertyp, arg); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
static bool isBusy() { |
|
|
|
if (!cardCommand(CMD13_XFERTYP, m_rca)) { |
|
|
|
// Should have timeout in caller? |
|
|
|
return true; |
|
|
|
// Read 16 byte CID or CSD register. |
|
|
|
static bool readReg16(uint32_t xfertyp, void* data) { |
|
|
|
uint8_t* d = reinterpret_cast<uint8_t*>(data); |
|
|
|
if (!cardCommand(xfertyp, m_rca)) { |
|
|
|
return false; // Caller will set errorCode. |
|
|
|
} |
|
|
|
return !(SDHC_CMDRSP0 & CARD_STATUS_READY_FOR_DATA); |
|
|
|
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) { |
|
|
@@ -294,55 +389,99 @@ static void setSdclk(uint32_t kHzMax) { |
|
|
|
// Enable the SDHC clock. |
|
|
|
SDHC_SYSCTL |= SDHC_SYSCTL_SDCLKEN; |
|
|
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
static bool cardCMD6(uint32_t arg, uint8_t* status) { |
|
|
|
// CMD6 returns 64 bytes. |
|
|
|
while (isBusy()) { |
|
|
|
} |
|
|
|
enableDmaIrs(); |
|
|
|
SDHC_DSADDR = (uint32_t)status; |
|
|
|
SDHC_CMDARG = arg; |
|
|
|
SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(64); |
|
|
|
SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; |
|
|
|
SDHC_XFERTYP = CMD6_XFERTYP; |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
static bool transferStop() { |
|
|
|
DBG_IRQSTAT(); |
|
|
|
|
|
|
|
if (!waitDmaStatus()) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD6); |
|
|
|
if (!cardCommand(CMD12_XFERTYP, 0)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD12); |
|
|
|
} |
|
|
|
if (yieldTimeout(isBusyCMD13)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD13); |
|
|
|
} |
|
|
|
// 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; |
|
|
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
static bool cardInit() { |
|
|
|
uint32_t kbaudrate; |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
// 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. |
|
|
|
} |
|
|
|
//============================================================================= |
|
|
|
bool SdioCard::begin() { |
|
|
|
uint32_t kHzSdClk; |
|
|
|
uint32_t arg; |
|
|
|
m_initDone = false; |
|
|
|
m_errorCode = SD_CARD_ERROR_NONE; |
|
|
|
m_highCapacity = false; |
|
|
|
m_version2 = false; |
|
|
|
|
|
|
|
// initialize controller. |
|
|
|
SDHC_Init(); |
|
|
|
initSDHC(); |
|
|
|
|
|
|
|
if (!cardCommand(CMD0_XFERTYP, 0)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD0); |
|
|
|
} |
|
|
|
if (cardCommand(CMD8_XFERTYP, 0X1AA)) { |
|
|
|
if (SDHC_CMDRSP0 != 0X1AA) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD8); |
|
|
|
// 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; |
|
|
|
} |
|
|
|
m_version2 = true; |
|
|
|
} |
|
|
|
|
|
|
|
arg = m_version2 ? 0X40300000 : 0x00300000; |
|
|
|
int i = 0; |
|
|
|
uint32_t m = micros(); |
|
|
|
do { |
|
|
|
if (!cardAcmd(0, ACMD41_XFERTYP, arg) || i++ > 1000) { |
|
|
|
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 |
|
|
|
// Is high capacity. |
|
|
|
m_highCapacity = true; |
|
|
|
} |
|
|
|
if (!cardCommand(CMD2_XFERTYP, 0)) { |
|
|
@@ -362,80 +501,45 @@ static bool cardInit() { |
|
|
|
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 Data bus width to four. |
|
|
|
// 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); |
|
|
|
|
|
|
|
// Determine if High Speed mode is supported and set frequency. |
|
|
|
uint8_t status[64]; |
|
|
|
if (cardCMD6(0X00FFFFFF, status) && (2 & status[13]) && |
|
|
|
cardCMD6(0X80FFFFF1, status) && (status[16] & 0XF) == 1) { |
|
|
|
kbaudrate = 50000; |
|
|
|
kHzSdClk = 50000; |
|
|
|
} else { |
|
|
|
kbaudrate = 25000; |
|
|
|
kHzSdClk = 25000; |
|
|
|
} |
|
|
|
|
|
|
|
// disable GPIO |
|
|
|
enableGPIO(false); |
|
|
|
|
|
|
|
// Set the SDHC SCK frequency. |
|
|
|
setSdclk(kbaudrate); |
|
|
|
setSdclk(kHzSdClk); |
|
|
|
|
|
|
|
// enable GPIO |
|
|
|
enableGPIO(true); |
|
|
|
|
|
|
|
m_initDone = true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
static bool readReg16(uint32_t xfertyp, void* data) { |
|
|
|
if (!cardCommand(xfertyp, m_rca)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
uint8_t* d = reinterpret_cast<uint8_t*>(data); |
|
|
|
d[0] = SDHC_CMDRSP3 >> 16; |
|
|
|
d[1] = SDHC_CMDRSP3 >> 8; |
|
|
|
d[2] = SDHC_CMDRSP3; |
|
|
|
d[3] = SDHC_CMDRSP2 >> 24; |
|
|
|
d[4] = SDHC_CMDRSP2 >> 16; |
|
|
|
d[5] = SDHC_CMDRSP2 >> 8; |
|
|
|
d[6] = SDHC_CMDRSP2; |
|
|
|
d[7] = SDHC_CMDRSP1 >> 24; |
|
|
|
d[8] = SDHC_CMDRSP1 >> 16; |
|
|
|
d[9] = SDHC_CMDRSP1 >> 8; |
|
|
|
d[10] = SDHC_CMDRSP1; |
|
|
|
d[11] = SDHC_CMDRSP0 >> 24; |
|
|
|
d[12] = SDHC_CMDRSP0 >> 16; |
|
|
|
d[13] = SDHC_CMDRSP0 >> 8; |
|
|
|
d[14] = SDHC_CMDRSP0; |
|
|
|
d[15] = 0; |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
//============================================================================= |
|
|
|
bool SdioCard::begin() { |
|
|
|
return cardInit(); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
uint32_t SdioCard::cardSize() { |
|
|
|
return sdCardCapacity(&m_csd); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::dmaBusy() { |
|
|
|
return m_waitCmd13 ? isBusy() : !m_dmaDone; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::erase(uint32_t firstBlock, uint32_t lastBlock) { |
|
|
|
csd_t csd; |
|
|
|
if (!readCSD(&csd)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
// check for single block erase |
|
|
|
if (!csd.v1.erase_blk_en) { |
|
|
|
if (!m_csd.v1.erase_blk_en) { |
|
|
|
// erase size mask |
|
|
|
uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low; |
|
|
|
uint8_t m = (m_csd.v1.sector_size_high << 1) | m_csd.v1.sector_size_low; |
|
|
|
if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) { |
|
|
|
// error card can't erase specified area |
|
|
|
return sdError(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); |
|
|
@@ -454,7 +558,8 @@ bool SdioCard::erase(uint32_t firstBlock, uint32_t lastBlock) { |
|
|
|
if (!cardCommand(CMD38_XFERTYP, 0)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD38); |
|
|
|
} |
|
|
|
while (isBusy()) { |
|
|
|
if (waitTimeout(isBusyCMD13)) { |
|
|
|
return sdError(SD_CARD_ERROR_ERASE_TIMEOUT); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
@@ -471,6 +576,10 @@ uint32_t SdioCard::errorLine() { |
|
|
|
return m_errorLine; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::isBusy() { |
|
|
|
return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13(); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
uint32_t SdioCard::kHzSdClk() { |
|
|
|
return m_sdClkKhz; |
|
|
|
} |
|
|
@@ -480,7 +589,7 @@ bool SdioCard::readBlock(uint32_t lba, uint8_t* buf) { |
|
|
|
|
|
|
|
uint8_t* ptr = (uint32_t)buf & 3 ? aligned : buf; |
|
|
|
|
|
|
|
if (!rdWrBlocks(CMD17_XFERTYP, lba, ptr, 1)) { |
|
|
|
if (!rdWrBlocks(CMD17_DMA_XFERTYP, lba, ptr, 1)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD18); |
|
|
|
} |
|
|
|
if (ptr != buf) { |
|
|
@@ -493,17 +602,17 @@ bool SdioCard::readBlocks(uint32_t lba, uint8_t* buf, size_t n) { |
|
|
|
if ((uint32_t)buf & 3) { |
|
|
|
for (size_t i = 0; i < n; i++, lba++, buf += 512) { |
|
|
|
if (!readBlock(lba, buf)) { |
|
|
|
return false; |
|
|
|
return false; // readBlock will set errorCode. |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
if (!rdWrBlocks(CMD18_XFERTYP, lba, buf, n)) { |
|
|
|
if (!rdWrBlocks(CMD18_DMA_XFERTYP, lba, buf, n)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD18); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::readCID(void* cid) { |
|
|
|
memcpy(cid, &m_cid, 16); |
|
|
|
return true; |
|
|
@@ -514,11 +623,74 @@ bool SdioCard::readCSD(void* csd) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::readData(uint8_t *dst) { |
|
|
|
DBG_IRQSTAT(); |
|
|
|
uint32_t *p32 = reinterpret_cast<uint32_t*>(dst); |
|
|
|
|
|
|
|
if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_RTA)) { |
|
|
|
SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; |
|
|
|
if ((SDHC_BLKATTR & 0XFFFF0000) == 0X10000) { |
|
|
|
// Don't stop at block gap if last block. Allows auto CMD12. |
|
|
|
SDHC_PROCTL |= SDHC_PROCTL_CREQ; |
|
|
|
} else { |
|
|
|
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::readStart(uint32_t lba) { |
|
|
|
// K66/K65 Errata - SDHC: Does not support Infinite Block Transfer Mode. |
|
|
|
return sdError(SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
// SDHC will do Auto CMD12 after count blocks. |
|
|
|
bool SdioCard::readStart(uint32_t lba, uint32_t count) { |
|
|
|
DBG_IRQSTAT(); |
|
|
|
if (count > 0XFFFF) { |
|
|
|
return sdError(SD_CARD_ERROR_READ_START); |
|
|
|
} |
|
|
|
if (yieldTimeout(isBusyCMD13)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD13); |
|
|
|
} |
|
|
|
if (count > 1) { |
|
|
|
SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; |
|
|
|
} |
|
|
|
SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(count) | SDHC_BLKATTR_BLKSIZE(512); |
|
|
|
if (!cardCommand(CMD18_PGM_XFERTYP, m_highCapacity ? lba : 512*lba)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD18); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::readStop() { |
|
|
|
return transferStop(); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::syncBlocks() { |
|
|
|
return true; |
|
|
|
} |
|
|
@@ -537,7 +709,7 @@ bool SdioCard::writeBlock(uint32_t lba, const uint8_t* buf) { |
|
|
|
} else { |
|
|
|
ptr = const_cast<uint8_t*>(buf); |
|
|
|
} |
|
|
|
if (!rdWrBlocks(CMD24_XFERTYP, lba, ptr, 1)) { |
|
|
|
if (!rdWrBlocks(CMD24_DMA_XFERTYP, lba, ptr, 1)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD24); |
|
|
|
} |
|
|
|
return true; |
|
|
@@ -548,14 +720,76 @@ bool SdioCard::writeBlocks(uint32_t lba, const uint8_t* buf, size_t n) { |
|
|
|
if (3 & (uint32_t)ptr) { |
|
|
|
for (size_t i = 0; i < n; i++, lba++, ptr += 512) { |
|
|
|
if (!writeBlock(lba, ptr)) { |
|
|
|
return false; |
|
|
|
return false; // writeBlock will set errorCode. |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
if (!rdWrBlocks(CMD25_XFERTYP, lba, ptr, n)) { |
|
|
|
if (!rdWrBlocks(CMD25_DMA_XFERTYP, lba, ptr, n)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD25); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::writeData(const uint8_t* src) { |
|
|
|
DBG_IRQSTAT(); |
|
|
|
const uint32_t* p32 = reinterpret_cast<const uint32_t*>(src); |
|
|
|
|
|
|
|
if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_WTA)) { |
|
|
|
SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; |
|
|
|
// Don't stop at block gap if last block. Allows auto CMD12. |
|
|
|
if ((SDHC_BLKATTR & 0XFFFF0000) == 0X10000) { |
|
|
|
SDHC_PROCTL |= SDHC_PROCTL_CREQ; |
|
|
|
} else { |
|
|
|
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; |
|
|
|
} |
|
|
|
if (waitTimeout(isBusyTransferComplete)) { |
|
|
|
return sdError(SD_CARD_ERROR_WRITE_TIMEOUT); |
|
|
|
} |
|
|
|
m_irqstat = SDHC_IRQSTAT; |
|
|
|
SDHC_IRQSTAT = m_irqstat; |
|
|
|
return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::writeStart(uint32_t lba) { |
|
|
|
// K66/K65 Errata - SDHC: Does not support Infinite Block Transfer Mode. |
|
|
|
return sdError(SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
// SDHC will do Auto CMD12 after count blocks. |
|
|
|
bool SdioCard::writeStart(uint32_t lba, uint32_t count) { |
|
|
|
if (count > 0XFFFF) { |
|
|
|
return sdError(SD_CARD_ERROR_WRITE_START); |
|
|
|
} |
|
|
|
DBG_IRQSTAT(); |
|
|
|
if (yieldTimeout(isBusyCMD13)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD13); |
|
|
|
} |
|
|
|
if (count > 1) { |
|
|
|
SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; |
|
|
|
} |
|
|
|
SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(count) | SDHC_BLKATTR_BLKSIZE(512); |
|
|
|
|
|
|
|
if (!cardCommand(CMD25_PGM_XFERTYP, m_highCapacity ? lba : 512*lba)) { |
|
|
|
return sdError(SD_CARD_ERROR_CMD25); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool SdioCard::writeStop() { |
|
|
|
return transferStop(); |
|
|
|
} |
|
|
|
#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) |