/* ---------------------------------------------------------------------------- * 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 "NandFlashModel.h" #include "NandCommon.h" #if defined(CHIP_NAND_CTRL) #include #endif #include #include //------------------------------------------------------------------------------ // Internal definitions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// Get the power of input, given a certain result, i.e. input^(power) = result. /// returns the value of "power" if succesfully find the power. /// \param result a certain output we want to calculate. /// \param input the input of the power. //------------------------------------------------------------------------------ #if defined(OP_BOOTSTRAP_on) unsigned int CALPOW(unsigned int result, unsigned int input) { unsigned int i=0; while(i<32) { if(result == (input << i)) return i; i++; } return 0; } #endif //------------------------------------------------------------------------------ /// Get the interger part of input, given a certain result, i.e. return = result / input. /// returns the value of interger part of the result/input. /// \param result a certain output we want to calculate. /// \param input the input of the division. //------------------------------------------------------------------------------ #if defined(OP_BOOTSTRAP_on) unsigned int CALINT(unsigned int result, unsigned int input) { unsigned int i=0; unsigned int tmpInput=0; while(1) { tmpInput +=input; i++; if(tmpInput == result) return i; else if (tmpInput > result) return (i-1); } } #endif //------------------------------------------------------------------------------ // Exported functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// Looks for a NandFlashModel corresponding to the given ID inside a list of /// model. If found, the model variable is filled with the correct values. /// This function returns 0 if a matching model has been found; otherwise it /// returns NandCommon_ERROR_UNKNOWNMODEL. /// \param modelList List of NandFlashModel instances. /// \param size Number of models in list. /// \param chipId Identifier returned by the Nand(id1|(id2<<8)|(id3<<16)|(id4<<24)). /// \param model NandFlashModel instance to update with the model parameters. //------------------------------------------------------------------------------ unsigned char NandFlashModel_Find( const struct NandFlashModel *modelList, unsigned int size, unsigned int chipId, struct NandFlashModel *model) { unsigned char found = 0, id2, id4; unsigned int i; #if defined(CHIP_NAND_CTRL) unsigned char pageSize = 0; #endif id2 = (unsigned char)(chipId>>8); id4 = (unsigned char)(chipId>>24); TRACE_INFO("Nandflash ID is 0x%08X\n\r", chipId); for(i=0; iblockSizeInKBytes == 0 || model->pageSizeInBytes == 0) { TRACE_DEBUG("Fetch from ID4(0x%.2x):\r\n", id4); /// Fetch from the extended ID4 /// ID4 D5 D4 BlockSize || D1 D0 PageSize /// 0 0 64K || 0 0 1K /// 0 1 128K || 0 1 2K /// 1 0 256K || 1 0 4K /// 1 1 512K || 1 1 8k #if !defined(OP_BOOTSTRAP_on) switch(id4 & 0x03) { case 0x00: model->pageSizeInBytes = 1024; break; case 0x01: model->pageSizeInBytes = 2048; break; case 0x02: model->pageSizeInBytes = 4096; break; case 0x03: model->pageSizeInBytes = 8192; break; } switch(id4 & 0x30) { case 0x00: model->blockSizeInKBytes = 64; break; case 0x10: model->blockSizeInKBytes = 128; break; case 0x20: model->blockSizeInKBytes = 256; break; case 0x30: model->blockSizeInKBytes = 512; break; } #else model->pageSizeInBytes = 1024 << (id4 & 0x03); model->blockSizeInKBytes = (64) << ((id4 & 0x30) >>4); #endif } #if defined(CHIP_NAND_CTRL) switch(model->pageSizeInBytes) { case 1024: pageSize = AT91C_HSMC4_PAGESIZE_1056_Bytes; break; case 2048: pageSize = AT91C_HSMC4_PAGESIZE_2112_Bytes; break; case 4096: pageSize = AT91C_HSMC4_PAGESIZE_4224_Bytes; break; default: TRACE_ERROR("Unsupportted page size for NAND Flash Controller\n\r"); } HSMC4_SetMode(pageSize | AT91C_HSMC4_DTOMUL_1048576 | AT91C_HSMC4_EDGECTRL |AT91C_HSMC4_DTOCYC | AT91C_HSMC4_RSPARE); #endif } TRACE_DEBUG("NAND Model found:\r\n"); TRACE_DEBUG(" * deviceId = 0x%02X\r\n", model->deviceId); TRACE_DEBUG(" * deviceSizeInMegaBytes = %d\r\n", model->deviceSizeInMegaBytes); TRACE_DEBUG(" * blockSizeInkBytes = %d\r\n", model->blockSizeInKBytes); TRACE_DEBUG(" * pageSizeInBytes = %d\r\n", model->pageSizeInBytes); TRACE_DEBUG(" * options = 0x%02X\r\n", model->options); break; } } // Check if chip has been detected if (found) { return 0; } else { return NandCommon_ERROR_UNKNOWNMODEL; } } //------------------------------------------------------------------------------ /// Translates address/size access of a NandFlashModel to block, page and /// offset values. The values are stored in the provided variables if their /// pointer is not 0. /// Returns 0 if the access is correct; otherwise returns /// NandCommon_ERROR_OUTOFBOUNDS. /// \param model Pointer to a NandFlashModel instance. /// \param address Access address. /// \param size Access size in bytes. /// \param block Stores the first accessed block number. /// \param page Stores the first accessed page number inside the first block. /// \param offset Stores the byte offset inside the first accessed page. //------------------------------------------------------------------------------ unsigned char NandFlashModel_TranslateAccess( const struct NandFlashModel *model, unsigned int address, unsigned int size, unsigned short *block, unsigned short *page, unsigned short *offset) { // Check that access is not too big #if !defined(OP_BOOTSTRAP_on) if ((address + size) > NandFlashModel_GetDeviceSizeInBytes(model)) { TRACE_DEBUG("NandFlashModel_TranslateAccess: out-of-bounds access.\n\r"); return NandCommon_ERROR_OUTOFBOUNDS; } #endif // Get Nand info unsigned int blockSize = NandFlashModel_GetBlockSizeInBytes(model); unsigned int pageSize = NandFlashModel_GetPageDataSize(model); // Translate address #if !defined(OP_BOOTSTRAP_on) unsigned short tmpBlock = address / blockSize; address -= tmpBlock * blockSize; unsigned short tmpPage = address / pageSize; address -= tmpPage * pageSize; unsigned short tmpOffset = address; #else unsigned short tmpBlock = CALINT(address, blockSize); address -= tmpBlock * blockSize; unsigned short tmpPage = CALINT(address, pageSize); address -= tmpPage * pageSize; unsigned short tmpOffset= address; #endif // Save results if (block) { *block = tmpBlock; } if (page) { *page = tmpPage; } if (offset) { *offset = tmpOffset; } return 0; } //------------------------------------------------------------------------------ /// Returns the spare area placement scheme used by a particular nandflash /// model. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ const struct NandSpareScheme * NandFlashModel_GetScheme( const struct NandFlashModel *model) { return model->scheme; } //------------------------------------------------------------------------------ /// Returns the device ID of a particular NandFlash model. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned char NandFlashModel_GetDeviceId( const struct NandFlashModel *model) { return model->deviceId; } //------------------------------------------------------------------------------ /// Returns the number of blocks in the entire device. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned short NandFlashModel_GetDeviceSizeInBlocks( const struct NandFlashModel *model) { #if !defined(OP_BOOTSTRAP_on) return ((1024) / model->blockSizeInKBytes) * model->deviceSizeInMegaBytes; #else unsigned int pow; pow = CALPOW((1024 * model->deviceSizeInMegaBytes), model->blockSizeInKBytes); return (0x1 << pow); #endif } //------------------------------------------------------------------------------ /// Returns the number of pages in the entire device. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned int NandFlashModel_GetDeviceSizeInPages( const struct NandFlashModel *model) { return (unsigned int) NandFlashModel_GetDeviceSizeInBlocks(model) //* 8 // HACK * NandFlashModel_GetBlockSizeInPages(model); } //------------------------------------------------------------------------------ /// Returns the size of the whole device in bytes (this does not include the /// size of the spare zones). /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned long long NandFlashModel_GetDeviceSizeInBytes( const struct NandFlashModel *model) { return ((unsigned long long) model->deviceSizeInMegaBytes) << 20; } //------------------------------------------------------------------------------ /// Returns the size of the whole device in Mega bytes (this does not include the /// size of the spare zones). /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned int NandFlashModel_GetDeviceSizeInMBytes( const struct NandFlashModel *model) { return ((unsigned int) model->deviceSizeInMegaBytes); } //------------------------------------------------------------------------------ /// Returns the number of pages in one single block of a device. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned short NandFlashModel_GetBlockSizeInPages( const struct NandFlashModel *model) { #if !defined(OP_BOOTSTRAP_on) return model->blockSizeInKBytes * 1024 / model->pageSizeInBytes; #else unsigned int pow; pow = CALPOW((model->blockSizeInKBytes * 1024), model->pageSizeInBytes); return (0x1 << pow); #endif } //------------------------------------------------------------------------------ /// Returns the size in bytes of one single block of a device. This does not /// take into account the spare zones size. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned int NandFlashModel_GetBlockSizeInBytes( const struct NandFlashModel *model) { return (model->blockSizeInKBytes *1024); } //------------------------------------------------------------------------------ /// Returns the size of the data area of a page in bytes. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned short NandFlashModel_GetPageDataSize( const struct NandFlashModel *model) { return model->pageSizeInBytes; } //------------------------------------------------------------------------------ /// Returns the size of the spare area of a page in bytes. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned char NandFlashModel_GetPageSpareSize( const struct NandFlashModel *model) { return (model->pageSizeInBytes>>5); /// Spare size is 16/512 of data size } //------------------------------------------------------------------------------ /// Returns the number of bits used by the data bus of a NandFlash device. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned char NandFlashModel_GetDataBusWidth( const struct NandFlashModel *model) { return (model->options&NandFlashModel_DATABUS16)? 16: 8; } //------------------------------------------------------------------------------ /// Returns 1 if the given NandFlash model uses the "small blocks/pages" /// command set; otherwise returns 0. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned char NandFlashModel_HasSmallBlocks( const struct NandFlashModel *model) { return (model->pageSizeInBytes <= 512 )? 1: 0; } //------------------------------------------------------------------------------ /// Returns 1 if the device supports the copy-back operation. Otherwise returns /// 0. /// \param model Pointer to a NandFlashModel instance. //------------------------------------------------------------------------------ unsigned char NandFlashModel_SupportsCopyBack( const struct NandFlashModel *model) { return ((model->options & NandFlashModel_COPYBACK) != 0); }