/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support
 * ----------------------------------------------------------------------------
 * Copyright (c) 2008, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

//------------------------------------------------------------------------------
//         Headers
//------------------------------------------------------------------------------

#include "mci_hs.h"
#include <utility/assert.h>
#include <utility/trace.h>

#include <dmad/dmad.h>
#include <dma/dma.h>

//------------------------------------------------------------------------------
//         Local constants
//------------------------------------------------------------------------------

/// Bit mask for status register errors.
#define STATUS_ERRORS ((unsigned int)(AT91C_MCI_UNRE  \
                       | AT91C_MCI_OVRE \
                       | AT91C_MCI_BLKOVRE \
                       | AT91C_MCI_CSTOE \
                       | AT91C_MCI_DTOE \
                       | AT91C_MCI_DCRCE \
                       | AT91C_MCI_RTOE \
                       | AT91C_MCI_RENDE \
                       | AT91C_MCI_RCRCE \
                       | AT91C_MCI_RDIRE \
                       | AT91C_MCI_RINDE))

#define STATUS_ERRORS_RESP ((unsigned int)(AT91C_MCI_CSTOE \
                            | AT91C_MCI_RTOE \
                            | AT91C_MCI_RENDE \
                            | AT91C_MCI_RCRCE \
                            | AT91C_MCI_RDIRE \
                            | AT91C_MCI_RINDE))

#define STATUS_ERRORS_DATA ((unsigned int)(AT91C_MCI_UNRE \
                            | AT91C_MCI_OVRE \
                            | AT91C_MCI_BLKOVRE \
                            | AT91C_MCI_CSTOE \
                            | AT91C_MCI_DTOE \
                            | AT91C_MCI_DCRCE))


/// MCI data timeout configuration with 1048576 MCK cycles between 2 data transfers.
#define DTOR_1MEGA_CYCLES           (AT91C_MCI_DTOCYC | AT91C_MCI_DTOMUL)

/// MCI MR: disable MCI Clock when FIFO is full
#ifndef AT91C_MCI_WRPROOF
    #define AT91C_MCI_WRPROOF 0
#endif
#ifndef AT91C_MCI_RDPROOF
    #define AT91C_MCI_RDPROOF 0
#endif

#define SDCARD_APP_OP_COND_CMD      (41 | AT91C_MCI_SPCMD_NONE \
                                        | AT91C_MCI_RSPTYP_48 \
                                        | AT91C_MCI_TRCMD_NO )
#define MMC_SEND_OP_COND_CMD        (1  | AT91C_MCI_TRCMD_NO \
                                        | AT91C_MCI_SPCMD_NONE \
                                        | AT91C_MCI_RSPTYP_48 \
                                        | AT91C_MCI_OPDCMD)
#define SDIO_SEND_OP_COND_CMD       (5 | AT91C_MCI_TRCMD_NO \
                                       | AT91C_MCI_SPCMD_NONE \
                                       | AT91C_MCI_RSPTYP_48 \
                                       | AT91C_MCI_OPDCMD)


#define DISABLE    0    // Disable MCI interface
#define ENABLE     1    // Enable MCI interface


//------------------------------------------------------------------------------
//         Local macros
//------------------------------------------------------------------------------

/// Used to write in PMC registers.
#define WRITE_PMC(pPmc, regName, value)     pPmc->regName = (value)

/// Used to write in MCI registers.
#define WRITE_MCI(pMci, regName, value)     pMci->regName = (value)

/// Used to read from MCI registers.
#define READ_MCI(pMci, regName)             (pMci->regName)

/// Enable MCI Peripheral
#define PERIPH_ENABLE(mciId)   WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << mciId))
/// Disable MCI Peripheral
#define PERIPH_DISABLE(mciId)  WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << mciId))

/// Enable MCI
#define MCI_ENABLE(pMciHw)     WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIEN)
/// Disable MCI
#define MCI_DISABLE(pMciHw)    WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS)
/// Reset MCI
#define MCI_RESET(pMciHw)      WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST)

/// Return word count from byte count
#define toHWCOUNT(byteCnt) (((byteCnt)&0x1) ? (((byteCnt)/2)+1) : ((byteCnt)/2))
#define toWCOUNT(byteCnt)  (((byteCnt)&0x3) ? (((byteCnt)/4)+1) : ((byteCnt)/4))

//------------------------------------------------------------------------------
//         Local variables
//------------------------------------------------------------------------------

//static unsigned char perChunkSize = 1;
//static unsigned char memChunkSize = 1;

//------------------------------------------------------------------------------
//         Internal Functions
//------------------------------------------------------------------------------
#if defined(MCI_DMA_ENABLE)
#define MCI_RD_FIFO_LIMIT    1

#define MCI_FIFO_SIZE  (0x4000-0x400)     // 15.5K
static unsigned short xfredBlocks = 0xFFFF;
static unsigned char  dmaLastLliNdx = 0;

#define DMA_XFR_SIZE (0xF00)    // SAM3: 0xFFF, 9M10: 0xFFFF
#define NUM_LLI      (5)
#define DMA_TOTAL    (DMA_XFR_SIZE*NUM_LLI)
static DmaLinkList  LLI_MCI [NUM_LLI];   // Max: > 64K Bytes

#define     LAST_ROW            0x100
static void AT91F_Prepare_Multiple_Transfer(unsigned int Channel,
                                            unsigned int LLI_rownumber,
                                            unsigned int LLI_Last_Row,
                                            unsigned int From_add,
                                            unsigned int To_add,
                                            unsigned int Ctrla,
                                            unsigned int Ctrlb)
{
    LLI_MCI[LLI_rownumber].sourceAddress =  From_add;
    LLI_MCI[LLI_rownumber].destAddress =  To_add;
    LLI_MCI[LLI_rownumber].controlA =  Ctrla;
    LLI_MCI[LLI_rownumber].controlB =  Ctrlb;
    if (LLI_Last_Row != LAST_ROW)
        LLI_MCI[LLI_rownumber].descriptor =
             (unsigned int)&LLI_MCI[LLI_rownumber + 1] + 0;
    else {
        dmaLastLliNdx = LLI_rownumber;
        LLI_MCI[LLI_rownumber].descriptor = 0;
    }
}

