/* ----------------------------------------------------------------------------
 *         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 "sdmmc_mci.h"

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

#include <pio/pio.h>

#include <string.h>

//------------------------------------------------------------------------------
//         Global variables
//------------------------------------------------------------------------------

#if defined(MCI2_INTERFACE)
unsigned char gSdmmcAutoHsEnable = 1;
#else
unsigned char gSdmmcAutoHsEnable = 0;
#endif

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

//#define SINGLE_READ
//#define SINGLE_WRITE

// Timeout wait loop
#define TO_LOOP             0x10000

// SD card operation states
#define SD_STATE_IDLE        0
#define SD_STATE_INIT        1
#define SD_STATE_READY       2
#define SD_STATE_READ     0x10
#define SD_STATE_RD_RDY   0x11
#define SD_STATE_RD_BSY   0x12
#define SD_STATE_WRITE    0x20
#define SD_STATE_WR_RDY   0x21
#define SD_STATE_WR_BSY   0x22
#define SD_STATE_BOOT     0x30

// Delay between sending MMC commands
#define MMC_DELAY     0x4FF

#define SD_ADDRESS(pSd, address) \
    ( ((pSd)->totalSize == 0xFFFFFFFF) ? \
                            (address):((address) << SD_BLOCK_SIZE_BIT) )

//-----------------------------------------------------------------------------
/// MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS
/// R1 is the low order byte; R2 is the next highest byte, when present.
//-----------------------------------------------------------------------------
#define R1_SPI_IDLE             (1 << 0)
#define R1_SPI_ERASE_RESET      (1 << 1)
#define R1_SPI_ILLEGAL_COMMAND  (1 << 2)
#define R1_SPI_COM_CRC          (1 << 3)
#define R1_SPI_ERASE_SEQ        (1 << 4)
#define R1_SPI_ADDRESS          (1 << 5)
#define R1_SPI_PARAMETER        (1 << 6)
// R1 bit 7 is always zero
#define R2_SPI_CARD_LOCKED      (1 << 0)
#define R2_SPI_WP_ERASE_SKIP    (1 << 1)
#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP
#define R2_SPI_ERROR            (1 << 2)
#define R2_SPI_CC_ERROR         (1 << 3)
#define R2_SPI_CARD_ECC_ERROR   (1 << 4)
#define R2_SPI_WP_VIOLATION     (1 << 5)
#define R2_SPI_ERASE_PARAM      (1 << 6)
#define R2_SPI_OUT_OF_RANGE     (1 << 7)
#define R2_SPI_CSD_OVERWRITE    R2_SPI_OUT_OF_RANGE

// Status register constants
#define STATUS_APP_CMD          (1UL << 5)
#define STATUS_SWITCH_ERROR     (1UL << 7)
#define STATUS_READY_FOR_DATA   (1UL << 8)
#define STATUS_IDLE             (0UL << 9)
#define STATUS_READY            (1UL << 9)
#define STATUS_IDENT            (2UL << 9)
#define STATUS_STBY             (3UL << 9)
#define STATUS_TRAN             (4UL << 9)
#define STATUS_DATA             (5UL << 9)
#define STATUS_RCV              (6UL << 9)
#define STATUS_PRG              (7UL << 9)
#define STATUS_DIS              (8UL << 9)
#define STATUS_STATE          (0xFUL << 9)
#define STATUS_ERASE_RESET       (1UL << 13)
#define STATUS_WP_ERASE_SKIP     (1UL << 15)
#define STATUS_CIDCSD_OVERWRITE  (1UL << 16)
#define STATUS_OVERRUN           (1UL << 17)
#define STATUS_UNERRUN           (1UL << 18)
#define STATUS_ERROR             (1UL << 19)
#define STATUS_CC_ERROR          (1UL << 20)
#define STATUS_CARD_ECC_FAILED   (1UL << 21)
#define STATUS_ILLEGAL_COMMAND   (1UL << 22)
#define STATUS_COM_CRC_ERROR     (1UL << 23)
#define STATUS_UN_LOCK_FAILED    (1UL << 24)
#define STATUS_CARD_IS_LOCKED    (1UL << 25)
#define STATUS_WP_VIOLATION      (1UL << 26)
#define STATUS_ERASE_PARAM       (1UL << 27)
#define STATUS_ERASE_SEQ_ERROR   (1UL << 28)
#define STATUS_BLOCK_LEN_ERROR   (1UL << 29)
#define STATUS_ADDRESS_MISALIGN  (1UL << 30)
#define STATUS_ADDR_OUT_OR_RANGE (1UL << 31)

#define STATUS_STOP ( STATUS_CARD_IS_LOCKED \
                        | STATUS_COM_CRC_ERROR \
                        | STATUS_ILLEGAL_COMMAND \
                        | STATUS_CC_ERROR \
                        | STATUS_ERROR \
                        | STATUS_STATE \
                        | STATUS_READY_FOR_DATA )

#define STATUS_WRITE ( STATUS_ADDR_OUT_OR_RANGE \
                        | STATUS_ADDRESS_MISALIGN \
                        | STATUS_BLOCK_LEN_ERROR \
                        | STATUS_WP_VIOLATION \
                        | STATUS_CARD_IS_LOCKED \
                        | STATUS_COM_CRC_ERROR \
                        | STATUS_ILLEGAL_COMMAND \
                        | STATUS_CC_ERROR \
                        | STATUS_ERROR \
                        | STATUS_ERASE_RESET \
                        | STATUS_STATE \
                        | STATUS_READY_FOR_DATA )

#define STATUS_READ  ( STATUS_ADDR_OUT_OR_RANGE \
                        | STATUS_ADDRESS_MISALIGN \
                        | STATUS_BLOCK_LEN_ERROR \
                        | STATUS_CARD_IS_LOCKED \
                        | STATUS_COM_CRC_ERROR \
                        | STATUS_ILLEGAL_COMMAND \
                        | STATUS_CARD_ECC_FAILED \
                        | STATUS_CC_ERROR \
                        | STATUS_ERROR \
                        | STATUS_ERASE_RESET \
                        | STATUS_STATE \
                        | STATUS_READY_FOR_DATA )

#define STATUS_SD_SWITCH ( STATUS_ADDR_OUT_OR_RANGE \
                            | STATUS_CARD_IS_LOCKED \
                            | STATUS_COM_CRC_ERROR \
                            | STATUS_ILLEGAL_COMMAND \
                            | STATUS_CARD_ECC_FAILED \
                            | STATUS_CC_ERROR \
                            | STATUS_ERROR \
                            | STATUS_UNERRUN \
                            | STATUS_OVERRUN \
                            | STATUS_STATE)

#define STATUS_MMC_SWITCH ( STATUS_CARD_IS_LOCKED \
                            | STATUS_COM_CRC_ERROR \
                            | STATUS_ILLEGAL_COMMAND \
                            | STATUS_CC_ERROR \
                            | STATUS_ERROR \
                            | STATUS_ERASE_RESET \
                            | STATUS_STATE \
                            | STATUS_READY_FOR_DATA \
                            | STATUS_SWITCH_ERROR )

//                            | (0x3UL << 12) /* IO_CURRENT_STATE */
#define STATUS_SDIO_CMD52 ( (1UL << 15) /* COM_CRC_ERROR */ \
                            | (1UL << 14) /* ILLEGAL_COMMAND */ \
                            | (1UL << 11) /* ERRIR */ \
                            | (1UL <<  9) /* FUNCTION_NUMBER */ \
                            | (1UL <<  8) /* OUT_OF_RANGE */)

//-----------------------------------------------------------------------------
/// OCR Register
//-----------------------------------------------------------------------------
#define AT91C_VDD_16_17          (1UL << 4)
#define AT91C_VDD_17_18          (1UL << 5)
#define AT91C_VDD_18_19          (1UL << 6)
#define AT91C_VDD_19_20          (1UL << 7)
#define AT91C_VDD_20_21          (1UL << 8)
#define AT91C_VDD_21_22          (1UL << 9)
#define AT91C_VDD_22_23          (1UL << 10)
#define AT91C_VDD_23_24          (1UL << 11)
#define AT91C_VDD_24_25          (1UL << 12)
#define AT91C_VDD_25_26          (1UL << 13)
#define AT91C_VDD_26_27          (1UL << 14)
#define AT91C_VDD_27_28          (1UL << 15)
#define AT91C_VDD_28_29          (1UL << 16)
#define AT91C_VDD_29_30          (1UL << 17)
#define AT91C_VDD_30_31          (1UL << 18)
#define AT91C_VDD_31_32          (1UL << 19)
#define AT91C_VDD_32_33          (1UL << 20)
#define AT91C_VDD_33_34          (1UL << 21)
#define AT91C_VDD_34_35          (1UL << 22)
#define AT91C_VDD_35_36          (1UL << 23)
#define AT91C_SDIO_MP            (1UL << 27)
#define AT91C_SDIO_NF            (7UL << 28)
#define AT91C_MMC_OCR_BIT2930    (3UL << 29)
#define AT91C_CARD_POWER_UP_BUSY (1UL << 31)

#define AT91C_MMC_HOST_VOLTAGE_RANGE     (AT91C_VDD_27_28 +\
                                          AT91C_VDD_28_29 +\
                                          AT91C_VDD_29_30 +\
                                          AT91C_VDD_30_31 +\
                                          AT91C_VDD_31_32 +\
                                          AT91C_VDD_32_33)

// MMC OCR response for Bit 29, 30
#define AT91C_MMC_NORM_DENSITY   (0x0UL << 29)
#define AT91C_MMC_HIGH_DENSITY   (0x2UL << 29)

#define AT91C_CCS    (1 << 30)

// MCI_CMD Register Value
#define AT91C_POWER_ON_INIT      (0 | AT91C_MCI_TRCMD_NO    \
                                    | AT91C_MCI_SPCMD_INIT  \
                                    | AT91C_MCI_OPDCMD)

//-----------------------------------------------------------------------------
/// eMMC CMD6
//-----------------------------------------------------------------------------
#define AT91C_EMMC_CMD6ARG_ACCESS_BITS        (0x3UL << 24)
#define AT91C_EMMC_CMD6ARG_ACCESS_SHIFT       (24)
// change command sets
#define AT91C_EMMC_CMD6ARG_ACCESS_CMDSETS     (0x0UL << 24)
// set bits in the value field
#define AT91C_EMMC_CMD6ARG_ACCESS_SETBITS     (0x1UL << 24)
// clear bits in the value field
#define AT91C_EMMC_CMD6ARG_ACCESS_CLRBITS     (0x2UL << 24)
// the value field is written into the pointed byte
#define AT91C_EMMC_CMD6ARG_ACCESS_WRBYTES     (0x3UL << 24) 
#define AT91C_EMMC_CMD6ARG_INDEX_BITS         (0xffUL << 16)
#define AT91C_EMMC_CMD6ARG_INDEX_SHIFT        (16)
#define AT91C_EMMC_CMD6ARG_VALUE_BITS         (0xffUL << 8)
#define AT91C_EMMC_CMD6ARG_VALUE_SHIFT        (8)
#define AT91C_EMMC_CMD6ARG_CMDSET_BITS        (0x7UL << 0)
#define AT91C_EMMC_CMD6ARG_CMDSET_SHIFT       (0)

//-----------------------------------------------------------------------------
// Command Classes
//-----------------------------------------------------------------------------
//
// Class 0, 2, 4, 5, 7 and 8 are mandatory and shall be supported by
// all SD Memory Cards.
//
// Basic Commands (class 0)
//
// Cmd0 MCI + SPI
#define   AT91C_GO_IDLE_STATE_CMD     (0 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE )
// Cmd1 SPI
#define   AT91C_MMC_SEND_OP_COND_CMD  (1 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_48 \
                                         | AT91C_MCI_OPDCMD)
// Cmd2 MCI
#define   AT91C_ALL_SEND_CID_CMD      (2 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_OPDCMD \
                                         | AT91C_MCI_RSPTYP_136 )
// Cmd3 MCI
#define   AT91C_SET_RELATIVE_ADDR_CMD (3 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_48 \
                                         | AT91C_MCI_OPDCMD \
                                         | AT91C_MCI_MAXLAT )
// Cmd4 MCI
#define AT91C_SET_DSR_CMD             (4 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_NO \
                                         | AT91C_MCI_MAXLAT )
// Cmd5 MCI
#define AT91C_IO_SEND_OP_COND_CMD     (5 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_48 \
                                         | AT91C_MCI_OPDCMD )
// Cmd6 SD/MMC
#if defined(MCI2_INTERFACE)
#define AT91C_MMC_SWITCH_CMD          (6 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_R1B \
                                         | AT91C_MCI_MAXLAT )
#else
#define AT91C_MMC_SWITCH_CMD          (6 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_48 \
                                         | AT91C_MCI_MAXLAT )
#endif
#define AT91C_SD_SWITCH_CMD           (6 | AT91C_MCI_TRCMD_START \
                                         | AT91C_MCI_TRTYP_BLOCK \
                                         | AT91C_MCI_TRDIR_READ \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_48 \
                                         | AT91C_MCI_MAXLAT )
// cmd7 MCI
#define   AT91C_SEL_DESEL_CARD_CMD    (7 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_48 \
                                         | AT91C_MCI_MAXLAT )
#define   AT91C_SEL_CARD_CMD          (7 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_48 \
                                         | AT91C_MCI_MAXLAT )
#define   AT91C_DESEL_CARD_CMD        (7 | AT91C_MCI_TRCMD_NO \
                                         | AT91C_MCI_SPCMD_NONE \
                                         | AT91C_MCI_RSPTYP_NO \
                                         | AT91C_MCI_MAXLAT )
// Cmd8 MCI + SPI
#define   AT91C_SEND_IF_COND          (8  | AT91C_MCI_TRCMD_NO \
                                          | AT91C_MCI_SPCMD_NONE \
                                          | AT91C_MCI_RSPTYP_48 \
                                          | AT91C_MCI_MAXLAT )
// Cmd9 MCI + SPI
#define   AT91C_SEND_CSD_CMD          (9  | AT91C_MCI_TRCMD_NO \
                                          | AT91C_MCI_SPCMD_NONE \
                                          | AT91C_MCI_RSPTYP_136 \
                                          | AT91C_MCI_MAXLAT )
// Cmd10 MCI + SPI
#define   AT91C_SEND_CID_CMD          (10 | AT91C_MCI_TRCMD_NO \
                                          | AT91C_MCI_SPCMD_NONE \
                                          | AT91C_MCI_RSPTYP_136 \
                                          | AT91C_MCI_MAXLAT )
// Cmd12 MCI + SPI
#if defined(MCI2_INTERFACE)
#define   AT91C_STOP_TRANSMISSION_CMD (12 | AT91C_MCI_TRCMD_STOP \
                                          | AT91C_MCI_SPCMD_NONE \
                                          | AT91C_MCI_RSPTYP_R1B \
                                          | AT91C_MCI_MAXLAT )
#else
#define   AT91C_STOP_TRANSMISSION_CMD (12 | AT91C_MCI_TRCMD_STOP \
                                          | AT91C_MCI_SPCMD_NONE \
                                          | AT91C_MCI_RSPTYP_48 \
                                          | AT91C_MCI_MAXLAT )
#endif
// Cmd13 MCI + SPI
#define   AT91C_SEND_STATUS_CMD       (13 | AT91C_MCI_TRCMD_NO \
                                          | AT91C_MCI_SPCMD_NONE \
                                          | AT91C_MCI_RSPTYP_48 \
                                          | AT91C_MCI_MAXLAT )
// Cmd15 MCI
#define AT91C_GO_INACTIVE_STATE_CMD   (15 | AT91C_MCI_RSPTYP_NO )

// Cmd58 SPI
#define   AT91C_READ_OCR_CMD          (58 | AT91C_MCI_RSPTYP_48 \
                                          | AT91C_MCI_SPCMD_NONE \
                                          | AT91C_MCI_MAXLAT )
// Cmd59 SPI
#define   AT91C_CRC_ON_OFF_CMD        (59 | AT91C_MCI_RSPTYP_48 \
                                          | AT91C_MCI_SPCMD_NONE \
                                          | AT91C_MCI_MAXLAT )

//*------------------------------------------------
//* Class 2 commands: Block oriented Read commands
//*------------------------------------------------

// Cmd8 for MMC
#define AT91C_SEND_EXT_CSD_CMD          (8 | AT91C_MCI_SPCMD_NONE \
                                           | AT91C_MCI_OPDCMD_PUSHPULL \
                                           | AT91C_MCI_RSPTYP_48 \
                                           | AT91C_MCI_TRCMD_START \
                                           | AT91C_MCI_TRTYP_BLOCK \
                                           | AT91C_MCI_TRDIR \
                                           | AT91C_MCI_MAXLAT)

// Cmd16
#define AT91C_SET_BLOCKLEN_CMD          (16 | AT91C_MCI_TRCMD_NO \
                                            | AT91C_MCI_SPCMD_NONE \
                                            | AT91C_MCI_RSPTYP_48 \
                                            | AT91C_MCI_MAXLAT )
// Cmd17
#define AT91C_READ_SINGLE_BLOCK_CMD     (17 | AT91C_MCI_SPCMD_NONE \
                                            | AT91C_MCI_RSPTYP_48 \
                                            | AT91C_MCI_TRCMD_START \
                                            | AT91C_MCI_TRTYP_BLOCK \
                                            | AT91C_MCI_TRDIR \
                                            | AT91C_MCI_MAXLAT)
// Cmd18
#define AT91C_READ_MULTIPLE_BLOCK_CMD   (18 | AT91C_MCI_SPCMD_NONE \
                                            | AT91C_MCI_RSPTYP_48 \
                                            | AT91C_MCI_TRCMD_START \
                                            | AT91C_MCI_TRTYP_MULTIPLE \
                                            | AT91C_MCI_TRDIR \
                                            | AT91C_MCI_MAXLAT)

//*------------------------------------------------
//* Class 4 commands: Block oriented write commands
//*------------------------------------------------
// Cmd24
#define AT91C_WRITE_BLOCK_CMD           (24 | AT91C_MCI_SPCMD_NONE \
                                            | AT91C_MCI_RSPTYP_48 \
                                            | AT91C_MCI_TRCMD_START \
                                            | (AT91C_MCI_TRTYP_BLOCK \
                                                & ~(AT91C_MCI_TRDIR)) \
                                            | AT91C_MCI_MAXLAT)
// Cmd25
#define AT91C_WRITE_MULTIPLE_BLOCK_CMD  (25 | AT91C_MCI_SPCMD_NONE \
                                            | AT91C_MCI_RSPTYP_48 \
                                            | AT91C_MCI_TRCMD_START \
                                            | (AT91C_MCI_TRTYP_MULTIPLE \
                                                & ~(AT91C_MCI_TRDIR)) \
                                            | AT91C_MCI_MAXLAT)
// Cmd27
#define AT91C_PROGRAM_CSD_CMD           (27 | AT91C_MCI_RSPTYP_48 )

