//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
//  Copyright (C) 2007 Freescale Semiconductor, Inc. All Rights Reserved.
//  THIS SOURCE CODE, AND ITS USE AND DISTRIBUTION, IS SUBJECT TO THE TERMS
//  AND CONDITIONS OF THE APPLICABLE LICENSE AGREEMENT
//
//------------------------------------------------------------------------------
#include "sdmmc.h"

//-----------------------------------------------------------------------------
// External Functions


//-----------------------------------------------------------------------------
// External Variables
extern SDCARD_CARD_REGISTERS sdCardReg;
extern SDH_HARDWARE_CONTEXT	SDController;
extern USHORT Card_rca;                   /* Relative Card Address */
extern BOOL bHighDensityCard;
extern UINT32 mmcSpecVersion;

//-----------------------------------------------------------------------------
// Defines
#define MMC_SPEC_VER_MASK 0x3C
#define MMC_SPEC_VER_SHIFT 2

#define MMC_R1_SWITCH_ERROR_MASK 0x80
#define SWITCH_ERROR_SHIFT 7
#define BUS_SIZE_SHIFT 2
#define BUS_WIDTH 0x03b70000

#define MMC_READ_BL_LEN 0xF0
#define MMC_READ_BL_LEN_SHIFT 4
#define READ_BL_LEN_512		9

//-----------------------------------------------------------------------------
// Types


//-----------------------------------------------------------------------------
// Global Variables

//-----------------------------------------------------------------------------
// Local Variables


//-----------------------------------------------------------------------------
// Local Functions
static UINT32 MMC_GetSpecVer(void);
static UINT32 MMC_SetBusWidth (UINT32,UINT32);
static UINT32 MMC_VoltageValidation (void);

//------------------------------------------------------------------------------
//
// Function: MMC_Init
//
//	This function will initialize the MMC memory card.
//
// Parameters:
//		None
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
UINT32 MMC_Init(void)
{
    UINT32 status = SDHC_STATUS_FAILURE;
    UINT32 spec_version=0;
	
	OALMSG(OAL_FUNC, (_T("+ MMC_Init\r\n")));
	
	if (MMC_VoltageValidation()) // Not a MMC card
		return status;

    /* Get CID number of MMC Card */
    if(!SDMMC_get_cid())
    {
        /* Set RCA of the MMC Card */
        if (!SDMMC_send_rca(TRUE))
        {
			if (!SDMMC_get_csd())
			{
				/* Get Spec version supported by the card */
				spec_version = MMC_GetSpecVer();
			}

			/*Enable operating frequency */
			SetRate (MMC_FULL_SPEED_RATE);

			/*Put MMC in Transfer State */
			if(!SDMMC_set_data_transfer_mode ())
			{
				if (!SDMMC_set_blocklen (SDHC_BLK_LEN))
				{
					if (!MMC_SetBusWidth (spec_version, MMC_SPEC_VER_4_0))
					{
						SDController.BusWidthSetting = 2; // 4 Bit Mode
						SDController.Units_in_fifo = 4*4;
						SDController.Bytes_in_fifo = 64; // 64 bytes
						OALMSG(OAL_INFO, (_T("MMC: Switched to 4 bit mode\r\n")));
					}
					/* Set status variable as SUCESS */
					status = SDHC_STATUS_PASS; 
				}
			 }
         }
    }
 
	OALMSG(OAL_FUNC, (_T("- MMC_Init\r\n")));

    return status;
}