// trans_size: number of transfers in SRC side
// forceByte: 1 - byte count, 0 - word count
static unsigned int DMACH_MCI_P2M(unsigned int   channel_index,
                                  unsigned char* src_addr,
                                  unsigned char* dest_addr,
                                  unsigned int   trans_size,
                                  unsigned char  addrIncMode,
                                  unsigned char  forceByte)
{
    unsigned int srcAddress;
    unsigned int destAddress;
    unsigned int buffSize;
    unsigned int LLI_rownumber = 0;
    unsigned int srcAddressMode = addrIncMode ?
                                  (AT91C_HDMA_SRC_ADDRESS_MODE_INCR)
                                : (AT91C_HDMA_SRC_ADDRESS_MODE_FIXED);
    unsigned int scSize, dcSize, mWidth, perWidth, addrInc;

    // Disable dma channel
    DMA_DisableChannel(channel_index);

    // DMA channel configuration
    srcAddress  = (unsigned int)src_addr;    // Set the data start address
    destAddress = (unsigned int)dest_addr; //(unsigned int)SSC_THR_ADD; 
    buffSize    = trans_size;

    // Memory width can be WORD if address is aligned
    mWidth = ((destAddress & 0x3) == 0) ? AT91C_HDMA_DST_WIDTH_WORD
                                        : AT91C_HDMA_DST_WIDTH_BYTE;
    // Peripheral width is byte if FBYTE mode
    perWidth = forceByte ? AT91C_HDMA_SRC_WIDTH_BYTE
                         : AT91C_HDMA_SRC_WIDTH_WORD;
    addrInc  = forceByte ? 1 : 4;
  #if MCI_RD_FIFO_LIMIT
    if(buffSize >= DMA_TOTAL){
        TRACE_WARNING("SD DMA, size too big %d\n\r", buffSize);
        buffSize = DMA_TOTAL;
    }
  #endif
    // Set DMA channel source address
    DMA_SetSourceAddr(channel_index, srcAddress);

    // Set DMA channel destination address
    DMA_SetDestinationAddr(channel_index,destAddress);

    // Set DMA channel DSCR
    DMA_SetDescriptorAddr(channel_index, (unsigned int)&LLI_MCI[0]);

    // Set DMA channel control A 
    DMA_SetSourceBufferSize(channel_index, buffSize,
            (perWidth >> 24),
            (mWidth >> 28), 0);

	//Set DMA channel control B
    DMA_SetSourceBufferMode(channel_index, DMA_TRANSFER_LLI,
                            srcAddressMode >> 24);
    DMA_SetDestBufferMode(channel_index, DMA_TRANSFER_LLI,
                            (AT91C_HDMA_DST_ADDRESS_MODE_INCR >> 28));

    // Set DMA channel config
    DMA_SetConfiguration(channel_index, BOARD_SD_DMA_HW_SRC_REQ_ID \
                                        | BOARD_SD_DMA_HW_DEST_REQ_ID \
                                        | AT91C_HDMA_SRC_H2SEL_HW \
                                        | AT91C_HDMA_DST_H2SEL_HW \
                                        | AT91C_HDMA_SOD_DISABLE \
                                        | AT91C_HDMA_FIFOCFG_ENOUGHSPACE);

    //scSize = (perChunkSize == 4) ? AT91C_HDMA_SCSIZE_4 : AT91C_HDMA_SCSIZE_1;
    //dcSize = (memChunkSize == 4) ? AT91C_HDMA_DCSIZE_4 : AT91C_HDMA_DCSIZE_1;
    scSize = AT91C_HDMA_SCSIZE_1;    dcSize = AT91C_HDMA_DCSIZE_4;

    // Set link list
    buffSize *= addrInc;    // convert size to byte count
    while(destAddress < ((unsigned int)(dest_addr + buffSize))) {
        if(((unsigned int)(dest_addr + buffSize)) - destAddress <= (DMA_XFR_SIZE*addrInc) )
        {
            AT91F_Prepare_Multiple_Transfer(channel_index, LLI_rownumber, LAST_ROW,
                                        srcAddress,
                                        destAddress,
                                        (((((unsigned int)(dest_addr + buffSize))
                                               - destAddress)/addrInc)
                                                | perWidth
                                                | mWidth
                                                | scSize
                                                | dcSize
                                                    ),
                                        ( AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM
                                           | AT91C_HDMA_DST_ADDRESS_MODE_INCR
                                           | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE
                                           | srcAddressMode
                                           | AT91C_HDMA_FC_PER2MEM));
        }
        else
        {
            AT91F_Prepare_Multiple_Transfer(channel_index, LLI_rownumber, 0,
                                        srcAddress,
                                        destAddress,
                                        ( (DMA_XFR_SIZE)
                                            | perWidth
                                            | mWidth
                                            | scSize
                                            | dcSize
                                                ),
                                        (  AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM
                                            | AT91C_HDMA_DST_ADDRESS_MODE_INCR
                                            | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE
                                            | srcAddressMode
                                            | AT91C_HDMA_FC_PER2MEM));

        }

        destAddress += DMA_XFR_SIZE*addrInc;

        LLI_rownumber++;
    }

    return 0;
}

