/** * \brief This function sends the write command to MMCSD card. * * \param mmcsdCtrlInfo It holds the mmcsd control information. * \param ptr It determines the address from where data has to written * \param block It determines to which block data to be written * \param nblks It determines the number of blocks to be written * * \returns 1 - successfull written of data. * 0 - failure to write the data. **/ unsigned int MMCSDWriteCmdSend(mmcsdCtrlInfo *ctrl, void *ptr, unsigned int block, unsigned int nblks) { mmcsdCardInfo *card = ctrl->card; unsigned int status = 0; unsigned int address; mmcsdCmd cmd; /* * Address is in blks for high cap cards and in actual bytes * for standard capacity cards */ if (card->highCap) { address = block; } else { address = block * card->blkLen; } #ifdef CACHE /* Clean the data cache. */ CacheDataCleanBuff((unsigned int) ptr, (512 * nblks)); #endif ctrl->xferSetup(ctrl, 0, ptr, 512, nblks); cmd.flags = SD_CMDRSP_WRITE | SD_CMDRSP_DATA | (ctrl->dmaEnable << SD_CMDRSP_DMAEN_OFFSET); cmd.arg = address; cmd.nblks = nblks; if (nblks > 1) { cmd.idx = SD_CMD(25); } else { cmd.idx = SD_CMD(24); } status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { return 0; } status = ctrl->xferStatusGet(ctrl); if (status == 0) { return 0; } while (!MMCSDWaitCardReadyForData(ctrl)); return 1; }
/** * \brief Configure the MMC/SD bus width * * \param mmcsdCtrlInfo It holds the mmcsd control information. * * \param buswidth SD/MMC bus width.\n * * buswidth can take the values.\n * HS_MMCSD_BUS_WIDTH_4BIT.\n * HS_MMCSD_BUS_WIDTH_1BIT.\n * * \return None. * **/ unsigned int MMCSDBusWidthSet(mmcsdCtrlInfo *ctrl) { mmcsdCardInfo *card = ctrl->card; unsigned int status = 0; mmcsdCmd capp; if (card->cardType == MMCSD_CARD_MMC) {//на проверку шины положили потому что не получается нифига эта проверка) //а не сделать ли нам все по спецификации и не через анус? - не сделать) //если надо 4 бита if (card->sd_ver < 4) { UARTPuts("Card restricted to 1-wire wide bus mode.\n", -1); return 1; //какие мы молодцы! } if (card->busWidth & SD_BUS_WIDTH_4BIT) { if (ctrl->busWidth & SD_BUS_WIDTH_4BIT) { //тест шины 4 бита - забили на него ctrl->busWidthConfig(ctrl, SD_BUS_WIDTH_4BIT); capp.idx = SD_CMD(6); capp.flags = SD_CMDRSP_BUSY; capp.arg = 0x03B70100; status = ctrl->cmdSend(ctrl, &capp); if (status == 0) return 0; //аццки важный фикс!! без этого вылетает! //ждем пока карта не снимет busy! while (!(HWREG(ctrl->memBase + MMCHS_PSTATE) & (unsigned int)BIT(20))); } } else if (card->busWidth & SD_BUS_WIDTH_8BIT) //хотим 8 бит { if (ctrl->busWidth & SD_BUS_WIDTH_8BIT) { //тест шины 8 бит - забили на него ctrl->busWidthConfig(ctrl, SD_BUS_WIDTH_8BIT); capp.idx = SD_CMD(6); capp.flags = SD_CMDRSP_BUSY; capp.arg = 0x03B70200; status = ctrl->cmdSend(ctrl, &capp); if (status == 0) return 0; //аццки важный фикс!! без этого вылетает! //ждем пока карта не снимет busy! while (!(HWREG(ctrl->memBase + MMCHS_PSTATE) & (unsigned int)BIT(20))); } } } else { return 0; } return 1; //какие мы молодцы! }
/** * \brief This function sends the write command to MMCSD card. * * \param mmcsdCtrlInfo It holds the mmcsd control information. * \param ptr It determines the address to where data has to read * \param block It determines from which block data to be read * \param nblks It determines the number of blocks to be read * * \returns 1 - successfull reading of data. * 0 - failure to the data. **/ unsigned int MMCSDReadCmdSend(mmcsdCtrlInfo *ctrl, void *ptr, unsigned int block, unsigned int nblks) { mmcsdCardInfo *card = ctrl->card; unsigned int status = 0; unsigned int address; mmcsdCmd cmd; /* * Address is in blks for high cap cards and in actual bytes * for standard capacity cards */ if (card->highCap) { address = block; } else { address = block * card->blkLen; } ctrl->xferSetup(ctrl, 1, ptr, 512, nblks); cmd.flags = SD_CMDRSP_READ | SD_CMDRSP_DATA | (ctrl->dmaEnable << SD_CMDRSP_DMAEN_OFFSET); cmd.arg = address; cmd.nblks = nblks; if (nblks > 1) { cmd.idx = SD_CMD(18); } else { cmd.idx = SD_CMD(17); } status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { return 0; } status = ctrl->xferStatusGet(ctrl); if (status == 0) { return 0; } #ifdef CACHE /* Invalidate the data cache. */ CacheDataInvalidateBuff((unsigned int)(ptr), 512 * nblks); #endif return 1; }
/** * @brief send sd command. * * The clock must be less than 400khz when the sd controller in identification mode. * @author Huang Xin * @date 2010-07-14 * @param cmd_index[in] The command index. * @param rsp[in] The command response:no response ,short reponse or long response * @param arg[in] The cmd argument. * @return T_BOOL * @retval AK_TRUE: CMD sent successfully * @retval AK_FALSE: CMD sent failed */ T_BOOL send_cmd(T_U8 cmd_index, T_U8 resp, T_U32 arg) { T_U32 cmd_value = 0; T_U32 status; if (cmd_index == SD_CMD(1) || cmd_index == SD_CMD(41) || cmd_index == SD_CMD(5)) //R3 is no crc { cmd_value = CPSM_ENABLE | ( resp << WAIT_REP_OFFSET) | ( cmd_index << CMD_INDEX_OFFSET) | RSP_CRC_NO_CHK; } else { cmd_value = CPSM_ENABLE | ( resp << WAIT_REP_OFFSET ) | ( cmd_index << CMD_INDEX_OFFSET) ; } REG32(s_SdReg_Base + oSD_ARG) = arg; REG32(s_SdReg_Base + oSD_CMD) = cmd_value; if (SD_NO_RESPONSE == resp) { while(1) { status = REG32(s_SdReg_Base + oSD_INT_STA); if (status & CMD_SENT) { return AK_TRUE; } } } else if ((SD_SHORT_RESPONSE == resp) ||(SD_LONG_RESPONSE == resp)) { while(1) { status = REG32(s_SdReg_Base + oSD_INT_STA); if ((status & CMD_TIME_OUT)||(status & CMD_CRC_FAIL)) { drv_print("ce, ", (cmd_index << 16) | status, AK_TRUE); return AK_FALSE; } else if (status & CMD_RESP_END) { return AK_TRUE; } } } else { return AK_FALSE; } }
/** * \brief This function configures the transmission speed in MMCSD. * * \param mmcsdCtrlInfo It holds the mmcsd control information. * * \returns 1 - successfull. * 0 - failed. **/ unsigned int MMCSDTranSpeedSet(mmcsdCtrlInfo *ctrl) { mmcsdCardInfo *card = ctrl->card; int status; mmcsdCmd cmd; if (card->cardType == MMCSD_CARD_MMC) { //при инициализации eMMC уже была выставлена максимальная частота //на которую может быть настроена карта без HS_TIMING по TRAN_SPEED из CSD //теперь если highspeed = 1 делаем 48 МГц if (card->sd_ver < 4) return 1; //карта уже на макс скорости cmd.idx = SD_CMD(6); cmd.flags = SD_CMDRSP_BUSY; //взято из спецификации eMMC cmd.arg = 0x03B90100; status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) return 0; status = ctrl->busFreqConfig(ctrl, 48000000); if (status != 0) return 0; ctrl->opClk = 48000000; //тут проблемка, не сразу после выхода из этой функции карта готова //нужна задержка какая-то //наерно если подождать пока DAT0 не поднимется в '1' будет норм while (!(HWREG(ctrl->memBase + MMCHS_PSTATE) & (unsigned int)BIT(20))); } else { return 0; } return 1; }
/** * \brief This function resets the MMCSD card. * * \param mmcsdCtrlInfo It holds the mmcsd control information. * * \returns 1 - successfull reset of card. * 0 - fails to reset the card. **/ unsigned int MMCSDCardReset(mmcsdCtrlInfo *ctrl) { unsigned int status = 0; mmcsdCmd cmd; cmd.idx = SD_CMD(0); cmd.flags = SD_CMDRSP_NONE; cmd.arg = 0; status = ctrl->cmdSend(ctrl, &cmd); return status; }
//Будем ждать готовности карты в течение времени таймаута и возвращать 1 в случае успеха //Функция добавлена КП Дорошенко для фикса глюка карты, когда после инициализации или записи //карта не сразу готова к новым подвигам unsigned int MMCSDWaitCardReadyForData(mmcsdCtrlInfo *ctrl) { mmcsdCardInfo *card = ctrl->card; unsigned int status = 0; mmcsdCmd cmd; unsigned int retry = 0xffffffff; cmd.idx = SD_CMD(13); cmd.flags = 0; cmd.arg = card->rca << 16; do{ status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) return status; //если нет ответа, можно выходить } while (!(cmd.rsp[0] & ((unsigned int)BIT(8))) && --retry); //кол-во попыток вышло, либо стоит бит READY_FOR_DATA if (0 == retry) return 0; return 1; }
/** * Write block(s) from MMC/SD card. The implementation follows * '26.3.5 MMC/SD Mode Single-Block Read Operation Using EDMA' in 'spruh82a'. * @param mmcsdCtrlInfo It holds the mmcsd control information. * @param ptr It determines the address from where data has to written * @param block It determines to which block data to be written (block >= 0) * @param nblks It determines the number of blocks to be written (nblks >= 1) * @retval 1 success * @retval 0 fail */ unsigned int MMCSDWriteCmdSend(mmcsdCtrlInfo *ctrl, void *ptr, unsigned int block, unsigned int nblks) { // TODO: workaround for buggy WRITE_MULTI_BLOCK if (nblks > 1) { for (unsigned int i = 0; i < nblks; ++i) { unsigned int res = MMCSDWriteCmdSend(ctrl, ptr + i * MMCSD_MAX_BLOCK_LEN, block + i, 1); assert(res == 1); } return 1; } assert(nblks == 1); unsigned int status = 1; // 1 for success volatile struct st_mmcsd *mmc = ctrl->memBase; ER ercd; /* 1. Write the card's relative address to the MMC argument registers (MMCARGH and MMCARGL). */ mmcsdCardInfo *card = ctrl->card; unsigned int address; /* * TODO: check this -- ertl-liyixiao * Address is in blks for high cap cards and in actual bytes * for standard capacity cards */ assert(card->highCap); if (card->highCap) address = block; else address = block * card->blkLen; mmc->MMCARGHL = address; /* 2. Read card CSD to determine the card's maximum block length. */ // TODO: // syslog(LOG_ERROR, "card->raw_csd[0]: 0x%08x", card->raw_csd[0]); // syslog(LOG_ERROR, "card->raw_csd[1]: 0x%08x", card->raw_csd[1]); // syslog(LOG_ERROR, "card->raw_csd[2]: 0x%08x", card->raw_csd[2]); // syslog(LOG_ERROR, "card->raw_csd[3]: 0x%08x", card->raw_csd[3]); syslog(LOG_ERROR, "MMCCTL: 0x%08x", mmc->MMCCTL); /* 3. Use the MMC command register (MMCCMD) to send the SET_BLOCKLEN command (if the block length is different than the length used in the previous operation). The block length must be a multiple of 512 bytes and less then the maximum block length specified in the CSD. */ syslog(LOG_ERROR, "MMCCTL: 0x%08x", mmc->MMCCTL); syslog(LOG_ERROR, "MMCCLK: 0x%08x", mmc->MMCBLEN); // TODO: /* 4. Reset the FIFO (FIFORST bit in MMCFIFOCTL). */ mmc->MMCFIFOCTL |= MMCSD_MMCFIFOCTL_FIFORST; mmc->MMCFIFOCTL &= ~MMCSD_MMCFIFOCTL_FIFORST; /* 5. Set the FIFO direction to send (FIFODIR bit in MMCFIFOCTL). */ mmc->MMCFIFOCTL |= MMCSD_MMCFIFOCTL_FIFODIR; /* 6. Set the access width (ACCWD bits in MMCFIFOCTL). */ mmc->MMCFIFOCTL &= ~MMCSD_MMCFIFOCTL_ACCWD; mmc->MMCFIFOCTL |= (MMCSD_MMCFIFOCTL_ACCWD_4BYTES << MMCSD_MMCFIFOCTL_ACCWD_SHIFT); // => 4 bytes /* 7. Set the FIFO threshold (FIFOLEV bit in MMCFIFOCTL). */ mmc->MMCFIFOCTL |= MMCSD_MMCFIFOCTL_FIFOLEV; // => 64 bytes /* 8. Set up DMA (DMA size needs to be greater than or equal to FIFOLEV setting). */ CacheDataCleanBuff((unsigned int) ptr, (MMCSD_MAX_BLOCK_LEN * nblks)); // Clean data buffer to send arm926_drain_write_buffer(); // Memory barrier for data buffer to send ctrl->xferSetup(ctrl, 0, ptr, MMCSD_MAX_BLOCK_LEN, nblks); { syslog(LOG_ERROR, "origSrcAddr: 0x%08x", ptr); EDMA3CCPaRAMEntry param; EDMA3GetPaRAM(&EDMA3_CC0, EDMA3_CHA_MMCSD0_TX, ¶m); syslog(LOG_ERROR, "srcAddr: 0x%08x", param.srcAddr); } /* 9. Use MMCCMD to send the WRITE_BLOCK command to the card. */ #if defined(DEBUG) syslog(LOG_ERROR, "%s(): Use MMCCMD to send the WRITE_BLOCK/WRITE_MULTI_BLOCK command to the card.", __FUNCTION__); #endif mmcsdCmd cmd; cmd.flags = SD_CMDRSP_R1 | SD_CMDRSP_WRITE | SD_CMDRSP_DATA; cmd.arg = address; cmd.nblks = nblks; if (nblks > 1) { cmd.idx = SD_CMD(25); cmd.flags |= SD_CMDRSP_ABORT; } else { cmd.idx = SD_CMD(24); } status = MMCSDCmdSend(ctrl, &cmd); if (status == 0) { syslog(LOG_ERROR, "%s(): MMCSDCmdSend() failed.", __FUNCTION__); goto error_exit; } // CPU Mode // uint32_t *buf = ptr; // Assume uint32_t is little-endian // while (1) { // EDMA3CCPaRAMEntry param; //// syslog(LOG_ERROR, "MMCSD0.MMCST0: 0x%08x", MMCSD0.MMCST0); //// syslog(LOG_ERROR, "MMCSD0.MMCST1: 0x%08x", MMCSD0.MMCST1); //// syslog(LOG_ERROR, "EDMA3_CC0.ER: 0x%08x", EDMA3_CC0.ER); //// syslog(LOG_ERROR, "EDMA3_CC0.EMR: 0x%08x", EDMA3_CC0.EMR); //// syslog(LOG_ERROR, "EDMA3_CC0.SER: 0x%08x", EDMA3_CC0.SER); // EDMA3GetPaRAM(&EDMA3_CC0, EDMA3_CHA_MMCSD0_TX, ¶m); //// syslog(LOG_ERROR, "origSrcAddr: 0x%08x", ptr); // syslog(LOG_ERROR, "srcAddr: 0x%08x", param.srcAddr); //// syslog(LOG_ERROR, "CCNT: %d", param.cCnt); // // tslp_tsk(1000); // } /* 10. Set the DMATRIG bit in MMCCMD to trigger the first data transfer. */ #if DEBUG_PRINT // syslog(LOG_ERROR, "%s(): Set the DMATRIG bit, MMCCMD: 0x%08x=>0x%08x", __FUNCTION__, mmc->MMCCMD, mmc->MMCCMD | MMCSD_MMCCMD_DMATRIG); #endif // mmc->MMCCMD /*|*/= MMCSD_MMCCMD_DMATRIG; /* 11. Wait for DMA sequence to complete. */ status = ctrl->xferStatusGet(ctrl); if (status == 0) { assert(false); goto error_exit; } error_exit: return status; #if 0 mmcsdCardInfo *card = ctrl->card; unsigned int status = 0; unsigned int address; mmcsdCmd cmd; #if DEBUG_PRINT UARTprintf("----\r\n"); UARTprintf("%s(0x%x)\r\n", __FUNCTION__, ctrl); #endif }
/** * Read or write block(s) from MMC/SD card. The implementation follows * '26.3.5 MMC/SD Mode Single-Block Read Operation Using EDMA' and * '26.3.3 MMC/SD Mode Single-Block Write Operation Using EDMA' in 'spruh82a'. * @param mmcsdCtrlInfo It holds the mmcsd control information. * @param ptr It determines the address to store or fetch the data * @param block It determines from which block data to be read or written (block >= 0) * @param nblks It determines the number of blocks to be read or written (nblks >= 1) * @retval 1 success * @retval 0 fail */ static unsigned int MMCSDReadWriteCmdSend(mmcsdCtrlInfo *ctrl, void *ptr, unsigned int block, unsigned int nblks, bool_t rx) { unsigned int status = 0; // 0 for failure /** * Handle unaligned READ operation using data_recv_buf */ if (rx && ((unsigned int)ptr % SOC_EDMA3_ALIGN_SIZE) != 0) { static uint8_t data_recv_buf[MMCSD_MAX_BLOCK_LEN * 8] __attribute__((aligned(SOC_EDMA3_ALIGN_SIZE))); #define DATA_RECV_BUF_MAX_BLOCKS (sizeof(data_recv_buf) / MMCSD_MAX_BLOCK_LEN) for (unsigned int i = 0; i < nblks; i += DATA_RECV_BUF_MAX_BLOCKS) { unsigned int blocks_to_process = (i + DATA_RECV_BUF_MAX_BLOCKS <= nblks) ? DATA_RECV_BUF_MAX_BLOCKS : nblks - i; status = MMCSDReadWriteCmdSend(ctrl, data_recv_buf, block + i, blocks_to_process, rx); memcpy(ptr + i * MMCSD_MAX_BLOCK_LEN, data_recv_buf, MMCSD_MAX_BLOCK_LEN * blocks_to_process); if (status == 0) break; } goto error_exit; } #if defined(DEBUG_MMCSD) syslog(LOG_NOTICE, "%s(): %s %d block(s), ptr=%p", __FUNCTION__, rx ? "Read" : "Write", nblks, ptr); #endif /* 1. Write the card's relative address to the MMC argument registers (MMCARGH and MMCARGL). */ volatile struct st_mmcsd *mmc = (struct st_mmcsd *)ctrl->memBase; if (!ctrl->card->highCap) { // Only SDHC card is supported by now. syslog(LOG_ERROR, "%s(): Standard capacity SD card is not supported, please use an SDHC card!", __FUNCTION__); goto error_exit; } mmc->MMCARGHL = block; #if 0 mmcsdCardInfo *card = ctrl->card; unsigned int address; /* * Address is in blks for high cap cards and in actual bytes * for standard capacity cards */ assert(card->highCap); if (card->highCap) address = block; else address = block * card->blkLen; mmc->MMCARGHL = address; #endif /* 2. Read card CSD to determine the card's maximum block length. */ // TODO: This step is unnecessary for SDHC card /* 3. Use the MMC command register (MMCCMD) to send the SET_BLOCKLEN command (if the block length is different than the length used in the previous operation). The block length must be a multiple of 512 bytes and less then the maximum block length specified in the CSD. */ // TODO: This step is unnecessary for SDHC card /* 4. Reset the FIFO (FIFORST bit in MMCFIFOCTL). */ mmc->MMCFIFOCTL |= MMCSD_MMCFIFOCTL_FIFORST; mmc->MMCFIFOCTL &= ~MMCSD_MMCFIFOCTL_FIFORST; /* 5. Set the FIFO direction (FIFODIR bit in MMCFIFOCTL). */ if (rx) // FIFO to receive mmc->MMCFIFOCTL &= ~MMCSD_MMCFIFOCTL_FIFODIR; else // FIFO to send mmc->MMCFIFOCTL |= MMCSD_MMCFIFOCTL_FIFODIR; /* 6. Set the access width (ACCWD bits in MMCFIFOCTL). */ mmc->MMCFIFOCTL &= ~MMCSD_MMCFIFOCTL_ACCWD; mmc->MMCFIFOCTL |= (MMCSD_MMCFIFOCTL_ACCWD_4BYTES << MMCSD_MMCFIFOCTL_ACCWD_SHIFT); // => 4 bytes /* 7. Set the FIFO threshold (FIFOLEV bit in MMCFIFOCTL). */ mmc->MMCFIFOCTL |= MMCSD_MMCFIFOCTL_FIFOLEV; // => 64 bytes /* 8. Set up DMA (DMA size needs to be greater than or equal to FIFOLEV setting). */ #if defined(DEBUG_MMCSD) && 0 syslog(LOG_NOTICE, "%s(): Set up DMA", __FUNCTION__); #endif if (rx) { CacheDataCleanBuff(ptr, MMCSD_MAX_BLOCK_LEN * nblks); // Clean 'data_recv_buf' arm926_drain_write_buffer(); // Memory barrier for 'data_recv_buf' ctrl->xferSetup(ctrl, 1, ptr, MMCSD_MAX_BLOCK_LEN, nblks); } else { data_cache_clean_buffer(ptr, MMCSD_MAX_BLOCK_LEN * nblks); // Clean data buffer to send arm926_drain_write_buffer(); // Memory barrier for data buffer to send ctrl->xferSetup(ctrl, 0, ptr, MMCSD_MAX_BLOCK_LEN, nblks); } /* 9. Use MMCCMD to send the READ_BLOCK/WRITE_BLOCK command to the card. */ #if defined(DEBUG_MMCSD) && 0 syslog(LOG_NOTICE, "%s(): Send READ/WRITE command", __FUNCTION__); #endif mmcsdCmd cmd; if (rx) { cmd.flags = SD_CMDRSP_R1 | SD_CMDRSP_READ | SD_CMDRSP_DATA; cmd.idx = SD_CMD(17); cmd.nblks = nblks; if (nblks > 1) { cmd.flags |= SD_CMDRSP_ABORT; cmd.idx = SD_CMD(18); } else { cmd.idx = SD_CMD(17); } } else { cmd.flags = SD_CMDRSP_R1 | SD_CMDRSP_WRITE | SD_CMDRSP_DATA; cmd.idx = SD_CMD(24); cmd.nblks = nblks; if (nblks > 1) { cmd.idx = SD_CMD(25); cmd.flags |= SD_CMDRSP_ABORT; } else { cmd.idx = SD_CMD(24); } } status = MMCSDCmdSend(ctrl, &cmd); if (status == 0) { syslog(LOG_ERROR, "%s(): MMCSDCmdSend() failed.", __FUNCTION__); goto error_exit; } /* 10. Set the DMATRIG bit in MMCCMD to trigger the first data transfer. */ // TODO: This step has already been done by last step. //mmc->MMCCMD |= MMCSD_MMCCMD_DMATRIG; /* 11. Wait for DMA sequence to complete. */ #if defined(DEBUG_MMCSD) && 0 syslog(LOG_NOTICE, "%s(): Wait for DMA sequence to complete", __FUNCTION__); #endif status = ctrl->xferStatusGet(ctrl); if (status == 0) { assert(false); goto error_exit; } if (rx) { // Copy data received CacheDataInvalidateBuff((unsigned int) ptr, MMCSD_MAX_BLOCK_LEN * nblks); // Invalidate the data cache. } /* 12. Use the MMC status register 0 (MMCST0) to check for errors. */ if (rx || nblks == 1) { #if defined(DEBUG_MMCSD) && 0 syslog(LOG_NOTICE, "%s(): Wait for DATDNE", __FUNCTION__); #endif ctrl->waitMMCST0(ctrl, MMCSD_MMCST0_DATDNE); } /* 13. Use MMCCMD to send the STOP_TRANSMISSION command. */ if (cmd.nblks > 1) { #if defined(DEBUG_MMCSD) && 0 syslog(LOG_NOTICE, "%s(): Send STOP_TRANSMISSION", __FUNCTION__); #endif status = MMCSDStopCmdSend(ctrl); assert(status != 0); ctrl->waitMMCST0(ctrl, MMCSD_MMCST0_BSYDNE); //while(HWREG(ctrl->memBase + MMCSD_MMCST1) & MMCSD_MMCST1_BUSY); } error_exit: return status; }
/** * Read block(s) from MMC/SD card. The implementation follows * '26.3.5 MMC/SD Mode Single-Block Read Operation Using EDMA' in 'spruh82a'. * @param mmcsdCtrlInfo It holds the mmcsd control information. * @param ptr It determines the address to where data has to read * @param block It determines from which block data to be read (block >= 0) * @param nblks It determines the number of blocks to be read (nblks >= 1) * @retval 1 success * @retval 0 fail */ unsigned int MMCSDReadCmdSend(mmcsdCtrlInfo *ctrl, void *ptr, unsigned int block, unsigned int nblks) { // TODO: workaround for buggy READ_MULTI_BLOCK if (nblks > 1) { for (unsigned int i = 0; i < nblks; ++i) { unsigned int res = MMCSDReadCmdSend(ctrl, ptr + i * MMCSD_MAX_BLOCK_LEN, block + i, 1); assert(res == 1); } return 1; } assert(nblks == 1); #if defined(DEBUG) // UARTprintf("----\r\n"); // UARTprintf("%s(0x%x)\r\n", __FUNCTION__, ctrl); // syslog(LOG_ERROR, "----"); syslog(LOG_ERROR, "%s(ctrl=0x%p,ptr=0x%p,block=%d,nblks=%d)", __FUNCTION__, ctrl, ptr, block, nblks); #endif assert(nblks * MMCSD_MAX_BLOCK_LEN <= sizeof(data_recv_buf)); // MMCSDDataTimeoutSet(ctrl->memBase, 0x0, 0x3FFFFFF);// Infinite wait for CMD response, maximum wait for data transfer // cmdTimeout = 0; // TODO: fix this (refactoring) // for(int i = 0; i < sizeof(data_recv_buf); ++i) data_recv_buf[i] = 0xFF; // Fill data_recv_buf for debug unsigned int status = 1; // 1 for success volatile struct st_mmcsd *mmc = ctrl->memBase; ER ercd; /* 1. Write the card's relative address to the MMC argument registers (MMCARGH and MMCARGL). */ mmcsdCardInfo *card = ctrl->card; unsigned int address; /* * TODO: check this -- ertl-liyixiao * Address is in blks for high cap cards and in actual bytes * for standard capacity cards */ assert(card->highCap); if (card->highCap) address = block; else address = block * card->blkLen; mmc->MMCARGHL = address; /* 2. Read card CSD to determine the card's maximum block length. */ // TODO: /* 3. Use the MMC command register (MMCCMD) to send the SET_BLOCKLEN command (if the block length is different than the length used in the previous operation). The block length must be a multiple of 512 bytes and less then the maximum block length specified in the CSD. */ // TODO: /* 4. Reset the FIFO (FIFORST bit in MMCFIFOCTL). */ mmc->MMCFIFOCTL |= MMCSD_MMCFIFOCTL_FIFORST; mmc->MMCFIFOCTL &= ~MMCSD_MMCFIFOCTL_FIFORST; /* 5. Set the FIFO direction to receive (FIFODIR bit in MMCFIFOCTL). */ mmc->MMCFIFOCTL &= ~MMCSD_MMCFIFOCTL_FIFODIR; /* 6. Set the access width (ACCWD bits in MMCFIFOCTL). */ mmc->MMCFIFOCTL &= ~MMCSD_MMCFIFOCTL_ACCWD; mmc->MMCFIFOCTL |= (MMCSD_MMCFIFOCTL_ACCWD_4BYTES << MMCSD_MMCFIFOCTL_ACCWD_SHIFT); // => 4 bytes /* 7. Set the FIFO threshold (FIFOLEV bit in MMCFIFOCTL). */ mmc->MMCFIFOCTL |= MMCSD_MMCFIFOCTL_FIFOLEV; // => 64 bytes /* 8. Set up DMA (DMA size needs to be greater than or equal to FIFOLEV setting). */ CacheDataCleanBuff(data_recv_buf, sizeof(data_recv_buf)); // Clean 'data_recv_buf' arm926_drain_write_buffer(); // Memory barrier for 'data_recv_buf' ctrl->xferSetup(ctrl, 1, data_recv_buf/*ptr*/, MMCSD_MAX_BLOCK_LEN, nblks); /* 9. Use MMCCMD to send the READ_BLOCK command to the card. */ #if defined(DEBUG) syslog(LOG_ERROR, "%s(): Use MMCCMD to send the READ_BLOCK/READ_MULTI_BLOCK command to the card.", __FUNCTION__); #endif // ercd = clr_flg(MMCSD_ISR_FLG, ~(MMCSD_ISR_FLGPTN_DATATIMEOUT)); // Clear data time-out flag // mmc->MMCIM |= MMCSD_MMCIM_ETOUTRD; // Enable data time-out interrupt assert(ercd == E_OK); mmcsdCmd cmd; cmd.flags = SD_CMDRSP_R1 | SD_CMDRSP_READ | SD_CMDRSP_DATA; cmd.arg = address; cmd.nblks = nblks; if (nblks > 1) { cmd.flags |= SD_CMDRSP_ABORT; cmd.idx = SD_CMD(18); } else { cmd.idx = SD_CMD(17); } status = MMCSDCmdSend(ctrl, &cmd); if (status == 0) { syslog(LOG_ERROR, "%s(): MMCSDCmdSend() failed.", __FUNCTION__); goto error_exit; } /* 10. Set the DMATRIG bit in MMCCMD to trigger the first data transfer. */ #if DEBUG_PRINT // syslog(LOG_ERROR, "%s(): Set the DMATRIG bit, MMCCMD: 0x%08x=>0x%08x", __FUNCTION__, mmc->MMCCMD, mmc->MMCCMD | MMCSD_MMCCMD_DMATRIG); #endif // mmc->MMCCMD /*|*/= MMCSD_MMCCMD_DMATRIG; /* 11. Wait for DMA sequence to complete. */ status = ctrl->xferStatusGet(ctrl); if (status == 0) { assert(false); goto error_exit; } /* Invalidate the data cache. */ CacheDataInvalidateBuff((unsigned int) data_recv_buf/*ptr*/, (MMCSD_MAX_BLOCK_LEN * nblks)); /* 12. Use the MMC status register 0 (MMCST0) to check for errors. */ // TODO: check this #if 0 syslog(LOG_ERROR, "%s(): MMCST0: 0x%08x", __FUNCTION__, mmc->MMCST0); syslog(LOG_ERROR, "%s(): MMCST1: 0x%08x", __FUNCTION__, mmc->MMCST1); syslog(LOG_ERROR, "%s(): MMCNBLK: %d", __FUNCTION__, mmc->MMCNBLK); syslog(LOG_ERROR, "%s(): MMCNBLC: %d", __FUNCTION__, mmc->MMCNBLC); #endif assert(sizeof(data_recv_buf) >= MMCSD_MAX_BLOCK_LEN * nblks); memcpy(ptr, data_recv_buf, MMCSD_MAX_BLOCK_LEN * nblks); // TODO: ertl-liyixiao #if defined(DEBUG) syslog(LOG_ERROR, "%s sector: %d, count: %d\n", __FUNCTION__, block, nblks); for(int i = 0; i < MMCSD_MAX_BLOCK_LEN * nblks;) { //((uint8_t*)ptr)[i] = ((uint8_t*)ptr)[i]; uint8_t *val = ((uint8_t*)ptr) + i; syslog(LOG_ERROR, "%02x %02x %02x %02x %02x", val[0], val[1], val[2], val[3], val[4]); //printf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x \n", // val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9], val[10], val[11], val[12], val[13], val[14], val[15]); i += 5; } #endif #if 0 // TODO: !IMPORTANT! STOP_TRANSMISSION must not be sent since MMCSD_MMCNBLK has been set to the exact number. /* Send a STOP_TRANSMISSION after reading multiple blocks */ if (cmd.nblks > 1) { status = MMCSDStopCmdSend(ctrl); assert(status != 0); // TODO: check status } #endif error_exit: // mmc->MMCIM &= ~MMCSD_MMCIM_ETOUTRD; // Disable data time-out interrupt TODO: fix me return status; #if 0 // TODO: -- ertl-liyixiao #ifdef CACHE_SUPPORTED /* Clean the data cache. */ CacheDataCleanBuff((unsigned int) ptr, (MMCSD_MAX_BLOCK_LEN * nblks)); #endif mmcsd_reset_fifo(true); /* Send a STOP */ if (cmd.nblks > 1) { status = MMCSDStopCmdSend(ctrl); if (status == 0) { #if DEBUG_PRINT UARTprintf("%s(0x%x):MMCSDStopCmdSend() returned 0\r\n", __FUNCTION__, ctrl); #endif return 0; } } #endif }
unsigned int MMCSDCardInit(mmcsdCtrlInfo *ctrl) { mmcsdCardInfo *card = ctrl->card; unsigned int retry = 0xFFFF; unsigned int status = 0; unsigned int khz; mmcsdCmd cmd; memset(ctrl->card, 0, sizeof(mmcsdCardInfo)); card->ctrl = ctrl; /* CMD0 - reset card */ status = MMCSDCardReset(ctrl); if (status == 0) { card->error = 1; return 0; } //mmc card initialization ctrl->card->cardType = MMCSD_CARD_MMC; //open drain для команд инициализации HWREG(ctrl->memBase + MMCHS_CON) |= MMCHS_CON_OD; //Set SD_SYSCTL[25] SRC //bit to 0x1 and wait until it returns to 0x0 HWREG(ctrl->memBase + MMCHS_SYSCTL) |= MMCHS_SYSCTL_SRC; while(!(HWREG(ctrl->memBase + MMCHS_SYSCTL) & MMCHS_SYSCTL_SRC)); while(HWREG(ctrl->memBase + MMCHS_SYSCTL) & MMCHS_SYSCTL_SRC); /* CMD1 - SEND_OP_COND */ retry = RETRY; //с потолка cmd.idx = SD_CMD(1); cmd.flags = 0; cmd.arg = 0x40ff8080;/////карта больше 2 Гб? cmd.rsp[0] = 0; do{ status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; //если нет ответа, можно выходить } //добавил задержку для кривой eMMC СОТА delay(10); } while (!(cmd.rsp[0] & ((unsigned int)BIT(31))) && retry--); if (0xffffffff == retry) //карта до 2 Гб? { retry = RETRY; //c потолка cmd.arg = 0x00ff8080; //волшебная цыфорка do{ status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; //если нет ответа, можно выходить } //добавил задержку для кривой eMMC СОТА delay(10); } while (!(cmd.rsp[0] & ((unsigned int)BIT(31))) && retry--); } if (0xffffffff == retry) { card->error = 1; return 0; } //сохраняем OCR card->ocr = cmd.rsp[0]; card->highCap = (card->ocr & SD_OCR_HIGH_CAPACITY) ? 1 : 0; /* CMD2 - ALL_SEND_CID */ cmd.idx = SD_CMD(2); cmd.flags = SD_CMDRSP_136BITS; cmd.arg = 0; status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; //если нет ответа, можно выходить } //Сохраняем CID карты memcpy(card->raw_cid, cmd.rsp, 16); /* CMD3 - SET_RELATIVE_ADDR */ cmd.idx = SD_CMD(3); cmd.flags = 0; cmd.arg = 2 << 16; status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; //если нет ответа, можно выходить } card->rca = 2; //тупо //вырубаем open drain для команд инициализации HWREG(ctrl->memBase + MMCHS_CON) &= ~MMCHS_CON_OD; /* Send CMD9, to get the card specific data */ cmd.idx = SD_CMD(9); cmd.flags = SD_CMDRSP_136BITS; cmd.arg = card->rca << 16; status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; //если нет ответа, можно выходить } memcpy(card->raw_csd, cmd.rsp, 16); card->sd_ver = SD_CARD_CSD_VERSION(card); card->tranSpeed = SD_CARD0_TRANSPEED(card); //Меняем тактовую частоту на повыше //если где-то еще используется - запихнуть в функцию switch (card->tranSpeed & 0x00000007) { case 0: khz = 100e3; break; case 1: khz = 1000e3; break; case 2: khz = 10000e3; break; case 3: khz = 100000e3; break; default: UARTPuts("TRAN_SPEED incorrect value read", -1); card->error = 1; return 0; } switch ((card->tranSpeed) >> 3) { case 1: ctrl->opClk = 1 * khz; break; case 2: ctrl->opClk = 1.2 * khz; break; case 3: ctrl->opClk = 1.3 * khz; break; case 4: ctrl->opClk = 1.5 * khz; break; case 5: ctrl->opClk = 2 * khz; break; case 6: ctrl->opClk = 2.6 * khz; break; case 7: ctrl->opClk = 3 * khz; break; case 8: ctrl->opClk = 3.5 * khz; break; case 9: ctrl->opClk = 4 * khz; break; case 10: ctrl->opClk = 4.5 * khz; break; case 11: ctrl->opClk = 5.2 * khz; break; case 12: ctrl->opClk = 5.5 * khz; break; case 13: ctrl->opClk = 6 * khz; break; case 14: ctrl->opClk = 7 * khz; break; case 15: ctrl->opClk = 8 * khz; break; default: UARTPuts("TRAN_SPEED incorrect value read", -1); card->error = 1; return 0; } status = ctrl->busFreqConfig(ctrl, ctrl->opClk); if (status != 0) //эта функция возвращает ноль при успехе { card->error = 1; UARTPuts("HS MMC/SD TRAN_SPEED freqval set failed\n\r", -1); return 0; } //если спецификация вер. 4.0 и выше if (card->sd_ver > 3) { /* Send CMD7 select card */ cmd.idx = SD_CMD(7); cmd.flags = 0; //ответ R1 cmd.arg = card->rca << 16; status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; } #if 0 //говнокод if ((cmd.rsp[0] & BIT(25))) //если карта залочена стираем нафиг с нее все { //устанавливаем длину блока 1 байт /* Send CMD16 */ cmd.idx = SD_CMD(16); cmd.flags = 0; //ответ R1 cmd.arg = 1; //1 байт длина блока status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; } dataBuffer[0] = BIT(3); //Force-erase bit dataBuffer[1] = 0; //Force-erase bit dataBuffer[2] = 0; //Force-erase bit dataBuffer[3] = BIT(3); //Force-erase bit #ifdef CACHE /* Invalidate the data cache. */ CacheDataCleanBuff((unsigned int)dataBuffer, 4); #endif ctrl->xferSetup(ctrl, 0/*0 - WRITE*/, dataBuffer, 1, 1); //Achtung! посылка одного байта cmd.idx = SD_CMD(42); cmd.flags = SD_CMDRSP_WRITE | SD_CMDRSP_DATA | (ctrl->dmaEnable << SD_CMDRSP_DMAEN_OFFSET); cmd.arg = 0; cmd.nblks = 1; cmd.data = (signed char*)dataBuffer; status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; } status = ctrl->xferStatusGet(ctrl); // if (status == 0) return 0; //проверяем статус карты cmd.idx = SD_CMD(13); cmd.flags = 0; //ответ R1 cmd.arg = card->rca << 16; //1 байт длина блока status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; } //ждем пока карта не снимет busy! while (!(HWREG(ctrl->memBase + MMCHS_PSTATE) & (unsigned int)BIT(20))); //возвращаем длину блока в нормальное состояние /* Send CMD16 */ cmd.idx = SD_CMD(16); cmd.flags = 0; //ответ R1 cmd.arg = 512; //512 байт длина блока status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; } while(1); } #endif //надо затянуть EXT_CSD ctrl->xferSetup(ctrl, 1, dataBuffer, 512, 1); cmd.idx = SD_CMD(8); cmd.flags = SD_CMDRSP_READ | SD_CMDRSP_DATA | (ctrl->dmaEnable << SD_CMDRSP_DMAEN_OFFSET); cmd.arg = 0; cmd.nblks = 1; cmd.data = (signed char*)dataBuffer; status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; } status = ctrl->xferStatusGet(ctrl); if (status == 0) { card->error = 1; return status; } #ifdef CACHE /* Invalidate the data cache. */ CacheDataInvalidateBuff((unsigned int)dataBuffer, DATA_RESPONSE_WIDTH); #endif } else { UARTPuts("Old Slowpoke eMMC card\n\r", -1); } //разное определение размера карты для highCap карт и обычных карт if (!(card->highCap)) { //не факт, что работает для !highCap card->blkLen = 1 << (SD_CARD0_RDBLKLEN(card)); card->size = SD_CARD0_SIZE(card); card->nBlks = card->size / card->blkLen; /* Set data block length to 512 (for byte addressing cards) */ if (card->blkLen != 512) { cmd.idx = SD_CMD(16); cmd.flags = 0; //resp R1 cmd.arg = 512; status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) { card->error = 1; return status; } else { card->blkLen = 512; } } } else //highcap - берем sector size из EXT_CSD { //надо сохранить нужную инфу из EXT_CSD на будущее //пока только SEC_COUNT card->blkLen = 512; card->nBlks = (dataBuffer[212] << 0) | (dataBuffer[213] << 8) | (dataBuffer[214] << 16) | (dataBuffer[215] << 24); card->size = card->nBlks; //для highcap карты размер в секторах } card->busWidth = 1; #if 0 //деселект рабочий но забили на него - карта у нас одна //deselect card /* Send CMD7 select card */ cmd.idx = SD_CMD(7); cmd.flags = SD_CMDRSP_NONE; //ответ R1 cmd.arg = 0; //rca = 0 status = ctrl->cmdSend(ctrl, &cmd); if (status == 0) return status; #endif //end emmc initialization return 1; }