//*----------------------------------------
//* Class 5 commands: Erase commands
//*----------------------------------------
// Cmd32
//#define AT91C_TAG_SECTOR_START_CMD          (32 | AT91C_MCI_SPCMD_NONE  | AT91C_MCI_RSPTYP_48   | AT91C_MCI_TRCMD_NO    | AT91C_MCI_MAXLAT)
// Cmd33
//#define AT91C_TAG_SECTOR_END_CMD            (33 | AT91C_MCI_SPCMD_NONE  | AT91C_MCI_RSPTYP_48   | AT91C_MCI_TRCMD_NO    | AT91C_MCI_MAXLAT)
// Cmd38
//#define AT91C_ERASE_CMD                     (38 | AT91C_MCI_SPCMD_NONE  | AT91C_MCI_RSPTYP_48   | AT91C_MCI_TRCMD_NO    | AT91C_MCI_MAXLAT )

//*----------------------------------------
//* Class 7 commands: Lock commands
//*----------------------------------------
// Cmd42
//#define AT91C_LOCK_UNLOCK           (42 | AT91C_MCI_SPCMD_NONE  | AT91C_MCI_RSPTYP_48   | AT91C_MCI_TRCMD_NO    | AT91C_MCI_MAXLAT) // not tested

//*-----------------------------------------------
// Class 8 commands: Application specific commands
//*-----------------------------------------------
// Cmd55
#define AT91C_APP_CMD               (55 | AT91C_MCI_SPCMD_NONE \
                                        | AT91C_MCI_RSPTYP_48 \
                                        | AT91C_MCI_TRCMD_NO \
                                        | AT91C_MCI_MAXLAT)
// cmd 56
//#define AT91C_GEN_CMD               (56 | AT91C_MCI_SPCMD_NONE  | AT91C_MCI_RSPTYP_48   | AT91C_MCI_TRCMD_NO | AT91C_MCI_MAXLAT)    // not tested
// ACMD6
#define AT91C_SD_SET_BUS_WIDTH_CMD          (6  | AT91C_MCI_SPCMD_NONE \
                                                | AT91C_MCI_RSPTYP_48 \
                                                | AT91C_MCI_TRCMD_NO \
                                                | AT91C_MCI_MAXLAT)
// ACMD13
#define AT91C_SD_STATUS_CMD                 (13 | AT91C_MCI_SPCMD_NONE \
                                                | AT91C_MCI_RSPTYP_48 \
                                                | AT91C_MCI_TRCMD_START \
                                                | AT91C_MCI_TRTYP_BLOCK \
                                                | AT91C_MCI_TRDIR_READ \
                                                | AT91C_MCI_MAXLAT)
// ACMD22
//#define AT91C_SD_SEND_NUM_WR_BLOCKS_CMD     (22 | AT91C_MCI_SPCMD_NONE  | AT91C_MCI_RSPTYP_48   | AT91C_MCI_TRCMD_NO    | AT91C_MCI_MAXLAT)
// ACMD23
//#define AT91C_SD_SET_WR_BLK_ERASE_COUNT_CMD (23 | AT91C_MCI_SPCMD_NONE  | AT91C_MCI_RSPTYP_48   | AT91C_MCI_TRCMD_NO    | AT91C_MCI_MAXLAT)
// ACMD41
#define AT91C_SD_APP_OP_COND_CMD            (41 | AT91C_MCI_SPCMD_NONE \
                                                | AT91C_MCI_RSPTYP_48 \
                                                | AT91C_MCI_TRCMD_NO )
// ACMD42
//#define AT91C_SD_SET_CLR_CARD_DETECT_CMD    (42 | AT91C_MCI_SPCMD_NONE  | AT91C_MCI_RSPTYP_48   | AT91C_MCI_TRCMD_NO    | AT91C_MCI_MAXLAT)
// ACMD51
#define AT91C_SD_SEND_SCR_CMD               (51 | AT91C_MCI_SPCMD_NONE \
                                                | AT91C_MCI_RSPTYP_48 \
                                                | AT91C_MCI_TRCMD_START \
                                                | AT91C_MCI_TRDIR_READ \
                                                | AT91C_MCI_TRTYP_BLOCK \
                                                | AT91C_MCI_MAXLAT)

// SDIO commands
// CMD5, R4
#define AT91C_SDIO_SEND_OP_COND (5 | AT91C_MCI_TRCMD_NO \
                                   | AT91C_MCI_SPCMD_NONE \
                                   | AT91C_MCI_RSPTYP_48 \
                                   | AT91C_MCI_OPDCMD)

// CMD52, R5
#define AT91C_SDIO_IO_RW_DIRECT (52| AT91C_MCI_TRCMD_NO \
                                   | AT91C_MCI_SPCMD_NONE \
                                   | AT91C_MCI_RSPTYP_48 \
                                   | AT91C_MCI_MAXLAT )

// CMD53, R5
#define AT91C_SDIO_IO_RW_EXTENDED (53 | AT91C_MCI_SPCMD_NONE \
                                      | AT91C_MCI_RSPTYP_48 \
                                      | AT91C_MCI_TRCMD_START \
                                      | AT91C_MCI_MAXLAT)

#ifdef AT91C_MCI_SPCMD_BOOTREQ
// BOOTREQ
#define AT91C_BOOTREQ           (AT91C_MCI_SPCMD_BOOTREQ \
                                    | AT91C_MCI_TRDIR_READ \
                                    | AT91C_MCI_TRCMD_START \
                                    | AT91C_MCI_MAXLAT)
// BOOTEND
#define AT91C_BOOTEND           (AT91C_MCI_SPCMD_BOOTEND \
                                    | AT91C_MCI_OPDCMD_PUSHPULL)
#endif
// Optional commands
#define SD_ACMD6_SUPPORT        (1UL << 0)
#define SD_ACMD13_SUPPORT       (1UL << 1)
#define SD_ACMD41_SUPPORT       (1UL << 2)
#define SD_ACMD51_SUPPORT       (1UL << 3)

#define SD_CMD16_SUPPORT        (1UL << 8)

//------------------------------------------------------------------------------
//         Local functions
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// DecodeR1
/// \param R1  
//------------------------------------------------------------------------------
void DecodeR1(unsigned char R1)
{
    if( (R1 & R1_SPI_IDLE)==R1_SPI_IDLE) {
        TRACE_DEBUG("R1_SPI_IDLE\n\r");
    }
    if( (R1 & R1_SPI_ERASE_RESET)==R1_SPI_ERASE_RESET) {
        TRACE_DEBUG("R1_SPI_ERASE_RESET\n\r");
    }
    if( (R1 & R1_SPI_ILLEGAL_COMMAND)==R1_SPI_ILLEGAL_COMMAND) {
        TRACE_DEBUG("R1_SPI_ILLEGAL_COMMAND\n\r");
    }
    if( (R1 & R1_SPI_COM_CRC)==R1_SPI_COM_CRC) {
        TRACE_DEBUG("R1_SPI_COM_CRC\n\r");
    }
    if( (R1 & R1_SPI_ERASE_SEQ)==R1_SPI_ERASE_SEQ) {
        TRACE_DEBUG("R1_SPI_ERASE_SEQ\n\r");
    }
    if( (R1 & R1_SPI_ADDRESS)==R1_SPI_ADDRESS) {
        TRACE_DEBUG("R1_SPI_ADDRESS\n\r");
    }
    if( (R1 & R1_SPI_PARAMETER)==R1_SPI_PARAMETER) {
        TRACE_DEBUG("R1_SPI_PARAMETER\n\r");
    }
}

//------------------------------------------------------------------------------
/// DecodeR2
/// \param R2  
//------------------------------------------------------------------------------
void DecodeR2(unsigned char R2)
{
    if( (R2 & R2_SPI_CARD_LOCKED)==R2_SPI_CARD_LOCKED) {
        TRACE_DEBUG("R2_SPI_CARD_LOCKED\n\r");
    }
    if( (R2 & R2_SPI_WP_ERASE_SKIP)==R2_SPI_WP_ERASE_SKIP) {
        TRACE_DEBUG("R2_SPI_WP_ERASE_SKIP/R2_SPI_LOCK_UNLOCK_FAIL\n\r");
    }
    if( (R2 & R2_SPI_ERROR)==R2_SPI_ERROR) {
        TRACE_DEBUG("R2_SPI_ERROR\n\r");
    }
    if( (R2 & R2_SPI_CC_ERROR)==R2_SPI_CC_ERROR) {
        TRACE_DEBUG("R2_SPI_CC_ERROR\n\r");
    }
    if( (R2 & R2_SPI_CARD_ECC_ERROR)==R2_SPI_CARD_ECC_ERROR) {
        TRACE_DEBUG("R2_SPI_CARD_ECC_ERROR\n\r");
    }
    if( (R2 & R2_SPI_WP_VIOLATION)==R2_SPI_WP_VIOLATION) {
        TRACE_DEBUG("R2_SPI_WP_VIOLATION\n\r");
    }
    if( (R2 & R2_SPI_ERASE_PARAM)==R2_SPI_ERASE_PARAM) {
        TRACE_DEBUG("R2_SPI_ERASE_PARAM\n\r");
    }
    if( (R2 & R2_SPI_OUT_OF_RANGE)==R2_SPI_OUT_OF_RANGE) {
        TRACE_DEBUG("R2_SPI_OUT_OF_RANGE/R2_SPI_CSD_OVERWRITE\n\r");
    }
}

//------------------------------------------------------------------------------
/// Get Trans Speed Value (Kbit/s)
/// \param tranSpeed The TRAN_SPEED value from SD(IO)/MMC enum information.
/// \param unitList  Transfer rate units (Kbit/s), 4 units listed.
/// \param nbUnits   Transfer rate units list size.
/// \param codeList  Time value codes list, 16 codes listed.
//------------------------------------------------------------------------------
static unsigned int MmcGetTranSpeed(unsigned int tranSpeed,
                            const unsigned int* unitList, unsigned int nbUnits,
                            const unsigned int* codeList)
{
    unsigned int unit, value;
    unit = tranSpeed & 0x7;
    if (unit < nbUnits) unit = unitList[unit];
    else                return 0;
    value = (tranSpeed >> 3) & 0xF;
    if (value < 16)   value = codeList[value];
    else                return 0;
    return (unit * value);
}

//------------------------------------------------------------------------------
/// Get Trans Speed Value
/// \param pSd    
//------------------------------------------------------------------------------
void GetTransSpeedValue(SdCard *pSd)
{
    // CSD register, TRANS_SPEED bit
    const unsigned int units[4] = {10, 100, 1000, 10000 }; // *Kbit/s 
    const unsigned int values_emmc[16] = {0, 10, 12, 13, 15, 20,
                                          26, 30, 35, 40, 45, 52,
                                          55, 60, 70, 80};
    const unsigned int values_sdmmc[16] = {0, 10, 12, 13, 15, 20,
                                           25, 30, 35, 40, 45, 50,
                                           55, 60, 70, 80};
  #if 0
    unsigned int unit, value;
    unit = (SD_CSD_TRAN_SPEED(pSd) & 0x7);
    if(unit < 4)    unit  = units[unit];
    else            return;
    value = (SD_CSD_TRAN_SPEED(pSd) >> 3) & 0xF;
    if (value < 16) {
        if (pSd->cardType >= CARD_MMC && SD_CID_BGA(pSd) == 1) {
            value = values_emmc[value];
        }
        else
            value = values_sdmmc[value];
    }
    else            return;
    pSd->transSpeed = (unit * value);
  #else
    pSd->transSpeed = MmcGetTranSpeed(SD_CSD_TRAN_SPEED(pSd),
                                      units, 4,
                                      (pSd->cardType >= CARD_MMC
                                       && SD_CID_BGA(pSd) == 1) ?
                                       values_emmc : values_sdmmc);
  #endif
    if (pSd->cardType >= CARD_MMC && SD_EXTCSD_HS_TIMING(pSd)) {
        pSd->transSpeed *= 2;
    }
    TRACE_WARNING_WP("-I- SD/MMC TRANS SPEED %d KBit/s\r\n", pSd->transSpeed);
    pSd->transSpeed *= 1000;  
}

#if 1
//------------------------------------------------------------------------------
/// Reset the SdCmd
//------------------------------------------------------------------------------
static void ResetCommand(SdCmd *pCommand)
{
  #if 0
    unsigned char* p = (unsigned char*)pCommand;
    unsigned int   l = sizeof(SdCmd);
    while(l --) *p++ = 0;
  #else
    pCommand->cmd       = 0;
    pCommand->arg       = 0;
    pCommand->pData     = 0;
    pCommand->blockSize = 0;
    pCommand->nbBlock   = 0;
    pCommand->pResp     = 0;
    pCommand->callback  = 0;
    pCommand->pArg      = 0;
    pCommand->resType   = 0;
    pCommand->dataTran  = 0;
    pCommand->tranType  = 0;
    pCommand->isRead    = 0;
    pCommand->status    = 0;
  #endif
}
#else
// GNU halt on memset now
# define ResetCommand(pCommand) memset(pCommand, 0, sizeof(SdCmd))
#endif

//------------------------------------------------------------------------------
/// Delay some loop
//------------------------------------------------------------------------------
static void Delay(volatile unsigned int loop)
{
    for(;loop > 0; loop --);
}

//------------------------------------------------------------------------------
/// Sends the current SD card driver command to the card.
/// Returns 0 if successful; Otherwise, returns the transfer status code or
/// SD_ERROR_DRIVER if there was a problem with the SD transfer.
/// \param pSd  Pointer to a SdCard driver instance.
//------------------------------------------------------------------------------
static unsigned char SendCommand(SdCard *pSd)
{
    SdCmd *pCommand = &(pSd->command);
    SdDriver *pSdDriver = pSd->pSdDriver;
    unsigned char error;

    // Send command
    error = MCI_SendCommand((Mci *)pSdDriver, (MciCmd *)pCommand);
    if (error) {
        TRACE_ERROR("MCI SendCommand: Failed to send command (%d)\n\r", error);
        return SD_ERROR_DRIVER;
    }

    // Wait for command to complete (if no callback defined)
    if (pCommand->callback == 0) {
        while (!MCI_IsTxComplete((Mci *)pSdDriver));
    }

    // Check for using fifo to transfer data
  #if !defined(MCI_DMA_ENABLE) && defined(MCI2_INTERFACE)
    if (pCommand->dataTran && pCommand->nbBlock) {
        MCI_FifoTransfer((Mci *)pSdDriver, (MciCmd *)pCommand);
    }
  #endif

    return pCommand->status;
}

//------------------------------------------------------------------------------
/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp
/// up time.
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SdCard driver instance.
//------------------------------------------------------------------------------
static unsigned char Pon(SdCard *pSd)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned int response;
    unsigned char error;

    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_POWER_ON_INIT;
    pCommand->pResp = &response;

    // Send command
    error =  SendCommand(pSd);
    return error;
}

#if defined(MCI2_INTERFACE) && defined(AT91C_MCI_SPCMD_BOOTREQ)
//------------------------------------------------------------------------------
/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp
/// up time, CMD keeps low so that the device run in boot mode.
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SdCard driver instance.
//------------------------------------------------------------------------------
static unsigned char PonBoot(SdCard *pSd)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned int response;
    unsigned char error;

    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_POWER_ON_INIT;
    pCommand->pResp = &response;

    // Send command
    error =  SendCommand(pSd);
    return error;
}
#endif

//------------------------------------------------------------------------------
/// Resets all cards to idle state
/// \param pSd  Pointer to a SdCard driver instance.
/// \param arg  Argument used.
/// \return the command transfer result (see SendCommand).
//------------------------------------------------------------------------------
static unsigned char Cmd0(SdCard *pSd, unsigned int arg)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned int response;
    unsigned char error;

    TRACE_DEBUG("Cmd0()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_GO_IDLE_STATE_CMD;
    pCommand->arg = arg;
    pCommand->pResp = &response;

    // send command
    error =  SendCommand(pSd);
    return error;
}

//------------------------------------------------------------------------------
/// MMC send operation condition command.
/// Sends host capacity support information and activates the card's
/// initialization process.
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SdCard driver instance.
/// \param hdSupport  Indicate whether the host support high density MMC.
/// \param pHdSupport  Indicate whether the card is a high density MMC.
//------------------------------------------------------------------------------
static unsigned char Cmd1(SdCard *pSd,
                          unsigned char hdSupport,
                          unsigned char *pHdSupport)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Cmd1()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_MMC_SEND_OP_COND_CMD;
    pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE;
    if(hdSupport) {
        pCommand->arg |= AT91C_MMC_HIGH_DENSITY;
    }
    else {
        pCommand->arg |= AT91C_MMC_NORM_DENSITY;
    }
    pCommand->resType = 3;
    pCommand->pResp = &response;

    // send command
    *pHdSupport = 0; 
    error = SendCommand(pSd);
    if (error) {
        return error;
    }
    if ((response & AT91C_CARD_POWER_UP_BUSY) == AT91C_CARD_POWER_UP_BUSY) {        if((response & AT91C_MMC_OCR_BIT2930) == AT91C_MMC_HIGH_DENSITY) {
            *pHdSupport = 1;
        }
        return 0;
    }
    else {
        return SD_ERROR_DRIVER;
    }
}

//------------------------------------------------------------------------------
/// Asks any card to send the CID numbers
/// on the CMD line (any card that is
/// connected to the host will respond)
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SD card driver instance.
/// \param pCid  Buffer for storing the CID numbers.
//------------------------------------------------------------------------------
static unsigned char Cmd2(SdCard *pSd)
{
    SdCmd *pCommand = &(pSd->command);

    TRACE_DEBUG("Cmd2()\n\r");
    ResetCommand(pCommand);
    // Fill the command information
    pCommand->cmd = AT91C_ALL_SEND_CID_CMD;
    pCommand->resType = 2;
    pCommand->pResp = pSd->cid;

    // Send the command
    return SendCommand(pSd);
}

//------------------------------------------------------------------------------
/// Ask the card to publish a new relative address (RCA)
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
static unsigned char Cmd3(SdCard *pSd)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned int cardAddress;
    unsigned char error;

    TRACE_DEBUG("Cmd3()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SET_RELATIVE_ADDR_CMD;

    // Assign relative address to MMC card
    if ((pSd->cardType == CARD_MMC) || (pSd->cardType == CARD_MMCHD)) {
        pCommand->arg = (0x1 << 16);
    }
    pCommand->resType = 1;
    pCommand->pResp = &cardAddress;

    // Send command
    error = SendCommand(pSd);
    if (error) {
        return error;
    }

    // Save card address in driver
    if ( (pSd->cardType == CARD_SD)
        || (pSd->cardType == CARD_SDHC)) {
        pSd->cardAddress = (cardAddress >> 16) & 0xFFFF;
    }
    else if (pSd->cardType >= CARD_SDIO) {
        pSd->cardAddress = (cardAddress >> 16) & 0xFFFF;
    }
    else {
        // Default MMC RCA is 0x0001
        pSd->cardAddress = 1;
    }

    return 0;
}