// trans_size: number of transfers in SRC side
// forceByte: 1 - byte count, 0 - word count
static unsigned int DMACH_MCI_M2P(unsigned int   channel_index,
                                  unsigned char* src_addr,
                                  unsigned char* dest_addr,
                                  unsigned int   trans_size,
                                  unsigned char  addrIncMode,
                                  unsigned char  forceByte)
{
    unsigned int srcAddress;
    unsigned int destAddress;
    unsigned int buffSize;
    unsigned int LLI_rownumber = 0;
    unsigned int dstAddressMode = addrIncMode ?
                                  (AT91C_HDMA_DST_ADDRESS_MODE_INCR)
                                : (AT91C_HDMA_DST_ADDRESS_MODE_FIXED);
    unsigned int dcSize, scSize, mWidth, perWidth, addrInc;

    // Disable dma channel
    DMA_DisableChannel(channel_index);

    buffSize = trans_size;
    if(buffSize >= DMA_TOTAL){
        TRACE_WARNING("SD DMA, size too big %d\n\r", buffSize);
        buffSize = DMA_TOTAL;
    }

    // DMA channel configuration
    srcAddress  = (unsigned int)src_addr;    // Set the data start address
    destAddress = (unsigned int)dest_addr;

    // Memory width
    mWidth = ((srcAddress & 0x3) == 0) ? AT91C_HDMA_SRC_WIDTH_WORD
                                       : AT91C_HDMA_SRC_WIDTH_BYTE;
    // One Transfer size (1 or 4)
    perWidth = forceByte ? AT91C_HDMA_DST_WIDTH_BYTE
                         : AT91C_HDMA_DST_WIDTH_WORD;
    //addrInc = forceByte ? 1 : 4;
    if (mWidth == AT91C_HDMA_SRC_WIDTH_BYTE) {
        addrInc = 1;
        if (!forceByte) buffSize *= 4;
    }
    else
        addrInc = 4;

    // Set DMA channel source address
    DMA_SetSourceAddr(channel_index, srcAddress);

    // Set DMA channel destination address
    DMA_SetDestinationAddr(channel_index,destAddress);

    // Set DMA channel DSCR
    DMA_SetDescriptorAddr(channel_index, (unsigned int)&LLI_MCI[0]);

    // Set DMA channel control A 
    DMA_SetSourceBufferSize(channel_index, buffSize,
                            (mWidth   >> 24),
                            (perWidth >> 28), 0);

    //Set DMA channel control B
    DMA_SetSourceBufferMode(channel_index,
                            DMA_TRANSFER_LLI,
                            (AT91C_HDMA_SRC_ADDRESS_MODE_INCR >> 24));
    DMA_SetDestBufferMode(channel_index,
                          DMA_TRANSFER_LLI,
                          dstAddressMode >> 28);

    // Set DMA channel config
    DMA_SetConfiguration(channel_index, BOARD_SD_DMA_HW_SRC_REQ_ID \
                                        | BOARD_SD_DMA_HW_DEST_REQ_ID \
                                        | AT91C_HDMA_SRC_H2SEL_SW \
                                        | AT91C_HDMA_DST_H2SEL_HW \
                                        | AT91C_HDMA_SOD_DISABLE \
                                        | AT91C_HDMA_FIFOCFG_LARGESTBURST);
    //dcSize = (perChunkSize == 4) ? AT91C_HDMA_DCSIZE_4 : AT91C_HDMA_DCSIZE_1;
    //scSize = (memChunkSize == 4) ? AT91C_HDMA_SCSIZE_4 : AT91C_HDMA_SCSIZE_1;
    dcSize = AT91C_HDMA_DCSIZE_1;    scSize = AT91C_HDMA_SCSIZE_4;

    // Set link list
    buffSize *= addrInc; // convert to byte address
    while(srcAddress < ((unsigned int)(src_addr + buffSize)))
    {
        if(((unsigned int)(src_addr + buffSize)) - srcAddress <= (DMA_XFR_SIZE*addrInc) )
        {
            AT91F_Prepare_Multiple_Transfer(channel_index, LLI_rownumber, LAST_ROW,
                                        srcAddress,
                                        destAddress,
                                        (((((unsigned int)(src_addr + buffSize))
                                                - srcAddress)/addrInc)
                                                  | mWidth
                                                  | perWidth
                                                  | scSize
                                                  | dcSize
                                                      ),
                                        ( AT91C_HDMA_DST_DSCR_FETCH_DISABLE
                                        | dstAddressMode
                                        | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM
                                        | AT91C_HDMA_SRC_ADDRESS_MODE_INCR
                                        | AT91C_HDMA_FC_MEM2PER));
        }
        else
        {
            AT91F_Prepare_Multiple_Transfer(channel_index, LLI_rownumber, 0,
                                        srcAddress,
                                        destAddress,
                                        ( (DMA_XFR_SIZE)
                                            | mWidth
                                            | perWidth
                                            | scSize
                                            | dcSize
                                                ),
                                        ( AT91C_HDMA_DST_DSCR_FETCH_DISABLE
                                        | dstAddressMode
                                        | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM
                                        | AT91C_HDMA_SRC_ADDRESS_MODE_INCR
                                        | AT91C_HDMA_FC_MEM2PER));

        }

        srcAddress += DMA_XFR_SIZE*addrInc;

        
        LLI_rownumber++;
    }
    
    return 0;
}

static inline void DMACH_EnableIt(AT91S_MCI *pMciHw,
                                 unsigned int channel)
{
    unsigned int intFlag;

    intFlag = DMA_GetInterruptMask();
    intFlag |= (AT91C_HDMA_BTC0 << channel);
    DMA_EnableIt(intFlag);
}
#endif

//------------------------------------------------------------------------------
//         Global functions
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// Reset MCI interface and disable it.
/// \param keepSettings Keep old register settings, including _MR, _SDCR, _DTOR
//------------------------------------------------------------------------------
void MCI_Reset(AT91PS_MCI pMciHw, unsigned int keepSettings)
{
    if (keepSettings) {

        unsigned int mciMr, mciSdcr, mciDtor, mciCstor;
        unsigned int mciDma, mciCfg;

        mciMr    = READ_MCI(pMciHw, MCI_MR);
        mciSdcr  = READ_MCI(pMciHw, MCI_SDCR);
        mciDtor  = READ_MCI(pMciHw, MCI_DTOR);
        mciCstor = READ_MCI(pMciHw, MCI_CSTOR);

        mciDma = READ_MCI(pMciHw, MCI_DMA);
        mciCfg = READ_MCI(pMciHw, MCI_CFG);

        WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST);
        WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_PWSDIS);

        WRITE_MCI(pMciHw, MCI_MR, mciMr);
        WRITE_MCI(pMciHw, MCI_SDCR, mciSdcr);
        WRITE_MCI(pMciHw, MCI_DTOR, mciDtor);
        WRITE_MCI(pMciHw, MCI_CSTOR, mciCstor);

        WRITE_MCI(pMciHw, MCI_DMA, mciDma);
        WRITE_MCI(pMciHw, MCI_CFG, mciCfg);
    }
    else {

        WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST);
        WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_PWSDIS);
    }
}

