//
// 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
#if __cplusplus
extern "C" {
#endif

extern PCSP_IOMUX_REGS g_pIOMUX;
extern PCSP_CCM_REGS g_pCCM;
extern BSP_ARGS *g_pBSPArgs;
extern PCSP_PBC_REGS g_pPBC;

#ifdef __cplusplus
}
#endif

//-----------------------------------------------------------------------------
// Defines
#define SDHC_INTERRUPTS_MASK	0x1F

//-----------------------------------------------------------------------------
// Types
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Global Variables
SDH_HARDWARE_CONTEXT	SDController;
PCSP_SDHC_REG			g_pSDMMCReg; // SD/MMC controller registers
PCSP_GPIO_REGS			g_pGPIO[1];

//-----------------------------------------------------------------------------
// Local Variables


//-----------------------------------------------------------------------------
// Local Functions

//-----------------------------------------------------------------------------
//
//  Function: SDConfigPins
//
//  This function configures the pins and enables the SD interface.
//
//  Parameters:
//		None.
//
//  Returns:
//		None.
//
//-----------------------------------------------------------------------------
void SDConfigPins (void)
{
	g_pSDMMCReg = (PCSP_SDHC_REG) OALPAtoUA(CSP_BASE_REG_PA_SDHC1);
	
	g_pGPIO[0] = (PCSP_GPIO_REGS)OALPAtoUA(CSP_BASE_REG_PA_GPIO1);
	
    OAL_IOMUX_SET_MUX(g_pIOMUX, DDK_IOMUX_PIN_SD1_CMD, DDK_IOMUX_OUT_FUNC, DDK_IOMUX_IN_FUNC);
    OAL_IOMUX_SET_MUX(g_pIOMUX, DDK_IOMUX_PIN_SD1_CLK, DDK_IOMUX_OUT_FUNC, DDK_IOMUX_IN_FUNC);
    OAL_IOMUX_SET_MUX(g_pIOMUX, DDK_IOMUX_PIN_SD1_DATA0, DDK_IOMUX_OUT_FUNC, DDK_IOMUX_IN_FUNC);
    OAL_IOMUX_SET_MUX(g_pIOMUX, DDK_IOMUX_PIN_SD1_DATA1, DDK_IOMUX_OUT_FUNC, DDK_IOMUX_IN_FUNC);
	OAL_IOMUX_SET_MUX(g_pIOMUX, DDK_IOMUX_PIN_SD1_DATA2, DDK_IOMUX_OUT_FUNC, DDK_IOMUX_IN_FUNC);
	OAL_IOMUX_SET_MUX(g_pIOMUX, DDK_IOMUX_PIN_SD1_DATA3, DDK_IOMUX_OUT_FUNC, DDK_IOMUX_IN_FUNC);

	OAL_IOMUX_SET_MUX(g_pIOMUX, DDK_IOMUX_PIN_GPIO1_1, DDK_IOMUX_OUT_GPIO, DDK_IOMUX_IN_GPIO);
	OAL_IOMUX_SET_PAD(g_pIOMUX, DDK_IOMUX_PAD_GPIO1_1, DDK_IOMUX_PAD_SLEW_SLOW,
						DDK_IOMUX_PAD_DRIVE_NORMAL, DDK_IOMUX_PAD_MODE_CMOS,
						DDK_IOMUX_PAD_TRIG_SCHMITT, DDK_IOMUX_PAD_PULL_UP_100K);
	
	// Set DDK_GPIO_PORT1_1 Direction as Input
	OUTREG32 (&g_pGPIO[0]->GDIR, (INREG32(&g_pGPIO[0]->GDIR) & 0xFFFFFFFD));
	//Set DDK_GPIO_PORT1_1 Interrupt High Level triggered
	OUTREG32 (&g_pGPIO[0]->ICR1, ((INREG32(&g_pGPIO[0]->GDIR) & 0xFFFFFFF3) | 0x4));
	//Clear DDK_GPIO_PORT1_1 Interrupt Pin
	OUTREG32(&g_pGPIO[0]->ISR, (1 << 1));
    // Enable card detect interface in PBC (BCTRL3[10] - CARD1_SEL)
    OUTREG16(&g_pPBC->BCTRL3_CLEAR, (1<<PBC_BCTRL3_CARD1_SEL));

}

