blob: 14b79d1263c5cfeb988184e42090d0460a25ff16 [file] [log] [blame]
/* ----------------------------------------------------------------------------
* 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);
}