//------------------------------------------------------------------------------
/// Enable/disable a MCI driver instance.
/// \param pMci  Pointer to a MCI driver instance.
/// \param enb  0 for disable MCI and 1 for enable MCI.
//------------------------------------------------------------------------------
void MCI_Enable(Mci *pMci, unsigned char enb)
{
    AT91S_MCI *pMciHw = pMci->pMciHw;

    SANITY_CHECK(pMci);
    SANITY_CHECK(pMci->pMciHw);

    // Set the Control Register: Enable/Disable MCI interface clock
    if(enb == DISABLE) {
        MCI_DISABLE(pMciHw);
    }
    else {
        MCI_ENABLE(pMciHw);
    }
}

//------------------------------------------------------------------------------
/// Initializes a MCI driver instance and the underlying peripheral.
/// \param pMci    Pointer to a MCI driver instance.
/// \param pMciHw  Pointer to a MCI peripheral.
/// \param mciId   MCI peripheral identifier.
/// \param mode    Slot and type of supported card (max bus width).
//------------------------------------------------------------------------------
void MCI_Init(
    Mci *pMci,
    AT91PS_MCI pMciHw,
    unsigned char mciId,
    unsigned int mode,
    unsigned int bPolling)
{
    unsigned short clkDiv;
    unsigned int mciCfg = 0;

    SANITY_CHECK(pMci);
    SANITY_CHECK(pMciHw);
    SANITY_CHECK(   (mode == MCI_MMC_SLOTA)  || (mode == MCI_SD_SLOTA)
                 || (mode == MCI_MMC_SLOTB)  || (mode == MCI_SD_SLOTB)
                 || (mode == MCI_MMC4_SLOTA) || (mode == MCI_MMC4_SLOTB));

#if MCI_BUSY_CHECK_FIX
    pMci->pPinDAT0 = 0;
#endif

    // Initialize the MCI driver structure
    pMci->pMciHw    = pMciHw;
    pMci->mciId     = mciId;
    pMci->mciMode   = mode;
    pMci->bPolling  = bPolling;
    pMci->semaphore = 1;
    pMci->pCommand  = 0;

#if !defined(OP_BOOTSTRAP_MCI_on)
    // Enable the MCI clock
    PERIPH_ENABLE(mciId);

     // Reset the MCI
    WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST);

    // Disable the MCI
    WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_PWSDIS);

    // Disable all the interrupts
    WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF);

    // Set the Data Timeout Register
    WRITE_MCI(pMciHw, MCI_DTOR, DTOR_1MEGA_CYCLES);

    // Set the Mode Register: 400KHz for MCK = 48MHz (CLKDIV = 58)
    clkDiv = (BOARD_MCK / (MCI_INITIAL_SPEED * 2)) - 1;
    WRITE_MCI(pMciHw, MCI_MR, (clkDiv | (AT91C_MCI_PWSDIV & (0x7 << 8))));

    // Set the SDCard Register
    WRITE_MCI(pMciHw, MCI_SDCR, mode);

    // Enable the MCI and the Power Saving
    WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIEN);

    // Disable the DMA interface
    WRITE_MCI(pMciHw, MCI_DMA, AT91C_MCI_DMAEN_DISABLE);

    // Configure MCI
    mciCfg = AT91C_MCI_FIFOMODE_AMOUNTDATA | AT91C_MCI_FERRCTRL_RWCMD;
    //mciCfg = AT91C_MCI_FIFOMODE_ONEDATA | AT91C_MCI_FERRCTRL_RWCMD;
    
    WRITE_MCI(pMciHw, MCI_CFG, mciCfg);

    // Disable the MCI peripheral clock.
    WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << mciId));
#else
    // Assume ROM code initialize the MCI already
    TRACE_INFO("SD bootstrap not init mci!\n\r");
#endif

}

#if MCI_BUSY_CHECK_FIX
/// Invoked after MCI_Init to enable busy line fix
void MCI_SetBusyFix(Mci *pMci, const Pin * pDAT0)
{
    pMci->pPinDAT0 = pDAT0;
}
#endif

//------------------------------------------------------------------------------
/// Close a MCI driver instance and the underlying peripheral.
/// \param pMci  Pointer to a MCI driver instance.
/// \param pMciHw  Pointer to a MCI peripheral.
/// \param mciId  MCI peripheral identifier.
//------------------------------------------------------------------------------
void MCI_Close(Mci *pMci)
{
    AT91S_MCI *pMciHw = pMci->pMciHw;

    SANITY_CHECK(pMci);
    if (!pMciHw) return;

    // Disable all the interrupts
    WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF);

    // Disable the MCI
    WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS);

    // Disable the MCI peripheral clock.
    WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pMci->mciId));

    // Initialize the MCI driver structure
    pMci->semaphore = 1;
    pMci->pCommand = 0;
}

//------------------------------------------------------------------------------
/// Get the  MCI CLKDIV in the MCI_MR register. The max. for MCI clock is
/// MCK/2 and corresponds to CLKDIV = 0
/// \param pMci  Pointer to the low level MCI driver.
/// \param mciSpeed  MCI clock speed in Hz.
//------------------------------------------------------------------------------
unsigned int MCI_GetSpeed(Mci *pMci, unsigned int *mciDiv)
{
    AT91S_MCI *pMciHw = pMci->pMciHw;
    unsigned int mciMr;

    SANITY_CHECK(pMci);
    SANITY_CHECK(pMci->pMciHw);

    // Get the Mode Register
    mciMr  = READ_MCI(pMciHw, MCI_MR);
    mciMr &= AT91C_MCI_CLKDIV;
    if (mciDiv) *mciDiv = mciMr;
    return (BOARD_MCK / 2 / (mciMr + 1));
}

