/* ---------------------------------------------------------------------------- | |
* ATMEL Microcontroller Software Support | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2010, 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. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** | |
* \file | |
* | |
* Implementation of Nand flash model related functions. | |
*/ | |
/*---------------------------------------------------------------------------- | |
* Headers | |
*----------------------------------------------------------------------------*/ | |
#include "memories.h" | |
#include <string.h> | |
/*---------------------------------------------------------------------------- | |
* Local funtion | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Get the power of input, given a certain result, i.e. input^(power) = result. | |
* | |
* \param result a certain output we want to calculate. | |
* \param input the input of the power. | |
* \return the value of "power" if succesfully find the power. | |
*/ | |
#if defined(OP_BOOTSTRAP_on) | |
uint32_t CALPOW(uint32_t result, uint32_t input) | |
{ | |
uint32_t i=0; | |
while(i<32) | |
{ | |
if(result == (input << i)) | |
return i; | |
i++; | |
} | |
return 0; | |
} | |
#endif | |
/** | |
* \brief Get the interger part of input, given a certain result, i.e. return = result / input. | |
* | |
* \param result a certain output we want to calculate. | |
* \param input the input of the division. | |
* \return the value of interger part of the result/input. | |
*/ | |
#if defined(OP_BOOTSTRAP_on) | |
uint32_t CALINT(uint32_t result, uint32_t input) | |
{ | |
uint32_t i=0; | |
uint32_t tmpInput=0; | |
while(1) | |
{ | |
tmpInput +=input; | |
i++; | |
if(tmpInput == result) | |
return i; | |
else if (tmpInput > result) | |
return (i-1); | |
} | |
} | |
#endif | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief 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. | |
* | |
* \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. | |
* \return 0 if a matching model has been found; otherwise returns NandCommon_ERROR_UNKNOWNMODEL. | |
*/ | |
uint8_t NandFlashModel_Find( | |
const struct NandFlashModel *modelList, | |
uint32_t size, | |
uint32_t chipId, | |
struct NandFlashModel *model) | |
{ | |
uint8_t found = 0, id2, id4; | |
uint32_t i; | |
#if defined(CHIP_NAND_CTRL) | |
uint8_t pageSize = 0; | |
#endif | |
id2 = (uint8_t)(chipId>>8); | |
id4 = (uint8_t)(chipId>>24); | |
TRACE_INFO("Nandflash ID is 0x%08X\n\r", chipId); | |
for(i=0; i<size; i++) { | |
if(modelList[i].deviceId == id2) { | |
found = 1; | |
if(model) { | |
memcpy(model, &modelList[i], sizeof(struct NandFlashModel)); | |
if(model->blockSizeInKBytes == 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 512: pageSize = SMC_CFG_PAGESIZE_PS512_16; break; | |
case 1024: pageSize = SMC_CFG_PAGESIZE_PS1024_32; break; | |
case 2048: pageSize = SMC_CFG_PAGESIZE_PS2048_64; break; | |
case 4096: pageSize = SMC_CFG_PAGESIZE_PS4096_128; break; | |
default: TRACE_ERROR("Unsupportted page size for NAND Flash Controller\n\r"); | |
} | |
SMC_NFC_Configure(SMC, pageSize | SMC_CFG_DTOMUL_X1048576 | SMC_CFG_EDGECTRL | | |
SMC_CFG_DTOCYC(0xF) | SMC_CFG_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; | |
} | |
} | |
/** | |
* \brief Translates address/size access of a NandFlashModel to block, page and offset values. | |
* | |
* \param modelList List of NandFlashModel instances. | |
* \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. | |
* \return 0 if the access is correct; otherwise returns NandCommon_ERROR_OUTOFBOUNDS. | |
* \note The values are stored in the provided variables if their pointer is not 0. | |
*/ | |
uint8_t NandFlashModel_TranslateAccess( | |
const struct NandFlashModel *model, | |
uint32_t address, | |
uint32_t size, | |
uint16_t *block, | |
uint16_t *page, | |
uint16_t *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 */ | |
uint32_t blockSize = NandFlashModel_GetBlockSizeInBytes(model); | |
uint32_t pageSize = NandFlashModel_GetPageDataSize(model); | |
/* Translate address*/ | |
#if !defined(OP_BOOTSTRAP_on) | |
uint16_t tmpBlock = address / blockSize; | |
address -= tmpBlock * blockSize; | |
uint16_t tmpPage = address / pageSize; | |
address -= tmpPage * pageSize; | |
uint16_t tmpOffset = address; | |
#else | |
uint16_t tmpBlock = CALINT(address, blockSize); | |
address -= tmpBlock * blockSize; | |
uint16_t tmpPage = CALINT(address, pageSize); | |
address -= tmpPage * pageSize; | |
uint16_t tmpOffset= address; | |
#endif | |
/* Save results */ | |
if (block) { | |
*block = tmpBlock; | |
} | |
if (page) { | |
*page = tmpPage; | |
} | |
if (offset) { | |
*offset = tmpOffset; | |
} | |
return 0; | |
} | |
/** | |
* \brief 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; | |
} | |
/** | |
* \brief Returns the device ID of a particular NandFlash model. | |
* | |
* \param model Pointer to a NandFlashModel instance. | |
*/ | |
uint8_t NandFlashModel_GetDeviceId( | |
const struct NandFlashModel *model) | |
{ | |
return model->deviceId; | |
} | |
/** | |
* \brief Returns the number of blocks in the entire device. | |
* | |
* \param model Pointer to a NandFlashModel instance. | |
*/ | |
uint16_t NandFlashModel_GetDeviceSizeInBlocks( | |
const struct NandFlashModel *model) | |
{ | |
#if !defined(OP_BOOTSTRAP_on) | |
return ((1024) / model->blockSizeInKBytes) * model->deviceSizeInMegaBytes; | |
#else | |
uint32_t pow; | |
pow = CALPOW((1024 * model->deviceSizeInMegaBytes), model->blockSizeInKBytes); | |
return (0x1 << pow); | |
#endif | |
} | |
/** | |
* \brief Returns the number of pages in the entire device. | |
* | |
* \param model Pointer to a NandFlashModel instance. | |
*/ | |
uint32_t NandFlashModel_GetDeviceSizeInPages( | |
const struct NandFlashModel *model) | |
{ | |
return (uint32_t) NandFlashModel_GetDeviceSizeInBlocks(model) //* 8 // HACK | |
* NandFlashModel_GetBlockSizeInPages(model); | |
} | |
/** | |
* \brief 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; | |
} | |
/** | |
* \brief 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. | |
*/ | |
uint32_t NandFlashModel_GetDeviceSizeInMBytes( | |
const struct NandFlashModel *model) | |
{ | |
return ((uint32_t) model->deviceSizeInMegaBytes); | |
} | |
/** | |
* \brief Returns the number of pages in one single block of a device. | |
* | |
* \param model Pointer to a NandFlashModel instance. | |
*/ | |
uint16_t NandFlashModel_GetBlockSizeInPages( | |
const struct NandFlashModel *model) | |
{ | |
#if !defined(OP_BOOTSTRAP_on) | |
return model->blockSizeInKBytes * 1024 / model->pageSizeInBytes; | |
#else | |
uint32_t pow; | |
pow = CALPOW((model->blockSizeInKBytes * 1024), model->pageSizeInBytes); | |
return (0x1 << pow); | |
#endif | |
} | |
/** | |
* \brief 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. | |
*/ | |
uint32_t NandFlashModel_GetBlockSizeInBytes( | |
const struct NandFlashModel *model) | |
{ | |
return (model->blockSizeInKBytes *1024); | |
} | |
/** | |
* \brief Returns the size of the data area of a page in bytes. | |
* take into account the spare zones size. | |
* | |
* \param model Pointer to a NandFlashModel instance. | |
*/ | |
uint16_t NandFlashModel_GetPageDataSize( | |
const struct NandFlashModel *model) | |
{ | |
return model->pageSizeInBytes; | |
} | |
/** | |
* \brief Returns the size of the spare area of a page in bytes. | |
* | |
* \param model Pointer to a NandFlashModel instance. | |
*/ | |
uint8_t NandFlashModel_GetPageSpareSize( | |
const struct NandFlashModel *model) | |
{ | |
return (model->pageSizeInBytes>>5); /* Spare size is 16/512 of data size */ | |
} | |
/** | |
* \brief Returns the number of bits used by the data bus of a NandFlash device. | |
* | |
* \param model Pointer to a NandFlashModel instance. | |
*/ | |
uint8_t NandFlashModel_GetDataBusWidth( | |
const struct NandFlashModel *model) | |
{ | |
return (model->options&NandFlashModel_DATABUS16)? 16: 8; | |
} | |
/** | |
* \brief Check if the given NandFlash model uses the "small blocks/pages" | |
* | |
* \param model Pointer to a NandFlashModel instance. | |
* \return 1 if the model uses the "small blocks/pages"; otherwise return 0. | |
*/ | |
uint8_t NandFlashModel_HasSmallBlocks( | |
const struct NandFlashModel *model) | |
{ | |
return (model->pageSizeInBytes <= 512 )? 1: 0; | |
} | |
/** | |
* \brief Check if if the device supports the copy-back operation. | |
* | |
* \param model Pointer to a NandFlashModel instance. | |
* \return 1 if the model supports the copy-back operation; otherwise return 0. | |
*/ | |
uint8_t NandFlashModel_SupportsCopyBack( | |
const struct NandFlashModel *model) | |
{ | |
return ((model->options & NandFlashModel_COPYBACK) != 0); | |
} |