This is to keep the top-level directory clean, to make it clear what is core and what is a port, and to allow the repository to grow with new ports in a sustainable way.
434 lines
14 KiB
C
434 lines
14 KiB
C
//*****************************************************************************
|
|
// 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 <stdbool.h>
|
|
|
|
#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;
|
|
}
|