//------------------------------------------------------------------------------
/// Configure the  MCI CLKDIV in the MCI_MR register. The max. for MCI clock is
/// MCK/2 and corresponds to CLKDIV = 0
/// \param pMci  Pointer to the low level MCI driver.
/// \param mciSpeed  MCI clock speed in Hz.
/// \param mciLimit  MCI clock limit in Hz, if not limit, set mciLimit to zero.
/// \param mck       MCK to generate MCI Clock, in Hz
/// \return The actual speed used, 0 for fail.
//------------------------------------------------------------------------------
unsigned int MCI_SetSpeed(Mci *pMci,
                          unsigned int mciSpeed,
                          unsigned int mciLimit,
                          unsigned int mck)
{
    AT91S_MCI *pMciHw = pMci->pMciHw;
    unsigned int mciMr;
    unsigned int clkdiv;
    unsigned int divLimit = 0;

    SANITY_CHECK(pMci);
    SANITY_CHECK(pMci->pMciHw);

    mciMr = READ_MCI(pMciHw, MCI_MR) & (~(unsigned int)AT91C_MCI_CLKDIV);

    // Multimedia Card Interface clock (MCCK or MCI_CK) is Master Clock (MCK)
    // divided by (2*(CLKDIV+1))
    // mciSpeed = MCK / (2*(CLKDIV+1))
    if (mciLimit) {
        divLimit = (mck / 2 / mciLimit);
        if ((mck / 2) % mciLimit) divLimit ++;
    }
    if (mciSpeed > 0) {
        clkdiv = (mck / 2 / mciSpeed);
        if (mciLimit && clkdiv < divLimit)
            clkdiv = divLimit;
        if (clkdiv > 0) 
            clkdiv -= 1;
        ASSERT( (clkdiv & 0xFFFFFF00) == 0, "mciSpeed too small");
    }
    else    clkdiv = 0;

    // Actual MCI speed
    mciSpeed = mck / 2 / (clkdiv + 1);

    // Set the Data Timeout Register & Completion Timeout
    // Data timeout is 500ms, completion timeout 1s.
    //MCI_SetTimeout(pMciHw, mciSpeed / 2, mciSpeed);

    WRITE_MCI(pMciHw, MCI_MR, mciMr | clkdiv);
    return (mciSpeed);
}

//------------------------------------------------------------------------------
/// Configure the MCI_CFG to enable the HS mode
/// \param pMci     Pointer to the low level MCI driver.
/// \param hsEnable 1 to enable, 0 to disable HS mode.
//------------------------------------------------------------------------------
void MCI_EnableHsMode(Mci *pMci, unsigned char hsEnable)
{
    AT91S_MCI *pMciHw = pMci->pMciHw;
    unsigned int cfgr;

    SANITY_CHECK(pMci);
    SANITY_CHECK(pMci->pMciHw);

    cfgr = READ_MCI(pMciHw, MCI_CFG);
    if (hsEnable)   cfgr |=  AT91C_MCI_HSMODE_ENABLE;
    else            cfgr &= ~(unsigned int)AT91C_MCI_HSMODE_ENABLE;
    WRITE_MCI(pMciHw, MCI_CFG, cfgr);
}

//------------------------------------------------------------------------------
/// Configure the  MCI SDCBUS in the MCI_SDCR register. Only two modes available
///
/// \param pMci  Pointer to the low level MCI driver.
/// \param busWidth  MCI bus width mode.
//------------------------------------------------------------------------------
void MCI_SetBusWidth(Mci *pMci, unsigned char busWidth)
{
    AT91S_MCI *pMciHw = pMci->pMciHw;
    unsigned int mciSdcr;

    SANITY_CHECK(pMci);
    SANITY_CHECK(pMci->pMciHw);

    mciSdcr = (READ_MCI(pMciHw, MCI_SDCR) & ~((unsigned int)AT91C_MCI_SCDBUS));

    WRITE_MCI(pMciHw, MCI_SDCR, mciSdcr | busWidth);
}