//-----------------------------------------------------------------------------
//
//  Function: SDInterface_Init
//
//  This function configures and resets the SD controller.
//
//  Parameters:
//		None.
//
//  Returns:
//		TRUE for success/FALSE for failure.
//
//-----------------------------------------------------------------------------
BOOL SDInterface_Init (void)
{
	SDController.IsMMC = FALSE;
	SDController.BusWidthSetting = 0;
    SDController.Units_in_fifo  = 4;    
    SDController.Bytes_in_fifo  = 16 ;  //16bytes
	SDController.SendInitClocks = TRUE;

	if (!SDHC_IsCardPresent ())
	{
		OALMSG(OAL_WARN, (_T("WARNING: SD/MMC Card not present!!\r\n")));
		return FALSE; 
	}

	// Software Reset
    OUTREG32(&g_pSDMMCReg->STR_STP_CLK, CSP_BITFVAL(SDHC_SSCR_RESET, 1));
    OUTREG32(&g_pSDMMCReg->STR_STP_CLK, 
             CSP_BITFVAL(SDHC_SSCR_RESET, 1) | CSP_BITFVAL(SDHC_SSCR_STOP, 1));
    for (UINT32 ii = 0; ii < 8; ii++)
    {
        OUTREG32(&g_pSDMMCReg->STR_STP_CLK, CSP_BITFVAL(SDHC_SSCR_STOP, 1));
    }

	// Initialize registers
	OUTREG32(&g_pSDMMCReg->STATUS, 0xFFFFFFFF);
    OUTREG32(&g_pSDMMCReg->CMD_DAT_CONT, 0); 
    OUTREG32(&g_pSDMMCReg->RESPONSE_TO, 0xFF); //max time-out value
    OUTREG32(&g_pSDMMCReg->READ_TO, 0xFFFF);   //max time-out value

    // Mask all SDHC interrupts
    OUTREG32(&g_pSDMMCReg->INT_CNTR, 0);

	SetRate (SD_DEFAULT_CARD_ID_CLOCK_RATE); // Identification Frequency

	return TRUE;
}

//-----------------------------------------------------------------------------
//
//  Function: SDHC_IsCardPresent
//
//  This function detects the presence of SD/MMC card.
//
//  Parameters:
//		None.
//
//  Returns:
//		TRUE for success/FALSE for failure.
//
//-----------------------------------------------------------------------------
BOOL SDHC_IsCardPresent (void)
{
	BOOL bCardPresent = FALSE;

	if (INREG32(&g_pGPIO[0]->PSR) & 0x2)
		bCardPresent = TRUE;
	
	return bCardPresent;
}

