From 0eb3b39e5e66fca98864b6724534ba65740d2473 Mon Sep 17 00:00:00 2001 From: gdisirio Date: Sun, 8 Nov 2009 13:35:58 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1276 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/io/mmc_spi.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- os/io/mmc_spi.h | 42 ++++++++++++-- 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 @@ -31,6 +31,18 @@ /* Driver pre-compile time settings. */ /*===========================================================================*/ +/** + * @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. @@ -46,6 +58,23 @@ #define MMC_POLLING_DELAY 10 #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 -- cgit v1.2.3