//------------------------------------------------------------------------------
//
// Function: MMC_VoltageValidation
//
//	This function will validate the operating voltage range of MMCMemory card.
//
// Parameters:
//		None
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
static UINT32 MMC_VoltageValidation (void)
{
    SD_BUS_REQUEST sdRequest;
    PSD_COMMAND_RESPONSE response = &sdRequest.CommandResponse;
    UINT32 ocr_val=0;
    UINT32 voltage_validation = SDHC_STATUS_FAILURE;
	UINT32 StartTime = OEMEthGetSecs();
	
	OALMSG(OAL_FUNC, (_T("+ MMC_VoltageValidation\r\n")));

    ocr_val = (UINT32) ((MMC_OCR_VALUE) & 0xFFFFFFFF);
	
    while((OEMEthGetSecs() - StartTime < SDHC_DELAY_TIMEOUT) && (voltage_validation != SDHC_STATUS_PASS)) 
    {  	
        /* Configure CMD1 for MMC card */
        /* Argument will be expected OCR value */
        SDCard_command_config(&sdRequest, SD_CMD_MMC_SEND_OPCOND, ocr_val, SD_COMMAND, ResponseR3);
                 
        /* Issue CMD1 to MMC card to determine OCR value */
        if(SDHCSendCmdWaitResp(&sdRequest) != SDHC_STATUS_FAILURE)
        {
            /* Read Response from CMDRSP0 Register */
            response->ResponseType = ResponseR3;
            SDHCReadResponse(response);
			memcpy((&ocr_val), &response->ResponseBuffer[1], sizeof(UINT32));	
			/* Mask OCR value against 0x00FF8000 and compare with response*/
			if ((ocr_val & MMC_OCR_VALUE_MASK) == MMC_OCR_VALUE_MASK)
			{
				/* Check if card busy bit is cleared or not */
				if(ocr_val & CARD_BUSY_BIT)
				{
					if((ocr_val & MMC_OCR_HC_RES) == MMC_OCR_HC_RES)
					{  
						bHighDensityCard = TRUE; 
						OALMSG(OAL_INFO, (_T("MMC High Density card\r\n")));
					}
					else if((ocr_val & MMC_OCR_LC_RES) == MMC_OCR_LC_RES)
					{
						bHighDensityCard = FALSE; 
						OALMSG(OAL_INFO, (_T("MMC Low Density card\r\n")));
					}
					voltage_validation = SDHC_STATUS_PASS;
				}
             }
        }
    }
	
	OALMSG(OAL_FUNC, (_T("- MMC_VoltageValidation\r\n")));

    return voltage_validation;
}

//------------------------------------------------------------------------------
//
// Function: MMC_GetSpecVer
//
//	This function will return the MMC Spec version.
//
// Parameters:
//		None
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
static UINT32 MMC_GetSpecVer (void)
{
	mmcSpecVersion = ((sdCardReg.CSD[15] & MMC_SPEC_VER_MASK) >> MMC_SPEC_VER_SHIFT);
	OALMSG(OAL_VERBOSE, (_T("+ MMC Spec Version %x\r\n"), mmcSpecVersion));
	
    return mmcSpecVersion;		
}

//------------------------------------------------------------------------------
//
// Function: MMC_SetBusWidth
//
//	This function will set bus width of MMC Card.
//
// Parameters:
//		spec_version[in] - MMC Specification version
//		bus_width[in] - Bus Width to be set
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
static UINT32 MMC_SetBusWidth (UINT32 spec_version,UINT32 bus_width)
{
    SD_BUS_REQUEST sdRequest;
    PSD_COMMAND_RESPONSE response = &sdRequest.CommandResponse;
    UINT32 switch_error;
    UINT32 bus_size;
    UINT32 set_bus_width_status = SDHC_STATUS_FAILURE;
#if 0 // Remove-W4: Warning C4189 workaround
    UINT32 card_address = (Card_rca << RCA_SHIFT);
#endif

	OALMSG(OAL_FUNC, (_T("+ MMC_SetBusWidth\r\n")));
	
    if (((bus_width == 4 || bus_width == 8) && (spec_version >= MMC_SPEC_VER_4_0 )))
    {
        bus_size = (bus_width >> BUS_SIZE_SHIFT);
		
        /* Configure CMD6 to write to EXT_CSD register for BUS_WIDTH */
		SDCard_command_config(&sdRequest,SD_ACMD_SET_BUS_WIDTH,
							((BUS_WIDTH)|(bus_size<<8)),SD_COMMAND, ResponseR1b);		
		
        /* Sending the card from stand-by to transfer state
        */
        if(!SDHCSendCmdWaitResp(&sdRequest))
        {
			if (!SDMMC_R1b_busy_wait (response))
			{
				switch_error = 
					((UINT32) (response->ResponseBuffer[0] & MMC_R1_SWITCH_ERROR_MASK) >> SWITCH_ERROR_SHIFT);
				OALMSG(OAL_VERBOSE, (_T("+ switch error %x\r\n"), switch_error));
				if(!switch_error)
					set_bus_width_status = SDHC_STATUS_PASS;
			}
        }
    }
	
	OALMSG(OAL_FUNC, (_T("- MMC_SetBusWidth\r\n")));

    return set_bus_width_status;
}




