SAMV71 Xplained Ultra Software Package 1.5

MEDNandFlash.c

Go to the documentation of this file.
00001 /* ---------------------------------------------------------------------------- */
00002 /*                  Atmel Microcontroller Software Support                      */
00003 /*                       SAM Software Package License                           */
00004 /* ---------------------------------------------------------------------------- */
00005 /* Copyright (c) 2015, Atmel Corporation                                        */
00006 /*                                                                              */
00007 /* All rights reserved.                                                         */
00008 /*                                                                              */
00009 /* Redistribution and use in source and binary forms, with or without           */
00010 /* modification, are permitted provided that the following condition is met:    */
00011 /*                                                                              */
00012 /* - Redistributions of source code must retain the above copyright notice,     */
00013 /* this list of conditions and the disclaimer below.                            */
00014 /*                                                                              */
00015 /* Atmel's name may not be used to endorse or promote products derived from     */
00016 /* this software without specific prior written permission.                     */
00017 /*                                                                              */
00018 /* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
00019 /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
00020 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
00021 /* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
00022 /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
00023 /* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
00024 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
00025 /* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
00026 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
00027 /* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
00028 /* ---------------------------------------------------------------------------- */
00029 
00030 /**
00031  * \file
00032  *
00033  * Implementation of media layer for the NAND flash.
00034  *
00035  */
00036 
00037 /*------------------------------------------------------------------------------
00038  *         Headers
00039  *----------------------------------------------------------------------------*/
00040 
00041 #include "libstoragemedia.h"
00042 
00043 #include <string.h>
00044 #include <assert.h>
00045 
00046 /*------------------------------------------------------------------------------
00047  *         Definitions
00048  *----------------------------------------------------------------------------*/
00049 /// Casts
00050 #define MODEL(interface)        ((struct NandFlashModel *) interface)
00051 #define TRANSLATED(interface)   ((struct TranslatedNandFlash *) interface)
00052 
00053 /*----------------------------------------------------------------------------
00054  *        Local macros
00055  *----------------------------------------------------------------------------*/
00056 #define min(a, b) (((a) < (b)) ? (a) : (b))
00057 
00058 /*-----------------------------------------------------------------------------
00059  *         Internal variables
00060  *----------------------------------------------------------------------------*/
00061 
00062 static Media *currentMedia;
00063 
00064 static uint8_t pageWriteBuffer[NandCommon_MAXPAGEDATASIZE];
00065 static int16_t currentWriteBlock;
00066 static int16_t currentWritePage;
00067 
00068 static uint8_t pageReadBuffer[NandCommon_MAXPAGEDATASIZE];
00069 static int16_t currentReadBlock;
00070 static int16_t currentReadPage;
00071 /*------------------------------------------------------------------------------
00072  *         Internal functions
00073  *----------------------------------------------------------------------------*/
00074 /**
00075  * \brief Writes the current page of data on the NandFlash.
00076  * Returns 0 if successful; otherwise returns 1.
00077  * \param media  Pointer to a TranslatedNandFlash instance.
00078 */
00079 static uint8_t FlushCurrentPage(Media *media)
00080 {
00081     // Make sure there is a page to flush
00082     if (currentWritePage == -1)
00083         return 0;
00084 
00085     TRACE_DEBUG("FlushCurrentPage(B#%d:P#%d)\n\r",
00086                 currentWriteBlock, currentWritePage);
00087 
00088     // Write page
00089     if (TranslatedNandFlash_WritePage(TRANSLATED(media->interface),
00090                                       currentWriteBlock,
00091                                       currentWritePage,
00092                                       pageWriteBuffer,
00093                                       0)) {
00094         TRACE_ERROR("FlushCurrentPage: Failed to write page.\n\r");
00095         return 1;
00096     }
00097 
00098     // No current write page & block
00099     currentWriteBlock = -1;
00100     currentWritePage = -1;
00101     return 0;
00102 }
00103 
00104 /**
00105  * \brief Writes data at an unaligned (page-wise) address and size. The address
00106  * is provided as the block & page number plus an offset. The data to write MUST
00107  * NOT span more than one page.
00108  * Returns 0 if the data has been written; 1 otherwise.
00109  * \param media  Pointer to a NAND flash Media instance.
00110  * \param block  Number of the block to write.
00111  * \param page  Number of the page to write.
00112  * \param offset  Write offset.
00113  * \param buffer  Data buffer.
00114  * \param size  Number of bytes to write.
00115 */
00116 static uint8_t UnalignedWritePage(
00117     Media *media,
00118     uint16_t block,
00119     uint16_t page,
00120     uint16_t offset,
00121     uint8_t *buffer,
00122     uint32_t size)
00123 {
00124     uint8_t error;
00125     uint16_t pageDataSize = NandFlashModel_GetPageDataSize(MODEL(media->interface));
00126     uint8_t writePage = ((size + offset) == pageDataSize);
00127     uint8_t medChange = (currentMedia != media);
00128 
00129     TRACE_DEBUG("UnalignedWritePage(B%d:P%d@%d, %d)\n\r",
00130                  (int)block, (int)page, (int)offset, (int)size);
00131     assert((size + offset) <= pageDataSize);
00132     /* "UnalignedWrite: Write size and offset exceed page data size\n\r" */
00133 
00134     // If size is 0, return immediately
00135     if (size == 0)
00136         return 0;
00137 
00138     // If this is not the current page, flush the previous one
00139     if ((currentWriteBlock != block) || (currentWritePage != page) || (medChange)) {
00140         // Flush and make page the new current page
00141         if (currentMedia != 0) FlushCurrentPage(currentMedia);
00142 
00143         TRACE_DEBUG("Current write page: B#%d:P#%d\n\r", block, page);
00144         currentMedia = media;
00145         currentWriteBlock = block;
00146         currentWritePage = page;
00147 
00148         // Read existing page data in a temporary buffer if the page is not
00149         // entirely written
00150         if (size != pageDataSize) {
00151             error = TranslatedNandFlash_ReadPage(TRANSLATED(media->interface),
00152                                                  block,
00153                                                  page,
00154                                                  pageWriteBuffer,
00155                                                  0);
00156 
00157             if (error) {
00158                 TRACE_ERROR(
00159                     "UnalignedWrite: Could not read existing page data\n\r");
00160                 return 1;
00161             }
00162         }
00163     }
00164 
00165     // Copy data in temporary buffer
00166     memcpy(&(pageWriteBuffer[offset]), buffer, size);
00167 
00168     // Update read buffer if necessary
00169     if ((currentReadPage == currentWritePage)
00170         && (currentReadBlock == currentWriteBlock)
00171         && (!medChange)) {
00172 
00173         TRACE_DEBUG("Updating current read buffer\n\r");
00174         memcpy(&(pageReadBuffer[offset]), buffer, size);
00175     }
00176 
00177     // Flush page if it is complete
00178     if (writePage)
00179         FlushCurrentPage(media);
00180 
00181     return 0;
00182 }
00183 
00184 /**
00185  * \brief Writes a data buffer at the specified address on a NandFlash media. An
00186  * optional callback can be triggered after the transfer is completed.
00187  * Returns MED_STATUS_SUCCESS if the transfer has been started successfully;
00188  * otherwise returns MED_STATUS_ERROR.
00189  * \param media  Pointer to the NandFlash Media instance.
00190  * \param address  Address where the data shall be written.
00191  * \param data  Data buffer.
00192  * \param length  Number of bytes to write.
00193  * \param callback  Optional callback to call when the write is finished.
00194  * \param argument  Optional argument to the callback function.
00195 */
00196 static uint8_t MEDNandFlash_Write(
00197     Media *media,
00198     uint32_t address,
00199     void *data,
00200     uint32_t length,
00201     MediaCallback callback,
00202     void *argument)
00203 {
00204     uint16_t pageDataSize =
00205         NandFlashModel_GetPageDataSize(MODEL(media->interface));
00206     uint16_t blockSize =
00207         NandFlashModel_GetBlockSizeInPages(MODEL(media->interface));
00208     uint16_t block, page, offset;
00209     uint32_t writeSize;
00210     uint8_t *buffer = (uint8_t *) data;
00211     uint32_t remainingLength;
00212     uint8_t status;
00213 
00214     TRACE_INFO("MEDNandFlash_Write(0x%08X, %d)\n\r", address, (int)length);
00215 
00216     // Translate access
00217     if (NandFlashModel_TranslateAccess(MODEL(media->interface),
00218                                        address,
00219                                        length,
00220                                        &block,
00221                                        &page,
00222                                        &offset)) {
00223 
00224         TRACE_ERROR("MEDNandFlash_Write: Could not start write.\n\r");
00225         return MED_STATUS_ERROR;
00226     }
00227 
00228     TRACE_DEBUG("MEDNandFlash_Write(B#%d:P#%d@%d, %d)\n\r",
00229                 block, page, offset, (int)length);
00230 
00231     // Write pages
00232     remainingLength = length;
00233     status = MED_STATUS_SUCCESS;
00234 
00235     while ((status == MED_STATUS_SUCCESS) && (remainingLength > 0)) {
00236 
00237         // Write one page
00238         writeSize = min((uint32_t)(pageDataSize - offset), remainingLength);
00239 
00240         if (UnalignedWritePage(media, block, page, offset, buffer, writeSize)) {
00241 
00242             TRACE_ERROR("MEDNandFlash_Write: Failed to write page\n\r");
00243             status = MED_STATUS_ERROR;
00244         } else {
00245 
00246             // Update addresses
00247             remainingLength -= writeSize;
00248             buffer += writeSize;
00249             offset = 0;
00250             page++;
00251 
00252             if (page == blockSize) {
00253 
00254                 page = 0;
00255                 block++;
00256             }
00257         }
00258     }
00259 
00260     // Trigger callback
00261     if (callback)
00262 
00263         callback(argument, status, length - remainingLength, remainingLength);
00264 
00265     return status;
00266 
00267 }
00268 
00269 /**
00270  * \brief Reads data at an unaligned address and/or size. The address is
00271  * provided as the block & page numbers plus an offset.
00272  * Returns 0 if the data has been read; otherwise returns 1.
00273  * \param media  Pointer to a NAND Flash Media instance.
00274  * \param block  Number of the block to read.
00275  * \param page  Number of the page to read.
00276  * \param offset  Read offset.
00277  * \param buffer  Buffer for storing data.
00278  * \param size  Number of bytes to read.
00279 */
00280 static uint8_t UnalignedReadPage(
00281     Media *media,
00282     uint16_t block,
00283     uint16_t page,
00284     uint16_t offset,
00285     uint8_t *buffer,
00286     uint32_t size)
00287 {
00288     uint8_t error;
00289     uint16_t pageDataSize = NandFlashModel_GetPageDataSize(MODEL(media->interface));
00290     uint8_t medChange = (media != currentMedia);
00291 
00292     TRACE_DEBUG("UnalignedReadPage(B%d:P%d@%d, %d)\n\r",
00293                 (int)block, (int)page, (int)offset, (int)size);
00294 
00295     // Check that one page is read at most
00296     if ((size + offset) > pageDataSize) {
00297         TRACE_ERROR("UnalignedReadPage: Read size & offset exceed page data size\n\r");
00298         return 1;
00299     }
00300 
00301     // Check if this is not the current read page
00302     if ((block != currentReadBlock) || (page != currentReadPage) || (medChange)) {
00303         if (medChange) FlushCurrentPage(currentMedia);
00304 
00305         TRACE_DEBUG("Current read page: B#%d:P#%d\n\r", block, page);
00306         currentMedia = media;
00307         currentReadBlock = block;
00308         currentReadPage = page;
00309 
00310         // Check if this is the current write page
00311         if ((currentReadBlock == currentWriteBlock)
00312             && (currentReadPage == currentWritePage)
00313             && (!medChange)) {
00314 
00315             TRACE_DEBUG("Reading current write page\n\r");
00316             memcpy(pageReadBuffer, pageWriteBuffer, NandCommon_MAXPAGEDATASIZE);
00317         } else {
00318 
00319             // Read whole page into a temporary buffer
00320             error = TranslatedNandFlash_ReadPage(TRANSLATED(media->interface),
00321                                                  block,
00322                                                  page,
00323                                                  pageReadBuffer,
00324                                                  0);
00325 
00326             if (error) {
00327 
00328                 TRACE_ERROR("UnalignedRead: Could not read page\n\r");
00329                 return 1;
00330             }
00331         }
00332     }
00333 
00334     // Copy data into buffer
00335     memcpy(buffer, &(pageReadBuffer[offset]), size);
00336 
00337     return 0;
00338 }
00339 
00340 /**
00341  * \brief Reads data at the specified address of a NandFlash media. An optional
00342  * callback is invoked when the transfer completes.
00343  * Returns 1 if the transfer has been started; otherwise returns 0.
00344  * \param media  Pointer to the NandFlash Media to read.
00345  * \param address  Address at which the data shall be read.
00346  * \param data  Data buffer.
00347  * \param length  Number of bytes to read.
00348  * \param callback  Optional callback function.
00349  * \param argument  Optional argument to the callback function.
00350 */
00351 static uint8_t MEDNandFlash_Read(
00352     Media *media,
00353     uint32_t address,
00354     void *data,
00355     uint32_t length,
00356     MediaCallback callback,
00357     void *argument)
00358 {
00359     uint16_t block, page, offset;
00360     uint16_t pageDataSize = NandFlashModel_GetPageDataSize(MODEL(media->interface));
00361     uint16_t blockSizeInPages = NandFlashModel_GetBlockSizeInPages(MODEL(
00362                                     media->interface));
00363     uint32_t remainingLength;
00364     uint32_t readSize;
00365     uint8_t *buffer = (uint8_t *) data;
00366     uint8_t status;
00367 
00368     TRACE_INFO("MEDNandFlash_Read(0x%08X, %d)\n\r", address, (int)length);
00369 
00370     // Translate access into block, page and offset
00371     if (NandFlashModel_TranslateAccess(MODEL(media->interface),
00372                                        address,
00373                                        length,
00374                                        &block,
00375                                        &page,
00376                                        &offset)) {
00377 
00378         TRACE_ERROR("MEDNandFlash_Read: Cannot perform access\n\r");
00379         return MED_STATUS_ERROR;
00380     }
00381 
00382     // Read
00383     remainingLength = length;
00384     status = MED_STATUS_SUCCESS;
00385 
00386     while ((status == MED_STATUS_SUCCESS) && (remainingLength > 0)) {
00387 
00388         // Read page
00389         readSize = min((uint32_t)(pageDataSize - offset), remainingLength);
00390 
00391         if (UnalignedReadPage(media, block, page, offset, buffer, readSize)) {
00392 
00393             TRACE_ERROR("MEDNandFlash_Read: Could not read page\n\r");
00394             status = MED_STATUS_ERROR;
00395         } else {
00396             // Update values
00397             remainingLength -= readSize;
00398             buffer += readSize;
00399             offset = 0;
00400             page++;
00401 
00402             if (page == blockSizeInPages) {
00403                 page = 0;
00404                 block++;
00405             }
00406         }
00407     }
00408 
00409     // Trigger callback
00410     if (callback)
00411         callback(argument, status, length - remainingLength, remainingLength);
00412 
00413     return status;
00414 }
00415 
00416 /**
00417  * \brief Carries out all pending operations. Returns MED_STATUS_SUCCESS if
00418  * successful; otherwise, returns MED_STATUS_ERROR.
00419  * \param media  Pointer to a NandFlash Media instance.
00420 */
00421 static uint8_t MEDNandFlash_Flush(Media *media)
00422 {
00423     TRACE_INFO("MEDNandFlash_Flush()\n\r");
00424 
00425     if (FlushCurrentPage(media)) {
00426 
00427         TRACE_ERROR("MEDNandFlash_Flush: Could not flush current page\n\r");
00428         return MED_STATUS_ERROR;
00429     }
00430 
00431     if (TranslatedNandFlash_Flush(TRANSLATED(media->interface))) {
00432 
00433         TRACE_ERROR("MEDNandFlash_Flush: Could not flush translated NAND\n\r");
00434         return MED_STATUS_ERROR;
00435     }
00436 
00437     if (TranslatedNandFlash_SaveLogicalMapping(TRANSLATED(media->interface))) {
00438 
00439         TRACE_ERROR("MEDNandFlash_Flush: Could not save the logical mapping\n\r");
00440         return MED_STATUS_ERROR;
00441     }
00442 
00443     return MED_STATUS_SUCCESS;
00444 }
00445 
00446 /**
00447  * \brief Interrupt handler for the NAND flash media. Triggered when the flush
00448  * timer expires, initiating a MEDNandFlash_Flush().
00449  * \param media  Pointer to a NAND flash Media instance.
00450 */
00451 static void MEDNandFlash_InterruptHandler(Media *media)
00452 {
00453     TRACE_DEBUG("Flush timer expired\n\r");
00454     MEDNandFlash_Flush(media);
00455 
00456     // Acknowledge interrupt
00457     //dummy = AT91C_BASE_NANDFLUSHTIMER->TC_SR;
00458 }
00459 
00460 /*------------------------------------------------------------------------------
00461  *         Exported functions
00462  *----------------------------------------------------------------------------*/
00463 /**
00464  * \brief Initializes a media instance to operate on the given NandFlash device.
00465  * \param media  Pointer to a Media instance.
00466  * \param tnf  Pointer to the TranslatedNandFlash to use.
00467 */
00468 void MEDNandFlash_Initialize(sMedia *pMedia,
00469                               struct TranslatedNandFlash *translated)
00470 {
00471     TRACE_INFO("MEDNandFlash_Initialize()\n\r");
00472 
00473     pMedia->write = (Media_write)MEDNandFlash_Write;
00474     pMedia->read = (Media_read)MEDNandFlash_Read;
00475     pMedia->lock = 0;
00476     pMedia->unlock = 0;
00477     pMedia->flush = MEDNandFlash_Flush;
00478     pMedia->handler = MEDNandFlash_InterruptHandler;
00479 
00480     pMedia->interface = translated;
00481 
00482     pMedia->baseAddress = 0;
00483     pMedia->blockSize   = 1;
00484     pMedia->size = TranslatedNandFlash_GetDeviceSizeInBytes(translated);
00485 
00486     TRACE_INFO("NF Size: %d\n\r", (int)pMedia->size);
00487 
00488     pMedia->mappedRD  = 0;
00489     pMedia->mappedWR  = 0;
00490     pMedia->protected = 0;
00491     pMedia->removable = 0;
00492     pMedia->state = MED_STATE_READY;
00493 
00494     currentMedia = NULL;
00495     currentWriteBlock = -1;
00496     currentWritePage = -1;
00497     currentReadBlock = -1;
00498     currentReadPage = -1;
00499 
00500 }
00501 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines