blob: 236ac95a484fd2e434788526111075842d6ea689 [file] [log] [blame]
/******************************************************************************
*
* (C)Copyright 2005 - 2011 Marvell. All Rights Reserved.
*
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MARVELL.
* The copyright notice above does not evidence any actual or intended
* publication of such source code.
* This Module contains Proprietary Information of Marvell and should be
* treated as Confidential.
* The information in this file is provided for the exclusive use of the
* licensees of Marvell.
* Such users have the right to use, modify, and incorporate this code into
* products for purposes authorized by the license agreement provided they
* include this notice and the associated copyright notice with any such
* product.
* The information in this file is provided "AS IS" without warranty.
*
******************************************************************************/
#include "predefines.h"
#include "sdmmc_api.h"
#include "sdhc2.h"
#include "Errors.h"
#include "PlatformConfig.h"
#include "xllp_dfc_defs.h"
#include "loadoffsets.h"
static MM4_SDMMC_CONTEXT_T MM4_Context; // Only need one
extern ADMA_DESCRIPTOR *admaDesc;
UINT_T MM4_MMCDecodeCID(void)
{
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
UINT_T ManufactureID; // Manufactor ID
UINT_T OID; // OEM/Application ID
UINT_T SerialNumber;
UINT_T ProductName; // last 32bits
UINT_T ProductRevision;
ManufactureID = UNSTUFF_BITS(pSDMMCP->CardReg.CID.CID_VALUE, 120, 8);
OID = UNSTUFF_BITS(pSDMMCP->CardReg.CID.CID_VALUE, 104, 8);
ProductName = UNSTUFF_BITS(pSDMMCP->CardReg.CID.CID_VALUE, 56, 32);
SerialNumber = UNSTUFF_BITS(pSDMMCP->CardReg.CID.CID_VALUE, 16, 32);
ProductRevision = UNSTUFF_BITS(pSDMMCP->CardReg.CID.CID_VALUE, 48, 8);
switch (ManufactureID)
{
case 0x45:
case 0x02:
obm_printf("Manufacturer: SanDisK\n\r");
break;
case 0xfe:
case 0x13:
obm_printf("Manufacturer: Micron\n\r");
break;
case 0x70:
obm_printf("Manufacturer: Kingston\n\r");
break;
case 0x11:
obm_printf("Manufacturer: Toshiba\n\r");
break;
case 0x90:
obm_printf("Manufacturer: Hynix\n\r");
break;
case 0x15:
obm_printf("Manufacturer: Samsung\n\r");
break;
default:
obm_printf("New Manufacturer: 0x%08x\n\r", ManufactureID);
break;
}
}
UINT_T MM4_MMCDecodeEXTCSD(void)
{
UINT8_T pBuffer[512];
UINT_T ret = NoError;
UINT_T sectors;
UINT64 capacity;
UINT8_T erase_group_define, card_type;
UINT_T boot_partition_size;
UINT8_T spec_version; // system specification version number
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
ret = MM4_MMCReadEXTCSD((UINT_T *)pBuffer);
if (ret != NoError)
return ret;
if (pBuffer[212] || pBuffer[213] || pBuffer[214] || pBuffer[215]) // SEC_COUNT
{
sectors = pBuffer[212] | (pBuffer[213] << 8) | (pBuffer[214] << 16) | (pBuffer[215] << 24);
capacity = sectors;
capacity *= 512; // capacity equals to sectors multiply by 512B
#if USE_SERIAL_DEBUG
obm_printf("sectors: 0x%08x\n\r", sectors);
obm_printf("capacity MB: %d\n\r", sectors/2/1024);
#endif
if (pSDMMCP->AccessMode == SECTOR_ACCESS)
{
pSDMMCP->CardCapacity = sectors;
}
else
{
pSDMMCP->CardCapacity = capacity;
}
}
erase_group_define = pBuffer[175]; // ERASE_GROUP_DEF
if (erase_group_define)
{
pSDMMCP->EraseSize = pBuffer[224] * 512 * 1024; // overwrite it, and erase unit size = 512KB * HC_ERASE_GRP_SIZE
}
boot_partition_size = pBuffer[226] * 128 *1024; // BOOT_SIZE_MULT * 128KB
if (pSDMMCP->AccessMode == SECTOR_ACCESS)
{
pSDMMCP->BootPartitionSize = boot_partition_size / HARD512BLOCKLENGTH;
}
else
{
pSDMMCP->BootPartitionSize = boot_partition_size;
}
#if USE_SERIAL_DEBUG
obm_printf("Boot Partition Size: %dMB\n\r", boot_partition_size/1024/1024);
obm_printf("pSDMMCP->BootPartitionSize: 0x%08x\n\r", pSDMMCP->BootPartitionSize);
#endif
card_type = pBuffer[196] & 0xf; // CARD_TYPE
pSDMMCP->DeviceType = card_type;
obm_printf("pSDMMCP->DeviceType: 0x%x\n\r", pSDMMCP->DeviceType);
spec_version = pBuffer[192];
switch (spec_version)
{
case 0:
obm_printf("MMC Version 4.0\n\r");
break;
case 1:
obm_printf("MMC Version 4.1\n\r");
break;
case 2:
obm_printf("MMC Version 4.2\n\r");
break;
case 3:
obm_printf("MMC Version 4.3\n\r");
break;
case 5:
obm_printf("MMC Version 4.41\n\r");
break;
case 6:
obm_printf("MMC Version 4.5\n\r");
break;
case 4:
default:
obm_printf("Reserved Version: %d\n\r", spec_version);
break;
}
return ret;
}
/**********************************************************
* MM4_CardInit
* Initializes the inserted card
* Input:
* none
* Output:
* none
* Returns:
* WTP recoginized Success/Fail return code
***********************************************************/
UINT_T MM4_CardInit(UINT_T BaseAddress, UINT_T InterruptMask)
{
UINT_T argument,Retval;
UINT_T controllervoltage;
P_MM4_SDMMC_CONTEXT_T pContext;
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
// Assign pContext and clock values
MM4_PrepareMMCContext (pSDMMCP, BaseAddress);
pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
// enable ints moved out of ConfigureMMC to here
// because pending interrupts (left over from bootrom
// mmc activity) were kicking off before the context
// had enough info for the isr to service the request.
// now pads have been set up, and the context has
// enough information to service interrupts (specifically,
// it has the mm4 base regs address). ok to enable ints.
EnablePeripheralIRQInterrupt(InterruptMask);
// Issue a full reset.
MMC4FullSWReset(pContext);
// Enable and start clocks
MMC4SetBusRate(pContext, MM4CLOCK200KHZRATE);
// Set Read Response Timeout
MMC4SetDataTimeout(pContext, CLOCK_27_MULT);
// Unmask and Enable interrupts
MMC4EnableDisableIntSources(pContext, ENABLE_INTS);
//MMC4Gen74Clocks(pContext);
Retval = MM4_IDCard(pSDMMCP, &controllervoltage);
if (Retval != NoError)
//return Retval;
return SDMMCNotFound;
// Set up State
pSDMMCP->State = INITIALIZE;
// SD and MMC joint functionality again
// At this point we should have our OCR contents. See if they match the voltage range we choose for the controller
Retval = MM4_CheckVoltageCompatibility(pSDMMCP, controllervoltage);
if (Retval != NoError)
return SDMMCInitializationError; // We couldn't find any cards
//send CMD2 to get the CID numbers
argument = NO_ARGUMENT;
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD2, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R2 | MM4_136_RES);
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R2, 0x10);
if (Retval != NoError)
return SDMMCInitializationError;
#if USE_SERIAL_DEBUG
MM4_MMCDecodeCID();
#endif
// Next its CMD3 to assign an RCA to the cards
if (pSDMMCP->SD == XLLP_SD)
{
argument = NO_ARGUMENT;
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD3, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R6 | MM4_48_RES);
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R6, 0x10);
}
else
{
pSDMMCP->CardReg.RCA = (pSDMMCP->CardReg.CID.SerialNum) << 16;
argument = pSDMMCP->CardReg.RCA;
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD3, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R1 | MM4_48_RES);
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x10);
}
if (Retval != NoError)
return SDMMCInitializationError;
//send CMD13 to check the status of the card
Retval = MM4_CheckCardStatus(pSDMMCP, 0x700, R1_LOCKEDCARDMASK); // Make sure card is stdby mode
if (Retval != NoError)
return SDMMCInitializationError;
// now we are beyond the point where some cards have subtle non-compliance issues with the spec.
// for example, some cards leave the error bits from unrecognized commands (like 55 & 41) set
// until this point - which is correct (since CMD3 was the first R1 type command).
// other cards clear them earlier, which is incorrect - but presents fewer initialization failures.
// at this point it is safe, and necessary, to begin rigorously examining all error status bits.
pSDMMCP->StrictErrorCheck = 1;
// Send CMD 9 to retrieve the CSD
argument = pSDMMCP->CardReg.RCA;
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD9, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R2 | MM4_136_RES);
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R2, 0x100);
// Save off some information from the CSD
// There is no page size for SDMMC. However save some information so slot type functionality will work
pSDMMCP->pFlashP->BlockSize = pSDMMCP->pFlashP->PageSize = pSDMMCP->ReadBlockSize;
//send CMD7 to get card into transfer state
argument = pSDMMCP->CardReg.RCA;
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD7, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R1 | MM4_48_RES); // fixme: from standby to transfer state should be an r1, not r1b
Retval |= MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x100);
//send CMD13 to check the status of the card
Retval |= MM4_CheckCardStatus(pSDMMCP, 0x900, R1_LOCKEDCARDMASK); // Make sure card is transfer mode
if (Retval != NoError)
return SDMMCInitializationError;
// CMD 16 Set Block Length
argument = pSDMMCP->ReadBlockSize;
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD16, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R1 | MM4_48_RES);
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x100);
// Set the block length for the controller
pContext->pMMC4Reg->mm4_blk_cntl = argument;
// Check if High Speed is enabled in the fuses
if(MMCHighSpeedTimingEnabled())
Retval = MM4HighSpeedTiming();
else
MMC4SetBusRate(pContext, MM4CLOCK12_5MHZRATE);
//send CMD13 to check the status of the card
Retval = MM4_CheckCardStatus(pSDMMCP, 0x900, R1_LOCKEDCARDMASK); // Make sure card is transfer mode
if (Retval != NoError)
return SDMMCInitializationError;
Retval = MM4_MMCDecodeEXTCSD(); // MMC decodes extension CSD register
if (Retval != NoError)
return SDMMCInitializationError;
if (pSDMMCP->DeviceType & EXT_CSD_CARD_TYPE_DDR_52)
{
obm_printf("DDR mode\n\r");
// eMMC DDR mode, set bit31, clear bit30
//MMC4SetTXIntCLKSet(pContext, 0, 1);
MMC4SetTXConfig(pContext);
MMC4SetRXConfig(pContext);
MMC4SelectUHSMode(pContext, SDH_BUS_SPEED_DDR_CLK_50M); // host to DDR mode
Retval = MM4SetBusWidth(DDR_8); //set the bus width to 8, eMMC to DDR mode
if (Retval != NoError)
return SDMMCInitializationError;
}
else
{
obm_printf("SDR mode\n\r");
//#if HELN
// Helan eMMC DDR mode, set bit30, clear bit31
MMC4SetTXIntCLKSet(pContext, 1, 0);
//#endif
Retval = MM4SetBusWidth(SDR_8); //set the bus width to 8, eMMC to SDR mode
if (Retval != NoError)
return SDMMCInitializationError;
}
//send CMD13 to check the status of the card
Retval = MM4_CheckCardStatus(pSDMMCP, 0x900, R1_LOCKEDCARDMASK); // Make sure card is transfer mode
if (Retval != NoError)
return SDMMCInitializationError;
// Enable SDMA/ADMA Mode based on support
#if MMC_SDMA_MODE
obm_printf("SDMA mode\n\r");
pSDMMCP->SDMMC_DMA_Mode = SDMA;
#elif MMC_ADMA_MODE
obm_printf("ADMA2 mode\n\r");
pSDMMCP->SDMMC_DMA_Mode = ADMA2;
#else
obm_printf("NODMA mode\n\r");
pSDMMCP->SDMMC_DMA_Mode = NODMA;
#endif
// Set up State, Ready for Data transfers
pSDMMCP->State = READY;
return NoError;
}
/**********************************************************
* MM4_IDCard
* Identifies which type of card was inserted
* Input:
* none
* Output:
* none
* Returns:
* WTP recoginized Success/Fail return code
***********************************************************/
UINT MM4_IDCard(P_SDMMC_Properties_T pSDMMCP, UINT_T *pControllerVoltage)
{
// Local Variables
P_MM4_SDMMC_CONTEXT_T pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext; // Assign our context value
P_MM4_CTRL_REG pP_MM4_CTRL_REG;
UINT_T AnyError = 0;
UINT_T argument = 0;
UINT_T Retval = 0;
UINT_T HighCapacity = 0;
UINT_T attempts = 0;
UINT_T Ncr = 0x100; // command response timeout.
// Assign some context
pP_MM4_CTRL_REG = (P_MM4_CTRL_REG)&pContext->pMMC4Reg->mm4_mmc_ctrl_reg;
// Enable power
*pControllerVoltage = MM4_SetControllerVoltage(pContext);
pContext->pMMC4Reg->mm4_blk_cntl = 0;
// Send CMD0 (GO_IDLE_STATE) to get card into idle state
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD0, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_NONE | MM4_NO_RES);
AnyError += MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_NONE, Ncr);
// Check for High Capacity Cards First
do
{
// Start with SD
pP_MM4_CTRL_REG->mmc_card = 0;
// Try High Voltage range first:
// Note: this is a valid command for SD cards in the idle state.
// for mmc cards in the idle state, this is not valid, so no reponse is generated.
// however, even some SD cards may miss this command, so that's why it is
// sent out twice. See SD spec, step 4 of figure 9 in card init and id section.
argument = (SDVHS_2_7_TO_3_6 << SDVHSARGSHIFT) | SDVHSCHECKPATTERN; // 2.7-3.6V Range
MM4_SendSetupCommand(pSDMMCP, XLLP_SD_CMD8, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R7 | MM4_48_RES);
// get the response (if any) to XLLP_SD_CMD8.
MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R7, Ncr);
// Check for High Capacity Cards
HighCapacity = (pSDMMCP->SD_VHS == argument);
} while (!HighCapacity && ++attempts < 2);
pSDMMCP->SD = XLLP_SD;
// First time, pass NULL argument to get back values card is compatible with
// Send appropriate CMD Sequence to Identify the type of card inserted
argument = 0;
attempts = 0;
pSDMMCP->CardReg.OCR = 0; // Make sure to clear out OCR.
do
{
switch (pSDMMCP->SD)
{
case XLLP_SD: // Assume SD
MM4_SendSetupCommand(pSDMMCP, XLLP_SD_CMD55, MM4_CMD_TYPE_NORMAL, 0, MM4_RT_R1 | MM4_48_RES);
MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, Ncr);
MM4_SendSetupCommand(pSDMMCP, XLLP_SD_ACMD41, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R3 | MM4_48_RES);
MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R3, Ncr);
if (pSDMMCP->CardReg.OCR == 0)
{
pSDMMCP->SD = XLLP_MMC;
}
else
{
Retval = NoError;
}
break;
case XLLP_MMC: // Assume MMC
pP_MM4_CTRL_REG->mmc_card = 1;
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD1, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R3 | MM4_48_RES);
AnyError += MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R3, Ncr);
if (pSDMMCP->CardReg.OCR == 0)
{
Retval = NotFoundError;
}
else
{
Retval = NoError;
}
break;
}
argument = pSDMMCP->CardReg.OCR | 0x40000000;
attempts++;
Delay(100);
} while (((pSDMMCP->CardReg.OCR & 0x80000000) != 0x80000000) && (AnyError == 0) && (attempts < 10000));
if ((pSDMMCP->CardReg.OCR & 0x80000000) != 0x80000000)
return NotFoundError;
// Assign Access Mode.
if (pSDMMCP->CardReg.OCR & OCR_ACCESS_MODE_MASK)
pSDMMCP->AccessMode = SECTOR_ACCESS;
else
pSDMMCP->AccessMode = BYTE_ACCESS;
return Retval;
}
/***************************************************************
* MM4SetBusWidth()
* Sets the Bus width highest bus width supported.
* Input: UINT_T Width
* Output:
* Returns: NoError, ReadError or NotSupportedError
*
*****************************************************************/
UINT_T MM4SetBusWidth(UINT8_T value)
{
UINT8_T SDBusWidth = 0;
MMC_CMD6_OVERLAY Cmd6;
P_MM4_SDMMC_CONTEXT_T pContext;
P_MM4_CNTL1 pMM4_CNTL1;
UINT_T Retval = NoError;
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
// Assign our context value
pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
pMM4_CNTL1 = (P_MM4_CNTL1) &pContext->pMMC4Reg->mm4_cntl1;
// Check supported configurations first
if (pSDMMCP->SD == XLLP_SD)
{
// Assume 1 bit Mode.
SDBusWidth = SCRSD1BITMODE;
}
else
{
// Issue CMD 6 to set BUS WIDTH bits in EXT_CSD register byte 183
Cmd6.MMC_CMD6_Layout.Access = EXT_CSD_ACCESS_WRITE_BYTE; //Write Byte
Cmd6.MMC_CMD6_Layout.CmdSet = 0; // Don't Care
Cmd6.MMC_CMD6_Layout.Index = BUS_WIDTH_MMC_EXT_CSD_OFFSET; // Choose Bus Width
Cmd6.MMC_CMD6_Layout.Reserved0 = 0;
Cmd6.MMC_CMD6_Layout.Reserved1 = 0;
Cmd6.MMC_CMD6_Layout.Value = value; // Choose 8 bit mode.
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD6, MM4_CMD_TYPE_NORMAL, Cmd6.MMC_CMD6_Bits, MM4_RT_R1 | MM4_RT_BUSY | MM4_48_RES_WITH_BUSY);
// Wait for the Read to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY)); // TBD add timeout Let the ISR run, we'll either get a fault or finish
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1B, MMC_RESPONSE_R1B_TIMEOUT);
if (Retval != NoError)
return Retval;
}
//send CMD13 to check the status of the card
Retval |= MM4_CheckCardStatus(pSDMMCP, 0x900, R1_LOCKEDCARDMASK); // Make sure card is transfer mode
if (Retval != NoError)
{
pSDMMCP->State = READY;
return Retval;
}
// Now change the controller to boost bus width
//if (pSDMMCP->SD == XLLP_SD)
//{
// if (SDBusWidth == SCRSD4BITMODE)
// {
// pMM4_CNTL1->datawidth = 1; // Move to 4-bit mode
// return NoError;
// }
//}
//else
if (pSDMMCP->SD == XLLP_MMC)
pMM4_CNTL1->ex_data_width = 1; // Move to 8-bit mode.
return NoError;
}
/***************************************************************
* MM4HighSpeedTiming()
* Sets the Bus speed to high speed timing
* Input:
* Output:
* Returns: NoError, ReadError or NotSupportedError
*
*****************************************************************/
UINT_T MM4HighSpeedTiming(void)
{
MMC_CMD6_OVERLAY Cmd6;
P_MM4_SDMMC_CONTEXT_T pContext;
P_MM4_CNTL1 pMM4_CNTL1;
UINT_T Retval = NoError;
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
// Assign our context value
pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
pMM4_CNTL1 = (P_MM4_CNTL1) &pContext->pMMC4Reg->mm4_cntl1;
// Check supported configurations first
if (pSDMMCP->SD == XLLP_SD)
{
MMC4SetBusRate(pContext, MM4CLOCK12_5MHZRATE);
return NoError;
}
else
{
// Issue CMD 6 to set BUS WIDTH bits in EXT_CSD register byte 183
Cmd6.MMC_CMD6_Layout.Access = EXT_CSD_ACCESS_WRITE_BYTE; //Write Byte
Cmd6.MMC_CMD6_Layout.CmdSet = 0; // Don't Care
Cmd6.MMC_CMD6_Layout.Index = HS_TIMING_MMC_EXT_CSD_OFFSET; // Choose Bus Width
Cmd6.MMC_CMD6_Layout.Reserved0 = 0;
Cmd6.MMC_CMD6_Layout.Reserved1 = 0;
Cmd6.MMC_CMD6_Layout.Value = (UINT8_T) 1; // Choose High Speed Timing.
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD6, MM4_CMD_TYPE_NORMAL, Cmd6.MMC_CMD6_Bits, MM4_RT_R1 | MM4_RT_BUSY | MM4_48_RES_WITH_BUSY);
// Wait for the Read to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY));
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1B, MMC_RESPONSE_R1B_TIMEOUT);
if (Retval != NoError)
return Retval;
}
//send CMD13 to check the status of the card
Retval |= MM4_CheckCardStatus(pSDMMCP, 0x900, R1_LOCKEDCARDMASK); // Make sure card is transfer mode
if (Retval != NoError)
{
pSDMMCP->State = READY;
MMC4SetBusRate(pContext, MM4CLOCK12_5MHZRATE); // Failed, stick with lower speed
return NoError;
}
else
pMM4_CNTL1->hispeed = 1;
// Now change the speed to max through the controller
MMC4SetBusRate(pContext, MM4CLOCK50MHZRATE);
return NoError;
}
/***************************************************************
* MM4SwitchPartition
* If the Card supports partitioning (eSD) this routine will switch to the appropriate
* partition by using extended partition command set CMD37.
* Input:
* PartitionNumber - Contains the partition Number to switch to and enable bits for the boot partitions.
* Output:
* Returns: NoError, ReadError or NotSupportedError
*
*****************************************************************/
UINT_T MM4SwitchPartition(UINT_T PartitionNumber)
{
UINT_T argument, Cmd;
UINT_T Retval = NoError;
MMC_CMD6_OVERLAY Cmd6;
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
// Must set partition
if (pSDMMCP->SD == XLLP_SD)
{
// CMD 43 Select Partition
Cmd = XLLP_eSD_CMD43;
argument = PartitionNumber << 24;//partition number goes in bits [31:24], rest are 0's
MM4_SendSetupCommand(pSDMMCP, Cmd, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R1 | MM4_RT_BUSY | MM4_48_RES_WITH_BUSY);
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1B, MMC_RESPONSE_R1B_TIMEOUT);
}
else
{
// Issue CMD 6 to clear PARTITION_ACCESS bits in EXT_CSD register byte 179
Cmd6.MMC_CMD6_Layout.Access = EXT_CSD_ACCESS_CLEAR_BITS; // Clear bits
Cmd6.MMC_CMD6_Layout.CmdSet = 0; // Don't Care
Cmd6.MMC_CMD6_Layout.Index = PARTITION_CONFIG_MMC_EXT_CSD_OFFSET; // Choose Boot Config
Cmd6.MMC_CMD6_Layout.Reserved0 = 0;
Cmd6.MMC_CMD6_Layout.Reserved1 = 0;
Cmd6.MMC_CMD6_Layout.Value = PARTITION_ACCESS_BITS; // Clear out Partition Access bits
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD6, MM4_CMD_TYPE_NORMAL, Cmd6.MMC_CMD6_Bits, MM4_RT_R1 | MM4_RT_BUSY | MM4_48_RES_WITH_BUSY);
// Wait for the Read to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY));
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1B, 0x500);
if (Retval != NoError)
return Retval;
// Now issue CMD 6 again to set the right bits.
Cmd6.MMC_CMD6_Layout.Access = EXT_CSD_ACCESS_SET_BITS; // Clear bits
Cmd6.MMC_CMD6_Layout.CmdSet = 0; // Don't Care
Cmd6.MMC_CMD6_Layout.Index = PARTITION_CONFIG_MMC_EXT_CSD_OFFSET; // Choose Boot Config
Cmd6.MMC_CMD6_Layout.Reserved0 = 0;
Cmd6.MMC_CMD6_Layout.Reserved1 = 0;
Cmd6.MMC_CMD6_Layout.Value = PartitionNumber; // Set the correct partition
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD6, MM4_CMD_TYPE_NORMAL, Cmd6.MMC_CMD6_Bits, MM4_RT_R1 | MM4_RT_BUSY | MM4_48_RES_WITH_BUSY);
// Wait for the Read to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY));
Retval |= MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1B, 0x500);
if (Retval != NoError)
return Retval;
}
Retval |= MM4_CheckCardStatus(pSDMMCP, 0x900, R1_LOCKEDCARDMASK);
if (Retval != NoError)
{
pSDMMCP->State = READY;
return SDMMC_SWITCH_ERROR;
}
return NoError;
}
/**********************************************************
* MM4_CardShutdown
* Shuts down the MM4 hardware
* Input:
* Output:
* none
* Returns:
* WTP recoginized Success/Fail return code
***********************************************************/
UINT_T MM4_CardShutdown(void)
{
P_MM4_SDMMC_CONTEXT_T pContext;
P_MM4_CNTL1 pMM4_CNTL1;
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
pMM4_CNTL1 = (P_MM4_CNTL1) &pContext->pMMC4Reg->mm4_cntl1;
// send a CMD0, go idle here so the card gets into the idle state.
// even though that forces any subsequent software, like the os,
// to rediscover and reinit the card, this is worth doing. by
// putting the card into the idle state, it will be able to handle
// any bootrom flash probe operation if a platform reset occurs
// before the OS has completely initialized the device.
//
// Send CMD0 (GO_IDLE_STATE) to get card into idle state
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD0, MM4_CMD_TYPE_NORMAL, 0, MM4_RT_NONE | MM4_NO_RES);
MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_NONE, 1);
// Disable Bus Power
pMM4_CNTL1->buspwr = 1;
// Stop Bus Clock
MMC4StopBusClock (pSDMMCP->pContext);
// Mask all interrupts
MMC4EnableDisableIntSources(pSDMMCP->pContext, DISABLE_INTS);
// Disable internal clocks.
MMC4StopInternalBusClock(pSDMMCP->pContext);
return NoError;
}
/****************************************************************
* MM4PrepareMMCContext
* Sets certain Peripheral register address
* Input:
* P_SDMMC_Properties_T pSDMMCP - pointer to the SDMMC context structure
* Output:
* none
* Returns:
* none
*****************************************************************/
void MM4_PrepareMMCContext (P_SDMMC_Properties_T pSDMMCP, UINT_T BaseAddress)
{
pSDMMCP->pContext = &MM4_Context;
// First fill out pContext
((P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext)->pMMC4Reg = (P_MM4_SDMMC_T) BaseAddress;
}
/****************************************************************
* MM4_SetControllerVoltage
* Inspects the Capabilities Register for supported voltage types by the
* controller. Then programs the CNTL1 register with the desired range.
* Enables bus power
* Input:
* P_MM4_SDMMC_CONTEXT_T pContext
* Output:
* none
* Returns:
* none
*****************************************************************/
UINT_T MM4_SetControllerVoltage (P_MM4_SDMMC_CONTEXT_T pContext)
{
UINT_T controllervoltage;
P_MM4_CNTL1 pMM4_CNTL1 = (P_MM4_CNTL1) &pContext->pMMC4Reg->mm4_cntl1;
MM4_CNTL1_UNION MM4_cntl1;
// Capture the Value
MM4_cntl1.mm4_cntl1_value = *(VUINT_T*) pMM4_CNTL1;
// Note that this really doesn't control voltage, it just needs to match one of the supported values in the capabilities 2 register.
controllervoltage = MM4_VLTGSEL_3_3;
// Set the voltage to controller
MM4_cntl1.mm4_cntl1_bits.vltgsel = controllervoltage;
// Enable Bus Power
MM4_cntl1.mm4_cntl1_bits.buspwr = 1;
// Write back out.
*(VUINT_T*) pMM4_CNTL1 = MM4_cntl1.mm4_cntl1_value;
return controllervoltage;
}
/****************************************************************
* MM4_CheckVoltageCompatibility
* Checks to make sure that the OCR register of the device supports the
* voltage range that was selected for the controller
* Input:
* P_MM4_SDMMC_CONTEXT_T pContext, UINT_T ControllerVoltage
* Output:
* none
* Returns:
* none
*****************************************************************/
UINT_T MM4_CheckVoltageCompatibility(P_SDMMC_Properties_T pSDMMCP, UINT_T ControllerVoltage)
{
// Check SD vs MMC
if (pSDMMCP->SD == XLLP_SD)
{
switch (ControllerVoltage)
{
case MM4_VLTGSEL_3_3:
if (pSDMMCP->CardReg.OCR & SD_OCR_VOLTAGE_3_3_TO_3_6)
return NoError;
case MM4_VLTGSEL_3_0:
if (pSDMMCP->CardReg.OCR & SD_OCR_VOLTAGE_1_8_TO_3_3)
return NoError;
case MM4_VLTGSEL_1_8:
if (pSDMMCP->CardReg.OCR & SD_OCR_VOLTAGE_1_8)
return NoError;
}
}
else
{
if ((pSDMMCP->CardReg.OCR & MMC_OCR_VOLTAGE_ALL) == MMC_OCR_VOLTAGE_ALL)
return NoError;
}
return SDMMCDeviceVoltageNotSupported;
}
/******************************************************************************
Description:
Set up the registers of the controller to start the transaction to
communicate to the card for data related command. The commands are clearly defined in the MMC
specification.
Input Parameters:
P_SDMMC_Properties_T pSDMMCP - Generic SD/MMC driver properties structure
Cmd
Command Index - See MMC or SD specification
argument
the argument of the command. MSW is for ARGH and LSW is for ARGL
BlockType
Multiple or Single Block Type
ResType
Expected response type
Output Parameters:
None
Returns:
None
*******************************************************************************/
void MM4_SendDataCommand (P_SDMMC_Properties_T pSDMMCP,
UINT_T Cmd,
UINT_T Argument,
UINT_T BlockType,
UINT_T DataDirection,
UINT_T ResType)
{
// Assign our context value
P_MM4_SDMMC_CONTEXT_T pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
// no need to clear out any fault state that may be left over from a previously failed transaction.
// that's because the caller has set State to read or write before calling here.
// No Response to the command yet
pSDMMCP->CardReponse.CommandComplete = 0;
pSDMMCP->CardReponse.TransferComplete = 0;
// save the info for use by the isr:
pSDMMCP->Trans.RespType = (ResType >> 8) & 0x000000ff;
pSDMMCP->Trans.Cmd = Cmd; // Fixme: how to know when to set the ACMD flag?
MMC4SendDataCommand(pContext,
Cmd,
Argument,
BlockType,
DataDirection,
ResType & 0x000000ff, // clear out any bits not for the SD_CMD.RES_TYPE field
((pSDMMCP->SDMMC_DMA_Mode == NODMA) ? 0 : 1),
((pSDMMCP->SDMMC_DMA_Mode == ADMA2) ? 1 : 0));
}
/******************************************************************************
Description:
Set up the registers of the controller to start the transaction to
communicate to the card for data related command. The commands are clearly defined in the MMC
specification.
Input Parameters:
P_SDMMC_Properties_T pSDMMCP - Generic SD/MMC driver properties structure
Cmd
Command Index - See MMC or SD specification
argument
the argument of the command. MSW is for ARGH and LSW is for ARGL
BlockType
Multiple or Single Block Type
ResType
Expected response type
Output Parameters:
None
Returns:
None
*******************************************************************************/
void MM4_SendDataCommandNoAuto12 (P_SDMMC_Properties_T pSDMMCP,
UINT_T Cmd,
UINT_T Argument,
UINT_T BlockType,
UINT_T DataDirection,
UINT_T ResType)
{
// Assign our context value
P_MM4_SDMMC_CONTEXT_T pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
// no need to clear out any fault state that may be left over from a previously failed transaction.
// that's because the caller has set State to read or write before calling here.
// No Response to the command yet
pSDMMCP->CardReponse.CommandComplete = 0;
pSDMMCP->CardReponse.TransferComplete = 0;
// save the info for use by the isr:
pSDMMCP->Trans.RespType = (ResType >> 8) & 0x000000ff;
pSDMMCP->Trans.Cmd = Cmd; // Fixme: how to know when to set the ACMD flag?
MMC4SendDataCommandNoAuto12(pContext,
Cmd,
Argument,
BlockType,
DataDirection,
ResType & 0x000000ff, // clear out any bits not for the SD_CMD.RES_TYPE field
((pSDMMCP->SDMMC_DMA_Mode == NODMA) ? 0 : 1),
((pSDMMCP->SDMMC_DMA_Mode == ADMA2) ? 0 : 1)); //Block count is disabled for ADMA2
}
/******************************************************************************
Description:
Set up the registers of the controller to start the transaction to
communicate to the card for setup related commands.
The commands are clearly defined in the MMC specification.
Input Parameters:
P_SDMMC_Properties_T pSDMMCP - Generic SD/MMC driver properties structure
Cmd
Command Index - See MMC or SD specification
argument
the argument of the command. MSW is for ARGH and LSW is for ARGL
ResType
Expected response type
Output Parameters:
None
Returns:
None
*******************************************************************************/
void MM4_SendSetupCommand(P_SDMMC_Properties_T pSDMMCP,
UINT_T Cmd,
UINT_T CmdType,
UINT_T Argument,
UINT_T ResType)
{
// Assign our context value
P_MM4_SDMMC_CONTEXT_T pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
// clear out any fault status that may be left over from a previously failed transaction.
pSDMMCP->State = READY;
// No Response to the command yet
pSDMMCP->CardReponse.CommandComplete = 0;
pSDMMCP->CardReponse.TransferComplete = 0;
// save the info for use by the isr:
pSDMMCP->Trans.RespType = (ResType >> 8) & 0x000000ff;
pSDMMCP->Trans.Cmd = Cmd; // Fixme: how to know when to set the ACMD flag?
MMC4SendSetupCommand(pContext,
Cmd,
CmdType,
Argument,
ResType & 0x000000ff); // clear out any bits not for the SD_CMD.RES_TYPE field
}
/****************************************************************
* MM4_Read_Response
* Reads the response from the Controller Buffer Registers to the Local Buffer.
* According to the last command and response type it does the correct interpretation.
* There is also a timeout as the routine waits for the ISR to signal last command completion.
* Input:
* P_SDMMC_Properties_T pSDMMCP - Generic SD/MMC driver properties structure
* ResponseType - See SD/MMC specifications
* ResponseTimeOut - A time out value in millisec's
* Output:
* none
* Returns:
* MM4InterpretTimeOutError or NoError
*****************************************************************/
UINT_T MM4_Interpret_Response(P_SDMMC_Properties_T pSDMMCP, UINT_T ResponseType, UINT_T ResponseTimeOut)
{
UINT_T i, temp, temp2, temp3, startTime, endTime;
UINT_T Retval = NoError;
P_MM4_SDMMC_CONTEXT_T pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext; // Assign our context value
P_MM4_CMD_XFRMD_UNION pMM4_XFRMD;
startTime = GetOSCR0(); // get the start time
endTime = startTime;
// Overlap XFRMD register contents using uniun
pMM4_XFRMD = (P_MM4_CMD_XFRMD_UNION) &pContext->pMMC4Reg->mm4_cmd_xfrmd;
// Wait for the Response based on the CommandComplete interrupt signal
while (1)
{
// if the command had an error, the command may have aborted
// without setting the command complete bit. for example,
// if no response is received, then the command is aborted,
// the MM4_ISR sets the state to FAULT, and command complete
// will not assert. check for such a scenario here.
if( pSDMMCP->State == FAULT )
{
Retval = SDMMC_GENERAL_ERROR;
break; // continue to capture command response to return a more specific error
}
if (pSDMMCP->CardReponse.CommandComplete)
{
if(ResponseType != MMC_RESPONSE_R1B)
break; // Done no issue.
else if (pSDMMCP->CardReponse.TransferComplete)
break; // R1B type commands now require MMC xfer_complete bit to be set
}
// check if too much time has elapsed. if so, timeout.
endTime = GetOSCR0();
if (OSCR0IntervalInMilli(startTime, endTime) > ResponseTimeOut)
{
Retval = MM4InterpretTimeOutError;
return Retval;
}
}
// Read in the Buffers
switch (ResponseType)
{
case MMC_RESPONSE_NONE:
break;
case MMC_RESPONSE_R1:
case MMC_RESPONSE_R1B:
pSDMMCP->CardReponse.R1_RESP = pSDMMCP->CardReponse.pBuffer[0];
break;
case MMC_RESPONSE_R2: // This is for CID or CSD register
{
if (pMM4_XFRMD->mm4_cmd_xfrmd_bits.cmd_idx == XLLP_MMC_CMD9) //CSD
{
// Copy the CSD values from the buffer
for (i=0; i<4; i++)
pSDMMCP->CardReg.CSD.CSD_VALUE[i] = pSDMMCP->CardReponse.pBuffer[i];
UINT16_T MMC_RESPONSE[8]; // for shift CSD register
MMC_RESPONSE[7] = (UINT16_T)((pSDMMCP->CardReg.CSD.CSD_VALUE[3] >> 16) & 0xffff);
MMC_RESPONSE[6] = (UINT16_T)(pSDMMCP->CardReg.CSD.CSD_VALUE[3] & 0xffff);
MMC_RESPONSE[5] = (UINT16_T)((pSDMMCP->CardReg.CSD.CSD_VALUE[2] >> 16) & 0xffff);
MMC_RESPONSE[4] = (UINT16_T)(pSDMMCP->CardReg.CSD.CSD_VALUE[2] & 0xffff);
MMC_RESPONSE[3] = (UINT16_T)((pSDMMCP->CardReg.CSD.CSD_VALUE[1] >> 16) & 0xffff);
MMC_RESPONSE[2] = (UINT16_T)(pSDMMCP->CardReg.CSD.CSD_VALUE[1] & 0xffff);
MMC_RESPONSE[1] = (UINT16_T)((pSDMMCP->CardReg.CSD.CSD_VALUE[0] >> 16) & 0xffff);
MMC_RESPONSE[0] = (UINT16_T)(pSDMMCP->CardReg.CSD.CSD_VALUE[0] & 0xffff);
pSDMMCP->CardReg.CSD.CSD_VALUE[0] = (UINT32_T)(MMC_RESPONSE[5] >> 8 | MMC_RESPONSE[6] << 8 | MMC_RESPONSE[7] << 24);
pSDMMCP->CardReg.CSD.CSD_VALUE[1] = (UINT32_T)(MMC_RESPONSE[3] >> 8 | MMC_RESPONSE[4] << 8 | MMC_RESPONSE[5] << 24);
pSDMMCP->CardReg.CSD.CSD_VALUE[2] = (UINT32_T)(MMC_RESPONSE[1] >> 8 | MMC_RESPONSE[2] << 8 | MMC_RESPONSE[3] << 24);
pSDMMCP->CardReg.CSD.CSD_VALUE[3] = (UINT32_T)(MMC_RESPONSE[0] << 8 | MMC_RESPONSE[1] << 24);
// Optionally we could record maximum block lengths from the CSD.
// But some devices cheat and put incorrect values in this field.
// Save off read Block Size, play it safe, for now hard code to 512 Bytes
pSDMMCP->ReadBlockSize = HARD512BLOCKLENGTH;
// Save off Write Block Size
pSDMMCP->WriteBlockSize = HARD512BLOCKLENGTH;
// Capture Erase Granularity.
if (pSDMMCP->SD == XLLP_SD)
{
// Check Erase Single Block Enable - Bit 46
if ((pSDMMCP->CardReg.CSD.CSD_VALUE[1] >> 14) & 1)
pSDMMCP->EraseSize = pSDMMCP->WriteBlockSize;
else
{
pSDMMCP->EraseSize = ((pSDMMCP->CardReg.CSD.CSD_VALUE[1] >> 7) & 0x7F) + 1;
pSDMMCP->EraseSize *= pSDMMCP->WriteBlockSize;
}
}
else // MMC Card
{
// if the card use high capacity erase unit size, pSDMMCP->EraseSize will be overwritten
//pSDMMCP->EraseSize = ((pSDMMCP->CardReg.CSD.CSD_VALUE[1] >> 5) & 0x1F) + 1; // Get ERASE_GRP_MULT
//pSDMMCP->EraseSize *= (((pSDMMCP->CardReg.CSD.CSD_VALUE[1] >> 10) & 0x1F) + 1); // Get ERASE_GRP_SIZE
pSDMMCP->EraseSize = ((pSDMMCP->CardReg.CSD.CSD_VALUE[2] >> 5) & 0x1F) + 1; // Get ERASE_GRP_MULT
pSDMMCP->EraseSize *= (((pSDMMCP->CardReg.CSD.CSD_VALUE[2] >> 10) & 0x1F) + 1); // Get ERASE_GRP_SIZE
pSDMMCP->EraseSize *= pSDMMCP->WriteBlockSize;
}
// Now calculate the capacity of this card
// the execution of CMD9 is before CMD8
// if the card is larger than 2GB, pSDMMCP->CardCapacity will be overwritten
//temp = ((pSDMMCP->CardReg.CSD.CSD_VALUE[2] >> 16) & 0xF); // Get READ_BL_LEN
temp = ((pSDMMCP->CardReg.CSD.CSD_VALUE[1] >> 16) & 0xF); // Get READ_BL_LEN
temp = 1 << temp;
// Now we have Max Block Length
//temp2 = ((pSDMMCP->CardReg.CSD.CSD_VALUE[1] >> 15) & 0x7) + 2; // Get C_SIZE_MULT
temp2 = ((pSDMMCP->CardReg.CSD.CSD_VALUE[2] >> 15) & 0x7) + 2; // Get C_SIZE_MULT
temp2 = 1 << temp2;
//temp3 = ((pSDMMCP->CardReg.CSD.CSD_VALUE[1] >> 30) & 0x3); // Get C_SIZE
temp3 = ((pSDMMCP->CardReg.CSD.CSD_VALUE[2] >> 30) & 0x3); // Get C_SIZE
//temp3 |= ((pSDMMCP->CardReg.CSD.CSD_VALUE[2] & 0x3FF) << 2); // Get C_SIZE
temp3 |= ((pSDMMCP->CardReg.CSD.CSD_VALUE[1] & 0x3FF) << 2); // Get C_SIZE
temp3++;
pSDMMCP->CardCapacity = temp3 * temp2 * temp; // Total Size of the card in Bytes
}
else // Assume CID
{
// Copy the CSD values from the buffer
for (i=0; i<4; i++)
pSDMMCP->CardReg.CID.CID_VALUE[i] = pSDMMCP->CardReponse.pBuffer[i];
UINT16_T MMC_RESPONSE1[8]; // for shift CID register
MMC_RESPONSE1[7] = (UINT16_T)((pSDMMCP->CardReg.CID.CID_VALUE[3] >> 16) & 0xffff);
MMC_RESPONSE1[6] = (UINT16_T)(pSDMMCP->CardReg.CID.CID_VALUE[3] & 0xffff);
MMC_RESPONSE1[5] = (UINT16_T)((pSDMMCP->CardReg.CID.CID_VALUE[2] >> 16) & 0xffff);
MMC_RESPONSE1[4] = (UINT16_T)(pSDMMCP->CardReg.CID.CID_VALUE[2] & 0xffff);
MMC_RESPONSE1[3] = (UINT16_T)((pSDMMCP->CardReg.CID.CID_VALUE[1] >> 16) & 0xffff);
MMC_RESPONSE1[2] = (UINT16_T)(pSDMMCP->CardReg.CID.CID_VALUE[1] & 0xffff);
MMC_RESPONSE1[1] = (UINT16_T)((pSDMMCP->CardReg.CID.CID_VALUE[0] >> 16) & 0xffff);
MMC_RESPONSE1[0] = (UINT16_T)(pSDMMCP->CardReg.CID.CID_VALUE[0] & 0xffff);
pSDMMCP->CardReg.CID.CID_VALUE[0] = (UINT32_T)(MMC_RESPONSE1[5] >> 8 | MMC_RESPONSE1[6] << 8 | MMC_RESPONSE1[7] << 24);
pSDMMCP->CardReg.CID.CID_VALUE[1] = (UINT32_T)(MMC_RESPONSE1[3] >> 8 | MMC_RESPONSE1[4] << 8 | MMC_RESPONSE1[5] << 24);
pSDMMCP->CardReg.CID.CID_VALUE[2] = (UINT32_T)(MMC_RESPONSE1[1] >> 8 | MMC_RESPONSE1[2] << 8 | MMC_RESPONSE1[3] << 24);
pSDMMCP->CardReg.CID.CID_VALUE[3] = (UINT32_T)(MMC_RESPONSE1[0] << 8 | MMC_RESPONSE1[1] << 24);
// Now capture the serial number from the CID - 32 bit number
if (pSDMMCP->SD == XLLP_MMC)
{
//pSDMMCP->CardReg.CID.SerialNum = (pSDMMCP->CardReg.CID.CID_VALUE[0] >> 16) & (0xFF);
pSDMMCP->CardReg.CID.SerialNum = (pSDMMCP->CardReg.CID.CID_VALUE[3] >> 16) & (0xFFFF);
//pSDMMCP->CardReg.CID.SerialNum |= (pSDMMCP->CardReg.CID.CID_VALUE[1] << 16);
pSDMMCP->CardReg.CID.SerialNum |= (pSDMMCP->CardReg.CID.CID_VALUE[2] << 16);
}
else
{
pSDMMCP->CardReg.CID.SerialNum = (pSDMMCP->CardReg.CID.CID_VALUE[0] >> 24) & (0xFF);
pSDMMCP->CardReg.CID.SerialNum |= (pSDMMCP->CardReg.CID.CID_VALUE[1] << 8);
}
}
break;
}
case MMC_RESPONSE_R3:
{
pSDMMCP->CardReg.OCR = pSDMMCP->CardReponse.pBuffer[0];
break;
}
case MMC_RESPONSE_R4: // These modes are not supported by the driver
case MMC_RESPONSE_R5:
case MMC_RESPONSE_R5B:
break;
case MMC_RESPONSE_R6: // Publishes RCA for SD cards
{
pSDMMCP->CardReg.RCA = pSDMMCP->CardReponse.pBuffer[0];
break;
}
case MMC_RESPONSE_R7:
{
pSDMMCP->SD_VHS = pSDMMCP->CardReponse.pBuffer[0];
break;
}
}
return Retval;
}
/****************************************************************
* SDMMCGetMatchWaitCardStatus
* Gets the status of the card by issuing CMD 13. The rerturn from the routine is based
* on a check against the expected value which is passed in
* Input:
* pSDMMCP - pointer to the current context
* MaxWaitMSec - Maximum wait time in millisec
* R1_Resp_Match - Desired Value to be matched
* Output:
* none
* Returns:
* none
*****************************************************************/
UINT_T MM4_CheckCardStatus(P_SDMMC_Properties_T pSDMMCP, UINT_T R1_Resp_Match, UINT_T Mask)
{
UINT_T argument, cardStatus, retval;
//send CMD13 to check the status of the card
argument = pSDMMCP->CardReg.RCA;
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD13, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R1 | MM4_48_RES);
retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x100);
// Mask out undesired check bits
cardStatus = (pSDMMCP->CardReponse.R1_RESP) & Mask;
if ((cardStatus == R1_Resp_Match) && (retval == NoError))
return NoError;
else
return MM4CheckCardStatusTimeOutError;
}
/***********************************************************
* MM4_ReadBlocks()
* Reads the given block off of the SD/MMC card and
* into LocalAddr or empty buffer
* input:
* none
* output:
* LocalAddr will contain the contents of the block
* returns:
* none
************************************************************/
UINT_T MM4_ReadBlocks(void)
{
UINT_T argument;
UINT_T Retval = NoError;
P_MM4_SDMMC_CONTEXT_T pContext;
P_MM4_BLK_CNTL pMM4_BLK_CNTL;
P_MM4_CNTL1 pMM4_CNTL1;
UINT_T total_blks,loadaddr,blks_to_txfr;
UINT_T i=0;
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
// Assign our context value
pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
// Must set MMC NUMBLK
pMM4_BLK_CNTL = (P_MM4_BLK_CNTL) &pContext->pMMC4Reg->mm4_blk_cntl;
//blk_cnt is used in PIO/SDMA mode
pMM4_BLK_CNTL->blk_cnt = pSDMMCP->Trans.NumBlocks;
pMM4_CNTL1 = (P_MM4_CNTL1)&pContext->pMMC4Reg->mm4_cntl1;
total_blks = pSDMMCP->Trans.NumBlocks;
loadaddr = pSDMMCP->Trans.LocalAddr;
if (pSDMMCP->SDMMC_DMA_Mode == SDMA)
{
pMM4_BLK_CNTL->dma_bufsz = MM4_512_HOST_DMA_BDRY;
pContext->pMMC4Reg->mm4_sysaddr = pSDMMCP->Trans.LocalAddr;
}
else if (pSDMMCP->SDMMC_DMA_Mode == ADMA2)
{
memset(admaDesc, 0, sizeof(ADMA_DESCRIPTOR)*NO_ADMA_TX_DESCS);
while(total_blks)
{
blks_to_txfr = (total_blks > MAX_TRANS_BLKS_ADMA_DESC)? MAX_TRANS_BLKS_ADMA_DESC: total_blks,
MM4_SetupADMADesc(
&admaDesc[i],
VALID_DESC,
(total_blks > MAX_TRANS_BLKS_ADMA_DESC)? NOT_LAST_DESC: LAST_DESC,
NO_DMA_INT,
TRANSFER,
blks_to_txfr,
loadaddr);
total_blks -= blks_to_txfr;
loadaddr += blks_to_txfr * pSDMMCP->ReadBlockSize;
i++;
}
pMM4_CNTL1->dma_sel = 2; //ADMA2
//Set the ADMA register to the address of descriptor table
pContext->pMMC4Reg->mm4_adma_system_address1 = (UINT_T)&admaDesc[0];
// Set the block count register to the total number of blocks for transterring
// and the max limmit is 65535
pMM4_BLK_CNTL->blk_cnt = pSDMMCP->Trans.NumBlocks;
//AutoCMD23 should be sent prior to CMD18 or CMD25 if ADMA is used and BLK_CNT is disabled
//mm4_sysaddr holds the argument field for Auto CMD23 that is the number of blocks
pContext->pMMC4Reg->mm4_sysaddr = pSDMMCP->Trans.NumBlocks;
#if ENABLE_MMU
dcache_clean_range(&admaDesc[0], sizeof(ADMA_DESCRIPTOR)*NO_ADMA_TX_DESCS);
#endif
}
#if ENABLE_MMU && (MMC_ADMA_MODE || MMC_SDMA_MODE)
// if MMU is enabled, for DMA operations need to invalidate data cache for data consistency
dcache_invalidate_range(pSDMMCP->Trans.LocalAddr, pSDMMCP->Trans.NumBlocks * 512);
#endif
// Set up State
pSDMMCP->State = READ;
// Do a CMD 18 Read Multiple Block
// In byte mode addressing; all addresses need to be specified as byte offsets.
argument = pSDMMCP->Trans.CardAddress;
if (pSDMMCP->AccessMode == BYTE_ACCESS)
argument *= HARD512BLOCKLENGTH;
// Kick off the Read
MM4_SendDataCommand(pSDMMCP, XLLP_MMC_CMD18, argument, MM4_MULTI_BLOCK_TRAN, MM4_CARD_TO_HOST_DATA, MM4_RT_R1 | MM4_48_RES);
// Wait for the Read to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY));
// Get the Card Response
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x100);
if ((Retval != NoError) || ((pSDMMCP->CardReponse.R1_RESP & R1_LOCKEDCARDMASK) != 0x900) || (pSDMMCP->State == FAULT))
{
Retval = SDMMCReadError;
pSDMMCP->State = FAULT;
}
else
{
pSDMMCP->State = READY;
}
return Retval;
}
// fixme: change this to use an r1b type...
UINT_T MM4_WaitReady(UINT_T TimeOutMilliSec)
{
UINT_T startTime, endTime;
UINT_T writecomplete = 0;
UINT_T Retval = NoError;
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
UINT_T argument = pSDMMCP->CardReg.RCA;
// issue a series of get status commands until the (new) status indicates ready.
// limit the amount of time to wait to the input parameter TimeOutMilliSec
startTime = GetOSCR0();
startTime = GetOSCR0();
do
{
//send CMD13 to check the status of the card
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD13, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R1 | MM4_48_RES);
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x100); // FIXME: this timeout value should be dependent on CSD and operating configuration
if (Retval != NoError) break; // failed to complete transaction
// examine the new status, which was just extracted from the response field of the get stauts command.
if( ( pSDMMCP->CardReponse.R1_RESP & 0x00000100 ) == 0x00000100 ) // card_status:READY_FOR_DATA asserted. write is complete.
{
writecomplete = 1;
break;
}
endTime = GetOSCR0(); // check for end of wait interval
}
while( OSCR0IntervalInMilli(startTime, endTime) < TimeOutMilliSec );
return writecomplete;
}
/***********************************************************
* MM4_WriteBlocks()
* Writes the required number of blocks to CardAddress
* input:
* none
* output:
* Address starting with CardAddress will contain content from LocalAddress
* returns:
* none
************************************************************/
UINT_T MM4_WriteBlocks(void)
{
UINT_T argument;
UINT_T Retval = NoError;
P_MM4_SDMMC_CONTEXT_T pContext;
P_MM4_BLK_CNTL pMM4_BLK_CNTL;
P_MM4_CNTL1 pMM4_CNTL1;
UINT_T total_blks,loadaddr,blks_to_txfr;
UINT_T i=0;
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
// Assign our context value
pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
// Must set MMC NUMBLK
pMM4_BLK_CNTL = (P_MM4_BLK_CNTL) &pContext->pMMC4Reg->mm4_blk_cntl;
//Required for PIO and SDMA mode
pMM4_BLK_CNTL->blk_cnt = pSDMMCP->Trans.NumBlocks;
pMM4_CNTL1 = (P_MM4_CNTL1)&pContext->pMMC4Reg->mm4_cntl1;
total_blks = pSDMMCP->Trans.NumBlocks;
loadaddr = pSDMMCP->Trans.LocalAddr;
if (pSDMMCP->SDMMC_DMA_Mode == SDMA)
{
pMM4_BLK_CNTL->dma_bufsz = MM4_512_HOST_DMA_BDRY;
pContext->pMMC4Reg->mm4_sysaddr = pSDMMCP->Trans.LocalAddr;
}
else if(pSDMMCP->SDMMC_DMA_Mode == ADMA2)
{
memset(admaDesc, 0, sizeof(ADMA_DESCRIPTOR)*NO_ADMA_TX_DESCS);
while(total_blks)
{
blks_to_txfr = (total_blks > MAX_TRANS_BLKS_ADMA_DESC)? MAX_TRANS_BLKS_ADMA_DESC: total_blks,
MM4_SetupADMADesc(&admaDesc[i],
VALID_DESC,
(total_blks > MAX_TRANS_BLKS_ADMA_DESC)? NOT_LAST_DESC: LAST_DESC,
NO_DMA_INT,
TRANSFER,
blks_to_txfr,
loadaddr);
total_blks -= blks_to_txfr;
loadaddr += blks_to_txfr * pSDMMCP->ReadBlockSize;
i++;
}
pMM4_CNTL1->dma_sel = 2; //ADMA2
//Set the ADMA register to the address of descriptor table
pContext->pMMC4Reg->mm4_adma_system_address1 = (UINT_T)&admaDesc[0];
// Set the block count register to the total number of blocks for transterring
// and the max limmit is 65535
pMM4_BLK_CNTL->blk_cnt = pSDMMCP->Trans.NumBlocks;
//AutoCMD23 should be sent prior to CMD18 or CMD25 if ADMA is used and BLK_CNT is disabled
//mm4_sysaddr holds the argument field for Auto CMD23 that is the number of blocks
pContext->pMMC4Reg->mm4_sysaddr = pSDMMCP->Trans.NumBlocks;
#if ENABLE_MMU
dcache_clean_range(&admaDesc[0], sizeof(ADMA_DESCRIPTOR)*NO_ADMA_TX_DESCS);
#endif
}
#if ENABLE_MMU && (MMC_ADMA_MODE || MMC_SDMA_MODE)
// if MMU is enabled, for DMA operations need to clean data cache for data consistency
dcache_clean_range(pSDMMCP->Trans.LocalAddr, pSDMMCP->Trans.NumBlocks * 512);
#endif
// Set up State
pSDMMCP->State = WRITE;
// Do a CMD 25 Write Multiple Blocks
// In byte mode addressing; all addresses need to be specified as byte offsets.
argument = pSDMMCP->Trans.CardAddress;
if (pSDMMCP->AccessMode == BYTE_ACCESS)
argument *= HARD512BLOCKLENGTH;
MM4_SendDataCommand(pSDMMCP, XLLP_MMC_CMD25, argument, MM4_MULTI_BLOCK_TRAN, MM4_HOST_TO_CARD_DATA, MM4_RT_R1 | MM4_48_RES);
// Wait for the Write to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY)); // TBD add timeout Let the ISR run, we'll either get a fault or finish
// Get the Card Response
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x100);
if ((Retval != NoError) || (pSDMMCP->State == FAULT) || ((pSDMMCP->CardReponse.R1_RESP & R1_NOMASK) != 0x900))
{
Retval = WriteError;
pSDMMCP->State = FAULT;
return Retval;
}
// the write data transfer completed...now must wait for the card to assert the ready line.
// (the ready status from the last data transfer is meaningless here because it was left
// over from the write command that initiated the data transfer.)
//
// section 7.7.2 of the mmc 4.3 spec defines a formula for the timeout value.
// for now, just use a constant. Samsung KLMxGxxExM defines max write timeout as 600 ms
// FIXME: implement the formula, which is based on info from the CSD...
if( !MM4_WaitReady(600) )
{
Retval = WriteError;
pSDMMCP->State = FAULT;
return Retval;
}
pSDMMCP->State = READY;
return NoError;
}
/***********************************************************
* MM4_EraseBlocks()
* Erases required number of blocks at CardAddress
* input:
* none
* output:
* Blocks erased on erase group alignment
* returns:
* none
************************************************************/
UINT_T MM4_EraseBlocks(void)
{
UINT_T argument, Cmd;
UINT_T Retval = NoError;
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
// CMD 32/35 Define Erase Group Start Address
if (pSDMMCP->SD == XLLP_SD)
Cmd = XLLP_SD_CMD32;
else
Cmd = XLLP_MMC_CMD35;
// In byte mode addressing; all addresses need to be specified as byte offsets.
argument = pSDMMCP->Trans.CardAddress;
if (pSDMMCP->AccessMode == BYTE_ACCESS)
argument *= HARD512BLOCKLENGTH;
MM4_SendSetupCommand(pSDMMCP, Cmd, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R1 | MM4_48_RES);
// Wait for the command to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY)); // TBD add timeout Let the ISR run, we'll either get a fault or finish
if( pSDMMCP->State == FAULT ) // This state entered if ISR detected an error.
{
return SDMMC_ERASE_PARAM_ERROR;
}
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x100);
if (Retval != NoError)
{
pSDMMCP->State = FAULT;
return EraseError;
}
// CMD 33/36 Define Erase Group End Address
if (pSDMMCP->SD == XLLP_SD)
Cmd = XLLP_SD_CMD33;
else
Cmd = XLLP_MMC_CMD36;
// In byte mode addressing; all addresses need to be specified as byte offsets.
argument = pSDMMCP->Trans.CardAddress + pSDMMCP->Trans.TransWordSize*4;
if (pSDMMCP->AccessMode == BYTE_ACCESS)
argument *= HARD512BLOCKLENGTH;
MM4_SendSetupCommand(pSDMMCP, Cmd, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R1 | MM4_48_RES);
// Wait for the command to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY)); // TBD add timeout Let the ISR run, we'll either get a fault or finish
if( pSDMMCP->State == FAULT ) // This state entered if ISR detected an error.
{
return SDMMC_ERASE_PARAM_ERROR;
}
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x100);
if (Retval != NoError)
{
pSDMMCP->State = FAULT;
return EraseError;
}
// CMD 38
Cmd = XLLP_MMC_CMD38;
if (pSDMMCP->Trans.CardAddress % pSDMMCP->EraseSize ||
(pSDMMCP->Trans.TransWordSize*4) % pSDMMCP->EraseSize)
argument = 0x1; /* trim */
else
argument = 0x0; /* erase */
MM4_SendSetupCommand(pSDMMCP, Cmd, MM4_CMD_TYPE_NORMAL, argument, MM4_RT_R1 | MM4_RT_BUSY | MM4_48_RES_WITH_BUSY);
// Wait for the command to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY)); // TBD add timeout Let the ISR run, we'll either get a fault or finish
if( pSDMMCP->State == FAULT ) // This state entered if ISR detected an error.
{
return SDMMC_ERASE_PARAM_ERROR;
}
Retval = MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1B, 0x8000);
if (Retval != NoError)
{
Retval = EraseError;
pSDMMCP->State = FAULT;
}
else
{
pSDMMCP->State = READY;
}
return Retval;
}
/***********************************************************
* SDMMCWriteFifo
* Writes 2048 bytes (512 words) to the FIFO
* Input:
* P_SDMMC_Properties_T pSDMMCP - pointer to the SDMMC context structure
* Output:
* none
* Returns:
* none
*************************************************************/
void MM4_WriteFifo(P_SDMMC_Properties_T pSDMMCP)
{
int i, t = 0;
UINT_T Buffer =0x0;
P_MM4_SDMMC_CONTEXT_T pContext;
volatile UINT_T *pMMC_TX_Fifo;
// Assign our context value
pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
pMMC_TX_Fifo = (volatile UINT_T *)&(pContext->pMMC4Reg->mm4_dp);
t = pSDMMCP->Trans.WordIndex;
// Ignore Pre Bytes
for (i=0; (i < MM4FIFOWORDSIZE) && (t < pSDMMCP->Trans.StartDiscardWords); i++, t++)
*pMMC_TX_Fifo = Buffer;
// Write Requested Data
for (; ((i < MM4FIFOWORDSIZE) && (t < (pSDMMCP->Trans.TransWordSize-pSDMMCP->Trans.EndDiscardWords))); i++, t++)
*pMMC_TX_Fifo = ((UINT_T*) pSDMMCP->Trans.LocalAddr)[t];
// Ignore Trailing Bytes
for (; (i < MM4FIFOWORDSIZE) && (t < pSDMMCP->Trans.TransWordSize); i++, t++)
*pMMC_TX_Fifo = Buffer;
pSDMMCP->Trans.WordIndex = t;
}
/***********************************************************
* MM4_ReadFifo
* Reads the contents of the read fifo (512 words)
* Input:
* P_SDMMC_Properties_T pSDMMCP - pointer to the SDMMC context structure
* Output:
* buffer will contain the contents of the read fifo
* Returns:
* none
*************************************************************/
void MM4_ReadFifo(P_SDMMC_Properties_T pSDMMCP)
{
int i, t = 0;
UINT_T Buffer =0x0;
P_MM4_SDMMC_CONTEXT_T pContext;
volatile UINT_T *pMMC_RX_Fifo;
// Assign our context value
pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
pMMC_RX_Fifo = (volatile UINT_T *)&(pContext->pMMC4Reg->mm4_dp);
t = pSDMMCP->Trans.WordIndex;
// Ignore Pre Bytes
for (i=0; (i < MM4FIFOWORDSIZE) && (t < pSDMMCP->Trans.StartDiscardWords); i++, t++)
Buffer = *pMMC_RX_Fifo;
// Read Requested Data
for (; ((i < MM4FIFOWORDSIZE) && (t < (pSDMMCP->Trans.TransWordSize-pSDMMCP->Trans.EndDiscardWords))); i++, t++)
((UINT_T*) pSDMMCP->Trans.LocalAddr)[t] = *pMMC_RX_Fifo;
// Ignore Trailing Bytes
for (; (i < MM4FIFOWORDSIZE) && (t < pSDMMCP->Trans.TransWordSize); i++, t++)
Buffer = *pMMC_RX_Fifo;
pSDMMCP->Trans.WordIndex = t;
}
/****************************************************************
* MM4_SendStopCommand
* Issues a stop command for open ended read and write block operations
*
* Input:
* P_SDMMC_Properties_T pSDMMCP - pointer to the SDMMC context structure
* Output:
* none
* Returns:
* none
*****************************************************************/
void MM4_SendStopCommand(P_SDMMC_Properties_T pSDMMCP)
{
if(pSDMMCP->State == READ)
{
// Send a CMD 12 to stop transmissions.
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD12, MM4_CMD_TYPE_NORMAL, NULL, MM4_RT_R1 | MM4_48_RES); // fixme: r1 for read, r1b for write.
MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1, 0x100);
}
if(pSDMMCP->State == WRITE)
{
// Send a CMD 12 to stop transmissions.
MM4_SendSetupCommand(pSDMMCP, XLLP_MMC_CMD12, MM4_CMD_TYPE_NORMAL, NULL, MM4_RT_R1 | MM4_RT_BUSY | MM4_48_RES_WITH_BUSY); // fixme: r1 for read, r1b for write.
MM4_Interpret_Response(pSDMMCP, MMC_RESPONSE_R1B, MMC_RESPONSE_R1B_TIMEOUT);
}
}
/****************************************************************
* MM4_ISR
* Interrupt Service Routine for SDMMC controller
* Controls flow and catches faults asynchronously
* Input:
* P_SDMMC_Properties_T pSDMMCP
* Output:
* none
* Returns:
* none
*****************************************************************/
void MM4_ISR(P_SDMMC_Properties_T pSDMMCP)
{
UINT_T i;
UINT_T i_stat_copy; // Keep a copy of i stat register
UINT_T i_err_stat; // contains only error status bits.
UINT_T i_acmd12_err_stat;
P_MM4_I_STAT p_i_stat_copy; // Pointer to the copy.
UINT_T cmderror = 0;
UINT_T resptype;
P_MM4_SDMMC_CONTEXT_T pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext; // Assign our context value
UINT_T r1_resp_error_bits = 0xfdffc080; // that strange mask is all possible error bits in the card stat field.
// Assign required pointers to registers
VUINT_T *pmm4_resp0 = (VUINT_T *)(&pContext->pMMC4Reg->mm4_resp0);
VUINT_T *pmm4_acmd12_er = (VUINT_T *)(&pContext->pMMC4Reg->mm4_acmd12_er);
VUINT_T *pmm4_istat = (VUINT_T *)(&pContext->pMMC4Reg->mm4_i_stat);
VUINT_T *pmm4_sysaddr = (VUINT_T *)(&pContext->pMMC4Reg->mm4_sysaddr);
// Grab snapshot copies of i_stat
i_stat_copy = *(UINT_T *) (&pContext->pMMC4Reg->mm4_i_stat); // Grab a single copy.
p_i_stat_copy = (P_MM4_I_STAT)&i_stat_copy;
// command response will send out anyway, no matter command completed or not
for (i = 0; i < 4; i++)
pSDMMCP->CardReponse.pBuffer[i] = pmm4_resp0[i];
// Check for any error
if( p_i_stat_copy->cmdcomp )
{
// if we're in strict error checking mode, and
// if the completing command has an R1 or R1B status,
// look for any error bits in the card status field
if( pSDMMCP->StrictErrorCheck )
{
resptype = (pSDMMCP->Trans.RespType << 8) & MM4_RT_MASK; // convert to a value more convenient for comparisons...
if( resptype == MM4_RT_R1 )
{
cmderror = (*pmm4_resp0 & r1_resp_error_bits ); // that strange mask is all possible error bits.
}
}
else cmderror = 0; // don't examine error because not strict error checking mode or this is not an R1 type of command.
}
if (p_i_stat_copy->errint || cmderror )
{
// each error condition has its own handling/recovery requirements....
// move the error bits down to a low halfword so the defines in sd.h
// can be used. (they were based on 16 bit io access)
i_err_stat = i_stat_copy >> 16;
pSDMMCP->DeviceStatus = i_stat_copy;
// check & handle each error condition
// the following errors require an sd host controller reset:
if( ( i_err_stat & SD_ERROR_INT_STATUS_AXI_RESP_ERR ) ||
( i_err_stat & SD_ERROR_INT_STATUS_DATA_TIMEOUT_ERR ) ||
( i_err_stat & SD_ERROR_INT_STATUS_CMD_TIMEOUT_ERR ) ||
cmderror )
{
MMC4CMDSWReset(pContext); // this cleas the command inhibit flag in sd_present_state_1.
MMC4DataSWReset(pContext); // this clears the data inhibit flag and stops mclk.
}
// acmd 12 error requires examining a separate error status register:
else if( i_err_stat & SD_ERROR_INT_STATUS_AUTO_CMD12_ERR )
{
i_acmd12_err_stat = *pmm4_acmd12_er;
// any acmd12 error requires an sd host controller reset
// because there's no way to know if the command was processed.
MMC4CMDSWReset(pContext); // this cleas the command inhibit flag in sd_present_state_1.
MMC4DataSWReset(pContext); // this clears the data inhibit flag and stops mclk.
// clear the acmd12 error bits.
*pmm4_acmd12_er = i_acmd12_err_stat;
}
// clear the general error status bits
*pmm4_istat = i_stat_copy;
i = *pmm4_istat; // Must make a dummy read to allow interrupts to clear.
// set a flag so the caller knows this request failed.
pSDMMCP->State = FAULT;
// done with handling an error condition.
// nothing more to do.
return;
}
// is this an sdma interrupt event?
if(*pmm4_istat & (1u<<3)) // bit 3 is dmaint
{
// the transfer halted because the boundary specified in ... was reached.
// rewriting the sysaddr with the next address allows the transfer to resume.
// fortunately the sysaddr register itself contains the next address.
// so, just re-write the sysaddr register with its own current contents.
//Coverity - 10008: Self Assignment error - No Effect.
//This is OK. That's the way the hardware works!
if (pSDMMCP->SDMMC_DMA_Mode == SDMA)
{
i = *pmm4_sysaddr;
*pmm4_sysaddr = i; // sysaddr points to next addr to write.
}
// for ADMA2, no ISR for per DMA transfer, when all data transfer complete, it will generate XFER_COMP ISR
if (pSDMMCP->SDMMC_DMA_Mode == ADMA2)
pSDMMCP->State = READY;
*pmm4_istat = (1u<<3); // bit 3 is dmaint
i = *pmm4_istat; // read-back to ensure write completes
}
// fall through the rest of the isr.
// end of dmaint handling.
// Has the Command completed? If so read the response register
if (p_i_stat_copy->cmdcomp)
{
// Indicate that the response has been read
pSDMMCP->CardReponse.CommandComplete = 1;
}
if (p_i_stat_copy->xfrcomp)
{
// Indicate that the response has been read
pSDMMCP->CardReponse.TransferComplete = 1;
}
// Are we DMA enabled?
if((pSDMMCP->SDMMC_DMA_Mode))
{
if (p_i_stat_copy->xfrcomp)
pSDMMCP->State = READY;
}
// Clear the interrupts
*pmm4_istat = i_stat_copy; // Clear the interrupt source.
i = *pmm4_istat; // Must make a dummy read to allow interrupts to clear.
if(pSDMMCP->SDMMC_DMA_Mode == NODMA)
{
// Handle State based interrupts XFRCOMP, BUFRDRDY, BUFWRRDY
switch (pSDMMCP->State)
{
case WRITE:
{
if (p_i_stat_copy->bufwrrdy)
MM4_WriteFifo(pSDMMCP);
// Are we done sending all of data?
if (pSDMMCP->Trans.TransWordSize == pSDMMCP->Trans.WordIndex)
pSDMMCP->State = DATATRAN;
break;
}
case READ:
{
if (p_i_stat_copy->bufrdrdy)
MM4_ReadFifo(pSDMMCP);
// Are we done sending all of data?
if (pSDMMCP->Trans.TransWordSize == pSDMMCP->Trans.WordIndex)
pSDMMCP->State = DATATRAN;
break;
}
case DATATRAN:
{
// Wait for Transfer Complete Signal
if (p_i_stat_copy->xfrcomp)
pSDMMCP->State = READY;
break;
}
default:
break;
}
}
return;
}
/***************************************************************
* MM4_MMCReadEXTCSD
* Reads in 512 bytes of Extended CSD
* Input: Pointer to 512 byte buffer
* Output:
* Returns: NoError, ReadError or NotSupportedError
*
*****************************************************************/
UINT_T MM4_MMCReadEXTCSD (UINT_T *pBuffer)
{
UINT_T Retval;
P_MM4_SDMMC_CONTEXT_T pContext;
P_MM4_CNTL1 pMM4_CNTL1;
P_MM4_BLK_CNTL pMM4_BLK_CNTL;
ADMA_DESCRIPTOR admaDesc;
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
if (pSDMMCP->SD == XLLP_SD)
return NoError; // This is an MMC command only!
// Assign our context value
pContext = (P_MM4_SDMMC_CONTEXT_T) pSDMMCP->pContext;
pMM4_CNTL1 = (P_MM4_CNTL1) &pContext->pMMC4Reg->mm4_cntl1;
// Set up State
pSDMMCP->State = READ;
// This requires a transfer over the data lines.
pMM4_BLK_CNTL = (P_MM4_BLK_CNTL) &pContext->pMMC4Reg->mm4_blk_cntl;
pMM4_BLK_CNTL->blk_cnt = 0;
if (pSDMMCP->SDMMC_DMA_Mode == SDMA)
{
pMM4_BLK_CNTL->dma_bufsz = MM4_512_HOST_DMA_BDRY;
pContext->pMMC4Reg->mm4_sysaddr = (UINT_T) pBuffer;
} //MMC_SDMA_MODE
else if(pSDMMCP->SDMMC_DMA_Mode == ADMA2)
{
MM4_SetupADMADesc(&admaDesc,
VALID_DESC,
LAST_DESC,
NO_DMA_INT,
TRANSFER,
1,//EXT_CSD is sent as a block of data
(UINT_T)pBuffer);
pMM4_CNTL1->dma_sel = 2; //ADMA2
//Set the ADMA register to the address of descriptor table
pContext->pMMC4Reg->mm4_adma_system_address1 = (UINT_T)&admaDesc;
}
pSDMMCP->Trans.StartDiscardWords = 0;
pSDMMCP->Trans.EndDiscardWords = 0; // We'll take all 512 bytes
pSDMMCP->Trans.TransWordSize = pSDMMCP->ReadBlockSize / 4; // Total Transfer Size including pre and post bytes
pSDMMCP->Trans.LocalAddr = (UINT_T) pBuffer;
pSDMMCP->Trans.WordIndex = 0; // Stores Index of Current write position
MM4_SendDataCommandNoAuto12(pSDMMCP, XLLP_MMC_CMD8, NO_ARGUMENT, MM4_SINGLE_BLOCK_TRAN, MM4_CARD_TO_HOST_DATA, MM4_RT_R1 | MM4_48_RES);
// Wait for the Write to Complete
while ((pSDMMCP->State != FAULT) && (pSDMMCP->State != READY));
if( pSDMMCP->State == FAULT ) // This state entered if ISR detected an error.
{
return SDMMCReadError;
}
//send CMD13 to check the status of the card
Retval |= MM4_CheckCardStatus(pSDMMCP, 0x900, R1_LOCKEDCARDMASK); // Make sure card is transfer mode
pSDMMCP->State = READY;
if (Retval != NoError)
return SDMMC_SWITCH_ERROR;
return NoError;
}
/***********************************************************
* MM4_SetupADMADesc()
* Function to setup ADMA descriptor i.e; fills the descriptor contents
* input:
* P_ADMA_DESCRIPTOR -> Address of the ADMA descriptor
* DESC_VAL_INVAL -> Indicates whether the descriptor is valid or not
* END_DESC -> Indicates if the current descriptor is the last of the chain
* ATTRIBUTES -> Indicates the type of descriptor whether it is transfer type or link type
* NumBlocks -> Number of blocks that are transferred by this descriptor
* Address -> Destination address if the descriptor is of type transfer or the descriptor
* it is linked to if it is of type link
* output:
* Fills the input descriptor
* returns:
* none
************************************************************/
void MM4_SetupADMADesc(P_ADMA_DESCRIPTOR pAdmaDesc,
DESC_VAL_INVAL ValidDesc,
END_DESC EndDesc,
DMA_INT_EN DmaInterrupt,
ATTRIBUTES Attributes,
UINT_T NumBlocks,
UINT_T Address)
{
// Initialize Flash Properties
P_SDMMC_Properties_T pSDMMCP = GetSDMMCProperties();
memset(pAdmaDesc,0, sizeof(ADMA_DESCRIPTOR));
pAdmaDesc->Address = Address;
pAdmaDesc->Length = NumBlocks * pSDMMCP->ReadBlockSize;
pAdmaDesc->Rsvd = 0;
pAdmaDesc->attr = Attributes;
pAdmaDesc->Rsvd0 = 0;
pAdmaDesc->dma_int_en = DmaInterrupt;
pAdmaDesc->end_desc = EndDesc;
pAdmaDesc->desc_val_inval = ValidDesc;
return;
}