//------------------------------------------------------------------------------
//
// Function: SetRate
//
// Sets the desired SD/MMC clock frequency. Note: The closest frequecy
//            to the desired setting is chosen.
//
// Parameters:
//          dwRate[in] - desired clock rate in Hz
//
// Returns:
//		None
//
//------------------------------------------------------------------------------
void SetRate(DWORD dwRate)
{
    const ULONG ulMaxDivisior = 15;
    const ULONG ulMaxPrescaler = 0x0800;
    ULONG ulClockRate;
    ULONG ulCalRate;
    ULONG clk_div = 0, prescaler = 0, ulErr;
    ULONG ulCurErr, i, j;
    ULONG ulMaxClockRate; // = g_pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_PER];//Get BSP specific clock frequency
 
	OALMSG(OAL_FUNC, (_T("+ SetRate\r\n")));

	ulMaxClockRate = g_pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_PER];//Get BSP specific clock frequency

    ulClockRate = dwRate;

    if (ulClockRate <= SD_DEFAULT_CARD_ID_CLOCK_RATE)
        ulClockRate = SD_DEFAULT_CARD_ID_CLOCK_RATE;

    if (ulClockRate >= BSP_SDHC_MAX_SDBUS_CLK_SUPPORTED)
    {
        // If the requested bus clock cannot be supported
        ulClockRate = BSP_SDHC_MAX_SDBUS_CLK_SUPPORTED;
    }

    ulErr = 0xFFFFFFFF;
    // Get best clk_div and prescaler
    for (i = 1; i <= ulMaxDivisior; i++)
    {
        for (j = 0; j <= ulMaxPrescaler;)
        {
            if (j == 0)
                ulCalRate = (ulMaxClockRate / (i + 1));
            else
                ulCalRate = (ulMaxClockRate / ((i + 1) * j * 2));

            ulCurErr = abs((ulCalRate - ulClockRate));
            if (ulCalRate <= ulClockRate)
            {
                if (ulCurErr < ulErr)
                {
                    clk_div = i;
                    prescaler = j;
                    ulErr = ulCurErr;
                }
            }

            if (j == 0)
                j = 1;
            else
                j <<= 1;
        }
    }

    // set the actual clock rate 
    if (prescaler == 0)
        SDController.dwClockRate = ulMaxClockRate / (clk_div + 1);
    else
        SDController.dwClockRate = ulMaxClockRate / ((clk_div + 1) * (prescaler * 2));

    // set the new clock rate if requested
	OALMSG(OAL_VERBOSE, (_T("SDSetRate - Requested Rate: %d, Setting clock rate to %d \r\n"), 
                                 dwRate, SDController.dwClockRate));
    OALMSG(OAL_VERBOSE, (_T("SDSetRate - clk_div %d, prescaler %d \r\n"), clk_div, prescaler));

    INSREG32BF(&g_pSDMMCReg->CLK_RATE, SDHC_CRATE_DIV, clk_div);
    INSREG32BF(&g_pSDMMCReg->CLK_RATE, SDHC_CRATE_PRES, prescaler);

	OALMSG(OAL_FUNC, (_T("- SetRate\r\n")));
}

//------------------------------------------------------------------------------
//
// Function: SDHCCmdConfig
//
// Configure SDHC registers for sending a command to MMC/SD.
//
// Parameters:
//          pReq[in] - structure containing necesary fields to issue a command
//
// Returns:
//		None
//
//------------------------------------------------------------------------------
static void SDHCCmdConfig(PSD_BUS_REQUEST pReq)                                                           
{
	DWORD cmdatRegister = 0;

    /* Write command index */
    OUTREG32 (&g_pSDMMCReg->CMD, pReq->CommandCode);

    /* Write command arg */ 
    OUTREG32 (&g_pSDMMCReg->ARG, pReq->CommandArgument);

	//Set the response type
    switch (pReq->CommandResponse.ResponseType)
    {
    case NoResponse:
        CSP_BITFINS(cmdatRegister, SDHC_CDC_FORMAT, 0);
        break;      
        //ARM11 spec does not have busy bit register set. 
        //Need to check busy bit Hw implementation and handling.This affects commands with 
        //response R1b and for SDIOAbort transfers.
    case ResponseR1b:
    case ResponseR1:
    case ResponseR5:
    case ResponseR6:
        CSP_BITFINS(cmdatRegister, SDHC_CDC_FORMAT, 1);
        break;  
    case ResponseR2:    
        CSP_BITFINS(cmdatRegister, SDHC_CDC_FORMAT, 2);
        break;
    case ResponseR3:
    case ResponseR4:    
        // R4 is really same as an R3 response on an MMC controller (non-CRC)
        // Note: sdbus send for R4 although specs say R3
        CSP_BITFINS(cmdatRegister, SDHC_CDC_FORMAT, 3);
        break;
    }

	    // check for Command Only
    if ((SD_COMMAND != pReq->TransferClass))
    {
        // its a command with a data phase
        CSP_BITFINS(cmdatRegister, SDHC_CDC_DE, 1);
		if (SD_WRITE == pReq->TransferClass)
		{
			CSP_BITFINS(cmdatRegister, SDHC_CDC_WR, 1);
		}
    }

	if (SDController.SendInitClocks)
	{
        CSP_BITFINS(cmdatRegister, SDHC_CDC_INIT, 1);
		SDController.SendInitClocks = FALSE;
	}

    // check to see if we need to enable wide bus (4 bit) data transfer mode
    CSP_BITFINS(cmdatRegister, SDHC_CDC_BW, SDController.BusWidthSetting);

	    // write the CMDAT register
    OUTREG32(&g_pSDMMCReg->CMD_DAT_CONT, cmdatRegister);
    
    return; 
}

