ChibiOS/RT Architecture - Reference Manual - Guides |
00001 /* 00002 ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010 Giovanni Di Sirio. 00003 00004 This file is part of ChibiOS/RT. 00005 00006 ChibiOS/RT is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 3 of the License, or 00009 (at your option) any later version. 00010 00011 ChibiOS/RT is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program. If not, see <http://www.gnu.org/licenses/>. 00018 00019 --- 00020 00021 A special exception to the GPL can be applied should you wish to distribute 00022 a combined work that includes ChibiOS/RT, without being obliged to provide 00023 the source code for any proprietary components. See the file exception.txt 00024 for full details of how and when the exception can be applied. 00025 */ 00026 00027 /** 00028 * @file mmc_spi.c 00029 * @brief MMC over SPI driver code. 00030 * 00031 * @addtogroup MMC_SPI 00032 * @{ 00033 */ 00034 00035 #include "ch.h" 00036 #include "hal.h" 00037 00038 #if CH_HAL_USE_MMC_SPI || defined(__DOXYGEN__) 00039 00040 /*===========================================================================*/ 00041 /* Driver exported variables. */ 00042 /*===========================================================================*/ 00043 00044 /*===========================================================================*/ 00045 /* Driver local variables. */ 00046 /*===========================================================================*/ 00047 00048 /*===========================================================================*/ 00049 /* Driver local functions. */ 00050 /*===========================================================================*/ 00051 00052 /** 00053 * @brief Inserion monitor timer callback function. 00054 * 00055 * @param[in] p pointer to the @p MMCDriver object 00056 */ 00057 void tmrfunc(void *p) { 00058 MMCDriver *mmcp = p; 00059 00060 if (mmcp->mmc_cnt > 0) { 00061 if (mmcp->mmc_is_inserted()) { 00062 if (--mmcp->mmc_cnt == 0) { 00063 mmcp->mmc_state = MMC_INSERTED; 00064 chEvtBroadcastI(&mmcp->mmc_inserted_event); 00065 } 00066 } 00067 else 00068 mmcp->mmc_cnt = MMC_POLLING_INTERVAL; 00069 } 00070 else { 00071 if (!mmcp->mmc_is_inserted()) { 00072 mmcp->mmc_state = MMC_WAIT; 00073 mmcp->mmc_cnt = MMC_POLLING_INTERVAL; 00074 chEvtBroadcastI(&mmcp->mmc_removed_event); 00075 } 00076 } 00077 chVTSetI(&mmcp->mmc_vt, MS2ST(MMC_POLLING_DELAY), tmrfunc, mmcp); 00078 } 00079 00080 /** 00081 * @brief Waits an idle condition. 00082 * 00083 * @param[in] mmcp pointer to the @p MMCDriver object 00084 */ 00085 static void wait(MMCDriver *mmcp) { 00086 int i; 00087 uint8_t buf[4]; 00088 00089 for (i = 0; i < 16; i++) { 00090 spiReceive(mmcp->mmc_spip, 1, buf); 00091 if (buf[0] == 0xFF) 00092 break; 00093 } 00094 /* Looks like it is a long wait.*/ 00095 while (TRUE) { 00096 spiReceive(mmcp->mmc_spip, 1, buf); 00097 if (buf[0] == 0xFF) 00098 break; 00099 #ifdef MMC_NICE_WAITING 00100 /* Trying to be nice with the other threads.*/ 00101 chThdSleep(1); 00102 #endif 00103 } 00104 } 00105 00106 /** 00107 * @brief Sends a command header. 00108 * 00109 * @param[in] mmcp pointer to the @p MMCDriver object 00110 * @param cmd[in] the command id 00111 * @param arg[in] the command argument 00112 */ 00113 static void send_hdr(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) { 00114 uint8_t buf[6]; 00115 00116 /* Wait for the bus to become idle if a write operation was in progress. */ 00117 wait(mmcp); 00118 00119 buf[0] = 0x40 | cmd; 00120 buf[1] = arg >> 24; 00121 buf[2] = arg >> 16; 00122 buf[3] = arg >> 8; 00123 buf[4] = arg; 00124 buf[5] = 0x95; /* Valid for CMD0 ignored by other commands. */ 00125 spiSend(mmcp->mmc_spip, 6, buf); 00126 } 00127 00128 /** 00129 * @brief Receives a single byte response. 00130 * 00131 * @param[in] mmcp pointer to the @p MMCDriver object 00132 * @return The response as an @p uint8_t value. 00133 * @retval 0xFF timed out. 00134 */ 00135 static uint8_t recvr1(MMCDriver *mmcp) { 00136 int i; 00137 uint8_t r1[1]; 00138 00139 for (i = 0; i < 9; i++) { 00140 spiReceive(mmcp->mmc_spip, 1, r1); 00141 if (r1[0] != 0xFF) 00142 return r1[0]; 00143 } 00144 return 0xFF; 00145 } 00146 00147 /** 00148 * @brief Sends a command an returns a single byte response. 00149 * 00150 * @param[in] mmcp pointer to the @p MMCDriver object 00151 * @param cmd[in] the command id 00152 * @param arg[in] the command argument 00153 * @return The response as an @p uint8_t value. 00154 * @retval 0xFF timed out. 00155 */ 00156 static uint8_t send_command(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) { 00157 uint8_t r1; 00158 00159 spiSelect(mmcp->mmc_spip); 00160 send_hdr(mmcp, cmd, arg); 00161 r1 = recvr1(mmcp); 00162 spiUnselect(mmcp->mmc_spip); 00163 return r1; 00164 } 00165 00166 /** 00167 * @brief Waits that the card reaches an idle state. 00168 * 00169 * @param[in] mmcp pointer to the @p MMCDriver object 00170 */ 00171 static void sync(MMCDriver *mmcp) { 00172 uint8_t buf[1]; 00173 00174 spiSelect(mmcp->mmc_spip); 00175 while (TRUE) { 00176 spiReceive(mmcp->mmc_spip, 1, buf); 00177 if (buf[0] == 0xFF) 00178 break; 00179 #ifdef MMC_NICE_WAITING 00180 chThdSleep(1); /* Trying to be nice with the other threads.*/ 00181 #endif 00182 } 00183 spiUnselect(mmcp->mmc_spip); 00184 } 00185 00186 /*===========================================================================*/ 00187 /* Driver exported functions. */ 00188 /*===========================================================================*/ 00189 00190 /** 00191 * @brief MMC over SPI driver initialization. 00192 */ 00193 void mmcInit(void) { 00194 00195 } 00196 00197 /** 00198 * @brief Initializes an instance. 00199 * 00200 * @param[in] mmcp pointer to the @p MMCDriver object 00201 * @param[in] spip pointer to the SPI driver to be used as interface 00202 * @param[in] lscfg low speed configuration for the SPI driver 00203 * @param[in] hscfg high speed configuration for the SPI driver 00204 * @param[in] is_protected function that returns the card write protection 00205 * setting 00206 * @param[in] is_inserted function that returns the card insertion sensor 00207 * status 00208 */ 00209 void mmcObjectInit(MMCDriver *mmcp, SPIDriver *spip, 00210 const SPIConfig *lscfg, const SPIConfig *hscfg, 00211 mmcquery_t is_protected, mmcquery_t is_inserted) { 00212 00213 mmcp->mmc_state = MMC_STOP; 00214 mmcp->mmc_config = NULL; 00215 mmcp->mmc_spip = spip; 00216 mmcp->mmc_lscfg = lscfg; 00217 mmcp->mmc_hscfg = hscfg; 00218 mmcp->mmc_is_protected = is_protected; 00219 mmcp->mmc_is_inserted = is_inserted; 00220 chEvtInit(&mmcp->mmc_inserted_event); 00221 chEvtInit(&mmcp->mmc_removed_event); 00222 } 00223 00224 /** 00225 * @brief Configures and activates the MMC peripheral. 00226 * 00227 * @param[in] mmcp pointer to the @p MMCDriver object 00228 * @param[in] config pointer to the @p MMCConfig object 00229 */ 00230 void mmcStart(MMCDriver *mmcp, const MMCConfig *config) { 00231 00232 chDbgCheck((mmcp != NULL) && (config != NULL), "mmcStart"); 00233 00234 chSysLock(); 00235 chDbgAssert(mmcp->mmc_state == MMC_STOP, "mmcStart(), #1", "invalid state"); 00236 mmcp->mmc_config = config; 00237 mmcp->mmc_state = MMC_WAIT; 00238 mmcp->mmc_cnt = MMC_POLLING_INTERVAL; 00239 chVTSetI(&mmcp->mmc_vt, MS2ST(MMC_POLLING_DELAY), tmrfunc, mmcp); 00240 chSysUnlock(); 00241 } 00242 00243 /** 00244 * @brief Disables the MMC peripheral. 00245 * 00246 * @param[in] mmcp pointer to the @p MMCDriver object 00247 */ 00248 void mmcStop(MMCDriver *mmcp) { 00249 00250 chDbgCheck(mmcp != NULL, "mmcStop"); 00251 00252 chSysLock(); 00253 chDbgAssert((mmcp->mmc_state != MMC_UNINIT) && 00254 (mmcp->mmc_state != MMC_READING) && 00255 (mmcp->mmc_state != MMC_WRITING), 00256 "mmcStop(), #1", 00257 "invalid state"); 00258 if (mmcp->mmc_state != MMC_STOP) { 00259 mmcp->mmc_state = MMC_STOP; 00260 chVTResetI(&mmcp->mmc_vt); 00261 } 00262 chSysUnlock(); 00263 spiStop(mmcp->mmc_spip); 00264 } 00265 00266 /** 00267 * @brief Performs the initialization procedure on the inserted card. 00268 * @details This function should be invoked when a card is inserted and 00269 * brings the driver in the @p MMC_READY state where it is possible 00270 * to perform read and write operations. 00271 * @note It is possible to invoke this function from the insertion event 00272 * handler. 00273 * 00274 * @param[in] mmcp pointer to the @p MMCDriver object 00275 * @return The operation status. 00276 * @retval FALSE the operation was successful and the driver is now 00277 * in the @p MMC_READY state. 00278 * @retval TRUE the operation failed. 00279 */ 00280 bool_t mmcConnect(MMCDriver *mmcp) { 00281 unsigned i; 00282 bool_t result; 00283 00284 chDbgCheck(mmcp != NULL, "mmcConnect"); 00285 00286 chDbgAssert((mmcp->mmc_state != MMC_UNINIT) && 00287 (mmcp->mmc_state != MMC_STOP), 00288 "mmcConnect(), #1", 00289 "invalid state"); 00290 00291 if (mmcp->mmc_state == MMC_INSERTED) { 00292 /* Slow clock mode and 128 clock pulses.*/ 00293 spiStart(mmcp->mmc_spip, mmcp->mmc_lscfg); 00294 spiIgnore(mmcp->mmc_spip, 16); 00295 00296 /* SPI mode selection.*/ 00297 i = 0; 00298 while (TRUE) { 00299 if (send_command(mmcp, MMC_CMDGOIDLE, 0) == 0x01) 00300 break; 00301 if (++i >= MMC_CMD0_RETRY) 00302 return TRUE; 00303 chThdSleepMilliseconds(10); 00304 } 00305 00306 /* Initialization. */ 00307 i = 0; 00308 while (TRUE) { 00309 uint8_t b = send_command(mmcp, MMC_CMDINIT, 0); 00310 if (b == 0x00) 00311 break; 00312 if (b != 0x01) 00313 return TRUE; 00314 if (++i >= MMC_CMD1_RETRY) 00315 return TRUE; 00316 chThdSleepMilliseconds(10); 00317 } 00318 00319 /* Initialization complete, full speed. */ 00320 spiStart(mmcp->mmc_spip, mmcp->mmc_hscfg); 00321 00322 /* Setting block size.*/ 00323 if (send_command(mmcp, MMC_CMDSETBLOCKLEN, MMC_SECTOR_SIZE) != 0x00) 00324 return TRUE; 00325 00326 /* Transition to MMC_READY state (if not extracted).*/ 00327 chSysLock(); 00328 if (mmcp->mmc_state == MMC_INSERTED) { 00329 mmcp->mmc_state = MMC_READY; 00330 result = FALSE; 00331 } 00332 else 00333 result = TRUE; 00334 chSysUnlock(); 00335 return result; 00336 } 00337 if (mmcp->mmc_state == MMC_READY) 00338 return FALSE; 00339 /* Any other state is invalid.*/ 00340 return TRUE; 00341 } 00342 00343 /** 00344 * @brief Brings the driver in a state safe for card removal. 00345 * 00346 * @param[in] mmcp pointer to the @p MMCDriver object 00347 * @return The operation status. 00348 * @retval FALSE the operation was successful and the driver is now 00349 * in the @p MMC_INSERTED state. 00350 * @retval TRUE the operation failed. 00351 */ 00352 bool_t mmcDisconnect(MMCDriver *mmcp) { 00353 bool_t status; 00354 00355 chDbgCheck(mmcp != NULL, "mmcDisconnect"); 00356 00357 chDbgAssert((mmcp->mmc_state != MMC_UNINIT) && 00358 (mmcp->mmc_state != MMC_STOP), 00359 "mmcDisconnect(), #1", 00360 "invalid state"); 00361 switch (mmcp->mmc_state) { 00362 case MMC_READY: 00363 /* Wait for the pending write operations to complete.*/ 00364 sync(mmcp); 00365 chSysLock(); 00366 if (mmcp->mmc_state == MMC_READY) 00367 mmcp->mmc_state = MMC_INSERTED; 00368 chSysUnlock(); 00369 case MMC_INSERTED: 00370 status = FALSE; 00371 default: 00372 status = TRUE; 00373 } 00374 spiStop(mmcp->mmc_spip); 00375 return status; 00376 } 00377 00378 /** 00379 * @brief Starts a sequential read. 00380 * 00381 * @param[in] mmcp pointer to the @p MMCDriver object 00382 * @param[in] startblk first block to read 00383 * @return The operation status. 00384 * @retval FALSE the operation was successful. 00385 * @retval TRUE the operation failed. 00386 */ 00387 bool_t mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk) { 00388 00389 chDbgCheck(mmcp != NULL, "mmcStartSequentialRead"); 00390 00391 chSysLock(); 00392 if (mmcp->mmc_state != MMC_READY) { 00393 chSysUnlock(); 00394 return TRUE; 00395 } 00396 mmcp->mmc_state = MMC_READING; 00397 chSysUnlock(); 00398 00399 spiStart(mmcp->mmc_spip, mmcp->mmc_hscfg); 00400 spiSelect(mmcp->mmc_spip); 00401 send_hdr(mmcp, MMC_CMDREADMULTIPLE, startblk * MMC_SECTOR_SIZE); 00402 if (recvr1(mmcp) != 0x00) { 00403 spiUnselect(mmcp->mmc_spip); 00404 chSysLock(); 00405 if (mmcp->mmc_state == MMC_READING) 00406 mmcp->mmc_state = MMC_READY; 00407 chSysUnlock(); 00408 return TRUE; 00409 } 00410 return FALSE; 00411 } 00412 00413 /** 00414 * @brief Reads a block within a sequential read operation. 00415 * 00416 * @param[in] mmcp pointer to the @p MMCDriver object 00417 * @param[out] buffer pointer to the read buffer 00418 * @return The operation status. 00419 * @retval FALSE the operation was successful. 00420 * @retval TRUE the operation failed. 00421 */ 00422 bool_t mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) { 00423 int i; 00424 00425 chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialRead"); 00426 00427 chSysLock(); 00428 if (mmcp->mmc_state != MMC_READING) { 00429 chSysUnlock(); 00430 return TRUE; 00431 } 00432 chSysUnlock(); 00433 00434 for (i = 0; i < MMC_WAIT_DATA; i++) { 00435 spiReceive(mmcp->mmc_spip, 1, buffer); 00436 if (buffer[0] == 0xFE) { 00437 spiReceive(mmcp->mmc_spip, MMC_SECTOR_SIZE, buffer); 00438 /* CRC ignored. */ 00439 spiIgnore(mmcp->mmc_spip, 2); 00440 return FALSE; 00441 } 00442 } 00443 /* Timeout.*/ 00444 spiUnselect(mmcp->mmc_spip); 00445 chSysLock(); 00446 if (mmcp->mmc_state == MMC_READING) 00447 mmcp->mmc_state = MMC_READY; 00448 chSysUnlock(); 00449 return TRUE; 00450 } 00451 00452 /** 00453 * @brief Stops a sequential read gracefully. 00454 * 00455 * @param[in] mmcp pointer to the @p MMCDriver object 00456 * @return The operation status. 00457 * @retval FALSE the operation was successful. 00458 * @retval TRUE the operation failed. 00459 */ 00460 bool_t mmcStopSequentialRead(MMCDriver *mmcp) { 00461 static const uint8_t stopcmd[] = {0x40 | MMC_CMDSTOP, 0, 0, 0, 0, 1, 0xFF}; 00462 bool_t result; 00463 00464 chDbgCheck(mmcp != NULL, "mmcStopSequentialRead"); 00465 00466 chSysLock(); 00467 if (mmcp->mmc_state != MMC_READING) { 00468 chSysUnlock(); 00469 return TRUE; 00470 } 00471 chSysUnlock(); 00472 00473 spiSend(mmcp->mmc_spip, sizeof(stopcmd), stopcmd); 00474 /* result = recvr1(mmcp) != 0x00;*/ 00475 /* Note, ignored r1 response, it can be not zero, unknown issue.*/ 00476 recvr1(mmcp); 00477 result = FALSE; 00478 spiUnselect(mmcp->mmc_spip); 00479 00480 chSysLock(); 00481 if (mmcp->mmc_state == MMC_READING) 00482 mmcp->mmc_state = MMC_READY; 00483 chSysUnlock(); 00484 return result; 00485 } 00486 00487 /** 00488 * @brief Starts a sequential write. 00489 * 00490 * @param[in] mmcp pointer to the @p MMCDriver object 00491 * @param[in] startblk first block to write 00492 * @return The operation status. 00493 * @retval FALSE the operation was successful. 00494 * @retval TRUE the operation failed. 00495 */ 00496 bool_t mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk) { 00497 00498 chDbgCheck(mmcp != NULL, "mmcStartSequentialWrite"); 00499 00500 chSysLock(); 00501 if (mmcp->mmc_state != MMC_READY) { 00502 chSysUnlock(); 00503 return TRUE; 00504 } 00505 mmcp->mmc_state = MMC_WRITING; 00506 chSysUnlock(); 00507 00508 spiStart(mmcp->mmc_spip, mmcp->mmc_hscfg); 00509 spiSelect(mmcp->mmc_spip); 00510 send_hdr(mmcp, MMC_CMDWRITEMULTIPLE, startblk * MMC_SECTOR_SIZE); 00511 if (recvr1(mmcp) != 0x00) { 00512 spiUnselect(mmcp->mmc_spip); 00513 chSysLock(); 00514 if (mmcp->mmc_state == MMC_WRITING) 00515 mmcp->mmc_state = MMC_READY; 00516 chSysUnlock(); 00517 return TRUE; 00518 } 00519 return FALSE; 00520 } 00521 00522 /** 00523 * @brief Writes a block within a sequential write operation. 00524 * 00525 * @param[in] mmcp pointer to the @p MMCDriver object 00526 * @param[out] buffer pointer to the write buffer 00527 * @return The operation status. 00528 * @retval FALSE the operation was successful. 00529 * @retval TRUE the operation failed. 00530 */ 00531 bool_t mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer) { 00532 static const uint8_t start[] = {0xFF, 0xFC}; 00533 uint8_t b[1]; 00534 00535 chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialWrite"); 00536 00537 chSysLock(); 00538 if (mmcp->mmc_state != MMC_WRITING) { 00539 chSysUnlock(); 00540 return TRUE; 00541 } 00542 chSysUnlock(); 00543 00544 spiSend(mmcp->mmc_spip, sizeof(start), start); /* Data prologue. */ 00545 spiSend(mmcp->mmc_spip, MMC_SECTOR_SIZE, buffer); /* Data. */ 00546 spiIgnore(mmcp->mmc_spip, 2); /* CRC ignored. */ 00547 spiReceive(mmcp->mmc_spip, 1, b); 00548 if ((b[0] & 0x1F) == 0x05) { 00549 wait(mmcp); 00550 return FALSE; 00551 } 00552 00553 /* Error.*/ 00554 spiUnselect(mmcp->mmc_spip); 00555 chSysLock(); 00556 if (mmcp->mmc_state == MMC_WRITING) 00557 mmcp->mmc_state = MMC_READY; 00558 chSysUnlock(); 00559 return TRUE; 00560 } 00561 00562 /** 00563 * @brief Stops a sequential write gracefully. 00564 * 00565 * @param[in] mmcp pointer to the @p MMCDriver object 00566 * @return The operation status. 00567 * @retval FALSE the operation was successful. 00568 * @retval TRUE the operation failed. 00569 */ 00570 bool_t mmcStopSequentialWrite(MMCDriver *mmcp) { 00571 static const uint8_t stop[] = {0xFD, 0xFF}; 00572 00573 chDbgCheck(mmcp != NULL, "mmcStopSequentialWrite"); 00574 00575 chSysLock(); 00576 if (mmcp->mmc_state != MMC_WRITING) { 00577 chSysUnlock(); 00578 return TRUE; 00579 } 00580 chSysUnlock(); 00581 00582 spiSend(mmcp->mmc_spip, sizeof(stop), stop); 00583 spiUnselect(mmcp->mmc_spip); 00584 00585 chSysLock(); 00586 if (mmcp->mmc_state == MMC_WRITING) { 00587 mmcp->mmc_state = MMC_READY; 00588 chSysUnlock(); 00589 return FALSE; 00590 } 00591 chSysUnlock(); 00592 return TRUE; 00593 } 00594 00595 #endif /* CH_HAL_USE_MMC_SPI */ 00596 00597 /** @} */