Skip to content
Snippets Groups Projects
mmcsd_sdio.c 93.8 KiB
Newer Older
/****************************************************************************
 * drivers/mmcsd/mmcsd_sdio.c
 *   Copyright (C) 2009-2011 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS 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.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <debug.h>
#include <errno.h>

#include <nuttx/fs.h>
#include <nuttx/ioctl.h>
#include <nuttx/clock.h>
#include <nuttx/arch.h>
#include <nuttx/rwbuffer.h>
#include <nuttx/sdio.h>
#include <nuttx/mmcsd.h>

#include "mmcsd_internal.h"
#include "mmcsd_sdio.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/* The maximum number of references on the driver (because a uint8_t is used.
 * Use a larger type if more references are needed.
 */

#define MAX_CREFS               0xff

patacongo's avatar
patacongo committed
/* Timing (all in units of microseconds) */
#define MMCSD_
#define MMCSD_POWERUP_DELAY     ((useconds_t)250)    /* 74 clock cycles @ 400KHz = 185uS */
#define MMCSD_IDLE_DELAY        ((useconds_t)50000)  /* Short delay to allow change to IDLE state */
#define MMCSD_DSR_DELAY         ((useconds_t)100000) /* Time to wait after setting DSR */
#define MMCSD_CLK_DELAY         ((useconds_t)500000) /* Delay after changing clock speeds */
/* Data delays (all in units of milliseconds).
 *
 *   For MMC & SD V1.x, these should be based on Nac = TAAC + NSAC; The maximum
 *   value of TAAC is 80MS and the maximum value of NSAC is 25.5K clock cycle.
 *   For SD V2.x, a fixed delay of 100MS is recommend which is preety close to
 *   the worst case SD V1.x Nac.  Here we just use 100MS delay for all data
 *   transfers.
 */

#define MMCSD_SCR_DATADELAY     (100)      /* Wait up to 100MS to get SCR */
patacongo's avatar
patacongo committed
#define MMCSD_BLOCK_DATADELAY   (100)      /* Wait up to 100MS to get one data block */
#define IS_EMPTY(priv) (priv->type == MMCSD_CARDTYPE_UNKNOWN)

/****************************************************************************
 * Private Types
 ****************************************************************************/

/* This structure is contains the unique state of the MMC/SD block driver */

struct mmcsd_state_s
{
patacongo's avatar
patacongo committed
  FAR struct sdio_dev_s *dev;      /* The SDIO device bound to this instance */
  uint8_t  crefs;                  /* Open references on the driver */
  sem_t  sem;                      /* Assures mutually exclusive access to the slot */

  /* Status flags */

  uint8_t probed:1;                /* true: mmcsd_probe() discovered a card */
  uint8_t widebus:1;               /* true: Wide 4-bit bus selected */
  uint8_t mediachanged:1;          /* true: Media changed since last check */
  uint8_t wrbusy:1;                /* true: Last transfer was a write, card may be busy */
  uint8_t wrprotect:1;             /* true: Card is write protected (from CSD) */
  uint8_t locked:1;                /* true: Media is locked (from R1) */
  uint8_t dsrimp:1;                /* true: card supports CMD4/DSR setting (from CSD) */
#ifdef CONFIG_SDIO_DMA
  uint8_t dma:1;                   /* true: hardware supports DMA */
  uint8_t mode:2;                  /* (See MMCSDMODE_* definitions) */
  uint8_t type:4;                  /* Card type (See MMCSD_CARDTYPE_* definitions) */
  uint8_t buswidth:4;              /* Bus widthes supported (SD only) */
  uint16_t selblocklen;            /* The currently selected block length */
  uint16_t rca;                    /* Relative Card Address (RCS) register */

  /* Memory card geometry (extracted from the CSD) */

  uint8_t  blockshift;               /* Log2 of blocksize */
  uint16_t blocksize;              /* Read block length (== block size) */
  size_t nblocks;                  /* Number of blocks */
  size_t capacity;                 /* Total capacity of volume */

