/* * 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 SDIO_InitModule(); 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(); //BYTE rxtxbuffer[1<IDR & INS_PIN) == INS_ACTIVE_LEVEL)) return STA_NODISK; #endif /* SDIO_ENABLE_INS */ if (cardInfo.type == CARD_NONE) { return STA_NOINIT; } #if SDIO_ENABLE_WRITEPROT==1 if (!((WRITEPROT_PORT->IDR & WRITEPROT_PIN) == WRITEPROT_ACTIVE_LEVEL)) return STA_PROTECT; #endif /* SDIO_ENABLE_WRITEPROT */ return 0; } DSTATUS SDIO_initialize(){ CMD8_RESP_t res8; ACMD41_RESP_t resa41; uint8_t hcs_flag = 0; cardInfo.rca = 0; cardInfo.type = CARD_NONE; SDIO_InitModule(); #if SDIO_ENABLE_WRITEPROT==1 WRITEPROT_PORT->PUPDR |= ((WRITEPROT_PULLUP ? 1 : 0)<PUPDR |= ((INS_PULLUP? 1 : 0)<IDR & INS_PIN) == INS_ACTIVE_LEVEL)) return STA_NODISK; #endif /* SDIO_ENABLE_INS */ SDIO_send_CMD0(); res8 = SDIO_send_CMD8(); switch (res8) { case CMD8_RESP_ACK: // SDV2 Card hcs_flag = 1; break; case CMD8_RESP_ERR: // should not happen return STA_NOINIT; break; case CMD8_RESP_TIMEOUT: // SDV1 Card hcs_flag=0; break; default: return STA_NOINIT; break; } do { resa41 = SDIO_send_ACMD41(hcs_flag); }while(resa41 == ACMD41_RESP_INIT); switch (resa41) { case ACMD41_RESP_SDSC: cardInfo.type = (hcs_flag ? SD_V2_SC : SD_V1); break; case ACMD41_RESP_SDXC: cardInfo.type = SD_V2_HC; break; default: return STA_NOINIT; break; } if (SDIO_send_CMD2()) return STA_NOINIT; if (SDIO_send_CMD3(&cardInfo.rca)) return STA_NOINIT; //TODO: Set block length //TODO: Set 4 bit mode //TODO: #if SDIO_ENABLE_WRITEPROT==1 if (!((WRITEPROT_PORT->IDR & WRITEPROT_PIN) == WRITEPROT_ACTIVE_LEVEL)) return STA_PROTECT; #endif /* SDIO_ENABLE_WRITEPROT */ return 0; } DRESULT SDIO_disk_read(BYTE *buff, DWORD sector, UINT count){ return RES_OK; } DRESULT SDIO_disk_write(const BYTE *buff, DWORD sector, UINT count){ return RES_OK; } DRESULT SDIO_disk_ioctl(BYTE cmd, void* buff){ DRESULT res = RES_OK; switch(cmd) { case GET_BLOCK_SIZE: *((DWORD*)buff) = 0x01; break; case GET_SECTOR_SIZE: *((WORD*)buff) = (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; } //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 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; }