//------------------------------------------------------------------------------
/// Starts a MCI  transfer. This is a non blocking function. It will return
/// as soon as the transfer is started.
/// Return 0 if successful; otherwise returns MCI_ERROR_LOCK if the driver is
/// already in use.
/// \param pMci  Pointer to an MCI driver instance.
/// \param pCommand  Pointer to the command to execute.
//------------------------------------------------------------------------------
unsigned char MCI_SendCommand(Mci *pMci, MciCmd *pCommand)
{
    AT91PS_MCI pMciHw = pMci->pMciHw;
    unsigned int mciIer, mciMr;
    unsigned int transSize;
    unsigned int mciBlkr;

  #if defined(MCI_DMA_ENABLE)
    unsigned int mciDma;
  #endif

    SANITY_CHECK(pMci);
    SANITY_CHECK(pMciHw);
    SANITY_CHECK(pCommand);

    // Try to acquire the MCI semaphore
    if (pMci->semaphore == 0) {

        return MCI_ERROR_LOCK;
    }
    pMci->semaphore--;

    // Command is now being executed
    pMci->pCommand = pCommand;
    pCommand->status = MCI_STATUS_PENDING;

    // Enable the MCI peripheral clock
    PERIPH_ENABLE(pMci->mciId);

    // Set Default Mode register value
    mciMr = READ_MCI(pMciHw, MCI_MR) & (~( AT91C_MCI_WRPROOF
                                          |AT91C_MCI_RDPROOF
                                          |AT91C_MCI_BLKLEN));
    // Command with DATA stage
    if (pCommand->blockSize && pCommand->nbBlock) {
        // Enable dma
      #if defined(MCI_DMA_ENABLE)
        mciDma = READ_MCI(pMciHw, MCI_DMA) | AT91C_MCI_DMAEN_ENABLE;
        WRITE_MCI(pMciHw, MCI_DMA, mciDma);
      #endif

        transSize = (pCommand->nbBlock * pCommand->blockSize);
      #if defined(MCI_DMA_ENABLE)
        if (pCommand->isRead) {
          #if MCI_RD_FIFO_LIMIT
            if (transSize > MCI_FIFO_SIZE) {
                xfredBlocks = MCI_FIFO_SIZE/pCommand->blockSize;
                transSize = MCI_FIFO_SIZE;
            }
            else
          #endif
            {
                xfredBlocks = pCommand->nbBlock;
            }
        }
        else {
            xfredBlocks = pCommand->nbBlock;
        }
      #endif
        if ((pCommand->blockSize & 0x3) != 0) {
            // Force byte, DataReg & DMA should be BYTE based
            mciMr |= AT91C_MCI_PDCFBYTE;
        }
        else
            transSize = toWCOUNT(transSize);

        // New transfer
        if(pCommand->tranType == MCI_NEW_TRANSFER)
        {

            // Set block size
            WRITE_MCI(pMciHw, MCI_MR, mciMr | AT91C_MCI_RDPROOF
                                            | AT91C_MCI_WRPROOF
                                            |(pCommand->blockSize << 16));

            mciBlkr = READ_MCI(pMciHw, MCI_BLKR)
                        & (~(unsigned int)AT91C_MCI_BCNT);
            WRITE_MCI(pMciHw, MCI_BLKR, mciBlkr
                                        | (transSize/pCommand->blockSize));
        }

        // DATA transfer from card to host
        if (pCommand->isRead) {
            
          #if defined(MCI_DMA_ENABLE)
            DMACH_MCI_P2M(BOARD_MCI_DMA_CHANNEL,
                          (unsigned char*)&pMciHw->MCI_FIFO,
                          (unsigned char*) pCommand->pData,
                          transSize, 0,
                          (mciMr & AT91C_MCI_PDCFBYTE) > 0);
            DMACH_EnableIt(pMciHw, BOARD_MCI_DMA_CHANNEL);
            DMA_EnableChannel(BOARD_MCI_DMA_CHANNEL);
            mciIer = AT91C_MCI_DMADONE | STATUS_ERRORS;
          #else 
            mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS;
          #endif
        }
        // DATA transfer from host to card
        else {

          #if defined(MCI_DMA_ENABLE)
            if ((mciMr & AT91C_MCI_PDCFBYTE) > 0) {
                #if 1
                // Still using WORD mode to write FIFO
                DMACH_MCI_M2P(BOARD_MCI_DMA_CHANNEL,
                              (unsigned char*) pCommand->pData,
                              (unsigned char*)&pMciHw->MCI_FIFO,
                              toWCOUNT(transSize), 0,
                              0);
                #else
                // Still write to TDR with WORD!
                DMACH_MCI_M2P(BOARD_MCI_DMA_CHANNEL,
                              (unsigned char*) pCommand->pData,
                              (unsigned char*)&pMciHw->MCI_TDR,
                              toWCOUNT(transSize), 0,
                              0);
                #endif
            }
            else {
                 DMACH_MCI_M2P(BOARD_MCI_DMA_CHANNEL,
                              (unsigned char*) pCommand->pData,
                              (unsigned char*)&pMciHw->MCI_FIFO,
                              transSize, 0,
                              0);
            }
            DMACH_EnableIt(pMciHw, BOARD_MCI_DMA_CHANNEL);
            DMA_EnableChannel(BOARD_MCI_DMA_CHANNEL);
            mciIer = AT91C_MCI_DMADONE | STATUS_ERRORS;
          #else
            mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS;
          #endif
        }
    }
    // Start an infinite block transfer (but no data in current command)
    else if (pCommand->dataTran) {
        // Set block size
        WRITE_MCI(pMciHw, MCI_MR, mciMr | AT91C_MCI_RDPROOF
                                        | AT91C_MCI_WRPROOF
                                        |(pCommand->blockSize << 16));
        // Set data length: 0
        mciBlkr = READ_MCI(pMciHw, MCI_BLKR) & (~(unsigned int)AT91C_MCI_BCNT);
        WRITE_MCI(pMciHw, MCI_BLKR, mciBlkr);
        mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS;
    }
    // No data transfer: stop at the end of the command
    else{
        WRITE_MCI(pMciHw, MCI_MR, mciMr);
        mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS;
    }

    // Enable MCI
    MCI_ENABLE(pMciHw);

    // Send the command
    if((pCommand->tranType != MCI_CONTINUE_TRANSFER)
        || (pCommand->blockSize == 0)) {

        WRITE_MCI(pMciHw, MCI_ARGR, pCommand->arg);
        WRITE_MCI(pMciHw, MCI_CMDR, pCommand->cmd);
    }

    // Ignore CRC error for R3 & R4
    if (pCommand->resType == 3 || pCommand->resType == 4) {
        mciIer &= ~((unsigned int)AT91C_MCI_RCRCE);
    }
    // Ignore errors for stop command :)
    if (pCommand->tranType == MCI_STOP_TRANSFER) {
        mciIer &= ~((unsigned int)(AT91C_MCI_DCRCE
                                    |AT91C_MCI_BLKOVRE
                                    |AT91C_MCI_DTOE
                                    |AT91C_MCI_CSTOE));
    }

    // Ignore data error
    mciIer &= ~(  0
                | AT91C_MCI_UNRE
                | AT91C_MCI_OVRE
                | AT91C_MCI_DTOE
                | AT91C_MCI_DCRCE
                | AT91C_MCI_BLKOVRE
                | AT91C_MCI_CSTOE
                );

    // Interrupt enable shall be done after PDC TXTEN and RXTEN
    WRITE_MCI(pMciHw, MCI_IER, mciIer);

    return 0;
}