//------------------------------------------------------------------------------
//
// Function: SetClock
//
// Enable/disable MMC clock
//
// Parameters:
//          Start[in] - True to enable/False to disable the clock
//
// Returns:
//
//------------------------------------------------------------------------------
static void SetClock(BOOL Start)
{
	OALMSG(OAL_FUNC, (_T("+ SetClock\r\n")));
    if (Start)
    {
        do 
        {
            OUTREG32(&g_pSDMMCReg->STR_STP_CLK, 0x2) ;
        } while (!EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_CBCR));      
    } 
	else
    {
        do
        {
            OUTREG32(&g_pSDMMCReg->STR_STP_CLK, 0x1) ;
        } while (EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_CBCR));
    }
	OALMSG(OAL_FUNC, (_T("- SetClock\r\n")));
}

//------------------------------------------------------------------------------
//
// Function: SDHCWaitEndCmdRespIntr
//
//	Wait a END_CMD_RESP interrupt by polling status register. The fields 
//	RESP_CRC_ERR (Status[5]) and TIME_OUT_RESP(STATUS[1]) are checked 
//	to determine if an error has occurred.
//
// Parameters:
//		None
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
static UINT32 SDHCWaitEndCmdRespIntr ()
{
	UINT32 sdhc_status = SDHC_STATUS_FAILURE;

	OALMSG(OAL_FUNC, (_T("+ SDHCWaitEndCmdRespIntr\r\n")));

    while (!EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_ECR));

	/* Check whether the interrupt is an END_CMD_RESP  
     * or a time out or a CRC error 
     */ 		
     if((EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_ECR)) &&
		!(EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_TORESP)) &&
		!(EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_RSPCERR)) )
     {
          sdhc_status = SDHC_STATUS_PASS;
     }

	 OALMSG(OAL_VERBOSE, (_T("SDHCWaitEndCmdRespIntr Status: %x\r\n"), (INREG32(&g_pSDMMCReg->STATUS))));

	 OALMSG(OAL_FUNC, (_T("- SDHCWaitEndCmdRespIntr\r\n")));
     
     return sdhc_status;
}

//------------------------------------------------------------------------------
//
// Function: SetBlockLen
//
//	Set SDHC_BLK_LEN and SDHC_NOB registers.
//
// Parameters:
//		blkLen[in] - Number of Bytes for a block
//		nob[in] - Number of Blocks
//
// Returns:
//		None
//
//------------------------------------------------------------------------------
void SetBlockLen (UINT32 blkLen, UINT32 nob)
{
    OUTREG32 (&(g_pSDMMCReg->BLK_LEN), blkLen);
    OUTREG32 (&g_pSDMMCReg->NOB, nob);
}

