//***************************************************************************** // sd_diskio.c // // Low level SD Card access hookup for FatFS // // Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ // // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the // distribution. // // Neither the name of Texas Instruments Incorporated nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //***************************************************************************** #include #include "py/mpconfig.h" #include "py/mphal.h" #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" #include "hw_types.h" #include "hw_memmap.h" #include "hw_ints.h" #include "rom_map.h" #include "sd_diskio.h" #include "sdhost.h" #include "pin.h" #include "prcm.h" #include "stdcmd.h" #include "utils.h" //***************************************************************************** // Macros //***************************************************************************** #define DISKIO_RETRY_TIMEOUT 0xFFFFFFFF #define CARD_TYPE_UNKNOWN 0 #define CARD_TYPE_MMC 1 #define CARD_TYPE_SDCARD 2 #define CARD_CAP_CLASS_SDSC 0 #define CARD_CAP_CLASS_SDHC 1 #define CARD_VERSION_1 0 #define CARD_VERSION_2 1 //***************************************************************************** // Disk Info for attached disk //***************************************************************************** DiskInfo_t sd_disk_info = {CARD_TYPE_UNKNOWN, CARD_VERSION_1, CARD_CAP_CLASS_SDSC, 0, 0, STA_NOINIT, 0}; //***************************************************************************** // //! Send Command to card //! //! \param ulCmd is the command to be send //! \paran ulArg is the command argument //! //! This function sends command to attached card and check the response status //! if any. //! //! \return Returns 0 on success, 1 otherwise // //***************************************************************************** static unsigned int CardSendCmd (unsigned int ulCmd, unsigned int ulArg) { unsigned long ulStatus; // Clear the interrupt status MAP_SDHostIntClear(SDHOST_BASE,0xFFFFFFFF); // Send command MAP_SDHostCmdSend(SDHOST_BASE,ulCmd,ulArg); // Wait for command complete or error do { ulStatus = MAP_SDHostIntStatus(SDHOST_BASE); ulStatus = (ulStatus & (SDHOST_INT_CC | SDHOST_INT_ERRI)); } while (!ulStatus); // Check error status if (ulStatus & SDHOST_INT_ERRI) { // Reset the command line MAP_SDHostCmdReset(SDHOST_BASE); return 1; } else { return 0; } } //***************************************************************************** // //! Get the capacity of specified card //! //! \param ulRCA is the Relative Card Address (RCA) //! //! This function gets the capacity of card addressed by \e ulRCA paramaeter. //! //! \return Returns 0 on success, 1 otherwise. // //***************************************************************************** static unsigned int CardCapacityGet(DiskInfo_t *psDiskInfo) { unsigned long ulRet; unsigned long ulResp[4]; unsigned long ulBlockSize; unsigned long ulBlockCount; unsigned long ulCSizeMult; unsigned long ulCSize; // Read the CSD register ulRet = CardSendCmd(CMD_SEND_CSD, (psDiskInfo->usRCA << 16)); if(ulRet == 0) { // Read the response MAP_SDHostRespGet(SDHOST_BASE,ulResp); // 136 bit CSD register is read into an array of 4 words. // ulResp[0] = CSD[31:0] // ulResp[1] = CSD[63:32] // ulResp[2] = CSD[95:64] // ulResp[3] = CSD[127:96] if(ulResp[3] >> 30) { ulBlockSize = SD_SECTOR_SIZE * 1024; ulBlockCount = (ulResp[1] >> 16 | ((ulResp[2] & 0x3F) << 16)) + 1; } else { ulBlockSize = 1 << ((ulResp[2] >> 16) & 0xF); ulCSizeMult = ((ulResp[1] >> 15) & 0x7); ulCSize = ((ulResp[1] >> 30) | (ulResp[2] & 0x3FF) << 2); ulBlockCount = (ulCSize + 1) * (1 << (ulCSizeMult + 2)); } // Calculate the card capacity in bytes psDiskInfo->ulBlockSize = ulBlockSize; psDiskInfo->ulNofBlock = ulBlockCount; } return ulRet; } //***************************************************************************** // //! Select a card for reading or writing //! //! \param Card is the pointer to card attribute structure. //! //! This function selects a card for reading or writing using its RCA from //! \e Card parameter. //! //! \return Returns 0 success, 1 otherwise. // //***************************************************************************** static unsigned int CardSelect (DiskInfo_t *sDiskInfo) { unsigned long ulRCA; unsigned long ulRet; ulRCA = sDiskInfo->usRCA; // Send select command with card's RCA. ulRet = CardSendCmd(CMD_SELECT_CARD, (ulRCA << 16)); if (ulRet == 0) { while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC)); } // Delay 250ms for the card to become ready mp_hal_delay_ms(250); return ulRet; } //***************************************************************************** // //! Initializes physical drive //! //! This function initializes the physical drive //! //! \return Returns 0 on succeeded. //***************************************************************************** DSTATUS sd_disk_init (void) { unsigned long ulRet; unsigned long ulResp[4]; if (sd_disk_info.bStatus != 0) { sd_disk_info.bStatus = STA_NODISK; // Send std GO IDLE command if (CardSendCmd(CMD_GO_IDLE_STATE, 0) == 0) { // Get interface operating condition for the card ulRet = CardSendCmd(CMD_SEND_IF_COND,0x000001A5); MAP_SDHostRespGet(SDHOST_BASE,ulResp); // It's a SD ver 2.0 or higher card if (ulRet == 0 && ((ulResp[0] & 0xFF) == 0xA5)) { // Version 1 card do not respond to this command sd_disk_info.ulVersion = CARD_VERSION_2; sd_disk_info.ucCardType = CARD_TYPE_SDCARD; // Wait for card to become ready. do { // Send ACMD41 CardSendCmd(CMD_APP_CMD, 0); ulRet = CardSendCmd(CMD_SD_SEND_OP_COND, 0x40E00000); // Response contains 32-bit OCR register MAP_SDHostRespGet(SDHOST_BASE, ulResp); } while (((ulResp[0] >> 31) == 0)); if (ulResp[0] & (1UL<<30)) { sd_disk_info.ulCapClass = CARD_CAP_CLASS_SDHC; } sd_disk_info.bStatus = 0; } //It's a MMC or SD 1.x card else { // Wait for card to become ready. do { CardSendCmd(CMD_APP_CMD, 0); ulRet = CardSendCmd(CMD_SD_SEND_OP_COND,0x00E00000); if (ulRet == 0) { // Response contains 32-bit OCR register MAP_SDHostRespGet(SDHOST_BASE, ulResp); } } while (((ulRet == 0) && (ulResp[0] >> 31) == 0)); if (ulRet == 0) { sd_disk_info.ucCardType = CARD_TYPE_SDCARD; sd_disk_info.bStatus = 0; } else { if (CardSendCmd(CMD_SEND_OP_COND, 0) == 0) { // MMC not supported by the controller sd_disk_info.ucCardType = CARD_TYPE_MMC; } } } } // Get the RCA of the attached card if (sd_disk_info.bStatus == 0) { ulRet = CardSendCmd(CMD_ALL_SEND_CID, 0); if (ulRet == 0) { CardSendCmd(CMD_SEND_REL_ADDR,0); MAP_SDHostRespGet(SDHOST_BASE, ulResp); // Fill in the RCA sd_disk_info.usRCA = (ulResp[0] >> 16); // Get tha card capacity CardCapacityGet(&sd_disk_info); } // Select the card. ulRet = CardSelect(&sd_disk_info); if (ulRet == 0) { sd_disk_info.bStatus = 0; } } } return sd_disk_info.bStatus; } //***************************************************************************** // //! De-initializes the physical drive //! //! This function de-initializes the physical drive //***************************************************************************** void sd_disk_deinit (void) { sd_disk_info.ucCardType = CARD_TYPE_UNKNOWN; sd_disk_info.ulVersion = CARD_VERSION_1; sd_disk_info.ulCapClass = CARD_CAP_CLASS_SDSC; sd_disk_info.ulNofBlock = 0; sd_disk_info.ulBlockSize = 0; sd_disk_info.bStatus = STA_NOINIT; sd_disk_info.usRCA = 0; } //***************************************************************************** // //! Reads sector(s) from the disk drive. //! //! //! This function reads specified number of sectors from the drive //! //! \return Returns RES_OK on success. // //***************************************************************************** DRESULT sd_disk_read (BYTE* pBuffer, DWORD ulSectorNumber, UINT SectorCount) { DRESULT Res = RES_ERROR; unsigned long ulSize; if (SectorCount > 0) { // Return if disk not initialized if (sd_disk_info.bStatus & STA_NOINIT) { return RES_NOTRDY; } // SDSC uses linear address, SDHC uses block address if (sd_disk_info.ulCapClass == CARD_CAP_CLASS_SDSC) { ulSectorNumber = ulSectorNumber * SD_SECTOR_SIZE; } // Set the block count MAP_SDHostBlockCountSet(SDHOST_BASE, SectorCount); // Compute the number of words ulSize = (SD_SECTOR_SIZE * SectorCount) / 4; // Check if 1 block or multi block transfer if (SectorCount == 1) { // Send single block read command if (CardSendCmd(CMD_READ_SINGLE_BLK, ulSectorNumber) == 0) { // Read the block of data while (ulSize--) { MAP_SDHostDataRead(SDHOST_BASE, (unsigned long *)pBuffer); pBuffer += 4; } Res = RES_OK; } } else { // Send multi block read command if (CardSendCmd(CMD_READ_MULTI_BLK, ulSectorNumber) == 0) { // Read the data while (ulSize--) { MAP_SDHostDataRead(SDHOST_BASE, (unsigned long *)pBuffer); pBuffer += 4; } CardSendCmd(CMD_STOP_TRANS, 0); while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC)); Res = RES_OK; } } } return Res; } //***************************************************************************** // //! Wrties sector(s) to the disk drive. //! //! //! This function writes specified number of sectors to the drive //! //! \return Returns RES_OK on success. // //***************************************************************************** DRESULT sd_disk_write (const BYTE* pBuffer, DWORD ulSectorNumber, UINT SectorCount) { DRESULT Res = RES_ERROR; unsigned long ulSize; if (SectorCount > 0) { // Return if disk not initialized if (sd_disk_info.bStatus & STA_NOINIT) { return RES_NOTRDY; } // SDSC uses linear address, SDHC uses block address if (sd_disk_info.ulCapClass == CARD_CAP_CLASS_SDSC) { ulSectorNumber = ulSectorNumber * SD_SECTOR_SIZE; } // Set the block count MAP_SDHostBlockCountSet(SDHOST_BASE, SectorCount); // Compute the number of words ulSize = (SD_SECTOR_SIZE * SectorCount) / 4; // Check if 1 block or multi block transfer if (SectorCount == 1) { // Send single block write command if (CardSendCmd(CMD_WRITE_SINGLE_BLK, ulSectorNumber) == 0) { // Write the data while (ulSize--) { MAP_SDHostDataWrite (SDHOST_BASE, (*(unsigned long *)pBuffer)); pBuffer += 4; } // Wait for data transfer complete while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC)); Res = RES_OK; } } else { // Set the card write block count if (sd_disk_info.ucCardType == CARD_TYPE_SDCARD) { CardSendCmd(CMD_APP_CMD,sd_disk_info.usRCA << 16); CardSendCmd(CMD_SET_BLK_CNT, SectorCount); } // Send multi block write command if (CardSendCmd(CMD_WRITE_MULTI_BLK, ulSectorNumber) == 0) { // Write the data buffer while (ulSize--) { MAP_SDHostDataWrite(SDHOST_BASE, (*(unsigned long *)pBuffer)); pBuffer += 4; } // Wait for transfer complete while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC)); CardSendCmd(CMD_STOP_TRANS, 0); while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC)); Res = RES_OK; } } } return Res; }