/* * shimatta_sdio-driver.c * * Created on: Apr 30, 2015 * Mario Hüttel */ #include #define SETAF(PORT,PIN,AF) PORT->AFR[(PIN < 8 ? 0 : 1)] |= AF << ((PIN < 8 ? PIN : (PIN - 8)) * 4) #define READCTRL ((BLOCKSIZE << 4) | SDIO_DCTRL_DMAEN) #define DMAP2M (DMA_SxCR_CHSEL_2 | DMA_SxCR_PBURST_0 | DMA_SxCR_MBURST_0 | DMA_SxCR_MSIZE_1 | DMA_SxCR_PSIZE_1 | DMA_SxCR_MINC | DMA_SxCR_PFCTRL) #define DMAM2P (DMA_SxCR_CHSEL_2 | DMA_SxCR_PBURST_0 | DMA_SxCR_MBURST_0 | DMA_SxCR_MSIZE_1 | DMA_SxCR_PSIZE_1 | DMA_SxCR_MINC | DMA_SxCR_PFCTRL | DMA_SxCR_DIR_0) #define SHORT_ANS 1 #define LONG_ANS 3 #define NO_ANS 0 #define CCRCFAIL -1 #define CTIMEOUT -2 /* OCR Register Masks */ #define OCS_CCS (1<<30) #define OCS_BUSY (1<<31) typedef enum {ACMD41_RESP_INIT, ACMD41_RESP_ERR, ACMD41_RESP_SDSC, ACMD41_RESP_SDXC} ACMD41_RESP_t; typedef enum {CMD8_RESP_TIMEOUT, CMD8_RESP_ACK, CMD8_RESP_ERR} CMD8_RESP_t; typedef uint8_t CID_t; void initModuleHw(); int SDIO_sendCmd(uint8_t CMD, uint32_t arg, uint8_t expectedAns); int SDIO_getResp(uint8_t expectedCMD, uint8_t typeOfAns, uint32_t* responseBuffer); void SDIO_wait_cmd_sent(); ACMD41_RESP_t SDIO_send_ACMD41(uint8_t HCS); int SDIO_send_CMD55(); int SDIO_send_CMD2(); int SDIO_send_CMD3(uint16_t* rca); int SDIO_send_CMD0(); CMD8_RESP_t SDIO_send_CMD8(); void initDetectandProtectionPins(); int checkNotInserted(); // Returns 0 if inserted! int checkWriteProtection(); // returns 0 if write protected void switchPrescaler(uint8_t clkdiv); //BYTE rxtxbuffer[1<AHB1ENR |= PORTCLKMASK; RCC->APB2ENR |= RCC_APB2ENR_SDIOEN; //Init Alternate Functions CLKPORT->MODER |= (2<MODER |= (2<PUPDR |= (1<MODER |= (2<PUPDR |= (1<MODER |= (2<PUPDR |= (1<MODER |= (2<PUPDR |= (1<AFR[(CLKPIN < 8 ? 0 : 1)] |= ALTFUNC << ((CLKPIN < 8 ? CLKPIN : (CLKPIN - 8)) * 4); SETAF(CLKPORT, CLKPIN, ALTFUNC); SETAF(D0PORT, D0PIN, ALTFUNC); #if BUSWIDTH==1 SETAF(D1PORT, D1PIN, ALTFUNC); SETAF(D2PORT, D2PIN, ALTFUNC); SETAF(D3PORT, D3PIN, ALTFUNC); #endif //Init Module //Set CLK Control Register SDIO->CLKCR = (HW_FLOW<<14) | (BUSWIDTH<<11) | SDIO_CLKCR_CLKEN | (INITCLK & SDIO_CLKCR_CLKDIV); //Set Data Timeout SDIO->DTIMER = DTIMEOUT; //Set Data Parameters //SDIO->DCTRL = (BLOCKSIZE << 4) | SDIO_DCTRL_DMAEN; //Set Power Register: Power up Card CLK SDIO->POWER = SDIO_POWER_PWRCTRL_0 | SDIO_POWER_PWRCTRL_1; } void switchPrescaler(uint8_t clkdiv) { uint32_t reg; reg = SDIO->CLKCR; reg &= ~SDIO_CLKCR_CLKDIV; // Clear prescaler reg |= (SDIO_CLKCR_CLKDIV & clkdiv); // Set bits SDIO->CLKCR = reg; } //Send Command //Clear respone Flags //->CRC Fail, complete response, Timeout int SDIO_sendCmd(uint8_t CMD, uint32_t arg, uint8_t expectedAns){ //Clear Flags SDIO->ICR = SDIO_ICR_CCRCFAILC | SDIO_ICR_CMDRENDC | SDIO_ICR_CTIMEOUTC | SDIO_ICR_CMDSENTC; //Send command SDIO->ARG = arg; SDIO->CMD = (CMD & SDIO_CMD_CMDINDEX) | SDIO_CMD_CPSMEN | SDIO_CMD_WAITPEND | ((expectedAns << 6) & SDIO_CMD_WAITRESP); return 0; } void SDIO_wait_cmd_sent() { while (!(SDIO->STA & SDIO_STA_CMDSENT)); SDIO->ICR |= SDIO_ICR_CMDSENTC; } int SDIO_getResp(uint8_t expectedCMD, uint8_t typeOfAns, uint32_t *responseBuffer) { //Return with success because no data is needed if (typeOfAns == NO_ANS) return 0; //Wait for error or success while (1) { if (SDIO->STA & SDIO_STA_CMDREND) break; //Correct Respone Received //Exclude ACMD41 and CMD2 from valid CRC check if ((SDIO->STA & SDIO_STA_CCRCFAIL)) { if(expectedCMD == 0x3f) { //This command does not have a CRC...Doushite.... break;//Hopefully the response is correct. Even without CRC.... } else return CCRCFAIL; } if (SDIO->STA & SDIO_STA_CTIMEOUT) return CTIMEOUT; } //Valid Respone Received if ((SDIO->RESPCMD & SDIO_RESPCMD_RESPCMD) != expectedCMD) return -1; //Not the expected respose //If case of a correct Response *(responseBuffer++) = SDIO->RESP1; //Long response. if (typeOfAns == LONG_ANS) { *(responseBuffer++) = SDIO->RESP2; *(responseBuffer++) = SDIO->RESP3; *(responseBuffer++) = SDIO->RESP4; } } int SDIO_send_CMD55(){ int retry = 0x20; StatusConv_t converter; uint32_t response; do { //Execute Command and check for valid response SDIO_sendCmd(55, cardInfo.rca, SHORT_ANS); if (!SDIO_getResp(55, SHORT_ANS, &response)) { //Response valid. Check if Card has accepted switch to application command mode converter.value = response; if (converter.statusstruct.APP_CMD == 1) return 0; } }while(--retry > 0); return -1; } ACMD41_RESP_t SDIO_send_ACMD41(uint8_t HCS){ uint32_t response; int retry = 0x20; if (SDIO_send_CMD55()) return -1; do { SDIO_sendCmd(41, (HCS ? (1<<30) : 0) | (1<<28), SHORT_ANS); if (!SDIO_getResp(0x3F, SHORT_ANS, &response)) { if (response & OCS_BUSY) { // Card is ready... Who knows why this bit is called busy... if (response & OCS_CCS) { return ACMD41_RESP_SDXC; } else { return ACMD41_RESP_SDSC; } } else { return ACMD41_RESP_INIT; } } }while(--retry > 0); return ACMD41_RESP_ERR; } int SDIO_send_CMD2() { uint32_t response[4]; int retry = 0x20; do { SDIO_sendCmd(2, 0, LONG_ANS); if (!SDIO_getResp(0x3F, LONG_ANS, response)) return 0; }while(retry-- > 0); } int SDIO_send_CMD3(uint16_t* rca) { uint32_t response; int retry = 0x20; do { SDIO_sendCmd(3, 0, SHORT_ANS); if (!SDIO_getResp(3, SHORT_ANS, &response)) { // TODO: Do some *optional* checking *rca = ((response & 0xFFFF0000) >> 16); return 0; } }while(retry-- > 0); return -1; } int SDIO_send_CMD0() { SDIO_sendCmd(0, 0, NO_ANS); SDIO_wait_cmd_sent(); return 0; } CMD8_RESP_t SDIO_send_CMD8() { uint32_t response; int res = 0; int retry = 0x20; do { SDIO_sendCmd(8, 0x1CC, SHORT_ANS); res = SDIO_getResp(8, SHORT_ANS, &response); if (res == 0) { if (response & 0x100) return CMD8_RESP_ACK; else return CMD8_RESP_ERR; } }while(retry-- > 0); return CMD8_RESP_TIMEOUT; } void initDetectandProtectionPins() { #if SDIO_ENABLE_WRITEPROT==1 WRITEPROT_PORT->PUPDR |= ((WRITEPROT_PULLUP ? 1 : 0)<PUPDR |= ((INS_PULLUP? 1 : 0)<IDR & INS_PIN) == INS_ACTIVE_LEVEL ? 0 : 1); #else return 0; // Assume Card is inserted #endif } int checkWriteProtection() { #if SDIO_ENABLE_WRITEPROT return ((WRITEPROT_PORT->IDR & WRITEPROT_PIN) == WRITEPROT_ACTIVE_LEVEL ? 1 : 0); #else return 0; // Assume Card is not write protected #endif }