From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- memories/MEDNandFlash.c | 510 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 memories/MEDNandFlash.c (limited to 'memories/MEDNandFlash.c') diff --git a/memories/MEDNandFlash.c b/memories/MEDNandFlash.c new file mode 100644 index 0000000..300af36 --- /dev/null +++ b/memories/MEDNandFlash.c @@ -0,0 +1,510 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MEDNandFlash.h" +#include "nandflash/NandCommon.h" +#include "nandflash/TranslatedNandFlash.h" +#include "nandflash/NandFlashModel.h" +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Casts +#define MODEL(interface) ((struct NandFlashModel *) interface) +#define TRANSLATED(interface) ((struct TranslatedNandFlash *) interface) + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +static unsigned char pageWriteBuffer[NandCommon_MAXPAGEDATASIZE]; +static signed short currentWriteBlock; +static signed short currentWritePage; + +static unsigned char pageReadBuffer[NandCommon_MAXPAGEDATASIZE]; +static signed short currentReadBlock; +static signed short currentReadPage; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Writes the current page of data on the NandFlash. +/// Returns 0 if successful; otherwise returns 1. +/// \param tnf Pointer to a TranslatedNandFlash instance. +//------------------------------------------------------------------------------ +static unsigned char FlushCurrentPage(Media *media) +{ + // Make sure there is a page to flush + if (currentWritePage == -1) { + + return 0; + } + + TRACE_DEBUG("FlushCurrentPage(B#%d:P#%d)\n\r", + currentWriteBlock, currentWritePage); + + // Write page + if (TranslatedNandFlash_WritePage(TRANSLATED(media->interface), + currentWriteBlock, + currentWritePage, + pageWriteBuffer, + 0)) { + + TRACE_ERROR("FlushCurrentPage: Failed to write page.\n\r"); + return 1; + } + + // No current write page & block + currentWriteBlock = -1; + currentWritePage = -1; + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes data at an unaligned (page-wise) address and size. The address is +/// provided as the block & page number plus an offset. The data to write MUST +/// NOT span more than one page. +/// Returns 0 if the data has been written; 1 otherwise. +/// \param media Pointer to a nandflash Media instance. +/// \param block Number of the block to write. +/// \param page Number of the page to write. +/// \param offset Write offset. +/// \param buffer Data buffer. +/// \param size Number of bytes to write. +//------------------------------------------------------------------------------ +static unsigned char UnalignedWritePage( + Media *media, + unsigned short block, + unsigned short page, + unsigned short offset, + unsigned char *buffer, + unsigned int size) +{ + unsigned char error; + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(media->interface)); + unsigned char writePage = ((size + offset) == pageDataSize); + + TRACE_DEBUG("UnalignedWritePage(B%d:P%d@%d, %d)\n\r", + block, page, offset, size); + ASSERT((size + offset) <= pageDataSize, + "UnalignedWrite: Write size and offset exceed page data size\n\r"); + + // If size is 0, return immediately + if (size == 0) { + + return 0; + } + + // If this is not the current page, flush the previous one + if ((currentWriteBlock != block) || (currentWritePage != page)) { + + // Flush and make page the new current page + FlushCurrentPage(media); + TRACE_DEBUG("Current write page: B#%d:P#%d\n\r", block, page); + currentWriteBlock = block; + currentWritePage = page; + + // Read existing page data in a temporary buffer if the page is not + // entirely written + if (size != pageDataSize) { + + error = TranslatedNandFlash_ReadPage(TRANSLATED(media->interface), + block, + page, + pageWriteBuffer, + 0); + if (error) { + + TRACE_ERROR( + "UnalignedWrite: Could not read existing page data\n\r"); + return 1; + } + } + } + + // Copy data in temporary buffer + memcpy(&(pageWriteBuffer[offset]), buffer, size); + // Update read buffer if necessary + if ((currentReadPage == currentWritePage) + && (currentReadBlock == currentWriteBlock)) { + + TRACE_DEBUG("Updating current read buffer\n\r"); + memcpy(&(pageReadBuffer[offset]), buffer, size); + } + + // Flush page if it is complete + if (writePage) { + + FlushCurrentPage(media); + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes a data buffer at the specified address on a NandFlash media. An +/// optional callback can be triggered after the transfer is completed. +/// Returns MED_STATUS_SUCCESS if the transfer has been started successfully; +/// otherwise returns MED_STATUS_ERROR. +/// \param media Pointer to the NandFlash Media instance. +/// \param address Address where the data shall be written. +/// \param data Data buffer. +/// \param length Number of bytes to write. +/// \param callback Optional callback to call when the write is finished. +/// \param argument Optional argument to the callback function. +//------------------------------------------------------------------------------ +static unsigned char MEDNandFlash_Write( + Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned short pageDataSize = + NandFlashModel_GetPageDataSize(MODEL(media->interface)); + unsigned short blockSize = + NandFlashModel_GetBlockSizeInPages(MODEL(media->interface)); + unsigned short block, page, offset; + unsigned int writeSize; + unsigned char *buffer = (unsigned char *) data; + unsigned int remainingLength; + unsigned char status; + + TRACE_INFO("MEDNandFlash_Write(0x%08X, %d)\n\r", address, length); + + // Translate access + if (NandFlashModel_TranslateAccess(MODEL(media->interface), + address, + length, + &block, + &page, + &offset)) { + + TRACE_ERROR("MEDNandFlash_Write: Could not start write.\n\r"); + return MED_STATUS_ERROR; + } + + TRACE_DEBUG("MEDNandFlash_Write(B#%d:P#%d@%d, %d)\n\r", + block, page, offset, length); + + // Write pages + remainingLength = length; + status = MED_STATUS_SUCCESS; + while ((status == MED_STATUS_SUCCESS) && (remainingLength > 0)) { + + // Write one page + writeSize = min(pageDataSize-offset, remainingLength); + if (UnalignedWritePage(media, block, page, offset, buffer, writeSize)) { + + TRACE_ERROR("MEDNandFlash_Write: Failed to write page\n\r"); + status = MED_STATUS_ERROR; + } + else { + + // Update addresses + remainingLength -= writeSize; + buffer += writeSize; + offset = 0; + page++; + if (page == blockSize) { + + page = 0; + block++; + } + } + } + + // Trigger callback + if (callback) { + + callback(argument, status, length - remainingLength, remainingLength); + } + + // Reset flush timer + //AT91C_BASE_NANDFLUSHTIMER->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Reads data at an unaligned address and/or size. The address is provided as +/// the block & page numbers plus an offset. +/// Returns 0 if the data has been read; otherwise returns 1. +/// \param media Pointer to a nandflash Media instance. +/// \param block Number of the block to read. +/// \param page Number of the page to read. +/// \param offset Read offset. +/// \param buffer Buffer for storing data. +/// \param size Number of bytes to read. +//------------------------------------------------------------------------------ +static unsigned char UnalignedReadPage( + Media *media, + unsigned short block, + unsigned short page, + unsigned short offset, + unsigned char *buffer, + unsigned int size) +{ + unsigned char error; + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(media->interface)); + + TRACE_DEBUG("UnalignedReadPage(B%d:P%d@%d, %d)\n\r", block, page, offset, size); + + // Check that one page is read at most + ASSERT((size + offset) <= pageDataSize, + "UnalignedReadPage: Read size & offset exceed page data size\n\r"); + + // Check if this is not the current read page + if ((block != currentReadBlock) || (page != currentReadPage)) { + + TRACE_DEBUG("Current read page: B#%d:P#%d\n\r", block, page); + currentReadBlock = block; + currentReadPage = page; + + // Check if this is the current write page + if ((currentReadBlock == currentWriteBlock) + && (currentReadPage == currentWritePage)) { + + TRACE_DEBUG("Reading current write page\n\r"); + memcpy(pageReadBuffer, pageWriteBuffer, NandCommon_MAXPAGEDATASIZE); + } + else { + + // Read whole page into a temporary buffer + error = TranslatedNandFlash_ReadPage(TRANSLATED(media->interface), + block, + page, + pageReadBuffer, + 0); + if (error) { + + TRACE_ERROR("UnalignedRead: Could not read page\n\r"); + return 1; + } + } + } + + // Copy data into buffer + memcpy(buffer, &(pageReadBuffer[offset]), size); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Reads data at the specified address of a NandFlash media. An optional +/// callback is invoked when the transfer completes. +/// Returns 1 if the transfer has been started; otherwise returns 0. +/// \param media Pointer to the NandFlash Media to read. +/// \param address Address at which the data shall be read. +/// \param data Data buffer. +/// \param length Number of bytes to read. +/// \param callback Optional callback function. +/// \param argument Optional argument to the callback function. +//------------------------------------------------------------------------------ +static unsigned char MEDNandFlash_Read( + Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned short block, page, offset; + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(media->interface)); + unsigned short blockSizeInPages = NandFlashModel_GetBlockSizeInPages(MODEL(media->interface)); + unsigned int remainingLength; + unsigned int readSize; + unsigned char *buffer = (unsigned char *) data; + unsigned char status; + + TRACE_INFO("MEDNandFlash_Read(0x%08X, %d)\n\r", address, length); + + // Translate access into block, page and offset + if (NandFlashModel_TranslateAccess(MODEL(media->interface), + address, + length, + &block, + &page, + &offset)) { + + TRACE_ERROR("MEDNandFlash_Read: Cannot perform access\n\r"); + return MED_STATUS_ERROR; + } + + // Read + remainingLength = length; + status = MED_STATUS_SUCCESS; + while ((status == MED_STATUS_SUCCESS) && (remainingLength > 0)) { + + // Read page + readSize = min(pageDataSize-offset, remainingLength); + if (UnalignedReadPage(media, block, page, offset, buffer, readSize)) { + + TRACE_ERROR("MEDNandFlash_Read: Could not read page\n\r"); + status = MED_STATUS_ERROR; + } + else { + + // Update values + remainingLength -= readSize; + buffer += readSize; + offset = 0; + page++; + if (page == blockSizeInPages) { + + page = 0; + block++; + } + } + } + + // Trigger callback + if (callback) { + + callback(argument, status, length - remainingLength, remainingLength); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Carries out all pending operations. Returns MED_STATUS_SUCCESS if +/// succesful; otherwise, returns MED_STATUS_ERROR. +/// \param media Pointer to a NandFlash Media instance. +//------------------------------------------------------------------------------ +static unsigned char MEDNandFlash_Flush(Media *media) +{ + TRACE_INFO("MEDNandFlash_Flush()\n\r"); + + if (FlushCurrentPage(media)) { + + TRACE_ERROR("MEDNandFlash_Flush: Could not flush current page\n\r"); + return MED_STATUS_ERROR; + } + + if (TranslatedNandFlash_Flush(TRANSLATED(media->interface))) { + + TRACE_ERROR("MEDNandFlash_Flush: Could not flush translated nand\n\r"); + return MED_STATUS_ERROR; + } + + if (TranslatedNandFlash_SaveLogicalMapping(TRANSLATED(media->interface))) { + + TRACE_ERROR("MEDNandFlash_Flush: Could not save the logical mapping\n\r"); + return MED_STATUS_ERROR; + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Interrupt handler for the nandflash media. Triggered when the flush timer +/// expires, initiating a MEDNandFlash_Flush(). +/// \param media Pointer to a nandflash Media instance. +//------------------------------------------------------------------------------ +static void MEDNandFlash_InterruptHandler(Media *media) +{ + //volatile unsigned int dummy; + + TRACE_DEBUG("Flush timer expired\n\r"); + MEDNandFlash_Flush(media); + + // Acknowledge interrupt + //dummy = AT91C_BASE_NANDFLUSHTIMER->TC_SR; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a media instance to operate on the given NandFlash device. +/// \param media Pointer to a Media instance. +/// \param tnf Pointer to the TranslatedNandFlash to use. +//------------------------------------------------------------------------------ +void MEDNandFlash_Initialize(Media *media, + struct TranslatedNandFlash *translated) +{ + TRACE_INFO("MEDNandFlash_Initialize()\n\r"); + + media->write = MEDNandFlash_Write; + media->read = MEDNandFlash_Read; + media->lock = 0; + media->unlock = 0; + media->flush = MEDNandFlash_Flush; + media->handler = MEDNandFlash_InterruptHandler; + + media->interface = translated; + + media->baseAddress = 0; + media->blockSize = 1; + media->size = TranslatedNandFlash_GetDeviceSizeInBytes(translated); + + TRACE_INFO("NF Size: %d\n\r", media->size); + + media->mappedRD = 0; + media->mappedWR = 0; + media->protected = 0; + media->removable = 0; + media->state = MED_STATE_READY; + + currentWriteBlock = -1; + currentWritePage = -1; + currentReadBlock = -1; + currentReadPage = -1; + + // Configure flush timer + /* + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_NANDFLUSHTIMER); + AT91C_BASE_NANDFLUSHTIMER->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_NANDFLUSHTIMER->TC_IDR = 0xFFFFFFFF; + AT91C_BASE_NANDFLUSHTIMER->TC_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK + | AT91C_TC_CPCSTOP + | AT91C_TC_CPCDIS + | AT91C_TC_WAVESEL_UP_AUTO + | AT91C_TC_WAVE; + AT91C_BASE_NANDFLUSHTIMER->TC_RC = 0xFFFF; + AT91C_BASE_NANDFLUSHTIMER->TC_IER = AT91C_TC_CPCS; + */ +} + -- cgit v1.2.3