changeset 3256:4d4c7b86b1c8

Add definitions of V2 specific commands, responses and card structure. [ Bugzilla 1001764 ]
author vae
date Mon, 01 Jul 2013 17:39:53 +0000
parents fa7c73ed1dac
children b3010a200e62
files packages/devs/disk/generic/mmc/current/ChangeLog packages/devs/disk/generic/mmc/current/include/mmc_protocol.h packages/devs/disk/generic/mmc/current/src/mmc_spi.c
diffstat 3 files changed, 300 insertions(+), 98 deletions(-) [+]
line wrap: on
line diff
--- a/packages/devs/disk/generic/mmc/current/ChangeLog
+++ b/packages/devs/disk/generic/mmc/current/ChangeLog
@@ -1,3 +1,9 @@
+2013-02-18  Ilija Stanislevik  <ilijas@siva.mk>
+
+	* src/mmc_spi.c: Add support for high capacity SD cards.
+	* include/mmc_protocol.h: Add definitions of V2 specific commands,
+	responses and card structure. [ Bugzilla 1001764 ]
+
 2013-02-21  Mike Jones  <mike@proclivis.com>
 
 	* src/mmc_spi.c (mmc_spi_check_for_disk): Modify DEBUG1 to to show
--- a/packages/devs/disk/generic/mmc/current/include/mmc_protocol.h
+++ b/packages/devs/disk/generic/mmc/current/include/mmc_protocol.h
@@ -102,9 +102,17 @@
 #define MMC_REQUEST_READ_OCR                0x3A
 #define MMC_REQUEST_CRC_ON_OFF              0x3B
 
+// SD V2.x specific commands
+#define SD_REQUEST_SEND_IF_COND             8
+#define SD_REQUEST_SD_SEND_OP_COND          41
+#define SD_REQUEST_APP_CMD                  55
+
 // Response formats are different for MMC vs. SPI mode, so are defined
 // in the appropriate source files.
 