#if MCI_SDIO_ENABLE
//------------------------------------------------------------------------------
/// SDIO send operation condition command.
/// Sends host capacity support information and activates the card's
/// initialization process.
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SdCard driver instance.
/// \param pIo  Pointer to data send as well as response buffer.
//------------------------------------------------------------------------------
static unsigned char Cmd5(SdCard *pSd,
                          unsigned int *pIo)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;

    TRACE_DEBUG("Cmd5()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_IO_SEND_OP_COND_CMD;
    pCommand->arg = *pIo;
    pCommand->resType = 4;
    pCommand->pResp = pIo;

    // send command
    error = SendCommand(pSd);
    return error;
}
#endif

//------------------------------------------------------------------------------
/// Command toggles a card between the
/// stand-by and transfer states or between
/// the programming and disconnect states.
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SD card driver instance.
/// \param address  Relative Card Address (0 deselects all).
//------------------------------------------------------------------------------
static unsigned char Cmd7(SdCard *pSd, unsigned int address)
{
    SdCmd *pCommand = &(pSd->command);

    TRACE_DEBUG("Cmd7()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SEL_DESEL_CARD_CMD;
    pCommand->arg = address << 16;
    pCommand->resType = 1;

    // Send command
    return SendCommand(pSd);
}

//------------------------------------------------------------------------------
/// Switches the mode of operation of the selected card (SD/MMC) or
/// modifies the EXT_CSD registers (for MMC only).
/// CMD6 is valid under the "trans" state.
/// \return The command transfer result (see SendCommand).
/// \param  pSd         Pointer to a SD/MMC card driver instance.
/// \param  pSwitchArg  Pointer to a MmcCmd6Arg instance.
/// \param  pStatus     Pointer to where the 512bit status is returned.
/// \param  pResp       Pointer to where the response is returned.
//------------------------------------------------------------------------------
static unsigned char Cmd6(SdCard *pSd,
                          const void * pSwitchArg,
                          unsigned int  * pStatus,
                          unsigned int  * pResp)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned int  response;
    unsigned char error;
    SdCmd6Arg  * pSdSwitch;
    MmcCmd6Arg * pMmcSwitch;

    SANITY_CHECK(pSd);
    SANITY_CHECK(pSwitchArg);

    TRACE_DEBUG("CMD6()\n\r");

    ResetCommand(pCommand);

    if (pSd->cardType >= CARD_MMC) {
        pMmcSwitch = (MmcCmd6Arg*)pSwitchArg;
        // R1b response
        pCommand->cmd = AT91C_MMC_SWITCH_CMD;
        pCommand->resType = 1;
        pCommand->arg =   (pMmcSwitch->access << 24)
                        | (pMmcSwitch->index  << 16)
                        | (pMmcSwitch->value  <<  8)
                        | (pMmcSwitch->cmdSet <<  0);
    }
    else if (pSd->cardType >= CARD_SD) {

        pSdSwitch = (SdCmd6Arg*)pSwitchArg;
        // R1 response & 512 bits of status on DAT
        pCommand->cmd = AT91C_SD_SWITCH_CMD;
        pCommand->resType = 1;
        pCommand->arg =   (pSdSwitch->mode << 31)
                        | (pSdSwitch->reserved << 30)
                        | (pSdSwitch->reserveFG6 << 20)
                        | (pSdSwitch->reserveFG5 << 16)
                        | (pSdSwitch->reserveFG4 << 12)
                        | (pSdSwitch->reserveFG3 <<  8)
                        | (pSdSwitch->command << 4)
                        | (pSdSwitch->accessMode << 0);
        if (pStatus) {
            pCommand->blockSize = 512 / 8;
            pCommand->nbBlock = 1;
            pCommand->pData = (unsigned char*)pStatus;

            pCommand->dataTran = 1;
            pCommand->isRead = 1;
            pCommand->tranType = MCI_NEW_TRANSFER;
        }
    }
    pCommand->pResp = &response;

    TRACE_INFO("CMD6(%d) arg 0x%X\n\r", pSd->cardType, pCommand->arg);

    error = SendCommand(pSd);

    if (error)
        return error;
    else if (pResp)
        *pResp = response;

    return 0;
}

//------------------------------------------------------------------------------
/// SD:  Sends SD Memory Card interface condition, which includes host supply
///      voltage information and asks the card whether card supports voltage.
///      Should be performed at initialization time to detect the card type.
/// MMC: SEND_EXT_CSD, to get EXT_CSD register as a block of data.
///      Valid under "trans" state.
/// \param pSd   Pointer to a SD card driver instance.
/// \param sdCmd For SD Memory Card interface condition 
/// \param arg   Expected supply voltage(SD) or 512 byte buffer pointer (MMC).
/// \return 0 if successful;
///         otherwise returns SD_ERROR_NORESPONSE if the card did not answer
///         the command, or SD_ERROR_DRIVER.
//------------------------------------------------------------------------------
static unsigned char Cmd8(SdCard *pSd,
                          unsigned char sdCmd,
                          void* arg)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned int response;
    unsigned char error;
    unsigned char supplyVoltage = (unsigned char)((unsigned int)arg);

    TRACE_DEBUG("Cmd8()\n\r");
    ResetCommand(pCommand);

    if (sdCmd) {

        // Fill command information
        pCommand->cmd = AT91C_SEND_IF_COND;
        pCommand->arg = (supplyVoltage << 8) | (0xAA);
        pCommand->resType = 7;

        TRACE_DEBUG("supplyVoltage: 0x%x\n\r", supplyVoltage);
    }
    else {

        pCommand->cmd = AT91C_SEND_EXT_CSD_CMD;
        pCommand->resType = 1;

        pCommand->blockSize = SD_BLOCK_SIZE;
        pCommand->nbBlock = 512 / SD_BLOCK_SIZE;
        pCommand->pData = arg;

        pCommand->dataTran = 1;
        pCommand->isRead = 1;
        pCommand->tranType = MCI_NEW_TRANSFER;
    }
    pCommand->pResp = &response;

    // Send command
    error = SendCommand(pSd);

    if (sdCmd) {

        // Check result
        if (error == MCI_STATUS_NORESPONSE) {

            return SD_ERROR_NORESPONSE;
        }
        // SD_R7
        // Bit 0 - 7: check pattern (echo-back)
        // Bit 8 -11: voltage accepted
        else if (!error &&
                ((response & 0x00000FFF) == ((supplyVoltage << 8) | 0xAA))) {
            return 0;
        }
        else {
            return SD_ERROR_DRIVER;
        }
    }

    return error;
}

//------------------------------------------------------------------------------
/// Addressed card sends its card-specific
/// data (CSD) on the CMD line.
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
static unsigned char Cmd9(SdCard *pSd)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;

    TRACE_DEBUG("Cmd9()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SEND_CSD_CMD;
    pCommand->arg = pSd->cardAddress << 16;
    pCommand->resType = 2;
    pCommand->pResp = pSd->csd;

    // Send command
    error = SendCommand(pSd);
    return error;
}

//------------------------------------------------------------------------------
/// Forces the card to stop transmission
/// \param pSd      Pointer to a SD card driver instance.
/// \param stopRead Stop reading stream/writing stream.
/// \param pStatus  Pointer to a status variable.
//------------------------------------------------------------------------------
static unsigned char Cmd12(SdCard *pSd,
                           unsigned char stopRead,
                           unsigned int *pStatus)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Cmd12()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_STOP_TRANSMISSION_CMD;
    pCommand->isRead = stopRead;
    pCommand->tranType = MCI_STOP_TRANSFER;
    pCommand->resType = 1;
    pCommand->pResp = &response;

    // Send command
    error = SendCommand(pSd);
    if (pStatus) *pStatus = response;
    return error;
}

//------------------------------------------------------------------------------
/// Addressed card sends its status register.
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SD card driver instance.
/// \param pStatus  Pointer to a status variable.
//------------------------------------------------------------------------------
static unsigned char Cmd13(SdCard *pSd, unsigned int *pStatus)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;

    TRACE_DEBUG("Cmd13()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SEND_STATUS_CMD;
    pCommand->arg = pSd->cardAddress << 16;
    pCommand->resType = 1;
    pCommand->pResp = pStatus;

    // Send command
    error = SendCommand(pSd);

    return error;
}

//------------------------------------------------------------------------------
/// In the case of a Standard Capacity SD Memory Card, this command sets the
/// block length (in bytes) for all following block commands
/// (read, write, lock).
/// Default block length is fixed to 512 Bytes.
/// Set length is valid for memory access commands only if partial block read
/// operation are allowed in CSD.
/// In the case of a High Capacity SD Memory Card, block length set by CMD16
/// command does not affect the memory read and write commands. Always 512
/// Bytes fixed block length is used. This command is effective for LOCK_UNLOCK
/// command. In both cases, if block length is set larger than 512Bytes, the
/// card sets the BLOCK_LEN_ERROR bit.
/// \param pSd  Pointer to a SD card driver instance.
/// \param blockLength  Block length in bytes.
//------------------------------------------------------------------------------
static unsigned char Cmd16(SdCard *pSd, unsigned short blockLength)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Cmd16()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SET_BLOCKLEN_CMD;
    pCommand->arg = blockLength;
    pCommand->resType = 1;
    pCommand->pResp = &response;

    // Send command
    error = SendCommand(pSd);

    return error;
}

#ifdef SINGLE_READ
static unsigned char Cmd17(SdCard *pSd,
                           unsigned char  *pData,
                           unsigned int   address,
                           unsigned int   *pStatus)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Cmd17()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_READ_SINGLE_BLOCK_CMD;
    pCommand->arg = address;
    pCommand->resType = 1;
    pCommand->pResp = &response;

    pCommand->blockSize = SD_BLOCK_SIZE;
    pCommand->nbBlock = 1;
    pCommand->pData = pData;

    pCommand->dataTran = 1;
    pCommand->isRead = 1;
    pCommand->tranType = MCI_NEW_TRANSFER;

    // Send command
    error = SendCommand(pSd);

    if (pStatus) *pStatus = response;
    return error;
}
#endif

//------------------------------------------------------------------------------
/// Continously transfers datablocks from card to host until interrupted by a
/// STOP_TRANSMISSION command.
/// \param pSd        Pointer to a SD card driver instance.
/// \param blockSize  Block size (shall be set to 512 in case of high capacity).
/// \param pData      Pointer to the DW aligned buffer to be filled.
/// \param address    SD card address.
//------------------------------------------------------------------------------
static unsigned char Cmd18(SdCard *pSd,
                           unsigned short nbBlock,
                           unsigned char *pData,
                           unsigned int address,
                           unsigned int *pStatus)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Cmd18()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_READ_MULTIPLE_BLOCK_CMD;
    pCommand->arg = address;
    pCommand->resType = 1;
    pCommand->pResp = &response;

    pCommand->blockSize = SD_BLOCK_SIZE;
    pCommand->nbBlock = nbBlock;
    pCommand->pData = pData;

    pCommand->dataTran = 1;
    pCommand->isRead = 1;
    pCommand->tranType = MCI_NEW_TRANSFER;

    // Send command
    error = SendCommand(pSd);

    if (pStatus) *pStatus = response;

    return error;
}
#ifdef SINGLE_WRITE
static unsigned char Cmd24(SdCard *pSd,
                           unsigned char  *pData,
                           unsigned int   address,
                           unsigned int   *pStatus)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Cmd24()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_WRITE_BLOCK_CMD;
    pCommand->arg = address;
    pCommand->resType = 1;
    pCommand->pResp = &response;

    pCommand->blockSize = SD_BLOCK_SIZE;
    pCommand->nbBlock = 1;
    pCommand->pData = pData;

    pCommand->dataTran = 1;
    pCommand->isRead = 0;
    pCommand->tranType = MCI_NEW_TRANSFER;

    // Send command
    error = SendCommand(pSd);

    if (pStatus) *pStatus = response;
    return error;
}
#endif
//------------------------------------------------------------------------------
/// Write block command
/// \param pSd  Pointer to a SD card driver instance.
/// \param blockSize  Block size (shall be set to 512 in case of high capacity).
/// \param pData  Pointer to the DW aligned buffer to be filled.
/// \param address  SD card address.
//------------------------------------------------------------------------------
static unsigned char Cmd25(SdCard *pSd,
                           unsigned short nbBlock,
                           unsigned char *pData,
                           unsigned int address,
                           unsigned int *pStatus)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Cmd25()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_WRITE_MULTIPLE_BLOCK_CMD;
    pCommand->arg = address;
    pCommand->resType = 1;
    pCommand->pResp = &response;

    pCommand->blockSize = SD_BLOCK_SIZE;
    pCommand->nbBlock = nbBlock;
    pCommand->pData = pData;

    pCommand->dataTran = 1;
    pCommand->tranType = MCI_NEW_TRANSFER;

    // Send command
    error = SendCommand(pSd);
    if (pStatus) *pStatus = response;

    return error;
}

#if MCI_SDIO_ENABLE
//------------------------------------------------------------------------------
/// SDIO R/W Byte Direct, response R5
/// \param pSd Pointer to SdCard instance.
/// \param func Number of function.
/// \param rw   The direction of IO operation, 1 for write.
/// \param raw  Read after write
/// \param addr The register address to access.
/// \param pIoData Pointer to fill written data and response.
//------------------------------------------------------------------------------
static unsigned char Cmd52(SdCard *pSd,
                           unsigned char func,
                           unsigned char rw,
                           unsigned char raw,
                           unsigned int addr,
                           unsigned int *pIoData)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;
    unsigned char byte = 0;

    TRACE_DEBUG("Cmd52()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SDIO_IO_RW_DIRECT;
    // - argument
    if (pIoData) byte = *pIoData;
    pCommand->arg = byte
                   | (addr << 9)    // register address 25: 9
                   | (raw  << 27)   // ReadAfterWrite   27
                   | (func << 28)   // FunctionNumber   30:28
                   | (rw << 31);    // R/W              31
    pCommand->resType = 5;
    pCommand->pResp = &response;
    // Send command
    error = SendCommand(pSd);
    if (pIoData) *pIoData = response;
    return error;
}

//------------------------------------------------------------------------------
/// SDIO R/W Extended, response R5
/// \param pSd Pointer to SdCard instance.
/// \param func Number of function.
/// \param rw   The direction of IO operation, 1 for write.
/// \param blockMode  R/O on a block basis
/// \param incAddress R/W to incrementing address (1) or fixed address (0)
/// \param addr The register address to access.
/// \param pIoData Pointer to fill written data and response.
/// \param size    Data size base on bytes or blocks depending on blockMode
/// \param pResp   Pointer to response buffer
//------------------------------------------------------------------------------
static unsigned char Cmd53(SdCard *pSd,
                           unsigned char func,
                           unsigned char rw,
                           unsigned char blockMode,
                           unsigned char incAddress,
                           unsigned int  addr,
                           unsigned char *pIoData,
                           unsigned short size,
                           unsigned int *pResp)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;

    TRACE_DEBUG("Cmd53()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SDIO_IO_RW_EXTENDED;
    // - argument
    pCommand->arg = size
                   | (addr << 9)        // register address 25: 9
                   | (incAddress << 26) // OP Code          26
                   | (blockMode << 27)  // ReadAfterWrite   27
                   | (func << 28)       // FunctionNumber   30:28
                   | (rw << 31);        // R/W              31
    pCommand->resType = 5;
    pCommand->pResp   = pResp;
    
    // - Write...
    if (rw) {}
    else {
        pCommand->cmd |= AT91C_MCI_TRDIR_READ;
        pCommand->isRead = 1;
    }
    // - Block...
    if (blockMode) {
        pCommand->cmd |= AT91C_MCI_TRTYP_SDIO_BLOCK
                       | AT91C_MCI_TRCMD_START;
        pCommand->blockSize = SD_BLOCK_SIZE;
    }
    else {
        pCommand->cmd |= AT91C_MCI_TRTYP_SDIO_BYTE
                       | AT91C_MCI_TRCMD_START;
        pCommand->blockSize = 1;
    }
    pCommand->nbBlock  = size;
    pCommand->pData    = pIoData;
    pCommand->dataTran = 1;
    pCommand->tranType = MCI_NEW_TRANSFER;

    // Send command
    error = SendCommand(pSd);
    return error;
}
#endif

//------------------------------------------------------------------------------
/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply
/// ramp up time.
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
static unsigned char Cmd55(SdCard *pSd)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Cmd55()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_APP_CMD;
    pCommand->arg = (pSd->cardAddress << 16);
    pCommand->resType = 1;
    pCommand->pResp = &response;

    // Send command
    error = SendCommand(pSd);

    return error;
}

/*
//------------------------------------------------------------------------------
/// SPI Mode, Reads the OCR register of a card
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SD card driver instance.
/// \param pOcr   OCR value of the card
//------------------------------------------------------------------------------
static unsigned char Cmd58(SdCard *pSd, unsigned int *pOcr)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response[2];

    TRACE_DEBUG("Cmd58()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_READ_OCR_CMD;
    pCommand->resType = 3;
    pCommand->pResp = &response[0];

    // Send command
    error = SendCommand(pSd);
    return error;
}

//------------------------------------------------------------------------------
/// SPI Mode, Set CRC option of a card
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SD card driver instance.
/// \param option  CRC option, 1 to turn on, 0 to trun off
//------------------------------------------------------------------------------
static unsigned char Cmd59(SdCard *pSd, unsigned char option)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Cmd59()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_CRC_ON_OFF_CMD;
    pCommand->arg = (option & 0x1);
    pCommand->resType = 1;
    pCommand->pResp = &response;

    // Send command
    error = SendCommand(pSd);

    return error;
}
*/

//------------------------------------------------------------------------------
/// Defines the data bus width (00=1bit or 10=4 bits bus) to be used for data
/// transfer.
/// The allowed data bus widths are given in SCR register.
/// \param pSd  Pointer to a SD card driver instance.
/// \param busWidth  Bus width in bits.
/// \return the command transfer result (see SendCommand).
//------------------------------------------------------------------------------
static unsigned char Acmd6(SdCard *pSd, unsigned char busWidth)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response;

    TRACE_DEBUG("Acmd6()\n\r");

    error = Cmd55(pSd);
    if (error) {
        TRACE_ERROR("Acmd6.Cmd55: %d\n\r", error);
        return error;
    }

    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SD_SET_BUS_WIDTH_CMD;
    pCommand->arg = (busWidth == 4) ? SD_STAT_DATA_BUS_WIDTH_4BIT :
                                      SD_STAT_DATA_BUS_WIDTH_1BIT;
    pCommand->resType = 1;
    pCommand->pResp = &response;

    // Send command
    return SendCommand(pSd);
}

//------------------------------------------------------------------------------
/// The SD Status contains status bits that are related to the SD memory Card
/// proprietary features and may be used for future application-specific usage.
/// Can be sent to a card only in 'tran_state'.
//------------------------------------------------------------------------------
static unsigned char Acmd13(SdCard *pSd, unsigned int * pSdSTAT)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response[1];

    TRACE_DEBUG("Acmd13()\n\r");

    error = Cmd55(pSd);
    if (error) {
        TRACE_ERROR("Acmd13.Cmd55: %d\n\r", error);
        return error;
    }

    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SD_STATUS_CMD;
    pCommand->resType = 1;
    pCommand->pResp = response;

    pCommand->blockSize = 512 / 8;
    pCommand->nbBlock = 1;
    pCommand->pData = (unsigned char*)pSdSTAT;

    pCommand->dataTran = 1;
    pCommand->isRead = 1;
    pCommand->tranType = MCI_NEW_TRANSFER;

    // Send command
    error = SendCommand(pSd);

    return error;
}