  /* Read-ahead and write buffering support */

#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
  struct rwbuffer_s rwbuffer;
#endif
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* Misc Helpers *************************************************************/

patacongo's avatar
patacongo committed
static void    mmcsd_takesem(FAR struct mmcsd_state_s *priv);
#define mmcsd_givesem(p) sem_post(&priv->sem);

patacongo's avatar
patacongo committed
/* Command/response helpers *************************************************/

static int     mmcsd_sendcmdpoll(FAR struct mmcsd_state_s *priv,
                 uint32_t cmd, uint32_t arg);
static int     mmcsd_recvR1(FAR struct mmcsd_state_s *priv, uint32_t cmd);
static int     mmcsd_recvR6(FAR struct mmcsd_state_s *priv, uint32_t cmd);
static int     mmcsd_getSCR(FAR struct mmcsd_state_s *priv, uint32_t scr[2]);
static void    mmcsd_decodeCSD(FAR struct mmcsd_state_s *priv,
                 uint32_t csd[4]);
patacongo's avatar
patacongo committed
#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
static void    mmcsd_decodeCID(FAR struct mmcsd_state_s *priv,
                 uint32_t cid[4]);
patacongo's avatar
patacongo committed
#else
#  define mmcsd_decodeCID(priv,cid)
patacongo's avatar
patacongo committed
#endif
static void    mmcsd_decodeSCR(FAR struct mmcsd_state_s *priv,
                uint32_t scr[2]);
static int     mmcsd_getR1(FAR struct mmcsd_state_s *priv, FAR uint32_t *r1);
static int     mmcsd_verifystate(FAR struct mmcsd_state_s *priv,
                 uint32_t status);
/* Transfer helpers *********************************************************/

patacongo's avatar
patacongo committed
#ifdef CONFIG_FS_WRITABLE
static bool    mmcsd_wrprotected(FAR struct mmcsd_state_s *priv);
patacongo's avatar
patacongo committed
#endif
static int     mmcsd_eventwait(FAR struct mmcsd_state_s *priv,
patacongo's avatar
patacongo committed
static int     mmcsd_transferready(FAR struct mmcsd_state_s *priv);
static int     mmcsd_stoptransmission(FAR struct mmcsd_state_s *priv);
static int     mmcsd_setblocklen(FAR struct mmcsd_state_s *priv,
patacongo's avatar
patacongo committed
static ssize_t mmcsd_readsingle(FAR struct mmcsd_state_s *priv,
patacongo's avatar
patacongo committed
static ssize_t mmcsd_readmultiple(FAR struct mmcsd_state_s *priv,
                 FAR uint8_t *buffer, off_t startblock, size_t nblocks);
patacongo's avatar
patacongo committed
#ifdef CONFIG_FS_READAHEAD
static ssize_t mmcsd_reload(FAR void *dev, FAR uint8_t *buffer,
                 off_t startblock, size_t nblocks);
patacongo's avatar
patacongo committed
#endif
#ifdef CONFIG_FS_WRITABLE
static ssize_t mmcsd_writesingle(FAR struct mmcsd_state_s *priv,
patacongo's avatar
patacongo committed
static ssize_t mmcsd_writemultiple(FAR struct mmcsd_state_s *priv,
                 FAR const uint8_t *buffer, off_t startblock, size_t nblocks);
patacongo's avatar
patacongo committed
#ifdef CONFIG_FS_WRITEBUFFER
static ssize_t mmcsd_flush(FAR void *dev, FAR const uint8_t *buffer,
                 off_t startblock, size_t nblocks);
patacongo's avatar
patacongo committed
#endif
#endif

/* Block driver methods *****************************************************/

static int     mmcsd_open(FAR struct inode *inode);
static int     mmcsd_close(FAR struct inode *inode);
patacongo's avatar
patacongo committed
static ssize_t mmcsd_read(FAR struct inode *inode, FAR unsigned char *buffer,
                 size_t startsector, unsigned int nsectors);
#ifdef CONFIG_FS_WRITABLE
patacongo's avatar
patacongo committed
static ssize_t mmcsd_write(FAR struct inode *inode,
                 FAR const unsigned char *buffer, size_t startsector,
                 unsigned int nsectors);
#endif
static int     mmcsd_geometry(FAR struct inode *inode,
patacongo's avatar
patacongo committed
                 FAR struct geometry *geometry);
static int     mmcsd_ioctl(FAR struct inode *inode, int cmd,
                 unsigned long arg);

/* Initialization/uninitialization/reset ************************************/

static void    mmcsd_mediachange(FAR void *arg);
patacongo's avatar
patacongo committed
static int     mmcsd_widebus(FAR struct mmcsd_state_s *priv);
#ifdef CONFIG_MMCSD_MMCSUPPORT
patacongo's avatar
patacongo committed
static int     mmcsd_mmcinitialize(FAR struct mmcsd_state_s *priv);
patacongo's avatar
patacongo committed
static int     mmcsd_sdinitialize(FAR struct mmcsd_state_s *priv);
static int     mmcsd_cardidentify(FAR struct mmcsd_state_s *priv);
static int     mmcsd_probe(FAR struct mmcsd_state_s *priv);
static int     mmcsd_removed(FAR struct mmcsd_state_s *priv);
static int     mmcsd_hwinitialize(FAR struct mmcsd_state_s *priv);
static void    mmcsd_hwuninitialize(FAR struct mmcsd_state_s *priv);

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const struct block_operations g_bops =
{
  mmcsd_open,     /* open     */
  mmcsd_close,    /* close    */
  mmcsd_read,     /* read     */
#ifdef CONFIG_FS_WRITABLE
  mmcsd_write,    /* write    */
#else
  NULL,           /* write    */
#endif
  mmcsd_geometry, /* geometry */
  mmcsd_ioctl     /* ioctl    */
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Misc Helpers
 ****************************************************************************/

patacongo's avatar
patacongo committed
static void mmcsd_takesem(FAR struct mmcsd_state_s *priv)
{
  /* Take the semaphore (perhaps waiting) */

  while (sem_wait(&priv->sem) != 0)
    {
      /* The only case that an error should occr here is if the wait was
       * awakened by a signal.
       */

      ASSERT(errno == EINTR);
    }
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Command/Response Helpers
 ****************************************************************************/

/****************************************************************************
 * Name: mmcsd_sendcmdpoll
 *
 * Description:
patacongo's avatar
patacongo committed
 *   Send a command and poll-wait for the response.
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

static int mmcsd_sendcmdpoll(FAR struct mmcsd_state_s *priv, uint32_t cmd,
                             uint32_t arg)
patacongo's avatar
patacongo committed
{
  int ret;

  /* Send the command */

  SDIO_SENDCMD(priv->dev, cmd, arg);

  /* Then poll-wait until the response is available */

  ret = SDIO_WAITRESPONSE(priv->dev, cmd);
  if (ret != OK)
    {
patacongo's avatar
patacongo committed
      fdbg("ERROR: Wait for response to cmd: %08x failed: %d\n", cmd, ret);
patacongo's avatar
patacongo committed
    }
  return ret;
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: mmcsd_sendcmdpoll
 *
 * Description:
 *   Set the Driver Stage Register (DSR) if (1) a CONFIG_MMCSD_DSR has been
 *   provided and (2) the card supports a DSR register.  If no DSR value
 *   the card default value (0x0404) will be used.
 *
 ****************************************************************************/

patacongo's avatar
patacongo committed
static inline int mmcsd_sendcmd4(FAR struct mmcsd_state_s *priv)
patacongo's avatar
patacongo committed
{
  int ret = OK;

#ifdef CONFIG_MMCSD_DSR
  /* The dsr_imp bit from the CSD will tell us if the card supports setting
   * the DSR via CMD4 or not.
   */

patacongo's avatar
patacongo committed
    {
      /* CMD4 = SET_DSR will set the cards DSR register. The DSR and CMD4
       * support are optional.  However, since this is a broadcast command
       * with no response (like CMD0), we will never know if the DSR was
       * set correctly or not
       */

      mmcsd_sendcmdpoll(priv, MMCSD_CMD4, CONFIG_MMCSD_DSR << 16);
      up_udelay(MMCSD_DSR_DELAY);

      /* Send it again to have more confidence */

      mmcsd_sendcmdpoll(priv, MMCSD_CMD4, CONFIG_MMCSD_DSR << 16);
      up_udelay(MMCSD_DSR_DELAY);
    }
#endif
  return ret;
}

/****************************************************************************
 * Name: mmcsd_recvR1
 *
 * Description:
 *   Receive R1 response and check for errors.
 *
 ****************************************************************************/

static int mmcsd_recvR1(FAR struct mmcsd_state_s *priv, uint32_t cmd)
patacongo's avatar
patacongo committed
  int ret;
patacongo's avatar
patacongo committed

  /* Get the R1 response from the hardware */
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
  ret = SDIO_RECVR1(priv->dev, cmd, &r1);
  if (ret == OK)
    {
      /* Check if R1 reports an error */

      if ((r1 & MMCSD_R1_ERRORMASK) != 0)
        {
patacongo's avatar
patacongo committed
          /* Card locked is considered an error. Save the card locked
           * indication for later use.
           */

          fvdbg("ERROR: R1=%08x\n", r1);
patacongo's avatar
patacongo committed
          priv->locked = ((r1 & MMCSD_R1_CARDISLOCKED) != 0);
patacongo's avatar
patacongo committed
          ret = -EIO;
        }
    }
  return ret;
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: mmcsd_recvR6
 *
 * Description:
 *   Receive R6 response and check for errors.  On success, priv->rca is set
 *   to the received RCA
 *
 ****************************************************************************/

static int mmcsd_recvR6(FAR struct mmcsd_state_s *priv, uint32_t cmd)
patacongo's avatar
patacongo committed
  int ret;

  /* R6  Published RCA Response (48-bit, SD card only)
   *     47        0               Start bit
   *     46        0               Transmission bit (0=from card)
   *     45:40     bit5   - bit0   Command index (0-63)
   *     39:8      bit31  - bit0   32-bit Argument Field, consisting of:
   *                               [31:16] New published RCA of card
   *                               [15:0]  Card status bits {23,22,19,12:0}
   *     7:1       bit6   - bit0   CRC7
   *     0         1               End bit
   *
   * Get the R1 response from the hardware
   */

  ret = SDIO_RECVR6(priv->dev, cmd, &r6);
  if (ret == OK)
    {
      /* Check if R6 reports an error */

      if ((r6 & MMCSD_R6_ERRORMASK) == 0)
        {
          /* No, save the RCA and return success */

patacongo's avatar
patacongo committed
          return OK;
        }

      /* Otherwise, return an I/O failure */

      ret = -EIO;
    }

  fdbg("ERROR: Failed to get RCA. R6=%08x: %d\n", r6, ret);
  return ret;
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: mmcsd_getSCR
 *
 * Description:
 *   Obtain the SD card's Configuration Register (SCR)
 *
 * Returned Value:
 *   OK on success; a negated ernno on failure.
 *
 ****************************************************************************/

static int mmcsd_getSCR(FAR struct mmcsd_state_s *priv, uint32_t scr[2])
{
  int ret;
patacongo's avatar
patacongo committed

  /* Set Block Size To 8 Bytes */

patacongo's avatar
patacongo committed
  ret = mmcsd_setblocklen(priv, 8);
patacongo's avatar
patacongo committed
  if (ret != OK)
    {
patacongo's avatar
patacongo committed
      fdbg("ERROR: mmcsd_setblocklen failed: %d\n", ret);
patacongo's avatar
patacongo committed
      return ret;
    }

  /* Send CMD55 APP_CMD with argument as card's RCA */

  mmcsd_sendcmdpoll(priv, SD_CMD55, (uint32_t)priv->rca << 16);
patacongo's avatar
patacongo committed
  ret = mmcsd_recvR1(priv, SD_CMD55);
  if (ret != OK)
    {
      fdbg("ERROR: RECVR1 for CMD55 failed: %d\n", ret);
patacongo's avatar
patacongo committed
      return ret;
    }
patacongo's avatar
patacongo committed
  /* Setup up to receive data with interrupt mode */
patacongo's avatar
patacongo committed
  SDIO_BLOCKSETUP(priv->dev, 8, 1);
  SDIO_RECVSETUP(priv->dev, (FAR uint8_t*)scr, 8);
patacongo's avatar
patacongo committed

  /* Send ACMD51 SD_APP_SEND_SCR with argument as 0 to start data receipt */

  (void)SDIO_WAITENABLE(priv->dev, SDIOWAIT_TRANSFERDONE|SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR);
patacongo's avatar
patacongo committed
  mmcsd_sendcmdpoll(priv, SD_ACMD51, 0);
  ret = mmcsd_recvR1(priv, SD_ACMD51);
  if (ret != OK)
    {
      fdbg("ERROR: RECVR1 for ACMD51 failed: %d\n", ret);
      SDIO_CANCEL(priv->dev);
patacongo's avatar
patacongo committed
      return ret;
patacongo's avatar
patacongo committed
  /* Wait for data to be transferred */
  ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR, MMCSD_SCR_DATADELAY);
  if (ret != OK)
    {
      fdbg("ERROR: mmcsd_eventwait for READ DATA failed: %d\n", ret);
patacongo's avatar
patacongo committed
    }
patacongo's avatar
patacongo committed
  return ret;
}

/****************************************************************************
 * Name: mmcsd_decodeCSD
patacongo's avatar
patacongo committed
 *
 * Description:
 *   Decode and extract necessary information from the CSD. If debug is
 *   enabled, then decode and show the full contents of the CSD.
 *
 * Returned Value:
 *   OK on success; a negated ernno on failure.  On success, the following
 *   values will be set in the driver state structure:
 *
 *   priv->dsrimp      true: card supports CMD4/DSR setting (from CSD)
 *   priv->wrprotect   true: card is write protected (from CSD)
patacongo's avatar
patacongo committed
 *   priv->blocksize   Read block length (== block size)
patacongo's avatar
patacongo committed
 *   priv->nblocks     Number of blocks
 *   priv->capacity    Total capacity of volume
 *
 ****************************************************************************/

static void mmcsd_decodeCSD(FAR struct mmcsd_state_s *priv, uint32_t csd[4])
patacongo's avatar
patacongo committed
#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
  struct mmcsd_csd_s decoded;
#endif
  unsigned int readbllen;
patacongo's avatar
patacongo committed

  /* Word 1: Bits 127-96:
   *
   * CSD_STRUCTURE      127:126 CSD structure
   * SPEC_VERS          125:122 (MMC) Spec version
   * TAAC               119:112 Data read access-time-1
   *   TIME_VALUE         6:3   Time mantissa
   *   TIME_UNIT          2:0   Time exponent
   * NSAC               111:104 Data read access-time-2 in CLK cycle(NSAC*100)
   * TRAN_SPEED         103:96 Max. data transfer rate
   *   TIME_VALUE         6:3  Rate exponent
   *   TRANSFER_RATE_UNIT 2:0 Rate mantissa
   */

#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
  decoded.csdstructure               =  csd[0] >> 30;
  decoded.mmcspecvers                = (csd[0] >> 26) & 0x0f;
  decoded.taac.timevalue             = (csd[0] >> 19) & 0x0f;
  decoded.taac.timeunit              = (csd[0] >> 16) & 7;
  decoded.nsac                       = (csd[0] >> 8)  & 0xff;
  decoded.transpeed.timevalue        = (csd[0] >> 3)  & 0x0f;
  decoded.transpeed.transferrateunit =  csd[0]        & 7;
#endif

  /* Word 2: Bits 64:95
   *   CCC                95:84 Card command classes
   *   READ_BL_LEN        83:80 Max. read data block length
   *   READ_BL_PARTIAL    79:79 Partial blocks for read allowed
   *   WRITE_BLK_MISALIGN 78:78 Write block misalignment
   *   READ_BLK_MISALIGN  77:77 Read block misalignment
   *   DSR_IMP            76:76 DSR implemented
   * Byte addressed SD and MMC:
   *   C_SIZE             73:62 Device size
   * Block addressed SD:
   *                      75:70 (reserved)
   *   C_SIZE             48:69 Device size
   */

  priv->dsrimp             = (csd[1] >> 12) & 1;
  readbllen                = (csd[1] >> 16) & 0x0f;

#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
  decoded.ccc              = (csd[1] >> 20) & 0x0fff;
  decoded.readbllen        = (csd[1] >> 16) & 0x0f;
  decoded.readblpartial    = (csd[1] >> 15) & 1;
  decoded.writeblkmisalign = (csd[1] >> 14) & 1;
  decoded.readblkmisalign  = (csd[1] >> 13) & 1;
  decoded.dsrimp           = priv->dsrimp;
#endif

  /* Word 3: Bits 32-63
   * 
   * Byte addressed SD:
   *   C_SIZE             73:62 Device size
   *   VDD_R_CURR_MIN     61:59 Max. read current at Vcc min
   *   VDD_R_CURR_MAX     58:56 Max. read current at Vcc max
   *   VDD_W_CURR_MIN     55:53 Max. write current at Vcc min
   *   VDD_W_CURR_MAX     52:50 Max. write current at Vcc max
   *   C_SIZE_MULT        49:47 Device size multiplier
   *   SD_ER_BLK_EN       46:46 Erase single block enable (SD only)
   *   SD_SECTOR_SIZE     45:39 Erase sector size
   *   SD_WP_GRP_SIZE     38:32 Write protect group size
   * Block addressed SD:
   *                      75:70 (reserved)
   *   C_SIZE             48:69 Device size
   *                      47:47 (reserved)
   *   SD_ER_BLK_EN       46:46 Erase single block enable (SD only)
   *   SD_SECTOR_SIZE     45:39 Erase sector size
   *   SD_WP_GRP_SIZE     38:32 Write protect group size
   * MMC:
   *   C_SIZE             73:62 Device size
   *   VDD_R_CURR_MIN     61:59 Max. read current at Vcc min
   *   VDD_R_CURR_MAX     58:56 Max. read current at Vcc max
   *   VDD_W_CURR_MIN     55:53 Max. write current at Vcc min
   *   VDD_W_CURR_MAX     52:50 Max. write current at Vcc max
   *   C_SIZE_MULT        49:47 Device size multiplier
   *   MMC_SECTOR_SIZE    46:42 Erase sector size
   *   MMC_ER_GRP_SIZE    41:37 Erase group size (MMC)
   *   MMC_WP_GRP_SIZE    36:32 Write protect group size
   */

patacongo's avatar
patacongo committed
  if (IS_BLOCK(priv->type))
patacongo's avatar
patacongo committed
    {
      /* Block addressed SD:
       *
       * C_SIZE: 69:64 from Word 2 and 63:48 from Word 3
patacongo's avatar
patacongo committed
       *
       *   512      = (1 << 9)
       *   1024     = (1 << 10)
       *   512*1024 = (1 << 19)
       */

      uint32_t csize                 = ((csd[1] & 0x3f) << 16) | (csd[2] >> 16);
patacongo's avatar
patacongo committed
      priv->capacity                 = (csize + 1) << 19;
patacongo's avatar
patacongo committed
      priv->blockshift               = 9;
patacongo's avatar
patacongo committed
      priv->blocksize                = 1 << 9;
patacongo's avatar
patacongo committed
      priv->nblocks                  = priv->capacity >> 9;
patacongo's avatar
patacongo committed

#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
      decoded.u.sdblock.csize        = csize; 
      decoded.u.sdblock.sderblen     = (csd[2] >> 14) & 1;
      decoded.u.sdblock.sdsectorsize = (csd[2] >> 7) & 0x7f;
      decoded.u.sdblock.sdwpgrpsize  =  csd[2] & 0x7f;
#endif
patacongo's avatar
patacongo committed
    }
patacongo's avatar
patacongo committed
  else
    {
      /* Byte addressed SD:
       *
       * C_SIZE: 73:64 from Word 2 and 63:62 from Word 3
       */
patacongo's avatar
patacongo committed

      uint16_t csize                 = ((csd[1] & 0x03ff) << 2) | ((csd[2] >> 30) & 3);
      uint8_t  csizemult             = (csd[2] >> 15) & 7;
patacongo's avatar
patacongo committed

      priv->nblocks                  = ((uint32_t)csize + 1) * (1 << (csizemult + 2));
      priv->blockshift               = readbllen;
      priv->blocksize                = (1 << readbllen);
      priv->capacity                 = (priv->nblocks << readbllen);

      /* Some devices, such as 2Gb devices, report blocksizes larger than 512 bytes
       * but still expect to be accessed with a 512 byte blocksize.
       *
       * NOTE: A minor optimization would be to eliminated priv->blocksize and
       * priv->blockshift:  Those values will be 512 and 9 in all cases anyway.
      if (priv->blocksize > 512)
        {
          priv->nblocks            <<= (priv->blockshift - 9);
          priv->blocksize            = 512;
          priv->blockshift           = 9;
        }
patacongo's avatar
patacongo committed

#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
patacongo's avatar
patacongo committed
      if (IS_SD(priv->type))
        {
patacongo's avatar
patacongo committed
          decoded.u.sdbyte.csize            = csize; 
          decoded.u.sdbyte.vddrcurrmin      = (csd[2] >> 27) & 7;
          decoded.u.sdbyte.vddrcurrmax      = (csd[2] >> 24) & 7;
          decoded.u.sdbyte.vddwcurrmin      = (csd[2] >> 21) & 7;
          decoded.u.sdbyte.vddwcurrmax      = (csd[2] >> 18) & 7;
          decoded.u.sdbyte.csizemult        = csizemult;
          decoded.u.sdbyte.sderblen         = (csd[2] >> 14) & 1;
          decoded.u.sdbyte.sdsectorsize     = (csd[2] >> 7) & 0x7f;
          decoded.u.sdbyte.sdwpgrpsize      =  csd[2] & 0x7f;
        }
#ifdef CONFIG_MMCSD_MMCSUPPORT
      else if (IS_MMC(priv->type))
        {
          decoded.u.mmc.csize               = csize; 
          decoded.u.mmc.vddrcurrmin         = (csd[2] >> 27) & 7;
          decoded.u.mmc.vddrcurrmax         = (csd[2] >> 24) & 7;
          decoded.u.mmc.vddwcurrmin         = (csd[2] >> 21) & 7;
          decoded.u.mmc.vddwcurrmax         = (csd[2] >> 18) & 7;
          decoded.u.mmc.csizemult           = csizemult;
          decoded.u.mmc.er.mmc22.sectorsize = (csd[2] >> 10) & 0x1f;
          decoded.u.mmc.er.mmc22.ergrpsize  = (csd[2] >> 5) & 0x1f;
          decoded.u.mmc.mmcwpgrpsize        =  csd[2] & 0x1f;
        }
#endif
#endif
    }

  /* Word 4: Bits 0-31
   *   WP_GRP_EN           31:31 Write protect group enable
   *   MMC DFLT_ECC        30:29 Manufacturer default ECC (MMC only)
   *   R2W_FACTOR          28:26 Write speed factor
   *   WRITE_BL_LEN        25:22 Max. write data block length
   *   WRITE_BL_PARTIAL    21:21 Partial blocks for write allowed
   *   FILE_FORMAT_GROUP   15:15 File format group
   *   COPY                14:14 Copy flag (OTP)
   *   PERM_WRITE_PROTECT  13:13 Permanent write protection
   *   TMP_WRITE_PROTECT   12:12 Temporary write protection
   *   FILE_FORMAT         10:11 File format
   *   ECC                  9:8  ECC (MMC only)
   *   CRC                  7:1  CRC
   *   Not used             0:0 
   */

patacongo's avatar
patacongo committed
  permwriteprotect         = (csd[3] >> 13) & 1;
  tmpwriteprotect          = (csd[3] >> 12) & 1;
  priv->wrprotect          = (permwriteprotect || tmpwriteprotect);

patacongo's avatar
patacongo committed
#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
  decoded.wpgrpen          =  csd[3] >> 31;
  decoded.mmcdfltecc       = (csd[3] >> 29) & 3;
  decoded.r2wfactor        = (csd[3] >> 26) & 7;
  decoded.writebllen       = (csd[3] >> 22) & 0x0f;
  decoded.writeblpartial   = (csd[3] >> 21) & 1;
  decoded.fileformatgrp    = (csd[3] >> 15) & 1;
  decoded.copy             = (csd[3] >> 14) & 1;
patacongo's avatar
patacongo committed
  decoded.permwriteprotect = permwriteprotect;
  decoded.tmpwriteprotect  = tmpwriteprotect;
patacongo's avatar
patacongo committed
  decoded.fileformat       = (csd[3] >> 10) & 3;
  decoded.mmcecc           = (csd[3] >> 8)  & 3;
  decoded.crc              = (csd[3] >> 1)  & 0x7f;
 
  fvdbg("CSD:\n");
  fvdbg("  CSD_STRUCTURE: %d SPEC_VERS: %d (MMC)\n",
        decoded.csdstructure, decoded.mmcspecvers);
patacongo's avatar
patacongo committed
  fvdbg("  TAAC {TIME_UNIT: %d TIME_VALUE: %d} NSAC: %d\n",
patacongo's avatar
patacongo committed
        decoded.taac.timeunit, decoded.taac.timevalue, decoded.nsac);
  fvdbg("  TRAN_SPEED {TRANSFER_RATE_UNIT: %d TIME_VALUE: %d}\n",
        decoded.transpeed.transferrateunit, decoded.transpeed.timevalue);
  fvdbg("  CCC: %d\n", decoded.ccc);
  fvdbg("  READ_BL_LEN: %d READ_BL_PARTIAL: %d\n",
        decoded.readbllen, decoded.readblpartial);
  fvdbg("  WRITE_BLK_MISALIGN: %d READ_BLK_MISALIGN: %d\n",
        decoded.writeblkmisalign, decoded.readblkmisalign);
  fvdbg("  DSR_IMP: %d\n",
        decoded.dsrimp);

patacongo's avatar
patacongo committed
  if (IS_BLOCK(priv->type))
patacongo's avatar
patacongo committed
    {
      fvdbg("  SD Block Addressing:\n");
      fvdbg("    C_SIZE: %d SD_ER_BLK_EN: %d\n",
            decoded.u.sdblock.csize, decoded.u.sdblock.sderblen);
      fvdbg("    SD_SECTOR_SIZE: %d SD_WP_GRP_SIZE: %d\n",
            decoded.u.sdblock.sdsectorsize, decoded.u.sdblock.sdwpgrpsize);
patacongo's avatar
patacongo committed
    }
patacongo's avatar
patacongo committed
  else if (IS_SD(priv->type))
    {
      fvdbg("  SD Byte Addressing:\n");
      fvdbg("    C_SIZE: %d C_SIZE_MULT: %d\n",
            decoded.u.sdbyte.csize, decoded.u.sdbyte.csizemult);
      fvdbg("    VDD_R_CURR_MIN: %d VDD_R_CURR_MAX: %d\n",
            decoded.u.sdbyte.vddrcurrmin, decoded.u.sdbyte.vddrcurrmax);
      fvdbg("    VDD_W_CURR_MIN: %d VDD_W_CURR_MAX: %d\n",
            decoded.u.sdbyte.vddwcurrmin, decoded.u.sdbyte.vddwcurrmax);
      fvdbg("    SD_ER_BLK_EN: %d SD_SECTOR_SIZE: %d (SD) SD_WP_GRP_SIZE: %d\n",
            decoded.u.sdbyte.sderblen, decoded.u.sdbyte.sdsectorsize, decoded.u.sdbyte.sdwpgrpsize);
    }
#ifdef CONFIG_MMCSD_MMCSUPPORT
  else if (IS_MMC(priv->type))
    {
      fvdbg("  MMC:\n");
      fvdbg("    C_SIZE: %d C_SIZE_MULT: %d\n",
            decoded.u.mmc.csize, decoded.u.mmc.csizemult);
      fvdbg("    VDD_R_CURR_MIN: %d VDD_R_CURR_MAX: %d\n",
            decoded.u.mmc.vddrcurrmin, decoded.u.mmc.vddrcurrmax);
      fvdbg("    VDD_W_CURR_MIN: %d VDD_W_CURR_MAX: %d\n",
            decoded.u.mmc.vddwcurrmin, decoded.u.mmc.vddwcurrmax);
      fvdbg("    MMC_SECTOR_SIZE: %d MMC_ER_GRP_SIZE: %d MMC_WP_GRP_SIZE: %d\n",
            decoded.u.mmc.er.mmc22.sectorsize, decoded.u.mmc.er.mmc22.ergrpsize,
            decoded.u.mmc.mmcwpgrpsize);
    }
#endif

  fvdbg("  WP_GRP_EN: %d MMC DFLT_ECC: %d (MMC) R2W_FACTOR: %d\n",
        decoded.wpgrpen, decoded.mmcdfltecc, decoded.r2wfactor);
  fvdbg("  WRITE_BL_LEN: %d WRITE_BL_PARTIAL: %d\n",
        decoded.writebllen, decoded.writeblpartial);
  fvdbg("  FILE_FORMAT_GROUP: %d COPY: %d\n",
        decoded.fileformatgrp, decoded.copy);
  fvdbg("  PERM_WRITE_PROTECT: %d TMP_WRITE_PROTECT: %d\n",
        decoded.permwriteprotect, decoded.tmpwriteprotect);
  fvdbg("  FILE_FORMAT: %d ECC: %d (MMC) CRC: %d\n",
        decoded.fileformat, decoded.mmcecc, decoded.crc);

patacongo's avatar
patacongo committed
  fvdbg("Capacity: %dKb, Block size: %db, nblocks: %d wrprotect: %d\n",
         priv->capacity / 1024, priv->blocksize, priv->nblocks, priv->wrprotect);
patacongo's avatar
patacongo committed
#endif
patacongo's avatar
patacongo committed
}

/****************************************************************************
 * Name: mmcsd_decodeCID
patacongo's avatar
patacongo committed
 *
 * Description:
 *   Show the contents of the Card Indentification Data (CID) (for debug
 *   purposes only)
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
static void mmcsd_decodeCID(FAR struct mmcsd_state_s *priv, uint32_t cid[4])
patacongo's avatar
patacongo committed
{
  struct mmcsd_cid_s decoded;

  /* Word 1: Bits 127-96:
   *   mid - 127-120  8-bit Manufacturer ID
   *   oid - 119-104 16-bit OEM/Application ID (ascii)
   *   pnm - 103-64  40-bit Product Name (ascii) + null terminator
   *         pnm[0] 103:96
   */

  decoded.mid    =  cid[0] >> 24;
patacongo's avatar
patacongo committed
  decoded.oid    = (cid[0] >> 16) & 0xffff;
patacongo's avatar
patacongo committed
  decoded.pnm[0] =  cid[0] & 0xff;

  /* Word 2: Bits 64:95
   *   pnm - 103-64  40-bit Product Name (ascii) + null terminator
   *         pnm[1] 95:88
   *         pnm[2] 87:80
   *         pnm[3] 79:72
   *         pnm[4] 71:64
   */

  decoded.pnm[1] =  cid[1] >> 24;
  decoded.pnm[2] = (cid[1] >> 16) & 0xff;
  decoded.pnm[3] = (cid[1] >> 8) & 0xff;
  decoded.pnm[4] =  cid[1] & 0xff;
  decoded.pnm[5] = '\0';

  /* Word 3: Bits 32-63
   *   prv -  63-56   8-bit Product revision
   *   psn -  55-24  32-bit Product serial number
   */

  decoded.prv    = cid[2] >> 24;
  decoded.psn    = cid[2] << 8;

  /* Word 4: Bits 0-31
   *   psn -  55-24  32-bit Product serial number
   *          23-20   4-bit (reserved)
   *   mdt -  19:8   12-bit Manufacturing date
   *   crc -   7:1    7-bit CRC7
   */

patacongo's avatar
patacongo committed
  decoded.psn   |=  cid[3] >> 24;
patacongo's avatar
patacongo committed
  decoded.mdt    = (cid[3] >> 8) & 0x0fff;
  decoded.crc    = (cid[3] >> 1) & 0x7f;

patacongo's avatar
patacongo committed
  fvdbg("mid: %02x oid: %04x pnm: %s prv: %d psn: %d mdt: %02x crc: %02x\n",
      decoded.mid, decoded.oid, decoded.pnm, decoded.prv,
      decoded.psn, decoded.mdt, decoded.crc);
/****************************************************************************
 * Name: mmcsd_decodeSCR
 *
 * Description:
 *   Show the contents of the SD Configuration Register (SCR).  The only
 *   value retained is:  priv->buswidth;
 *
 ****************************************************************************/

static void mmcsd_decodeSCR(FAR struct mmcsd_state_s *priv, uint32_t scr[2])
{
#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
struct mmcsd_scr_s decoded;
#endif

  /* Word 1, bits 63:32
   *   SCR_STRUCTURE          63:60 4-bit SCR structure version
   *   SD_VERSION             59:56 4-bit SD memory spec. version
   *   DATA_STATE_AFTER_ERASE 55:55 1-bit erase status
   *   SD_SECURITY            54:52 3-bit SD security support level
   *   SD_BUS_WIDTHS          51:48 4-bit bus width indicator
   *   Reserved               47:32 16-bit SD reserved space
   */

patacongo's avatar
patacongo committed
#ifdef CONFIG_ENDIAN_BIG	/* Card transfers SCR in big-endian order */
  priv->buswidth     = (scr[0] >> 16) & 15;
patacongo's avatar
patacongo committed
#else
  priv->buswidth     = (scr[0] >> 8) & 15;
#endif

#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
patacongo's avatar
patacongo committed
#ifdef CONFIG_ENDIAN_BIG	/* Card SCR is big-endian order / CPU also big-endian
                             *   60   56   52   48   44   40   36   32
                             * VVVV SSSS ESSS BBBB RRRR RRRR RRRR RRRR */
  decoded.scrversion =  scr[0] >> 28;
  decoded.sdversion  = (scr[0] >> 24) & 15;
  decoded.erasestate = (scr[0] >> 23) & 1;
  decoded.security   = (scr[0] >> 20) & 7;
patacongo's avatar
patacongo committed
#else                       /* Card SCR is big-endian order / CPU is little-endian
                             *   36   32   44   40   52   48   60   56
                             * RRRR RRRR RRRR RRRR ESSS BBBB VVVV SSSS */
  decoded.scrversion = (scr[0] >> 4)  & 15;
  decoded.sdversion  =  scr[0]        & 15;
  decoded.erasestate = (scr[0] >> 15) & 1;
  decoded.security   = (scr[0] >> 12) & 7;
#endif
  decoded.buswidth   = priv->buswidth;
#endif

  /* Word 1, bits 63:32
   *   Reserved               31:0  32-bits reserved for manufacturing usage.
   */

#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
patacongo's avatar
patacongo committed
  decoded.mfgdata   = scr[1];  /* Might be byte reversed! */

  fvdbg("SCR:\n");
  fvdbg("  SCR_STRUCTURE: %d SD_VERSION: %d\n",
        decoded.scrversion,decoded.sdversion);
  fvdbg("  DATA_STATE_AFTER_ERASE: %d SD_SECURITY: %d SD_BUS_WIDTHS: %x\n",
        decoded.erasestate, decoded.security, decoded.buswidth);
  fvdbg("  Manufacturing data: %08x\n",
        decoded.mfgdata);
#endif
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: mmcsd_getR1
 *
 * Description:
 *   Get the R1 status of the card using CMD13
 *
 ****************************************************************************/

static int mmcsd_getR1(FAR struct mmcsd_state_s *priv, FAR uint32_t *r1)
patacongo's avatar
patacongo committed
  int ret;

  DEBUGASSERT(priv != NULL && r1 != NULL);

  /* Send CMD13, SEND_STATUS.  The addressed card responds by sending its
   * R1 card status register.
   */

  mmcsd_sendcmdpoll(priv, MMCSD_CMD13, (uint32_t)priv->rca << 16);
patacongo's avatar
patacongo committed
  ret = SDIO_RECVR1(priv->dev, MMCSD_CMD13, &localR1);
  if (ret == OK)
    {
      /* Check if R1 reports an error */

      if ((localR1 & MMCSD_R1_ERRORMASK) != 0)
        {
          /* Card locked is considered an error. Save the card locked
           * indication for later use.
           */

          priv->locked = ((localR1 & MMCSD_R1_CARDISLOCKED) != 0);
          ret = -EIO;
        }
      else
        {
          /* No errors, return R1 */

          *r1 = localR1;
        }
    }
  return ret;
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: mmcsd_verifystate
patacongo's avatar
patacongo committed
 *
 * Description:
 *   Verify that the card is in STANDBY state
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

static int mmcsd_verifystate(FAR struct mmcsd_state_s *priv, uint32_t state)
  int ret;

  /* Get the current R1 status from the card */

  ret = mmcsd_getR1(priv, &r1);
  if (ret != OK)
    {
      fdbg("ERROR: mmcsd_getR1 failed: %d\n", ret);
      return ret;
    }

  /* Now check if the card is in the expected state. */

  if (IS_STATE(r1, state))
    {
      /* Yes.. return Success */

/****************************************************************************
 ****************************************************************************/

/****************************************************************************
 * Name: mmcsd_wrprotected
 *
 * Description:
 *   Return true if the the card is unlocked an not write protected.  The
 ****************************************************************************/