+// SD V2.x specific responses
+#define SD_ARGUMENT_HCS                    ( 1 << 30 )
+
 // The CID register is generally treated as an opaque data structure
 // used only for unique identification of cards.
 typedef struct mmc_cid_register {
@@ -122,7 +130,7 @@ typedef struct mmc_cid_register {
                                                          ((_data_)->cid_data[13]))
 #define MMC_CID_REGISTER_MDT(_data_)                    ((_data_)->cid_data[14])
 #define MMC_CID_REGISTER_CRC(_data_)                    ((_data_)->cid_data[15] >> 1)
-                                                         
+
 
 // The CSD register is just lots of small bitfields. For now keep it
 // as an array of 16 bytes and provide macros to read the fields.
@@ -170,6 +178,19 @@ typedef struct mmc_csd_register {
 #define MMC_CSD_REGISTER_ECC(_data_)                    ((_data_)->csd_data[14] & 0x0003)
 #define MMC_CSD_REGISTER_CRC(_data_)                    (((_data_)->csd_data[15] & 0xFE) >> 1)
 
+// CSD V2.x structure
+#define SD_CSD_V2_REGISTER_C_SIZE(_data_)              ((((_data_)->csd_data[7] & 0x007f) << 16) |     \
+                                                          ((_data_)->csd_data[8] << 8)            |     \
+                                                          ((_data_)->csd_data[9] ))
+
+// OCR
+typedef struct sd_ocr_register {
+    cyg_uint8   ocr_data[4];
+} sd_ocr_register_t;
+
+#define SD_OCR_REGISTER_POWER_UP(_data_)               (((_data_)->ocr_data[0] & 0x80) >> 7)
+// CCS bit denotes Standard (0) or High/Extended (1) capacity
+#define SD_OCR_REGISTER_CCS(_data_)                    (((_data_)->ocr_data[0] & 0x40) >> 6)
 
 #endif //  ifdef CYGONCE_DEVS_DISK_MMC_PROTOCOL_H
 
--- a/packages/devs/disk/generic/mmc/current/src/mmc_spi.c
+++ b/packages/devs/disk/generic/mmc/current/src/mmc_spi.c
@@ -113,6 +113,11 @@ cyg_bool    cyg_mmc_spi_polled = false;
 // ----------------------------------------------------------------------------
 // SPI-specific parts of the MMC protocol.
 //
+// Host supply voltage information
+#define MMC_SPI_VHS                         0x01  // 2.7 - 3.6V
+// Check pattern
+#define MMC_CMD8_CHECK_PATTERN              0xaa
+//
 // The main response byte. 0 indicates success, other bits
 // indicate various error conditions.
 #define MMC_REPLY_SUCCESS                   0x00
@@ -163,14 +168,20 @@ static cyg_uint8        mmc_spi_ff_data[
     memset(mmc_spi_ff_data, 0x00FF, 512);   \
     CYG_MACRO_END
 
+typedef enum sd_capacity_e {
+  STANDARD_CAPACITY,
+  HIGH_CAPACITY,
+  EXTENDED_CAPACITY
+} sd_capacity_t;
+
 // Details of a specific MMC card
 typedef struct cyg_mmc_spi_disk_info_t {
     cyg_spi_device*     mmc_spi_dev;
     cyg_uint32          mmc_saved_baudrate;
     cyg_uint32          mmc_block_count;
-#ifdef MMC_SPI_BACKGROUND_WRITES    
+#ifdef MMC_SPI_BACKGROUND_WRITES
     cyg_bool            mmc_writing;
-#endif    
+#endif
     cyg_bool            mmc_read_only;
     cyg_bool            mmc_connected;
     cyg_uint32          mmc_heads_per_cylinder;
@@ -178,6 +189,10 @@ typedef struct cyg_mmc_spi_disk_info_t {
     cyg_uint32          mmc_read_block_length;
     cyg_uint32          mmc_write_block_length;
     mmc_cid_register    mmc_id;
+    sd_ocr_register_t   sd_ocr;
+    cyg_uint32          sd_version;
+    sd_capacity_t       sd_capacity;
+    cyg_uint32          sd_csd_version;
 } cyg_mmc_spi_disk_info_t;
 
 // There is no need for a hardware-specific disk controller structure.
@@ -201,7 +216,7 @@ mmc_spi_send_init(cyg_mmc_spi_disk_info_
     cyg_spi_bus     *bus = dev->spi_bus;
 #endif
 
-    DEBUG2("mmc_spi_send_init(): dev pointer 0x%p, %d\n", disk->mmc_spi_dev, cyg_mmc_spi_polled );
+    DEBUG2("%s(): dev pointer 0x%p, %d\n", __FUNCTION__, disk->mmc_spi_dev, cyg_mmc_spi_polled );
     DEBUG2("                   : begin pointer %p\n", bus->spi_transaction_begin );
     cyg_spi_tick(disk->mmc_spi_dev, cyg_mmc_spi_polled, 10);
 }
@@ -218,7 +233,7 @@ mmc_spi_send_command_start(cyg_mmc_spi_d
     cyg_uint8       reply;
     int             i;
 
-#ifdef MMC_SPI_BACKGROUND_WRITES    
+#ifdef MMC_SPI_BACKGROUND_WRITES
     // If the last operation was a block write, those can take a while
     // to complete. Rather than wait at the end of the write(), do so
     // at the beginning of the next operation i.e. here. This also
@@ -233,25 +248,29 @@ mmc_spi_send_command_start(cyg_mmc_spi_d
     // the end, Either way, when the card sends a byte 0xff it should
     // be ready for the next command.
     if (disk->mmc_writing) {
-        DEBUG2("mmc_spi_send_command_start(): polling for completion of previous write\n");
+        DEBUG2("%s(): polling for completion of previous write\n", __FUNCTION__);
         disk->mmc_writing   = 0;
         response[0]    = 0x00;
         for (i = 0; (i < MMC_SPI_WRITE_BUSY_RETRIES) && (0x00FF != response[0]); i++) {
             cyg_spi_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, response);
         }
     }
-#endif    
-    
+#endif
+
     request[0]  = command | 0x0040;
     request[1]  = (arg >> 24) & 0x00FF;
     request[2]  = (arg >> 16) & 0x00FF;
     request[3]  = (arg >>  8) & 0x00FF;
     request[4]  = arg         & 0x00FF;
     // A CRC is needed for the go-idle-state command, because that
-    // command switches the device from MMC to SPI mode. That CRC is
-    // well-known. Once in SPI mode the card will not use CRCs by
-    // default.
-    request[5]  = (command == 0x00) ? 0x0095 : 0x00ff;
+    // command switches the device from MMC to SPI mode.  Also, a CRC
+    // is needed in case of CMD8. Here we take for granted that the
+    // host has 2.7-3.6V range and that the check pattern is 0xaa.
+    // These CRCs are well-known. Once in SPI mode the card will not
+    // use CRCs by default.
+    request[5]  = (command == 0x00) ? 0x0095 :
+                  (command == SD_REQUEST_SEND_IF_COND) ? 0x87 : 0xff;
+
     // There will need to be at least one extra byte transfer to get
     // the card's response, so send that straightaway. Extra
     // outgoing data like this should be 0xff so that the card
@@ -261,13 +280,13 @@ mmc_spi_send_command_start(cyg_mmc_spi_d
     // Lock the SPI bus. It remains locked until a subsequent call to
     // mmc_spi_end_command().
     cyg_spi_transaction_begin(dev);
-    
+
     // Transfer the whole command, and try to read the response back
     // immediately.
     cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 7, request, response, 0);
     DEBUG2("Sent command %02x %d: reply bytes %02x %02x %02x %02x %02x %02x %02x\n", command, arg, \
                 response[0], response[1], response[2], response[3], response[4], response[5], response[6]);
-    
+
     // The response will be a single byte with the top bit clear.
     // The remaining bits are error/status flags. If the command
     // involves an additional response then that will be handled
@@ -339,9 +358,9 @@ mmc_spi_read_data(cyg_mmc_spi_disk_info_
     for (i = 0; (i < retries) && (0x00FF == response[0]); i++) {
         cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, response, 0);
     }
-    
+
     if (MMC_DATA_TOKEN_SUCCESS != response[0]) {
-        DEBUG1("mmc_spi_read_data(): got error response %02x after %d iterations\n", response[0], i);
+        DEBUG1("%s(): got error response %02x after %d iterations\n", __FUNCTION__, response[0], i);
         return response[0];
     }
 
@@ -350,27 +369,100 @@ mmc_spi_read_data(cyg_mmc_spi_disk_info_
     cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, count, mmc_spi_ff_data, buf, 0);
     // And the CRC, which can be ignored
     cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 2, mmc_spi_ff_data, response, 0);
-    DEBUG2("mmc_spi_read_data(): got data and CRC %02x %02x\n", response[0], response[1]);
-    
+    DEBUG2("%s(): got data and CRC %02x %02x\n", __FUNCTION__, response[0], response[1]);
+
     return MMC_DATA_TOKEN_SUCCESS;
 }
 
+// Determine if card complies with SD Physical Spec. Version 1.x or 2.x
+// If it is V1, also set information on capacity. MMC cards are recognized as SD V1.
+static Cyg_ErrNo
+mmc_spi_check_version(cyg_mmc_spi_disk_info_t* disk)
+{
+#define R7_RESPONSE_LENGTH 5
+  cyg_uint32  reply;
+  cyg_uint8   response[R7_RESPONSE_LENGTH];
+  cyg_spi_device* dev = disk->mmc_spi_dev;
+
+  disk->sd_version = 0;
+  reply = mmc_spi_send_command_start(disk, SD_REQUEST_SEND_IF_COND,
+          ( MMC_SPI_VHS << 8 ) + MMC_CMD8_CHECK_PATTERN);
+  DEBUG2("SEND_IF_COND (CMD8) reply: %02x\n", reply);
+
+  if (MMC_REPLY_ILLEGAL_COMMAND & reply) { // V1 cards do not understand CMD8
+    disk->sd_version = 1;
+    disk->sd_capacity = STANDARD_CAPACITY;
+    mmc_spi_end_command(disk);
+    return ENOERR;
+  }
+  disk->sd_version = 2;
+  if (~MMC_REPLY_IN_IDLE_STATE & reply) { // Anything other than idle state?
+    DEBUG1("SEND_IF_COND (CMD8) replies error %02x\n", reply);
+    mmc_spi_end_command(disk);
+    return -EIO;
+  }
+
+  // Card gives no error, so the response is R7. Take the rest of it.
+  cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled,
+      R7_RESPONSE_LENGTH, mmc_spi_ff_data, response, 0);
+  DEBUG2("%s(): R7: %02x %02x %02x %02x %02x.\n", __FUNCTION__,
+      response[0], response[1], response[2], response[3], response[4]);
+  if (0 == (MMC_SPI_VHS & response[2])) {
+    DEBUG1("%s(): card doesn't accept voltage %02x\n", __FUNCTION__, MMC_SPI_VHS);
+    mmc_spi_end_command(disk);
+    return -ENOTSUP;
+  }
+
+  mmc_spi_end_command(disk);
+  return ENOERR;
+}
+
+static Cyg_ErrNo
+mmc_spi_sd_check_v2_capacity_class(cyg_mmc_spi_disk_info_t* disk)
+{
+#define R3_RESPONSE_LENGTH 4
+  cyg_uint32  reply;
+  cyg_spi_device* dev = disk->mmc_spi_dev;
+
+  reply = mmc_spi_send_command_start(disk, MMC_REQUEST_READ_OCR, 0);
+  if (MMC_REPLY_SUCCESS != reply) {
+    DEBUG1("READ_OCR (CMD58) replies error %02x\n", reply);
+    mmc_spi_end_command(disk);
+    return -EIO;
+  }
+
+  // Card gives no error. Take the rest of response.
+  cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled,
+      R3_RESPONSE_LENGTH, mmc_spi_ff_data, disk->sd_ocr.ocr_data, 0);
+  DEBUG2("%s(): R3: %02x %02x %02x %02x.\n", __FUNCTION__,
+      disk->sd_ocr.ocr_data[0], disk->sd_ocr.ocr_data[1], disk->sd_ocr.ocr_data[2],
+      disk->sd_ocr.ocr_data[3]);
+  mmc_spi_end_command(disk);
+
+  disk->sd_capacity = (SD_OCR_REGISTER_CCS(&(disk->sd_ocr))) ?
+      HIGH_CAPACITY : STANDARD_CAPACITY ;
+
+  return ENOERR;
+}
+
 // Read one of the card registers, e.g. CSD or CID
 static Cyg_ErrNo
 mmc_spi_read_register(cyg_mmc_spi_disk_info_t* disk, cyg_uint32 command, cyg_uint8* buf, cyg_uint32 count)
 {
     cyg_uint32      reply;
-    
+
     reply = mmc_spi_send_command_start(disk, command, 0);
     if (MMC_REPLY_SUCCESS != reply) {
-        DEBUG1("mmc_spi_read_register(): unexpected response to command %02x, reply code %02x\n", command, reply);
+        DEBUG1("%s(): unexpected response to command %02x, reply code %02x\n",
+            __FUNCTION__, command, reply);
         mmc_spi_end_command(disk);
         return (0x00FF == reply) ? -ENODEV : -EIO;
     }
     reply = mmc_spi_read_data(disk, buf, count, false);
     mmc_spi_end_command(disk);
     if (MMC_DATA_TOKEN_SUCCESS != reply) {
-        DEBUG1("mmc_spi_read_register(): unexpected response to command %02x, expected 0x00FE data token, got %02x\n", command, reply);
+        DEBUG1("%s(): unexpected response to command %02x, expected 0x00FE data token, got %02x\n",
+            __FUNCTION__, command, reply);
         return -EIO;
     }
     return ENOERR;
@@ -384,12 +476,18 @@ static Cyg_ErrNo
 mmc_spi_read_disk_block(cyg_mmc_spi_disk_info_t* disk, cyg_uint8* buf, cyg_uint32 block, cyg_bool extra_delay)
 {
     cyg_uint32 reply;
-    
+
     // First the command itself.
-    DEBUG2("mmc_spi_read_disk_block(%d): sending command\n", block);
-    reply = mmc_spi_send_command_start(disk, MMC_REQUEST_READ_SINGLE_BLOCK, block * MMC_SPI_BLOCK_SIZE);
+    DEBUG2("%s(%d): sending command\n", __FUNCTION__, block);
+    if (STANDARD_CAPACITY == disk->sd_capacity) {
+      reply = mmc_spi_send_command_start(disk, MMC_REQUEST_READ_SINGLE_BLOCK, block * MMC_SPI_BLOCK_SIZE);
+    }
+    else {
+      reply = mmc_spi_send_command_start(disk, MMC_REQUEST_READ_SINGLE_BLOCK, block);
+    }
     if (MMC_REPLY_SUCCESS != reply) {
-        DEBUG1("mmc_spi_read_disk_block(%d): unexpected response to READ_SINGLE_BLOCK command, code %02x\n", block, reply);
+        DEBUG1("%s(%d): unexpected response to READ_SINGLE_BLOCK command, code %02x\n",
+            __FUNCTION__, block, reply);
         mmc_spi_end_command(disk);
         // A byte 0xFF indicates the card has been removed.
         if (0x00FF == reply) {
@@ -406,15 +504,16 @@ mmc_spi_read_disk_block(cyg_mmc_spi_disk
         // else is an I/O error.
         return -EIO;
     }
-    
+
     // Now read back the data block. That code can be shared with other read
     // operations, e.g. for retrieving registers.
-    DEBUG2("mmc_spi_read_disk_block(%d): reading data token/data/crc\n", block);
+    DEBUG2("%s(%d): reading data token/data/crc\n", __FUNCTION__, block);
     reply = mmc_spi_read_data(disk, buf, MMC_SPI_BLOCK_SIZE, extra_delay);
     mmc_spi_end_command(disk);
     if (MMC_DATA_TOKEN_SUCCESS != reply) {
-        DEBUG1("mmc_spi_read_disk_block(%d): failed to retrieve data, error token %02x\n", block, reply);
-        
+        DEBUG1("%s(%d): failed to retrieve data, error token %02x\n",
+            __FUNCTION__, block, reply);
+
         // Possibilities are password-locked, range error, ECC failure
         // if the raw data is corrupt, CC error for an internal card
         // error, or some other error. A byte 0xFF indicates the card
@@ -444,12 +543,18 @@ mmc_spi_write_disk_block(cyg_mmc_spi_dis
     cyg_uint32      reply;
     cyg_uint8       extra[4];
     int             i;
-   
+
     // First, send the command itself and get the initial response
-    DEBUG2("mmc_spi_write_disk_block(), sending command\n");
-    reply = mmc_spi_send_command_start(disk, MMC_REQUEST_WRITE_BLOCK, block * MMC_SPI_BLOCK_SIZE);
+    DEBUG2("%s(): sending command\n", __FUNCTION__);
+    if (STANDARD_CAPACITY == disk->sd_capacity) {
+      reply = mmc_spi_send_command_start(disk, MMC_REQUEST_WRITE_BLOCK, block * MMC_SPI_BLOCK_SIZE);
+    }
+    else {
+      reply = mmc_spi_send_command_start(disk, MMC_REQUEST_WRITE_BLOCK, block );
+    }
     if (MMC_REPLY_SUCCESS != reply) {
-        DEBUG1("mmc_spi_write_disk_block(): unexpected response to WRITE_BLOCK command, code %02x\n", reply);
+        DEBUG1("%s(): unexpected response to WRITE_BLOCK command, code %02x\n",
+            __FUNCTION__, reply);
         mmc_spi_end_command(disk);
         if (0x00FF == reply) {
             return -ENODEV;
@@ -469,7 +574,7 @@ mmc_spi_write_disk_block(cyg_mmc_spi_dis
     // The card is now expecting a data block. This consists of a single byte
     // 0x00FE, then the data itself, and a dummy CRC. The reply from the card
     // does not contain any useful information.
-    DEBUG2("mmc_spi_write_disk_block(): sending data token/data/crc\n");
+    DEBUG2("%s(): sending data token/data/crc\n", __FUNCTION__);
     extra[0]    = 0x00FE;
     cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, extra, (cyg_uint8*)0, 0);
     cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, MMC_SPI_BLOCK_SIZE, buf, (cyg_uint8*)0, 0);
@@ -477,13 +582,13 @@ mmc_spi_write_disk_block(cyg_mmc_spi_dis
 
     // The card should respond immediately with a data response token.
     cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, extra, 0);
-    DEBUG2("mmc_spi_write_disk_block(): got data response token %02x\n", extra[0]);
+    DEBUG2("%s(): got data response token %02x\n", __FUNCTION__, extra[0]);
 
     // The bottom five bits contain the response. 00101 indicates success,
     // anything else is a CRC error. Everything else will have been checked
     // before the data got transferred.
     if (0x05 != (extra[0] & 0x1F)) {
-        DEBUG1("mmc_spi_write_disk_block(): invalid data response token %02x\n", extra[0]);
+        DEBUG1("%s(): invalid data response token %02x\n", __FUNCTION__, extra[0]);
         mmc_spi_end_command(disk);
         if (0x00FF == extra[0]) {
             return -ENODEV;
@@ -491,7 +596,7 @@ mmc_spi_write_disk_block(cyg_mmc_spi_dis
         return -EIO;
     }
 
-#ifdef MMC_SPI_BACKGROUND_WRITES    
+#ifdef MMC_SPI_BACKGROUND_WRITES
     // Mark the card as writing. The next operation will poll for completion.
     disk->mmc_writing   = true;
 #else
@@ -506,10 +611,10 @@ mmc_spi_write_disk_block(cyg_mmc_spi_dis
     extra[0]    = 0x00;
     for (i = 0; (i < MMC_SPI_WRITE_BUSY_RETRIES) && (0x00 == extra[0]); i++) {
         cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, extra, 0);
-        DEBUG2("mmc_spi_write_disk_block(), polling for ! busy, got response %02x\n", extra[0]);
+        DEBUG2("%s(): polling for ! busy, got response %02x\n", __FUNCTION__, extra[0]);
     }
 #endif
-    
+
     // Assume that the loop did in fact terminate.
     mmc_spi_end_command(disk);
     return ENOERR;
@@ -544,13 +649,13 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
     Cyg_ErrNo           code;
     mmc_csd_register    csd;
 
-#ifdef MMC_SPI_BACKGROUND_WRITES    
+#ifdef MMC_SPI_BACKGROUND_WRITES
     // If we have unmounted a disk and are remounting it, assume that
     // any writes have completed.
     disk->mmc_writing   = false;
-#endif    
+#endif
     reply               = 0x00ff;
-    
+
     for (i = 0; (i < MMC_SPI_GO_IDLE_RETRIES) && (0x01 != reply); i++) {
         // Allow platform HALs to provide additional initialization,
         // if the hardware needs it.
@@ -564,7 +669,7 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
         // been plugged in so there is no guarantee that any previous
         // init() calls or other traffic will have affected this card.
         mmc_spi_send_init(disk);
-    
+
         // Now set the card to idle state. This involves the GO_IDLE_STATE
         // command which will be accepted irrespective of whether the card is
         // currently in MMC or SPI mode, and will leave the card in SPI mode.
@@ -577,39 +682,74 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
         // inconvenient moment. Some dummy traffic is generated in the
         // hope that this gets things back in sync.
         if (0x01 != reply) {
-            DEBUG1("mmc_spi_check_for_disk(): loop %d, card did not enter idle state, code %02x\n", i, reply);
+            DEBUG1("%s(): loop %d, card did not enter idle state, code %02x\n",
+                __FUNCTION__, i, reply);
             if (0x0ff != reply) {
                 cyg_spi_transfer(dev, cyg_mmc_spi_polled, 128, mmc_spi_ff_data, (cyg_uint8*) 0);
             }
         }
     }
     if (0x0ff == reply) {
-        DEBUG1("mmc_spi_check_for_disk(): unable to get a response from the MMC card: code %02x\n", reply);
+        DEBUG1("%s(): unable to get a response from the MMC card: code %02x\n",
+            __FUNCTION__, reply);
         // A working card should be returning some data
         return -ENODEV;
     }
     if (0x01 != reply) {
-        DEBUG1("mmc_spi_check_for_disk(): card did not enter idle state, code %02x\n", reply);
+        DEBUG1("%s(): card did not enter idle state, code %02x\n", __FUNCTION__, reply);
         return -EIO;
     }
-    
+
+    // Determine if card complies with SD Physical Spec Version 1 or 2 or later.
+    reply = mmc_spi_check_version(disk);
+    if (ENOERR != reply) {
+      DEBUG1("%s(): can't determine card's version, code %02x\n", __FUNCTION__, reply);
+      return reply;
+    }
+    DEBUG2("%s(): card version %u\n", __FUNCTION__, disk->sd_version);
+
     // Next, wait for the card to initialize. This involves repeatedly
     // trying the SEND_OP_COND command until we get a reply that is
-    // not idle
+    // not idle.
     reply = 0x00ff;
     for (i = 0; (i < MMC_SPI_OP_COND_RETRIES) && ((0x00ff == reply) || (0 != (MMC_REPLY_IN_IDLE_STATE & reply))); i++) {
 #ifdef CYGPKG_DEVS_DISK_MMC_SPI_IDLE_RETRIES_WAIT
         CYGACC_CALL_IF_DELAY_US(CYGPKG_DEVS_DISK_MMC_SPI_IDLE_RETRIES_WAIT);
 #endif
-        reply = mmc_spi_send_command(disk, MMC_REQUEST_SEND_OP_COND, 0);
+        if (1 == disk->sd_version) {
+          reply = mmc_spi_send_command(disk, MMC_REQUEST_SEND_OP_COND, 0);
+        }
+        else {
+          reply = mmc_spi_send_command(disk, SD_REQUEST_APP_CMD, 0);
+          reply &= ~MMC_REPLY_IN_IDLE_STATE;
+          if (MMC_REPLY_SUCCESS != reply) {
+            DEBUG1("%s(): card doesn't accept APP_CMD: reply code %02x\n",
+                __FUNCTION__, reply);
+            return -EIO;
+          }
+          reply = mmc_spi_send_command(disk, SD_REQUEST_SD_SEND_OP_COND, SD_ARGUMENT_HCS);
+        }
     }
     if (MMC_REPLY_SUCCESS != reply) {
-        DEBUG1("mmc_spi_check_for_disk(): card has not entered operational state: reply code %02x\n", reply);
+        DEBUG1("%s(): card has not entered operational state: reply code %02x\n",
+            __FUNCTION__, reply);
         return (0x00FF == reply) ? -ENODEV : -EIO;
     }
 
     // The card has now generated sufficient responses that we don't need to
     // worry about a missing card anymore.
+    // In case of V2 card, determine its capacity class
+    if (2 == disk->sd_version) {
+      reply = mmc_spi_sd_check_v2_capacity_class(disk);
+      if (MMC_REPLY_SUCCESS != reply) {
+          DEBUG1("%s(): can't establish card's capacity class: reply code %02x\n",
+              __FUNCTION__, reply);
+          return -EIO;
+      }
+    }
+    DEBUG2("%s capacity card.\n", (STANDARD_CAPACITY == disk->sd_capacity) ?
+        "Standard" : (HIGH_CAPACITY == disk->sd_capacity) ? "High" : "Extended" );
+
     // Get hold of the card's unique ID and store it, to allow disk changes
     // to be detected.
     code = mmc_spi_read_register(disk, MMC_REQUEST_SEND_CID, (cyg_uint8*) &(disk->mmc_id), 16);
@@ -650,7 +790,9 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
            csd.csd_data[ 4], csd.csd_data[ 5], csd.csd_data[ 6], csd.csd_data[7],                           \
            csd.csd_data[ 8], csd.csd_data[ 9], csd.csd_data[10], csd.csd_data[11],                          \
            csd.csd_data[12], csd.csd_data[13], csd.csd_data[14], csd.csd_data[15]);
-    
+
+    disk->sd_csd_version = MMC_CSD_REGISTER_CSD_STRUCTURE(&csd) + 1 ;
+
     // Optionally dump the whole CSD register. This takes a lot of
     // code but gives a lot of info about the card. If the info looks
     // correct then we really are interacting properly with an MMC card.
@@ -668,6 +810,7 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
     } else {
         DEBUG1("        : Others/Unknown disk, FILE_FORMAT_GROUP 0, FILE_FORMAT 3\n");
     }
+
     {
         static const cyg_uint32 mantissa_speeds_x10[16]   = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
         static const cyg_uint32 exponent_speeds_div10[8]  = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
@@ -676,11 +819,15 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
         speed /= 1000;
         DEBUG1("        : TRAN_SPEED %d %d -> %d kbit/s\n", \
                MMC_CSD_REGISTER_TRAN_SPEED_MANTISSA(&csd), MMC_CSD_REGISTER_TRAN_SPEED_EXPONENT(&csd), speed);
-    }        
+    }
+
     DEBUG1("        : READ_BL_LEN block length 2^%d (%d)\n", MMC_CSD_REGISTER_READ_BL_LEN(&csd), \
                 0x01 << MMC_CSD_REGISTER_READ_BL_LEN(&csd));
-    DEBUG1("        : C_SIZE %d, C_SIZE_MULT %d\n", MMC_CSD_REGISTER_C_SIZE(&csd), MMC_CSD_REGISTER_C_SIZE_MULT(&csd));
-    {
+
+    if (1 == disk->sd_csd_version) {
+      DEBUG1("        : C_SIZE %d, C_SIZE_MULT %d\n", \
+          MMC_CSD_REGISTER_C_SIZE(&csd), MMC_CSD_REGISTER_C_SIZE_MULT(&csd));
+      {
         cyg_uint32 block_len = 0x01 << MMC_CSD_REGISTER_READ_BL_LEN(&csd);
         cyg_uint32 mult      = 0x01 << (MMC_CSD_REGISTER_C_SIZE_MULT(&csd) + 2);
         cyg_uint32 size      = block_len * mult * (MMC_CSD_REGISTER_C_SIZE(&csd) + 1);
@@ -688,10 +835,22 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
         cyg_uint32 sizeM     =  sizeK / 1024;
         sizeK  -= (sizeM * 1024);
         DEBUG1("        : total card size %dM%dK\n", sizeM, sizeK);
+      }
     }
+    else {
+      DEBUG1("        : C_SIZE %d\n", SD_CSD_V2_REGISTER_C_SIZE(&csd));
+      {
+        cyg_uint32 sizeK     = 512 * (SD_CSD_V2_REGISTER_C_SIZE(&csd) + 1 ) ;
+        cyg_uint32 sizeM     =  sizeK / 1024;
+        sizeK  -= (sizeM * 1024);
+        DEBUG1("        : total card size %uM%uK\n", sizeM, sizeK);
+      }
+    }
+
     DEBUG1("        : WR_BL_LEN block length 2^%d (%d)\n", \
            MMC_CSD_REGISTER_WRITE_BL_LEN(&csd), 0x01 << MMC_CSD_REGISTER_WRITE_BL_LEN(&csd));
-    {
+
+    if ( 1 == disk->sd_csd_version) {
         static cyg_uint32 taac_mantissa_speeds_x10[16]   = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
         static cyg_uint32 taac_exponent_speeds_div10[8]  = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
         cyg_uint32 taac_speed = taac_mantissa_speeds_x10[MMC_CSD_REGISTER_TAAC_MANTISSA(&csd)] *
@@ -700,15 +859,18 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
         DEBUG1("        : asynchronous read access time TAAC %d %d -> %d ns\n", \
                MMC_CSD_REGISTER_TAAC_MANTISSA(&csd), MMC_CSD_REGISTER_TAAC_EXPONENT(&csd), taac_speed);
     }
-    DEBUG1("        : synchronous read access time NSAC %d * 100 cycles\n", \
-           MMC_CSD_REGISTER_NSAC(&csd));
+    if ( 1 == disk->sd_csd_version) {
+      DEBUG1("        : synchronous read access time NSAC %d * 100 cycles\n", \
+             MMC_CSD_REGISTER_NSAC(&csd));
+    }
     DEBUG1("        : typical write program time %d * read time\n", MMC_CSD_REGISTER_R2W_FACTOR(&csd));
     DEBUG1("        : CCC command classes 0x%04x\n", MMC_CSD_REGISTER_CCC(&csd));
     DEBUG1("        : READ_BL_PARTIAL %d, WRITE_BLK_MISALIGN %d, READ_BLK_MISALIGN %d, DSR_IMP %d\n",   \
            MMC_CSD_REGISTER_READ_BL_PARTIAL(&csd), MMC_CSD_REGISTER_WRITE_BLK_MISALIGN(&csd),           \
            MMC_CSD_REGISTER_READ_BLK_MISALIGN(&csd), MMC_CSD_REGISTER_DSR_IMP(&csd));
     DEBUG1("        : WR_BL_PARTIAL %d\n", MMC_CSD_REGISTER_WR_BL_PARTIAL(&csd));
-    {
+
+    if ( 1 == disk->sd_csd_version) {
         static cyg_uint8    min_currents[8] = { 1, 1, 5, 10, 25, 35, 60, 100 };
         static cyg_uint8    max_currents[8] = { 1, 5, 10, 25, 35, 45, 80, 200 };
         DEBUG1("        : read current min %dmA, max %dmA\n",               \
@@ -718,26 +880,32 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
                     min_currents[MMC_CSD_REGISTER_VDD_W_CURR_MIN(&csd)],    \
                     max_currents[MMC_CSD_REGISTER_VDD_W_CURR_MAX(&csd)]);
     }
-    DEBUG1("        : erase sector size %d, erase group size %d\n", \
-           MMC_CSD_REGISTER_SECTOR_SIZE(&csd) + 1, MMC_CSD_REGISTER_ERASE_GRP_SIZE(&csd) + 1);
-    DEBUG1("        : write group enable %d, write group size %d\n", \
-           MMC_CSD_REGISTER_WR_GRP_ENABLE(&csd), MMC_CSD_REGISTER_WR_GRP_SIZE(&csd) + 1);
+    if ( 1 == disk->sd_csd_version) {
+      DEBUG1("        : erase sector size %d, erase group size %d\n", \
+             MMC_CSD_REGISTER_SECTOR_SIZE(&csd) + 1, MMC_CSD_REGISTER_ERASE_GRP_SIZE(&csd) + 1);
+      DEBUG1("        : write group enable %d, write group size %d\n", \
+             MMC_CSD_REGISTER_WR_GRP_ENABLE(&csd), MMC_CSD_REGISTER_WR_GRP_SIZE(&csd) + 1);
+    }
     DEBUG1("        : copy bit %d\n", MMC_CSD_REGISTER_COPY(&csd));
     DEBUG1("        : permanent write protect %d, temporary write protect %d\n", \
            MMC_CSD_REGISTER_PERM_WRITE_PROTECT(&csd), MMC_CSD_REGISTER_TMP_WRITE_PROTECT(&csd));
-    DEBUG1("        : ecc %d, default ecc %d\n", MMC_CSD_REGISTER_ECC(&csd), MMC_CSD_REGISTER_DEFAULT_ECC(&csd));
+    if ( 1 == disk->sd_csd_version) {
+      DEBUG1("        : ecc %d, default ecc %d\n", MMC_CSD_REGISTER_ECC(&csd), MMC_CSD_REGISTER_DEFAULT_ECC(&csd));
+    }
     DEBUG1("        : crc 0x%08x\n", MMC_CSD_REGISTER_CRC(&csd));
-#endif                
+#endif
 
-    // There is information available about the file format, e.g.
-    // partitioned vs. simple FAT. With the current version of the
-    // generic disk code this needs to be known statically, via
-    // the mbr field of the disk channel structure. If the card
-    // is inappropriately formatted, reject the mount request.
-    if ((0 != MMC_CSD_REGISTER_FILE_FORMAT_GROUP(&csd)) ||
-        (0 != MMC_CSD_REGISTER_FILE_FORMAT(&csd))) {
-        return -ENOTDIR;
-    }
+    if ( 1 == disk->sd_csd_version) {
+      // There is information available about the file format, e.g.
+      // partitioned vs. simple FAT. With the current version of the
+      // generic disk code this needs to be known statically, via
+      // the mbr field of the disk channel structure. If the card
+      // is inappropriately formatted, reject the mount request.
+      if ((0 != MMC_CSD_REGISTER_FILE_FORMAT_GROUP(&csd)) ||
+          (0 != MMC_CSD_REGISTER_FILE_FORMAT(&csd))) {
+          return -ENOTDIR;
+      }
+    } // According to Spec V3.01, host should not use these two fields in CSD V2 cards
 
     // Look for a write-protect bit (permanent or temporary), and set
     // the disk as read-only or read-write as appropriate. The
@@ -750,15 +918,20 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
         disk->mmc_read_only   = false;
     }
     DEBUG1("Disk read-only flag %d\n", disk->mmc_read_only);
-    
+
     // Calculate the disk size, primarily for assertion purposes.
-    // By design MMC cards are limited to 4GB, which still doesn't
-    // quite fit into 32 bits.
-    disk->mmc_block_count = (((cyg_uint64)(0x01 << MMC_CSD_REGISTER_READ_BL_LEN(&csd))) *
-                             ((cyg_uint64)(0x01 << (MMC_CSD_REGISTER_C_SIZE_MULT(&csd) + 2))) *
-                             ((cyg_uint64)(MMC_CSD_REGISTER_C_SIZE(&csd) + 1))) / (cyg_uint64)MMC_SPI_BLOCK_SIZE;
-    DEBUG1("Disk blockcount %d (0x%08x)\n", disk->mmc_block_count, disk->mmc_block_count);
-    
+    if ( 1 == disk->sd_csd_version) {
+      // By design MMC cards are limited to 4GB, which still doesn't
+      // quite fit into 32 bits.
+      disk->mmc_block_count = (((cyg_uint64)(0x01 << MMC_CSD_REGISTER_READ_BL_LEN(&csd))) *
+             ((cyg_uint64)(0x01 << (MMC_CSD_REGISTER_C_SIZE_MULT(&csd) + 2))) *
+             ((cyg_uint64)(MMC_CSD_REGISTER_C_SIZE(&csd) + 1))) / (cyg_uint64)MMC_SPI_BLOCK_SIZE;
+    }
+    else {
+      disk->mmc_block_count = 1024 * ( SD_CSD_V2_REGISTER_C_SIZE(&csd) + 1 ) ;
+    }
+    DEBUG1("Disk blockcount %u (0x%08x)\n", disk->mmc_block_count, disk->mmc_block_count);
+
     // Assume for now that the block length is 512 bytes. This is
     // probably a safe assumption since we have just got the card
     // initialized out of idle state. If it ever proves to be a problem
@@ -775,7 +948,7 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
         static const cyg_uint32 mantissa_speeds_x10[16]   = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
         static const cyg_uint32 exponent_speeds_div10[8]  = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
         cyg_uint32 speed, len;
-        
+
         len = sizeof(cyg_uint32);
         if (cyg_spi_get_config(dev, CYG_IO_GET_CONFIG_SPI_CLOCKRATE, (void*) &disk->mmc_saved_baudrate, &len)) {
             DEBUG1("Failed to retrieve current SPI device clockrate\n");
@@ -801,7 +974,7 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
         cyg_uint8   data[MMC_SPI_BLOCK_SIZE];
         cyg_uint8*  partition;
         cyg_uint32  lba_first, lba_size, lba_end, head, cylinder, sector;
-        
+
         code = mmc_spi_read_disk_block(disk, data, 0, true);
         if (code) {
             mmc_spi_restore_baud(disk);
@@ -839,7 +1012,7 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
                    partition[11], partition[10], partition[9], partition[8], \
                    partition[15], partition[14], partition[13], partition[12]);
         }
-#endif        
+#endif
         if ((0x0055 != data[0x1fe]) || (0x00aa != data[0x1ff])) {
             mmc_spi_restore_baud(disk);
             return -ENOTDIR;
@@ -869,7 +1042,7 @@ mmc_spi_check_for_disk(cyg_mmc_spi_disk_
         sector      = partition[6] & 0x3F;
         disk->mmc_heads_per_cylinder = ((((lba_end + 1) - sector) / disk->mmc_sectors_per_head) - head) / cylinder;
     }
-    
+
     return ENOERR;
 }
 
@@ -946,9 +1119,9 @@ mmc_spi_disk_lookup(struct cyg_devtab_en
     cyg_mmc_spi_disk_info_t*    disk    = (cyg_mmc_spi_disk_info_t*) chan->dev_priv;
     Cyg_ErrNo                   result;
 
-    DEBUG2("mmc_spi_disk_lookup(): target name=%s\n", name );
+    DEBUG2("%s(): target name=%s\n", __FUNCTION__, name );
     DEBUG2("                     : device name=%s dep_name=%s\n", tab[0]->name, tab[0]->dep_name );
-    DEBUG2("                     : sub    name=%s dep_name=%s\n", sub_tab->name, sub_tab->dep_name );
+//    DEBUG2("                     : sub    name=%s dep_name=%s\n", sub_tab->name, sub_tab->dep_name );
 
     if (disk->mmc_connected) {
         // There was a card plugged in last time we looked. Is it still there?
@@ -976,7 +1149,7 @@ mmc_spi_disk_lookup(struct cyg_devtab_en
         cyg_uint32          id_data;
         char*               where;
         int                 i;
-        
+
         // The world is consistent and the higher-level code does not
         // know anything about the current card, if any. Is there a
         // card?
@@ -1016,7 +1189,7 @@ mmc_spi_disk_lookup(struct cyg_devtab_en
             ident.firmware_rev[1] = id_data - 10 + 'A';
         }
         ident.firmware_rev[2]   = '\0';
-        
+
         // Model number. There is a six-byte product name in the CID.
         for (i = 0; i < 6; i++) {
             if ((disk->mmc_id.cid_data[i + 3] >= 0x20) && (disk->mmc_id.cid_data[i+3] <= 0x7E)) {
@@ -1058,9 +1231,10 @@ mmc_spi_disk_read(disk_channel* chan, vo
     cyg_uint32                  i;
     cyg_uint8*                  buf = (cyg_uint8*) buf_arg;
     Cyg_ErrNo                   code = ENOERR;
-    
-    DEBUG1("mmc_spi_disk_read(): first block %d, buf %p, len %lu blocks (%lu bytes)\n",
-           first_block, buf, (unsigned long)blocks, (unsigned long)blocks*512);
+
+    DEBUG1("%s(): first block %d, buf %p, len %lu blocks (%lu bytes)\n",
+        __FUNCTION__, first_block, buf, (unsigned long)blocks,
+        (unsigned long)blocks*512);
 
     if (! disk->mmc_connected) {
         return -ENODEV;
@@ -1084,8 +1258,9 @@ mmc_spi_disk_write(disk_channel* chan, c
     const cyg_uint8*            buf = (cyg_uint8*) buf_arg;
     Cyg_ErrNo                   code = ENOERR;
 
-    DEBUG1("mmc_spi_disk_write(): first block %d, buf %p, len %lu blocks (%lu bytes)\n",
-           first_block, buf, (unsigned long)blocks, (unsigned long)blocks*512);
+    DEBUG1("%s(): first block %d, buf %p, len %lu blocks (%lu bytes)\n",
+        __FUNCTION__, first_block, buf, (unsigned long)blocks,
+        (unsigned long)blocks*512);
 
     if (! disk->mmc_connected) {
         return -ENODEV;
@@ -1113,7 +1288,7 @@ mmc_spi_disk_get_config(disk_channel* ch
     CYG_UNUSED_PARAM(cyg_uint32, key);
     CYG_UNUSED_PARAM(const void*, buf);
     CYG_UNUSED_PARAM(cyg_uint32*, len);
-    
+
     return -EINVAL;
 }
 
@@ -1168,9 +1343,9 @@ DISK_FUNS(cyg_mmc_spi_disk_funs,
 
 static cyg_mmc_spi_disk_info_t cyg_mmc_spi_disk0_hwinfo = {
     .mmc_spi_dev        = &cyg_spi_mmc_dev0,
-#ifdef MMC_SPI_BACKGROUND_WRITES    
+#ifdef MMC_SPI_BACKGROUND_WRITES
     .mmc_writing        = 0,
-#endif    
+#endif
     .mmc_connected      = 0
 };
 
@@ -1185,7 +1360,7 @@ DISK_CHANNEL(cyg_mmc_spi_disk0_channel,
              true,                            /* MBR support */
              1                                /* Number of partitions supported */
              );
-             
+
 BLOCK_DEVTAB_ENTRY(cyg_mmc_spi_disk0_devtab_entry,
                    CYGDAT_DEVS_DISK_MMC_SPI_DISK0_NAME,
                    0,