forked from Imagelibrary/rtems
The xnandpsu driver includes functionality to map back and forth between the flash-based BBT and the memory-based BBT with the values in each being a bitwise inversion of each other. This resolves several bugs in this process and simplifies the inversion from operating on the block representation to operating on the entire BBT entry (4 blocks, 2 bits per block, one byte total). Bugs resolved in XNandPsu_ConvertBbt(): * The calculation of memory BBT entry offset was off by a factor of 4 * The entry offset into the flash BBT has been removed since each flash BBT directly describes the flash space it is contained within and has no reference to other devices in the chip Bugs resolved in XNandPsu_WriteBbt(): * The BBT length calculated was reduced to NumTargetBlocks from NumBlocks since only the relevant portion of the in-memory BBT should be written to the flash-based BBT space * An offset was applied to values retrieved from the in-memory BBT so that only the relevant portion was converted and written to the flash-based BBT
945 lines
27 KiB
C
945 lines
27 KiB
C
/******************************************************************************
|
|
* Copyright (C) 2015 - 2022 Xilinx, Inc. All rights reserved.
|
|
* SPDX-License-Identifier: MIT
|
|
******************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* @file xnandpsu_bbm.c
|
|
* @addtogroup Overview
|
|
* @{
|
|
*
|
|
* This file implements the Bad Block Management (BBM) functionality.
|
|
* See xnandpsu_bbm.h for more details.
|
|
*
|
|
* @note None
|
|
*
|
|
* <pre>
|
|
* MODIFICATION HISTORY:
|
|
*
|
|
* Ver Who Date Changes
|
|
* ----- ---- ---------- -----------------------------------------------
|
|
* 1.0 nm 05/06/2014 First release
|
|
* 2.0 sb 01/12/2015 Added support for writing BBT signature and version
|
|
* in page section by enabling XNANDPSU_BBT_NO_OOB.
|
|
* Modified Bbt Signature and Version Offset value for
|
|
* Oob and No-Oob region.
|
|
* 1.1 nsk 11/07/16 Change memcpy to Xil_MemCpy to handle word aligned
|
|
* data access.
|
|
* 1.4 nsk 04/10/18 Added ICCARM compiler support.
|
|
* 1.10 akm 01/05/22 Remove assert checks form static and internal APIs.
|
|
* </pre>
|
|
*
|
|
******************************************************************************/
|
|
|
|
/***************************** Include Files *********************************/
|
|
#include <string.h> /**< For Xil_MemCpy and memset */
|
|
#include "xil_types.h"
|
|
#include "xnandpsu.h"
|
|
#include "xnandpsu_bbm.h"
|
|
#include "xil_mem.h"
|
|
|
|
/************************** Constant Definitions *****************************/
|
|
|
|
/**************************** Type Definitions *******************************/
|
|
|
|
/***************** Macros (Inline Functions) Definitions *********************/
|
|
|
|
/************************** Function Prototypes ******************************/
|
|
static s32 XNandPsu_ReadBbt(XNandPsu *InstancePtr, u32 Target);
|
|
|
|
static s32 XNandPsu_SearchBbt(XNandPsu *InstancePtr, XNandPsu_BbtDesc *Desc,
|
|
u32 Target);
|
|
|
|
static void XNandPsu_CreateBbt(XNandPsu *InstancePtr, u32 Target);
|
|
|
|
static void XNandPsu_ConvertBbt(XNandPsu *InstancePtr, u8 *Buf, u32 Target);
|
|
|
|
static s32 XNandPsu_WriteBbt(XNandPsu *InstancePtr, XNandPsu_BbtDesc *Desc,
|
|
XNandPsu_BbtDesc *MirrorDesc, u32 Target);
|
|
|
|
static s32 XNandPsu_MarkBbt(XNandPsu* InstancePtr, XNandPsu_BbtDesc *Desc,
|
|
u32 Target);
|
|
|
|
static s32 XNandPsu_UpdateBbt(XNandPsu *InstancePtr, u32 Target);
|
|
|
|
/************************** 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 XNandPsu instance.
|
|
*
|
|
* @return
|
|
* - NONE
|
|
*
|
|
******************************************************************************/
|
|
void XNandPsu_InitBbtDesc(XNandPsu *InstancePtr)
|
|
{
|
|
u32 Index;
|
|
|
|
/* Initialize primary Bad Block Table(BBT) */
|
|
for (Index = 0U; Index < XNANDPSU_MAX_TARGETS; Index++) {
|
|
InstancePtr->BbtDesc.PageOffset[Index] =
|
|
XNANDPSU_BBT_DESC_PAGE_OFFSET;
|
|
}
|
|
if (InstancePtr->EccMode == XNANDPSU_ONDIE) {
|
|
InstancePtr->BbtDesc.SigOffset = XNANDPSU_ONDIE_SIG_OFFSET;
|
|
InstancePtr->BbtDesc.VerOffset = XNANDPSU_ONDIE_VER_OFFSET;
|
|
} else {
|
|
InstancePtr->BbtDesc.SigOffset = XNANDPSU_BBT_DESC_SIG_OFFSET;
|
|
InstancePtr->BbtDesc.VerOffset = XNANDPSU_BBT_DESC_VER_OFFSET;
|
|
}
|
|
InstancePtr->BbtDesc.SigLength = XNANDPSU_BBT_DESC_SIG_LEN;
|
|
InstancePtr->BbtDesc.MaxBlocks = XNANDPSU_BBT_DESC_MAX_BLOCKS;
|
|
(void)strcpy(&InstancePtr->BbtDesc.Signature[0], "Bbt0");
|
|
for (Index = 0U; Index < XNANDPSU_MAX_TARGETS; Index++) {
|
|
InstancePtr->BbtDesc.Version[Index] = 0U;
|
|
}
|
|
InstancePtr->BbtDesc.Valid = 0U;
|
|
|
|
/* Assuming that the flash device will have at least 4 blocks. */
|
|
if (InstancePtr->Geometry.NumTargetBlocks <= InstancePtr->
|
|
BbtDesc.MaxBlocks){
|
|
InstancePtr->BbtDesc.MaxBlocks = 4U;
|
|
}
|
|
|
|
/* Initialize mirror Bad Block Table(BBT) */
|
|
for (Index = 0U; Index < XNANDPSU_MAX_TARGETS; Index++) {
|
|
InstancePtr->BbtMirrorDesc.PageOffset[Index] =
|
|
XNANDPSU_BBT_DESC_PAGE_OFFSET;
|
|
}
|
|
if (InstancePtr->EccMode == XNANDPSU_ONDIE) {
|
|
InstancePtr->BbtMirrorDesc.SigOffset =
|
|
XNANDPSU_ONDIE_SIG_OFFSET;
|
|
InstancePtr->BbtMirrorDesc.VerOffset =
|
|
XNANDPSU_ONDIE_VER_OFFSET;
|
|
} else {
|
|
InstancePtr->BbtMirrorDesc.SigOffset =
|
|
XNANDPSU_BBT_DESC_SIG_OFFSET;
|
|
InstancePtr->BbtMirrorDesc.VerOffset =
|
|
XNANDPSU_BBT_DESC_VER_OFFSET;
|
|
}
|
|
InstancePtr->BbtMirrorDesc.SigLength = XNANDPSU_BBT_DESC_SIG_LEN;
|
|
InstancePtr->BbtMirrorDesc.MaxBlocks = XNANDPSU_BBT_DESC_MAX_BLOCKS;
|
|
(void)strcpy(&InstancePtr->BbtMirrorDesc.Signature[0], "1tbB");
|
|
for (Index = 0U; Index < XNANDPSU_MAX_TARGETS; Index++) {
|
|
InstancePtr->BbtMirrorDesc.Version[Index] = 0U;
|
|
}
|
|
InstancePtr->BbtMirrorDesc.Valid = 0U;
|
|
|
|
/* Assuming that the flash device will have at least 4 blocks. */
|
|
if (InstancePtr->Geometry.NumTargetBlocks <= InstancePtr->
|
|
BbtMirrorDesc.MaxBlocks){
|
|
InstancePtr->BbtMirrorDesc.MaxBlocks = 4U;
|
|
}
|
|
|
|
/* Initialize Bad block search pattern structure */
|
|
if (InstancePtr->Geometry.BytesPerPage > 512U) {
|
|
/* For flash page size > 512 bytes */
|
|
InstancePtr->BbPattern.Options = XNANDPSU_BBT_SCAN_2ND_PAGE;
|
|
InstancePtr->BbPattern.Offset =
|
|
XNANDPSU_BB_PTRN_OFF_LARGE_PAGE;
|
|
InstancePtr->BbPattern.Length =
|
|
XNANDPSU_BB_PTRN_LEN_LARGE_PAGE;
|
|
} else {
|
|
InstancePtr->BbPattern.Options = XNANDPSU_BBT_SCAN_2ND_PAGE;
|
|
InstancePtr->BbPattern.Offset =
|
|
XNANDPSU_BB_PTRN_OFF_SML_PAGE;
|
|
InstancePtr->BbPattern.Length =
|
|
XNANDPSU_BB_PTRN_LEN_SML_PAGE;
|
|
}
|
|
for(Index = 0U; Index < XNANDPSU_BB_PTRN_LEN_LARGE_PAGE; Index++) {
|
|
InstancePtr->BbPattern.Pattern[Index] = XNANDPSU_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 XNandPsu instance.
|
|
*
|
|
* @return
|
|
* - NONE
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_CreateBbt(XNandPsu *InstancePtr, u32 Target)
|
|
{
|
|
u32 BlockIndex;
|
|
u32 PageIndex;
|
|
u32 Length;
|
|
u32 BlockOffset;
|
|
u8 BlockShift;
|
|
u32 NumPages;
|
|
u32 Page;
|
|
#ifdef __ICCARM__
|
|
#pragma pack(push, 1)
|
|
u8 Buf[XNANDPSU_MAX_SPARE_SIZE] = {0U};
|
|
#pragma pack(pop)
|
|
#else
|
|
u8 Buf[XNANDPSU_MAX_SPARE_SIZE] __attribute__ ((aligned(64))) = {0U};
|
|
#endif
|
|
u32 StartBlock = Target * InstancePtr->Geometry.NumTargetBlocks;
|
|
u32 NumBlocks = InstancePtr->Geometry.NumTargetBlocks;
|
|
s32 Status;
|
|
|
|
/* Number of pages to search for bad block pattern */
|
|
if ((InstancePtr->BbPattern.Options & XNANDPSU_BBT_SCAN_2ND_PAGE) != 0U)
|
|
{
|
|
NumPages = 2U;
|
|
} else {
|
|
NumPages = 1U;
|
|
}
|
|
/* Scan all the blocks for factory marked bad blocks */
|
|
for(BlockIndex = StartBlock; BlockIndex < (StartBlock + NumBlocks);
|
|
BlockIndex++) {
|
|
/* Block offset in Bad Block Table(BBT) entry */
|
|
BlockOffset = BlockIndex >> XNANDPSU_BBT_BLOCK_SHIFT;
|
|
/* Block shift value in the byte */
|
|
BlockShift = XNandPsu_BbtBlockShift(BlockIndex);
|
|
Page = BlockIndex * InstancePtr->Geometry.PagesPerBlock;
|
|
/* Search for the bad block pattern */
|
|
for(PageIndex = 0U; PageIndex < NumPages; PageIndex++) {
|
|
Status = XNandPsu_ReadSpareBytes(InstancePtr,
|
|
(Page + PageIndex), &Buf[0]);
|
|
|
|
if (Status != XST_SUCCESS) {
|
|
/* Marking as bad block */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
(u8)(XNANDPSU_BLOCK_FACTORY_BAD <<
|
|
BlockShift);
|
|
break;
|
|
}
|
|
/*
|
|
* Read the spare bytes to check for bad block
|
|
* pattern
|
|
*/
|
|
for(Length = 0U; Length <
|
|
InstancePtr->BbPattern.Length; Length++) {
|
|
if (Buf[InstancePtr->BbPattern.Offset + Length]
|
|
!=
|
|
InstancePtr->BbPattern.Pattern[Length])
|
|
{
|
|
/* Bad block found */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
(u8)
|
|
(XNANDPSU_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 XNandPsu instance.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_ScanBbt(XNandPsu *InstancePtr)
|
|
{
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY)
|
|
|
|
s32 Status;
|
|
u32 Index;
|
|
u32 BbtLen;
|
|
|
|
/* Zero the RAM based Bad Block Table(BBT) entries */
|
|
BbtLen = InstancePtr->Geometry.NumBlocks >>
|
|
XNANDPSU_BBT_BLOCK_SHIFT;
|
|
(void)memset(&InstancePtr->Bbt[0], 0, BbtLen);
|
|
|
|
for (Index = 0U; Index < InstancePtr->Geometry.NumTargets; Index++) {
|
|
|
|
if (XNandPsu_ReadBbt(InstancePtr, Index) != XST_SUCCESS) {
|
|
/* Create memory based Bad Block Table(BBT) */
|
|
XNandPsu_CreateBbt(InstancePtr, Index);
|
|
/* Write the Bad Block Table(BBT) to the flash */
|
|
Status = XNandPsu_WriteBbt(InstancePtr,
|
|
&InstancePtr->BbtDesc,
|
|
&InstancePtr->BbtMirrorDesc, Index);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Write the Mirror Bad Block Table(BBT) to the flash */
|
|
Status = XNandPsu_WriteBbt(InstancePtr,
|
|
&InstancePtr->BbtMirrorDesc,
|
|
&InstancePtr->BbtDesc, Index);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/*
|
|
* Mark the blocks containing Bad Block Table
|
|
* (BBT) as Reserved
|
|
*/
|
|
Status = XNandPsu_MarkBbt(InstancePtr,
|
|
&InstancePtr->BbtDesc,
|
|
Index);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
Status = XNandPsu_MarkBbt(InstancePtr,
|
|
&InstancePtr->BbtMirrorDesc,
|
|
Index);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
}
|
|
}
|
|
|
|
Status = XST_SUCCESS;
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* 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 XNandPsu instance.
|
|
* @param Buf is the buffer which contains BBT read from flash.
|
|
*
|
|
* @return
|
|
* - NONE.
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_ConvertBbt(XNandPsu *InstancePtr, u8 *Buf, u32 Target)
|
|
{
|
|
#ifndef __rtems__
|
|
u32 BlockOffset;
|
|
u8 BlockShift;
|
|
u32 Data;
|
|
u8 BlockType;
|
|
u32 BlockIndex;
|
|
#endif
|
|
u32 BbtLen = InstancePtr->Geometry.NumTargetBlocks >>
|
|
XNANDPSU_BBT_BLOCK_SHIFT;
|
|
#ifdef __rtems__
|
|
u32 BbtOffset = Target * InstancePtr->Geometry.NumTargetBlocks / XNANDPSU_BBT_ENTRY_NUM_BLOCKS;
|
|
|
|
for(u32 BbtIndex = 0; BbtIndex < BbtLen; BbtIndex++) {
|
|
/* Invert the byte to convert from in-flash BBT to in-memory BBT */
|
|
InstancePtr->Bbt[BbtIndex + BbtOffset] = ~Buf[BbtIndex];
|
|
}
|
|
#else
|
|
u32 StartBlock = Target * InstancePtr->Geometry.NumTargetBlocks;
|
|
|
|
for(BlockOffset = StartBlock; BlockOffset < (StartBlock + BbtLen);
|
|
BlockOffset++) {
|
|
Data = *(Buf + BlockOffset);
|
|
/* Clear the RAM based Bad Block Table(BBT) contents */
|
|
InstancePtr->Bbt[BlockOffset] = 0x0U;
|
|
/* Loop through the every 4 blocks in the bitmap */
|
|
for(BlockIndex = 0U; BlockIndex < XNANDPSU_BBT_ENTRY_NUM_BLOCKS;
|
|
BlockIndex++) {
|
|
BlockShift = XNandPsu_BbtBlockShift(BlockIndex);
|
|
BlockType = (u8) ((Data >> BlockShift) &
|
|
XNANDPSU_BLOCK_TYPE_MASK);
|
|
switch(BlockType) {
|
|
case XNANDPSU_FLASH_BLOCK_FAC_BAD:
|
|
/* Factory bad block */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
(u8)
|
|
(XNANDPSU_BLOCK_FACTORY_BAD <<
|
|
BlockShift);
|
|
break;
|
|
case XNANDPSU_FLASH_BLOCK_RESERVED:
|
|
/* Reserved block */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
(u8)
|
|
(XNANDPSU_BLOCK_RESERVED <<
|
|
BlockShift);
|
|
break;
|
|
case XNANDPSU_FLASH_BLOCK_BAD:
|
|
/* Bad block due to wear */
|
|
InstancePtr->Bbt[BlockOffset] |=
|
|
(u8)(XNANDPSU_BLOCK_BAD <<
|
|
BlockShift);
|
|
break;
|
|
default:
|
|
/* Good block */
|
|
/* The BBT entry already defaults to
|
|
* zero */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* 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 XNandPsu instance.
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_ReadBbt(XNandPsu *InstancePtr, u32 Target)
|
|
{
|
|
u64 Offset;
|
|
#ifdef __ICCARM__
|
|
#pragma pack(push, 1)
|
|
u8 Buf[XNANDPSU_BBT_BUF_LENGTH]= {0U};
|
|
#pragma pack(pop)
|
|
#else
|
|
u8 Buf[XNANDPSU_BBT_BUF_LENGTH] __attribute__ ((aligned(64))) = {0U};
|
|
#endif
|
|
s32 Status1;
|
|
s32 Status2;
|
|
s32 Status;
|
|
u32 BufLen;
|
|
|
|
XNandPsu_BbtDesc *Desc = &InstancePtr->BbtDesc;
|
|
XNandPsu_BbtDesc *MirrorDesc = &InstancePtr->BbtMirrorDesc;
|
|
BufLen = InstancePtr->Geometry.NumBlocks >>
|
|
XNANDPSU_BBT_BLOCK_SHIFT;
|
|
/* Search the Bad Block Table(BBT) in flash */
|
|
Status1 = XNandPsu_SearchBbt(InstancePtr, Desc, Target);
|
|
Status2 = XNandPsu_SearchBbt(InstancePtr, MirrorDesc, Target);
|
|
if ((Status1 != XST_SUCCESS) && (Status2 != XST_SUCCESS)) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Bad block table not found\r\n",__func__);
|
|
#endif
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Bad block table found\r\n",__func__);
|
|
#endif
|
|
/* Bad Block Table found */
|
|
if ((Desc->Valid != 0U) && (MirrorDesc->Valid != 0U)) {
|
|
/* Valid BBT & Mirror BBT found */
|
|
if (Desc->Version[Target] > MirrorDesc->Version[Target]) {
|
|
Offset = (u64)Desc->PageOffset[Target] *
|
|
(u64)InstancePtr->Geometry.BytesPerPage;
|
|
Status = XNandPsu_Read(InstancePtr, Offset, BufLen,
|
|
&Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Convert flash BBT to memory based BBT */
|
|
XNandPsu_ConvertBbt(InstancePtr, &Buf[0], Target);
|
|
MirrorDesc->Version[Target] = Desc->Version[Target];
|
|
|
|
/* Write the BBT to Mirror BBT location in flash */
|
|
Status = XNandPsu_WriteBbt(InstancePtr, MirrorDesc,
|
|
Desc, Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
} else if (Desc->Version[Target] <
|
|
MirrorDesc->Version[Target]) {
|
|
Offset = (u64)MirrorDesc->PageOffset[Target] *
|
|
(u64)InstancePtr->Geometry.BytesPerPage;
|
|
Status = XNandPsu_Read(InstancePtr, Offset, BufLen,
|
|
&Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Convert flash BBT to memory based BBT */
|
|
XNandPsu_ConvertBbt(InstancePtr, &Buf[0], Target);
|
|
Desc->Version[Target] = MirrorDesc->Version[Target];
|
|
|
|
/* Write the Mirror BBT to BBT location in flash */
|
|
Status = XNandPsu_WriteBbt(InstancePtr, Desc,
|
|
MirrorDesc, Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
} else {
|
|
/* Both are up-to-date */
|
|
Offset = (u64)Desc->PageOffset[Target] *
|
|
(u64)InstancePtr->Geometry.BytesPerPage;
|
|
Status = XNandPsu_Read(InstancePtr, Offset, BufLen,
|
|
&Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Convert flash BBT to memory based BBT */
|
|
XNandPsu_ConvertBbt(InstancePtr, &Buf[0], Target);
|
|
}
|
|
} else if (Desc->Valid != 0U) {
|
|
/* Valid Primary BBT found */
|
|
Offset = (u64)Desc->PageOffset[Target] *
|
|
(u64)InstancePtr->Geometry.BytesPerPage;
|
|
Status = XNandPsu_Read(InstancePtr, Offset, BufLen, &Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Convert flash BBT to memory based BBT */
|
|
XNandPsu_ConvertBbt(InstancePtr, &Buf[0], Target);
|
|
MirrorDesc->Version[Target] = Desc->Version[Target];
|
|
|
|
/* Write the BBT to Mirror BBT location in flash */
|
|
Status = XNandPsu_WriteBbt(InstancePtr, MirrorDesc, Desc,
|
|
Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
} else {
|
|
/* Valid Mirror BBT found */
|
|
Offset = (u64)MirrorDesc->PageOffset[Target] *
|
|
(u64)InstancePtr->Geometry.BytesPerPage;
|
|
Status = XNandPsu_Read(InstancePtr, Offset, BufLen, &Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Convert flash BBT to memory based BBT */
|
|
XNandPsu_ConvertBbt(InstancePtr, &Buf[0], Target);
|
|
Desc->Version[Target] = MirrorDesc->Version[Target];
|
|
|
|
/* Write the Mirror BBT to BBT location in flash */
|
|
Status = XNandPsu_WriteBbt(InstancePtr, Desc, MirrorDesc,
|
|
Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
Status = XST_SUCCESS;
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function searches the BBT in flash.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPsu instance.
|
|
* @param Desc is the BBT descriptor pattern to search.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_SearchBbt(XNandPsu *InstancePtr, XNandPsu_BbtDesc *Desc,
|
|
u32 Target)
|
|
{
|
|
u32 StartBlock;
|
|
u32 SigOffset;
|
|
u32 VerOffset;
|
|
u32 MaxBlocks;
|
|
u32 PageOff;
|
|
u32 SigLength;
|
|
#ifdef __ICCARM__
|
|
#pragma pack(push, 1)
|
|
u8 Buf[XNANDPSU_MAX_SPARE_SIZE] = {0U};
|
|
#pragma pack(pop)
|
|
#else
|
|
u8 Buf[XNANDPSU_MAX_SPARE_SIZE] __attribute__ ((aligned(64))) = {0U};
|
|
#endif
|
|
u32 Block;
|
|
u32 Offset;
|
|
s32 Status;
|
|
|
|
StartBlock = ((Target + (u32)1) *
|
|
InstancePtr->Geometry.NumTargetBlocks) - (u32)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 = 0U; Block < MaxBlocks; Block++) {
|
|
PageOff = (StartBlock - Block) *
|
|
InstancePtr->Geometry.PagesPerBlock;
|
|
|
|
Status = XNandPsu_ReadSpareBytes(InstancePtr, PageOff, &Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
continue;
|
|
}
|
|
/* Check the Bad Block Table(BBT) signature */
|
|
for(Offset = 0U; Offset < SigLength; Offset++) {
|
|
if (Buf[Offset + SigOffset] !=
|
|
(u8)(Desc->Signature[Offset]))
|
|
{
|
|
break; /* Check the next blocks */
|
|
}
|
|
}
|
|
if (Offset >= SigLength) {
|
|
/* Bad Block Table(BBT) found */
|
|
Desc->PageOffset[Target] = PageOff;
|
|
Desc->Version[Target] = Buf[VerOffset];
|
|
Desc->Valid = 1U;
|
|
|
|
Status = XST_SUCCESS;
|
|
goto Out;
|
|
}
|
|
}
|
|
/* Bad Block Table(BBT) not found */
|
|
Status = XST_FAILURE;
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function writes Bad Block Table(BBT) from RAM to flash.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPsu 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 s32 XNandPsu_WriteBbt(XNandPsu *InstancePtr, XNandPsu_BbtDesc *Desc,
|
|
XNandPsu_BbtDesc *MirrorDesc, u32 Target)
|
|
{
|
|
u64 Offset;
|
|
u32 Block = {0U};
|
|
u32 EndBlock = ((Target + (u32)1) *
|
|
InstancePtr->Geometry.NumTargetBlocks) - (u32)1;
|
|
#ifdef __ICCARM__
|
|
#pragma pack(push, 1)
|
|
u8 Buf[XNANDPSU_BBT_BUF_LENGTH]= {0U};
|
|
u8 SpareBuf[XNANDPSU_MAX_SPARE_SIZE]= {0U};
|
|
#pragma pack(pop)
|
|
#else
|
|
u8 Buf[XNANDPSU_BBT_BUF_LENGTH] __attribute__ ((aligned(64))) = {0U};
|
|
u8 SpareBuf[XNANDPSU_MAX_SPARE_SIZE] __attribute__ ((aligned(64))) = {0U};
|
|
#endif
|
|
|
|
#ifndef __rtems__
|
|
u8 Mask[4] = {0x00U, 0x01U, 0x02U, 0x03U};
|
|
u8 Data;
|
|
u32 BlockOffset;
|
|
u8 BlockShift;
|
|
s32 Status;
|
|
u32 BlockIndex;
|
|
u32 Index;
|
|
u8 BlockType;
|
|
u32 BbtLen = InstancePtr->Geometry.NumBlocks >>
|
|
#else
|
|
s32 Status;
|
|
u32 Index;
|
|
u32 BbtLen = InstancePtr->Geometry.NumTargetBlocks >>
|
|
#endif
|
|
XNANDPSU_BBT_BLOCK_SHIFT;
|
|
/* Find a valid block to write the Bad Block Table(BBT) */
|
|
if ((!Desc->Valid) != 0U) {
|
|
for(Index = 0U; Index < Desc->MaxBlocks; Index++) {
|
|
Block = (EndBlock - Index);
|
|
#ifdef __rtems__
|
|
if (XNandPsu_IsBlockBad(InstancePtr, Block) == XST_FAILURE) {
|
|
continue;
|
|
}
|
|
#else
|
|
BlockOffset = Block >> XNANDPSU_BBT_BLOCK_SHIFT;
|
|
BlockShift = XNandPsu_BbtBlockShift(Block);
|
|
BlockType = (InstancePtr->Bbt[BlockOffset] >>
|
|
BlockShift) & XNANDPSU_BLOCK_TYPE_MASK;
|
|
switch(BlockType)
|
|
{
|
|
case XNANDPSU_BLOCK_BAD:
|
|
case XNANDPSU_BLOCK_FACTORY_BAD:
|
|
continue;
|
|
default:
|
|
/* Good Block */
|
|
break;
|
|
}
|
|
#endif
|
|
Desc->PageOffset[Target] = Block *
|
|
InstancePtr->Geometry.PagesPerBlock;
|
|
if (Desc->PageOffset[Target] !=
|
|
MirrorDesc->PageOffset[Target]) {
|
|
/* Free block found */
|
|
Desc->Valid = 1U;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Block not found for writing Bad Block Table(BBT) */
|
|
if (Index >= Desc->MaxBlocks) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Blocks unavailable for writing BBT\r\n",
|
|
__func__);
|
|
#endif
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
} else {
|
|
Block = Desc->PageOffset[Target] /
|
|
InstancePtr->Geometry.PagesPerBlock;
|
|
}
|
|
/* Convert the memory based BBT to flash based table */
|
|
(void)memset(Buf, 0xff, BbtLen);
|
|
|
|
#ifdef __rtems__
|
|
u32 BbtTargetOffset = BbtLen * Target;
|
|
/* Loop through the BBT entries */
|
|
for(u32 BbtIndex = 0U; BbtIndex < BbtLen; BbtIndex++) {
|
|
/* Invert byte to convert from in-memory BBT to in-flash BBT */
|
|
Buf[BbtIndex] = ~InstancePtr->Bbt[BbtIndex + BbtTargetOffset];
|
|
}
|
|
#else
|
|
/* Loop through the number of blocks */
|
|
for(BlockOffset = 0U; BlockOffset < BbtLen; BlockOffset++) {
|
|
Data = InstancePtr->Bbt[BlockOffset];
|
|
/* Calculate the bit mask for 4 blocks at a time in loop */
|
|
for(BlockIndex = 0U; BlockIndex < XNANDPSU_BBT_ENTRY_NUM_BLOCKS;
|
|
BlockIndex++) {
|
|
BlockShift = XNandPsu_BbtBlockShift(BlockIndex);
|
|
Buf[BlockOffset] &= ~(Mask[Data &
|
|
XNANDPSU_BLOCK_TYPE_MASK] <<
|
|
BlockShift);
|
|
Data >>= XNANDPSU_BBT_BLOCK_SHIFT;
|
|
}
|
|
}
|
|
#endif
|
|
/* Write the Bad Block Table(BBT) to flash */
|
|
Status = XNandPsu_EraseBlock(InstancePtr, 0U, Block);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
/* Write the BBT to page offset */
|
|
Offset = (u64)Desc->PageOffset[Target] *
|
|
(u64)InstancePtr->Geometry.BytesPerPage;
|
|
Status = XNandPsu_Write(InstancePtr, Offset, BbtLen, &Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Write the signature and version in the spare data area */
|
|
(void)memset(SpareBuf, 0xff, InstancePtr->Geometry.SpareBytesPerPage);
|
|
Status = XNandPsu_ReadSpareBytes(InstancePtr, Desc->PageOffset[Target],
|
|
&SpareBuf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
(void)Xil_MemCpy(SpareBuf + Desc->SigOffset, &Desc->Signature[0],
|
|
Desc->SigLength);
|
|
(void)memcpy(SpareBuf + Desc->VerOffset, &Desc->Version[Target], 1U);
|
|
|
|
Status = XNandPsu_WriteSpareBytes(InstancePtr,
|
|
Desc->PageOffset[Target], &SpareBuf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
Status = XST_SUCCESS;
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function updates the primary and mirror Bad Block Table(BBT) in the
|
|
* flash.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPsu instance.
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_UpdateBbt(XNandPsu *InstancePtr, u32 Target)
|
|
{
|
|
s32 Status;
|
|
u8 Version;
|
|
|
|
/* Update the version number */
|
|
Version = InstancePtr->BbtDesc.Version[Target];
|
|
InstancePtr->BbtDesc.Version[Target] = (u8)(((u16)Version +
|
|
(u16)1) % (u16)256U);
|
|
|
|
Version = InstancePtr->BbtMirrorDesc.Version[Target];
|
|
InstancePtr->BbtMirrorDesc.Version[Target] = (u8)(((u16)Version +
|
|
(u16)1) % (u16)256);
|
|
/* Update the primary Bad Block Table(BBT) in flash */
|
|
Status = XNandPsu_WriteBbt(InstancePtr, &InstancePtr->BbtDesc,
|
|
&InstancePtr->BbtMirrorDesc,
|
|
Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
/* Update the mirrored Bad Block Table(BBT) in flash */
|
|
Status = XNandPsu_WriteBbt(InstancePtr, &InstancePtr->BbtMirrorDesc,
|
|
&InstancePtr->BbtDesc,
|
|
Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
Status = XST_SUCCESS;
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function marks the block containing Bad Block Table as reserved
|
|
* and updates the BBT.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPsu instance.
|
|
* @param Desc is the BBT descriptor pointer.
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_MarkBbt(XNandPsu* InstancePtr, XNandPsu_BbtDesc *Desc,
|
|
u32 Target)
|
|
{
|
|
u32 BlockIndex;
|
|
u32 BlockOffset;
|
|
u8 BlockShift;
|
|
u8 OldVal;
|
|
u8 NewVal;
|
|
s32 Status;
|
|
u32 UpdateBbt = 0U;
|
|
u32 Index;
|
|
|
|
/* Mark the last four blocks as Reserved */
|
|
BlockIndex = ((Target + (u32)1) * InstancePtr->Geometry.NumTargetBlocks) -
|
|
Desc->MaxBlocks - (u32)1;
|
|
|
|
for(Index = 0U; Index < Desc->MaxBlocks; Index++) {
|
|
|
|
BlockOffset = BlockIndex >> XNANDPSU_BBT_BLOCK_SHIFT;
|
|
BlockShift = XNandPsu_BbtBlockShift(BlockIndex);
|
|
OldVal = InstancePtr->Bbt[BlockOffset];
|
|
NewVal = (u8) (OldVal | (XNANDPSU_BLOCK_RESERVED <<
|
|
BlockShift));
|
|
InstancePtr->Bbt[BlockOffset] = NewVal;
|
|
|
|
if (OldVal != NewVal) {
|
|
UpdateBbt = 1U;
|
|
}
|
|
BlockIndex++;
|
|
}
|
|
|
|
/* Update the BBT to flash */
|
|
if (UpdateBbt != 0U) {
|
|
Status = XNandPsu_UpdateBbt(InstancePtr, Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
Status = XST_SUCCESS;
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function checks whether a block is bad or not.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPsu instance.
|
|
*
|
|
* @param Block is the block number.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_IsBlockBad(XNandPsu *InstancePtr, u32 Block)
|
|
{
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY)
|
|
Xil_AssertNonvoid(Block < InstancePtr->Geometry.NumBlocks);
|
|
|
|
u8 Data;
|
|
u8 BlockShift;
|
|
u8 BlockType;
|
|
u32 BlockOffset;
|
|
s32 Status;
|
|
|
|
BlockOffset = Block >> XNANDPSU_BBT_BLOCK_SHIFT;
|
|
BlockShift = XNandPsu_BbtBlockShift(Block);
|
|
Data = InstancePtr->Bbt[BlockOffset]; /* Block information in BBT */
|
|
BlockType = (Data >> BlockShift) & XNANDPSU_BLOCK_TYPE_MASK;
|
|
|
|
if ((BlockType != XNANDPSU_BLOCK_GOOD) &&
|
|
(BlockType != XNANDPSU_BLOCK_RESERVED)) {
|
|
Status = XST_SUCCESS;
|
|
}
|
|
else {
|
|
Status = XST_FAILURE;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* 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 XNandPsu instance.
|
|
* @param Block is the block number.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_MarkBlockBad(XNandPsu *InstancePtr, u32 Block)
|
|
{
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY)
|
|
Xil_AssertNonvoid(Block < InstancePtr->Geometry.NumBlocks);
|
|
|
|
u8 Data;
|
|
u8 BlockShift;
|
|
u32 BlockOffset;
|
|
u8 OldVal;
|
|
u8 NewVal;
|
|
s32 Status;
|
|
u32 Target;
|
|
|
|
Target = Block / InstancePtr->Geometry.NumTargetBlocks;
|
|
|
|
BlockOffset = Block >> XNANDPSU_BBT_BLOCK_SHIFT;
|
|
BlockShift = XNandPsu_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 &= ~(XNANDPSU_BLOCK_TYPE_MASK << BlockShift);
|
|
Data |= (XNANDPSU_BLOCK_BAD << BlockShift);
|
|
NewVal = Data;
|
|
InstancePtr->Bbt[BlockOffset] = Data;
|
|
|
|
/* Update the Bad Block Table(BBT) in flash */
|
|
if (OldVal != NewVal) {
|
|
Status = XNandPsu_UpdateBbt(InstancePtr, Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
Status = XST_SUCCESS;
|
|
Out:
|
|
return Status;
|
|
}
|
|
/** @} */
|