
Added initial support Xilinx Embedded Software. Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
869 lines
24 KiB
C
Executable file
869 lines
24 KiB
C
Executable file
/******************************************************************************
|
|
*
|
|
* Copyright (C) 2009 - 2014 Xilinx, Inc. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* Use of the Software is limited solely to applications:
|
|
* (a) running on a Xilinx device, or
|
|
* (b) that interact with a Xilinx device through a bus or interconnect.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* Except as contained in this notice, the name of the Xilinx shall not be used
|
|
* in advertising or otherwise to promote the sale, use or other dealings in
|
|
* this Software without prior written authorization from Xilinx.
|
|
*
|
|
******************************************************************************/
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* @file xnandps_bbm.c
|
|
* This file implements the Bad Block Management (BBM) functionality.
|
|
* See xnandps_bbm.h for more details.
|
|
*
|
|
* @note None
|
|
*
|
|
* <pre>
|
|
* MODIFICATION HISTORY:
|
|
*
|
|
* Ver Who Date Changes
|
|
* ----- ---- ---------- -----------------------------------------------
|
|
* 1.00a nm 12/10/2010 First release
|
|
* 1.03a nm 10/22/2012 Fixed CR# 683787.
|
|
* </pre>
|
|
*
|
|
******************************************************************************/
|
|
|
|
/***************************** Include Files *********************************/
|
|
#include <string.h> /**< For memcpy and memset */
|
|
#include "xil_types.h"
|
|
#include "xnandps_bbm.h"
|
|
|
|
/************************** Constant Definitions *****************************/
|
|
|
|
/**************************** Type Definitions *******************************/
|
|
|
|
/***************** Macros (Inline Functions) Definitions *********************/
|
|
|
|
/************************** Function Prototypes ******************************/
|
|
static int XNandPs_ReadBbt(XNandPs *InstancePtr);
|
|
|
|
static int XNandPs_SearchBbt(XNandPs *InstancePtr, XNandPs_BbtDesc *Desc);
|
|
|
|
static void XNandPs_CreateBbt(XNandPs *InstancePtr);
|
|
|
|
static void XNandPs_ConvertBbt(XNandPs *InstancePtr, u8 *Buf);
|
|
|
|
static int XNandPs_WriteBbt(XNandPs *InstancePtr, XNandPs_BbtDesc *Desc,
|
|
XNandPs_BbtDesc *MirrorDesc);
|
|
|
|
static int XNandPs_MarkBbt(XNandPs* InstancePtr, XNandPs_BbtDesc *Desc);
|
|
|
|
static int XNandPs_UpdateBbt(XNandPs *InstancePtr);
|
|
|
|
extern int XNandPs_ReadSpareBytes(XNandPs *InstancePtr, u32 Page, u8 *Buf);
|
|
/************************** Variable Definitions *****************************/
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function initializes the Bad Block Table(BBT) descriptors with a
|
|
* predefined pattern for searching Bad Block Table(BBT) in flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPs instance.
|
|
*
|
|
* @return
|
|
* - NONE
|
|
*
|
|
******************************************************************************/
|
|
void XNandPs_InitBbtDesc(XNandPs *InstancePtr)
|
|
{
|
|
int Index;
|
|
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
/*
|
|
* Initialize primary Bad Block Table(BBT)
|
|
*/
|
|
InstancePtr->BbtDesc.PageOffset = XNANDPS_BBT_DESC_PAGE_OFFSET;
|
|
if (InstancePtr->EccMode == XNANDPS_ECC_ONDIE) {
|
|
InstancePtr->BbtDesc.SigOffset = 0x4;
|
|
InstancePtr->BbtDesc.VerOffset = 0x14;
|
|
} else {
|
|
InstancePtr->BbtDesc.SigOffset = XNANDPS_BBT_DESC_SIG_OFFSET;
|
|
InstancePtr->BbtDesc.VerOffset = XNANDPS_BBT_DESC_VER_OFFSET;
|
|
}
|
|
InstancePtr->BbtDesc.SigLength = XNANDPS_BBT_DESC_SIG_LEN;
|
|
InstancePtr->BbtDesc.MaxBlocks = XNANDPS_BBT_DESC_MAX_BLOCKS;
|
|
strcpy(&InstancePtr->BbtDesc.Signature[0], "Bbt0");
|
|
InstancePtr->BbtDesc.Version = 0;
|
|
InstancePtr->BbtDesc.Valid = 0;
|
|
|
|
/*
|
|
* Initialize mirror Bad Block Table(BBT)
|
|
*/
|
|
InstancePtr->BbtMirrorDesc.PageOffset = XNANDPS_BBT_DESC_PAGE_OFFSET;
|
|
if (InstancePtr->EccMode == XNANDPS_ECC_ONDIE) {
|
|
InstancePtr->BbtMirrorDesc.SigOffset = 0x4;
|
|
InstancePtr->BbtMirrorDesc.VerOffset = 0x14;
|
|
} else {
|
|
InstancePtr->BbtMirrorDesc.SigOffset = XNANDPS_BBT_DESC_SIG_OFFSET;
|
|
InstancePtr->BbtMirrorDesc.VerOffset = XNANDPS_BBT_DESC_VER_OFFSET;
|
|
}
|
|
InstancePtr->BbtMirrorDesc.SigLength = XNANDPS_BBT_DESC_SIG_LEN;
|
|
InstancePtr->BbtMirrorDesc.MaxBlocks = XNANDPS_BBT_DESC_MAX_BLOCKS;
|
|
strcpy(&InstancePtr->BbtMirrorDesc.Signature[0], "1tbB");
|
|
InstancePtr->BbtMirrorDesc.Version = 0;
|
|
InstancePtr->BbtMirrorDesc.Valid = 0;
|
|
|
|
/*
|
|
* Initialize Bad block search pattern structure
|
|
*/
|
|
if (InstancePtr->Geometry.BytesPerPage > 512) {
|
|
/* For flash page size > 512 bytes */
|
|
InstancePtr->BbPattern.Options = XNANDPS_BBT_SCAN_2ND_PAGE;
|
|
InstancePtr->BbPattern.Offset =
|
|
XNANDPS_BB_PATTERN_OFFSET_LARGE_PAGE;
|
|
InstancePtr->BbPattern.Length =
|
|
XNANDPS_BB_PATTERN_LENGTH_LARGE_PAGE;
|
|
} else {
|
|
InstancePtr->BbPattern.Options = XNANDPS_BBT_SCAN_2ND_PAGE;
|
|
InstancePtr->BbPattern.Offset =
|
|
XNANDPS_BB_PATTERN_OFFSET_SMALL_PAGE;
|
|
InstancePtr->BbPattern.Length =
|
|
XNANDPS_BB_PATTERN_LENGTH_SMALL_PAGE;
|
|
}
|
|
for(Index=0; Index < XNANDPS_BB_PATTERN_LENGTH_LARGE_PAGE; Index++) {
|
|
InstancePtr->BbPattern.Pattern[Index] = XNANDPS_BB_PATTERN;
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function scans the NAND flash for factory marked bad blocks and creates
|
|
* a RAM based Bad Block Table(BBT).
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPs instance.
|
|
*
|
|
* @return
|
|
* - NONE
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPs_CreateBbt(XNandPs *InstancePtr)
|
|
{
|
|
u32 BlockIndex;
|
|
u32 PageIndex;
|
|
u32 Length;
|
|
u32 BlockOffset;
|
|
u32 BlockShift;
|
|
u32 NumPages;
|
|
u32 Page;
|
|
u8 Buf[XNANDPS_MAX_SPARE_SIZE];
|
|
u32 BbtLen = InstancePtr->Geometry.NumBlocks >>
|
|
XNANDPS_BBT_BLOCK_SHIFT;
|
|
int Status;
|
|
|
|
/*
|
|
* Number of pages to search for bad block pattern
|
|
*/
|
|
if (InstancePtr->BbPattern.Options & XNANDPS_BBT_SCAN_2ND_PAGE) {
|
|
NumPages = 2;
|
|
} else {
|
|
NumPages = 1;
|
|
}
|
|
|
|
/*
|
|
* Zero the RAM based Bad Block Table(BBT) entries
|
|
*/
|
|
memset(&InstancePtr->Bbt[0], 0, BbtLen);
|
|
|
|
/*
|
|
* Scan all the blocks for factory marked bad blocks
|
|
*/
|
|
for(BlockIndex = 0; BlockIndex <
|
|
InstancePtr->Geometry.NumBlocks; BlockIndex++) {
|
|
/*
|
|
* Block offset in Bad Block Table(BBT) entry
|
|
*/
|
|
BlockOffset = BlockIndex >> XNANDPS_BBT_BLOCK_SHIFT;
|
|
/*
|
|
* Block shift value in the byte
|
|
*/
|
|
BlockShift = XNandPs_BbtBlockShift(BlockIndex);
|
|
Page = BlockIndex * InstancePtr->Geometry.PagesPerBlock;
|
|
|
|
/*
|
|
* Search for the bad block pattern
|
|
*/
|
|
for(PageIndex = 0; PageIndex < NumPages; PageIndex++) {
|
|
Status = XNandPs_ReadSpareBytes(InstancePtr,
|
|
(Page + PageIndex), &Buf[0]);
|
|
|
|
if (Status != XST_SUCCESS) {
|
|
/* Marking as bad block */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
(XNANDPS_BLOCK_FACTORY_BAD <<
|
|
BlockShift);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Read the spare bytes to check for bad block
|
|
* pattern
|
|
*/
|
|
for(Length = 0; Length <
|
|
InstancePtr->BbPattern.Length; Length++) {
|
|
if (Buf[InstancePtr->BbPattern.Offset + Length]
|
|
!=
|
|
InstancePtr->BbPattern.Pattern[Length])
|
|
{
|
|
/* Bad block found */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
(XNANDPS_BLOCK_FACTORY_BAD <<
|
|
BlockShift);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function reads the Bad Block Table(BBT) if present in flash. If not it
|
|
* scans the flash for detecting factory marked bad blocks and creates a bad
|
|
* block table and write the Bad Block Table(BBT) into the flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPs instance.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
int XNandPs_ScanBbt(XNandPs *InstancePtr)
|
|
{
|
|
int Status;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
if (XNandPs_ReadBbt(InstancePtr) != XST_SUCCESS) {
|
|
/*
|
|
* Create memory based Bad Block Table(BBT)
|
|
*/
|
|
XNandPs_CreateBbt(InstancePtr);
|
|
|
|
/*
|
|
* Write the Bad Block Table(BBT) to the flash
|
|
*/
|
|
Status = XNandPs_WriteBbt(InstancePtr,
|
|
&InstancePtr->BbtDesc,
|
|
&InstancePtr->BbtMirrorDesc);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* Write the Mirror Bad Block Table(BBT) to the flash
|
|
*/
|
|
Status = XNandPs_WriteBbt(InstancePtr,
|
|
&InstancePtr->BbtMirrorDesc,
|
|
&InstancePtr->BbtDesc);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* Mark the blocks containing Bad Block Table(BBT) as Reserved
|
|
*/
|
|
XNandPs_MarkBbt(InstancePtr, &InstancePtr->BbtDesc);
|
|
XNandPs_MarkBbt(InstancePtr, &InstancePtr->BbtMirrorDesc);
|
|
}
|
|
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function converts the Bad Block Table(BBT) read from the flash to the RAM
|
|
* based Bad Block Table(BBT).
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPs instance.
|
|
* @param Buf is the buffer which contains BBT read from flash.
|
|
*
|
|
* @return
|
|
* - NONE.
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPs_ConvertBbt(XNandPs *InstancePtr, u8 *Buf)
|
|
{
|
|
u32 BlockOffset;
|
|
u32 BlockShift;
|
|
u32 Data;
|
|
u8 BlockType;
|
|
u32 BlockIndex;
|
|
u32 BbtLen = InstancePtr->Geometry.NumBlocks >>
|
|
XNANDPS_BBT_BLOCK_SHIFT;
|
|
|
|
for(BlockOffset = 0; BlockOffset < BbtLen; BlockOffset++) {
|
|
Data = Buf[BlockOffset];
|
|
|
|
/*
|
|
* Clear the RAM based Bad Block Table(BBT) contents
|
|
*/
|
|
InstancePtr->Bbt[BlockOffset] = 0x0;
|
|
|
|
/*
|
|
* Loop through the every 4 blocks in the bitmap
|
|
*/
|
|
for(BlockIndex = 0; BlockIndex < XNANDPS_BBT_ENTRY_NUM_BLOCKS;
|
|
BlockIndex++) {
|
|
BlockShift = XNandPs_BbtBlockShift(BlockIndex);
|
|
BlockType = (Data >> BlockShift) &
|
|
XNANDPS_BLOCK_TYPE_MASK;
|
|
switch(BlockType) {
|
|
case XNANDPS_FLASH_BLOCK_FACTORY_BAD:
|
|
/* Factory bad block */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
XNANDPS_BLOCK_FACTORY_BAD <<
|
|
BlockShift;
|
|
break;
|
|
case XNANDPS_FLASH_BLOCK_RESERVED:
|
|
/* Reserved block */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
XNANDPS_BLOCK_RESERVED <<
|
|
BlockShift;
|
|
break;
|
|
case XNANDPS_FLASH_BLOCK_BAD:
|
|
/* Bad block due to wear */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
XNANDPS_BLOCK_BAD <<
|
|
BlockShift;
|
|
break;
|
|
default:
|
|
/* Good block */
|
|
/* The BBT entry already defaults to
|
|
* zero */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function searches the Bad Bloock Table(BBT) in flash and loads into the
|
|
* memory based Bad Block Table(BBT).
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPs instance.
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
static int XNandPs_ReadBbt(XNandPs *InstancePtr)
|
|
{
|
|
u64 Offset;
|
|
u8 Buf[XNANDPS_MAX_BLOCKS >> XNANDPS_BBT_BLOCK_SHIFT];
|
|
u32 Status1;
|
|
u32 Status2;
|
|
u32 Status;
|
|
u32 BbtLen;
|
|
|
|
XNandPs_BbtDesc *Desc = &InstancePtr->BbtDesc;
|
|
XNandPs_BbtDesc *MirrorDesc = &InstancePtr->BbtMirrorDesc;
|
|
BbtLen = InstancePtr->Geometry.NumBlocks >> XNANDPS_BBT_BLOCK_SHIFT;
|
|
|
|
/*
|
|
* Search the Bad Block Table(BBT) in flash
|
|
*/
|
|
Status1 = XNandPs_SearchBbt(InstancePtr, Desc);
|
|
Status2 = XNandPs_SearchBbt(InstancePtr, MirrorDesc);
|
|
if ((Status1 != XST_SUCCESS) && (Status2 != XST_SUCCESS)) {
|
|
return XST_FAILURE;
|
|
}
|
|
|
|
/*
|
|
* Bad Block Table found
|
|
*/
|
|
if (Desc->Valid && MirrorDesc->Valid) {
|
|
/*
|
|
* Valid BBT & Mirror BBT found
|
|
*/
|
|
if (Desc->Version > MirrorDesc->Version) {
|
|
Offset = Desc->PageOffset *
|
|
InstancePtr->Geometry.BytesPerPage;
|
|
XNandPs_Read(InstancePtr, Offset, BbtLen, &Buf, NULL);
|
|
|
|
/*
|
|
* Convert flash BBT to memory based BBT
|
|
*/
|
|
XNandPs_ConvertBbt(InstancePtr, &Buf[0]);
|
|
MirrorDesc->Version = Desc->Version;
|
|
|
|
/*
|
|
* Write the BBT to Mirror BBT location in flash
|
|
*/
|
|
Status = XNandPs_WriteBbt(InstancePtr, MirrorDesc,
|
|
Desc);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
} else if (Desc->Version < MirrorDesc->Version) {
|
|
Offset = MirrorDesc->PageOffset *
|
|
InstancePtr->Geometry.BytesPerPage;
|
|
XNandPs_Read(InstancePtr, Offset, BbtLen, &Buf, NULL);
|
|
|
|
/*
|
|
* Convert flash BBT to memory based BBT
|
|
*/
|
|
XNandPs_ConvertBbt(InstancePtr, &Buf[0]);
|
|
Desc->Version = MirrorDesc->Version;
|
|
|
|
/*
|
|
* Write the Mirror BBT to BBT location in flash
|
|
*/
|
|
Status = XNandPs_WriteBbt(InstancePtr, Desc,
|
|
MirrorDesc);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
} else {
|
|
/* Both are up-to-date */
|
|
Offset = Desc->PageOffset *
|
|
InstancePtr->Geometry.BytesPerPage;
|
|
XNandPs_Read(InstancePtr, Offset, BbtLen, &Buf, NULL);
|
|
|
|
/*
|
|
* Convert flash BBT to memory based BBT
|
|
*/
|
|
XNandPs_ConvertBbt(InstancePtr, &Buf[0]);
|
|
}
|
|
} else if (Desc->Valid) {
|
|
/*
|
|
* Valid Primary BBT found
|
|
*/
|
|
Offset = Desc->PageOffset * InstancePtr->Geometry.BytesPerPage;
|
|
XNandPs_Read(InstancePtr, Offset, BbtLen, &Buf, NULL);
|
|
|
|
/*
|
|
* Convert flash BBT to memory based BBT
|
|
*/
|
|
XNandPs_ConvertBbt(InstancePtr, &Buf[0]);
|
|
MirrorDesc->Version = Desc->Version;
|
|
|
|
/*
|
|
* Write the BBT to Mirror BBT location in flash
|
|
*/
|
|
Status = XNandPs_WriteBbt(InstancePtr, MirrorDesc, Desc);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
} else {
|
|
/*
|
|
* Valid Mirror BBT found
|
|
*/
|
|
Offset = MirrorDesc->PageOffset *
|
|
InstancePtr->Geometry.BytesPerPage;
|
|
XNandPs_Read(InstancePtr, Offset, BbtLen, &Buf, NULL);
|
|
|
|
/*
|
|
* Convert flash BBT to memory based BBT
|
|
*/
|
|
XNandPs_ConvertBbt(InstancePtr, &Buf[0]);
|
|
Desc->Version = MirrorDesc->Version;
|
|
|
|
/*
|
|
* Write the Mirror BBT to BBT location in flash
|
|
*/
|
|
Status = XNandPs_WriteBbt(InstancePtr, Desc, MirrorDesc);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function searches the BBT in flash.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPs instance.
|
|
* @param Desc is the BBT descriptor pattern to search.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
static int XNandPs_SearchBbt(XNandPs *InstancePtr, XNandPs_BbtDesc *Desc)
|
|
{
|
|
u32 StartBlock;
|
|
u32 SigOffset;
|
|
u32 VerOffset;
|
|
u32 MaxBlocks;
|
|
u32 PageOff;
|
|
u32 SigLength;
|
|
u8 Buf[XNANDPS_MAX_SPARE_SIZE];
|
|
u32 Block;
|
|
u32 Offset;
|
|
int Status;
|
|
|
|
StartBlock = InstancePtr->Geometry.NumBlocks - 1;
|
|
SigOffset = Desc->SigOffset;
|
|
VerOffset = Desc->VerOffset;
|
|
MaxBlocks = Desc->MaxBlocks;
|
|
SigLength = Desc->SigLength;
|
|
|
|
/*
|
|
* Read the last 4 blocks for Bad Block Table(BBT) signature
|
|
*/
|
|
for(Block = 0; Block < MaxBlocks; Block++) {
|
|
PageOff = (StartBlock - Block) *
|
|
InstancePtr->Geometry.PagesPerBlock;
|
|
|
|
Status = XNandPs_ReadSpareBytes(InstancePtr, PageOff, &Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Check the Bad Block Table(BBT) signature
|
|
*/
|
|
for(Offset = 0; Offset < SigLength; Offset++) {
|
|
if (Buf[Offset + SigOffset] != Desc->Signature[Offset])
|
|
{
|
|
break; /* Check the next blocks */
|
|
}
|
|
}
|
|
if (Offset >= SigLength) {
|
|
/*
|
|
* Bad Block Table(BBT) found
|
|
*/
|
|
Desc->PageOffset = PageOff;
|
|
Desc->Version = Buf[VerOffset];
|
|
Desc->Valid = 1;
|
|
return XST_SUCCESS;
|
|
}
|
|
}
|
|
/*
|
|
* Bad Block Table(BBT) not found
|
|
*/
|
|
return XST_FAILURE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function writes Bad Block Table(BBT) from RAM to flash.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPs instance.
|
|
* @param Desc is the BBT descriptor to be written to flash.
|
|
* @param MirrorDesc is the mirror BBT descriptor.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
static int XNandPs_WriteBbt(XNandPs *InstancePtr, XNandPs_BbtDesc *Desc,
|
|
XNandPs_BbtDesc *MirrorDesc)
|
|
{
|
|
u64 Offset;
|
|
u32 Block = InstancePtr->Geometry.NumBlocks - 1;
|
|
u8 Buf[XNANDPS_MAX_BLOCKS >> XNANDPS_BBT_BLOCK_SHIFT];
|
|
u8 SpareBuf[XNANDPS_MAX_SPARE_SIZE];
|
|
u8 Mask[4] = {0x00, 0x01, 0x02, 0x03};
|
|
u8 Data;
|
|
u32 BlockOffset;
|
|
u32 BlockShift;
|
|
u32 Status;
|
|
u32 BlockIndex;
|
|
u32 Index;
|
|
u8 BlockType;
|
|
u32 BbtLen = InstancePtr->Geometry.NumBlocks >>
|
|
XNANDPS_BBT_BLOCK_SHIFT;
|
|
|
|
/*
|
|
* Find a valid block to write the Bad Block Table(BBT)
|
|
*/
|
|
if (!Desc->Valid) {
|
|
for(Index = 0; Index < Desc->MaxBlocks; Index++) {
|
|
Block = (Block - Index);
|
|
BlockOffset = Block >> XNANDPS_BBT_BLOCK_SHIFT;
|
|
BlockShift = XNandPs_BbtBlockShift(Block);
|
|
BlockType = (InstancePtr->Bbt[BlockOffset] >>
|
|
BlockShift) & XNANDPS_BLOCK_TYPE_MASK;
|
|
switch(BlockType)
|
|
{
|
|
case XNANDPS_BLOCK_BAD:
|
|
case XNANDPS_BLOCK_FACTORY_BAD:
|
|
continue;
|
|
default:
|
|
/* Good Block */
|
|
break;
|
|
}
|
|
Desc->PageOffset = Block *
|
|
InstancePtr->Geometry.PagesPerBlock;
|
|
if (Desc->PageOffset != MirrorDesc->PageOffset) {
|
|
/* Free block found */
|
|
Desc->Valid = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Block not found for writing Bad Block Table(BBT)
|
|
*/
|
|
if (Index >= Desc->MaxBlocks) {
|
|
return XST_FAILURE;
|
|
}
|
|
} else {
|
|
Block = Desc->PageOffset/InstancePtr->Geometry.PagesPerBlock;
|
|
}
|
|
|
|
/*
|
|
* Convert the memory based BBT to flash based table
|
|
*/
|
|
memset(Buf, 0xff, BbtLen);
|
|
|
|
/*
|
|
* Loop through the number of blocks
|
|
*/
|
|
for(BlockOffset = 0; BlockOffset < BbtLen; BlockOffset++) {
|
|
Data = InstancePtr->Bbt[BlockOffset];
|
|
/*
|
|
* Calculate the bit mask for 4 blocks at a time in loop
|
|
*/
|
|
for(BlockIndex = 0; BlockIndex < XNANDPS_BBT_ENTRY_NUM_BLOCKS;
|
|
BlockIndex++) {
|
|
BlockShift = XNandPs_BbtBlockShift(BlockIndex);
|
|
Buf[BlockOffset] &= ~(Mask[Data &
|
|
XNANDPS_BLOCK_TYPE_MASK] <<
|
|
BlockShift);
|
|
Data >>= XNANDPS_BBT_BLOCK_SHIFT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write the Bad Block Table(BBT) to flash
|
|
*/
|
|
Status = XNandPs_EraseBlock(InstancePtr, Block);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* Write the signature and version in the spare data area
|
|
*/
|
|
memset(SpareBuf, 0xff, InstancePtr->Geometry.SpareBytesPerPage);
|
|
memcpy(SpareBuf + Desc->SigOffset, &Desc->Signature[0],
|
|
Desc->SigLength);
|
|
memcpy(SpareBuf + Desc->VerOffset, &Desc->Version, 1);
|
|
|
|
/*
|
|
* Write the BBT to page offset
|
|
*/
|
|
Offset = Desc->PageOffset * InstancePtr->Geometry.BytesPerPage;
|
|
Status = XNandPs_Write(InstancePtr, Offset, BbtLen, &Buf[0], SpareBuf);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function updates the primary and mirror Bad Block Table(BBT) in the
|
|
* flash.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPs instance.
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
static int XNandPs_UpdateBbt(XNandPs *InstancePtr)
|
|
{
|
|
int Status;
|
|
u8 Version;
|
|
|
|
/*
|
|
* Update the version number
|
|
*/
|
|
Version = InstancePtr->BbtDesc.Version;
|
|
InstancePtr->BbtDesc.Version = (Version + 1) % 256;
|
|
Version = InstancePtr->BbtMirrorDesc.Version;
|
|
InstancePtr->BbtMirrorDesc.Version = (Version + 1) % 256;
|
|
|
|
/*
|
|
* Update the primary Bad Block Table(BBT) in flash
|
|
*/
|
|
Status = XNandPs_WriteBbt(InstancePtr, &InstancePtr->BbtDesc,
|
|
&InstancePtr->BbtMirrorDesc);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* Update the mirrored Bad Block Table(BBT) in flash
|
|
*/
|
|
Status = XNandPs_WriteBbt(InstancePtr, &InstancePtr->BbtMirrorDesc,
|
|
&InstancePtr->BbtDesc);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function marks the block containing Bad Block Table as reserved
|
|
* and updates the BBT.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPs instance.
|
|
* @param Desc is the BBT descriptor pointer.
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
static int XNandPs_MarkBbt(XNandPs* InstancePtr, XNandPs_BbtDesc *Desc)
|
|
{
|
|
u32 BlockIndex;
|
|
u32 BlockOffset;
|
|
u8 BlockShift;
|
|
u8 OldVal;
|
|
u8 NewVal;
|
|
int Status;
|
|
u32 UpdateBbt = 0;
|
|
u32 Index;
|
|
|
|
/*
|
|
* Mark the last four blocks as Reserved
|
|
*/
|
|
BlockIndex = InstancePtr->Geometry.NumBlocks - Desc->MaxBlocks - 1;
|
|
|
|
for(Index = 0; Index < Desc->MaxBlocks; Index++,BlockIndex++) {
|
|
|
|
BlockOffset = BlockIndex >> XNANDPS_BBT_BLOCK_SHIFT;
|
|
BlockShift = XNandPs_BbtBlockShift(BlockIndex);
|
|
OldVal = InstancePtr->Bbt[BlockOffset];
|
|
NewVal = OldVal | (XNANDPS_BLOCK_RESERVED << BlockShift);
|
|
InstancePtr->Bbt[BlockOffset] = NewVal;
|
|
|
|
if (OldVal != NewVal) {
|
|
UpdateBbt = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update the BBT to flash
|
|
*/
|
|
if (UpdateBbt) {
|
|
Status = XNandPs_UpdateBbt(InstancePtr);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function checks whether a block is bad or not.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPs instance.
|
|
*
|
|
* @param Block is the block number.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
int XNandPs_IsBlockBad(XNandPs *InstancePtr, u32 Block)
|
|
{
|
|
u8 Data;
|
|
u8 BlockShift;
|
|
u8 BlockType;
|
|
u32 BlockOffset;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
Xil_AssertNonvoid(Block < InstancePtr->Geometry.NumBlocks);
|
|
|
|
BlockOffset = Block >> XNANDPS_BBT_BLOCK_SHIFT;
|
|
BlockShift = XNandPs_BbtBlockShift(Block);
|
|
Data = InstancePtr->Bbt[BlockOffset]; /* Block information in BBT */
|
|
BlockType = (Data >> BlockShift) & XNANDPS_BLOCK_TYPE_MASK;
|
|
|
|
if (BlockType != XNANDPS_BLOCK_GOOD)
|
|
return XST_SUCCESS;
|
|
else
|
|
return XST_FAILURE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function marks a block as bad in the RAM based Bad Block Table(BBT). It
|
|
* also updates the Bad Block Table(BBT) in the flash.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPs instance.
|
|
* @param Block is the block number.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
int XNandPs_MarkBlockBad(XNandPs *InstancePtr, u32 Block)
|
|
{
|
|
u8 Data;
|
|
u8 BlockShift;
|
|
u32 BlockOffset;
|
|
u8 OldVal;
|
|
u8 NewVal;
|
|
u32 Status;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
Xil_AssertNonvoid(Block < InstancePtr->Geometry.NumBlocks);
|
|
|
|
BlockOffset = Block >> XNANDPS_BBT_BLOCK_SHIFT;
|
|
BlockShift = XNandPs_BbtBlockShift(Block);
|
|
Data = InstancePtr->Bbt[BlockOffset]; /* Block information in BBT */
|
|
|
|
/*
|
|
* Mark the block as bad in the RAM based Bad Block Table
|
|
*/
|
|
OldVal = Data;
|
|
Data &= ~(XNANDPS_BLOCK_TYPE_MASK << BlockShift);
|
|
Data |= (XNANDPS_BLOCK_BAD << BlockShift);
|
|
NewVal = Data;
|
|
InstancePtr->Bbt[BlockOffset] = Data;
|
|
|
|
/*
|
|
* Update the Bad Block Table(BBT) in flash
|
|
*/
|
|
if (OldVal != NewVal) {
|
|
Status = XNandPs_UpdateBbt(InstancePtr);
|
|
if (Status != XST_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
return XST_SUCCESS;
|
|
}
|