//------------------------------------------------------------------------------
/// Asks to all cards to send their operations conditions.
/// Returns the command transfer result (see SendCommand).
/// \param pSd  Pointer to a SD card driver instance.
/// \param hcs  Shall be true if Host support High capacity.
/// \param pCCS  Set the pointed flag to 1 if hcs != 0 and SD OCR CCS flag is set.
//------------------------------------------------------------------------------
static unsigned char Acmd41(SdCard *pSd, unsigned char hcs, unsigned char *pCCS)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int  response;

    do {
        error = Cmd55(pSd);
        if (error) {
            return error;
        }

        ResetCommand(pCommand);
        // Fill command information
        pCommand->cmd = AT91C_SD_APP_OP_COND_CMD;
        pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE;
        if (hcs) {
            pCommand->arg |= AT91C_CCS;
        }

        pCommand->resType = 3;
        pCommand->pResp = &response;

        // Send command
        TRACE_DEBUG("Acmd41()\n\r");
        error = SendCommand(pSd);
        if (error) {
            return error;
        }
        *pCCS  = ((response & AT91C_CCS) != 0);
    }
    while ((response & AT91C_CARD_POWER_UP_BUSY) != AT91C_CARD_POWER_UP_BUSY);

    return 0;
}

//------------------------------------------------------------------------------
/// SD Card Configuration Register (SCR) provides information on the SD Memory
/// Card's special features that were configured into the given card. The size
/// of SCR register is 64 bits.
//------------------------------------------------------------------------------
static unsigned char Acmd51(SdCard *pSd, unsigned int * pSCR)
{
    SdCmd *pCommand = &(pSd->command);
    unsigned char error;
    unsigned int response[1];

    TRACE_DEBUG("Acmd51()\n\r");

    error = Cmd55(pSd);
    if (error) {
        TRACE_ERROR("Acmd51.Cmd55: %d\n\r", error);
        return error;
    }

    ResetCommand(pCommand);
    // Fill command information
    pCommand->cmd = AT91C_SD_SEND_SCR_CMD;
    pCommand->resType = 1;
    pCommand->pResp = response;

    pCommand->blockSize = 64 / 8;
    pCommand->nbBlock = 1;
    pCommand->pData = (unsigned char*)pSCR;

    pCommand->dataTran = 1;
    pCommand->isRead = 1;
    pCommand->tranType = MCI_NEW_TRANSFER;

    // Send command
    error = SendCommand(pSd);

    //if (!error) Int2MsbFirstStream((unsigned char*)pSCR, 8 / 4);

    return error;
}

#if defined(MCI2_INTERFACE) && defined(AT91C_MCI_SPCMD_BOOTREQ)
//------------------------------------------------------------------------------
/// Terminate boot stream.
/// \param pSd      Pointer to SdCard instance.
//------------------------------------------------------------------------------
static unsigned char BootEnd(SdCard *pSd)
{
    SdCmd * pCommand = &(pSd->command);

    TRACE_DEBUG("BootEnd()\n\r");
    ResetCommand(pCommand);

    // Send boot end
    pCommand->cmd = AT91C_BOOTEND;

    return SendCommand(pSd);
}

//------------------------------------------------------------------------------
/// In boot operation mode, the processor can read boot data from the slave,
/// by keeping the CMD line low after power-on before issuing CMD1.
/// BootEnd() must be invoked after the boot request done.
/// \param pSd      Pointer to SdCard instance.
/// \param pBuffer  The buffer holding received data.
/// \param length   The buffer length.
//------------------------------------------------------------------------------
static unsigned char BootREQ(SdCard *pSd,
                             unsigned char* pBuffer,
                             unsigned int   nbBlocks,
                             unsigned char  ackEnable)
{
    SdCmd * pCommand = &(pSd->command);
    unsigned char error;

    TRACE_DEBUG("BootREQ()\n\r");
    ResetCommand(pCommand);

    // Send boot request
    pCommand->cmd = ackEnable ? (AT91C_BOOTREQ | AT91C_MCI_BOOTACK)
                              :  AT91C_BOOTREQ;
    pCommand->blockSize = SD_BLOCK_SIZE;
    pCommand->nbBlock   = nbBlocks;
    pCommand->pData     = pBuffer;
    pCommand->isRead    = 1;
    pCommand->tranType = MCI_NEW_TRANSFER;

    error = SendCommand(pSd);
    if (error) {
        TRACE_ERROR("BootOperation.BootReq: %d\n\r", error);
        return error;
    }
    return error;
}
#endif

//------------------------------------------------------------------------------
/// Continue to transfer datablocks from card to host until interrupted by a
/// STOP_TRANSMISSION command.
/// \param pSd  Pointer to a SD card driver instance.
/// \param blockSize  Block size (shall be set to 512 in case of high capacity).
/// \param pData  Pointer to the application buffer to be filled.
/// \param address  SD card address.
//------------------------------------------------------------------------------
static unsigned char ContinuousRead(SdCard *pSd,
                                    unsigned short nbBlock,
                                    unsigned char *pData,
                                    SdCallback     pCb,
                                    void          *pArg)
{
    SdCmd *pCommand = &(pSd->command);

    TRACE_DEBUG("Read()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->blockSize = SD_BLOCK_SIZE;
    pCommand->nbBlock   = nbBlock;
    pCommand->pData     = pData;

    pCommand->dataTran = 1;
    pCommand->tranType = MCI_CONTINUE_TRANSFER;
    pCommand->isRead = 1;
    
    pCommand->callback = pCb;
    pCommand->pArg     = pArg;

    // Send command
    return SendCommand(pSd);
}

//------------------------------------------------------------------------------
/// Continue to transfer datablocks from host to card until interrupted by a
/// STOP_TRANSMISSION command.
/// \param pSd  Pointer to a SD card driver instance.
/// \param blockSize  Block size (shall be set to 512 in case of high capacity).
/// \param pData  Pointer to the application buffer to be filled.
//------------------------------------------------------------------------------
static unsigned char ContinuousWrite(SdCard *pSd,
                                     unsigned short nbBlock,
                                     const unsigned char *pData,
                                     SdCallback     pCb,
                                     void          *pArg)
{
    SdCmd *pCommand = &(pSd->command);

    TRACE_DEBUG("Write()\n\r");
    ResetCommand(pCommand);
    // Fill command information
    pCommand->blockSize = SD_BLOCK_SIZE;
    pCommand->nbBlock   = nbBlock;
    pCommand->pData     = (unsigned char*)pData;

    pCommand->dataTran = 1;
    pCommand->tranType = MCI_CONTINUE_TRANSFER;
    
    pCommand->callback = pCb;
    pCommand->pArg     = pArg;

    // Send command
    return SendCommand(pSd);
}

//------------------------------------------------------------------------------
/// Try SW Reset several times (CMD0 with ARG 0)
/// \param pSd      Pointer to a SD card driver instance.
/// \param retry    Retry times.
/// \return 0 or MCI error code.
//------------------------------------------------------------------------------
static unsigned char SwReset(SdCard *pSd, unsigned int retry)
{
    unsigned int i;
    unsigned char error = 0;

    for (i = 0; i < retry; i ++) {
        error = Cmd0(pSd, 0);
        if (error != MCI_STATUS_NORESPONSE)
            break;
    }
    return error;
}
/*
//------------------------------------------------------------------------------
/// Re-init card to trans state.
//------------------------------------------------------------------------------
static unsigned char ReInit(SdCard *pSd)
{
    #if 0
    unsigned char error;
    error = SwReset(pSd, 1);
    if (error) {
        TRACE_ERROR("ReInit.Cmd0: %d\n\r", error);
        return error;
    }
    error = Cmd1(pSd);
    if (error) {
        TRACE_ERROR("ReInit.Cmd1: %d\n\r", error);
        return error;
    }
    error = Cmd2(pSd);
    if (error) {
        TRACE_ERROR("ReInit.Cmd2: %d\n\r", error);
        return error;
    }
    error = Cmd3(pSd);
    if (error) {
        TRACE_ERROR("ReInit.Cmd3: %d\n\r", error);
        return error;
    }
    error = Cmd7(pSd, pSd->cardAddress);
    if (error) {
        TRACE_ERROR("ReInit.Cmd7: %d\n\r", error);
        return error;
    }
    #endif
    return 0;
}
*/
//------------------------------------------------------------------------------
/// Move SD card to transfer state.
//------------------------------------------------------------------------------
static unsigned char MoveToTranState(SdCard * pSd)
{
    unsigned char error;
    unsigned int  status;

    // Quit transfer state
    if((pSd->state == SD_STATE_READ)
    || (pSd->state == SD_STATE_WRITE)) {

        error = Cmd12(pSd,
                      (pSd->state == SD_STATE_READ),
                      &status);
        if (error) {
            TRACE_ERROR("MvToTran.Cmd12: %d\n\r", error);
            return error;
        }
    }

    // Put device into tran state
    error = Cmd13(pSd, &status);
    if (error) {
        TRACE_ERROR("MvToTran.Cmd13: %d\n\r", error);
        return error;
    }
    if ((status & STATUS_STATE) == STATUS_STBY) {
        error = Cmd7(pSd, pSd->cardAddress);
        if (error) {
            TRACE_ERROR("MvToTran.Cmd7: %d\n\r", error);
            return error;
        }
    }

    return 0;
}

#if defined(SINGLE_READ) || defined(SINGLE_WRITE)
static unsigned char PerformSingleTransfer(SdCard *pSd,
                                           unsigned int address,
                                           unsigned char *pData,
                                           unsigned char isRead)
{
    unsigned int status;
    unsigned char error = 0;

    /* Reset transfer state if previous in multi- mode */
    if(    (pSd->state == SD_STATE_READ)
        || (pSd->state == SD_STATE_WRITE)) {
        /* Stop transfer */
        error = Cmd12(pSd, (pSd->state == SD_STATE_READ), &status);
        if (error) {
            TRACE_ERROR("SingleTx.Cmd12: st%x, er%d\n\r", pSd->state, error);
        }
        pSd->state = SD_STATE_READY;
        pSd->preBlock = 0xFFFFFFFF;
    }

#ifdef SINGLE_READ
    if(isRead) {
        // Wait for card to be ready for data transfers
        do {
            error = Cmd13(pSd, &status);
            if (error) {
                TRACE_ERROR("SingleTx.RD.Cmd13: %d\n\r", error);
                return error;
            }
            if(  ((status & STATUS_STATE) == STATUS_IDLE)
               ||((status & STATUS_STATE) == STATUS_READY)
               ||((status & STATUS_STATE) == STATUS_IDENT)) {
                TRACE_ERROR("SingleTx.mode\n\r");
                return SD_ERROR_NOT_INITIALIZED;
            }
            // If the card is in sending data state or in receivce data state
            if (  ((status & STATUS_STATE) == STATUS_RCV)
                ||((status & STATUS_STATE) == STATUS_DATA) ){

                TRACE_DEBUG("SingleTx.state = 0x%X\n\r", (status & STATUS_STATE) >> 9);
            }
        }
        while (    ((status & STATUS_READY_FOR_DATA) == 0)
                || ((status & STATUS_STATE) != STATUS_TRAN) );
        ASSERT((status & STATUS_STATE) == STATUS_TRAN,
                "SD Card can't be configured in transfer state 0x%X\n\r",
                (status & STATUS_STATE)>>9);
        // Read data
        // Move to Sending data state
        error = Cmd17(pSd, pData, SD_ADDRESS(pSd,address), &status);
        if (error) {
            TRACE_ERROR("SingleTx.Cmd17: %d\n\r", error);
            return error;
        }
        if (status & ~(STATUS_READY_FOR_DATA | STATUS_STATE)) {
            TRACE_ERROR("CMD17.stat: %x\n\r",
                status & ~(STATUS_READY_FOR_DATA | STATUS_STATE));
            return SD_ERROR_DRIVER;
        }
        return error;
    }
#endif
#ifdef SINGLE_WRITE
    // Write
    {
        // Wait for card to be ready for data transfers
        do {
            error = Cmd13(pSd, &status);
            if (error) {
                TRACE_ERROR("SingleTx.WR.Cmd13: %d\n\r", error);
                return error;
            }
        }
        while ((status & STATUS_READY_FOR_DATA) == 0);
        // Move to Sending data state
        error = Cmd24(pSd, pData, SD_ADDRESS(pSd,address), &status);
        if (error) {
            TRACE_DEBUG("SingleTx.Cmd25: %d\n\r", error);
            return error;
        }
        if (status & (STATUS_WRITE & ~(STATUS_READY_FOR_DATA | STATUS_STATE))) {
            TRACE_ERROR("CMD24(0x%x).stat: %x\n\r",
                SD_ADDRESS(pSd,address), 
                status & (STATUS_WRITE
                            & ~(STATUS_READY_FOR_DATA | STATUS_STATE)));
            return SD_ERROR_DRIVER;
        }
    }
    return error;
#endif
}
#endif

//------------------------------------------------------------------------------
/// Move SD card to transfer state. The buffer size must be at
/// least 512 byte long. This function checks the SD card status register and
/// address the card if required before sending the transfer command.
/// Returns 0 if successful; otherwise returns an code describing the error.
/// \param pSd      Pointer to a SD card driver instance.
/// \param address  Address of the block to transfer.
/// \param nbBlocks Number of blocks to be transfer, 0 for infinite transfer.
/// \param pData    Data buffer whose size is at least the block size.
/// \param isRead   1 for read data and 0 for write data.
//------------------------------------------------------------------------------
static unsigned char MoveToTransferState(SdCard *pSd,
                                         unsigned int address,
                                         unsigned short nbBlocks,
                                         unsigned char *pData,
                                         unsigned char isRead)
{
    unsigned int status;
    unsigned char error;

    if(    (pSd->state == SD_STATE_READ)
        || (pSd->state == SD_STATE_WRITE)) {
#if 1//!defined(MCI2_INTERFACE)
        if (pSd->state == SD_STATE_WRITE) {
            DBGU_PutChar(0);
            DBGU_PutChar(0);
            DBGU_PutChar(0);
            DBGU_PutChar(0);
        }
#endif
        // RW MULTI with length
        error = Cmd12(pSd,
                      (pSd->state == SD_STATE_READ),
                      &status);
        if (error) {
            TRACE_ERROR("MTTranState.Cmd12: st%x, er%d\n\r", pSd->state, error);
            return error;
        }
#if !defined(MCI2_INTERFACE)
        // RW MULTI Infinite
        if (pSd->state == SD_STATE_WRITE) {
            while(MCI_CheckBusy((Mci *)pSd->pSdDriver) != 0);
        }
#endif
    }

    if(isRead) {
        // Wait for card to be ready for data transfers
        do {
            error = Cmd13(pSd, &status);
            if (error) {
                TRACE_ERROR("MTTranState.RD.Cmd13: %d\n\r", error);
                return error;
            }
            if(  ((status & STATUS_STATE) == STATUS_IDLE)
               ||((status & STATUS_STATE) == STATUS_READY)
               ||((status & STATUS_STATE) == STATUS_IDENT)) {
                TRACE_ERROR("Pb Card Identification mode\n\r");
                return SD_ERROR_NOT_INITIALIZED;
            }
            // If the card is in sending data state or in receivce data state
            if (  ((status & STATUS_STATE) == STATUS_RCV)
                ||((status & STATUS_STATE) == STATUS_DATA) ){

                TRACE_DEBUG("state = 0x%X\n\r", (status & STATUS_STATE) >> 9);
            }
        }
        while (    ((status & STATUS_READY_FOR_DATA) == 0)
                || ((status & STATUS_STATE) != STATUS_TRAN) );
        ASSERT((status & STATUS_STATE) == STATUS_TRAN,
                "SD Card can't be configured in transfer state 0x%X\n\r",
                (status & STATUS_STATE)>>9);
        // Read data
        // Move to Sending data state
        error = Cmd18(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address), &status);
        if (error) {
            TRACE_ERROR("MTTranState.Cmd18: %d\n\r", error);
            return error;
        }
        if (status & ~(STATUS_READY_FOR_DATA | STATUS_STATE)) {
            TRACE_ERROR("CMD18.stat: %x\n\r",
                status & ~(STATUS_READY_FOR_DATA | STATUS_STATE));
            return SD_ERROR_DRIVER;
        }
    }
    else {
        // Wait for card to be ready for data transfers
        do {
            error = Cmd13(pSd, &status);
            if (error) {
                TRACE_ERROR("MoveToTransferState.WR.Cmd13: %d\n\r", error);
                return error;
            }
        }
        while ((status & STATUS_READY_FOR_DATA) == 0);
        // Move to Sending data state
        error = Cmd25(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address), &status);
        if (error) {
            TRACE_DEBUG("MoveToTransferState.Cmd25: %d\n\r", error);
            return error;
        }
        if (status & (STATUS_WRITE & ~(STATUS_READY_FOR_DATA | STATUS_STATE))) {
            TRACE_ERROR("CMD25(0x%x, %d).stat: %x\n\r",
                SD_ADDRESS(pSd,address), nbBlocks,
                status & (STATUS_WRITE
                            & ~(STATUS_READY_FOR_DATA | STATUS_STATE)));
            return SD_ERROR_DRIVER;
        }
    }

    if (!error) pSd->preBlock = address + (nbBlocks-1);
    return error;
}

