
Now the QEMU nand model supports extended parameter page information. So, removing the QEMU specific hacks from the driver. Signed-off-by: Punnaiah Choudary Kalluri <punnaia@xilinx.com> Acked-by: Sai Pavan Boddu <saipava@xilinx.com>
2868 lines
84 KiB
C
2868 lines
84 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright (C) 2015 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
|
|
* XILINX 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 xnandpsu.c
|
|
* @addtogroup nandpsu_v1_0
|
|
* @{
|
|
*
|
|
* This file contains the implementation of the interface functions for
|
|
* XNandPsu driver. Refer to the header file xnandpsu.h for more detailed
|
|
* information.
|
|
*
|
|
* This module supports for NAND flash memory devices that conform to the
|
|
* "Open NAND Flash Interface" (ONFI) 3.0 Specification. This modules
|
|
* implements basic flash operations like read, write and erase.
|
|
*
|
|
* @note Driver has been renamed to nandpsu after change in
|
|
* naming convention.
|
|
*
|
|
* <pre>
|
|
* MODIFICATION HISTORY:
|
|
*
|
|
* Ver Who Date Changes
|
|
* ----- ---- ---------- -----------------------------------------------
|
|
* 1.0 nm 05/06/2014 First release
|
|
* 2.0 sb 01/12/2015 Removed Null checks for Buffer passed
|
|
* as parameter to Read API's
|
|
* - XNandPsu_Read()
|
|
* - XNandPsu_ReadPage
|
|
* Modified
|
|
* - XNandPsu_SetFeature()
|
|
* - XNandPsu_GetFeature()
|
|
* and made them public.
|
|
* Removed Failure Return for BCF Error check in
|
|
* XNandPsu_ReadPage() and added BCH_Error counter
|
|
* in the instance pointer structure.
|
|
* Added XNandPsu_Prepare_Cmd API
|
|
* Replaced
|
|
* - XNandPsu_IntrStsEnable
|
|
* - XNandPsu_IntrStsClear
|
|
* - XNandPsu_IntrClear
|
|
* - XNandPsu_SetProgramReg
|
|
* with XNandPsu_WriteReg call
|
|
* Modified xnandpsu.c file API's with above changes.
|
|
* Corrected the program command for Set Feature API.
|
|
* Modified
|
|
* - XNandPsu_OnfiReadStatus
|
|
* - XNandPsu_GetFeature
|
|
* - XNandPsu_SetFeature
|
|
* to add support for DDR mode.
|
|
* Changed Convention for SLC/MLC
|
|
* SLC --> HAMMING
|
|
* MLC --> BCH
|
|
* SlcMlc --> IsBCH
|
|
* Removed extra DMA mode initialization from
|
|
* the XNandPsu_CfgInitialize API.
|
|
* Modified
|
|
* - XNandPsu_SetEccAddrSize
|
|
* ECC address now is calculated based upon the
|
|
* size of spare area
|
|
* Modified Block Erase API, removed clearing of
|
|
* packet register before erase.
|
|
* Clearing Data Interface Register before
|
|
* XNandPsu_OnfiReset call.
|
|
* Modified XNandPsu_ChangeTimingMode API supporting
|
|
* SDR and NVDDR interface for timing modes 0 to 5.
|
|
* Modified Bbt Signature and Version Offset value for
|
|
* Oob and No-Oob region.
|
|
* 1.0 kpc 17/6/2015 Added timer based timeout intsead of sw counter.
|
|
* </pre>
|
|
*
|
|
******************************************************************************/
|
|
|
|
/***************************** Include Files *********************************/
|
|
#include "xnandpsu.h"
|
|
#include "xnandpsu_bbm.h"
|
|
/************************** Constant Definitions *****************************/
|
|
|
|
const static XNandPsu_EccMatrix EccMatrix[] = {
|
|
/* 512 byte page */
|
|
{XNANDPSU_PAGE_SIZE_512, 9U, 1U, XNANDPSU_HAMMING, 0x20DU, 0x3U},
|
|
{XNANDPSU_PAGE_SIZE_512, 9U, 4U, XNANDPSU_BCH, 0x209U, 0x7U},
|
|
{XNANDPSU_PAGE_SIZE_512, 9U, 8U, XNANDPSU_BCH, 0x203U, 0xDU},
|
|
/* 2K byte page */
|
|
{XNANDPSU_PAGE_SIZE_2K, 9U, 1U, XNANDPSU_HAMMING, 0x834U, 0xCU},
|
|
{XNANDPSU_PAGE_SIZE_2K, 9U, 4U, XNANDPSU_BCH, 0x826U, 0x1AU},
|
|
{XNANDPSU_PAGE_SIZE_2K, 9U, 8U, XNANDPSU_BCH, 0x80cU, 0x34U},
|
|
{XNANDPSU_PAGE_SIZE_2K, 9U, 12U, XNANDPSU_BCH, 0x822U, 0x4EU},
|
|
{XNANDPSU_PAGE_SIZE_2K, 10U, 24U, XNANDPSU_BCH, 0x81cU, 0x54U},
|
|
/* 4K byte page */
|
|
{XNANDPSU_PAGE_SIZE_4K, 9U, 1U, XNANDPSU_HAMMING, 0x1068U, 0x18U},
|
|
{XNANDPSU_PAGE_SIZE_4K, 9U, 4U, XNANDPSU_BCH, 0x104cU, 0x34U},
|
|
{XNANDPSU_PAGE_SIZE_4K, 9U, 8U, XNANDPSU_BCH, 0x1018U, 0x68U},
|
|
{XNANDPSU_PAGE_SIZE_4K, 9U, 12U, XNANDPSU_BCH, 0x1044U, 0x9CU},
|
|
{XNANDPSU_PAGE_SIZE_4K, 10U, 24U, XNANDPSU_BCH, 0x1038U, 0xA8U},
|
|
/* 8K byte page */
|
|
{XNANDPSU_PAGE_SIZE_8K, 9U, 1U, XNANDPSU_HAMMING, 0x20d0U, 0x30U},
|
|
{XNANDPSU_PAGE_SIZE_8K, 9U, 4U, XNANDPSU_BCH, 0x2098U, 0x68U},
|
|
{XNANDPSU_PAGE_SIZE_8K, 9U, 8U, XNANDPSU_BCH, 0x2030U, 0xD0U},
|
|
{XNANDPSU_PAGE_SIZE_8K, 9U, 12U, XNANDPSU_BCH, 0x2088U, 0x138U},
|
|
{XNANDPSU_PAGE_SIZE_8K, 10U, 24U, XNANDPSU_BCH, 0x2070U, 0x150U},
|
|
/* 16K byte page */
|
|
{XNANDPSU_PAGE_SIZE_16K, 9U, 1U, XNANDPSU_HAMMING, 0x4460U, 0x60U},
|
|
{XNANDPSU_PAGE_SIZE_16K, 9U, 4U, XNANDPSU_BCH, 0x43f0U, 0xD0U},
|
|
{XNANDPSU_PAGE_SIZE_16K, 9U, 8U, XNANDPSU_BCH, 0x4320U, 0x1A0U},
|
|
{XNANDPSU_PAGE_SIZE_16K, 9U, 12U, XNANDPSU_BCH, 0x4250U, 0x270U},
|
|
{XNANDPSU_PAGE_SIZE_16K, 10U, 24U, XNANDPSU_BCH, 0x4220U, 0x2A0U}
|
|
};
|
|
|
|
/**************************** Type Definitions *******************************/
|
|
/***************** Macros (Inline Functions) Definitions *********************/
|
|
|
|
/************************** Function Prototypes ******************************/
|
|
|
|
static s32 XNandPsu_FlashInit(XNandPsu *InstancePtr);
|
|
|
|
static void XNandPsu_InitGeometry(XNandPsu *InstancePtr, OnfiParamPage *Param);
|
|
|
|
static void XNandPsu_InitFeatures(XNandPsu *InstancePtr, OnfiParamPage *Param);
|
|
|
|
static s32 XNandPsu_PollRegTimeout(XNandPsu *InstancePtr, u32 RegOffset,
|
|
u32 Mask, u32 Timeout);
|
|
|
|
static void XNandPsu_SetPktSzCnt(XNandPsu *InstancePtr, u32 PktSize,
|
|
u32 PktCount);
|
|
|
|
static void XNandPsu_SetPageColAddr(XNandPsu *InstancePtr, u32 Page, u16 Col);
|
|
|
|
static void XNandPsu_SetPageSize(XNandPsu *InstancePtr);
|
|
|
|
static void XNandPsu_SelectChip(XNandPsu *InstancePtr, u32 Target);
|
|
|
|
static s32 XNandPsu_OnfiReset(XNandPsu *InstancePtr, u32 Target);
|
|
|
|
static s32 XNandPsu_OnfiReadStatus(XNandPsu *InstancePtr, u32 Target,
|
|
u16 *OnfiStatus);
|
|
|
|
static s32 XNandPsu_OnfiReadId(XNandPsu *InstancePtr, u32 Target, u8 IdAddr,
|
|
u32 IdLen, u8 *Buf);
|
|
|
|
static s32 XNandPsu_OnfiReadParamPage(XNandPsu *InstancePtr, u32 Target,
|
|
u8 *Buf);
|
|
|
|
static s32 XNandPsu_ProgramPage(XNandPsu *InstancePtr, u32 Target, u32 Page,
|
|
u32 Col, u8 *Buf);
|
|
|
|
static s32 XNandPsu_ReadPage(XNandPsu *InstancePtr, u32 Target, u32 Page,
|
|
u32 Col, u8 *Buf);
|
|
|
|
static s32 XNandPsu_CheckOnDie(XNandPsu *InstancePtr, OnfiParamPage *Param);
|
|
|
|
static void XNandPsu_SetEccAddrSize(XNandPsu *InstancePtr);
|
|
|
|
static s32 XNandPsu_ChangeReadColumn(XNandPsu *InstancePtr, u32 Target,
|
|
u32 Col, u32 PktSize, u32 PktCount,
|
|
u8 *Buf);
|
|
|
|
static s32 XNandPsu_ChangeWriteColumn(XNandPsu *InstancePtr, u32 Target,
|
|
u32 Col, u32 PktSize, u32 PktCount,
|
|
u8 *Buf);
|
|
|
|
static s32 XNandPsu_InitExtEcc(XNandPsu *InstancePtr, OnfiExtPrmPage *ExtPrm);
|
|
|
|
static s32 XNandPsu_Data_ReadWrite(XNandPsu *InstancePtr, u8* Buf, u32 PktCount,
|
|
u32 PktSize, u32 Operation, u8 DmaMode);
|
|
|
|
static s32 XNandPsu_WaitFor_Transfer_Complete(XNandPsu *InstancePtr);
|
|
|
|
static s32 XNandPsu_Device_Ready(XNandPsu *InstancePtr, u32 Target);
|
|
|
|
static void XNandPsu_Fifo_Read(XNandPsu *InstancePtr, u8* Buf, u32 Size);
|
|
|
|
static void XNandPsu_Fifo_Write(XNandPsu *InstancePtr, u8* Buf, u32 Size);
|
|
|
|
static void XNandPsu_Update_DmaAddr(XNandPsu *InstancePtr, u8* Buf);
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function initializes a specific XNandPsu instance. This function must
|
|
* be called prior to using the NAND flash device to read or write any data.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param ConfigPtr points to XNandPsu device configuration structure.
|
|
* @param EffectiveAddr is the base address of NAND flash controller.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
* @note The user needs to first call the XNandPsu_LookupConfig() API
|
|
* which returns the Configuration structure pointer which is
|
|
* passed as a parameter to the XNandPsu_CfgInitialize() API.
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_CfgInitialize(XNandPsu *InstancePtr, XNandPsu_Config *ConfigPtr,
|
|
u32 EffectiveAddr)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(ConfigPtr != NULL);
|
|
|
|
/* Initialize InstancePtr Config structure */
|
|
InstancePtr->Config.DeviceId = ConfigPtr->DeviceId;
|
|
InstancePtr->Config.BaseAddress = EffectiveAddr;
|
|
/* Operate in Polling Mode */
|
|
InstancePtr->Mode = XNANDPSU_POLLING;
|
|
/* Enable MDMA mode by default */
|
|
InstancePtr->DmaMode = XNANDPSU_MDMA;
|
|
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
|
|
|
|
/* Initialize the NAND flash targets */
|
|
Status = XNandPsu_FlashInit(InstancePtr);
|
|
if (Status != XST_SUCCESS) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Flash init failed\r\n",__func__);
|
|
#endif
|
|
goto Out;
|
|
}
|
|
/* Set ECC mode */
|
|
if (InstancePtr->Features.EzNand != 0U) {
|
|
InstancePtr->EccMode = XNANDPSU_EZNAND;
|
|
} else if (InstancePtr->Features.OnDie != 0U) {
|
|
InstancePtr->EccMode = XNANDPSU_ONDIE;
|
|
} else {
|
|
InstancePtr->EccMode = XNANDPSU_HWECC;
|
|
}
|
|
|
|
/* Initialize Ecc Error flip counters */
|
|
InstancePtr->Ecc_Stat_PerPage_flips = 0U;
|
|
InstancePtr->Ecc_Stats_total_flips = 0U;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
XNandPsu_InitBbtDesc(InstancePtr);
|
|
Status = XNandPsu_ScanBbt(InstancePtr);
|
|
if (Status != XST_SUCCESS) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: BBT scan failed\r\n",__func__);
|
|
#endif
|
|
goto Out;
|
|
}
|
|
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function initializes the NAND flash and gets the geometry information.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_FlashInit(XNandPsu *InstancePtr)
|
|
{
|
|
u32 Target;
|
|
u8 Id[ONFI_SIG_LEN] = {0U};
|
|
OnfiParamPage Param = {0U};
|
|
s32 Status = XST_FAILURE;
|
|
u32 Index;
|
|
u32 Crc;
|
|
u32 PrmPgOff;
|
|
u32 PrmPgLen;
|
|
OnfiExtPrmPage ExtParam __attribute__ ((aligned(64)));
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
/* Clear Data Interface Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_DATA_INTF_OFFSET, 0U);
|
|
|
|
/* Clear DMA Buffer Boundary Register */
|
|
XNandPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_DMA_BUF_BND_OFFSET, 0U);
|
|
|
|
for (Target = 0U; Target < XNANDPSU_MAX_TARGETS; Target++) {
|
|
/* Reset the Target */
|
|
Status = XNandPsu_OnfiReset(InstancePtr, Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Read ONFI ID */
|
|
Status = XNandPsu_OnfiReadId(InstancePtr, Target,
|
|
ONFI_READ_ID_ADDR,
|
|
ONFI_SIG_LEN,
|
|
(u8 *)&Id[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
if (!IS_ONFI(Id)) {
|
|
if (Target == 0U) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: ONFI ID doesn't match\r\n",
|
|
__func__);
|
|
#endif
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
/* Read Parameter Page */
|
|
for(Index = 0U; Index < ONFI_MND_PRM_PGS; Index++) {
|
|
if (Index == 0U) {
|
|
Status = XNandPsu_OnfiReadParamPage(InstancePtr,
|
|
Target, (u8 *)&Param);
|
|
} else {
|
|
PrmPgOff = Index * ONFI_PRM_PG_LEN;
|
|
PrmPgLen = ONFI_PRM_PG_LEN;
|
|
Status = XNandPsu_ChangeReadColumn(InstancePtr,
|
|
Target,PrmPgOff,
|
|
ONFI_PRM_PG_LEN, 1U,
|
|
(u8 *) &Param);
|
|
}
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Check CRC */
|
|
Crc = XNandPsu_OnfiParamPageCrc((u8*)&Param, 0U,
|
|
ONFI_CRC_LEN);
|
|
if (Crc != Param.Crc) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: ONFI parameter page (%d) crc check failed\r\n",
|
|
__func__, Index);
|
|
#endif
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (Index >= ONFI_MND_PRM_PGS) {
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
/* Fill Geometry for the first target */
|
|
if (Target == 0U) {
|
|
XNandPsu_InitGeometry(InstancePtr, &Param);
|
|
XNandPsu_InitFeatures(InstancePtr, &Param);
|
|
if ((!InstancePtr->Features.EzNand) != 0U) {
|
|
Status =XNandPsu_CheckOnDie(InstancePtr,&Param);
|
|
if (Status != XST_SUCCESS) {
|
|
InstancePtr->Features.OnDie = 0U;
|
|
}
|
|
}
|
|
|
|
if ((InstancePtr->Geometry.NumBitsECC == 0xFFU) &&
|
|
(InstancePtr->Features.ExtPrmPage != 0U)) {
|
|
/* ONFI 3.1 section 5.7.1.6 & 5.7.1.7 */
|
|
PrmPgLen = (u32)Param.ExtParamPageLen * 16U;
|
|
PrmPgOff = (u32)((u32)Param.NumOfParamPages *
|
|
ONFI_PRM_PG_LEN) +
|
|
(Index * (u32)PrmPgLen);
|
|
Status = XNandPsu_ChangeReadColumn(
|
|
InstancePtr,
|
|
Target,
|
|
PrmPgOff,
|
|
PrmPgLen, 1U,
|
|
(u8 *)(void *)&ExtParam);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Check CRC */
|
|
Crc = XNandPsu_OnfiParamPageCrc(
|
|
(u8 *)&ExtParam,
|
|
2U,
|
|
PrmPgLen);
|
|
if (Crc != ExtParam.Crc) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: ONFI extended parameter page (%d) crc check failed\r\n",
|
|
__func__, Index);
|
|
#endif
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
/* Initialize Extended ECC info */
|
|
Status = XNandPsu_InitExtEcc(
|
|
InstancePtr,
|
|
&ExtParam);
|
|
if (Status != XST_SUCCESS) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Init extended ecc failed\r\n",__func__);
|
|
#endif
|
|
goto Out;
|
|
}
|
|
}
|
|
/* Configure ECC settings */
|
|
XNandPsu_SetEccAddrSize(InstancePtr);
|
|
}
|
|
InstancePtr->Geometry.NumTargets++;
|
|
}
|
|
/* Calculate total number of blocks and total size of flash */
|
|
InstancePtr->Geometry.NumPages = InstancePtr->Geometry.NumTargets *
|
|
InstancePtr->Geometry.NumTargetPages;
|
|
InstancePtr->Geometry.NumBlocks = InstancePtr->Geometry.NumTargets *
|
|
InstancePtr->Geometry.NumTargetBlocks;
|
|
InstancePtr->Geometry.DeviceSize =
|
|
(u64)InstancePtr->Geometry.NumTargets *
|
|
InstancePtr->Geometry.TargetSize;
|
|
|
|
Status = XST_SUCCESS;
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function initializes the geometry information from ONFI parameter page.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Param is pointer to the ONFI parameter page.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_InitGeometry(XNandPsu *InstancePtr, OnfiParamPage *Param)
|
|
{
|
|
/* Assert the input arguments. */
|
|
Xil_AssertVoid(Param != NULL);
|
|
|
|
InstancePtr->Geometry.BytesPerPage = Param->BytesPerPage;
|
|
InstancePtr->Geometry.SpareBytesPerPage = Param->SpareBytesPerPage;
|
|
InstancePtr->Geometry.PagesPerBlock = Param->PagesPerBlock;
|
|
InstancePtr->Geometry.BlocksPerLun = Param->BlocksPerLun;
|
|
InstancePtr->Geometry.NumLuns = Param->NumLuns;
|
|
InstancePtr->Geometry.RowAddrCycles = Param->AddrCycles & 0xFU;
|
|
InstancePtr->Geometry.ColAddrCycles = (Param->AddrCycles >> 4U) & 0xFU;
|
|
InstancePtr->Geometry.NumBitsPerCell = Param->BitsPerCell;
|
|
InstancePtr->Geometry.NumBitsECC = Param->EccBits;
|
|
InstancePtr->Geometry.BlockSize = (Param->PagesPerBlock *
|
|
Param->BytesPerPage);
|
|
InstancePtr->Geometry.NumTargetBlocks = (Param->BlocksPerLun *
|
|
(u32)Param->NumLuns);
|
|
InstancePtr->Geometry.NumTargetPages = (Param->BlocksPerLun *
|
|
(u32)Param->NumLuns *
|
|
Param->PagesPerBlock);
|
|
InstancePtr->Geometry.TargetSize = ((u64)Param->BlocksPerLun *
|
|
(u64)Param->NumLuns *
|
|
(u64)Param->PagesPerBlock *
|
|
(u64)Param->BytesPerPage);
|
|
InstancePtr->Geometry.EccCodeWordSize = 9U; /* 2 power of 9 = 512 */
|
|
if (InstancePtr->Geometry.NumTargetBlocks > XNANDPSU_MAX_BLOCKS)
|
|
xil_printf("!!! Device contains more blocks than the max defined blocks in driver\r\n");
|
|
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("Manufacturer: %s\r\n", Param->DeviceManufacturer);
|
|
xil_printf("Device Model: %s\r\n", Param->DeviceModel);
|
|
xil_printf("Jedec ID: 0x%x\r\n", Param->JedecManufacturerId);
|
|
xil_printf("Bytes Per Page: 0x%x\r\n", Param->BytesPerPage);
|
|
xil_printf("Spare Bytes Per Page: 0x%x\r\n", Param->SpareBytesPerPage);
|
|
xil_printf("Pages Per Block: 0x%x\r\n", Param->PagesPerBlock);
|
|
xil_printf("Blocks Per LUN: 0x%x\r\n", Param->BlocksPerLun);
|
|
xil_printf("Number of LUNs: 0x%x\r\n", Param->NumLuns);
|
|
xil_printf("Number of bits per cell: 0x%x\r\n", Param->BitsPerCell);
|
|
xil_printf("Number of ECC bits: 0x%x\r\n", Param->EccBits);
|
|
xil_printf("Block Size: 0x%x\r\n", InstancePtr->Geometry.BlockSize);
|
|
|
|
xil_printf("Number of Target Blocks: 0x%x\r\n",
|
|
InstancePtr->Geometry.NumTargetBlocks);
|
|
xil_printf("Number of Target Pages: 0x%x\r\n",
|
|
InstancePtr->Geometry.NumTargetPages);
|
|
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function initializes the feature list from ONFI parameter page.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Param is pointer to ONFI parameter page buffer.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_InitFeatures(XNandPsu *InstancePtr, OnfiParamPage *Param)
|
|
{
|
|
/* Assert the input arguments. */
|
|
Xil_AssertVoid(Param != NULL);
|
|
|
|
InstancePtr->Features.NvDdr = ((Param->Features & (1U << 5)) != 0U) ?
|
|
1U : 0U;
|
|
InstancePtr->Features.EzNand = ((Param->Features & (1U << 9)) != 0U) ?
|
|
1U : 0U;
|
|
InstancePtr->Features.ExtPrmPage = ((Param->Features & (1U << 7)) != 0U) ?
|
|
1U : 0U;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function checks if the flash supports on-die ECC.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Param is pointer to ONFI parameter page.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_CheckOnDie(XNandPsu *InstancePtr, OnfiParamPage *Param)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
u8 JedecId[2] = {0U};
|
|
u8 EccSetFeature[4] = {0x08U, 0x00U, 0x00U, 0x00U};
|
|
u8 EccGetFeature[4] ={0U};
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Param != NULL);
|
|
|
|
/*
|
|
* Check if this flash supports On-Die ECC.
|
|
* For more information, refer to Micron TN2945.
|
|
* Micron Flash: MT29F1G08ABADA, MT29F1G08ABBDA
|
|
* MT29F1G16ABBDA,
|
|
* MT29F2G08ABBEA, MT29F2G16ABBEA,
|
|
* MT29F2G08ABAEA, MT29F2G16ABAEA,
|
|
* MT29F4G08ABBDA, MT29F4G16ABBDA,
|
|
* MT29F4G08ABADA, MT29F4G16ABADA,
|
|
* MT29F8G08ADBDA, MT29F8G16ADBDA,
|
|
* MT29F8G08ADADA, MT29F8G16ADADA
|
|
*/
|
|
|
|
/* Read JEDEC ID */
|
|
Status = XNandPsu_OnfiReadId(InstancePtr, 0U, 0x00U, 2U, &JedecId[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
if ((JedecId[0] == 0x2CU) &&
|
|
/* 1 Gb flash devices */
|
|
((JedecId[1] == 0xF1U) ||
|
|
(JedecId[1] == 0xA1U) ||
|
|
(JedecId[1] == 0xB1U) ||
|
|
/* 2 Gb flash devices */
|
|
(JedecId[1] == 0xAAU) ||
|
|
(JedecId[1] == 0xBAU) ||
|
|
(JedecId[1] == 0xDAU) ||
|
|
(JedecId[1] == 0xCAU) ||
|
|
/* 4 Gb flash devices */
|
|
(JedecId[1] == 0xACU) ||
|
|
(JedecId[1] == 0xBCU) ||
|
|
(JedecId[1] == 0xDCU) ||
|
|
(JedecId[1] == 0xCCU) ||
|
|
/* 8 Gb flash devices */
|
|
(JedecId[1] == 0xA3U) ||
|
|
(JedecId[1] == 0xB3U) ||
|
|
(JedecId[1] == 0xD3U) ||
|
|
(JedecId[1] == 0xC3U))) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Ondie flash detected, jedec id 0x%x 0x%x\r\n",
|
|
__func__, JedecId[0], JedecId[1]);
|
|
#endif
|
|
/* On-Die Set Feature */
|
|
Status = XNandPsu_SetFeature(InstancePtr, 0U, 0x90U,
|
|
&EccSetFeature[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Ondie set_feature failed\r\n",
|
|
__func__);
|
|
#endif
|
|
goto Out;
|
|
}
|
|
/* Check to see if ECC feature is set */
|
|
Status = XNandPsu_GetFeature(InstancePtr, 0U, 0x90U,
|
|
&EccGetFeature[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Ondie get_feature failed\r\n",
|
|
__func__);
|
|
#endif
|
|
goto Out;
|
|
}
|
|
if ((EccGetFeature[0] & 0x08U) != 0U) {
|
|
InstancePtr->Features.OnDie = 1U;
|
|
Status = XST_SUCCESS;
|
|
}
|
|
} else {
|
|
/* On-Die flash not found */
|
|
Status = XST_FAILURE;
|
|
}
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function enables DMA mode of controller operation.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
void XNandPsu_EnableDmaMode(XNandPsu *InstancePtr)
|
|
{
|
|
/* Assert the input arguments. */
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
InstancePtr->DmaMode = XNANDPSU_MDMA;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function disables DMA mode of driver/controller operation.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
void XNandPsu_DisableDmaMode(XNandPsu *InstancePtr)
|
|
{
|
|
/* Assert the input arguments. */
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
InstancePtr->DmaMode = XNANDPSU_PIO;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function enables ECC mode of driver/controller operation.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
void XNandPsu_EnableEccMode(XNandPsu *InstancePtr)
|
|
{
|
|
/* Assert the input arguments. */
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
InstancePtr->EccMode = XNANDPSU_HWECC;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function disables ECC mode of driver/controller operation.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
void XNandPsu_DisableEccMode(XNandPsu *InstancePtr)
|
|
{
|
|
/* Assert the input arguments. */
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
InstancePtr->EccMode = XNANDPSU_NONE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function polls for a register bit set status till the timeout.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param RegOffset is the offset of register.
|
|
* @param Mask is the bitmask.
|
|
* @param Timeout is the timeout value.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_PollRegTimeout(XNandPsu *InstancePtr, u32 RegOffset,
|
|
u32 Mask, u32 Timeout)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
volatile u32 RegVal;
|
|
u32 TimeoutVar = Timeout;
|
|
|
|
while (TimeoutVar > 0U) {
|
|
RegVal = XNandPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
RegOffset) & Mask;
|
|
if (RegVal != 0U) {
|
|
break;
|
|
}
|
|
TimeoutVar--;
|
|
usleep(1);
|
|
}
|
|
|
|
if (TimeoutVar <= 0U) {
|
|
Status = XST_FAILURE;
|
|
} else {
|
|
Status = XST_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sets packet size and packet count values in packet register.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param PktSize is the packet size.
|
|
* @param PktCount is the packet count.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_SetPktSzCnt(XNandPsu *InstancePtr, u32 PktSize,
|
|
u32 PktCount)
|
|
{
|
|
/* Assert the input arguments. */
|
|
Xil_AssertVoid(PktSize <= XNANDPSU_MAX_PKT_SIZE);
|
|
Xil_AssertVoid(PktCount <= XNANDPSU_MAX_PKT_COUNT);
|
|
|
|
/* Update Packet Register with pkt size and count */
|
|
XNandPsu_ReadModifyWrite(InstancePtr, XNANDPSU_PKT_OFFSET,
|
|
((u32)XNANDPSU_PKT_PKT_SIZE_MASK |
|
|
(u32)XNANDPSU_PKT_PKT_CNT_MASK),
|
|
((PktSize & XNANDPSU_PKT_PKT_SIZE_MASK) |
|
|
((PktCount << XNANDPSU_PKT_PKT_CNT_SHIFT) &
|
|
XNANDPSU_PKT_PKT_CNT_MASK)));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sets Page and Column values in the Memory address registers.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Page is the page value.
|
|
* @param Col is the column value.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_SetPageColAddr(XNandPsu *InstancePtr, u32 Page, u16 Col)
|
|
{
|
|
/* Program Memory Address Register 1 */
|
|
XNandPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_MEM_ADDR1_OFFSET,
|
|
(((u32)Col & XNANDPSU_MEM_ADDR1_COL_ADDR_MASK) |
|
|
((Page << (u32)XNANDPSU_MEM_ADDR1_PG_ADDR_SHIFT) &
|
|
XNANDPSU_MEM_ADDR1_PG_ADDR_MASK)));
|
|
/* Program Memory Address Register 2 */
|
|
XNandPsu_ReadModifyWrite(InstancePtr, XNANDPSU_MEM_ADDR2_OFFSET,
|
|
XNANDPSU_MEM_ADDR2_MEM_ADDR_MASK,
|
|
((Page >> XNANDPSU_MEM_ADDR1_PG_ADDR_SHIFT) &
|
|
XNANDPSU_MEM_ADDR2_MEM_ADDR_MASK));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sets the size of page in Command Register.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_SetPageSize(XNandPsu *InstancePtr)
|
|
{
|
|
u32 PageSizeMask = 0;
|
|
u32 PageSize = InstancePtr->Geometry.BytesPerPage;
|
|
|
|
/* Calculate page size mask */
|
|
switch(PageSize) {
|
|
case XNANDPSU_PAGE_SIZE_512:
|
|
PageSizeMask = (0U << XNANDPSU_CMD_PG_SIZE_SHIFT);
|
|
break;
|
|
case XNANDPSU_PAGE_SIZE_2K:
|
|
PageSizeMask = (1U << XNANDPSU_CMD_PG_SIZE_SHIFT);
|
|
break;
|
|
case XNANDPSU_PAGE_SIZE_4K:
|
|
PageSizeMask = (2U << XNANDPSU_CMD_PG_SIZE_SHIFT);
|
|
break;
|
|
case XNANDPSU_PAGE_SIZE_8K:
|
|
PageSizeMask = (3U << XNANDPSU_CMD_PG_SIZE_SHIFT);
|
|
break;
|
|
case XNANDPSU_PAGE_SIZE_16K:
|
|
PageSizeMask = (4U << XNANDPSU_CMD_PG_SIZE_SHIFT);
|
|
break;
|
|
case XNANDPSU_PAGE_SIZE_1K_16BIT:
|
|
PageSizeMask = (5U << XNANDPSU_CMD_PG_SIZE_SHIFT);
|
|
break;
|
|
default:
|
|
/* Not supported */
|
|
break;
|
|
}
|
|
/* Update Command Register */
|
|
XNandPsu_ReadModifyWrite(InstancePtr, XNANDPSU_CMD_OFFSET,
|
|
XNANDPSU_CMD_PG_SIZE_MASK, PageSizeMask);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function setup the Ecc Register.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_SetEccAddrSize(XNandPsu *InstancePtr)
|
|
{
|
|
u32 PageSize = InstancePtr->Geometry.BytesPerPage;
|
|
u32 CodeWordSize = InstancePtr->Geometry.EccCodeWordSize;
|
|
u32 NumEccBits = InstancePtr->Geometry.NumBitsECC;
|
|
u32 Index;
|
|
u32 Found = 0U;
|
|
u8 BchModeVal;
|
|
|
|
for (Index = 0U; Index < (sizeof(EccMatrix)/sizeof(XNandPsu_EccMatrix));
|
|
Index++) {
|
|
if ((EccMatrix[Index].PageSize == PageSize) &&
|
|
(EccMatrix[Index].CodeWordSize >= CodeWordSize)) {
|
|
if (EccMatrix[Index].NumEccBits >= NumEccBits) {
|
|
Found = Index;
|
|
break;
|
|
}
|
|
else {
|
|
Found = Index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Found != 0U) {
|
|
if(InstancePtr->Geometry.SpareBytesPerPage < 64U) {
|
|
InstancePtr->EccCfg.EccAddr = (u16)PageSize;
|
|
}
|
|
else {
|
|
InstancePtr->EccCfg.EccAddr = ((u16)PageSize +
|
|
(InstancePtr->Geometry.SpareBytesPerPage
|
|
- EccMatrix[Found].EccSize));
|
|
}
|
|
InstancePtr->EccCfg.EccSize = EccMatrix[Found].EccSize;
|
|
InstancePtr->EccCfg.NumEccBits = EccMatrix[Found].NumEccBits;
|
|
InstancePtr->EccCfg.CodeWordSize =
|
|
EccMatrix[Found].CodeWordSize;
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("ECC: addr 0x%x size 0x%x numbits %d "
|
|
"codesz %d\r\n",
|
|
InstancePtr->EccCfg.EccAddr,
|
|
InstancePtr->EccCfg.EccSize,
|
|
InstancePtr->EccCfg.NumEccBits,
|
|
InstancePtr->EccCfg.CodeWordSize);
|
|
#endif
|
|
if (EccMatrix[Found].IsBCH == XNANDPSU_HAMMING) {
|
|
InstancePtr->EccCfg.IsBCH = 0U;
|
|
} else {
|
|
InstancePtr->EccCfg.IsBCH = 1U;
|
|
}
|
|
/* Write ECC register */
|
|
XNandPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
(u32)XNANDPSU_ECC_OFFSET,
|
|
((u32)InstancePtr->EccCfg.EccAddr |
|
|
((u32)InstancePtr->EccCfg.EccSize << (u32)16) |
|
|
((u32)InstancePtr->EccCfg.IsBCH << (u32)27)));
|
|
|
|
if (EccMatrix[Found].IsBCH == XNANDPSU_BCH) {
|
|
/* Write memory address register 2 */
|
|
switch(InstancePtr->EccCfg.NumEccBits) {
|
|
case 16U:
|
|
BchModeVal = 0x0U;
|
|
break;
|
|
case 12U:
|
|
BchModeVal = 0x1U;
|
|
break;
|
|
case 8U:
|
|
BchModeVal = 0x2U;
|
|
break;
|
|
case 4U:
|
|
BchModeVal = 0x3U;
|
|
break;
|
|
case 24U:
|
|
BchModeVal = 0x4U;
|
|
break;
|
|
default:
|
|
BchModeVal = 0x0U;
|
|
break;
|
|
}
|
|
XNandPsu_ReadModifyWrite(InstancePtr,
|
|
XNANDPSU_MEM_ADDR2_OFFSET,
|
|
XNANDPSU_MEM_ADDR2_NFC_BCH_MODE_MASK,
|
|
((u32)BchModeVal <<
|
|
(u32)XNANDPSU_MEM_ADDR2_NFC_BCH_MODE_SHIFT));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function setup the Ecc Spare Command Register.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_SetEccSpareCmd(XNandPsu *InstancePtr, u16 SpareCmd,
|
|
u8 AddrCycles)
|
|
{
|
|
XNandPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
(u32)XNANDPSU_ECC_SPR_CMD_OFFSET,
|
|
(u32)SpareCmd | ((u32)AddrCycles << 28U));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sets the chip select value in memory address2 register.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_SelectChip(XNandPsu *InstancePtr, u32 Target)
|
|
{
|
|
/* Update Memory Address2 register with chip select */
|
|
XNandPsu_ReadModifyWrite(InstancePtr, XNANDPSU_MEM_ADDR2_OFFSET,
|
|
XNANDPSU_MEM_ADDR2_CHIP_SEL_MASK,
|
|
((Target << XNANDPSU_MEM_ADDR2_CHIP_SEL_SHIFT) &
|
|
XNANDPSU_MEM_ADDR2_CHIP_SEL_MASK));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends ONFI Reset command to the flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_OnfiReset(XNandPsu *InstancePtr, u32 Target)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Target < XNANDPSU_MAX_TARGETS);
|
|
|
|
/* Enable Transfer Complete Interrupt in Interrupt Status Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET,
|
|
XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
|
|
/* Program Command Register */
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_RST, ONFI_CMD_INVALID, 0U,
|
|
0U, 0U);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Set Reset in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET, XNANDPSU_PROG_RST_MASK);
|
|
|
|
/* Poll for Transfer Complete event */
|
|
Status = XNandPsu_WaitFor_Transfer_Complete(InstancePtr);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends ONFI Read Status command to the flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param OnfiStatus is the ONFI status value to return.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_OnfiReadStatus(XNandPsu *InstancePtr, u32 Target,
|
|
u16 *OnfiStatus)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Target < XNANDPSU_MAX_TARGETS);
|
|
Xil_AssertNonvoid(OnfiStatus != NULL);
|
|
/* Enable Transfer Complete Interrupt in Interrupt Status Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET,
|
|
XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
|
|
/* Program Command Register */
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_RD_STS, ONFI_CMD_INVALID,
|
|
0U, 0U, 0U);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Program Packet Size and Packet Count */
|
|
if(InstancePtr->DataInterface == XNANDPSU_SDR)
|
|
XNandPsu_SetPktSzCnt(InstancePtr, 1U, 1U);
|
|
else
|
|
XNandPsu_SetPktSzCnt(InstancePtr, 2U, 1U);
|
|
|
|
/* Set Read Status in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_RD_STS_MASK);
|
|
/* Poll for Transfer Complete event */
|
|
Status = XNandPsu_WaitFor_Transfer_Complete(InstancePtr);
|
|
/* Read Flash Status */
|
|
*OnfiStatus = (u16) XNandPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_FLASH_STS_OFFSET);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends ONFI Read ID command to the flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Buf is the ONFI ID value to return.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_OnfiReadId(XNandPsu *InstancePtr, u32 Target, u8 IdAddr,
|
|
u32 IdLen, u8 *Buf)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
u32 Index;
|
|
u32 Rem;
|
|
u32 *BufPtr = (u32 *)(void *)Buf;
|
|
u32 RegVal;
|
|
u32 RemIdx;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Target < XNANDPSU_MAX_TARGETS);
|
|
Xil_AssertNonvoid(Buf != NULL);
|
|
|
|
/*
|
|
* Enable Buffer Read Ready Interrupt in Interrupt Status Enable
|
|
* Register
|
|
*/
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET,
|
|
XNANDPSU_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
|
|
/* Program Command */
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_RD_ID, ONFI_CMD_INVALID, 0U,
|
|
0U, ONFI_READ_ID_ADDR_CYCLES);
|
|
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, 0U, IdAddr);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, IdLen, 1U);
|
|
/* Set Read ID in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_RD_ID_MASK);
|
|
|
|
/* Poll for Buffer Read Ready event */
|
|
Status = XNandPsu_PollRegTimeout(
|
|
InstancePtr,
|
|
XNANDPSU_INTR_STS_OFFSET,
|
|
XNANDPSU_INTR_STS_BUFF_RD_RDY_STS_EN_MASK,
|
|
XNANDPSU_INTR_POLL_TIMEOUT);
|
|
if (Status != XST_SUCCESS) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Poll for buf read ready timeout\r\n",
|
|
__func__);
|
|
#endif
|
|
goto Out;
|
|
}
|
|
/*
|
|
* Enable Transfer Complete Interrupt in Interrupt
|
|
* Status Enable Register
|
|
*/
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET,
|
|
XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
|
|
|
|
/*
|
|
* Clear Buffer Read Ready Interrupt in Interrupt Status
|
|
* Register
|
|
*/
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_OFFSET,
|
|
XNANDPSU_INTR_STS_BUFF_RD_RDY_STS_EN_MASK);
|
|
/* Read Packet Data from Data Port Register */
|
|
for (Index = 0U; Index < (IdLen/4); Index++) {
|
|
*(BufPtr+Index) = XNandPsu_ReadReg(
|
|
InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_BUF_DATA_PORT_OFFSET);
|
|
}
|
|
Rem = IdLen % 4;
|
|
if (Rem != 0U) {
|
|
RegVal = XNandPsu_ReadReg(
|
|
InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_BUF_DATA_PORT_OFFSET);
|
|
for (RemIdx = 0U; RemIdx < Rem; RemIdx++) {
|
|
*(Buf + (Index * 4U) + RemIdx) = (u8) (RegVal >>
|
|
(RemIdx * 8U)) & 0xFFU;
|
|
}
|
|
}
|
|
|
|
Status = XNandPsu_WaitFor_Transfer_Complete(InstancePtr);
|
|
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends the ONFI Read Parameter Page command to flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param PrmIndex is the index of parameter page.
|
|
* @param Buf is the parameter page information to return.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_OnfiReadParamPage(XNandPsu *InstancePtr, u32 Target,
|
|
u8 *Buf)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Target < XNANDPSU_MAX_TARGETS);
|
|
Xil_AssertNonvoid(Buf != NULL);
|
|
|
|
/*
|
|
* Enable Buffer Read Ready Interrupt in Interrupt Status Enable
|
|
* Register
|
|
*/
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET,
|
|
XNANDPSU_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
|
|
/* Program Command */
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_RD_PRM_PG, ONFI_CMD_INVALID,
|
|
0U, 0U, ONFI_PRM_PG_ADDR_CYCLES);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, 0U, 0U);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, ONFI_PRM_PG_LEN, 1U);
|
|
/* Set Read Parameter Page in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_RD_PRM_PG_MASK);
|
|
|
|
Status = XNandPsu_Data_ReadWrite(InstancePtr, Buf, 1, ONFI_PRM_PG_LEN, 0, 0);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function returns the length including bad blocks from a given offset and
|
|
* length.
|
|
*
|
|
* @param InstancePtr is the pointer to the XNandPsu instance.
|
|
* @param Offset is the flash data address to read from.
|
|
* @param Length is number of bytes to read.
|
|
*
|
|
* @return
|
|
* - Return actual length including bad blocks.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_CalculateLength(XNandPsu *InstancePtr, u64 Offset,
|
|
u64 Length)
|
|
{
|
|
s32 Status;
|
|
u32 BlockSize;
|
|
u32 BlockLen;
|
|
u32 Block;
|
|
u64 TempLen = 0;
|
|
u64 OffsetVar = Offset;
|
|
|
|
BlockSize = InstancePtr->Geometry.BlockSize;
|
|
|
|
while (TempLen < Length) {
|
|
Block = (u32)(OffsetVar/BlockSize);
|
|
BlockLen = BlockSize - (u32)(OffsetVar % BlockSize);
|
|
if (OffsetVar >= InstancePtr->Geometry.DeviceSize) {
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
/* Check if the block is bad */
|
|
Status = XNandPsu_IsBlockBad(InstancePtr, Block);
|
|
if (Status != XST_SUCCESS) {
|
|
/* Good block */
|
|
TempLen += BlockLen;
|
|
}
|
|
OffsetVar += BlockLen;
|
|
}
|
|
|
|
Status = XST_SUCCESS;
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function writes to the flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Offset is the starting offset of flash to write.
|
|
* @param Length is the number of bytes to write.
|
|
* @param SrcBuf is the source data buffer to write.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_Write(XNandPsu *InstancePtr, u64 Offset, u64 Length, u8 *SrcBuf)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
u32 Page;
|
|
u32 Col;
|
|
u32 Target;
|
|
u32 Block;
|
|
u32 PartialBytes = 0;
|
|
u32 NumBytes;
|
|
u32 RemLen;
|
|
u8 *BufPtr;
|
|
u8 *SrcBufPtr = (u8 *)SrcBuf;
|
|
u64 OffsetVar = Offset;
|
|
u64 LengthVar = Length;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
Xil_AssertNonvoid(SrcBuf != NULL);
|
|
Xil_AssertNonvoid(LengthVar != 0U);
|
|
Xil_AssertNonvoid((OffsetVar + LengthVar) <=
|
|
InstancePtr->Geometry.DeviceSize);
|
|
|
|
/*
|
|
* Check if write operation exceeds flash size when including
|
|
* bad blocks.
|
|
*/
|
|
Status = XNandPsu_CalculateLength(InstancePtr, OffsetVar, LengthVar);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
while (LengthVar > 0U) {
|
|
Block = (u32) (OffsetVar/InstancePtr->Geometry.BlockSize);
|
|
/*
|
|
* Skip the bad blocks. Increment the offset by block size.
|
|
* For better results, always program the flash starting at
|
|
* a block boundary.
|
|
*/
|
|
if (XNandPsu_IsBlockBad(InstancePtr, Block) == XST_SUCCESS) {
|
|
OffsetVar += (u64)InstancePtr->Geometry.BlockSize;
|
|
continue;
|
|
}
|
|
/* Calculate Page and Column address values */
|
|
Page = (u32) (OffsetVar/InstancePtr->Geometry.BytesPerPage);
|
|
Col = (u32) (OffsetVar &
|
|
(InstancePtr->Geometry.BytesPerPage - 1U));
|
|
PartialBytes = 0U;
|
|
/*
|
|
* Check if partial write.
|
|
* If column address is > 0 or Length is < page size
|
|
*/
|
|
if ((Col > 0U) ||
|
|
(LengthVar < InstancePtr->Geometry.BytesPerPage)) {
|
|
RemLen = InstancePtr->Geometry.BytesPerPage - Col;
|
|
PartialBytes = (RemLen < (u32)LengthVar) ?
|
|
RemLen : (u32)LengthVar;
|
|
}
|
|
|
|
Target = (u32) (OffsetVar/InstancePtr->Geometry.TargetSize);
|
|
if (Page > InstancePtr->Geometry.NumTargetPages) {
|
|
Page %= InstancePtr->Geometry.NumTargetPages;
|
|
}
|
|
|
|
/* Check if partial write */
|
|
if (PartialBytes > 0U) {
|
|
BufPtr = &InstancePtr->PartialDataBuf[0];
|
|
(void)memset(BufPtr, 0xFF,
|
|
InstancePtr->Geometry.BytesPerPage);
|
|
(void)memcpy(BufPtr + Col, SrcBufPtr, PartialBytes);
|
|
|
|
NumBytes = PartialBytes;
|
|
} else {
|
|
BufPtr = (u8 *)SrcBufPtr;
|
|
NumBytes = (InstancePtr->Geometry.BytesPerPage <
|
|
(u32)LengthVar) ?
|
|
InstancePtr->Geometry.BytesPerPage :
|
|
(u32)LengthVar;
|
|
}
|
|
/* Program page */
|
|
Status = XNandPsu_ProgramPage(InstancePtr, Target, Page, 0U,
|
|
BufPtr);
|
|
if (Status != XST_SUCCESS)
|
|
goto Out;
|
|
|
|
Status = XNandPsu_Device_Ready(InstancePtr, Target);
|
|
if (Status != XST_SUCCESS)
|
|
goto Out;
|
|
|
|
SrcBufPtr += NumBytes;
|
|
OffsetVar += NumBytes;
|
|
LengthVar -= NumBytes;
|
|
}
|
|
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function reads from the flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Offset is the starting offset of flash to read.
|
|
* @param Length is the number of bytes to read.
|
|
* @param DestBuf is the destination data buffer to fill in.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_Read(XNandPsu *InstancePtr, u64 Offset, u64 Length, u8 *DestBuf)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
u32 Page;
|
|
u32 Col;
|
|
u32 Target;
|
|
u32 Block;
|
|
u32 PartialBytes = 0U;
|
|
u32 RemLen;
|
|
u32 NumBytes;
|
|
u8 *BufPtr;
|
|
u8 *DestBufPtr = (u8 *)DestBuf;
|
|
u64 OffsetVar = Offset;
|
|
u64 LengthVar = Length;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
Xil_AssertNonvoid(LengthVar != 0U);
|
|
Xil_AssertNonvoid((OffsetVar + LengthVar) <=
|
|
InstancePtr->Geometry.DeviceSize);
|
|
|
|
/*
|
|
* Check if read operation exceeds flash size when including
|
|
* bad blocks.
|
|
*/
|
|
Status = XNandPsu_CalculateLength(InstancePtr, OffsetVar, LengthVar);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
while (LengthVar > 0U) {
|
|
Block = (u32)(OffsetVar/InstancePtr->Geometry.BlockSize);
|
|
/*
|
|
* Skip the bad block. Increment the offset by block size.
|
|
* The flash programming utility must make sure to start
|
|
* writing always at a block boundary and skip blocks if any.
|
|
*/
|
|
if (XNandPsu_IsBlockBad(InstancePtr, Block) == XST_SUCCESS) {
|
|
OffsetVar += (u64)InstancePtr->Geometry.BlockSize;
|
|
continue;
|
|
}
|
|
/* Calculate Page and Column address values */
|
|
Page = (u32) (OffsetVar/InstancePtr->Geometry.BytesPerPage);
|
|
Col = (u32) (OffsetVar &
|
|
(InstancePtr->Geometry.BytesPerPage - 1U));
|
|
PartialBytes = 0U;
|
|
/*
|
|
* Check if partial write.
|
|
* If column address is > 0 or Length is < page size
|
|
*/
|
|
if ((Col > 0U) ||
|
|
(LengthVar < InstancePtr->Geometry.BytesPerPage)) {
|
|
RemLen = InstancePtr->Geometry.BytesPerPage - Col;
|
|
PartialBytes = ((u32)RemLen < (u32)LengthVar) ?
|
|
(u32)RemLen : (u32)LengthVar;
|
|
}
|
|
|
|
Target = (u32) (OffsetVar/InstancePtr->Geometry.TargetSize);
|
|
if (Page > InstancePtr->Geometry.NumTargetPages) {
|
|
Page %= InstancePtr->Geometry.NumTargetPages;
|
|
}
|
|
/* Check if partial read */
|
|
if (PartialBytes > 0U) {
|
|
BufPtr = &InstancePtr->PartialDataBuf[0];
|
|
NumBytes = PartialBytes;
|
|
} else {
|
|
BufPtr = DestBufPtr;
|
|
NumBytes = (InstancePtr->Geometry.BytesPerPage <
|
|
(u32)LengthVar) ?
|
|
InstancePtr->Geometry.BytesPerPage :
|
|
(u32)LengthVar;
|
|
}
|
|
/* Read page */
|
|
Status = XNandPsu_ReadPage(InstancePtr, Target, Page, 0U,
|
|
BufPtr);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
if (PartialBytes > 0U) {
|
|
(void)memcpy(DestBufPtr, BufPtr + Col, NumBytes);
|
|
}
|
|
DestBufPtr += NumBytes;
|
|
OffsetVar += NumBytes;
|
|
LengthVar -= NumBytes;
|
|
}
|
|
|
|
Status = XST_SUCCESS;
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function erases the flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Offset is the starting offset of flash to erase.
|
|
* @param Length is the number of bytes to erase.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note
|
|
* The Offset and Length should be aligned to block size boundary
|
|
* to get better results.
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_Erase(XNandPsu *InstancePtr, u64 Offset, u64 Length)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
u32 Target = 0;
|
|
u32 StartBlock;
|
|
u32 NumBlocks = 0;
|
|
u32 Block;
|
|
u32 AlignOff;
|
|
u32 EraseLen;
|
|
u32 BlockRemLen;
|
|
u16 OnfiStatus;
|
|
u64 OffsetVar = Offset;
|
|
u64 LengthVar = Length;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
Xil_AssertNonvoid(LengthVar != 0U);
|
|
Xil_AssertNonvoid((OffsetVar + LengthVar) <=
|
|
InstancePtr->Geometry.DeviceSize);
|
|
|
|
/*
|
|
* Check if erase operation exceeds flash size when including
|
|
* bad blocks.
|
|
*/
|
|
Status = XNandPsu_CalculateLength(InstancePtr, OffsetVar, LengthVar);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Calculate number of blocks to erase */
|
|
StartBlock = (u32) (OffsetVar/InstancePtr->Geometry.BlockSize);
|
|
|
|
while (LengthVar > 0U) {
|
|
Block = (u32) (OffsetVar/InstancePtr->Geometry.BlockSize);
|
|
if (XNandPsu_IsBlockBad(InstancePtr, Block) ==
|
|
XST_SUCCESS) {
|
|
OffsetVar += (u64)InstancePtr->Geometry.BlockSize;
|
|
NumBlocks++;
|
|
continue;
|
|
}
|
|
|
|
AlignOff = (u32)OffsetVar &
|
|
(InstancePtr->Geometry.BlockSize - (u32)1);
|
|
if (AlignOff > 0U) {
|
|
BlockRemLen = InstancePtr->Geometry.BlockSize -
|
|
AlignOff;
|
|
EraseLen = (BlockRemLen < (u32)LengthVar) ?
|
|
BlockRemLen :(u32)LengthVar;
|
|
} else {
|
|
EraseLen = (InstancePtr->Geometry.BlockSize <
|
|
(u32)LengthVar) ?
|
|
InstancePtr->Geometry.BlockSize:
|
|
(u32)LengthVar;
|
|
}
|
|
NumBlocks++;
|
|
OffsetVar += EraseLen;
|
|
LengthVar -= EraseLen;
|
|
}
|
|
|
|
for (Block = StartBlock; Block < (StartBlock + NumBlocks); Block++) {
|
|
Target = Block/InstancePtr->Geometry.NumTargetBlocks;
|
|
Block %= InstancePtr->Geometry.NumTargetBlocks;
|
|
/* Don't erase bad block */
|
|
if (XNandPsu_IsBlockBad(InstancePtr, Block) ==
|
|
XST_SUCCESS)
|
|
continue;
|
|
/* Block Erase */
|
|
Status = XNandPsu_EraseBlock(InstancePtr, Target, Block);
|
|
if (Status != XST_SUCCESS)
|
|
goto Out;
|
|
|
|
Status = XNandPsu_Device_Ready(InstancePtr, Target);
|
|
if (Status != XST_SUCCESS)
|
|
goto Out;
|
|
|
|
}
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends ONFI Program Page command to flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Page is the page address value to program.
|
|
* @param Col is the column address value to program.
|
|
* @param Buf is the data buffer to program.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_ProgramPage(XNandPsu *InstancePtr, u32 Target, u32 Page,
|
|
u32 Col, u8 *Buf)
|
|
{
|
|
u32 AddrCycles = InstancePtr->Geometry.RowAddrCycles +
|
|
InstancePtr->Geometry.ColAddrCycles;
|
|
u32 PktSize;
|
|
u32 PktCount;
|
|
u32 BufWrCnt = 0U;
|
|
s32 Status = XST_FAILURE;
|
|
u32 IsrValue;
|
|
u32 Index;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Page < InstancePtr->Geometry.NumPages);
|
|
Xil_AssertNonvoid(Buf != NULL);
|
|
|
|
if (InstancePtr->EccCfg.CodeWordSize > 9U) {
|
|
PktSize = 1024U;
|
|
} else {
|
|
PktSize = 512U;
|
|
}
|
|
PktCount = InstancePtr->Geometry.BytesPerPage/PktSize;
|
|
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_PG_PROG1, ONFI_CMD_PG_PROG2,
|
|
1U, 1U, (u8)AddrCycles);
|
|
|
|
if (InstancePtr->DmaMode == XNANDPSU_MDMA) {
|
|
IsrValue = XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK |
|
|
XNANDPSU_INTR_STS_EN_DMA_INT_STS_EN_MASK;
|
|
Xil_DCacheFlushRange((INTPTR)(void *)Buf, (PktSize * PktCount));
|
|
XNandPsu_Update_DmaAddr(InstancePtr, Buf);
|
|
} else {
|
|
IsrValue = XNANDPSU_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK;
|
|
}
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, IsrValue);
|
|
/* Program Page Size */
|
|
XNandPsu_SetPageSize(InstancePtr);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, PktSize, PktCount);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, Page, (u16)Col);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Set ECC */
|
|
if (InstancePtr->EccMode == XNANDPSU_HWECC) {
|
|
XNandPsu_SetEccSpareCmd(InstancePtr, ONFI_CMD_CHNG_WR_COL,
|
|
InstancePtr->Geometry.ColAddrCycles);
|
|
}
|
|
/* Set Page Program in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_PG_PROG_MASK);
|
|
|
|
|
|
Status = XNandPsu_Data_ReadWrite(InstancePtr, Buf, PktCount, PktSize, 1, 1);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends ONFI Program Page command to flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Page is the page address value to program.
|
|
* @param Col is the column address value to program.
|
|
* @param Buf is the data buffer to program.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_WriteSpareBytes(XNandPsu *InstancePtr, u32 Page, u8 *Buf)
|
|
{
|
|
u32 AddrCycles = InstancePtr->Geometry.RowAddrCycles +
|
|
InstancePtr->Geometry.ColAddrCycles;
|
|
u32 Col = InstancePtr->Geometry.BytesPerPage;
|
|
u32 Target = Page/InstancePtr->Geometry.NumTargetPages;
|
|
u32 PktSize = InstancePtr->Geometry.SpareBytesPerPage;
|
|
u32 PktCount = 1U;
|
|
u32 BufWrCnt = 0U;
|
|
u32 *BufPtr = (u32 *)(void *)Buf;
|
|
u16 PreEccSpareCol = 0U;
|
|
u16 PreEccSpareWrCnt = 0U;
|
|
u16 PostEccSpareCol = 0U;
|
|
u16 PostEccSpareWrCnt = 0U;
|
|
u32 PostWrite = 0U;
|
|
OnfiCmdFormat Cmd;
|
|
s32 Status = XST_FAILURE;
|
|
u32 Index;
|
|
u32 PageVar = Page;
|
|
u32 RegVal;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
Xil_AssertNonvoid(PageVar < InstancePtr->Geometry.NumPages);
|
|
Xil_AssertNonvoid(Buf != NULL);
|
|
|
|
PageVar %= InstancePtr->Geometry.NumTargetPages;
|
|
|
|
if (InstancePtr->EccMode == XNANDPSU_HWECC) {
|
|
/* Calculate ECC free positions before and after ECC code */
|
|
PreEccSpareCol = 0x0U;
|
|
PreEccSpareWrCnt = InstancePtr->EccCfg.EccAddr -
|
|
(u16)InstancePtr->Geometry.BytesPerPage;
|
|
|
|
PostEccSpareCol = PreEccSpareWrCnt +
|
|
InstancePtr->EccCfg.EccSize;
|
|
PostEccSpareWrCnt = InstancePtr->Geometry.SpareBytesPerPage -
|
|
PostEccSpareCol;
|
|
|
|
PreEccSpareWrCnt = (PreEccSpareWrCnt/4U) * 4U;
|
|
PostEccSpareWrCnt = (PostEccSpareWrCnt/4U) * 4U;
|
|
|
|
if (PreEccSpareWrCnt > 0U) {
|
|
PktSize = PreEccSpareWrCnt;
|
|
PktCount = 1U;
|
|
Col = InstancePtr->Geometry.BytesPerPage +
|
|
PreEccSpareCol;
|
|
BufPtr = (u32 *)(void *)Buf;
|
|
if (PostEccSpareWrCnt > 0U) {
|
|
PostWrite = 1U;
|
|
}
|
|
} else if (PostEccSpareWrCnt > 0U) {
|
|
PktSize = PostEccSpareWrCnt;
|
|
PktCount = 1U;
|
|
Col = InstancePtr->Geometry.BytesPerPage +
|
|
PostEccSpareCol;
|
|
BufPtr = (u32 *)(Buf + Col);
|
|
} else {
|
|
/* No free spare bytes available for writing */
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
if (InstancePtr->DmaMode == XNANDPSU_MDMA) {
|
|
RegVal = XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK;
|
|
Xil_DCacheFlushRange((INTPTR)(void *)BufPtr, (PktSize * PktCount));
|
|
XNandPsu_Update_DmaAddr(InstancePtr, (u8 *)BufPtr);
|
|
} else {
|
|
RegVal = XNANDPSU_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK;
|
|
}
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, RegVal);
|
|
/* Program Command hack for change write column */
|
|
if (PostWrite > 0U) {
|
|
Cmd.Command1 = 0x80U;
|
|
Cmd.Command2 = 0x00U;
|
|
XNandPsu_Prepare_Cmd(InstancePtr, Cmd.Command1, Cmd.Command2,
|
|
0U , 1U, (u8)AddrCycles);
|
|
|
|
} else {
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_PG_PROG1,
|
|
ONFI_CMD_PG_PROG2, 0U , 1U, (u8)AddrCycles);
|
|
}
|
|
/* Program Page Size */
|
|
XNandPsu_SetPageSize(InstancePtr);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, PktSize, PktCount);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, PageVar, (u16)Col);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Set Page Program in Program Register */
|
|
if (PostWrite > 0U) {
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,((u32)XNANDPSU_PROG_PG_PROG_MASK |
|
|
(u32)XNANDPSU_PROG_CHNG_ROW_ADDR_MASK));
|
|
} else {
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_PG_PROG_MASK);
|
|
}
|
|
|
|
Status = XNandPsu_Data_ReadWrite(InstancePtr, (u8 *)BufPtr, PktCount,
|
|
PktSize, 1, 1);
|
|
|
|
if (InstancePtr->EccMode == XNANDPSU_HWECC) {
|
|
if (PostWrite > 0U) {
|
|
BufPtr = (u32 *)(Buf + PostEccSpareCol);
|
|
Status = XNandPsu_ChangeWriteColumn(InstancePtr,
|
|
Target,
|
|
PostEccSpareCol, PostEccSpareWrCnt, 1U,
|
|
(u8 *)(void *)BufPtr);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
}
|
|
}
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends ONFI Read Page command to flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Page is the page address value to read.
|
|
* @param Col is the column address value to read.
|
|
* @param Buf is the data buffer to fill in.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_ReadPage(XNandPsu *InstancePtr, u32 Target, u32 Page,
|
|
u32 Col, u8 *Buf)
|
|
{
|
|
u32 AddrCycles = InstancePtr->Geometry.RowAddrCycles +
|
|
InstancePtr->Geometry.ColAddrCycles;
|
|
u32 PktSize;
|
|
u32 PktCount;
|
|
u32 BufRdCnt = 0U;
|
|
s32 Status = XST_FAILURE;
|
|
u32 Index, RegVal;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Page < InstancePtr->Geometry.NumPages);
|
|
Xil_AssertNonvoid(Target < XNANDPSU_MAX_TARGETS);
|
|
|
|
if (InstancePtr->EccCfg.CodeWordSize > 9U) {
|
|
PktSize = 1024U;
|
|
} else {
|
|
PktSize = 512U;
|
|
}
|
|
PktCount = InstancePtr->Geometry.BytesPerPage/PktSize;
|
|
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_RD1, ONFI_CMD_RD2,
|
|
1U, 1U, (u8)AddrCycles);
|
|
|
|
if (InstancePtr->DmaMode == XNANDPSU_MDMA) {
|
|
RegVal = XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK |
|
|
XNANDPSU_INTR_STS_EN_DMA_INT_STS_EN_MASK;
|
|
Xil_DCacheInvalidateRange((INTPTR)(void *)Buf, (PktSize * PktCount));
|
|
XNandPsu_Update_DmaAddr(InstancePtr, Buf);
|
|
} else {
|
|
RegVal = XNANDPSU_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK;
|
|
}
|
|
/* Enable Single bit error and Multi bit error */
|
|
if (InstancePtr->EccMode == XNANDPSU_HWECC)
|
|
RegVal |= XNANDPSU_INTR_STS_EN_MUL_BIT_ERR_STS_EN_MASK |
|
|
XNANDPSU_INTR_STS_EN_ERR_INTR_STS_EN_MASK;
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, RegVal);
|
|
/* Program Page Size */
|
|
XNandPsu_SetPageSize(InstancePtr);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, Page, (u16)Col);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, PktSize, PktCount);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Set ECC */
|
|
if (InstancePtr->EccMode == XNANDPSU_HWECC) {
|
|
XNandPsu_SetEccSpareCmd(InstancePtr,
|
|
(ONFI_CMD_CHNG_RD_COL1 |
|
|
(ONFI_CMD_CHNG_RD_COL2 << (u8)8U)),
|
|
InstancePtr->Geometry.ColAddrCycles);
|
|
}
|
|
|
|
/* Set Read command in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_RD_MASK);
|
|
|
|
Status = XNandPsu_Data_ReadWrite(InstancePtr, Buf, PktCount, PktSize, 0, 1);
|
|
|
|
CheckEccError:
|
|
/* Check ECC Errors */
|
|
if (InstancePtr->EccMode == XNANDPSU_HWECC) {
|
|
/* Hamming Multi Bit Errors */
|
|
if (((u32)XNandPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_OFFSET) &
|
|
(u32)XNANDPSU_INTR_STS_MUL_BIT_ERR_STS_EN_MASK) != 0U) {
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_OFFSET,
|
|
XNANDPSU_INTR_STS_MUL_BIT_ERR_STS_EN_MASK);
|
|
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: ECC Hamming multi bit error\r\n",
|
|
__func__);
|
|
#endif
|
|
InstancePtr->Ecc_Stat_PerPage_flips =
|
|
((XNandPsu_ReadReg(
|
|
InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_ECC_ERR_CNT_OFFSET) &
|
|
0x1FF00U) >> 8U);
|
|
InstancePtr->Ecc_Stats_total_flips +=
|
|
InstancePtr->Ecc_Stat_PerPage_flips;
|
|
Status = XST_FAILURE;
|
|
}
|
|
/* Hamming Single Bit or BCH Errors */
|
|
if (((u32)XNandPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_OFFSET) &
|
|
(u32)XNANDPSU_INTR_STS_ERR_INTR_STS_EN_MASK) != 0U) {
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_OFFSET,
|
|
XNANDPSU_INTR_STS_ERR_INTR_STS_EN_MASK);
|
|
|
|
if (InstancePtr->EccCfg.IsBCH == 1U) {
|
|
InstancePtr->Ecc_Stat_PerPage_flips =
|
|
((XNandPsu_ReadReg(
|
|
InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_ECC_ERR_CNT_OFFSET)&
|
|
0x1FF00U) >> 8U);
|
|
InstancePtr->Ecc_Stats_total_flips +=
|
|
InstancePtr->Ecc_Stat_PerPage_flips;
|
|
Status = XST_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function reads spare bytes from flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Page is the page address value to read.
|
|
* @param Buf is the data buffer to fill in.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_ReadSpareBytes(XNandPsu *InstancePtr, u32 Page, u8 *Buf)
|
|
{
|
|
u32 AddrCycles = InstancePtr->Geometry.RowAddrCycles +
|
|
InstancePtr->Geometry.ColAddrCycles;
|
|
u32 Col = InstancePtr->Geometry.BytesPerPage;
|
|
u32 Target = Page/InstancePtr->Geometry.NumTargetPages;
|
|
u32 PktSize = InstancePtr->Geometry.SpareBytesPerPage;
|
|
u32 PktCount = 1U;
|
|
u32 BufRdCnt = 0U;
|
|
u32 *BufPtr = (u32 *)(void *)Buf;
|
|
s32 Status = XST_FAILURE;
|
|
u32 Index;
|
|
u32 PageVar = Page;
|
|
u32 RegVal;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
Xil_AssertNonvoid(PageVar < InstancePtr->Geometry.NumPages);
|
|
Xil_AssertNonvoid(Buf != NULL);
|
|
|
|
PageVar %= InstancePtr->Geometry.NumTargetPages;
|
|
|
|
if (InstancePtr->DmaMode == XNANDPSU_MDMA) {
|
|
RegVal = XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK;
|
|
Xil_DCacheInvalidateRange((INTPTR)(void *)Buf, (PktSize * PktCount));
|
|
XNandPsu_Update_DmaAddr(InstancePtr, Buf);
|
|
} else {
|
|
RegVal = XNANDPSU_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK;
|
|
}
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, RegVal);
|
|
/* Program Command */
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_RD1, ONFI_CMD_RD2, 0U,
|
|
1U, (u8)AddrCycles);
|
|
/* Program Page Size */
|
|
XNandPsu_SetPageSize(InstancePtr);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, PageVar, (u16)Col);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, PktSize, PktCount);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Set Read command in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_RD_MASK);
|
|
|
|
Status = XNandPsu_Data_ReadWrite(InstancePtr, Buf, PktCount, PktSize, 0, 1);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends ONFI block erase command to the flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Block is the block to erase.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_EraseBlock(XNandPsu *InstancePtr, u32 Target, u32 Block)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
u32 AddrCycles = InstancePtr->Geometry.RowAddrCycles;
|
|
u32 Page;
|
|
u32 ErasePage;
|
|
u32 EraseCol;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
Xil_AssertNonvoid(Target < XNANDPSU_MAX_TARGETS);
|
|
Xil_AssertNonvoid(Block < InstancePtr->Geometry.NumBlocks);
|
|
|
|
Page = Block * InstancePtr->Geometry.PagesPerBlock;
|
|
ErasePage = (Page >> 16U) & 0xFFFFU;
|
|
EraseCol = Page & 0xFFFFU;
|
|
|
|
/*
|
|
* Enable Transfer Complete Interrupt in Interrupt Status Enable
|
|
* Register
|
|
*/
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET,
|
|
XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
|
|
|
|
/* Program Command */
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_BLK_ERASE1,
|
|
ONFI_CMD_BLK_ERASE2, 0U , 0U, (u8)AddrCycles);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, ErasePage, (u16)EraseCol);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Set Block Erase in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_BLK_ERASE_MASK);
|
|
/* Poll for Transfer Complete event */
|
|
Status = XNandPsu_WaitFor_Transfer_Complete(InstancePtr);
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends ONFI Get Feature command to flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Feature is the feature selector.
|
|
* @param Buf is the buffer to fill feature value.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_GetFeature(XNandPsu *InstancePtr, u32 Target, u8 Feature,
|
|
u8 *Buf)
|
|
{
|
|
s32 Status;
|
|
u32 Index;
|
|
u32 PktSize = 4;
|
|
u32 PktCount = 1;
|
|
u32 *BufPtr = (u32 *)(void *)Buf;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Buf != NULL);
|
|
|
|
if (InstancePtr->DataInterface == XNANDPSU_NVDDR) {
|
|
PktSize = 8U;
|
|
}
|
|
|
|
/*
|
|
* Enable Buffer Read Ready Interrupt in Interrupt Status
|
|
* Enable Register
|
|
*/
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET,
|
|
XNANDPSU_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
|
|
/* Program Command */
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_GET_FEATURES,
|
|
ONFI_CMD_INVALID, 0U, 0U, 1U);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, 0x0U, Feature);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, PktSize, PktCount);
|
|
/* Set Read Parameter Page in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_GET_FEATURES_MASK);
|
|
|
|
Status = XNandPsu_Data_ReadWrite(InstancePtr, Buf, PktCount, PktSize, 0, 0);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function sends ONFI Set Feature command to flash.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Feature is the feature selector.
|
|
* @param Buf is the feature value to send.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_SetFeature(XNandPsu *InstancePtr, u32 Target, u8 Feature,
|
|
u8 *Buf)
|
|
{
|
|
s32 Status;
|
|
u32 Index;
|
|
u32 PktSize = 4U;
|
|
u32 PktCount = 1U;
|
|
u32 *BufPtr = (u32 *)(void *)Buf;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Buf != NULL);
|
|
if (InstancePtr->DataInterface == XNANDPSU_NVDDR) {
|
|
PktSize = 8U;
|
|
}
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, 0U);
|
|
|
|
/*
|
|
* Enable Buffer Write Ready Interrupt in Interrupt Status
|
|
* Enable Register
|
|
*/
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET,
|
|
XNANDPSU_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK);
|
|
|
|
/* Program Command */
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_SET_FEATURES,
|
|
ONFI_CMD_INVALID, 0U , 0U, 1U);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, 0x0U, Feature);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, PktSize, PktCount);
|
|
/* Set Read Parameter Page in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_SET_FEATURES_MASK);
|
|
|
|
Status = XNandPsu_Data_ReadWrite(InstancePtr, Buf, PktCount, PktSize, 1, 0);
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function changes clock frequency of flash controller.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param ClockFreq is the clock frequency to change.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_ChangeClockFreq(XNandPsu *InstancePtr, u32 ClockFreq)
|
|
{
|
|
/* Not implemented */
|
|
}
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function changes the data interface and timing mode.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param NewIntf is the new data interface.
|
|
* @param NewMode is the new timing mode.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
s32 XNandPsu_ChangeTimingMode(XNandPsu *InstancePtr,
|
|
XNandPsu_DataInterface NewIntf,
|
|
XNandPsu_TimingMode NewMode)
|
|
{
|
|
s32 Status = XST_SUCCESS;
|
|
u32 Target;
|
|
u32 Index;
|
|
u32 RegVal;
|
|
u8 Buf[4] = {0U};
|
|
u32 *Feature = (u32 *)(void *)&Buf[0];
|
|
u32 SetFeature = 0U;
|
|
u32 NewModeVar = (u32)NewMode;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
/* Check for valid input arguments */
|
|
if(((NewIntf != XNANDPSU_SDR) && (NewIntf != XNANDPSU_NVDDR)) ||
|
|
(NewModeVar > 5U)){
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
|
|
if(NewIntf == XNANDPSU_NVDDR){
|
|
NewModeVar = NewModeVar | (u32)0x10;
|
|
}
|
|
/* Get current data interface type and timing mode */
|
|
XNandPsu_DataInterface CurIntf = InstancePtr->DataInterface;
|
|
XNandPsu_TimingMode CurMode = InstancePtr->TimingMode;
|
|
|
|
/* Check if the flash is in same mode */
|
|
if ((CurIntf == NewIntf) && (CurMode == NewModeVar)) {
|
|
Status = XST_SUCCESS;
|
|
goto Out;
|
|
}
|
|
|
|
if ((CurIntf == XNANDPSU_NVDDR) && (NewIntf == XNANDPSU_SDR)) {
|
|
|
|
NewModeVar = XNANDPSU_SDR0;
|
|
|
|
/* Change the clock frequency */
|
|
XNandPsu_ChangeClockFreq(InstancePtr, XNANDPSU_SDR_CLK);
|
|
|
|
/* Update Data Interface Register */
|
|
RegVal = ((NewModeVar % 6U) << ((NewIntf == XNANDPSU_NVDDR) ? 3U : 0U)) |
|
|
((u32)NewIntf << XNANDPSU_DATA_INTF_DATA_INTF_SHIFT);
|
|
XNandPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_DATA_INTF_OFFSET, RegVal);
|
|
|
|
for (Target = 0U; Target < InstancePtr->Geometry.NumTargets;
|
|
Target++) {
|
|
Status = XNandPsu_OnfiReset(InstancePtr, Target);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
/* Set Feature */
|
|
for (Target = 0U; Target < InstancePtr->Geometry.NumTargets;
|
|
Target++) {
|
|
Status = XNandPsu_SetFeature(InstancePtr, Target, 0x01U,
|
|
(u8 *)(void *)&NewModeVar);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
InstancePtr->DataInterface = NewIntf;
|
|
InstancePtr->TimingMode = NewModeVar;
|
|
|
|
for (Target = 0U; Target < InstancePtr->Geometry.NumTargets;
|
|
Target++) {
|
|
Status = XNandPsu_GetFeature(InstancePtr, Target, 0x01U,
|
|
&Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
/* Check if set_feature was successful */
|
|
if (*Feature != NewModeVar) {
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
goto Out;
|
|
}
|
|
|
|
SetFeature = NewModeVar;
|
|
if((CurIntf == XNANDPSU_NVDDR) && (NewIntf == XNANDPSU_NVDDR)){
|
|
SetFeature |= SetFeature << 8U;
|
|
}
|
|
/* Set Feature */
|
|
for (Target = 0U; Target < InstancePtr->Geometry.NumTargets;
|
|
Target++) {
|
|
Status = XNandPsu_SetFeature(InstancePtr, Target, 0x01U,
|
|
(u8 *)(void *)&SetFeature);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
InstancePtr->DataInterface = NewIntf;
|
|
InstancePtr->TimingMode = NewModeVar;
|
|
/* Update Data Interface Register */
|
|
RegVal = ((NewMode % 6U) << ((NewIntf == XNANDPSU_NVDDR) ? 3U : 0U)) |
|
|
((u32)NewIntf << XNANDPSU_DATA_INTF_DATA_INTF_SHIFT);
|
|
XNandPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_DATA_INTF_OFFSET, RegVal);
|
|
|
|
/* Get Feature */
|
|
for (Target = 0U; Target < InstancePtr->Geometry.NumTargets;
|
|
Target++) {
|
|
Status = XNandPsu_GetFeature(InstancePtr, Target, 0x01U,
|
|
&Buf[0]);
|
|
if (Status != XST_SUCCESS) {
|
|
goto Out;
|
|
}
|
|
|
|
/* Check if set_feature was successful */
|
|
if (*Feature != NewModeVar) {
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
}
|
|
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function issues change read column and reads the data into buffer
|
|
* specified by user.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Col is the coulmn address.
|
|
* @param PktSize is the number of bytes to read.
|
|
* @param PktCount is the number of transactions to read.
|
|
* @param Buf is the data buffer to fill in.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_ChangeReadColumn(XNandPsu *InstancePtr, u32 Target,
|
|
u32 Col, u32 PktSize, u32 PktCount,
|
|
u8 *Buf)
|
|
{
|
|
u32 AddrCycles = InstancePtr->Geometry.ColAddrCycles;
|
|
u32 BufRdCnt = 0U;
|
|
u32 *BufPtr = (u32 *)(void *)Buf;
|
|
s32 Status = XST_FAILURE;
|
|
u32 Index;
|
|
u32 RegVal;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Target < XNANDPSU_MAX_TARGETS);
|
|
Xil_AssertNonvoid(Buf != NULL);
|
|
|
|
if (InstancePtr->DmaMode == XNANDPSU_MDMA) {
|
|
RegVal = XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK |
|
|
XNANDPSU_INTR_STS_EN_DMA_INT_STS_EN_MASK;
|
|
Xil_DCacheInvalidateRange((INTPTR)(void *)Buf, (PktSize * PktCount));
|
|
XNandPsu_Update_DmaAddr(InstancePtr, Buf);
|
|
} else {
|
|
RegVal = XNANDPSU_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK;
|
|
}
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, RegVal);
|
|
/* Program Command */
|
|
XNandPsu_Prepare_Cmd(InstancePtr, ONFI_CMD_CHNG_RD_COL1,
|
|
ONFI_CMD_CHNG_RD_COL2, 0U , 1U, (u8)AddrCycles);
|
|
/* Program Page Size */
|
|
XNandPsu_SetPageSize(InstancePtr);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, 0U, (u16)Col);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, PktSize, PktCount);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Set Read command in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_RD_MASK);
|
|
|
|
Status = XNandPsu_Data_ReadWrite(InstancePtr, Buf, PktCount, PktSize, 0, 1);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function issues change read column and reads the data into buffer
|
|
* specified by user.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chip select value.
|
|
* @param Col is the coulmn address.
|
|
* @param PktSize is the number of bytes to read.
|
|
* @param PktCount is the number of transactions to read.
|
|
* @param Buf is the data buffer to fill in.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_ChangeWriteColumn(XNandPsu *InstancePtr, u32 Target,
|
|
u32 Col, u32 PktSize, u32 PktCount,
|
|
u8 *Buf)
|
|
{
|
|
u32 AddrCycles = InstancePtr->Geometry.ColAddrCycles;
|
|
u32 BufWrCnt = 0U;
|
|
u32 *BufPtr = (u32 *)(void *)Buf;
|
|
s32 Status = XST_FAILURE;
|
|
OnfiCmdFormat OnfiCommand;
|
|
u32 Index;
|
|
u32 RegVal;
|
|
|
|
/* Assert the input arguments. */
|
|
Xil_AssertNonvoid(Target < XNANDPSU_MAX_TARGETS);
|
|
Xil_AssertNonvoid(Buf != NULL);
|
|
|
|
if (PktCount == 0U) {
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
if (InstancePtr->DmaMode == XNANDPSU_MDMA) {
|
|
RegVal = XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK |
|
|
XNANDPSU_INTR_STS_EN_DMA_INT_STS_EN_MASK;
|
|
XNandPsu_Update_DmaAddr(InstancePtr, Buf);
|
|
} else {
|
|
RegVal = XNANDPSU_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK;
|
|
}
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, RegVal);
|
|
/* Change write column hack */
|
|
OnfiCommand.Command1 = 0x85U;
|
|
OnfiCommand.Command2 = 0x10U;
|
|
XNandPsu_Prepare_Cmd(InstancePtr, OnfiCommand.Command1,
|
|
OnfiCommand.Command2, 0U , 0U, (u8)AddrCycles);
|
|
|
|
/* Program Page Size */
|
|
XNandPsu_SetPageSize(InstancePtr);
|
|
/* Program Column, Page, Block address */
|
|
XNandPsu_SetPageColAddr(InstancePtr, 0U, (u16)Col);
|
|
/* Program Packet Size and Packet Count */
|
|
XNandPsu_SetPktSzCnt(InstancePtr, PktSize, PktCount);
|
|
/* Program Memory Address Register2 for chip select */
|
|
XNandPsu_SelectChip(InstancePtr, Target);
|
|
/* Set Page Program in Program Register */
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_PROG_OFFSET,XNANDPSU_PROG_CHNG_ROW_ADDR_END_MASK);
|
|
|
|
Status = XNandPsu_Data_ReadWrite(InstancePtr, Buf, PktCount, PktSize, 1, 0);
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function initializes extended parameter page ECC information.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param ExtPrm is the Extended parameter page buffer.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_InitExtEcc(XNandPsu *InstancePtr, OnfiExtPrmPage *ExtPrm)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
u32 Index;
|
|
u32 SectionType;
|
|
u32 SectionLen;
|
|
u32 Offset = 0U;
|
|
u32 Found = 0U;
|
|
OnfiExtEccBlock *EccBlock;
|
|
|
|
if (ExtPrm->Section0Type != 0x2U) {
|
|
Offset += (u32)ExtPrm->Section0Len;
|
|
if (ExtPrm->Section1Type != 0x2U) {
|
|
#ifdef XNANDPSU_DEBUG
|
|
xil_printf("%s: Extended ECC section not found\r\n",__func__);
|
|
#endif
|
|
Status = XST_FAILURE;
|
|
} else {
|
|
Found = 1U;
|
|
}
|
|
} else {
|
|
Found = 1U;
|
|
}
|
|
|
|
if (Found != 0U) {
|
|
EccBlock = (OnfiExtEccBlock *)&ExtPrm->SectionData[Offset];
|
|
Xil_AssertNonvoid(EccBlock != NULL);
|
|
if (EccBlock->CodeWordSize == 0U) {
|
|
Status = XST_FAILURE;
|
|
} else {
|
|
InstancePtr->Geometry.NumBitsECC =
|
|
EccBlock->NumEccBits;
|
|
InstancePtr->Geometry.EccCodeWordSize =
|
|
(u32)EccBlock->CodeWordSize;
|
|
Status = XST_SUCCESS;
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function prepares command to be written into command register.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Cmd1 is the first Onfi Command.
|
|
* @param Cmd1 is the second Onfi Command.
|
|
* @param EccState is the flag to set Ecc State.
|
|
* @param DmaMode is the flag to set DMA mode.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
void XNandPsu_Prepare_Cmd(XNandPsu *InstancePtr, u8 Cmd1, u8 Cmd2, u8 EccState,
|
|
u8 DmaMode, u8 AddrCycles)
|
|
{
|
|
u32 RegValue = 0U;
|
|
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
|
|
RegValue = (u32)Cmd1 | (((u32)Cmd2 << (u32)XNANDPSU_CMD_CMD2_SHIFT) &
|
|
(u32)XNANDPSU_CMD_CMD2_MASK);
|
|
|
|
if ((EccState != 0U) && (InstancePtr->EccMode == XNANDPSU_HWECC)) {
|
|
RegValue |= 1U << XNANDPSU_CMD_ECC_ON_SHIFT;
|
|
}
|
|
|
|
if ((DmaMode != 0U) && (InstancePtr->DmaMode == XNANDPSU_MDMA)) {
|
|
RegValue |= XNANDPSU_MDMA << XNANDPSU_CMD_DMA_EN_SHIFT;
|
|
}
|
|
|
|
if (AddrCycles != 0U) {
|
|
RegValue |= (u32)AddrCycles <<
|
|
(u32)XNANDPSU_CMD_ADDR_CYCLES_SHIFT;
|
|
}
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_CMD_OFFSET, RegValue);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function Read/Writes data from the nand controller.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Buf is the data buffer.
|
|
* @param PktCount is the number packet chunks.
|
|
* @param PktSize is the size of the packet.
|
|
* @param Operation is 1 for write and 0 for read.
|
|
* @param DmaMode is 1 for Dma and 0 for PIO.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_Data_ReadWrite(XNandPsu *InstancePtr, u8* Buf, u32 PktCount,
|
|
u32 PktSize, u32 Operation, u8 DmaMode)
|
|
{
|
|
u32 BufRwCnt = 0U;
|
|
s32 Status = XST_FAILURE;
|
|
u32 Event = XNANDPSU_INTR_STS_BUFF_RD_RDY_STS_EN_MASK;
|
|
|
|
if ((DmaMode != 0U) && (InstancePtr->DmaMode == XNANDPSU_MDMA))
|
|
goto DmaDone;
|
|
|
|
if (Operation)
|
|
Event = XNANDPSU_INTR_STS_BUFF_WR_RDY_STS_EN_MASK;
|
|
|
|
while (BufRwCnt < PktCount) {
|
|
/* Poll for Buffer Write Ready event */
|
|
Status = XNandPsu_PollRegTimeout(InstancePtr,
|
|
XNANDPSU_INTR_STS_OFFSET, Event,
|
|
XNANDPSU_INTR_POLL_TIMEOUT);
|
|
if (Status != XST_SUCCESS) {
|
|
xil_printf("%s: Poll for buf write ready timeout\r\n",
|
|
__func__);
|
|
goto Out;
|
|
}
|
|
|
|
/* Increment Buffer Write Interrupt Count */
|
|
BufRwCnt++;
|
|
|
|
if (BufRwCnt == PktCount)
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET,
|
|
XNANDPSU_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
|
|
|
|
else
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, 0U);
|
|
/*
|
|
* Clear Buffer Write Ready Interrupt in Interrupt Status
|
|
* Register
|
|
*/
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_OFFSET, Event);
|
|
/* Write Packet Data to Data Port Register */
|
|
if (Operation)
|
|
XNandPsu_Fifo_Write(InstancePtr, Buf, PktSize);
|
|
else
|
|
XNandPsu_Fifo_Read(InstancePtr, Buf, PktSize);
|
|
|
|
Buf += PktSize;
|
|
|
|
if (BufRwCnt < PktCount)
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, Event);
|
|
else
|
|
break;
|
|
}
|
|
|
|
DmaDone:
|
|
Status = XNandPsu_WaitFor_Transfer_Complete(InstancePtr);
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function writes data to the fifo.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Buf is the buffer pointer.
|
|
* @param Size of the Buffer.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_Fifo_Write(XNandPsu *InstancePtr, u8* Buffer, u32 Size)
|
|
{
|
|
u32 *BufPtr = (u32 *)(void *)Buffer;
|
|
u32 Index;
|
|
|
|
for (Index = 0U; Index < Size/4U; Index++)
|
|
XNandPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_BUF_DATA_PORT_OFFSET,
|
|
BufPtr[Index]);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function reads data from the fifo.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Buf is the buffer pointer.
|
|
* @param Size of the Buffer.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_Fifo_Read(XNandPsu *InstancePtr, u8* Buf, u32 Size)
|
|
{
|
|
u32 *BufPtr = (u32 *)(void *)Buf;
|
|
u32 Index;
|
|
|
|
for (Index = 0U; Index < Size/4U; Index++)
|
|
BufPtr[Index] = XNandPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_BUF_DATA_PORT_OFFSET);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function configures the given dma address to the controller.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Buf is the buffer pointer.
|
|
*
|
|
* @return
|
|
* None
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static void XNandPsu_Update_DmaAddr(XNandPsu *InstancePtr, u8* Buf)
|
|
{
|
|
#ifdef __aarch64__
|
|
XNandPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_DMA_SYS_ADDR1_OFFSET,
|
|
(u32) (((INTPTR)Buf >> 32U) & 0xFFFFFFFFU));
|
|
#endif
|
|
XNandPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XNANDPSU_DMA_SYS_ADDR0_OFFSET,
|
|
(u32) ((INTPTR)(void *)Buf & 0xFFFFFFFFU));
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function waits for the device ready stataus.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
* @param Target is the chipselect value.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note None
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_Device_Ready(XNandPsu *InstancePtr, u32 Target)
|
|
{
|
|
s32 Status = XST_SUCCESS;
|
|
u16 OnfiStatus;
|
|
|
|
do {
|
|
Status = XNandPsu_OnfiReadStatus(InstancePtr, Target,
|
|
&OnfiStatus);
|
|
if (Status != XST_SUCCESS)
|
|
goto Out;
|
|
if ((OnfiStatus & (1U << 6U)) != 0U) {
|
|
if ((OnfiStatus & (1U << 0U)) != 0U) {
|
|
Status = XST_FAILURE;
|
|
goto Out;
|
|
}
|
|
}
|
|
} while (((OnfiStatus >> 6U) & 0x1U) == 0U);
|
|
|
|
Out:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function waits for the transfer complete event.
|
|
*
|
|
* @param InstancePtr is a pointer to the XNandPsu instance.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if failed.
|
|
*
|
|
* @note Expects that transfer complete event was set before calling
|
|
* this function.
|
|
*
|
|
******************************************************************************/
|
|
static s32 XNandPsu_WaitFor_Transfer_Complete(XNandPsu *InstancePtr)
|
|
{
|
|
s32 Status = XST_FAILURE;
|
|
|
|
/* Poll for Transfer Complete event */
|
|
Status = XNandPsu_PollRegTimeout(
|
|
InstancePtr,
|
|
XNANDPSU_INTR_STS_OFFSET,
|
|
XNANDPSU_INTR_STS_TRANS_COMP_STS_EN_MASK,
|
|
XNANDPSU_INTR_POLL_TIMEOUT);
|
|
if (Status != XST_SUCCESS) {
|
|
xil_printf("%s: Poll for xfer complete timeout\r\n", __func__);
|
|
goto Out;
|
|
}
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_EN_OFFSET, 0U);
|
|
|
|
XNandPsu_WriteReg((InstancePtr)->Config.BaseAddress,
|
|
XNANDPSU_INTR_STS_OFFSET,
|
|
XNANDPSU_INTR_STS_TRANS_COMP_STS_EN_MASK);
|
|
Out:
|
|
return Status;
|
|
}
|
|
/** @} */
|