//------------------------------------------------------------------------------
//
// Function: SDHCCheckDataStatus
//
//	If WRITE_OP_DONE/READ_OP_DONE occured check WR_CRC_ERR_CODE/RD_CRC_ERR_CODE 
//	and WRITE_CRC_ERR/READ_CRC_ERR to determine if an error occured
//
// Parameters:
//		op_done_mask[in] - WRITE_OP_DONE/READ_OP_DONE value
//		crc_err_code_mask[in] - WR_CRC_ERR_CODE/RD_CRC_ERR_CODE value
//		crc_err_mask[in] - WRITE_CRC_ERR/READ_CRC_ERR value
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
static UINT32 SDHCCheckDataStatus (UINT32 op_done_mask, UINT32 crc_err_code_mask, UINT32 crc_err_mask)
{
    UINT32 sdhc_status = INREG32 (&g_pSDMMCReg->STATUS);

    /* Check whether the interrupt is an OP_DONE  
     * or a data time out or a CRC error  */
     if((sdhc_status & op_done_mask) &&
       !(sdhc_status & crc_err_code_mask) &&
       !(sdhc_status & crc_err_mask))
     {
         sdhc_status = SDHC_STATUS_PASS;
     }
     else
     {
         sdhc_status = SDHC_STATUS_FAILURE;
     }
     return sdhc_status;
}

//------------------------------------------------------------------------------
//
// Function: SDHCSendCmdWaitResp
//
//	Execute a command and wait for the response
//
// Parameters:
//		pReq[in] - SD structure
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
UINT32 SDHCSendCmdWaitResp (PSD_BUS_REQUEST pReq)
{
	UINT32 sdhc_status = SDHC_STATUS_FAILURE;

    /* Start the clock */
    SetClock(TRUE);
 
    /* Clear Interrupt status Register */
    OUTREG32 (&g_pSDMMCReg->STATUS,0xFFFFFFFF);

    /* Enable interrupts */
    OUTREG32 (&g_pSDMMCReg->INT_CNTR, SDHC_INTERRUPTS_MASK);

    /* Configure Command */
    SDHCCmdConfig(pReq);

    /* Wait for interrupt end_command_resp */
    sdhc_status = SDHCWaitEndCmdRespIntr();

    /* Mask all interrupts */
    OUTREG32 (&g_pSDMMCReg->INT_CNTR, 0);

	/* Stop MMC clock */
    SetClock(FALSE);

    /* Check if an error occured */
    return sdhc_status;

}

//------------------------------------------------------------------------------
//
// Function: SDHCReadResponse
//
//	Read the response returned by the card after a command 
//
// Parameters:
//		pResp[in] - structure to fill the response
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
UINT32 SDHCReadResponse (PSD_COMMAND_RESPONSE pResp)
{
    UINT32 status = SDHC_STATUS_FAILURE;

	OALMSG(OAL_FUNC, (_T("+ SDHCReadResponse\r\n")));

	/* Start the clock */
    SetClock(TRUE);
 
	if (NoResponse != pResp->ResponseType)
    {
        LONG     ii;             // loop variable  
        LONG     startingOffset;     // starting offset in response buffer
        USHORT   responseBuffer[SDH_RESPONSE_FIFO_DEPTH]; // response buffer

        if (ResponseR2 == pResp->ResponseType)
        {
            // 8 words - 128 bits
            startingOffset = SDH_RESPONSE_FIFO_DEPTH - 1;
        }
        else
        {
            // 3 WORDS - 48 bits
            startingOffset = 2;
        }

        // read in the response words from the response fifo.
        for (ii = startingOffset; ii >= 0; ii--)
        {
            // read from the fifo
            responseBuffer[ii] =INREG16(&g_pSDMMCReg->RES_FIFO);
            OALMSG(OAL_VERBOSE, (TEXT("responseBuffer[%d]=0x%x\r\n"),ii,responseBuffer[ii]));       
        }

        memcpy(pResp->ResponseBuffer, responseBuffer, (sizeof(USHORT)) * (startingOffset + 1));
		status = SDHC_STATUS_PASS;
    }

	/* Clear w1c bits from STATUS register */
    OUTREG32 (&g_pSDMMCReg->STATUS, INREG32 (&g_pSDMMCReg->STATUS));	   

	/* Stop MMC clock */
    SetClock(FALSE);

	OALMSG(OAL_FUNC, (_T("- SDHCReadResponse\r\n")));
    
    return status;

}