//------------------------------------------------------------------------------
/// Switch the bus width of card
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// Switch the HS mode of card
/// \param pSd      Pointer to SdCard instance.
/// \param hsEnable 1 to enable, 0 to disable.
//------------------------------------------------------------------------------
static unsigned char SdMmcSwitchHsMode(SdCard *pSd, unsigned char hsEnable)
{
    unsigned int status;
    unsigned char error = SD_ERROR_DRIVER;
    if (pSd->mode == hsEnable)
        return 0;
    if (pSd->cardType >= CARD_MMC) {
        MmcCmd6Arg cmd6Arg;
        cmd6Arg.access = 0x3;
        cmd6Arg.index  = SD_EXTCSD_HS_TIMING_INDEX;
        cmd6Arg.value  = hsEnable ? SD_EXTCSD_HS_TIMING_ENABLE
                                  : SD_EXTCSD_HS_TIMING_DISABLE;
        cmd6Arg.cmdSet = 0;
        error = Cmd6(pSd, &cmd6Arg, 0, &status);
        if (error) {
            TRACE_ERROR("MMC SwitchHs.Cmd6: %d\n\r", error);
        }
        else if (status & STATUS_SWITCH_ERROR) {
            TRACE_WARNING("MMC HS SW Fail\n\r");
            error = SD_ERROR_DRIVER;
        }
        else {
            TRACE_WARNING_WP("-I- MMC HS %d\n\r", hsEnable);
            pSd->mode = hsEnable;
        }
    }
    else if (pSd->cardType >= CARD_SD) {
        SdCmd6Arg cmd6Arg;
        unsigned int switchStatus[512/32];
        cmd6Arg.mode = 1;
        cmd6Arg.reserved = 0;
        cmd6Arg.reserveFG6 = 0xF;
        cmd6Arg.reserveFG5 = 0xF;
        cmd6Arg.reserveFG4 = 0xF;
        cmd6Arg.reserveFG3 = 0xF;
        cmd6Arg.command = 0;
        cmd6Arg.accessMode = 1;
        error = Cmd6(pSd,
                     &cmd6Arg,
                     switchStatus,
                     &status);
      #if 0
        unsigned int i;
        printf("SD Switch status:");
        for(i = 0; i < 512 / 8; i ++) {
            if ((i % 8) == 0) printf("\n\r[%3d]", i);
            printf(" %02x", ((char*)switchStatus)[i]);
        }
        printf("\n\r");
        printf(" _FG1_INFO %x\n\r",
            SD_SW_STAT_FUN_GRP1_INFO(switchStatus));
        printf(" _FG1_RC   %x\n\r",
            SD_SW_STAT_FUN_GRP1_RC(switchStatus));
        printf(" _FG1_BUSY %x\n\r",
            SD_SW_STAT_FUN_GRP1_BUSY(switchStatus));
        printf(" _FG1_DS_V %x\n\r",
            SD_SW_STAT_DATA_STRUCT_VER(switchStatus));
      #endif
        if (error) {
            TRACE_ERROR("SD SwitchHs.Cmd6: %d\n\r", error);
        }
        else if (status & STATUS_SWITCH_ERROR) {
            TRACE_WARNING("SD HS SW Fail\n\r");
            error = SD_ERROR_DRIVER;
        }
        else if (SD_SW_STAT_FUN_GRP1_RC(switchStatus)
                        == SD_SW_STAT_FUN_GRP_RC_ERROR) {
            TRACE_ERROR_WP("-I- SD HS Not Supported\n\r");
            error = SD_ERROR_DRIVER;
        }
        else if (SD_SW_STAT_FUN_GRP1_BUSY(switchStatus)) {
            TRACE_WARNING("SD HS Busy\n\r");
            error = SD_ERROR_DRIVER;
        }
        else {
            TRACE_WARNING_WP("-I- SD HS %d\n\r", hsEnable);
            pSd->mode = hsEnable;
        }
    }

    return error;
}

#if defined(MCI2_INTERFACE) && defined(AT91C_MCI_SPCMD_BOOTREQ)
//------------------------------------------------------------------------------
/// Process a list of SWITCH command
/// \param  pSd      Pointer to SdCard instance.
/// \param  pArgList Argument list.
/// \param  listSize Number of arguments listed.
/// \return 0, or error code and argument index.
//------------------------------------------------------------------------------
static unsigned short MmcSwitchSettings(SdCard *pSd,
                                       const MmcCmd6Arg * pArgList,
                                       unsigned int listSize,
                                       unsigned int * pErrSta)
{
    unsigned int i, status;
    unsigned char error;

    SANITY_CHECK(pSd);
    SANITY_CHECK(pArgList);

    for (i = 0; i < listSize; i ++) {
        error = Cmd6(pSd, &pArgList[i], 0, &status);
        if (pErrSta) *pErrSta = status;
        if (error) {
            return (error | (i << 8));
        }
        if (status & ~(STATUS_STATE | STATUS_READY_FOR_DATA)) {
            TRACE_WARNING("Error in SWITCH.%d, 0x%x\n\r",
                          pArgList[i].index, status);
        }
        else {
            TRACE_INFO("SWITCH.%d: 0x%x\n\r",
                       pArgList[i].index, status);
        }
    }
    return 0;
}
#endif

//------------------------------------------------------------------------------
/// Switch card state between STBY and TRAN
/// \param pSd      Pointer to a SD card driver instance.
/// \param address  Card address to TRAN, 0 to STBY
/// \param check    Whether to check the state
//------------------------------------------------------------------------------
static unsigned char MmcSelectCard(SdCard *pSd,
                                   unsigned short address,
                                   unsigned char check)
{
    unsigned char error;
    unsigned int  status;
    unsigned int  targetState = address ? STATUS_TRAN : STATUS_STBY;
    unsigned int  srcState    = address ? STATUS_STBY : STATUS_TRAN;
    if (pSd->cardType == CARD_SDIO) check = 0;

    // At this stage the Initialization and identification process is achieved
    // The SD card is supposed to be in Stand-by State
    while(check) {
        error = Cmd13(pSd, &status);
        if (error) {
            TRACE_ERROR("MmcSelectCard.Cmd13 (%d)\n\r", error);
            return error;
        }
        if ((status & STATUS_READY_FOR_DATA)) {
            unsigned int currState = status & STATUS_STATE;
            if (currState == targetState) return 0;
            if (currState != srcState) {
                TRACE_ERROR("MmcSelectCard, wrong state %x\n\r", currState);
                return SD_ERROR_DRIVER;
            }
            break;
        }
    }

    // witch to TRAN mode to Select the current SD/MMC
    // so that SD ACMD6 can process or EXT_CSD can read.
    error = Cmd7(pSd, address);
    if (error == SD_ERROR_NOT_INITIALIZED && address == 0) {}
    else if (error) {
        TRACE_ERROR("MmcSelectCard.Cmd7 (%d)\n\r", error);
    }

    return error;
}

//------------------------------------------------------------------------------
/// Get EXT_CSD information
/// \param pSd      Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
static unsigned char MmcGetExtInformation(SdCard *pSd)
{
    unsigned char error;
    unsigned int i;

    // CSD 1.2 or Higher version
    if(SD_CSD_STRUCTURE(pSd) >= 2) {

        /* Clear EXT_CSD data */
        for (i = 0;i < 512/4; i ++) pSd->extData[i] = 0;
        error = Cmd8(pSd, 0, pSd->extData);
        if (error) {
            TRACE_ERROR("MmcGetExt.Cmd8: %d\n\r", error);
        }
    }
    return 0;
}

//------------------------------------------------------------------------------
/// Get SCR and SD Status information
/// \param pSd      Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
static unsigned char SdGetExtInformation(SdCard *pSd)
{
    unsigned char error;

    // Reset data (64 + 512 bits, 8 + 64 bytes)
    // memset(pSd->extData, 0x00, 512);

    // SD Status
    if (pSd->optCmdBitMap & SD_ACMD13_SUPPORT) {
        error = Acmd13(pSd, &pSd->extData[SD_EXT_OFFSET_SD_STAT]);
        if (error) {
            TRACE_ERROR("SdGetExt.Acmd13: %d\n\r", error);
            pSd->optCmdBitMap &= ~SD_ACMD13_SUPPORT;
        }
    }

    // SD SCR
    error = Acmd51(pSd, &pSd->extData[SD_EXT_OFFSET_SD_SCR]);
    if (error) {
        TRACE_ERROR("SdGetExt.Acmd51: %d\n\r", error);
    }

    return 0;
}

//------------------------------------------------------------------------------
/// Update SD/MMC information.
/// Update CSD for card speed switch.
/// Update ExtDATA for any card function switch.
/// \param pSd      Pointer to a SD card driver instance.
/// \return error code when update CSD error.
//------------------------------------------------------------------------------
static unsigned char SdMmcUpdateInformation(SdCard *pSd,
                                            unsigned char csd,
                                            unsigned char extData)
{
    unsigned char error;

    // Update CSD for new TRAN_SPEED value
    if (csd) {
        MmcSelectCard(pSd, 0, 1);
        Delay(800);
        error = Cmd9(pSd);
        if (error ) {
            TRACE_ERROR("SdMmcUpdateInfo.Cmd9 (%d)\n\r", error);
            return error;
        }
        error = MmcSelectCard(pSd, pSd->cardAddress, 1);
    }
    if (pSd->cardType >= CARD_MMC)     MmcGetExtInformation(pSd);
    else if (pSd->cardType >= CARD_SD) SdGetExtInformation(pSd);
    GetTransSpeedValue(pSd);

    return 0;
}

#if MCI_SDIO_ENABLE
//------------------------------------------------------------------------------
/// Find ManfID, Func0 tuple.
//------------------------------------------------------------------------------
static unsigned char SdioFindTuples(SdCard * pSd,
                                    unsigned int address, unsigned int size,
                                    unsigned int *pAddrManfID,
                                    unsigned int *pAddrFunc0)
{
    unsigned char error, tmp[3];
    unsigned int addr = address;
    unsigned char nbFound = 0;
    for (;;) {
        error = SDIO_ReadDirect(pSd, 0, addr, tmp, 3);
        if (error)
            return error;
        // ManfID
        if (tmp[0] == CISTPL_MANFID) {
            if (pAddrManfID) *pAddrManfID = addr;
            nbFound ++;
        }
        // Func0
        if (tmp[0] == CISTPL_FUNCE && tmp[2] == 0x00) {
            if (pAddrFunc0) *pAddrFunc0 = addr;
            nbFound ++;
        }
        // END
        if (tmp[0] == CISTPL_END) break;

        // All found
        if (nbFound >= 2)         break;
        // Not tuple?
        if (tmp[1] == 0)          break;

        // Next address
        addr += (tmp[1] + 2);
        if (addr > (address + size))
            break;
    }
    return 0;
}
#endif

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

#if MCI_SDIO_ENABLE
//------------------------------------------------------------------------------
/// Read at least one byte from SDIO card, using RW_DIRECT command.
/// \param pSd      Pointer to SdCard instance.
/// \param funNb    Function number.
/// \param address  First byte address of data in SDIO card.
/// \param pBytes   Pointer to data buffer.
/// \param size     Buffer size.
//------------------------------------------------------------------------------
unsigned char SDIO_ReadDirect(SdCard *pSd,
                              unsigned char funNb,
                              unsigned int  address,
                              unsigned char *pBytes,
                              unsigned int  size)
{
    unsigned char error;
    unsigned int status;
    if (pSd->cardType < CARD_SDIO) {
        return SD_ERROR_NOT_SUPPORT;
    }
    if (size == 0)
        return SD_ERROR_DRIVER;

    while(size --) {
        status = 0;
        error = Cmd52(pSd, funNb, 0, 0, address ++, &status);
        if (pBytes) *pBytes ++ = (unsigned char)status;
        if (error) {
            TRACE_ERROR("SDIO_ReadDirect.Cmd52: %d, %x\n\r", error, status);
            return SD_ERROR_DRIVER;
        }
    }

    return 0;
}

//------------------------------------------------------------------------------
/// Write one byte to SDIO card, using RW_DIRECT command.
/// \param pSd      Pointer to SdCard instance.
/// \param funNb    Function number.
/// \param address  First byte address of data in SDIO card.
/// \param pBytes   Pointer to data buffer.
/// \param size     Buffer size.
//------------------------------------------------------------------------------
unsigned char SDIO_WriteDirect(SdCard *pSd,
                               unsigned char funNb,
                               unsigned int address,
                               unsigned char byte)
{
    if (pSd->cardType < CARD_SDIO) {
        return SD_ERROR_NOT_SUPPORT;
    }
    unsigned char error;
    unsigned int status;
    status = byte;
    error = Cmd52(pSd, funNb, 1, 0, address, &status);
    if (error) {
        TRACE_ERROR("SDIO_ReadDirect.Cmd52: %d, %x\n\r", error, status);
        return SD_ERROR_DRIVER;
    }

    return 0;
}

//------------------------------------------------------------------------------
/// Read byte by byte from SDIO card, using RW_EXT command.
/// \param pSd          Pointer to SdCard instance.
/// \param funNb        Function number.
/// \param address      First byte address of data in SDIO card.
/// \param isFixedAddr  Address not increased.
/// \param pBytes       Pointer to data buffer.
/// \param size         Buffer size.
//------------------------------------------------------------------------------
unsigned char SDIO_ReadBytes(SdCard *pSd,
                             unsigned char funNb,
                             unsigned int  address,
                             unsigned char isFixedAddr,
                             unsigned char *pBytes,
                             unsigned int   size)
{
    unsigned char error;
    unsigned int status;
    if (pSd->cardType < CARD_SDIO) {
        return SD_ERROR_NOT_SUPPORT;
    }

    if (size == 0)
        return SD_ERROR_DRIVER;

    error = Cmd53(pSd, funNb,
                  0, 0, !isFixedAddr,
                  address, pBytes, size, &status);
    if (error || (status & STATUS_SDIO_CMD52)) {
        TRACE_ERROR("SDIO_ReadBytes.Cmd53: %d, %x\n\r", error, status);
        return SD_ERROR_DRIVER;
    }

    return 0;
}

//------------------------------------------------------------------------------
/// Write byte by byte to SDIO card, using RW_EXT command.
/// \param pSd          Pointer to SdCard instance.
/// \param funNb        Function number.
/// \param address      First byte address of data in SDIO card.
/// \param isFixedAddr  Address not increased.
/// \param pBytes       Pointer to data buffer.
/// \param size         Buffer size.
//------------------------------------------------------------------------------
unsigned char SDIO_WriteBytes(SdCard *pSd,
                              unsigned char funNb,
                              unsigned int  address,
                              unsigned char isFixedAddr,
                              unsigned char *pBytes,
                              unsigned int   size)
{
    unsigned char error;
    unsigned int status;
    if (pSd->cardType < CARD_SDIO) {
        return SD_ERROR_NOT_SUPPORT;
    }
    if (size == 0)
        return SD_ERROR_DRIVER;

    error = Cmd53(pSd, funNb,
                  1, 0, !isFixedAddr,
                  address, pBytes, size, &status);
    if (error || (status & STATUS_SDIO_CMD52)) {
        TRACE_ERROR("SDIO_ReadBytes.Cmd53: %d, %x\n\r", error, status);
        return SD_ERROR_DRIVER;
    }

    return 0;
}
#endif

//------------------------------------------------------------------------------
/// Read Block of data in a buffer pointed by pData. The buffer size must be at
/// least 512 byte long. This function checks the SD card status register and
/// address the card if required before sending the read command.
/// Returns 0 if successful; otherwise returns an code describing the error.
/// \param pSd      Pointer to a SD card driver instance.
/// \param address  Address of the block to read.
/// \param pData    Data buffer whose size is at least the block size, it can
///            be 1,2 or 4-bytes aligned when used with DMA.
/// \param length   Number of blocks to be read.
/// \param pCallback Pointer to callback function that invoked when read done.
///                  0 to start a blocked read.
/// \param pArgs     Pointer to callback function arguments.
//------------------------------------------------------------------------------
unsigned char SD_Read(SdCard        *pSd,
                      unsigned int   address,
                      void          *pData,
                      unsigned short length,
                      SdCallback     pCallback,
                      void          *pArgs)
{
    unsigned char error;

    // If callback is defined, performe none blocked reading
    if (pCallback) {
        if (MCI_IsTxComplete((Mci *)pSd) == 0) {
            return SD_ERROR_BUSY;
        }
    }

    if (   pSd->state != SD_STATE_READ
        || pSd->preBlock + 1 != address ) {
        // Start infinite block reading
        error = MoveToTransferState(pSd, address, 0, 0, 1);
    }
    else    error = 0;
    if (!error) {
        pSd->state = SD_STATE_READ;
        pSd->preBlock = address + (length - 1);
        error = ContinuousRead(pSd,
                               length,
                               pData,
                               pCallback, pArgs);
    }
    TRACE_DEBUG("SDrd(%u,%u):%u\n\r", address, length, error);

    return 0;    
}

//------------------------------------------------------------------------------
/// Write Block of data in a buffer pointed by pData. The buffer size must be at
/// least 512 byte long. This function checks the SD card status register and
/// address the card if required before sending the read command.
/// Returns 0 if successful; otherwise returns an code describing the error.
/// \param pSd      Pointer to a SD card driver instance.
/// \param address  Address of the block to read.
/// \param pData    Data buffer whose size is at least the block size, it can
///            be 1,2 or 4-bytes aligned when used with DMA.
/// \param length   Number of blocks to be read.
/// \param pCallback Pointer to callback function that invoked when read done.
///                  0 to start a blocked read.
/// \param pArgs     Pointer to callback function arguments.
//------------------------------------------------------------------------------
unsigned char SD_Write(SdCard        *pSd,
                       unsigned int   address,
                       void          *pData,
                       unsigned short length,
                       SdCallback     pCallback,
                       void          *pArgs)
{
    unsigned char error;
    // If callback is defined, performe none blocked writing
    if (pCallback) {
        if (MCI_IsTxComplete((Mci *)pSd) == 0) {
            return SD_ERROR_BUSY;
        }
    }
    if (   pSd->state != SD_STATE_WRITE
        || pSd->preBlock + 1 != address ) {
        // Start infinite block writing
        error = MoveToTransferState(pSd, address, 0, 0, 0);
    }
    else    error = 0;
    if (!error) {
        pSd->state = SD_STATE_WRITE;
        error = ContinuousWrite(pSd,
                                length,
                                pData,
                                pCallback, pArgs);
        pSd->preBlock = address + (length - 1);
    }
    TRACE_DEBUG("SDwr(%u,%u):%u\n\r", address, length, error);
    
    return 0;
}

//------------------------------------------------------------------------------
/// Read Block of data in a buffer pointed by pData. The buffer size must be at
/// least 512 byte long. This function checks the SD card status register and
/// address the card if required before sending the read command.
/// Returns 0 if successful; otherwise returns an code describing the error.
/// \param pSd  Pointer to a SD card driver instance.
/// \param address  Address of the block to read.
/// \param nbBlocks Number of blocks to be read.
/// \param pData    Data buffer whose size is at least the block size, it can
///            be 1,2 or 4-bytes aligned when used with DMA.
//------------------------------------------------------------------------------
unsigned char SD_ReadBlock(SdCard *pSd,
                           unsigned int address,
                           unsigned short nbBlocks,
                           unsigned char *pData)
{
    unsigned char error = 0;

    SANITY_CHECK(pSd);
    SANITY_CHECK(pData);
    SANITY_CHECK(nbBlocks);

    TRACE_DEBUG("ReadBlk(%d,%d)\n\r", address, nbBlocks);
#if defined(SINGLE_READ)
    while(nbBlocks --) {
        error = PerformSingleTransfer(pSd, address, pData, 1);
        if (error)
            break;
        // SDHC
        if (pSd->totalSize == 0xFFFFFFFF) {
            address += 1;
            pData = &pData[512];
        }
        else {
            address += 1;
            pData = &pData[512];
        }
    }
    return error;
#endif
#if !defined(MCI2_INTERFACE)
  #if !defined(AT91C_MCI_RDPROOF)
    error = MoveToTransferState(pSd, address, nbBlocks, pData, 1);
    pSd->state = SD_STATE_READ;
  #else
    if((pSd->state == SD_STATE_READ)
        && ((pSd->preBlock + 1) == address)) {

      #if defined(at91rm9200)
        error = Cmd12(pSd, 0);
        if (error) {
            return error;
        }
      #else
        TRACE_DEBUG("SD_ReadBlock:ContinuousRead\n\r");
        error = ContinuousRead(pSd,
                               nbBlocks,
                               pData,
                               0, 0);
        pSd->preBlock = address + (nbBlocks-1);
      #endif
    }
    else {
        error = MoveToTransferState(pSd, address, nbBlocks, pData, 1);
        pSd->state = SD_STATE_READ;
    }
  #endif
#else
    if (   pSd->state != SD_STATE_READ
        || pSd->preBlock + 1 != address ) {
        // Start infinite block reading
        error = MoveToTransferState(pSd, address, 0, 0, 1);
    }
    if (!error) {
        pSd->state = SD_STATE_READ;
        error = ContinuousRead(pSd,
                               nbBlocks,
                               pData,
                               0, 0);
        if (!error) pSd->preBlock = address + (nbBlocks - 1);
    }
#endif
    return error;
}