//------------------------------------------------------------------------------
/// Check NOTBUSY and DTIP bits of status register on the given MCI driver.
/// Return value, 0 for bus ready, 1 for bus busy
/// \param pMci  Pointer to a MCI driver instance.
//------------------------------------------------------------------------------
unsigned char MCI_CheckBusy(Mci *pMci)
{
    AT91S_MCI *pMciHw = pMci->pMciHw;
    volatile unsigned int status;

    // Enable MCI clock
    PERIPH_ENABLE(pMci->mciId);
    MCI_ENABLE(pMciHw);

    status = READ_MCI(pMciHw, MCI_SR);

#if MCI_BUSY_CHECK_FIX
    if ((status & AT91C_MCI_DTIP) == 0) {
        unsigned char isBusy = 1;
        if (pMci->pPinDAT0) {
            Pin pinBsy;
            pinBsy.mask      = pMci->pPinDAT0->mask;
            pinBsy.pio       = pMci->pPinDAT0->pio;
            pinBsy.id        = pMci->pPinDAT0->id;
            pinBsy.type      = PIO_INPUT;
            pinBsy.attribute = PIO_PULLUP;
            PIO_Configure(&pinBsy, 1);
            isBusy = (PIO_Get(&pinBsy) == 0);
            PIO_Configure(pMci->pPinDAT0, 1);
        }
        else {
            isBusy = ((status & AT91C_MCI_NOTBUSY) == 0);
        }
        if (isBusy == 0) {
            MCI_DISABLE(pMciHw);
            PERIPH_DISABLE(pMci->mciId);
        }
        return isBusy;
    }
    
    return 1;
#else

    if(    ((status & AT91C_MCI_NOTBUSY)!=0)
        && ((status & AT91C_MCI_DTIP)==0)
        ) {

        // Disable MCI clock
        MCI_DISABLE(pMciHw);
        PERIPH_DISABLE(pMci->mciId);
        return 0;
    }
    else {
        return 1;
    }
#endif
}

//------------------------------------------------------------------------------
/// Processes pending events on the given MCI driver.
/// \param pMci  Pointer to a MCI driver instance.
//------------------------------------------------------------------------------
void MCI_Handler(Mci *pMci)
{
    AT91S_MCI *pMciHw = pMci->pMciHw;
    MciCmd *pCommand = pMci->pCommand;
    volatile unsigned int status;
    unsigned int status0, mask;
    unsigned char i;

    SANITY_CHECK(pMci);
    SANITY_CHECK(pMciHw);
    SANITY_CHECK(pCommand);

    // Read the status register
    status0 = READ_MCI(pMciHw, MCI_SR);
    mask    = READ_MCI(pMciHw, MCI_IMR);
    //TRACE_INFO("iST %x\n\r", status);
    status  = status0 & mask;
    //TRACE_INFO("iSM %x\n\r", status);

    // Check if an error has occured
    if ((status & STATUS_ERRORS) != 0) {

        // Check error code
        if ((status & STATUS_ERRORS) == AT91C_MCI_RTOE) {

            pCommand->status = MCI_STATUS_NORESPONSE;
        }
        else {

            pCommand->status = MCI_STATUS_ERROR;
        }
        TRACE_INFO("iErr%x\n\r", (status & STATUS_ERRORS));
    }
    mask &= ~STATUS_ERRORS;

    // Check if a command has been completed
    if (status & AT91C_MCI_CMDRDY) {

        WRITE_MCI(pMciHw, MCI_IDR, AT91C_MCI_CMDRDY);
        if (pCommand->isRead == 0 &&
            pCommand->tranType == MCI_STOP_TRANSFER) {
            if (status0 & AT91C_MCI_XFRDONE) {
                MCI_DISABLE(pMciHw);
            }
            else {
                WRITE_MCI(pMciHw, MCI_IER, AT91C_MCI_XFRDONE);
            }
        }
        else {
            mask &= ~(unsigned int)AT91C_MCI_CMDRDY;
            if (pCommand->dataTran == 0) {
                MCI_DISABLE(pMciHw);
            }
        }
    }

    // Check if transfer stopped
    if (status & AT91C_MCI_XFRDONE) {
        mask &= ~(unsigned int)AT91C_MCI_XFRDONE;
        MCI_DISABLE(pMciHw);
    }

#if defined(MCI_DMA_ENABLE)

    // Check FIFOEMPTY
    if (status & AT91C_MCI_FIFOEMPTY) {
        
        WRITE_MCI(pMciHw, MCI_IDR, AT91C_MCI_FIFOEMPTY);
        if ( pCommand->isRead == 0 &&
            (status0 & AT91C_MCI_BLKE) == 0 ) {
            WRITE_MCI(pMciHw, MCI_IER, AT91C_MCI_BLKE);
        }
        else {
            mask &= ~(unsigned int)AT91C_MCI_FIFOEMPTY;
            MCI_DISABLE(pMciHw);
        }
    }
    if (status & AT91C_MCI_BLKE) {
        mask &= ~(unsigned int)AT91C_MCI_BLKE;
        MCI_DISABLE(pMciHw);
    }

    // Check if a DMA transfer has been completed
    if (   (status & AT91C_MCI_DMADONE)
        && (LLI_MCI[dmaLastLliNdx].controlA & AT91C_HDMA_DONE)) {

        unsigned int intFlag;
        intFlag = DMA_GetInterruptMask();
        intFlag = ~intFlag;
        intFlag |= (AT91C_HDMA_BTC0 << BOARD_MCI_DMA_CHANNEL);
        DMA_DisableIt(intFlag);

        // All data transferred
        if (xfredBlocks >= pCommand->nbBlock) {

            WRITE_MCI(pMciHw, MCI_IDR, AT91C_MCI_DMADONE);
            if ( pCommand->isRead == 0 &&
                (status0 & AT91C_MCI_FIFOEMPTY) == 0 ) {
                WRITE_MCI(pMciHw, MCI_IER, AT91C_MCI_FIFOEMPTY);
            }
            else {
                MCI_DISABLE(pMciHw);
                mask &= ~(unsigned int)AT91C_MCI_DMADONE;
            }
        }
        // Start later part of DMA
        else {
            unsigned int transSize;
            unsigned char* p;
            p = &pCommand->pData[xfredBlocks*pCommand->blockSize];
            transSize = ((pCommand->nbBlock - xfredBlocks)
                            * pCommand->blockSize);
            if (transSize > MCI_FIFO_SIZE) {
                transSize = MCI_FIFO_SIZE;
                xfredBlocks += MCI_FIFO_SIZE/pCommand->blockSize;
            }
            else {
                xfredBlocks  = pCommand->nbBlock;
            }
            #if 0
            WRITE_MCI(pMciHw, MCI_BLKR, (READ_MCI(pMciHw, MCI_BLKR)
                                         & (~(unsigned int)AT91C_MCI_BCNT))
                                           | (transSize/pCommand->blockSize));
            #endif
            if ((pCommand->blockSize & 0x3) == 0) {
                transSize = toWCOUNT(transSize);
            }
            DMACH_MCI_P2M(BOARD_MCI_DMA_CHANNEL,
                          (unsigned char*)&pMciHw->MCI_FIFO,
                          (unsigned char*) p,
                          transSize, 0,
                          (pCommand->blockSize & 0x3) > 0);
            DMA_EnableChannel(BOARD_MCI_DMA_CHANNEL);
        }
    }
#endif

    // All non-error mask done, complete the command
    if (!mask || pCommand->status != MCI_STATUS_PENDING) {

        // Store the card response in the provided buffer
        if (pCommand->pResp) {
            unsigned char resSize;
            switch (pCommand->resType) {
            case 1: case 3: case 4: case 5: case 6: case 7:
                     resSize = 1;           break;
            case 2:  resSize = 4;           break;
            default: resSize = 0;           break;
            }
            for (i=0; i < resSize; i++) {
                pCommand->pResp[i] = READ_MCI(pMciHw, MCI_RSPR[0]);
            }
        }

        // If no error occured, the transfer is successful
        if (pCommand->status == MCI_STATUS_PENDING)
            pCommand->status = 0;
        // Any error, reset registers
        else {
            MCI_Reset(pMciHw, 1);
        }

        // Disable interrupts
        WRITE_MCI(pMciHw, MCI_IDR, READ_MCI(pMciHw, MCI_IMR));
      #if defined(MCI_DMA_ENABLE)
        DMA_DisableChannel(BOARD_MCI_DMA_CHANNEL);
      #endif

        // Disable peripheral
        PERIPH_DISABLE(pMci->mciId);
        
        // Release the semaphore
        pMci->semaphore++;

        // Invoke the callback associated with the current command (if any)
        if (pCommand->callback) {
            (pCommand->callback)(pCommand->status, (void*)pCommand);
        }
    }
}

//------------------------------------------------------------------------------
/// Returns 1 if the given MCI transfer is complete; otherwise returns 0.
/// \param pCommand  Pointer to a MciCmd instance.
//------------------------------------------------------------------------------
unsigned char MCI_IsTxComplete(Mci *pMci)
{
    MciCmd *pCommand = pMci->pCommand;		
	
    if(pMci->bPolling == MCI_POLLING_MODE) {
        MCI_Handler(pMci);
    }
  
    if (pCommand->status != MCI_STATUS_PENDING) {
        if (pCommand->status != 0) {
            TRACE_DEBUG("MCI_IsTxComplete %d\n\r", pCommand->status);
        }
        return 1;
    }
    else {
        return 0;
    }
}

//------------------------------------------------------------------------------
/// Check whether the MCI is using the FIFO transfer mode
/// \param pMci  Pointer to a Mci instance.
/// \param pCommand  Pointer to a MciCmd instance.
//------------------------------------------------------------------------------
unsigned int MCI_FifoTransfer(Mci *pMci, MciCmd *pCommand)
{
    // If using DMA mode, return
#if defined(MCI_DMA_ENABLE)
    return 0;
#else

    unsigned int status=0;
    unsigned int nbTransfer=0;
    unsigned int i;
    AT91S_MCI *pMciHw = pMci->pMciHw;
    unsigned int *pMem;

    SANITY_CHECK(pMci);
    SANITY_CHECK(pCommand);


    TRACE_DEBUG("MCIFifo:%d,%d\n\r", pCommand->isRead, pCommand->nbBlock);

    if (pCommand->nbBlock == 0 || pCommand->blockSize == 0)
        return 0;

    pMem = (unsigned int*)pCommand->pData;

    // Get transfer size
    nbTransfer = (pCommand->blockSize) * (pCommand->nbBlock) / 4;
    if((pCommand->blockSize) * (pCommand->nbBlock) % 4) {
        nbTransfer++;
    }

    if (pCommand->isRead) {

        // Read RDR loop
        for(i=0; i<nbTransfer; i++) {
            while(1) {
                status = READ_MCI(pMciHw, MCI_SR);
                if (status & AT91C_MCI_RXRDY)
                    break;
              #if 1
                if (status & STATUS_ERRORS_DATA) {
                    TRACE_ERROR("MCI_FifoTransfer.R: 0x%x\n\r", status);
                    return status;
                }
              #endif
            }
            *pMem = READ_MCI(pMciHw, MCI_RDR);
            pMem++;
        }
    }
    else {

        // Write TDR loop
        for(i=0; i<nbTransfer; i++) {
            while(1) {
                status = READ_MCI(pMciHw, MCI_SR);
                if (status & (AT91C_MCI_TXRDY | AT91C_MCI_NOTBUSY))
                    break;
              #if 0
                if (status & STATUS_ERRORS_DATA) {
                    TRACE_ERROR("MCI_FifoTransfer.W: 0x%x\n\r", status);
                    return status;
                }
              #endif
            }
            WRITE_MCI(pMciHw, MCI_TDR, *pMem);
            pMem++;
        }
    }

    status = READ_MCI(pMciHw, MCI_SR);
    TRACE_DEBUG("MCI_FifoTransfer : All status %x\n\r", status);
    status &= READ_MCI(pMciHw, MCI_IMR);
    TRACE_DEBUG("MCI_FifoTransfer : Masked status %x\n\r", status);

  #if 0
  { unsigned int old = status;
    while(status & AT91C_MCI_DTIP) {
        status = READ_MCI(pMciHw, MCI_SR);
        if (status != old) {
            old = status;
            TRACE_DEBUG_WP(" -> %x", status);
        }
    }
    TRACE_DEBUG_WP("\n\r");
    TRACE_DEBUG(" DPIT 0 stat %x\n\r", status);
    while((status & (AT91C_MCI_FIFOEMPTY
                        | AT91C_MCI_BLKE
                        | AT91C_MCI_XFRDONE)) == 0) {
        status = READ_MCI(pMciHw, MCI_SR);
    }
    TRACE_DEBUG(" FIFO EMPTY stat %x\n\r", status);
  }
  #endif

  return status;
#endif
}
