embeddedsw/XilinxProcessorIPLib/drivers/nandps/src/xnandps.c
Punnaiah Choudary Kalluri 9350f7c80f nandps: Addd Modification history
Added modification history

Signed-off-by: Punnaiah Choudary Kalluri <punnaia@xilinx.com>
Acked-by: Anirudha Sarangi <anirudh@xilinx.com>
2014-09-02 11:21:16 +05:30

1963 lines
52 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.c
*
* This file contains the implementation of the interface functions for
* XNandPs driver. Refer to the header file xnandps.h for more detailed
* information.
*
* This module supports for NAND flash memory devices that conform to the
* "Open NAND Flash Interface" (ONFI) Specification. This modules implements
* basic flash operations like read, write and erase.
*
* @note None
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- ---------- -----------------------------------------------
* 1.00a nm 12/10/2010 First release
* 1.01a nm 28/02/2012 Fixed 16-bit issue with ONFI commands like
* read, write and read status command. The config
* structure width is updated after ONFI query
* with the parameter page width.
* 1.02a nm 20/09/2012 Removed setting of set_cycles and set_opmode
* register values as it is now done in FSBL using
* the PCW generated files. CR#678949.
* 1.03a nm 10/22/2012 Fixed CR# 673348.
* 1.04a nm 04/15/2013 Fixed CR# 704401. Removed warnings when compiled
* with -Wall and -Wextra option in bsp.
* 04/25/2013 Implemented PR# 699544. Added page cache read
* and program support. Added API's XNandPs_ReadCache
* and XNandPs_WriteCache for page cache support.
* Added ECC handling functions XNandPs_EccSetCfg,
* XNandPs_EccSetMemCmd1...etc, to support better
* usage of ECC block for page cache commands.
* Modified Read/Write API's so that there is common
* code for normal read/write and page cache commands.
* Disabling/Re-enabling ECC block in read/write API's
* of spare bytes since we don't calculate ECC for
* spare bytes.
* 2.01 kpc 07/24/2014 Fixed CR#808770. Update command register twice only
if flash device requires >= four address cycles.
* </pre>
*
******************************************************************************/
/***************************** Include Files *********************************/
#include "xnandps.h"
#include "xnandps_bbm.h"
#include "xnandps_onfi.h"
/************************** Constant Definitions *****************************/
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
static int XNandPs_EccHwInit(XNandPs *InstancePtr);
static int XNandPs_EccSwInit(XNandPs *InstancePtr);
static int XNandPs_ReadPage_HwEcc(XNandPs *InstancePtr, u8 *DstPtr);
static int XNandPs_ReadPage(XNandPs *InstancePtr, u8 *DstPtr);
static int XNandPs_WritePage_HwEcc(XNandPs *InstancePtr, u8 *SrcPtr);
static int XNandPs_WritePage(XNandPs *InstancePtr, u8 *SrcPtr);
static int XNandPs_EccCalculate(XNandPs *InstancePtr, u8 *EccData);
static int XNandPs_EccCorrect(u8 *Buf, u8 *EccCalc, u8 *EccCode);
static void XNandPs_ReadBuf(XNandPs *InstancePtr, u8 *Buf, u32 Length);
static void XNandPs_WriteBuf(XNandPs *InstancePtr, u8 *Buf, u32 Length);
static int XNandPs_IsBusy(XNandPs *InstancePtr);
void XNandPs_SendCommand(XNandPs *InstancePtr, XNandPs_CommandFormat
*Command, int Page, int Column);
static void XNandPs_EccSetCfg(XNandPs *InstancePtr, u32 EccConfig);
static void XNandPs_EccSetMemCmd1(XNandPs *InstancePtr, u32 EccCmd);
static void XNandPs_EccSetMemCmd2(XNandPs *InstancePtr, u32 EccCmd);
static void XNandPs_EccDisable(XNandPs *InstancePtr);
/* Bad block management routines */
extern void XNandPs_InitBbtDesc(XNandPs *InstancePtr);
extern int XNandPs_ScanBbt(XNandPs *InstancePtr);
/* ONFI routines */
extern u8 Onfi_CmdReadStatus(XNandPs *InstancePtr);
extern int Onfi_NandInit(XNandPs *InstancePtr);
/************************** Variable Definitions *****************************/
/* ECC data position in the spare data area for different page sizes */
u32 NandOob16[] = {13, 14, 15}; /**< Ecc position for 16 bytes spare area */
u32 NandOob32[] = {26, 27, 28, 29, 30, 31};
/**< Ecc position for 32 bytes spare area */
u32 NandOob64[] = {52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 63};
/**< Ecc position for 64 bytes spare area */
extern XNandPs_CommandFormat OnfiCommands[]; /**< ONFI commands */
/*****************************************************************************/
/**
*
* This function initializes a specific XNandPs device/instance. This function
* must be called prior to using the flash device to read or write any data.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param ConfigPtr points to the XNandPs device configuration structure.
* @param SmcBaseAddr is the base address of SMC controller.
* @param FlashBaseAddr is the base address of NAND flash.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note The user needs to first call the XNandPs_LookupConfig() API
* which returns the Configuration structure pointer which is
* passed as a parameter to the XNandPs_CfgInitialize() API.
*
******************************************************************************/
int XNandPs_CfgInitialize(XNandPs *InstancePtr, XNandPs_Config *ConfigPtr,
u32 SmcBaseAddr, u32 FlashBaseAddr)
{
u32 PageSize;
int Status;
/*
* Assert the input arguments.
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(ConfigPtr != NULL);
/*
* Set the values read from the device config and the base address.
*/
InstancePtr->Config.DeviceId = ConfigPtr->DeviceId;
InstancePtr->Config.SmcBase = SmcBaseAddr;
InstancePtr->Config.FlashBase = FlashBaseAddr;
InstancePtr->Config.FlashWidth = ConfigPtr->FlashWidth;
XNandPs_WriteReg(InstancePtr->Config.SmcBase +
XNANDPS_MEMC_CLR_CONFIG_OFFSET,
XNANDPS_CLR_CONFIG); /* Disable interrupts */
/*
* ONFI query to get geometry
*/
Status = Onfi_NandInit(InstancePtr);
if (Status != XST_SUCCESS) {
return Status;
}
/*
* Updating Config structure flash width
*/
InstancePtr->Config.FlashWidth = InstancePtr->Geometry.FlashWidth;
/*
* Fill the spare buffer pointer
*/
PageSize = InstancePtr->Geometry.BytesPerPage;
InstancePtr->SpareBufPtr = &InstancePtr->DataBuf[PageSize];
/*
* Initialize ECC Block Parameters
*/
switch (InstancePtr->EccMode)
{
case XNANDPS_ECC_NONE:
/* Fall through */
case XNANDPS_ECC_ONDIE:
/* Bypass the ECC block in the SMC controller */
XNandPs_EccDisable(InstancePtr);
/* Initialize the Read/Write page routines */
InstancePtr->ReadPage = XNandPs_ReadPage;
InstancePtr->WritePage = XNandPs_WritePage;
break;
case XNANDPS_ECC_HW:
/* Use SMC ECC controller ECC block */
Status = XNandPs_EccHwInit(InstancePtr);
if (Status != XST_SUCCESS)
return Status;
/* Initialize ECC SW parameters */
Status = XNandPs_EccSwInit(InstancePtr);
if (Status != XST_SUCCESS)
return Status;
/* Initialize the Read/Write page routines */
InstancePtr->ReadPage = XNandPs_ReadPage_HwEcc;
InstancePtr->WritePage = XNandPs_WritePage_HwEcc;
break;
default:
return XST_FAILURE;
}
/*
* Indicate the instance is now ready to use, initialized without error.
*/
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
/*
* Scan for the bad block table(bbt) stored in the flash & load it in
* memory(RAM). If bbt is not found, create bbt by scanning factory
* marked bad blocks and store it in last good blocks of flash.
*/
XNandPs_InitBbtDesc(InstancePtr);
Status = XNandPs_ScanBbt(InstancePtr);
if (Status != XST_SUCCESS) {
return Status;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function initializes the HW ECC block based on flash.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param EccConfig is the value of ECC config to update.
*
* @return None
*
* @note None
*
*****************************************************************************/
static void XNandPs_EccSetCfg(XNandPs *InstancePtr, u32 EccConfig)
{
/*
* Check the busy status of the ECC block
*/
while (XNandPs_ReadReg(InstancePtr->Config.SmcBase +
XNANDPS_ECC_STATUS_OFFSET(XNANDPS_IF1_ECC_OFFSET)) &
XNANDPS_ECC_STATUS_MASK);
/*
* Write ECC configuration register
*/
XNandPs_WriteReg(InstancePtr->Config.SmcBase +
(XNANDPS_ECC_MEMCFG_OFFSET(XNANDPS_IF1_ECC_OFFSET)),
EccConfig);
}
/*****************************************************************************/
/**
*
* This function writes ECC MEM CMD1 register with EccCmd value.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param EccCmd is register value to write.
*
* @return None
*
* @note None
*
*****************************************************************************/
static void XNandPs_EccSetMemCmd1(XNandPs *InstancePtr, u32 EccCmd)
{
/*
* Set the ECC mem command1 register
*/
XNandPs_WriteReg(InstancePtr->Config.SmcBase +
(XNANDPS_ECC_MEMCMD1_OFFSET(XNANDPS_IF1_ECC_OFFSET)),
EccCmd);
}
/*****************************************************************************/
/**
*
* This function writes ECC MEM CMD2 register with EccCmd value.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param EccCmd is register value to write.
*
* @return None
*
* @note None
*
*****************************************************************************/
static void XNandPs_EccSetMemCmd2(XNandPs *InstancePtr, u32 EccCmd)
{
/*
* Set the ECC mem command2 register
*/
XNandPs_WriteReg(InstancePtr->Config.SmcBase +
(XNANDPS_ECC_MEMCMD2_OFFSET(XNANDPS_IF1_ECC_OFFSET)),
EccCmd);
}
/*****************************************************************************/
/**
*
* This function disables ECC block.
*
* @param InstancePtr is a pointer to the XNandPs instance.
*
* @return None
*
* @note None
*
*****************************************************************************/
static void XNandPs_EccDisable(XNandPs *InstancePtr)
{
u32 EccConfig = 0;
/*
* Bypass the ECC block in the SMC controller
*/
EccConfig = XNandPs_ReadReg(InstancePtr->Config.SmcBase +
(XNANDPS_ECC_MEMCFG_OFFSET(XNANDPS_IF1_ECC_OFFSET)));
EccConfig &= ~XNANDPS_ECC_MEMCFG_ECC_MODE_MASK;
XNandPs_EccSetCfg(InstancePtr, EccConfig);
}
/*****************************************************************************/
/**
*
* This function initializes the HW ECC block based on flash.
*
* @param InstancePtr is a pointer to the XNandPs instance.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if the flash is not supported.
*
* @note None
*
*****************************************************************************/
static int XNandPs_EccHwInit(XNandPs *InstancePtr)
{
u32 PageSize;
u32 EccConfig = 0;
PageSize = InstancePtr->Geometry.BytesPerPage;
/*
* Set the ECC mem command1 and ECC mem command2 register
*/
XNandPs_EccSetMemCmd1(InstancePtr, XNANDPS_ECC_CMD1);
XNandPs_EccSetMemCmd2(InstancePtr, XNANDPS_ECC_CMD2);
/*
* Configure HW ECC block
*/
switch(PageSize) {
case XNANDPS_PAGE_SIZE_512:
EccConfig = (XNANDPS_ECC_MEMCFG |
XNANDPS_ECC_MEMCFG_PAGE_SIZE_512);
break;
case XNANDPS_PAGE_SIZE_1024:
EccConfig = (XNANDPS_ECC_MEMCFG |
XNANDPS_ECC_MEMCFG_PAGE_SIZE_1024);
break;
case XNANDPS_PAGE_SIZE_2048:
EccConfig = (XNANDPS_ECC_MEMCFG |
XNANDPS_ECC_MEMCFG_PAGE_SIZE_2048);
break;
default:
/*
* Page size 256 bytes & 4096 bytes not supported
* by ECC block
*/
return XST_FAILURE;
}
XNandPs_EccSetCfg(InstancePtr, EccConfig);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function initializes the software variables related
* to ECC generation, ECC checking and writing ECC bytes in spare bytes.
*
* @param InstancePtr is a pointer to the XNandPs instance.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if the flash is not supported.
*
* @note None
*
*****************************************************************************/
static int XNandPs_EccSwInit(XNandPs *InstancePtr)
{
u32 PageSize;
u32 SpareBytesSize;
u32 Index;
PageSize = InstancePtr->Geometry.BytesPerPage;
SpareBytesSize = InstancePtr->Geometry.SpareBytesPerPage;
/*
* Initialize ECC config structure parameters
*/
InstancePtr->EccConfig.BytesPerBlock = XNANDPS_ECC_BYTES;
InstancePtr->EccConfig.BlockSize = XNANDPS_ECC_BLOCK_SIZE;
InstancePtr->EccConfig.TotalBytes = (PageSize/XNANDPS_ECC_BLOCK_SIZE)
* XNANDPS_ECC_BYTES;
InstancePtr->EccConfig.NumSteps = PageSize/XNANDPS_ECC_BLOCK_SIZE;
/*
* Ecc write position in spare data area as per Linux mtd subsystem
*/
switch(SpareBytesSize) {
case XNANDPS_SPARE_SIZE_16:
for(Index = 0; Index <
InstancePtr->EccConfig.TotalBytes;
Index++) {
InstancePtr->EccConfig.EccPos[Index] =
NandOob16[Index];
}
break;
case XNANDPS_SPARE_SIZE_32:
for(Index = 0; Index <
InstancePtr->EccConfig.TotalBytes;
Index++) {
InstancePtr->EccConfig.EccPos[Index] =
NandOob32[Index];
}
break;
case XNANDPS_SPARE_SIZE_64:
for(Index = 0; Index <
InstancePtr->EccConfig.TotalBytes;
Index++) {
InstancePtr->EccConfig.EccPos[Index] =
NandOob64[Index];
}
break;
default:
return XST_FAILURE;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function reads the data from the Flash device and copies it into the
* specified user buffer. It doesn't check for the bad blocks while reading
* the flash pages that cross block boundary. User must take care of handling
* bad blocks.
*
* @param InstancePtr is the pointer to the XNandPs instance.
* @param Offset is the flash data address to read from.
* @param Length is number of bytes to read.
* @param DestPtr is the destination address to copy data to.
* @param UserSparePtr is the user buffer to which spare data must be
* copied.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note This function reads sequential pages from the Flash device.
*
******************************************************************************/
int XNandPs_Read(XNandPs *InstancePtr, u64 Offset, u32 Length, void *DestPtr,
u8 *UserSparePtr)
{
u32 Page;
u32 Col;
u32 PartialBytes;
u32 NumOfBytes;
int Status;
u32 PartialPageRead = 0;
u32 CopyOffset;
u8 *BufPtr;
u8 *Ptr = (u8 *)DestPtr;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(DestPtr != NULL);
Xil_AssertNonvoid((Offset + Length) < InstancePtr->Geometry.DeviceSize);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid(Length != 0);
Page = (u32) (Offset/InstancePtr->Geometry.BytesPerPage);
Col = (u32) (Offset & (InstancePtr->Geometry.BytesPerPage - 1));
PartialBytes = InstancePtr->Geometry.BytesPerPage - Col;
NumOfBytes = (PartialBytes < Length) ? PartialBytes:Length;
CopyOffset = InstancePtr->Geometry.BytesPerPage - PartialBytes;
/*
* Restore the ECC mem command1 and ECC mem command2 register
* if the previous command is read page cache.
*/
XNandPs_EccSetMemCmd1(InstancePtr, XNANDPS_ECC_CMD1);
XNandPs_EccSetMemCmd2(InstancePtr, XNANDPS_ECC_CMD2);
while (Length) {
/*
* Check if partial read
*/
if (NumOfBytes < InstancePtr->Geometry.BytesPerPage) {
BufPtr = &InstancePtr->DataBuf[0];
PartialPageRead = 1;
} else {
BufPtr = (u8 *)Ptr;
PartialPageRead = 0;
}
/*
* Send the ONFI Read command
*/
XNandPs_SendCommand(InstancePtr, &OnfiCommands[READ], Page, 0);
/*
* Poll the Memory controller status register
*/
while (XNandPs_IsBusy(InstancePtr) == TRUE) {
}
/*
* Clear the interrupt condition
*/
XNandPs_WriteReg((InstancePtr->Config.SmcBase +
XNANDPS_MEMC_CLR_CONFIG_OFFSET),
XNANDPS_MEMC_CLR_CONFIG_INT_CLR1_MASK);
/*
* Read the page data
*/
Status = InstancePtr->ReadPage(InstancePtr, BufPtr);
if (Status != XST_SUCCESS) {
return Status;
}
/*
* Fill the partial data in the buffer
*/
if (PartialPageRead) {
memcpy(Ptr, BufPtr + CopyOffset, NumOfBytes);
}
Ptr += NumOfBytes;
Length -= NumOfBytes;
Page++;
NumOfBytes = (Length > InstancePtr->Geometry.BytesPerPage) ?
InstancePtr->Geometry.BytesPerPage:Length;
CopyOffset = 0;
}
/*
* Copy the spare data to user spare buffer
*/
if (UserSparePtr) {
memcpy(UserSparePtr, InstancePtr->SpareBufPtr,
InstancePtr->Geometry.SpareBytesPerPage);
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function reads the data from the Flash device using read page cache
* command and copies it into the specified user buffer.
* It doesn't check for the bad blocks while reading the flash pages that
* cross block boundary. User must take care of handling bad blocks.
*
* @param InstancePtr is the pointer to the XNandPs instance.
* @param Offset is the flash data address to read from.
* @param Length is number of bytes to read.
* @param DestPtr is the destination address to copy data to.
* @param UserSparePtr is the user buffer to which spare data must be
* copied.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note This function reads sequential pages from the Flash device.
*
******************************************************************************/
int XNandPs_ReadCache(XNandPs *InstancePtr, u64 Offset, u32 Length,
void *DestPtr, u8 *UserSparePtr)
{
u32 Page;
u32 Col;
u32 PartialBytes;
u32 NumOfBytes;
int Status;
u32 PartialPageRead = 0;
u32 CopyOffset;
u8 *BufPtr;
u8 *Ptr = (u8 *)DestPtr;
u32 NumPages;
u32 EccConfig = 0;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(DestPtr != NULL);
Xil_AssertNonvoid((Offset + Length) < InstancePtr->Geometry.DeviceSize);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid(Length != 0);
/*
* Check if the flash supports read cache
*/
if (!InstancePtr->Features.ReadCache) {
return XNandPs_Read(InstancePtr, Offset, Length, DestPtr,
UserSparePtr);
}
Page = (u32) (Offset/InstancePtr->Geometry.BytesPerPage);
Col = (u32) (Offset & (InstancePtr->Geometry.BytesPerPage - 1));
PartialBytes = InstancePtr->Geometry.BytesPerPage - Col;
NumOfBytes = (PartialBytes < Length) ? PartialBytes:Length;
CopyOffset = InstancePtr->Geometry.BytesPerPage - PartialBytes;
/*
* Calculate number of pages to read
*/
NumPages = Length/InstancePtr->Geometry.BytesPerPage;
NumPages += (Length % InstancePtr->Geometry.BytesPerPage) ? 1:0;
/*
* Read, Read Cache start, Read Cache end
*/
if (NumPages <= 1) {
return XNandPs_Read(InstancePtr, Offset, Length, DestPtr,
UserSparePtr);
}
/*
* Change ECC commands in ECC registers for page cache support
*/
EccConfig |= ONFI_CMD_PAGE_CACHE_PROGRAM1;
EccConfig |= ONFI_CMD_READ_CACHE_ENHANCED1 << 8;
EccConfig |= ONFI_CMD_READ_CACHE_ENHANCED2 << 16;
EccConfig |= (XNANDPS_ECC_MEMCOMMAND1_RD_CMD_END_VALID_MASK);
XNandPs_EccSetMemCmd1(InstancePtr, EccConfig);
/*
* Send the ONFI Read command
*/
XNandPs_SendCommand(InstancePtr, &OnfiCommands[READ], Page, 0);
/*
* Poll the Memory controller status register
*/
while (XNandPs_IsBusy(InstancePtr) == TRUE) {
}
/*
* Clear the interrupt condition
*/
XNandPs_WriteReg((InstancePtr->Config.SmcBase +
XNANDPS_MEMC_CLR_CONFIG_OFFSET),
XNANDPS_MEMC_CLR_CONFIG_INT_CLR1_MASK);
/*
* Check ONFI Status Register
*/
Status = Onfi_CmdReadStatus(InstancePtr);
if (Status & ONFI_STATUS_FAIL) {
return XST_FAILURE;
}
while (Length && (NumPages > 0)) {
/*
* Check if partial read
*/
if (NumOfBytes < InstancePtr->Geometry.BytesPerPage) {
BufPtr = &InstancePtr->DataBuf[0];
PartialPageRead = 1;
} else {
BufPtr = (u8 *)Ptr;
PartialPageRead = 0;
}
/* Increment the page */
Page++;
if (NumPages <= 1) {
/*
* Change ECC commands in ECC registers to check
* change read column for ECC calculation
*/
EccConfig = 0;
EccConfig |= ONFI_CMD_PAGE_CACHE_PROGRAM1;
EccConfig |= ONFI_CMD_CHANGE_READ_COLUMN1 << 8;
EccConfig |= ONFI_CMD_CHANGE_READ_COLUMN2 << 16;
EccConfig |=
XNANDPS_ECC_MEMCOMMAND1_RD_CMD_END_VALID_MASK;
XNandPs_EccSetMemCmd1(InstancePtr, EccConfig);
/*
* Send NAND page cache end command 0x3F
*/
XNandPs_SendCommand(InstancePtr,
&OnfiCommands[READ_CACHE_END_SEQ],
XNANDPS_PAGE_NOT_VALID,
XNANDPS_COLUMN_NOT_VALID);
} else {
XNandPs_SendCommand(InstancePtr,
&OnfiCommands[READ_CACHE_RANDOM],
Page, 0);
}
/*
* Poll the Memory controller status register
*/
while (XNandPs_IsBusy(InstancePtr) == TRUE) {
}
/*
* Clear the interrupt condition
*/
XNandPs_WriteReg((InstancePtr->Config.SmcBase +
XNANDPS_MEMC_CLR_CONFIG_OFFSET),
XNANDPS_MEMC_CLR_CONFIG_INT_CLR1_MASK);
if (NumPages <= 1) {
XNandPs_SendCommand(InstancePtr,
&OnfiCommands[CHANGE_READ_COLUMN],
XNANDPS_PAGE_NOT_VALID,
0);
}
/*
* Read the page data
*/
Status = InstancePtr->ReadPage(InstancePtr, BufPtr);
if (Status != XST_SUCCESS) {
return Status;
}
/*
* Fill the partial data in the buffer
*/
if (PartialPageRead) {
memcpy(Ptr, BufPtr + CopyOffset, NumOfBytes);
}
Ptr += NumOfBytes;
Length -= NumOfBytes;
NumPages--;
NumOfBytes = (Length > InstancePtr->Geometry.BytesPerPage) ?
InstancePtr->Geometry.BytesPerPage:Length;
CopyOffset = 0;
}
/*
* Copy the spare data to user spare buffer
*/
if (UserSparePtr) {
memcpy(UserSparePtr, InstancePtr->SpareBufPtr,
InstancePtr->Geometry.SpareBytesPerPage);
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function programs the flash device(s) with data specified in the user
* buffer. The source and destination address must be aligned to the width of the
* flash's data bus. It doesn't check for the bad blocks while writing to
* the flash pages that cross block boundary. User must take care of handling
* bad blocks.
*
* @param InstancePtr is the pointer to the XNandPs instance.
* @param Offset is the flash data address to write to.
* @param Length is number of bytes to write.
* @param SrcPtr is the source address to write the data from.
* @param UserSparePtr is the user buffer which contains buffer to write
* into spare data area.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
* - XST_NAND_WRITE_PROTECTED if the flash is write protected.
*
* @note This function writes number of sequential pages into the
* Flash device.
*
******************************************************************************/
int XNandPs_Write(XNandPs *InstancePtr, u64 Offset, u32 Length, void *SrcPtr,
u8 *UserSparePtr)
{
u32 Page;
u32 Col;
u32 PartialBytes;
u32 NumOfBytes;
u32 CopyOffset;
u32 Status;
u8 *BufPtr;
u8 OnfiStatus;
u8 *Ptr = (u8 *)SrcPtr;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(SrcPtr != NULL);
Xil_AssertNonvoid((Offset + Length) < InstancePtr->Geometry.DeviceSize);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid(Length != 0);
/*
* Check if the flash is write protected
*/
OnfiStatus = Onfi_CmdReadStatus(InstancePtr);
if (!(OnfiStatus & ONFI_STATUS_WP)) {
return XST_NAND_WRITE_PROTECTED;
}
/*
* Copy the user spare data buffer
*/
if (UserSparePtr == NULL) {
memset(InstancePtr->SpareBufPtr, 0xff,
InstancePtr->Geometry.SpareBytesPerPage);
} else {
memcpy(InstancePtr->SpareBufPtr, UserSparePtr,
InstancePtr->Geometry.SpareBytesPerPage);
}
Page = (u32) (Offset/InstancePtr->Geometry.BytesPerPage);
Col = (u32) (Offset & (InstancePtr->Geometry.BytesPerPage - 1));
PartialBytes = InstancePtr->Geometry.BytesPerPage - Col;
NumOfBytes = (PartialBytes < Length) ? PartialBytes:Length;
CopyOffset = InstancePtr->Geometry.BytesPerPage - PartialBytes;
while (Length)
{
/*
* Partial write, fill the remaining buffer with 0xff
*/
if (NumOfBytes < InstancePtr->Geometry.BytesPerPage) {
BufPtr = &InstancePtr->DataBuf[0];
memset(BufPtr, 0xff,
InstancePtr->Geometry.BytesPerPage);
memcpy(BufPtr + CopyOffset, Ptr, NumOfBytes);
} else {
BufPtr = (u8 *)Ptr;
}
/*
* Send ONFI Program command
*/
XNandPs_SendCommand(InstancePtr, &OnfiCommands[PAGE_PROGRAM],
Page, 0);
/*
* Write the page data
*/
Status = InstancePtr->WritePage(InstancePtr, BufPtr);
if (Status != XST_SUCCESS) {
return Status;
}
Ptr += NumOfBytes;
Length -= NumOfBytes;
Page++;
NumOfBytes = (Length > InstancePtr->Geometry.BytesPerPage) ?
InstancePtr->Geometry.BytesPerPage:Length;
CopyOffset = 0;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function programs the flash device(s) with data specified in the user
* buffer using program cache command.
* The source and destination address must be aligned to the width of the
* flash's data bus. It doesn't check for the bad blocks while writing to
* the flash pages that cross block boundary. User must take care of handling
* bad blocks.
*
* @param InstancePtr is the pointer to the XNandPs instance.
* @param Offset is the flash data address to write to.
* @param Length is number of bytes to write.
* @param SrcPtr is the source address to write the data from.
* @param UserSparePtr is the user buffer which contains buffer to write
* into spare data area.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
* - XST_NAND_WRITE_PROTECTED if the flash is write protected.
*
* @note This function writes number of sequential pages into the
* Flash device.
*
******************************************************************************/
int XNandPs_WriteCache(XNandPs *InstancePtr, u64 Offset, u32 Length,
void *SrcPtr, u8 *UserSparePtr)
{
u32 Page;
u32 Col;
u32 PartialBytes;
u32 NumOfBytes;
u32 CopyOffset;
u32 Status;
u8 *BufPtr;
u8 OnfiStatus;
u8 *Ptr = (u8 *)SrcPtr;
u32 NumPages;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(SrcPtr != NULL);
Xil_AssertNonvoid((Offset + Length) < InstancePtr->Geometry.DeviceSize);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid(Length != 0);
/*
* Check if the flash is write protected
*/
OnfiStatus = Onfi_CmdReadStatus(InstancePtr);
if (!(OnfiStatus & ONFI_STATUS_WP)) {
return XST_NAND_WRITE_PROTECTED;
}
/*
* Copy the user spare data buffer
*/
if (UserSparePtr == NULL) {
memset(InstancePtr->SpareBufPtr, 0xff,
InstancePtr->Geometry.SpareBytesPerPage);
} else {
memcpy(InstancePtr->SpareBufPtr, UserSparePtr,
InstancePtr->Geometry.SpareBytesPerPage);
}
Page = (u32) (Offset/InstancePtr->Geometry.BytesPerPage);
Col = (u32) (Offset & (InstancePtr->Geometry.BytesPerPage - 1));
PartialBytes = InstancePtr->Geometry.BytesPerPage - Col;
NumOfBytes = (PartialBytes < Length) ? PartialBytes:Length;
CopyOffset = InstancePtr->Geometry.BytesPerPage - PartialBytes;
/*
* Calculate number of pages to write
*/
NumPages = Length/InstancePtr->Geometry.BytesPerPage;
NumPages += (Length % InstancePtr->Geometry.BytesPerPage) ? 1:0;
/*
* Check for enough pages for cache programming
*/
if (NumPages <= 1) {
return XNandPs_Write(InstancePtr, Offset, Length, SrcPtr,
UserSparePtr);
}
while (Length && (NumPages > 0))
{
/*
* Partial write, fill the remaining buffer with 0xff
*/
if (NumOfBytes < InstancePtr->Geometry.BytesPerPage) {
BufPtr = &InstancePtr->DataBuf[0];
memset(BufPtr, 0xff,
InstancePtr->Geometry.BytesPerPage);
memcpy(BufPtr + CopyOffset, Ptr, NumOfBytes);
} else {
BufPtr = (u8 *)Ptr;
}
if (NumPages > 1) {
/*
* Send ONFI Program cache command
*/
XNandPs_SendCommand(InstancePtr,
&OnfiCommands[PAGE_CACHE_PROGRAM],
Page, 0);
/*
* Write the page data
*/
Status = InstancePtr->WritePage(InstancePtr, BufPtr);
if (Status != XST_SUCCESS) {
return Status;
}
} else {
/*
* Send ONFI Program command
*/
XNandPs_SendCommand(InstancePtr,
&OnfiCommands[PAGE_PROGRAM],
Page, 0);
/*
* Write the page data
*/
Status = InstancePtr->WritePage(InstancePtr, BufPtr);
if (Status != XST_SUCCESS) {
return Status;
}
}
Ptr += NumOfBytes;
Length -= NumOfBytes;
Page++;
NumPages--;
NumOfBytes = (Length > InstancePtr->Geometry.BytesPerPage) ?
InstancePtr->Geometry.BytesPerPage:Length;
CopyOffset = 0;
}
return XST_SUCCESS;
}
/**************************************************************************/
/**
*
* This function sends a NAND command to the flash device.
*
* @param InstancePtr is the pointer to XNandPs struture
* @param Command is the NAND command to send
* @param Page is the page offset required for specific commands
* @param Column the column offset required for specific commands
*
* @return None
*
* @note None
*
***************************************************************************/
void XNandPs_SendCommand(XNandPs *InstancePtr, XNandPs_CommandFormat
*Command, int Page, int Column)
{
u32 EndCmdReq = 0;
u32 EccLast = 0;
u32 ClearCs = 0;
u32 CmdPhaseAddr;
u32 DataPhaseAddr;
u32 CmdPhaseData=0;
u32 PageShift;
Xil_AssertVoid(Command != NULL);
if (Command->EndCmdValid == XNANDPS_CMD_PHASE) {
EndCmdReq = 1;
}
/*
* Construct command phase address
*/
CmdPhaseAddr = InstancePtr->Config.FlashBase |
(Command->AddrCycles << XNANDPS_ADDR_CYCLES_SHIFT) |
(EndCmdReq << XNANDPS_END_CMD_VALID_SHIFT) |
XNANDPS_COMMAND_PHASE_MASK |
(Command->EndCmd << XNANDPS_END_CMD_SHIFT) |
(Command->StartCmd << XNANDPS_START_CMD_SHIFT);
InstancePtr->CommandPhaseAddr = CmdPhaseAddr;
EndCmdReq = 0;
/*
* Some NAND commands require end command to be sent after data phase
*/
if (Command->EndCmdValid == XNANDPS_DATA_PHASE) {
EndCmdReq = 1;
}
/*
* Construct data phase address
*/
DataPhaseAddr = InstancePtr->Config.FlashBase |
(ClearCs << XNANDPS_CLEAR_CS_SHIFT) |
(EndCmdReq << XNANDPS_END_CMD_VALID_SHIFT) |
XNANDPS_DATA_PHASE_MASK |
(Command->EndCmd << XNANDPS_END_CMD_SHIFT) |
(EccLast << XNANDPS_ECC_LAST_SHIFT);
InstancePtr->DataPhaseAddr = DataPhaseAddr;
/*
* Command phase data
*/
if (Column != XNANDPS_COLUMN_NOT_VALID && Page !=
XNANDPS_PAGE_NOT_VALID) {
if (InstancePtr->Geometry.FlashWidth ==
XNANDPS_FLASH_WIDTH_16) {
Column >>= 1;
}
CmdPhaseData = Column;
PageShift = InstancePtr->Geometry.ColAddrCycles * 8;
CmdPhaseData |= Page << PageShift;
if (Command->AddrCycles > 4) {
/*
* Send lower bytes of page address in first address
* cycle
*/
XNandPs_WriteReg(CmdPhaseAddr, CmdPhaseData);
/*
* Send the upper bytes of the page address in second
* address cycle
*/
CmdPhaseData = Page >> (32 - PageShift);
}
} else if (Page != XNANDPS_PAGE_NOT_VALID) {
CmdPhaseData = Page;
} else {
if (InstancePtr->Geometry.FlashWidth ==
XNANDPS_FLASH_WIDTH_16) {
Column >>= 1;
}
CmdPhaseData = Column;
}
/*
* Send command phase
*/
XNandPs_WriteReg(CmdPhaseAddr, CmdPhaseData);
}
/*****************************************************************************/
/**
*
* This function reads the spare area of a page.
*
* @param InstancePtr is the pointer to the XNandPs instance.
* @param Page is the page number from where spare data is read.
* @param Buf is pointer to the buffer where the spare data is filled.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note None.
*
******************************************************************************/
int XNandPs_ReadSpareBytes(XNandPs *InstancePtr, u32 Page, u8 *Buf)
{
u32 Col;
u32 Length;
u32 DataPhaseAddr;
u32 ZeroCommand;
u32 Status;
Xil_AssertNonvoid(Buf != NULL);
/*
* Bypass the ECC block in the SMC controller since
* we don't calculate ECC for spare bytes.
*/
if (InstancePtr->EccMode == XNANDPS_ECC_HW) {
XNandPs_EccDisable(InstancePtr);
}
Col = InstancePtr->Geometry.BytesPerPage;
Length = InstancePtr->Geometry.SpareBytesPerPage;
XNandPs_SendCommand(InstancePtr, &OnfiCommands[READ], Page, Col);
/*
* Poll the Memory controller status register for BUSY input signal
*/
while (XNandPs_IsBusy(InstancePtr) == TRUE) {
}
/*
* Clear the interrupt condition
*/
XNandPs_WriteReg((InstancePtr->Config.SmcBase +
XNANDPS_MEMC_CLR_CONFIG_OFFSET),
XNANDPS_MEMC_CLR_CONFIG_INT_CLR1_MASK);
/*
* Check ONFI Status Register
*/
Status = Onfi_CmdReadStatus(InstancePtr);
if (Status & ONFI_STATUS_FAIL) {
return XST_FAILURE;
}
/*
* ONFI : Reissue the 0x00 on the command line to start
* reading data
*/
ZeroCommand = InstancePtr->Config.FlashBase |
(0 << XNANDPS_ADDR_CYCLES_SHIFT)|
(0 << XNANDPS_END_CMD_VALID_SHIFT)|
(XNANDPS_COMMAND_PHASE_MASK)|
(0 << XNANDPS_END_CMD_SHIFT)|
(0 << XNANDPS_START_CMD_SHIFT);
/*
* AXI transaction for sending command 0x00 to the flash
*/
Xil_Out32(ZeroCommand, 0x00);
/*
* Read the spare data
*/
XNandPs_ReadBuf(InstancePtr, Buf, (Length - XNANDPS_AXI_DATA_WIDTH));
/*
* Clear chip select for last AXI transaction
*/
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr |= XNANDPS_CLR_CS;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
Buf += (Length - XNANDPS_AXI_DATA_WIDTH);
XNandPs_ReadBuf(InstancePtr, Buf, XNANDPS_AXI_DATA_WIDTH);
/*
* Re-enable ECC block in the SMC controller
*/
if (InstancePtr->EccMode == XNANDPS_ECC_HW) {
Status = XNandPs_EccHwInit(InstancePtr);
if (Status != XST_SUCCESS)
return XST_FAILURE;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function write to the spare area of a page.
*
* @param InstancePtr is the pointer to the XNandPs instance.
* @param Page is the page number to write.
* @param Buf is pointer to the buffer which holds the data.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note None.
*
******************************************************************************/
int XNandPs_WriteSpareBytes(XNandPs *InstancePtr, u32 Page, u8 *Buf)
{
u32 Col;
u32 Length;
u32 DataPhaseAddr;
u32 Status;
Xil_AssertNonvoid(Buf != NULL);
/*
* Bypass the ECC block in the SMC controller since
* we don't calculate ECC for spare bytes.
*/
if (InstancePtr->EccMode == XNANDPS_ECC_HW) {
XNandPs_EccDisable(InstancePtr);
}
Col = InstancePtr->Geometry.BytesPerPage;
Length = InstancePtr->Geometry.SpareBytesPerPage;
XNandPs_SendCommand(InstancePtr, &OnfiCommands[PAGE_PROGRAM], Page, Col);
/*
* Write to the spare area
*/
XNandPs_WriteBuf(InstancePtr, Buf, (Length -
XNANDPS_AXI_DATA_WIDTH));
/*
* Last transaction clear chip select
*/
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr |= XNANDPS_CLR_CS;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
Buf += (Length - XNANDPS_AXI_DATA_WIDTH);
XNandPs_WriteBuf(InstancePtr, Buf, XNANDPS_AXI_DATA_WIDTH);
/*
* Poll the Memory controller status register for BUSY input signal
*/
while (XNandPs_IsBusy(InstancePtr) == TRUE) {
}
/*
* Clear the interrupt condition
*/
XNandPs_WriteReg((InstancePtr->Config.SmcBase +
XNANDPS_MEMC_CLR_CONFIG_OFFSET),
XNANDPS_MEMC_CLR_CONFIG_INT_CLR1_MASK);
/*
* Check SR[0] bit
*/
Status = Onfi_CmdReadStatus(InstancePtr);
if (Status & ONFI_STATUS_FAIL) {
return XST_FAILURE;
}
/*
* Re-enable ECC block in the SMC controller
*/
if (InstancePtr->EccMode == XNANDPS_ECC_HW) {
Status = XNandPs_EccHwInit(InstancePtr);
if (Status != XST_SUCCESS)
return XST_FAILURE;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function checks whether SMC controller busy in processing a request.
*
* @param InstancePtr is a pointer to the XNandPs instance.
*
* @return - TRUE if SMC is busy
* - FALSE if SMC is free
*
* @note None.
*
******************************************************************************/
static int XNandPs_IsBusy(XNandPs *InstancePtr)
{
u32 Status;
/*
* Read the memory controller status register
*/
Status = XNandPs_ReadReg(InstancePtr->Config.SmcBase +
XNANDPS_MEMC_STATUS_OFFSET) &
XNANDPS_MEMC_STATUS_RAW_INT_STATUS1_MASK;
if (Status) {
return FALSE;
} else {
return TRUE;
}
}
/*****************************************************************************/
/**
*
* This function calculates the ECC value from the ECC registers.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param EccData is the buffer to fill the ECC value.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note None
*
******************************************************************************/
static int XNandPs_EccCalculate(XNandPs *InstancePtr, u8 *EccData)
{
u32 EccReg;
u32 EccValue;
u32 EccByte;
u32 EccStatus;
/*
* Check the busy status of the ECC block
*/
while (XNandPs_ReadReg(InstancePtr->Config.SmcBase +
XNANDPS_ECC_STATUS_OFFSET(XNANDPS_IF1_ECC_OFFSET)) &
XNANDPS_ECC_STATUS_MASK);
for(EccReg = 0; EccReg < 4; EccReg++) {
EccValue = XNandPs_ReadReg(InstancePtr->Config.SmcBase +
XNANDPS_ECC_VALUE0_OFFSET(XNANDPS_IF1_ECC_OFFSET +
(EccReg * 4)));
EccStatus = (EccValue >> 24) & 0xFF;
/*
* Check if the ECC value not valid
*/
if ((EccStatus >> 6) & 0x1) {
for(EccByte = 0; EccByte < 3; EccByte++) {
*EccData = EccValue & 0xFF;
EccValue = EccValue >> 8;
EccData++;
}
} else {
return XST_FAILURE;
}
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function corrects the ECC errors.
*
* @param Buf is the buffer which holds the data read from the page.
* @param EccCalc is the calculated ECC value.
* @param EccCode is the ECC read from the spare area.
*
* @return
* - XST_SUCCESS if the ECC error is corrected.
* - XST_FAILURE if the ECC error is not corrected.
*
* @note None
*
******************************************************************************/
static int XNandPs_EccCorrect(u8 *Buf, u8 *EccCalc, u8 *EccCode)
{
u8 BitPos;
u32 BytePos;
u16 EccOdd, EccEven;
u16 ReadEccLow, ReadEccHigh;
u16 CalcEccLow, CalcEccHigh;
/*
* Lower 12 bits of ECC Read
*/
ReadEccLow = (EccCode[0] | (EccCode[1] << 8)) & 0xfff;
/*
* Upper 12 bits of ECC Read
*/
ReadEccHigh = ((EccCode[1] >> 4) | (EccCode[2] << 4)) & 0xfff;
/*
* Lower 12 bits of ECC calculated
*/
CalcEccLow = (EccCalc[0] | (EccCalc[1] << 8)) & 0xfff;
/*
* Upper 12 bits of ECC Calculated
*/
CalcEccHigh = ((EccCalc[1] >> 4) | (EccCalc[2] << 4)) & 0xfff;
EccOdd = ReadEccLow ^ CalcEccLow;
EccEven = ReadEccHigh ^ CalcEccHigh;
/*
* No Error
*/
if ((EccOdd == 0) && (EccEven == 0)) {
return XST_SUCCESS;
}
/*
* Single bit error, correct it
*/
if (EccOdd == (~EccEven & 0xfff)) {
BytePos = (EccOdd >> 3) & XNANDPS_ECC_CORRECT_BYTE_MASK;
BitPos = EccOdd & XNANDPS_ECC_CORRECT_BIT_MASK;
/*
* Toggling error bit
*/
Buf[BytePos] ^= (1 << BitPos);
return XST_SUCCESS;
}
/*
* Parity error
*/
if (OneHot((EccOdd | EccEven)) == XST_SUCCESS) {
return XST_SUCCESS;
}
/*
* Multiple bit errors
*/
return XST_FAILURE;
}
/*****************************************************************************/
/**
*
* This function reads a specific page from NAND device using HW ECC block.
* It checks for the ECC errors and corrects single bit errors. The multiple bit
* error are reported as failure.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param DstPtr is a pointer to the destination buffer.
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note None
*
******************************************************************************/
static int XNandPs_ReadPage_HwEcc(XNandPs *InstancePtr, u8 *DstPtr)
{
u32 Status;
u32 BytesPerPage;
u32 SpareBytesPerPage;
u32 EccSteps;
u32 EccOffset;
u32 DataPhaseAddr;
u32 Index;
u32 *EccPos;
u8 *EccCode;
u8 *EccCalc;
u8 *Ptr = DstPtr;
u8 *SparePtr = InstancePtr->SpareBufPtr;
BytesPerPage = InstancePtr->Geometry.BytesPerPage;
SpareBytesPerPage = InstancePtr->Geometry.SpareBytesPerPage;
EccSteps = InstancePtr->EccConfig.NumSteps;
EccCode = &InstancePtr->EccCode[0];
EccCalc = &InstancePtr->EccCalc[0];
/*
* Read page sized bytes in one less AXI data width
*/
XNandPs_ReadBuf(InstancePtr, Ptr,
(BytesPerPage - XNANDPS_AXI_DATA_WIDTH));
Ptr += (BytesPerPage - XNANDPS_AXI_DATA_WIDTH);
/*
* Set the ECC Last bit
*/
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr |= XNANDPS_ECC_LAST;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
/*
* Read transaction with ECC enabled
*/
XNandPs_ReadBuf(InstancePtr, Ptr, XNANDPS_AXI_DATA_WIDTH);
/*
* Calculate the hardware ECC
*/
Ptr = DstPtr;
Status = XNandPs_EccCalculate(InstancePtr, EccCalc);
if (Status != XST_SUCCESS) {
return Status;
}
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr &= ~XNANDPS_ECC_LAST;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
XNandPs_ReadBuf(InstancePtr, SparePtr,
(SpareBytesPerPage - XNANDPS_AXI_DATA_WIDTH));
/*
* Clear chip select for last AXI transaction
*/
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr |= XNANDPS_CLR_CS;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
SparePtr += (SpareBytesPerPage - XNANDPS_AXI_DATA_WIDTH);
XNandPs_ReadBuf(InstancePtr, SparePtr, XNANDPS_AXI_DATA_WIDTH);
/*
* Read the stored ECC code
*/
EccPos = &InstancePtr->EccConfig.EccPos[0];
for(Index = 0; Index < InstancePtr->EccConfig.TotalBytes; Index++) {
EccCode[Index] = ~(InstancePtr->SpareBufPtr[EccPos[Index]]);
}
/*
* Check for ECC errors
*/
EccOffset = 0;
for(; EccSteps; EccSteps--) {
Status = XNandPs_EccCorrect(DstPtr,
&EccCalc[EccOffset],&EccCode[EccOffset]);
if (Status != XST_SUCCESS) {
return Status;
}
DstPtr += InstancePtr->EccConfig.BlockSize;
EccOffset += InstancePtr->EccConfig.BytesPerBlock;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function reads a specific page from NAND device. This doesn't use the
* HW ECC block for checking ECC errors.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param DstPtr is a pointer to the destination buffer.
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note None
*
******************************************************************************/
static int XNandPs_ReadPage(XNandPs *InstancePtr, u8 *DstPtr)
{
u32 Status;
u32 ZeroCommand;
u32 BytesPerPage;
u16 SpareBytesPerPage;
u32 DataPhaseAddr;
u8 *Ptr = DstPtr;
u8 *SparePtr = InstancePtr->SpareBufPtr;
BytesPerPage = InstancePtr->Geometry.BytesPerPage;
SpareBytesPerPage = InstancePtr->Geometry.SpareBytesPerPage;
/*
* Check ONFI Status Register
*/
Status = Onfi_CmdReadStatus(InstancePtr);
if (Status & ONFI_STATUS_FAIL) {
return XST_FAILURE;
}
/*
* ONFI : Reissue the 0x00 on the command line to start
* reading data
*/
ZeroCommand = InstancePtr->Config.FlashBase |
(0 << XNANDPS_ADDR_CYCLES_SHIFT)|
(0 << XNANDPS_END_CMD_VALID_SHIFT)|
(XNANDPS_COMMAND_PHASE_MASK)|
(0 << XNANDPS_END_CMD_SHIFT)|
(0 << XNANDPS_START_CMD_SHIFT);
/*
* AXI transaction for sending command 0x00 to the flash
*/
Xil_Out32(ZeroCommand, 0x00);
/*
* Read page data
*/
XNandPs_ReadBuf(InstancePtr, Ptr, BytesPerPage);
/*
* Read spare bytes in one less AXI data width
*/
XNandPs_ReadBuf(InstancePtr, SparePtr,
(SpareBytesPerPage - XNANDPS_AXI_DATA_WIDTH));
/*
* Clear chip select for last AXI transaction
*/
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr |= XNANDPS_CLR_CS;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
SparePtr += (SpareBytesPerPage - XNANDPS_AXI_DATA_WIDTH);
XNandPs_ReadBuf(InstancePtr, SparePtr, XNANDPS_AXI_DATA_WIDTH);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function writes a specific page in the NAND device using HW ECC block.
* The ECC code is written into the spare bytes of the page.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param SrcPtr is a pointer to the source buffer.
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note None
*
******************************************************************************/
static int XNandPs_WritePage_HwEcc(XNandPs *InstancePtr, u8 *SrcPtr)
{
u32 Status;
u32 BytesPerPage;
u32 SpareBytesPerPage;
u32 DataPhaseAddr;
u32 Index;
u32 *EccPos;
u8 *EccCalc;
u8 *Ptr = SrcPtr;
u8 *SparePtr = InstancePtr->SpareBufPtr;
BytesPerPage = InstancePtr->Geometry.BytesPerPage;
SpareBytesPerPage = InstancePtr->Geometry.SpareBytesPerPage;
EccCalc = &InstancePtr->EccCalc[0];
/*
* Transfer page sized bytes in one less AXI data width
*/
XNandPs_WriteBuf(InstancePtr, Ptr,
(BytesPerPage - XNANDPS_AXI_DATA_WIDTH));
Ptr += (BytesPerPage - XNANDPS_AXI_DATA_WIDTH);
/*
* Last page transaction with ECC set
*/
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr |= XNANDPS_ECC_LAST;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
XNandPs_WriteBuf(InstancePtr, Ptr, XNANDPS_AXI_DATA_WIDTH);
/*
* Calculate the ECC
*/
Ptr = SrcPtr;
Status = XNandPs_EccCalculate(InstancePtr, EccCalc);
if (Status != XST_SUCCESS) {
return Status;
}
/*
* Fill the Spare buffer with calculated ECC
*/
EccPos = &InstancePtr->EccConfig.EccPos[0];
for(Index = 0; Index < InstancePtr->EccConfig.TotalBytes; Index++) {
InstancePtr->SpareBufPtr[EccPos[Index]] = ~(EccCalc[Index]);
}
/*
* Write the spare area with the ECC
*/
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr &= ~XNANDPS_ECC_LAST;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
XNandPs_WriteBuf(InstancePtr, SparePtr,
(SpareBytesPerPage - XNANDPS_AXI_DATA_WIDTH));
/*
* Clear chip select for last AXI transaction
*/
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr |= XNANDPS_CLR_CS;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
SparePtr += (SpareBytesPerPage - XNANDPS_AXI_DATA_WIDTH);
XNandPs_WriteBuf(InstancePtr, SparePtr, XNANDPS_AXI_DATA_WIDTH);
/*
* Poll the Memory controller status register
*/
while (XNandPs_IsBusy(InstancePtr) == TRUE) {
}
/*
* Clear the interrupt condition
*/
XNandPs_WriteReg((InstancePtr->Config.SmcBase +
XNANDPS_MEMC_CLR_CONFIG_OFFSET),
XNANDPS_MEMC_CLR_CONFIG_INT_CLR1_MASK);
/*
* Check SR[0] bit
*/
Status = Onfi_CmdReadStatus(InstancePtr);
if (Status & ONFI_STATUS_FAIL) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function writes a specific page in the NAND device. This doesn't use the
* HW ECC block for ECC code generation.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param SrcPtr is a pointer to the source buffer.
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
*
* @note None
*
******************************************************************************/
static int XNandPs_WritePage(XNandPs *InstancePtr, u8 *SrcPtr)
{
u32 Status;
u32 DataPhaseAddr;
u32 BytesPerPage;
u16 SpareBytesPerPage;
u8 *Ptr = SrcPtr;
u8 *SparePtr = InstancePtr->SpareBufPtr;
BytesPerPage = InstancePtr->Geometry.BytesPerPage;
SpareBytesPerPage = InstancePtr->Geometry.SpareBytesPerPage;
/*
* Transfer page sized bytes
*/
XNandPs_WriteBuf(InstancePtr, Ptr, BytesPerPage);
/*
* Write the spare data bytes
*/
XNandPs_WriteBuf(InstancePtr, SparePtr,
(SpareBytesPerPage - XNANDPS_AXI_DATA_WIDTH));
/*
* Clear chip select for last AXI transaction
*/
DataPhaseAddr = InstancePtr->DataPhaseAddr;
DataPhaseAddr |= XNANDPS_CLR_CS;
InstancePtr->DataPhaseAddr = DataPhaseAddr;
SparePtr += (SpareBytesPerPage - XNANDPS_AXI_DATA_WIDTH);
XNandPs_WriteBuf(InstancePtr, SparePtr, XNANDPS_AXI_DATA_WIDTH);
/*
* Poll the Memory controller status register
*/
while (XNandPs_IsBusy(InstancePtr) == TRUE) {
}
/*
* Clear the interrupt condition
*/
XNandPs_WriteReg((InstancePtr->Config.SmcBase +
XNANDPS_MEMC_CLR_CONFIG_OFFSET),
XNANDPS_MEMC_CLR_CONFIG_INT_CLR1_MASK);
/*
* Check SR[0] bit
*/
Status = Onfi_CmdReadStatus(InstancePtr);
if (Status & ONFI_STATUS_FAIL) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function erases a specific block in the NAND device.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param BlockNum is the block number of the device.
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if fail.
* - XST_NAND_WRITE_PROTECTED if the flash is write
* protected.
*
* @note None
*
******************************************************************************/
int XNandPs_EraseBlock(XNandPs *InstancePtr, u32 BlockNum)
{
u8 OnfiStatus;
u32 Status;
u32 Page;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid(BlockNum < InstancePtr->Geometry.NumBlocks);
/*
* Check if the flash is write protected
*/
OnfiStatus = Onfi_CmdReadStatus(InstancePtr);
if (!(OnfiStatus & ONFI_STATUS_WP)) {
return XST_NAND_WRITE_PROTECTED;
}
Page = BlockNum * InstancePtr->Geometry.PagesPerBlock;
XNandPs_SendCommand(InstancePtr, &OnfiCommands[BLOCK_ERASE], Page,
XNANDPS_COLUMN_NOT_VALID);
/*
* Poll the Memory controller status register
*/
while (XNandPs_IsBusy(InstancePtr) == TRUE) {
}
/*
* Clear the interrupt condition
*/
XNandPs_WriteReg((InstancePtr->Config.SmcBase +
XNANDPS_MEMC_CLR_CONFIG_OFFSET),
XNANDPS_MEMC_CLR_CONFIG_INT_CLR1_MASK);
/*
* Check the SR[0] whether the erase operation is successful or not
*/
Status = Onfi_CmdReadStatus(InstancePtr);
if (Status & ONFI_STATUS_FAIL) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function reads the page data from the AXI Data Phase Address.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param Buf is the buffer pointer to store the byte.
* @param Length is the number of bytes to read.
*
* @return
* - None.
*
******************************************************************************/
static void XNandPs_ReadBuf(XNandPs *InstancePtr, u8 *Buf, u32 Length)
{
u32 Index;
u32 AxiLen = Length >> 2;
u32 *Ptr = (u32 *)Buf;
for(Index = 0; Index < AxiLen; Index++) {
Ptr[Index] = XNandPs_ReadReg(InstancePtr->DataPhaseAddr);
}
}
/*****************************************************************************/
/**
*
* This function writes the data to the AXI Data Phase Address.
*
* @param InstancePtr is a pointer to the XNandPs instance.
* @param Buf is the buffer pointer to write the data from.
* @param Length is the number of bytes to write.
*
* @return
* - None.
*
******************************************************************************/
static void XNandPs_WriteBuf(XNandPs *InstancePtr, u8 *Buf, u32 Length)
{
u32 Index;
u32 AxiLen = Length >> 2;
u32 *Ptr = (u32 *)Buf;
for(Index = 0; Index < AxiLen; Index++) {
XNandPs_WriteReg(InstancePtr->DataPhaseAddr, Ptr[Index]);
}
}