//------------------------------------------------------------------------------
/// Write Block of data pointed by pData. The buffer size must be at
/// least 512 byte long. This function checks the SD card status register and
/// address the card if required before sending the read command.
/// Returns 0 if successful; otherwise returns an SD_ERROR code.
/// \param pSd  Pointer to a SD card driver instance.
/// \param address  Address of block to write.
/// \param nbBlocks Number of blocks to be read
/// \param pData    Data buffer whose size is at least the block size, it can
///            be 1,2 or 4-bytes aligned when used with DMA.
//------------------------------------------------------------------------------
unsigned char SD_WriteBlock(SdCard *pSd,
                            unsigned int address,
                            unsigned short nbBlocks,
                            const unsigned char *pData)
{
    unsigned char error = 0;

    SANITY_CHECK(pSd);
    SANITY_CHECK(pData);
    SANITY_CHECK(nbBlocks);

    TRACE_DEBUG("WriteBlk(%d,%d)\n\r", address, nbBlocks);

#if defined(SINGLE_WRITE)
    unsigned char *pB = (unsigned char*)pData;
    while(nbBlocks --) {
        error = PerformSingleTransfer(pSd, address, pB, 0);
        if (error)
            break;
        // SDHC
        if (pSd->totalSize == 0xFFFFFFFF) {
            address += 1;
            pB = &pB[512];
        }
        else {
            address += 1;
            pB = &pB[512];
        }
    }
    return error;
#endif
#if !defined(MCI2_INTERFACE)
  #if !defined(AT91C_MCI_WRPROOF)
    error = MoveToTransferState(pSd, address, nbBlocks,
                                (unsigned char *)pData, 0);
    pSd->state = SD_STATE_WRITE;
  #else
    if((pSd->state == SD_STATE_WRITE)
        && ((pSd->preBlock + 1) == address)) {

        TRACE_DEBUG("SD_WriteBlock:ContinuousWrite\n\r");
        error = ContinuousWrite(pSd,
                                nbBlocks,
                                pData,
                                0, 0);
        pSd->preBlock = address + (nbBlocks-1);
    }
    else {

        //TRACE_FATAL("SD_WriteBlock:MoveToTransferState\n\r");
        error = MoveToTransferState(pSd, address, nbBlocks,
                                    (unsigned char *)pData, 0);
        pSd->state = SD_STATE_WRITE;
    }
  #endif
#else
    if (   pSd->state != SD_STATE_WRITE
        || pSd->preBlock + 1 != address ) {
        // Start infinite block writing
        error = MoveToTransferState(pSd, address, 0, 0, 0);
    }
    if (!error) {
        pSd->state = SD_STATE_WRITE;
        error = ContinuousWrite(pSd,
                                nbBlocks,
                                pData,
                                0, 0);
        if (!error) pSd->preBlock = address + (nbBlocks - 1);
    }
#endif

    return error;
}

//------------------------------------------------------------------------------
/// Run the SDcard SD/MMC/SDIO Mode initialization sequence.
/// This function resets both IO and memory controller, runs the initialisation
/// procedure and the identification process. Then it leaves the card in ready
/// state. The following command must check the card type and continue to put
/// the card into tran(for memory card) or cmd(for io card) state for data
/// exchange.
/// Returns 0 if successful; otherwise returns an SD_ERROR code.
/// \param pSd  Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
static unsigned char SdMmcIdentify(SdCard *pSd)
{
    unsigned char mem = 0, io = 0, f8 = 0, mp = 1, ccs = 0;
    unsigned char error = 0;
#if MCI_SDIO_ENABLE
    unsigned int  status;
#endif

    // Reset HC to default HS and BusMode
    MCI_EnableHsMode(pSd->pSdDriver, 0);
    MCI_SetBusWidth(pSd->pSdDriver, MCI_SDCBUS_1BIT);

#if MCI_SDIO_ENABLE
    // Reset SDIO
    // CMD52, write 1 to RES bit in the CCCR (bit 3 of register 6)
    status = (0x1 << 3);
    error = Cmd52(pSd, 0, 1, 0, 6, &status);
    if (!error && ((status & STATUS_SDIO_CMD52) == 0)) {}
    else if (error == MCI_STATUS_NORESPONSE) {}
    else {
        TRACE_DEBUG("SdMmcIdentify.Cmd52 fail: %d, %x\n\r", error, status);
    }
#endif

    // Reset MEM
    error = SwReset(pSd, 1);
    if (error) {
        TRACE_DEBUG("SdMmcIdentify.SwReset: %d\n\r", error)
    }

    // CMD8 is newly added in the Physical Layer Specification Version 2.00 to
    // support multiple voltage ranges and used to check whether the card
    // supports supplied voltage. The version 2.00 host shall issue CMD8 and
    // verify voltage before card initialization.
    // The host that does not support CMD8 shall supply high voltage range...
    error = Cmd8(pSd, 1, (void*)1);
    if (error == 0) {
        f8 = 1;
    }
    else if (error != SD_ERROR_NORESPONSE) {
        TRACE_ERROR("SdMmcIdentify.Cmd8: %d\n\r", error);
        return SD_ERROR_DRIVER;
    }
    else {
        // Delay after "no response"
        Delay(800);
    }

#if MCI_SDIO_ENABLE
    // CMD5 is added for SDIO OCR check
    status = 0;
    error = Cmd5(pSd, &status);
    if (error) {
        TRACE_WARNING("SdMmcIdentify.Cmd5: %d\n\r", error);
    }
    // SDIO or SD COMBO: FN > 0
    else if ((status & AT91C_SDIO_NF) > 0) {
        // Set New Voltage
        unsigned int   cmd5Retries = 10000;
        do {
            status &= AT91C_MMC_HOST_VOLTAGE_RANGE;
            error = Cmd5(pSd, &status);
            if (status & AT91C_CARD_POWER_UP_BUSY)
                break;
        } while(!error && cmd5Retries --);
        if (error) {
            TRACE_ERROR("SdMmcIdentify.Cmd5 V: %d\n\r", error);
            return SD_ERROR_DRIVER;
        }
        TRACE_INFO("SDIO\n\r");
        io = 1;
        // SDIO only?
        if ((status & AT91C_SDIO_MP) == 0) mp = 0;
    }
#endif
    // SD or MMC or COMBO: mp is 1
    if (mp) {
        // Try SD memory initialize
        error = Acmd41(pSd, f8, &ccs);
        if (error) {
            unsigned int   cmd1Retries = 10000;
            TRACE_DEBUG("SdMmcIdentify.Acmd41: %d, try MMC\n\r", error);

            // Try MMC initialize
            error = SwReset(pSd, 10);
            if (error) {
                TRACE_ERROR("SdMmcIdentify.Mmc.SwReset: %d\n\r", error);
                return SD_ERROR_DRIVER;
            }
            // - Set Voltage
            do {
                error = Cmd1(pSd, 1, &ccs);
            }
            while ((error) && (cmd1Retries-- > 0));
            if (error) {
                TRACE_ERROR("SdMmcIdentify.Cmd1: %d\n\r", error);
                return SD_ERROR_DRIVER;
            }
            else if (ccs) {
                pSd->cardType = CARD_MMCHD;
            }
            else {
                pSd->cardType = CARD_MMC;
            }

            // MMC Identified OK
            return 0;
        }
        else if (ccs) {
            TRACE_INFO("SDHC MEM\n\r");
        }
        else {
            TRACE_INFO("SD MEM\n\r");
        }
        mem = 1;
    }

    // SD(IO)+MEM ?
    if (!mem) {
        // SDIO only
        if (io) {
            pSd->cardType = CARD_SDIO;
            return 0;
        }
    }
    // SD COMBO, continue with memory initialize
    else if (io) {
        if (ccs) pSd->cardType = CARD_SDHCCOMBO;
        else     pSd->cardType = CARD_SDCOMBO;
    }
    // SD(HC), continue with memory initialize
    else {
        if (ccs) pSd->cardType = CARD_SDHC;
        else     pSd->cardType = CARD_SD;
    }
    return 0;
}

//------------------------------------------------------------------------------
/// Run the SDcard SD Mode enumeration sequence. This function runs after the
/// initialisation procedure and the identification process. It sets the
/// SD card in transfer state to set the block length and the bus width.
/// Returns 0 if successful; otherwise returns an SD_ERROR code.
/// \param pSd  Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
static unsigned char SdMmcEnum(SdCard *pSd)
{
    unsigned char mem = 0, io = 0;
    unsigned int   status;
    unsigned short error;
    unsigned char  isHsSupport = 0;
    unsigned char  updateInformation = 0;

    if (pSd->cardType & CARD_TYPE_bmSDMMC) mem = 1;
    if (pSd->cardType & CARD_TYPE_bmSDIO)  io = 1;

    // For MEM
    // The host then issues the command ALL_SEND_CID (CMD2) to the card to get
    // its unique card identification (CID) number.
    // Card that is unidentified (i.e. which is in Ready State) sends its CID
    // number as the response (on the CMD line).
    if (mem) {
        error = Cmd2(pSd);
        if (error) {
            TRACE_ERROR("SdMmcEnum.Cmd2: %d\n\r", error);
            return SD_ERROR_DRIVER;
        }
    }

    // For SDIO & MEM
    // Thereafter, the host issues CMD3 (SEND_RELATIVE_ADDR) asks the
    // card to publish a new relative card address (RCA), which is shorter than
    // CID and which is used to address the card in the future data transfer
    // mode. Once the RCA is received the card state changes to the Stand-by
    // State. At this point, if the host wants to assign another RCA number, it
    // can ask the card to publish a new number by sending another CMD3 command
    // to the card. The last published RCA is the actual RCA number of the card.
    error = Cmd3(pSd);
    if (error) {
        TRACE_ERROR("SdMmcInit.Cmd3 %d\n\r", error);
        return SD_ERROR_DRIVER;
    }

    // For MEM
    // SEND_CSD (CMD9) to obtain the Card Specific Data (CSD register),
    // e.g. block length, card storage capacity, etc...
    if (mem) {
        error = Cmd9(pSd);
        if (error) {
            TRACE_ERROR("SdMmcInit.Cmd9 %d\n\r", error);
            return SD_ERROR_DRIVER;
        }
    }

    // For SDIO & MEM
    // Now select the card, to TRAN state
    error = MmcSelectCard(pSd, pSd->cardAddress, 0);
    if (error) {
        TRACE_ERROR("SdMmcInit.Sel %d\n\r", error);
        return SD_ERROR_DRIVER;
    }
    // SDIO only card, enumeration done
    if (!mem && io) {
        // Default tranSpeed: 25MHz
        pSd->transSpeed = 25000000;
        return 0;
    }

    // For MEM cards or combo
    // If the card support EXT_CSD, read it!
    TRACE_INFO("Card Type %d, CSD_STRUCTURE %d\n\r",
               pSd->cardType, SD_CSD_STRUCTURE(pSd));

    // Get extended information of the card
    SdMmcUpdateInformation(pSd, 0, 0);

    // Advanced settings for HD & HS card
    if (pSd->cardType >= CARD_MMC){

        MmcCmd6Arg cmd6Arg;

        // MMC4 or later
        if (SD_CSD_SPEC_VERS(pSd) >= 4) {

            unsigned char busWidth, widthMode;

            // Calculate MMC busWidth (limited by slot information)
            switch (pSd->pSdDriver->mciMode & AT91C_MCI_SCDBUS) {
              #if defined(AT91C_MCI_SCDBUS_8BITS)
                case AT91C_MCI_SCDBUS_8BITS:
                    busWidth = 8;
                    widthMode = MCI_SDCBUS_8BIT;
                    break;
              #endif

              #if defined(AT91C_MCI_SCDBUS_4BITS)
                case AT91C_MCI_SCDBUS_4BITS:
                    busWidth = 4;
                    widthMode = MCI_SDCBUS_4BIT;
                    break;
              #endif

                default:
                    busWidth = 1;
                    widthMode = MCI_SDCBUS_1BIT;
            }

            // Switch to max bus width (4 now)
            cmd6Arg.access = 0x1;
            cmd6Arg.index  = SD_EXTCSD_BUS_WIDTH_INDEX;
            cmd6Arg.value  = SD_EXTCSD_BUS_WIDTH_4BIT;
            cmd6Arg.cmdSet = 0;
            error = Cmd6(pSd, &cmd6Arg, 0, &status);
            if (!error) {
                
              TRACE_WARNING_WP("-I- MMC %d-BIT BUS\n\r", busWidth);
              if (status
                  & (STATUS_MMC_SWITCH
                     & ~(STATUS_STATE | STATUS_READY_FOR_DATA))) {
                  printf("-E- Status %x\n\r", status);
              }
              else {
                  MCI_SetBusWidth(pSd->pSdDriver, widthMode);
                  updateInformation = 1;
              }
            }
            else {
                TRACE_WARNING("MMC %d-BIT not supported\n\r", busWidth)
            }
        }
        
        // CARD_TYPE 3
        if (SD_CSD_STRUCTURE(pSd) >= 2
            && (SD_EXTCSD_CARD_TYPE(pSd) & 0x2)) {

            #if !defined(OP_BOOTSTRAP_MCI_on)
            // Switch to HS mode
            if (gSdmmcAutoHsEnable) {
                cmd6Arg.access = 0x3;
                cmd6Arg.index  = SD_EXTCSD_HS_TIMING_INDEX;
                cmd6Arg.value  = SD_EXTCSD_HS_TIMING_ENABLE;
                cmd6Arg.cmdSet = 0;
                error = Cmd6(pSd, &cmd6Arg, 0, &status);
                if (error
                    || (status
                        & (STATUS_MMC_SWITCH
                            & ~(STATUS_STATE | STATUS_READY_FOR_DATA)))) {
                    TRACE_WARNING("MMC HS Fail, st %x\n\r", status);
                }
                else {
                    MCI_EnableHsMode(pSd->pSdDriver, 1);
                    TRACE_WARNING_WP("-I- MMC HS Enabled\n\r");
                    isHsSupport = 1;
                    updateInformation = 1;
                }
            }
            #endif // end of OP_BOOTSTRAP_MCI_on
        }
    }
    else if (pSd->cardType >= CARD_SD) {
      #if 1
        // Switch to 4-bits bus width
        // (All SD Card shall support 1-bit, 4 bitswidth)
        error = Acmd6(pSd, 4);
        TRACE_WARNING_WP("-I- SD 4-BITS BUS\n\r");
        if (error) {
            TRACE_ERROR("SdMmcInit.12 (%d)\n\r", error);
            return error;
        }
        MCI_SetBusWidth(pSd->pSdDriver, MCI_SDCBUS_4BIT);

        #if !defined(OP_BOOTSTRAP_MCI_on)
        // SD Spec V1.10 or higher, switch to high-speed mode
        if (gSdmmcAutoHsEnable) {
            if (SD_SCR_SD_SPEC(pSd) >= SD_SCR_SD_SPEC_1_10) {
                SdCmd6Arg cmd6Arg;
                unsigned int switchStatus[512/32];
                cmd6Arg.mode = 1;
                cmd6Arg.reserved = 0;
                cmd6Arg.reserveFG6 = 0xF;
                cmd6Arg.reserveFG5 = 0xF;
                cmd6Arg.reserveFG4 = 0xF;
                cmd6Arg.reserveFG3 = 0xF;
                cmd6Arg.command = 0;
                cmd6Arg.accessMode = 1;
                error = Cmd6(pSd,
                             &cmd6Arg,
                             switchStatus,
                             &status);
              #if 0
                unsigned int i;
                printf("SD Switch status:");
                for(i = 0; i < 512 / 8; i ++) {
                    if ((i % 8) == 0) printf("\n\r[%3d]", i);
                    printf(" %02x", ((char*)switchStatus)[i]);
                }
                printf("\n\r");
                printf(" _FG1_INFO %x\n\r",
                    SD_SW_STAT_FUN_GRP1_INFO(switchStatus));
                printf(" _FG1_RC   %x\n\r",
                    SD_SW_STAT_FUN_GRP1_RC(switchStatus));
                printf(" _FG1_BUSY %x\n\r",
                    SD_SW_STAT_FUN_GRP1_BUSY(switchStatus));
                printf(" _FG1_DS_V %x\n\r",
                    SD_SW_STAT_DATA_STRUCT_VER(switchStatus));
              #endif
                if (error || (status & STATUS_SWITCH_ERROR)) {
                    TRACE_WARNING("SD HS Fail\n\r");
                }
                else if (SD_SW_STAT_FUN_GRP1_RC(switchStatus)
                                == SD_SW_STAT_FUN_GRP_RC_ERROR) {
                    TRACE_ERROR_WP("-I- SD HS Not Supported\n\r");
                }
                else if (SD_SW_STAT_FUN_GRP1_BUSY(switchStatus)) {
                    TRACE_WARNING("SD HS Busy\n\r")
                }
                else {
                    MCI_EnableHsMode(pSd->pSdDriver, 1);
                    TRACE_WARNING_WP("-I- SD HS Enable\n\r");
                    isHsSupport = 1;
                }
            }
        }
        #endif
        // Update
        updateInformation = 1;
      #endif
    }

    if (updateInformation) {

        SdMmcUpdateInformation(pSd, isHsSupport, 1);
    }
    return 0;
}

