SAMV71 Xplained Ultra Software Package 1.3

MEDNandFlash.c

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