//------------------------------------------------------------------------------
//
// Function: SDHCDataRead
//
//	Read the data from the card
//
// Parameters:
//		dest_ptr[out] - Destination memory address
//		blk_len[in] - Block Length
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
UINT32 SDHCDataRead (UINT32* dest_ptr, UINT32 blk_len) 
{
    DWORD dwFifoSizeInWords		= SDController.Units_in_fifo;
    DWORD dwFifoFillCount		= (blk_len / SDController.Bytes_in_fifo);
    UINT32 status				= SDHC_STATUS_FAILURE;

	OALMSG(OAL_FUNC, (_T("+ SDHCDataRead\r\n")));

	/* Clear Interrupt status Register */
    OUTREG32 (&g_pSDMMCReg->STATUS,0xFFFFFFFF);

	/* Enable interrupts */
    OUTREG32 (&g_pSDMMCReg->INT_CNTR, SDHC_INTERRUPTS_MASK);
	
    // read the fifo as many times possible
    for (DWORD j=0; j<dwFifoFillCount; j++)
    {
        while (!EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_BRR));

        // read the fifo completely
        for (DWORD k=0; k<dwFifoSizeInWords; k++)
        {
           *dest_ptr = INREG32(&g_pSDMMCReg->BUFFER_ACCESS);
           dest_ptr++;
        }
    }

    /* Wait for transfer complete operation interrupt */	
    while(!EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_RODONE));
		
    /* Check for status errors */	
    status = SDHCCheckDataStatus (SDHC_STATUS_READ_OP_DONE_MSK, SDHC_STATUS_TIME_OUT_READ, SDHC_STATUS_READ_CRC_ERR_MSK);

	OALMSG(OAL_FUNC, (_T("- SDHCDataRead\r\n")));
	
    return status;
	
}


//------------------------------------------------------------------------------
//
// Function: SDHCDataWrite
//
//	Read the data from the card
//
// Parameters:
//		dest_ptr[out] - Destination memory address
//		blk_len[in] - Block Length
//
// Returns:
//		SDHC_STATUS_PASS for success/SDHC_STATUS_FAILURE for failure
//
//------------------------------------------------------------------------------
UINT32 SDHCDataWrite (UINT32* dest_ptr, UINT32 blk_len) 
{
    DWORD dwFifoSizeInWords		= SDController.Units_in_fifo;
    DWORD dwFifoFillCount		= (blk_len / SDController.Bytes_in_fifo);
    UINT32 status				= SDHC_STATUS_FAILURE;
        
	OALMSG(OAL_FUNC, (_T("+ SDHCDataWrite\r\n")));

	/* Clear Interrupt status Register */
    OUTREG32 (&g_pSDMMCReg->STATUS,0xFFFFFFFF);

	/* Enable interrupts */
    OUTREG32 (&g_pSDMMCReg->INT_CNTR, SDHC_INTERRUPTS_MASK);
	
    for (DWORD j=0; j<dwFifoFillCount; j++)
    {
        while (!EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_BWR));

        // fill the fifo completely
        for (DWORD k=0; k<dwFifoSizeInWords; k++)
        {
            OUTREG32(&g_pSDMMCReg->BUFFER_ACCESS, *dest_ptr);
            dest_ptr++;
        }
    }

	/* Wait for transfer complete operation interrupt */	
    while (!EXTREG32BF(&g_pSDMMCReg->STATUS, SDHC_SR_WODONE));
		
    /* Check for status errors */	
    status = SDHCCheckDataStatus(SDHC_STATUS_WRITE_OP_DONE_MSK,SDHC_STATUS_WR_CRC_ERR_CODE_MSK, SDHC_STATUS_WRITE_CRC_ERR_MSK);

	OALMSG(OAL_FUNC, (_T("- SDHCDataWrite\r\n")));
	
    return status;
	
}