//------------------------------------------------------------------------------
/// Run the SDcard initialization sequence. This function runs the
/// initialisation procedure and the identification process, then it sets the
/// SD card in transfer state to set the block length and the bus width.
/// Returns 0 if successful; otherwise returns an SD_ERROR code.
/// \param pSd  Pointer to a SD card driver instance.
/// \param pSdDriver  Pointer to SD driver already initialized.
//------------------------------------------------------------------------------
unsigned char SD_Init(SdCard *pSd, SdDriver *pSdDriver)
{
    unsigned char error;

    //TRACE_DEBUG("SD_Init()\n\r");

    // Initialize SdCard structure
    pSd->pSdDriver = pSdDriver;
    pSd->cardAddress = 0;
    pSd->preBlock = 0xffffffff;
    pSd->state = SD_STATE_INIT;
    pSd->cardType = CARD_UNKNOWN;
    pSd->optCmdBitMap = 0xFFFFFFFF;
    pSd->mode = 0;
    ResetCommand(&pSd->command);

    // Initialization delay: The maximum of 1 msec, 74 clock cycles and supply
    // ramp up time. Supply ramp up time provides the time that the power is
    // built up to the operating level (the bus master supply voltage) and the
    // time to wait until the SD card can accept the first command.

    // Power On Init Special Command
    //TRACE_DEBUG("Pon()\n\r");
    error = Pon(pSd);
    if (error) {
        TRACE_ERROR("SD_Init.1 (%d)\n\r", error);
        return error;
    }
    // After power-on or CMD0, all cards?CMD lines are in input mode, waiting
    // for start bit of the next command.
    // The cards are initialized with a default relative card address
    // (RCA=0x0000) and with a default driver stage register setting
    // (lowest speed, highest driving current capability).
    error = SdMmcIdentify(pSd);
    if (error) {
        TRACE_ERROR("SD_Init.Identify\n\r");
        return error;
    }
    error = SdMmcEnum(pSd);
    if (error) {
        TRACE_ERROR("SD_Init.Enum\n\r");
        return error;
    }

    // In the case of a Standard Capacity SD Memory Card, this command sets the
    // block length (in bytes) for all following block commands
    // (read, write, lock).
    // Default block length is fixed to 512 Bytes.
    // Set length is valid for memory access commands only if partial block read
    // operation are allowed in CSD.
    // In the case of a High Capacity SD Memory Card, block length set by CMD16
    // command does not affect the memory read and write commands. Always 512
    // Bytes fixed block length is used. This command is effective for
    // LOCK_UNLOCK command.
    // In both cases, if block length is set larger than 512Bytes, the card sets
    // the BLOCK_LEN_ERROR bit.
    if (pSd->cardType == CARD_SD) {
        error = Cmd16(pSd, SD_BLOCK_SIZE);
        if (error) {
            pSd->optCmdBitMap &= ~SD_CMD16_SUPPORT;
            TRACE_INFO("SD_Init.Cmd16 (%d)\n\r", error);
            TRACE_INFO("Fail to set BLK_LEN, default is 512\n\r");
        }
    }

    // Reset status for R/W
    pSd->state = SD_STATE_READY;

    // If SDIO Card
    if (pSd->cardType == CARD_SDIO) {
        pSd->blockNr = 0;
        pSd->totalSize = 0;
    }
    // If MMC Card & get size from EXT_CSD
    else if (pSd->cardType >= CARD_MMC && SD_CSD_C_SIZE(pSd) == 0xFFF) {
        pSd->blockNr = SD_EXTCSD_BLOCKNR(pSd);
        // Block number less than 0x100000000/512
        if (pSd->blockNr > 0x800000)
            pSd->totalSize = 0xFFFFFFFF;
        else
            pSd->totalSize = SD_EXTCSD_TOTAL_SIZE(pSd);
    }
    // If SD CSD v2.0
    else if(pSd->cardType >= CARD_SD
            && pSd->cardType < CARD_MMC
            && SD_CSD_STRUCTURE(pSd) >= 1) {
        pSd->blockNr   = SD_CSD_BLOCKNR_HC(pSd);
        pSd->totalSize = 0xFFFFFFFF;
    }
    // Normal card
    else {
        pSd->totalSize = SD_CSD_TOTAL_SIZE(pSd);
        pSd->blockNr = SD_CSD_BLOCKNR(pSd);
    }

    if (pSd->cardType == CARD_UNKNOWN) {
        return SD_ERROR_NOT_INITIALIZED;
    }
    else {
        return 0;
    }
}

//------------------------------------------------------------------------------
/// Stop the SDcard. This function stops all SD operations.
/// Returns 0 if successful; otherwise returns an SD_ERROR code.
/// \param pSd  Pointer to a SD card driver instance.
/// \param pSdDriver  Pointer to MCI driver already initialized.
//------------------------------------------------------------------------------
unsigned char SD_Stop(SdCard *pSd, SdDriver *pSdDriver)
{
    unsigned char error;
    SdCmd *pCommand = &(pSd->command);

    if (pSd == 0 || pSdDriver == 0)
        return 0;

    if(pCommand->tranType == MCI_CONTINUE_TRANSFER)
    {
        TRACE_DEBUG("SD_StopTransmission()\n\r");

        error = Cmd12(pSd, (pSd->state != SD_STATE_WRITE), 0);
        if(error) {
            return error;
        }
    }

    MCI_Close((Mci *)pSdDriver);
    return 0;
}

//------------------------------------------------------------------------------
/// Switch the SD/MMC card to High-Speed mode.
/// pSd->transSpeed will change to new speed limit.
/// Invoke MCI_SetSpeed() and MCI_EnableHsMode() to change MCI timing after.
/// For SD/MMC card, the speed mode will not change back until another init.
/// \param pSd      Pointer to a SD card driver instance.
/// \param hsMode   1 to enable HS mode, 0 to disable
///                 0xFF to return current mode.
/// \return current mode is hsMode is 0xFF;
///         error code if hsMode is 0 or 1.
//------------------------------------------------------------------------------
unsigned char SD_HighSpeedMode(SdCard *pSd,
                               unsigned char hsMode)
{
    unsigned char error = 0;

    if (hsMode == 0xFF)
        return pSd->mode;
    if (hsMode == 0) {
        TRACE_WARNING("Can not switch, do re-init to disable HS mode\n\r");
        return SD_ERROR_DRIVER;
    }

    // Quit transfer state
    error = MoveToTranState(pSd);
    if (error) {
        TRACE_ERROR("SD_HighSpeedMode.Tran: %d\n\r", error);
        return error;
    }

    if (pSd->mode != hsMode) {
        error = SdMmcSwitchHsMode(pSd, hsMode);
        if (error == 0)
            error = SdMmcUpdateInformation(pSd, 1, 1);
    }
    // Reset state for data R/W
    pSd->state = SD_STATE_READY;

    return error;
}

unsigned char SD_BusWidth(SdCard *pSd,
                          unsigned char busWidth)
{
    return 0;
}

#if defined(MCI2_INTERFACE) && defined(AT91C_MCI_SPCMD_BOOTREQ)
//------------------------------------------------------------------------------
/// Read Block of data in a buffer pointed by pData. The buffer size must be at
/// least 512 byte long. This function checks the SD card status register and
/// address the card if required before sending the read command.
/// Returns 0 if successful; otherwise returns an code describing the error.
/// \param pSd  Pointer to a SD card driver instance.
/// \param address  Address of the block to read.
/// \param nbBlocks Number of blocks to be read.
/// \param pData  Data buffer whose size is at least the block size.
//------------------------------------------------------------------------------
unsigned char MMC_BootRead(SdCard *pSd,
                           unsigned int   nbBlocks,
                           unsigned char *pData)
{
    unsigned char error;
    unsigned char bootAck  = 0;
    unsigned char busWidth = MCI_SDCBUS_4BIT;

    SANITY_CHECK(pSd);

    if (pSd->state != SD_STATE_BOOT)
        return SD_ERROR_DRIVER;

  #if 0
    switch(SD_EXTCSD_BOOT_BUS_WIDTH(pSd)) {
        case SD_EXTCSD_BOOT_BUS_1BIT:
            busWidth = MCI_SDCBUS_1BIT;
            break;
        case SD_EXTCSD_BOOT_BUS_8BIT:
            busWidth = MCI_SDCBUS_8BIT;
            break;
    }

    if (SD_EXTCSD_BOOT_CONFIG(pSd) & SD_EXTCSD_BOOT_PARTITION_ACK)
        bootAck = 1;
  #endif

    MCI_SetBusWidth((Mci*)pSd->pSdDriver, busWidth);
    error = BootREQ(pSd, pData, nbBlocks, bootAck);
    pSd->state = SD_STATE_BOOT;

    return error;
}

//------------------------------------------------------------------------------
/// In boot operation mode, the master can read boot data from the slave.
/// By keeping CMD line low after power-on
/// \param pSd  Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
unsigned char MMC_BootInit(SdCard *pSd)
{
    unsigned char error = 0;

    SANITY_CHECK(pSd);

    error = PonBoot(pSd);

    if (!error) {

        //error = BootREQ(pSd, 0, 0, 0);

        if (!error)
            pSd->state = SD_STATE_BOOT;
        else {
            TRACE_ERROR("MMC_BootInit.BootREQ: %d\n\r", error);
        }
    }
    else {
        TRACE_ERROR("MMC_BootInit.PonBoot: %d\n\r", error);
    }

    return error;
}

//------------------------------------------------------------------------------
/// In boot operation mode, the master can read boot data from the slave.
/// By sending CMD0 with argument + 0xFFFFFFFA
/// \param pSd  Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
unsigned char MMC_BootStart(SdCard *pSd)
{
    unsigned char error;

    SANITY_CHECK(pSd);

    if (pSd->state == SD_STATE_BOOT)
        return 0;

    if (pSd->cardType >= CARD_MMC
        && SD_CSD_STRUCTURE(pSd) >= 2
        && SD_CID_BGA(pSd) == 1
        && SD_EXTCSD_BOOT_INFO(pSd) == 1) {}
    else
        return SD_ERROR_NOT_SUPPORT;

    error = Cmd0(pSd, 0xFFFFFFFA);
    pSd->state = SD_STATE_BOOT;

    return 0;
}

//------------------------------------------------------------------------------
/// Terminate the boot operation mode
/// \param pSd  Pointer to a SD card driver instance.
//------------------------------------------------------------------------------
unsigned char MMC_BootStop(SdCard *pSd)
{
    unsigned char error;

    SANITY_CHECK(pSd);

    if (pSd->state != SD_STATE_BOOT)
        return 0;

    error = BootEnd(pSd);

    if(!error)
        pSd->state = SD_STATE_IDLE;

    return error;
}

//------------------------------------------------------------------------------
/// Setup Boot Settings
/// \param resetBus Wether bus width is reset to 1-bit after quit boot mode.
/// \param busWidth The bus width in boot operation.
/// \param bootPart The partition used for boot operation.
/// \param accPart  The partition to access with normal read/write.
/// \param bootAck  Enable boot acknoledge.
/// \return 0 or error code.
//------------------------------------------------------------------------------
unsigned char MMC_SetupBootMode(SdCard *pSd,
                                unsigned char resetBus,
                                unsigned char busWidth,
                                unsigned char bootPart,
                                unsigned char accPart,
                                unsigned char bootAck)
{
    unsigned int   status;
    unsigned short error;
    const MmcCmd6Arg bootArgs[] = {
        // BOOT_CONFIG
        {3, 179, (bootAck << 6)|(bootPart << 3)|(accPart << 0), 0},
        // BOOT_BUS_WIDTH
        {3, 177, (busWidth << 2)|(resetBus << 0), 0}
    };

    SANITY_CHECK(pSd);
    if (    pSd->cardType >= CARD_MMC
        &&  SD_CSD_STRUCTURE(pSd) >= 2
        &&  SD_CID_CBS(pSd) == 1) {}
    else return SD_ERROR_NOT_SUPPORT;
    //if (MMC_GetBootSizeKB(pSd) == 0) return SD_ERROR_NOT_SUPPORT;

    // Quit transfer state
    error = MoveToTranState(pSd);
    if (error) {
        TRACE_ERROR("MMC_SetupBootMode.Tran: %d\n\r", error);
        return error;
    }

    // Setup all boot informations
    error = MmcSwitchSettings(pSd,
                              bootArgs,
                              sizeof(bootArgs)/sizeof(MmcCmd6Arg),
                              &status);
    if (error) {
        TRACE_ERROR("MMC_SetupBootMode.Cmd6: 0x%x, %x\n\r", error, status);
        return (unsigned char)error;
    }

    // Update the EXT_CSD
  #if 1
    error = Cmd8(pSd, 0, pSd->extData);
    if (error) {
        TRACE_ERROR("MMC_SetupBootMode.Cmd8 (%d)\n\r", error);
    }

   #if 0
    if (   SD_EXTCSD_BOOT_BUS_WIDTH(pSd) != bootArgs[0].value
        || SD_EXTCSD_BOOT_CONFIG(pSd) != bootArgs[1].value ) {

        TRACE_ERROR("MMC_SetupBootMode: ExtCSD not changed\n\r");

      #if 1
        Cmd13(pSd, &status);
        TRACE_INFO("  CARD status: 0x%x\n\r", status);
      #endif
        return SD_ERROR_DRIVER;
    }
   #endif
  #endif

    // Reset state for data R/W
    pSd->state = SD_STATE_READY;

    return 0;
}

//------------------------------------------------------------------------------
/// \return 0 or error code.
//------------------------------------------------------------------------------
unsigned char MMC_StopBootMode()
{
    return 0;
}
#endif


//------------------------------------------------------------------------------
/// \return size of the card in KB
//------------------------------------------------------------------------------
unsigned int MMC_GetTotalSizeKB(SdCard *pSd)
{
    SANITY_CHECK(pSd);

    if (pSd->totalSize == 0xFFFFFFFF) {

        return pSd->blockNr / 2;
    }
    
    return pSd->totalSize / 1024;
}


//------------------------------------------------------------------------------
/// \return size of boot area if the card support boot operation.
//------------------------------------------------------------------------------
unsigned int MMC_GetBootSizeKB(SdCard *pSd)
{
    SANITY_CHECK(pSd);
    if (   pSd->cardType >= CARD_MMC
        && SD_CSD_STRUCTURE(pSd) >= 2) {

        return SD_EXTCSD_BOOT_SIZE_MULTI(pSd) * 128;
    }
    return 0;
}

#if MCI_SDIO_ENABLE
//------------------------------------------------------------------------------
/// Display the content of the CCCR
//------------------------------------------------------------------------------
void SDIO_DisplayCardInformation(SdCard *pSd)
{
    unsigned int  tmp = 0, addrCIS = 0, addrManfID = 0, addrFunc0 = 0;
    unsigned char* p = (unsigned char*)&tmp;
    unsigned char  buf[8];

    switch(pSd->cardType) {
        case CARD_SDIO:
            TRACE_INFO("** SDIO ONLY card\n\r");
            break;
        case CARD_SDCOMBO:
        case CARD_SDHCCOMBO:
            TRACE_INFO("** SDIO Combo card\n\r");
            break;
        default:
            TRACE_INFO("** NO SDIO, CCCR not supported\n\r");
            return;
    }
    // CCCR
    TRACE_INFO("====== CCCR ======\n\r");
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_CCCR_REG, p, 1);
    TRACE_INFO(".SDIO       %02X\n\r", (tmp & SDIO_SDIO) >> 4);
    TRACE_INFO(".CCCR       %02X\n\r", (tmp & SDIO_CCCR) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_SD_REV_REG, p, 1);
    TRACE_INFO(".SD         %02X\n\r", (tmp & SDIO_SD) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_IOE_REG, p, 1);
    TRACE_INFO(".IOE        %02X\n\r", (tmp & SDIO_IOE) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_IOR_REG, p, 1);
    TRACE_INFO(".IOR        %02X\n\r", (tmp & SDIO_IOR) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_IEN_REG, p, 1);
    TRACE_INFO(".IEN        %02X\n\r", (tmp & SDIO_IEN) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_INT_REG, p, 1);
    TRACE_INFO(".INT        %02X\n\r", (tmp & SDIO_INT) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_BUS_CTRL_REG, p, 1);
    TRACE_INFO(".CD         %x\n\r", (tmp & SDIO_CD) >> 7);
    TRACE_INFO(".SCSI       %x\n\r", (tmp & SDIO_SCSI) >> 6);
    TRACE_INFO(".ECSI       %x\n\r", (tmp & SDIO_ECSI) >> 5);
    TRACE_INFO(".BUS_WIDTH  %x\n\r", (tmp & SDIO_BUSWIDTH) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_CAP_REG, p, 1);
    TRACE_INFO(".4BLS       %x\n\r", (tmp & SDIO_4BLS) >> 7);
    TRACE_INFO(".LSC        %x\n\r", (tmp & SDIO_LSC) >> 6);
    TRACE_INFO(".E4MI       %x\n\r", (tmp & SDIO_E4MI) >> 5);
    TRACE_INFO(".S4MI       %x\n\r", (tmp & SDIO_S4MI) >> 4);
    TRACE_INFO(".SBS        %x\n\r", (tmp & SDIO_SBS) >> 3);
    TRACE_INFO(".SRW        %x\n\r", (tmp & SDIO_SRW) >> 2);
    TRACE_INFO(".SMB        %x\n\r", (tmp & SDIO_SMB) >> 1);
    TRACE_INFO(".SDC        %x\n\r", (tmp & SDIO_SDC) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_CIS_PTR_REG, p, 3);
    TRACE_INFO(".CIS_PTR    %06X\n\r", tmp);
    addrCIS = tmp; tmp = 0;
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_BUS_SUSP_REG, p, 1);
    TRACE_INFO(".BR         %x\n\r", (tmp & SDIO_BR) >> 1);
    TRACE_INFO(".BS         %x\n\r", (tmp & SDIO_BS) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_FUN_SEL_REG, p, 1);
    TRACE_INFO(".DF         %x\n\r", (tmp & SDIO_DF) >> 7);
    TRACE_INFO(".FS         %x\n\r", (tmp & SDIO_FS) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_EXEC_REG, p, 1);
    TRACE_INFO(".EX         %x\n\r", (tmp & SDIO_EX));
    TRACE_INFO(".EXM        %x\n\r", (tmp & SDIO_EXM) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_READY_REG, p, 1);
    TRACE_INFO(".RF         %x\n\r", (tmp & SDIO_RF));
    TRACE_INFO(".RFM        %x\n\r", (tmp & SDIO_RFM) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_FN0_BLKSIZ_REG, p, 2);
    TRACE_INFO(".FN0_SIZE   %d(%04X)\n\r", tmp, tmp);
    tmp = 0;
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_POWER_REG, p, 1);
    TRACE_INFO(".EMPC       %x\n\r", (tmp & SDIO_EMPC) >> 1);
    TRACE_INFO(".SMPC       %x\n\r", (tmp & SDIO_SMPC) >> 0);
    SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_HS_REG, p, 1);
    TRACE_INFO(".EHS        %x\n\r", (tmp & SDIO_EHS) >> 1);
    TRACE_INFO(".SHS        %x\n\r", (tmp & SDIO_SHS) >> 0);
    // Metaformat
    SdioFindTuples(pSd, addrCIS, 128, &addrManfID, &addrFunc0);
    if (addrManfID != 0) {
        SDIO_ReadDirect(pSd, SDIO_CIA, addrManfID, buf, 6);
        TRACE_INFO("==== CISTPL_MANFID ====\n\r");
        TRACE_INFO("._MANF %04X\n\r", buf[2] + (buf[3] << 8));
        TRACE_INFO("._CARD %04X\n\r", buf[4] + (buf[5] << 8));
    }
    if (addrFunc0 != 0) {
        SDIO_ReadDirect(pSd, SDIO_CIA, addrFunc0, buf, 6);
        TRACE_INFO("== CISTPL_FUNCE Fun0 ==\n\r");
        TRACE_INFO("._FN0_BLK_SIZE   %d(0x%04X)\n\r",
            buf[3] + (buf[4] << 8), buf[3] + (buf[4] << 8));
        TRACE_INFO("._MAX_TRAN_SPEED %02X\n\r", buf[5]);
    }
}
#endif

