aboutsummaryrefslogtreecommitdiffstats
path: root/os
diff options
context:
space:
mode:
authorgdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2009-11-08 13:35:58 +0000
committergdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2009-11-08 13:35:58 +0000
commit0eb3b39e5e66fca98864b6724534ba65740d2473 (patch)
tree168ce8dffc49c4afbeb74b2d1bb8fdfc26c00160 /os
parent70e33302eba298105eda45752b09915626d163fd (diff)
downloadChibiOS-0eb3b39e5e66fca98864b6724534ba65740d2473.tar.gz
ChibiOS-0eb3b39e5e66fca98864b6724534ba65740d2473.tar.bz2
ChibiOS-0eb3b39e5e66fca98864b6724534ba65740d2473.zip
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1276 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os')
-rw-r--r--os/io/mmc_spi.c167
-rw-r--r--os/io/mmc_spi.h42
2 files changed, 202 insertions, 7 deletions
diff --git a/os/io/mmc_spi.c b/os/io/mmc_spi.c
index 3c1b63cc7..ae29c4d34 100644
--- a/os/io/mmc_spi.c
+++ b/os/io/mmc_spi.c
@@ -55,6 +55,121 @@ void tmrfunc(void *p) {
chVTSetI(&mmcp->mmc_vt, MS2ST(MMC_POLLING_DELAY), tmrfunc, mmcp);
}
+/**
+ * @brief Waits an idle condition.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ */
+static void wait(MMCDriver *mmcp) {
+ int i;
+ uint8_t buf[4];
+
+ for (i = 0; i < 16; i++) {
+ spiReceive(mmcp->mmc_spip, 1, buf);
+ if (buf[0] == 0xFF)
+ break;
+ }
+ /* Looks like it is a long wait.*/
+ while (TRUE) {
+ spiReceive(mmcp->mmc_spip, 1, buf);
+ if (buf[0] == 0xFF)
+ break;
+#ifdef MMC_NICE_WAITING
+ /* Trying to be nice with the other threads.*/
+ chThdSleep(1);
+#endif
+ }
+}
+
+/**
+ * @brief Sends a command header.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param cmd[in] the command id
+ * @param arg[in] the command argument
+ */
+static void send_hdr(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
+ uint8_t buf[6];
+
+ /* Wait for the bus to become idle if a write operation was in progress. */
+ wait(mmcp);
+
+ buf[0] = 0x40 | cmd;
+ buf[1] = arg >> 24;
+ buf[2] = arg >> 16;
+ buf[3] = arg >> 8;
+ buf[4] = arg;
+ buf[5] = 0x95; /* Valid for CMD0 ignored by other commands. */
+ spiSend(mmcp->mmc_spip, 6, buf);
+}
+
+/**
+ * @brief Receives a single byte response.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ *
+ * @return The response as an @p uint8_t value.
+ * @retval 0xFF timed out.
+ */
+static uint8_t recvr1(MMCDriver *mmcp) {
+ int i;
+ uint8_t r1[1];
+
+ for (i = 0; i < 9; i++) {
+ spiReceive(mmcp->mmc_spip, 1, r1);
+ if (r1[0] != 0xFF)
+ return r1[0];
+ }
+ return 0xFF;
+}
+
+/**
+ * @brief Sends a command an returns a single byte response.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param cmd[in] the command id
+ * @param arg[in] the command argument
+ *
+ * @return The response as an @p uint8_t value.
+ * @retval 0xFF timed out.
+ */
+static uint8_t send_command(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
+ uint8_t r1;
+
+ spiSelect(mmcp->mmc_spip);
+ send_hdr(mmcp, cmd, arg);
+ r1 = recvr1(mmcp);
+ spiUnselect(mmcp->mmc_spip);
+ return r1;
+}
+
+/**
+ * @brief Receives a 512 bytes block and ignores 2 CRC bytes.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param[out] buf pointer to the buffer
+ *
+ * @return The operation status.
+ * @retval FALSE the operation was successful.
+ * @retval TRUE the operation timed out.
+ */
+static bool_t get_data(MMCDriver *mmcp, uint8_t *buf) {
+ int i;
+ uint8_t ignored[2];
+
+ for (i = 0; i < MMC_WAIT_DATA; i++) {
+ spiReceive(mmcp->mmc_spip, 1, buf);
+ if (buf[0] == 0xFE) {
+ spiReceive(mmcp->mmc_spip, 512, buf);
+ /* CRC ignored. */
+ spiReceive(mmcp->mmc_spip, 2, ignored);
+ return FALSE;
+ }
+ }
+ /* Timeout.*/
+ return TRUE;
+}
+
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
@@ -119,11 +234,61 @@ void mmcStop(MMCDriver *mmcp) {
(mmcp->mmc_state != MMC_RUNNING),
"mmcStop(), #1",
"invalid state");
- if (mmcp->mmc_state == MMC_READY) {
+ if (mmcp->mmc_state != MMC_STOP) {
mmcp->mmc_state = MMC_STOP;
chVTResetI(&mmcp->mmc_vt);
+ spiStop(mmcp->mmc_spip);
}
chSysUnlock();
}
+/**
+ * @brief Performs the initialization procedure on the inserted card.
+ * @details This function should be invoked when a card is inserted and
+ * brings the driver in the @p MMC_READY state where it is possible
+ * to perform read and write operations.
+ * @note It is possible to invoke this function from the insertion event
+ * handler.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ *
+ * @return The operation status.
+ * @retval FALSE the operation was successful and the driver is now
+ * in the @p MMC_READY state.
+ * @retval TRUE the operation failed.
+ */
+bool_t mmcOpen(MMCDriver *mmcp) {
+ unsigned i;
+
+ /* Slow clock mode and 128 clock pulses.*/
+ spiStart(mmcp->mmc_spip, mmcp->mmc_lscfg);
+
+ /* SPI mode selection.*/
+ i = 0;
+ while (TRUE) {
+ if (send_command(mmcp, MMC_CMDGOIDLE, 0) == 0x01)
+ break;
+ if (++i >= MMC_CMD0_RETRY)
+ return TRUE;
+ chThdSleepMilliseconds(10);
+ }
+
+ /* Initialization. */
+ i = 0;
+ while (TRUE) {
+ uint8_t b = send_command(mmcp, MMC_CMDINIT, 0);
+ if (b == 0x00)
+ break;
+ if (b != 0x01)
+ return TRUE;
+ if (++i >= MMC_CMD1_RETRY)
+ return TRUE;
+ chThdSleepMilliseconds(10);
+ }
+
+ /* Initialization complete, full speed. */
+ spiStart(mmcp->mmc_spip, mmcp->mmc_hscfg);
+ return FALSE;
+}
+
/** @} */
diff --git a/os/io/mmc_spi.h b/os/io/mmc_spi.h
index afe8946d1..00f0e617b 100644
--- a/os/io/mmc_spi.h
+++ b/os/io/mmc_spi.h
@@ -32,6 +32,18 @@
/*===========================================================================*/
/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ * This option is recommended also if the SPI driver does not
+ * use a DMA channel and heavily loads the CPU.
+ */
+#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__)
+#define MMC_NICE_WAITING TRUE
+#endif
+
+/**
* @brief Number of positive insertion queries before generating the
* insertion event.
*/
@@ -47,6 +59,23 @@
#endif
/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+#define MMC_CMD0_RETRY 10
+#define MMC_CMD1_RETRY 100
+#define MMC_WAIT_DATA 10000
+
+#define MMC_CMDGOIDLE 0
+#define MMC_CMDINIT 1
+#define MMC_CMDREADCSD 9
+#define MMC_CMDSTOP 12
+#define MMC_CMDREAD 17
+#define MMC_CMDREADMULTIPLE 18
+#define MMC_CMDWRITE 24
+#define MMC_CMDWRITEMULTIPLE 25
+
+/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
@@ -54,12 +83,12 @@
* @brief Driver state machine possible states.
*/
typedef enum {
- MMC_UNINIT = 0, /**< @brief Not initialized. */
- MMC_STOP = 1, /**< @brief Stopped. */
- MMC_WAIT = 2, /**< @brief Waiting card. */
- MMC_INSERTED = 3, /**< @brief Card inserted. */
- MMC_READY = 4, /**< @brief Card ready. */
- MMC_RUNNING = 5 /**< @brief Reading or writing. */
+ MMC_UNINIT = 0, /**< @brief Not initialized. *///!< MMC_UNINIT
+ MMC_STOP = 1, /**< @brief Stopped. *///!< MMC_STOP
+ MMC_WAIT = 2, /**< @brief Waiting card. *///!< MMC_WAIT
+ MMC_INSERTED = 3, /**< @brief Card inserted. *///!< MMC_INSERTED
+ MMC_READY = 4, /**< @brief Card ready. *///!< MMC_READY
+ MMC_RUNNING = 5 /**< @brief Reading or writing. *///!< MMC_RUNNING
} mmcstate_t;
/**
@@ -139,6 +168,7 @@ extern "C" {
mmcquery_t is_protected, mmcquery_t is_inserted);
void mmcStart(MMCDriver *mmcp, const MMCConfig *config);
void mmcStop(MMCDriver *mmcp);
+ bool_t mmcOpen(MMCDriver *mmcp);
#ifdef __cplusplus
}
#endif