summaryrefslogtreecommitdiff
path: root/nuttx/drivers/mmcsd/mmcsd_sdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/drivers/mmcsd/mmcsd_sdio.c')
-rw-r--r--nuttx/drivers/mmcsd/mmcsd_sdio.c505
1 files changed, 488 insertions, 17 deletions
diff --git a/nuttx/drivers/mmcsd/mmcsd_sdio.c b/nuttx/drivers/mmcsd/mmcsd_sdio.c
index c6e4fdb19..ab8bbce7b 100644
--- a/nuttx/drivers/mmcsd/mmcsd_sdio.c
+++ b/nuttx/drivers/mmcsd/mmcsd_sdio.c
@@ -41,6 +41,7 @@
#include <sys/types.h>
#include <sys/ioctl.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -48,16 +49,38 @@
#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
****************************************************************************/
-#define MAX_CREFS 0xff /* Because a ubyte is used. Use a larger
- * type if necessary */
+/* The maximum number of references on the driver (because a ubyte is used.
+ * Use a larger type if more references are needed.
+ */
+
+#define MAX_CREFS 0xff
+
+/* Timing */
+
+#define MMCSD_POWERUP_DELAY 250 /* 74 clock cycles @ 400KHz = 185uS */
+#define MMCSD_IDLE_DELAY 50 /* Short delay to allow change to IDLE state */
+
+#define IS_EMPTY(priv) (priv->type == MMCSD_CARDTYPE_UNKNOWN)
+
+/* Transfer mode */
+
+#define MMCSDMODE_POLLED 0
+#define MMCSDMODE_INTERRUPT 1
+#define MMCSDMODE_DMA 2
/****************************************************************************
* Private Types
@@ -72,12 +95,27 @@ struct mmcsd_state_s
/* Status flags */
+ ubyte probed:1; /* TRUE: mmcsd_probe() discovered a card */
ubyte widebus:1; /* TRUE: Wide 4-bit bus selected */
- ubyte mediachange:1; /* TRUE: Media changed since last check */
+ ubyte mediachanged:1; /* TRUE: Media changed since last check */
+ ubyte wrprotect:1; /* TRUE: Media is write protected */
+ ubyte selected:1; /* TRUE: card is selected */
#ifdef CONFIG_SDIO_DMA
ubyte dma:1; /* TRUE: hardware supports DMA */
#endif
+ ubyte type; /* Card type (See MMCSD_CARDTYPE_* definitions) */
+ ubyte mode; /* (See MMCSDMODE_* definitions) */
+ uint16 selblocklen; /* The currently selected block length */
+ uint16 rca; /* Relative Card Address (RCS) register */
+
+ /* Memory card geometry (extracted from the CSD) */
+
+ uint16 rdblocklen; /* Read block length (== block size) */
+ uint16 wrblocklen; /* Write block length */
+ 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)
@@ -113,6 +151,14 @@ static int mmcsd_ioctl(FAR struct inode *inode, int cmd,
/* Initialization/uninitialization/reset ************************************/
+static inline int
+ mmcsd_mmcinitialize(struct mmcsd_state_s *priv);
+static inline int
+ mmcsd_sdinitialize(struct mmcsd_state_s *priv);
+static inline int
+ mmcsd_cardidentify(struct mmcsd_state_s *priv);
+static int mmcsd_probe(struct mmcsd_state_s *priv);
+static int mmcsd_removed(struct mmcsd_state_s *priv);
static int mmcsd_hwinitialize(struct mmcsd_state_s *priv);
static inline void
mmcsd_hwuninitialize(struct mmcsd_state_s *priv);
@@ -288,18 +334,47 @@ static ssize_t mmcsd_write(FAR struct inode *inode, const unsigned char *buffer,
static int mmcsd_geometry(FAR struct inode *inode, struct geometry *geometry)
{
struct mmcsd_state_s *priv;
+ int ret = -EINVAL;
fvdbg("Entry\n");
DEBUGASSERT(inode && inode->i_private);
if (geometry)
{
+ /* Is there a (supported) card inserted in the slot? */
+
priv = (struct mmcsd_state_s *)inode->i_private;
-#ifdef CONFIG_CPP_HAVE_WARNING
-# warning "Not implemented"
+ if (IS_EMPTY(priv))
+ {
+ /* No.. return ENODEV */
+
+ fvdbg("IS_EMPTY\n");
+ ret = -ENODEV;
+ }
+ else
+ {
+ /* Yes.. return the geometry of the card */
+
+ geometry->geo_available = TRUE;
+ geometry->geo_mediachanged = priv->mediachanged;
+#ifdef CONFIG_FS_WRITABLE
+ geometry->geo_writeenabled = !priv->wrprotect;
+#else
+ geometry->geo_writeenabled = FALSE;
#endif
- return -ENOSYS;
+ geometry->geo_nsectors = priv->nblocks;
+ geometry->geo_sectorsize = priv->rdblocklen;
+
+ fvdbg("available: TRUE mediachanged: %s writeenabled: %s\n",
+ geometry->geo_mediachanged ? "TRUE" : "FALSE",
+ geometry->geo_writeenabled ? "TRUE" : "FALSE");
+ fvdbg("nsectors: %ld sectorsize: %d\n",
+ (long)geometry->geo_nsectors, geometry->geo_sectorsize);
+
+ priv->mediachanged = FALSE;
+ ret = OK;
+ }
}
- return -EINVAL;
+ return ret;
}
/****************************************************************************
@@ -311,25 +386,415 @@ static int mmcsd_geometry(FAR struct inode *inode, struct geometry *geometry)
static int mmcsd_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
{
- struct mmcsd_state_s *priv ;
+ struct mmcsd_state_s *priv;
+ int ret;
fvdbg("Entry\n");
DEBUGASSERT(inode && inode->i_private);
priv = (struct mmcsd_state_s *)inode->i_private;
-#ifdef CONFIG_CPP_HAVE_WARNING
-# warning "Not implemented"
-#endif
- return -ENOTTY;
+ /* Process the IOCTL by command */
+
+ switch (cmd)
+ {
+ case BIOC_PROBE: /* Check for media in the slot */
+ {
+ fvdbg("BIOC_PROBE\n");
+
+ /* Probe the MMC/SD slot for media */
+
+ ret = mmcsd_probe(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_probe failed: %d\n", ret);
+ }
+ }
+ break;
+
+ case BIOC_EJECT: /* Media has been removed from the slot */
+ {
+ fvdbg("BIOC_EJECT\n");
+
+ /* Process the removal of the card */
+
+ ret = mmcsd_removed(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_removed failed: %d\n", ret);
+ }
+ }
+ break;
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
}
/****************************************************************************
* Initialization/uninitialization/reset
****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_mmcinitialize
+ *
+ * Description:
+ * We believe that there is an MMC card in the slot. Attempt to initialize
+ * and configure the MMC card. This is called only from mmcsd_probe().
+ *
+ ****************************************************************************/
+
+static inline int mmcsd_mmcinitialize(struct mmcsd_state_s *priv)
+{
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: mmcsd_sdinitialize
+ *
+ * Description:
+ * We believe that there is an SD card in the slot. Attempt to initialize
+ * and configure the SD card. This is called only from mmcsd_probe().
+ *
+ ****************************************************************************/
+
+static inline int mmcsd_sdinitialize(struct mmcsd_state_s *priv)
+{
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: mmcsd_cardidentify
+ *
+ * Description:
+ * We believe that there is media in the slot. Attempt to initialize and
+ * configure the card. This is called only from mmcsd_probe().
+ *
+ ****************************************************************************/
+
+static inline int mmcsd_cardidentify(struct mmcsd_state_s *priv)
+{
+ uint32 response;
+ uint32 start;
+ uint32 elapsed;
+ uint32 state;
+ uint32 sdcapacity = MMCD_ACMD41_STDCAPACITY;
+ boolean cardbusy;
+ int ret;
+
+ /* Assume failure to identify the card */
+
+ priv->type = MMCSD_CARDTYPE_UNKNOWN;
+ priv->mode = MMCSDMODE_POLLED;
+
+ /* Check if there is a card present in the slot. This is normally a matter is
+ * of GPIO sensing.
+ */
+
+ if (SDIO_PRESENT(priv->dev))
+ {
+ fvdbg("No card present\n");
+ return -ENODEV;
+ }
+
+ /* Initialize device state structure */
+
+ priv->type = MMCSD_CARDTYPE_SDV1;
+ priv->mode = MMCSDMODE_POLLED;
+
+ /* Set ID mode clocking (<400KHz) */
+
+ SDIO_CLOCK(priv->dev, CLOCK_IDMODE);
+
+ /* After power up at least 74 clock cycles are required prior to starting bus
+ * communication
+ */
+
+ up_udelay(MMCSD_POWERUP_DELAY);
+
+ /* Then send CMD0 (twice just to be sure) */
+
+ SDIO_SENDCMD(priv->dev, MMCSD_CMD0, 0);
+ SDIO_WAITRESPONSE(priv->dev, MMCSD_CMD0);
+ SDIO_SENDCMD(priv->dev, MMCSD_CMD0, 0);
+ up_udelay(MMCSD_IDLE_DELAY);
+
+ /* Check for SDHC Version 2.x. Send CMD8 to verify SD card interface
+ * operating condition. CMD 8 is reserved on SD version 1.0 and MMC.
+ *
+ * CMD8 Argument:
+ * [31:12]: Reserved (shall be set to '0') * [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
+ * [7:0]: Check Pattern (recommended 0xaa)
+ * CMD8 Response: R7
+ */
+
+ SDIO_SENDCMD(priv->dev, SD_CMD8, MMCSD_CMD8CHECKPATTERN|MMCSD_CMD8VOLTAGE_27);
+ ret = SDIO_RECVR7(priv->dev, SD_CMD8, &response);
+ if (ret == OK)
+ {
+ /* CMD8 succeeded this is probably a SDHC card. Verify the operating
+ * voltage and that the check pattern was correctly echoed
+ */
+
+ if (((response & MMCSD_R7VOLTAGE_MASK) == MMCSD_R7VOLTAGE_27) &&
+ ((response & MMCSD_R7ECHO_MASK) == MMCSD_R7CHECKPATTERN))
+ {
+ fvdbg("SD V2.x card\n");
+ priv->type = MMCSD_CARDTYPE_SDV2;
+ sdcapacity = MMCD_ACMD41_HIGHCAPACITY;
+ }
+ else
+ {
+ fdbg("ERROR: R7: %08x\n", r7);
+ return -EIO;
+ }
+ }
+
+ /* At this point, type is either UNKNOWN or SDV2. Try sending
+ * CMD55 and (maybe) ACMD41 for up to 1 second or until the card
+ * exits the IDLE state. CMD55 is supported by SD V1.x and SD V2.x,
+ * but not MMC
+ */
+
+ start = g_system_timer;
+ elapsed = 0;
+ do
+ {
+ /* Send CMD55 */
+
+ SDIO_SENDCMD(priv->dev, SD_CMD55, 0);
+ ret = SDIO_RECVR1(priv->dev, SD_CMD55, &response);
+ if (ret != OK)
+ {
+ /* If the error is a timeout, then it is an MMC card */
+
+ if (ret == -ETIMEDOUT)
+ {
+ fvdbg("ERROR: CMD55 timeout, assuming MMC card\n");
+ priv->type = MMCSD_CARDTYPE_MMC;
+ break;
+ }
+ else
+ {
+ fdbg("ERROR: CMD55 RECVR1: %d\n", ret);
+ return -EIO;
+ }
+ }
+ else
+ {
+ /* CMD55 succeeded. CMD55 is supported by SD V1.x and SD V2.x,
+ * but not MMC. If we did not previoulsy determine that this is
+ * an SD V2.x (via CMD8), then this must be SD V1.x
+ */
+
+ if (priv->type == MMCSD_CARDTYPE_UNKNOWN)
+ {
+ fvdbg("SD V1.x card\n");
+ priv->type = MMCSD_CARDTYPE_SDV1;
+ }
+
+ /* Save the state from CMD55 R1 response */
+
+ state = response & MMCSD_R1_STATE_MASK;
+ fvdbg("CMD55 R1: %08x\n", response);
+
+ /* Send ACMD41 */
+
+ SDIO_SENDCMD(priv->dev, SD_ACMD41, MMCD_ACMD41_VOLTAGEWINDOW|sdcapacity);
+ ret = SDIO_RECVR3(priv->dev, SD_CMD55, &response);
+ if (ret != OK)
+ {
+ fdbg("ERROR: ACMD41 RECVR1: %d\n", ret);
+ return -EIO;
+ }
+ else
+ {
+ /* Check if the card is busy */
+
+ cardbusy = ((response >> 31) == 1);
+ if (!cardbusy)
+ {
+ /* Check if this is a SD V2.x card that supports block addressing */
+
+ if ((response & MMCD_R3_HIGHCAPACITY) != 0)
+ {
+ fvdbg("SD V2.x card with block addressing\n");
+ DEBUGASSERT(priv->type == MMCSD_CARDTYPE_SDV2);
+ priv->type |= MMCSD_CARDTYPE_BLOCK;
+ }
+ }
+ }
+ }
+ elapsed = g_system_timer - start;
+ }
+ while (elapsed < TICK_PER_SEC && (ret != OK || state != MMCSD_R1_STATE_IDLE || cardbusy));
+
+ /* We get here when the above loop completes, either (1) because this is
+ * an MMC card, (2) we could not communicate properly with the card due to
+ * errors (and the loop times out), or (3) it is an SD card that has successfully
+ * transitioned to the IDLE state.
+ */
+
+ if (elapsed >= TICK_PER_SEC || priv->type == MMCSD_CARDTYPE_UNKNOWN)
+ {
+ fdbg("ERROR: Failed to identify card\n");
+ return -EIO;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mmcsd_probe
+ *
+ * Description:
+ * Check for media inserted in a slot. Called (1) during initialization to
+ * see if there was a card in the slot at power up, (2) when/if a media
+ * insertion event occurs, or (3) if the BIOC_PROBE ioctl command is
+ * received.
+ *
+ ****************************************************************************/
+
+static int mmcsd_probe(struct mmcsd_state_s *priv)
+{
+ int ret;
+
+ fvdbg("type=%d probed=%d\n", priv->type, priv->probed);
+
+ /* If we have reliable card detection events and if we have
+ * already probed the card, then we don't need to do anything
+ * else
+ */
+
+#ifdef CONFIG_MMCSD_HAVECARDDETECT
+ if (priv->probed && SDIO_PRESENT(priv->dev))
+ {
+ return OK;
+ }
+#endif
+
+ /* Otherwise, we are going to probe the card. There are lots of
+ * possibilities here: We may think that there is a card in the slot,
+ * or not. There may be a card in the slot, or not. If there is
+ * card in the slot, perhaps it is a different card than we one we
+ * think is there? The safest thing to do is to process the card
+ * removal first and start from known place.
+ */
+
+ mmcsd_removed(priv);
+
+ /* Now.. is there a card in the slot? */
+
+ if (SDIO_PRESENT(priv->dev))
+ {
+ /* Yes.. probe it. First, what kind of card was inserted? */
+
+ ret = mmcsd_cardidentify(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Failed to initialize card: %d\n");
+ SDIO_EVENTENABLE(priv->dev, SDIOEVENT_INSERTED);
+ }
+ else
+ {
+ /* Then initialize the driver according to the identified card type */
+
+ switch (priv->type)
+ {
+ case MMCSD_CARDTYPE_MMC: /* MMC card */
+ ret = mmcsd_mmcinitialize(priv);
+
+ case MMCSD_CARDTYPE_SDV1: /* Bit 1: SD version 1.x */
+ case MMCSD_CARDTYPE_SDV2: /* SD version 2.x with byte addressing */
+ case MMCSD_CARDTYPE_SDV2|MMCSD_CARDTYPE_BLOCK: /* SD version 2.x with block addressing */
+ ret = mmcsd_sdinitialize(priv);
+
+ case MMCSD_CARDTYPE_UNKNOWN: /* Unknown card type */
+ default:
+ fdbg("ERROR: Internal confusion: %d\n", priv->type);
+ ret = -EPERM;
+ };
+
+ /* Was the card configured successfully? */
+
+ if (ret == OK)
+ {
+ /* Yes... */
+
+ fvdbg("Capacity: %d Kbytes\n", priv->capacity / 1024);
+ priv->mediachanged = TRUE;
+
+ /* Set up to receive asynchronous, media removal events */
+
+ SDIO_EVENTENABLE(priv->dev, SDIOEVENT_EJECTED);
+ }
+ }
+
+ /* In any event, we have probed this card */
+
+ priv->probed = TRUE;
+ }
+ else
+ {
+ /* There is no card in the slot */
+
+ fvdbg("No card\n");
+ SDIO_EVENTENABLE(priv->dev, SDIOEVENT_INSERTED);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_removed
+ *
+ * Description:
+ * Disable support for media in the slot. Called (1) when/if a media
+ * removal event occurs, or (2) if the BIOC_EJECT ioctl command is
+ * received.
+ *
+ ****************************************************************************/
+
+static int mmcsd_removed(struct mmcsd_state_s *priv)
+{
+ fvdbg("type: %d present: %d\n", priv->type, SDIO_PRESENT(priv->dev));
+
+ /* Forget the card geometry, pretend the slot is empty (it might not
+ * be), and that the card has never been initialized.
+ */
+
+ priv->capacity = 0; /* Capacity=0 sometimes means no media */
+ priv->rdblocklen = 0;
+ priv->wrblocklen = 0;
+ priv->mediachanged = FALSE;
+ priv->type = MMCSD_CARDTYPE_UNKNOWN;
+ priv->probed = FALSE;
+ priv->selected = FALSE;
+ priv->rca = 0;
+ priv->selblocklen = 0;
+
+ /* Go back to the default 1-bit data bus. */
+
+ SDIO_WIDEBUS(priv->dev, FALSE);
+ priv->widebus = FALSE;
+
+ /* Disable clocking to the card */
+
+ (void)SDIO_CLOCK(priv->dev, CLOCK_SDIO_DISABLED);
+
+ /* Enable logic to detect if a card is re-inserted */
+
+ SDIO_EVENTENABLE(priv->dev, SDIOEVENT_INSERTED);
+ return OK;
+}
+
/****************************************************************************
* Name: mmcsd_hwinitialize
*
- * Description: One-time hardware initialization
+ * Description:
+ * One-time hardware initialization
*
****************************************************************************/
@@ -344,13 +809,19 @@ static int mmcsd_hwinitialize(struct mmcsd_state_s *priv)
/****************************************************************************
* Name: mmcsd_hwinitialize
*
- * Description: Restore the MMC/SD slot to the uninitialized state
+ * Description:
+ * Restore the MMC/SD slot to the uninitialized state
*
****************************************************************************/
static inline void mmcsd_hwuninitialize(struct mmcsd_state_s *priv)
{
- SDIO_RESET(priv->dev);
+ if (priv)
+ {
+ mmcsd_removed(priv);
+ SDIO_RESET(priv->dev);
+ free(priv);
+ }
}
/****************************************************************************
@@ -385,7 +856,7 @@ int mmcsd_slotinitialize(int minor, int slotno, FAR struct sdio_dev_s *dev)
/* Sanity check */
#ifdef CONFIG_DEBUG
- if (minor < 0 || minor > 255 || !dev)
+ if ((unsigned)slotno >= CONFIG_MMCSD_NSLOTS || minor < 0 || minor > 255 || !dev)
{
return -EINVAL;
}
@@ -410,7 +881,7 @@ int mmcsd_slotinitialize(int minor, int slotno, FAR struct sdio_dev_s *dev)
/* Was the slot initialized successfully? */
- if (ret != 0)
+ if (ret != OK)
{
/* No... But the error ENODEV is returned if hardware initialization
* succeeded but no card is inserted in the slot. In this case, the