//------------------------------------------------------------------------------
/// Display the content of the CID register
/// \param pCid  Pointer to the Cid register value
//------------------------------------------------------------------------------
void SD_DisplayRegisterCID(SdCard *pSd)
{
    // CID for memory card only
    if (pSd->cardType == CARD_UNKNOWN || pSd->cardType >= CARD_SDIO)
        return;

    TRACE_INFO("======= CID =======\n\r");
  #if 0
    TRACE_INFO(" .Card/BGA         %X\n\r", SD_CID_BGA(pSd));
  #else
    TRACE_INFO("CID MID Manufacturer ID       %02X\n\r",
        SD_CID_MID(pSd));
    
    TRACE_INFO("CID OID OEM/Application ID    %c%c\n\r",
        (char)SD_CID_OID_BYTE_1(pSd),
        (char)SD_CID_OID_BYTE_0(pSd));
    
    TRACE_INFO("CID PNM Product revision      %c%c%c%c%c\n\r",
        (char)SD_CID_PNM_BYTE_4(pSd),
        (char)SD_CID_PNM_BYTE_3(pSd),
        (char)SD_CID_PNM_BYTE_2(pSd),
        (char)SD_CID_PNM_BYTE_1(pSd),
        (char)SD_CID_PNM_BYTE_0(pSd));
    
    TRACE_INFO("CID PRV Product serial number %02X%04X\n\r", 
         SD_CID_PRV_2(pSd),
         SD_CID_PRV_1(pSd));

    TRACE_INFO("CID MDT Manufacturing date    %04d/%02d\n\r",
        (unsigned short)SD_CID_MDT_YEAR(pSd),
        (unsigned char)SD_CID_MDT_MONTH(pSd));               
    
    TRACE_INFO("CID CRC checksum              %02X\n\r",   
         SD_CID_CRC(pSd));
  #endif
}

//------------------------------------------------------------------------------
/// Display the content of the CSD register
/// \param pSd  
//------------------------------------------------------------------------------
void SD_DisplayRegisterCSD(SdCard *pSd)
{ 
    // CID for memory card only
    if (pSd->cardType == CARD_UNKNOWN || pSd->cardType >= CARD_SDIO)
        return;

    TRACE_INFO("======== CSD ========");
  #if 0
  {
    unsigned int i;
    unsigned char *p = (unsigned char *)pSd->csd;
    for(i = 0; i < 128 / 8; i++) {
        if ((i % 16) == 0) TRACE_INFO_WP("\n\r [%3d]:", i);
        TRACE_INFO_WP(" %2x", p[i]);
    }
    TRACE_INFO_WP("\n\r");
    TRACE_INFO("------------------------\n\r");
  }
  #else
    TRACE_INFO_WP("\n\r");
  #endif
    TRACE_INFO(" .CSD_STRUCTURE      0x%x\r\n", SD_CSD_STRUCTURE(pSd));
    TRACE_INFO(" .SPEC_VERS          0x%x\r\n", SD_CSD_SPEC_VERS(pSd));
    TRACE_INFO(" .TAAC               0x%X\r\n", SD_CSD_TAAC(pSd)              );
    TRACE_INFO(" .NSAC               0x%X\r\n", SD_CSD_NSAC(pSd)              );
    TRACE_INFO(" .TRAN_SPEED         0x%X\r\n", SD_CSD_TRAN_SPEED(pSd)        );
    TRACE_INFO(" .CCC                0x%X\r\n", SD_CSD_CCC(pSd)               );
    TRACE_INFO(" .READ_BL_LEN        0x%X\r\n", SD_CSD_READ_BL_LEN(pSd)       );
    TRACE_INFO(" .READ_BL_PARTIAL    0x%X\r\n", SD_CSD_READ_BL_PARTIAL(pSd)   );
    TRACE_INFO(" .WRITE_BLK_MISALIGN 0x%X\r\n", SD_CSD_WRITE_BLK_MISALIGN(pSd));
    TRACE_INFO(" .READ_BLK_MISALIGN  0x%X\r\n", SD_CSD_READ_BLK_MISALIGN(pSd) );
    TRACE_INFO(" .DSR_IMP            0x%X\r\n", SD_CSD_DSR_IMP(pSd)           );
    TRACE_INFO(" .C_SIZE             0x%X\r\n", SD_CSD_C_SIZE(pSd)            );
    TRACE_INFO(" .C_SIZE_HC          0x%X\r\n", SD_CSD_C_SIZE_HC(pSd)         );
    TRACE_INFO(" .VDD_R_CURR_MIN     0x%X\r\n", SD_CSD_VDD_R_CURR_MIN(pSd)    );
    TRACE_INFO(" .VDD_R_CURR_MAX     0x%X\r\n", SD_CSD_VDD_R_CURR_MAX(pSd)    );
    TRACE_INFO(" .VDD_W_CURR_MIN     0x%X\r\n", SD_CSD_VDD_W_CURR_MIN(pSd)    );
    TRACE_INFO(" .VDD_W_CURR_MAX     0x%X\r\n", SD_CSD_VDD_W_CURR_MAX(pSd)    );
    TRACE_INFO(" .C_SIZE_MULT        0x%X\r\n", SD_CSD_C_SIZE_MULT(pSd)       );
    TRACE_INFO(" .ERASE_BLK_EN       0x%X\r\n", SD_CSD_ERASE_BLK_EN(pSd)      );
    TRACE_INFO(" .SECTOR_SIZE        0x%X\r\n", SD_CSD_SECTOR_SIZE(pSd)       );
    TRACE_INFO(" .WP_GRP_SIZE        0x%X\r\n", SD_CSD_WP_GRP_SIZE(pSd)       );
    TRACE_INFO(" .WP_GRP_ENABLE      0x%X\r\n", SD_CSD_WP_GRP_ENABLE(pSd)     );
    TRACE_INFO(" .R2W_FACTOR         0x%X\r\n", SD_CSD_R2W_FACTOR(pSd)        );
    TRACE_INFO(" .WRITE_BL_LEN       0x%X\r\n", SD_CSD_WRITE_BL_LEN(pSd)      );
    TRACE_INFO(" .WRITE_BL_PARTIAL   0x%X\r\n", SD_CSD_WRITE_BL_PARTIAL(pSd)  );
    TRACE_INFO(" .FILE_FORMAT_GRP    0x%X\r\n", SD_CSD_FILE_FORMAT_GRP(pSd)   );
    TRACE_INFO(" .COPY               0x%X\r\n", SD_CSD_COPY(pSd)              );
    TRACE_INFO(" .PERM_WRITE_PROTECT 0x%X\r\n", SD_CSD_PERM_WRITE_PROTECT(pSd));
    TRACE_INFO(" .TMP_WRITE_PROTECT  0x%X\r\n", SD_CSD_TMP_WRITE_PROTECT(pSd) );
    TRACE_INFO(" .FILE_FORMAT        0x%X\r\n", SD_CSD_FILE_FORMAT(pSd)       );
    TRACE_INFO(" .ECC                0x%X\r\n", SD_CSD_ECC(pSd)               );
    TRACE_INFO(" .CRC                0x%X\r\n", SD_CSD_CRC(pSd)               );
    TRACE_INFO(" .MULT               0x%X\r\n", SD_CSD_MULT(pSd)              );
    TRACE_INFO(" .BLOCKNR            0x%X\r\n", SD_CSD_BLOCKNR(pSd)           );
    TRACE_INFO(" .BLOCKNR_HC         0x%X\r\n", SD_CSD_BLOCKNR_HC(pSd)        );
    TRACE_INFO(" .BLOCK_LEN          0x%X\r\n", SD_CSD_BLOCK_LEN(pSd)         );
    TRACE_INFO(" .TOTAL_SIZE         0x%X\r\n", SD_CSD_TOTAL_SIZE(pSd)        );
    TRACE_INFO(" .TOTAL_SIZE_HC      0x%X\r\n", SD_CSD_TOTAL_SIZE_HC(pSd)     );
    TRACE_INFO(" -SD_TOTAL_SIZE      0x%X\r\n", SD_TOTAL_SIZE(pSd)            );
    TRACE_INFO(" -SD_TOTAL_BLOCK     0x%X\r\n", SD_TOTAL_BLOCK(pSd)           );
}   

//------------------------------------------------------------------------------
/// Display the content of the EXT_CSD register
/// \param pSd  
//------------------------------------------------------------------------------
void SD_DisplayRegisterECSD(SdCard *pSd)
{
    if (pSd->cardType >= CARD_MMC && pSd->cardType <= CARD_MMCHD
        && SD_CSD_STRUCTURE(pSd) >= 2) {}
    else {
        TRACE_INFO("** EXT_CSD NOT SUPPORTED\n\r");
        return;
    }
    TRACE_INFO("======= EXT_CSD =======");
  #if 0
  {
    unsigned int i;
    unsigned char *p = (unsigned char *)pSd->extData;
    for(i = 0; i < 512; i++) {
        if ((i % 16) == 0) TRACE_INFO_WP("\n\r [%3d]:", i);
        TRACE_INFO_WP(" %2x", p[i]);
    }
    TRACE_INFO_WP("\n\r");
    TRACE_INFO("------------------------\n\r");
  }
  #else
    TRACE_INFO_WP("\n\r");
  #endif
    TRACE_INFO(" .S_CMD_SET            : 0x%X\n\r",
        SD_EXTCSD_S_CMD_SET(pSd));
    TRACE_INFO(" .BOOT_INFO            : 0x%X\n\r",
        SD_EXTCSD_BOOT_INFO(pSd));
    TRACE_INFO(" .BOOT_SIZE_MULTI      : 0x%X\n\r",
        SD_EXTCSD_BOOT_SIZE_MULTI(pSd));
    TRACE_INFO(" .ACC_SIZE             : 0x%X\n\r",
        SD_EXTCSD_ACC_SIZE(pSd));
    TRACE_INFO(" .HC_ERASE_GRP_SIZE    : 0x%X\n\r",
        SD_EXTCSD_HC_ERASE_GRP_SIZE(pSd));
    TRACE_INFO(" .ERASE_TIMEOUT_MULT   : 0x%X\n\r",
        SD_EXTCSD_ERASE_TIMEOUT_MULT(pSd));
    TRACE_INFO(" .REL_WR_SEC_C         : 0x%X\n\r",
        SD_EXTCSD_REL_WR_SEC_C(pSd));
    TRACE_INFO(" .HC_WP_GRP_SIZE       : 0x%X\n\r",
        SD_EXTCSD_HC_WP_GRP_SIZE(pSd));
    TRACE_INFO(" .S_C_VCC              : 0x%X\n\r",
        SD_EXTCSD_S_C_VCC(pSd));
    TRACE_INFO(" .S_C_VCCQ             : 0x%X\n\r",
        SD_EXTCSD_S_C_VCCQ(pSd));
    TRACE_INFO(" .S_A_TIMEOUT          : 0x%X\n\r",
        SD_EXTCSD_S_A_TIMEOUT(pSd));
    TRACE_INFO(" .SEC_COUNT            : 0x%X\n\r",
        SD_EXTCSD_SEC_COUNT(pSd));
    TRACE_INFO(" .MIN_PERF_W_8_52      : 0x%X\n\r",
        SD_EXTCSD_MIN_PERF_W_8_52(pSd));
    TRACE_INFO(" .MIN_PERF_R_8_52      : 0x%X\n\r",
        SD_EXTCSD_MIN_PERF_R_8_52(pSd));
    TRACE_INFO(" .MIN_PERF_W_8_26_4_52 : 0x%X\n\r",
        SD_EXTCSD_MIN_PERF_W_8_26_4_52(pSd));
    TRACE_INFO(" .MIN_PERF_R_8_26_4_52 : 0x%X\n\r",
        SD_EXTCSD_MIN_PERF_R_8_26_4_52(pSd));
    TRACE_INFO(" .MIN_PERF_W_4_26      : 0x%X\n\r",
        SD_EXTCSD_MIN_PERF_W_4_26(pSd));
    TRACE_INFO(" .MIN_PERF_R_4_26      : 0x%X\n\r",
        SD_EXTCSD_MIN_PERF_R_4_26(pSd));
    TRACE_INFO(" .PWR_CL_26_360        : 0x%X\n\r",
        SD_EXTCSD_PWR_CL_26_360(pSd));
    TRACE_INFO(" .PWR_CL_52_360        : 0x%X\n\r",
        SD_EXTCSD_PWR_CL_52_360(pSd));
    TRACE_INFO(" .PWR_CL_26_195        : 0x%X\n\r",
        SD_EXTCSD_PWR_CL_26_195(pSd));
    TRACE_INFO(" .PWR_CL_52_195        : 0x%X\n\r",
        SD_EXTCSD_PWR_CL_52_195(pSd));
    TRACE_INFO(" .CARD_TYPE            : 0x%X\n\r",
        SD_EXTCSD_CARD_TYPE(pSd));
    TRACE_INFO(" .CSD_STRUCTURE        : 0x%X\n\r",
        SD_EXTCSD_CSD_STRUCTURE(pSd));
    TRACE_INFO(" .EXT_CSD_REV          : 0x%X\n\r",
        SD_EXTCSD_EXT_CSD_REV(pSd));
    TRACE_INFO(" .CMD_SET              : 0x%X\n\r",
        SD_EXTCSD_CMD_SET(pSd));
    TRACE_INFO(" .CMD_SET_REV          : 0x%X\n\r",
        SD_EXTCSD_CMD_SET_REV(pSd));
    TRACE_INFO(" .POWER_CLASS          : 0x%X\n\r",
        SD_EXTCSD_POWER_CLASS(pSd));
    TRACE_INFO(" .HS_TIMING            : 0x%X\n\r",
        SD_EXTCSD_HS_TIMING(pSd));
    TRACE_INFO(" .BUS_WIDTH            : 0x%X\n\r",
        SD_EXTCSD_BUS_WIDTH(pSd));
    TRACE_INFO(" .ERASED_MEM_CONT      : 0x%X\n\r",
        SD_EXTCSD_ERASED_MEM_CONT(pSd));
    TRACE_INFO(" .BOOT_CONFIG          : 0x%X\n\r",
        SD_EXTCSD_BOOT_CONFIG(pSd));
    TRACE_INFO(" .BOOT_BUS_WIDTH       : 0x%X\n\r",
        SD_EXTCSD_BOOT_BUS_WIDTH(pSd));
    TRACE_INFO(" .ERASE_GROUP_DEF      : 0x%X\n\r",
        SD_EXTCSD_ERASE_GROUP_DEF(pSd));
}

//------------------------------------------------------------------------------
/// Display the content of the SCR register
/// \param pSd  Pointer to SdCard instance.
//------------------------------------------------------------------------------
void SD_DisplayRegisterSCR(SdCard *pSd)
{ 
    if (pSd->cardType >= CARD_SD && pSd->cardType <= CARD_SDHC) {}
    else {
        TRACE_INFO("** SCR NOT Supported!\n\r");
        return;
    }
    TRACE_INFO("========== SCR ==========");
  #if 0
  {
    unsigned int i;
    unsigned char *p = (unsigned char*)pSd->extData;
    //TRACE_INFO_WP("\n\r");
    //TRACE_INFO("DATA @ 0x%X", (unsigned int)p);
    for(i = 0; i < 16; i ++) {
        if ((i % 8) == 0) TRACE_INFO_WP("\n\r [%3d]:", i);
        TRACE_INFO_WP(" %02x", p[i]);
    }
    TRACE_INFO_WP("\n\r");
    TRACE_INFO("------------------------\n\r");
  }
  #else
    TRACE_INFO_WP("\n\r");
  #endif

    TRACE_INFO(" .SCR_STRUCTURE         :0x%X\n\r",
        SD_SCR_SCR_STRUCTURE(pSd));
    TRACE_INFO(" .SD_SPEC               :0x%X\n\r",
        SD_SCR_SD_SPEC(pSd));
    TRACE_INFO(" .DATA_STAT_AFTER_ERASE :0x%X\n\r",
        SD_SCR_DATA_STAT_AFTER_ERASE(pSd));
    TRACE_INFO(" .SD_SECURITY           :0x%X\n\r",
        SD_SCR_SD_SECURITY(pSd));
    TRACE_INFO(" .SD_BUS_WIDTHS         :0x%X\n\r",
        SD_SCR_SD_BUS_WIDTHS(pSd));
}

//------------------------------------------------------------------------------
/// Display the content of the SD Status
/// \param pSd  Pointer to SdCard instance.
//------------------------------------------------------------------------------
void SD_DisplaySdStatus(SdCard *pSd)
{
    if (   pSd->cardType >= CARD_SD
        && pSd->cardType <= CARD_SDHC
        && (pSd->optCmdBitMap & SD_ACMD13_SUPPORT) ) {}
    else {
        TRACE_INFO("** SD Status NOT Supported!\n\r");
        return;
    }
    TRACE_INFO("=========== STAT ============");
  #if 0
  {
    unsigned int i;
    unsigned char *p = (unsigned char*)pSd->extData;
    //TRACE_INFO_WP("\n\r");
    //TRACE_INFO("DATA @ 0x%X", (unsigned int)p);
    for(i = 0; i < 72; i ++) {
        if ((i % 8) == 0) TRACE_INFO_WP("\n\r [%3d]:", i);
        TRACE_INFO_WP(" %02x", p[i]);
    }
    TRACE_INFO_WP("\n\r");
    TRACE_INFO("------------------------\n\r");
  }
  #else
    TRACE_INFO_WP("\n\r");
  #endif

    TRACE_INFO(" .DAT_BUS_WIDTH          :0x%X\n\r",
        SD_STAT_DAT_BUS_WIDTH(pSd));
    TRACE_INFO(" .SECURED_MODE           :0x%X\n\r",
        SD_STAT_SECURED_MODE(pSd));
    TRACE_INFO(" .SD_CARD_TYPE           :0x%X\n\r",
        SD_STAT_SD_CARD_TYPE(pSd));
    TRACE_INFO(" .SIZE_OF_PROTECTED_AREA :0x%X\n\r",
        SD_STAT_SIZE_OF_PROTECTED_AREA(pSd));
    TRACE_INFO(" .SPEED_CLASS            :0x%X\n\r",
        SD_STAT_SPEED_CLASS(pSd));
    TRACE_INFO(" .PERFORMANCE_MOVE       :0x%X\n\r",
        SD_STAT_PERFORMANCE_MOVE(pSd));
    TRACE_INFO(" .AU_SIZE                :0x%X\n\r",
        SD_STAT_AU_SIZE(pSd));
    TRACE_INFO(" .ERASE_SIZE             :0x%X\n\r",
        SD_STAT_ERASE_SIZE(pSd));
    TRACE_INFO(" .ERASE_TIMEOUT          :0x%X\n\r",
        SD_STAT_ERASE_TIMEOUT(pSd));
    TRACE_INFO(" .ERASE_OFFSET           :0x%X\n\r",
        SD_STAT_ERASE_OFFSET(pSd));
}
