| /****************************************************************************** |
| * |
| * (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; |
| } |