embeddedsw/XilinxProcessorIPLib/drivers/nandps8_v2_0/src/xnandps8.c
Shakti Bhatnagar 9572c366af nandps8_v2_0: nandps8_v2_0: Modified XNandPs8_FlashInit API
Modified XNandPs8_FlashInit API present in xnanps8.c
Added Macro for Onfi Read ID check.

Signed-off-by: Shakti Bhatnagar <shaktib@xilinx.com>
2014-12-15 10:48:54 +05:30

4125 lines
107 KiB
C
Executable file

/******************************************************************************
*
* Copyright (C) 2014 Xilinx, Inc. All rights reserved.
*
* This file contains confidential and proprietary information of Xilinx, Inc.
* and is protected under U.S. and international copyright and other
* intellectual property laws.
*
* DISCLAIMER
* This disclaimer is not a license and does not grant any rights to the
* materials distributed herewith. Except as otherwise provided in a valid
* license issued to you by Xilinx, and to the maximum extent permitted by
* applicable law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND WITH ALL
* FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS,
* IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
* MERCHANTABILITY, NON- INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE
* and
* (2) Xilinx shall not be liable (whether in contract or tort, including
* negligence, or under any other theory of liability) for any loss or damage
* of any kind or nature related to, arising under or in connection with these
* materials, including for any direct, or any indirect, special, incidental,
* or consequential loss or damage (including loss of data, profits,
* goodwill, or any type of loss or damage suffered as a result of any
* action brought by a third party) even if such damage or loss was
* reasonably foreseeable or Xilinx had been advised of the possibility
* of the same.
*
* CRITICAL APPLICATIONS
* Xilinx products are not designed or intended to be fail- safe, or for use
* in any application requiring fail-safe performance, such as life-support
* or safety devices or systems, Class III medical devices, nuclear
* facilities, applications related to the deployment of airbags, or any
* other applications that could lead to death, personal injury, or severe
* property or environmental damage (individually and collectively,
* "Critical Applications"). Customer assumes the sole risk and liability
* of any use of Xilinx products in Critical Applications, subject only to
* applicable laws and regulations governing limitations on product liability.
*
* THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART
* OF THIS FILE AT ALL TIMES.
*
******************************************************************************/
/*****************************************************************************/
/**
*
* @file xnandps8.c
*
* This file contains the implementation of the interface functions for
* XNandPs8 driver. Refer to the header file xnandps8.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 None
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- ---------- -----------------------------------------------
* 1.0 nm 05/06/2014 First release
* 2.0 sb 11/04/2014 Removed Null checks for Buffer passed
* as parameter to Read API's
* - XNandPs8_Read()
* - XNandPs8_ReadPage
* Modified
* - XNandPs8_SetFeature()
* - XNandPs8_GetFeature()
* and made them public.
* Removed Failure Return for BCF Error check in
* XNandPs8_ReadPage() and added BCH_Error counter
* in the instance pointer structure.
* Added XNandPs8_Prepare_Cmd API
* Replaced
* - XNandPs8_IntrStsEnable
* - XNandPs8_IntrStsClear
* - XNandPs8_IntrClear
* - XNandPs8_SetProgramReg
* with XNandPs8_WriteReg call
* Modified xnandps8.c file API's with above changes.
* Corrected the program command for Set Feature API.
* Modified
* - XNandPs8_OnfiReadStatus
* - XNandPs8_GetFeature
* - XNandPs8_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 XNandPs8_CfgInitialize API.
* Modified
* - XNandPs8_SetEccAddrSize
* ECC address now is calculated based upon the
* size of spare area
* </pre>
*
******************************************************************************/
/***************************** Include Files *********************************/
#include "xnandps8.h"
#include "xnandps8_bbm.h"
/************************** Constant Definitions *****************************/
const static XNandPs8_TimingModeDesc TimingDesc[] = {
/*
* SDR to SDR
*/
{SDR, SDR, SDR0, 0U, 0x00000000U},
{SDR, SDR, SDR1, 0U, 0x00000001U},
{SDR, SDR, SDR2, 0U, 0x00000002U},
{SDR, SDR, SDR3, 0U, 0x00000003U},
{SDR, SDR, SDR4, 0U, 0x00000004U},
{SDR, SDR, SDR5, 0U, 0x00000005U},
/*
* NVDDR to NVDDR
*/
{NVDDR, NVDDR, NVDDR0, NVDDR_CLK_0, 0x00001010U},
{NVDDR, NVDDR, NVDDR1, NVDDR_CLK_1, 0x00001111U},
{NVDDR, NVDDR, NVDDR2, NVDDR_CLK_2, 0x00001212U},
{NVDDR, NVDDR, NVDDR3, NVDDR_CLK_3, 0x00001313U},
{NVDDR, NVDDR, NVDDR4, NVDDR_CLK_4, 0x00001414U},
{NVDDR, NVDDR, NVDDR5, NVDDR_CLK_5, 0x00001515U},
/*
* SDR to NVDDR
*/
{SDR, NVDDR, NVDDR0, NVDDR_CLK_0, 0x00000010U},
{SDR, NVDDR, NVDDR1, NVDDR_CLK_1, 0x00000011U},
{SDR, NVDDR, NVDDR2, NVDDR_CLK_2, 0x00000012U},
{SDR, NVDDR, NVDDR3, NVDDR_CLK_3, 0x00000013U},
{SDR, NVDDR, NVDDR4, NVDDR_CLK_4, 0x00000014U},
{SDR, NVDDR, NVDDR5, NVDDR_CLK_5, 0x00000015U},
/*
* NVDDR to SDR
*/
{NVDDR, SDR, SDR0, SDR_CLK, 0U},
};
const XNandPs8_EccMatrix EccMatrix[] = {
/*
* 512 byte page
*/
{XNANDPS8_PAGE_SIZE_512, 9U, 1U, XNANDPS8_HAMMING, 0x20DU, 0x3U},
{XNANDPS8_PAGE_SIZE_512, 9U, 4U, XNANDPS8_BCH, 0x209U, 0x7U},
{XNANDPS8_PAGE_SIZE_512, 9U, 8U, XNANDPS8_BCH, 0x203U, 0xDU},
/*
* 2K byte page
*/
{XNANDPS8_PAGE_SIZE_2K, 9U, 1U, XNANDPS8_HAMMING, 0x834U, 0xCU},
{XNANDPS8_PAGE_SIZE_2K, 9U, 4U, XNANDPS8_BCH, 0x826U, 0x1AU},
{XNANDPS8_PAGE_SIZE_2K, 9U, 8U, XNANDPS8_BCH, 0x80cU, 0x34U},
{XNANDPS8_PAGE_SIZE_2K, 9U, 12U, XNANDPS8_BCH, 0x822U, 0x4EU},
{XNANDPS8_PAGE_SIZE_2K, 9U, 16U, XNANDPS8_BCH, 0x808U, 0x68U},
{XNANDPS8_PAGE_SIZE_2K, 10U, 24U, XNANDPS8_BCH, 0x81cU, 0x54U},
/*
* 4K byte page
*/
{XNANDPS8_PAGE_SIZE_4K, 9U, 1U, XNANDPS8_HAMMING, 0x1068U, 0x18U},
{XNANDPS8_PAGE_SIZE_4K, 9U, 4U, XNANDPS8_BCH, 0x104cU, 0x34U},
{XNANDPS8_PAGE_SIZE_4K, 9U, 8U, XNANDPS8_BCH, 0x1018U, 0x68U},
{XNANDPS8_PAGE_SIZE_4K, 9U, 12U, XNANDPS8_BCH, 0x1044U, 0x9CU},
{XNANDPS8_PAGE_SIZE_4K, 9U, 16U, XNANDPS8_BCH, 0x1010U, 0xD0U},
{XNANDPS8_PAGE_SIZE_4K, 10U, 24U, XNANDPS8_BCH, 0x1038U, 0xA8U},
/*
* 8K byte page
*/
{XNANDPS8_PAGE_SIZE_8K, 9U, 1U, XNANDPS8_HAMMING, 0x20d0U, 0x30U},
{XNANDPS8_PAGE_SIZE_8K, 9U, 4U, XNANDPS8_BCH, 0x2098U, 0x68U},
{XNANDPS8_PAGE_SIZE_8K, 9U, 8U, XNANDPS8_BCH, 0x2030U, 0xD0U},
{XNANDPS8_PAGE_SIZE_8K, 9U, 12U, XNANDPS8_BCH, 0x2088U, 0x138U},
{XNANDPS8_PAGE_SIZE_8K, 9U, 16U, XNANDPS8_BCH, 0x2020U, 0x1A0U},
{XNANDPS8_PAGE_SIZE_8K, 10U, 24U, XNANDPS8_BCH, 0x2070U, 0x150U},
/*
* 16K byte page
*/
{XNANDPS8_PAGE_SIZE_16K, 9U, 1U, XNANDPS8_HAMMING, 0x4460U, 0x60U},
{XNANDPS8_PAGE_SIZE_16K, 9U, 4U, XNANDPS8_BCH, 0x43f0U, 0xD0U},
{XNANDPS8_PAGE_SIZE_16K, 9U, 8U, XNANDPS8_BCH, 0x4320U, 0x1A0U},
{XNANDPS8_PAGE_SIZE_16K, 9U, 12U, XNANDPS8_BCH, 0x4250U, 0x270U},
{XNANDPS8_PAGE_SIZE_16K, 9U, 16U, XNANDPS8_BCH, 0x4180U, 0x340U},
{XNANDPS8_PAGE_SIZE_16K, 10U, 24U, XNANDPS8_BCH, 0x4220U, 0x2A0U}
};
/**************************** Type Definitions *******************************/
static u8 isQemuPlatform = 0U;
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
static s32 XNandPs8_FlashInit(XNandPs8 *InstancePtr);
static void XNandPs8_InitGeometry(XNandPs8 *InstancePtr, OnfiParamPage *Param);
static void XNandPs8_InitFeatures(XNandPs8 *InstancePtr, OnfiParamPage *Param);
static s32 XNandPs8_PollRegTimeout(XNandPs8 *InstancePtr, u32 RegOffset,
u32 Mask, u32 Timeout);
static void XNandPs8_SetPktSzCnt(XNandPs8 *InstancePtr, u32 PktSize,
u32 PktCount);
static void XNandPs8_SetPageColAddr(XNandPs8 *InstancePtr, u32 Page, u16 Col);
static void XNandPs8_SetPageSize(XNandPs8 *InstancePtr);
static void XNandPs8_SetBusWidth(XNandPs8 *InstancePtr);
static void XNandPs8_SelectChip(XNandPs8 *InstancePtr, u32 Target);
static s32 XNandPs8_OnfiReset(XNandPs8 *InstancePtr, u32 Target);
static s32 XNandPs8_OnfiReadStatus(XNandPs8 *InstancePtr, u32 Target,
u16 *OnfiStatus);
static s32 XNandPs8_OnfiReadId(XNandPs8 *InstancePtr, u32 Target, u8 IdAddr,
u32 IdLen, u8 *Buf);
static s32 XNandPs8_OnfiReadParamPage(XNandPs8 *InstancePtr, u32 Target,
u8 *Buf);
static s32 XNandPs8_ProgramPage(XNandPs8 *InstancePtr, u32 Target, u32 Page,
u32 Col, u8 *Buf);
static s32 XNandPs8_ReadPage(XNandPs8 *InstancePtr, u32 Target, u32 Page,
u32 Col, u8 *Buf);
static s32 XNandPs8_CheckOnDie(XNandPs8 *InstancePtr, OnfiParamPage *Param);
static void XNandPs8_SetEccAddrSize(XNandPs8 *InstancePtr);
static s32 XNandPs8_ChangeReadColumn(XNandPs8 *InstancePtr, u32 Target,
u32 Col, u32 PktSize, u32 PktCount,
u8 *Buf);
static s32 XNandPs8_ChangeWriteColumn(XNandPs8 *InstancePtr, u32 Target,
u32 Col, u32 PktSize, u32 PktCount,
u8 *Buf);
static s32 XNandPs8_InitExtEcc(XNandPs8 *InstancePtr, OnfiExtPrmPage *ExtPrm);
/*****************************************************************************/
/**
*
* This function initializes a specific XNandPs8 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 XNandPs8 instance.
* @param ConfigPtr points to XNandPs8 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 XNandPs8_LookupConfig() API
* which returns the Configuration structure pointer which is
* passed as a parameter to the XNandPs8_CfgInitialize() API.
*
******************************************************************************/
s32 XNandPs8_CfgInitialize(XNandPs8 *InstancePtr, XNandPs8_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 = POLLING;
/*
* Enable MDMA mode by default
*/
InstancePtr->DmaMode = MDMA;
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
/*
* Temporary hack for disabling the ecc on qemu as currently there
* is no support in the utility for writing images with ecc enabled.
*/
#define CSU_VER_REG 0xFFCA0044U
#define CSU_VER_PLATFORM_MASK 0xF000U
#define CSU_VER_PLATFORM_QEMU_VAL 0x3000U
if ((*(u32 *)CSU_VER_REG & CSU_VER_PLATFORM_MASK) ==
CSU_VER_PLATFORM_QEMU_VAL) {
isQemuPlatform = 1U;
}
/*
* Initialize the NAND flash targets
*/
Status = XNandPs8_FlashInit(InstancePtr);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Flash init failed\r\n",__func__);
#endif
goto Out;
}
/*
* Set ECC mode
*/
if (InstancePtr->Features.EzNand != 0U) {
InstancePtr->EccMode = EZNAND;
} else if (InstancePtr->Features.OnDie != 0U) {
InstancePtr->EccMode = ONDIE;
} else {
InstancePtr->EccMode = HWECC;
}
if (isQemuPlatform != 0U) {
InstancePtr->EccMode = NONE;
goto Out;
}
/*
* Initialize BCH Error counter
*/
InstancePtr->BCH_Error_Status = 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.
*/
XNandPs8_InitBbtDesc(InstancePtr);
Status = XNandPs8_ScanBbt(InstancePtr);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_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 XNandPs8 instance.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if failed.
*
* @note None
*
******************************************************************************/
static s32 XNandPs8_FlashInit(XNandPs8 *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);
for (Target = 0U; Target < XNANDPS8_MAX_TARGETS; Target++) {
/*
* Reset the Target
*/
Status = XNandPs8_OnfiReset(InstancePtr, Target);
if (Status != XST_SUCCESS) {
goto Out;
}
/*
* Read ONFI ID
*/
Status = XNandPs8_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 XNANDPS8_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 = XNandPs8_OnfiReadParamPage(InstancePtr,
Target, (u8 *)&Param);
} else {
PrmPgOff = Index * ONFI_PRM_PG_LEN;
PrmPgLen = ONFI_PRM_PG_LEN;
Status = XNandPs8_ChangeReadColumn(InstancePtr,
Target,PrmPgOff,
ONFI_PRM_PG_LEN, 1U,
(u8 *) &Param);
}
if (Status != XST_SUCCESS) {
goto Out;
}
/* Check CRC */
Crc = XNandPs8_OnfiParamPageCrc((u8*)&Param, 0U,
ONFI_CRC_LEN);
if (Crc != Param.Crc) {
#ifdef XNANDPS8_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) {
XNandPs8_InitGeometry(InstancePtr, &Param);
XNandPs8_InitFeatures(InstancePtr, &Param);
if ((!InstancePtr->Features.EzNand) != 0U) {
Status =XNandPs8_CheckOnDie(InstancePtr,&Param);
if (Status != XST_SUCCESS) {
InstancePtr->Features.OnDie = 0U;
}
}
if (isQemuPlatform != 0U) {
InstancePtr->Geometry.NumTargets++;
break;
}
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 = XNandPs8_ChangeReadColumn(
InstancePtr,
Target,
PrmPgOff,
PrmPgLen, 1U,
(u8 *)(void *)&ExtParam);
if (Status != XST_SUCCESS) {
goto Out;
}
/*
* Check CRC
*/
Crc = XNandPs8_OnfiParamPageCrc(
(u8 *)&ExtParam,
2U,
PrmPgLen);
if (Crc != ExtParam.Crc) {
#ifdef XNANDPS8_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 = XNandPs8_InitExtEcc(
InstancePtr,
&ExtParam);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Init extended ecc failed\r\n",__func__);
#endif
goto Out;
}
}
/* Configure ECC settings */
XNandPs8_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 XNandPs8 instance.
* @param Param is pointer to the ONFI parameter page.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_InitGeometry(XNandPs8 *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 */
#ifdef XNANDPS8_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 XNandPs8 instance.
* @param Param is pointer to ONFI parameter page buffer.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_InitFeatures(XNandPs8 *InstancePtr, OnfiParamPage *Param)
{
/*
* Assert the input arguments.
*/
Xil_AssertVoid(Param != NULL);
InstancePtr->Features.BusWidth = ((Param->Features & (1U << 0U)) != 0U) ?
XNANDPS8_BUS_WIDTH_16 :
XNANDPS8_BUS_WIDTH_8;
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 XNandPs8 instance.
* @param Param is pointer to ONFI parameter page.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static s32 XNandPs8_CheckOnDie(XNandPs8 *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 = XNandPs8_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 XNANDPS8_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 = XNandPs8_SetFeature(InstancePtr, 0U, 0x90U,
&EccSetFeature[0]);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Ondie set_feature failed\r\n",
__func__);
#endif
goto Out;
}
/*
* Check to see if ECC feature is set
*/
Status = XNandPs8_GetFeature(InstancePtr, 0U, 0x90U,
&EccGetFeature[0]);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_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 XNandPs8 instance.
*
* @return
* None
*
* @note None
*
******************************************************************************/
void XNandPs8_EnableDmaMode(XNandPs8 *InstancePtr)
{
/*
* Assert the input arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
InstancePtr->DmaMode = MDMA;
}
/*****************************************************************************/
/**
*
* This function disables DMA mode of driver/controller operation.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
*
* @return
* None
*
* @note None
*
******************************************************************************/
void XNandPs8_DisableDmaMode(XNandPs8 *InstancePtr)
{
/*
* Assert the input arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
InstancePtr->DmaMode = PIO;
}
/*****************************************************************************/
/**
*
* This function enables ECC mode of driver/controller operation.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
*
* @return
* None
*
* @note None
*
******************************************************************************/
void XNandPs8_EnableEccMode(XNandPs8 *InstancePtr)
{
/*
* Assert the input arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
InstancePtr->EccMode = HWECC;
}
/*****************************************************************************/
/**
*
* This function disables ECC mode of driver/controller operation.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
*
* @return
* None
*
* @note None
*
******************************************************************************/
void XNandPs8_DisableEccMode(XNandPs8 *InstancePtr)
{
/*
* Assert the input arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
InstancePtr->EccMode = NONE;
}
/*****************************************************************************/
/**
*
* This function polls for a register bit set status till the timeout.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_PollRegTimeout(XNandPs8 *InstancePtr, u32 RegOffset,
u32 Mask, u32 Timeout)
{
s32 Status = XST_FAILURE;
volatile u32 RegVal;
u32 TimeoutVar = Timeout;
while (TimeoutVar > 0U) {
RegVal = XNandPs8_ReadReg(InstancePtr->Config.BaseAddress,
RegOffset);
if ((RegVal & Mask) != 0U) {
break;
}
TimeoutVar--;
}
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 XNandPs8 instance.
* @param PktSize is the packet size.
* @param PktCount is the packet count.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_SetPktSzCnt(XNandPs8 *InstancePtr, u32 PktSize,
u32 PktCount)
{
/*
* Assert the input arguments.
*/
Xil_AssertVoid(PktSize <= XNANDPS8_MAX_PKT_SIZE);
Xil_AssertVoid(PktCount <= XNANDPS8_MAX_PKT_COUNT);
/*
* Update Packet Register with pkt size and count
*/
XNandPs8_ReadModifyWrite(InstancePtr, XNANDPS8_PKT_OFFSET,
((u32)XNANDPS8_PKT_PKT_SIZE_MASK |
(u32)XNANDPS8_PKT_PKT_CNT_MASK),
((PktSize & XNANDPS8_PKT_PKT_SIZE_MASK) |
((PktCount << XNANDPS8_PKT_PKT_CNT_SHIFT) &
XNANDPS8_PKT_PKT_CNT_MASK)));
}
/*****************************************************************************/
/**
*
* This function sets Page and Column values in the Memory address registers.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
* @param Page is the page value.
* @param Col is the column value.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_SetPageColAddr(XNandPs8 *InstancePtr, u32 Page, u16 Col)
{
/*
* Program Memory Address Register 1
*/
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_MEM_ADDR1_OFFSET,
((Col & XNANDPS8_MEM_ADDR1_COL_ADDR_MASK) |
((Page << (u32)XNANDPS8_MEM_ADDR1_PG_ADDR_SHIFT) &
XNANDPS8_MEM_ADDR1_PG_ADDR_MASK)));
/*
* Program Memory Address Register 2
*/
XNandPs8_ReadModifyWrite(InstancePtr, XNANDPS8_MEM_ADDR2_OFFSET,
XNANDPS8_MEM_ADDR2_MEM_ADDR_MASK,
((Page >> XNANDPS8_MEM_ADDR1_PG_ADDR_SHIFT) &
XNANDPS8_MEM_ADDR2_MEM_ADDR_MASK));
}
/*****************************************************************************/
/**
*
* This function sets the size of page in Command Register.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_SetPageSize(XNandPs8 *InstancePtr)
{
u32 PageSizeMask = 0;
u32 PageSize = InstancePtr->Geometry.BytesPerPage;
/*
* Calculate page size mask
*/
switch(PageSize) {
case XNANDPS8_PAGE_SIZE_512:
PageSizeMask = (0U << XNANDPS8_CMD_PG_SIZE_SHIFT);
break;
case XNANDPS8_PAGE_SIZE_2K:
PageSizeMask = (1U << XNANDPS8_CMD_PG_SIZE_SHIFT);
break;
case XNANDPS8_PAGE_SIZE_4K:
PageSizeMask = (2U << XNANDPS8_CMD_PG_SIZE_SHIFT);
break;
case XNANDPS8_PAGE_SIZE_8K:
PageSizeMask = (3U << XNANDPS8_CMD_PG_SIZE_SHIFT);
break;
case XNANDPS8_PAGE_SIZE_16K:
PageSizeMask = (4U << XNANDPS8_CMD_PG_SIZE_SHIFT);
break;
case XNANDPS8_PAGE_SIZE_1K_16BIT:
PageSizeMask = (5U << XNANDPS8_CMD_PG_SIZE_SHIFT);
break;
default:
/*
* Not supported
*/
break;
}
/*
* Update Command Register
*/
XNandPs8_ReadModifyWrite(InstancePtr, XNANDPS8_CMD_OFFSET,
XNANDPS8_CMD_PG_SIZE_MASK, PageSizeMask);
}
/*****************************************************************************/
/**
*
* This function setup the Ecc Register.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_SetEccAddrSize(XNandPs8 *InstancePtr)
{
u32 PageSize = InstancePtr->Geometry.BytesPerPage;
u32 CodeWordSize = InstancePtr->Geometry.EccCodeWordSize;
u32 NumEccBits = InstancePtr->Geometry.NumBitsECC;
u32 Index;
u32 Found = 0U;
u8 BchModeVal = 0U;
for (Index = 0U; Index < (sizeof(EccMatrix)/sizeof(XNandPs8_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) {
#ifdef XNANDPS8_DEBUG
xil_printf("ECC: addr 0x%x size 0x%x numbits %d codesz %d\r\n",
PageSize + (InstancePtr->Geometry.SpareBytesPerPage
- EccMatrix[Found].EccSize),
EccMatrix[Found].EccSize,
EccMatrix[Found].NumEccBits,
EccMatrix[Found].CodeWordSize);
#endif
InstancePtr->EccCfg.EccAddr = 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;
if (EccMatrix[Found].IsBCH == XNANDPS8_HAMMING) {
InstancePtr->EccCfg.IsBCH = 0U;
} else {
InstancePtr->EccCfg.IsBCH = 1U;
}
/*
* Write ECC register
*/
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
(u32)XNANDPS8_ECC_OFFSET,
((u32)InstancePtr->EccCfg.EccAddr |
((u32)InstancePtr->EccCfg.EccSize << (u32)16) |
((u32)InstancePtr->EccCfg.IsBCH << (u32)27)));
if (EccMatrix[Found].IsBCH == XNANDPS8_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;
}
XNandPs8_ReadModifyWrite(InstancePtr,
XNANDPS8_MEM_ADDR2_OFFSET,
XNANDPS8_MEM_ADDR2_NFC_BCH_MODE_MASK,
(BchModeVal <<
(u32)XNANDPS8_MEM_ADDR2_NFC_BCH_MODE_SHIFT));
}
}
}
/*****************************************************************************/
/**
*
* This function setup the Ecc Spare Command Register.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_SetEccSpareCmd(XNandPs8 *InstancePtr, u16 SpareCmd,
u8 AddrCycles)
{
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
(u32)XNANDPS8_ECC_SPR_CMD_OFFSET,
(u32)SpareCmd | ((u32)AddrCycles << 28U));
}
/*****************************************************************************/
/**
*
* This function sets the flash bus width in memory address2 register.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_SetBusWidth(XNandPs8 *InstancePtr)
{
/*
* Update Memory Address2 register with bus width
*/
XNandPs8_ReadModifyWrite(InstancePtr, XNANDPS8_MEM_ADDR2_OFFSET,
XNANDPS8_MEM_ADDR2_BUS_WIDTH_MASK,
(InstancePtr->Features.BusWidth <<
XNANDPS8_MEM_ADDR2_BUS_WIDTH_SHIFT));
}
/*****************************************************************************/
/**
*
* This function sets the chip select value in memory address2 register.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
* @param Target is the chip select value.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_SelectChip(XNandPs8 *InstancePtr, u32 Target)
{
/*
* Update Memory Address2 register with chip select
*/
XNandPs8_ReadModifyWrite(InstancePtr, XNANDPS8_MEM_ADDR2_OFFSET,
XNANDPS8_MEM_ADDR2_CHIP_SEL_MASK,
((Target << XNANDPS8_MEM_ADDR2_CHIP_SEL_SHIFT) &
XNANDPS8_MEM_ADDR2_CHIP_SEL_MASK));
}
/*****************************************************************************/
/**
*
* This function sends ONFI Reset command to the flash.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
* @param Target is the chip select value.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if failed.
*
* @note None
*
******************************************************************************/
static s32 XNandPs8_OnfiReset(XNandPs8 *InstancePtr, u32 Target)
{
s32 Status = XST_FAILURE;
/*
* Assert the input arguments.
*/
Xil_AssertNonvoid(Target < XNANDPS8_MAX_TARGETS);
/*
* Enable Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
/*
* Program Command Register
*/
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_RST, ONFI_CMD_INVALID, 0U,
0U, 0U);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Set Reset in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET, XNANDPS8_PROG_RST_MASK);
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
(XNANDPS8_INTR_STS_EN_OFFSET), 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function sends ONFI Read Status command to the flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_OnfiReadStatus(XNandPs8 *InstancePtr, u32 Target,
u16 *OnfiStatus)
{
s32 Status = XST_FAILURE;
/*
* Assert the input arguments.
*/
Xil_AssertNonvoid(Target < XNANDPS8_MAX_TARGETS);
Xil_AssertNonvoid(OnfiStatus != NULL);
/*
* Enable Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
/*
* Program Command Register
*/
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_RD_STS, ONFI_CMD_INVALID,
0U, 0U, 0U);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Program Packet Size and Packet Count
*/
if(InstancePtr->DataInterface == SDR){
XNandPs8_SetPktSzCnt(InstancePtr, 1U, 1U);
}
else{
XNandPs8_SetPktSzCnt(InstancePtr, 2U, 1U);
}
/*
* Set Read Status in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_RD_STS_MASK);
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
/*
* Read Flash Status
*/
*OnfiStatus = (u8) XNandPs8_ReadReg(InstancePtr->Config.BaseAddress,
XNANDPS8_FLASH_STS_OFFSET);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function sends ONFI Read ID command to the flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_OnfiReadId(XNandPs8 *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 < XNANDPS8_MAX_TARGETS);
Xil_AssertNonvoid(Buf != NULL);
/*
* Enable Buffer Read Ready Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
/*
* Program Command
*/
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_RD_ID, ONFI_CMD_INVALID, 0U,
0U, ONFI_READ_ID_ADDR_CYCLES);
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, 0U, IdAddr);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, IdLen, 1U);
/*
* Set Read ID in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_RD_ID_MASK);
/*
* Poll for Buffer Read Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_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
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
/*
* Clear Buffer Read Ready Interrupt in Interrupt Status
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK);
/*
* Read Packet Data from Data Port Register
*/
for (Index = 0U; Index < (IdLen/4); Index++) {
BufPtr[Index] = XNandPs8_ReadReg(
InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET);
}
Rem = IdLen % 4;
if (Rem != 0U) {
RegVal = XNandPs8_ReadReg(
InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET);
for (RemIdx = 0U; RemIdx < Rem; RemIdx++) {
Buf[(Index * 4U) + RemIdx] = (u8) (RegVal >>
(RemIdx * 8U)) & 0xFFU;
}
}
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function sends the ONFI Read Parameter Page command to flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_OnfiReadParamPage(XNandPs8 *InstancePtr, u32 Target,
u8 *Buf)
{
s32 Status = XST_FAILURE;
u32 *BufPtr = (u32 *)(void *)Buf;
u32 Index;
/*
* Assert the input arguments.
*/
Xil_AssertNonvoid(Target < XNANDPS8_MAX_TARGETS);
Xil_AssertNonvoid(Buf != NULL);
/*
* Enable Buffer Read Ready Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
/*
* Program Command
*/
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_RD_PRM_PG, ONFI_CMD_INVALID,
0U, 0U, ONFI_PRM_PG_ADDR_CYCLES);
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, 0U, 0U);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, ONFI_PRM_PG_LEN, 1U);
/*
* Set Read Parameter Page in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_RD_PRM_PG_MASK);
/*
* Poll for Buffer Read Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_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
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
(XNANDPS8_INTR_STS_EN_OFFSET),
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
/*
* Clear Buffer Read Ready Interrupt in Interrupt Status
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK);
/*
* Read Packet Data from Data Port Register
*/
for (Index = 0U; Index < (ONFI_PRM_PG_LEN/4); Index++) {
BufPtr[Index] = XNandPs8_ReadReg(
InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET);
}
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function returns the length including bad blocks from a given offset and
* length.
*
* @param InstancePtr is the pointer to the XNandPs8 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 XNandPs8_CalculateLength(XNandPs8 *InstancePtr, u64 Offset,
u64 Length)
{
s32 Status;
u32 BlockSize;
u32 BlockLen;
u32 Block;
u32 TempLen = 0;
u64 OffsetVar = Offset;
BlockSize = InstancePtr->Geometry.BlockSize;
while (TempLen < Length) {
Block = (u32) ((u32)OffsetVar/BlockSize);
BlockLen = BlockSize - ((u32)OffsetVar % BlockSize);
/*
* Check if the block is bad
*/
Status = XNandPs8_IsBlockBad(InstancePtr, Block);
if (Status != XST_SUCCESS) {
/*
* Good block
*/
TempLen += BlockLen;
}
if (OffsetVar >= InstancePtr->Geometry.DeviceSize) {
Status = XST_FAILURE;
goto Out;
}
OffsetVar += BlockLen;
}
Status = XST_SUCCESS;
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function writes to the flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_Write(XNandPs8 *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 *Ptr = (u8 *)SrcBuf;
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(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 = XNandPs8_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 (XNandPs8_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];
memset(BufPtr, 0xFF,
InstancePtr->Geometry.BytesPerPage);
memcpy(BufPtr + Col, Ptr, PartialBytes);
NumBytes = PartialBytes;
} else {
BufPtr = (u8 *)Ptr;
NumBytes = (InstancePtr->Geometry.BytesPerPage <
(u32)LengthVar) ?
InstancePtr->Geometry.BytesPerPage :
(u32)LengthVar;
}
/*
* Program page
*/
Status = XNandPs8_ProgramPage(InstancePtr, Target, Page, 0U,
BufPtr);
if (Status != XST_SUCCESS) {
goto Out;
}
/*
* ONFI ReadStatus
*/
do {
Status = XNandPs8_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);
Ptr += NumBytes;
OffsetVar += NumBytes;
LengthVar -= NumBytes;
}
Status = XST_SUCCESS;
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function reads from the flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_Read(XNandPs8 *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 *Ptr = (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 = XNandPs8_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 (XNandPs8_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 = Ptr;
NumBytes = (InstancePtr->Geometry.BytesPerPage <
(u32)LengthVar) ?
InstancePtr->Geometry.BytesPerPage :
(u32)LengthVar;
}
/*
* Read page
*/
Xil_AssertNonvoid(BufPtr != NULL);
Status = XNandPs8_ReadPage(InstancePtr, Target, Page, 0U,
BufPtr);
if (Status != XST_SUCCESS) {
goto Out;
}
if (PartialBytes > 0U) {
memcpy(Ptr, BufPtr + Col, NumBytes);
}
Ptr += NumBytes;
OffsetVar += NumBytes;
LengthVar -= NumBytes;
}
Status = XST_SUCCESS;
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function erases the flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_Erase(XNandPs8 *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 = XNandPs8_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 (XNandPs8_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;
if (XNandPs8_IsBlockBad(InstancePtr, Block) ==
XST_SUCCESS) {
/*
* Don't erase bad block
*/
continue;
}
/*
* Block Erase
*/
Status = XNandPs8_EraseBlock(InstancePtr, Target, Block);
if (Status != XST_SUCCESS) {
goto Out;
}
/*
* ONFI ReadStatus
*/
do {
Status = XNandPs8_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);
}
Status = XST_SUCCESS;
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function sends ONFI Program Page command to flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_ProgramPage(XNandPs8 *InstancePtr, u32 Target, u32 Page,
u32 Col, u8 *Buf)
{
u32 AddrCycles = InstancePtr->Geometry.RowAddrCycles +
InstancePtr->Geometry.ColAddrCycles;
u32 PktSize;
u32 PktCount;
u32 BufWrCnt = 0U;
u32 *BufPtr = (u32 *)(void *)Buf;
s32 Status = XST_FAILURE;
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;
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_PG_PROG1, ONFI_CMD_PG_PROG2,
1U, 1U, (u8)AddrCycles);
if (InstancePtr->DmaMode == MDMA) {
/*
* Enable DMA boundary Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK |
XNANDPS8_INTR_STS_EN_DMA_INT_STS_EN_MASK);
} else {
/*
* Enable Buffer Write Ready Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK);
}
/*
* Program Page Size
*/
XNandPs8_SetPageSize(InstancePtr);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, PktSize, PktCount);
/*
* Program DMA system address and DMA buffer boundary
*/
if (InstancePtr->DmaMode == MDMA) {
/*
* Flush the Data Cache
*/
Xil_DCacheFlushRange(Buf, (PktSize * PktCount));
#ifdef __aarch64__
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR1_OFFSET,
(u32) (((INTPTR)Buf >> 32) & 0xFFFFFFFFU));
#endif
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR0_OFFSET,
(u32) ((INTPTR)(void *)Buf & 0xFFFFFFFFU));
}
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, Page, (u16)Col);
/*
* Set Bus Width
*/
XNandPs8_SetBusWidth(InstancePtr);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Set ECC
*/
if (InstancePtr->EccMode == HWECC) {
XNandPs8_SetEccSpareCmd(InstancePtr, ONFI_CMD_CHNG_WR_COL,
InstancePtr->Geometry.ColAddrCycles);
}
/*
* Set Page Program in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_PG_PROG_MASK);
if (InstancePtr->DmaMode == MDMA) {
goto WriteDmaDone;
}
while (BufWrCnt < PktCount) {
/*
* Poll for Buffer Write Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_WR_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for buf write ready timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Increment Buffer Write Interrupt Count
*/
BufWrCnt++;
if (BufWrCnt == PktCount) {
/*
* Enable Transfer Complete Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
} else {
/*
* Clear Buffer Write Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
}
/*
* Clear Buffer Write Ready Interrupt in Interrupt Status
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_WR_RDY_STS_EN_MASK);
/*
* Write Packet Data to Data Port Register
*/
for (Index = 0U; Index < (PktSize/4U); Index++) {
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET,
BufPtr[Index]);
}
BufPtr += (PktSize/4U);
if (BufWrCnt < PktCount) {
/*
* Enable Buffer Write Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK);
} else {
break;
}
}
WriteDmaDone:
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function sends ONFI Program Page command to flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_WriteSpareBytes(XNandPs8 *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;
/*
* 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 == 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 *)(void *)&Buf[Col];
} else {
/*
* No free spare bytes available for writing
*/
Status = XST_FAILURE;
goto Out;
}
}
if (InstancePtr->DmaMode == MDMA) {
/*
* Enable Transfer Complete Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
} else {
/*
* Enable Buffer Write Ready Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK);
}
/*
* Program Command hack for change write column
*/
if (PostWrite > 0U) {
Cmd.Command1 = 0x80U;
Cmd.Command2 = 0x00U;
XNandPs8_Prepare_Cmd(InstancePtr, Cmd.Command1, Cmd.Command2,
0U , 1U, (u8)AddrCycles);
} else {
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_PG_PROG1,
ONFI_CMD_PG_PROG2, 0U , 1U, (u8)AddrCycles);
}
/*
* Program Page Size
*/
XNandPs8_SetPageSize(InstancePtr);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, PktSize, PktCount);
/*
* Program DMA system address and DMA buffer boundary
*/
if (InstancePtr->DmaMode == MDMA) {
/*
* Flush the Data Cache
*/
Xil_DCacheFlushRange(BufPtr, (PktSize * PktCount));
#ifdef __aarch64__
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR1_OFFSET,
(u32) (((INTPTR)BufPtr >> 32) & 0xFFFFFFFFU));
#endif
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR0_OFFSET,
(u32) ((INTPTR)(void *)BufPtr & 0xFFFFFFFFU));
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_BUF_BND_OFFSET,
XNANDPS8_DMA_BUF_BND_512K);
}
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, PageVar, (u16)Col);
/*
* Set Bus Width
*/
XNandPs8_SetBusWidth(InstancePtr);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Set Page Program in Program Register
*/
if (PostWrite > 0U) {
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,((u32)XNANDPS8_PROG_PG_PROG_MASK |
(u32)XNANDPS8_PROG_CHNG_ROW_ADDR_MASK));
} else {
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_PG_PROG_MASK);
}
if (InstancePtr->DmaMode == MDMA) {
goto WriteDmaDone;
}
while (BufWrCnt < PktCount) {
/*
* Poll for Buffer Write Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_WR_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for buf write ready timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Increment Buffer Write Interrupt Count
*/
BufWrCnt++;
if (BufWrCnt == PktCount) {
/*
* Enable Transfer Complete Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
} else {
/*
* Clear Buffer Write Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
}
/*
* Clear Buffer Write Ready Interrupt in Interrupt Status
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_WR_RDY_STS_EN_MASK);
/*
* Write Packet Data to Data Port Register
*/
for (Index = 0U; Index < (PktSize/4U); Index++) {
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET,
BufPtr[Index]);
}
BufPtr += (PktSize/4U);
if (BufWrCnt < PktCount) {
/*
* Enable Buffer Write Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK);
} else {
break;
}
}
WriteDmaDone:
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
if (InstancePtr->EccMode == HWECC) {
if (PostWrite > 0U) {
BufPtr = (u32 *)(void *)&Buf[PostEccSpareCol];
Status = XNandPs8_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 XNandPs8 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 XNandPs8_ReadPage(XNandPs8 *InstancePtr, u32 Target, u32 Page,
u32 Col, u8 *Buf)
{
u32 AddrCycles = InstancePtr->Geometry.RowAddrCycles +
InstancePtr->Geometry.ColAddrCycles;
u32 PktSize;
u32 PktCount;
u32 BufRdCnt = 0U;
u32 *BufPtr = (u32 *)(void *)Buf;
s32 Status = XST_FAILURE;
u32 Index;
/*
* Assert the input arguments.
*/
Xil_AssertNonvoid(Page < InstancePtr->Geometry.NumPages);
Xil_AssertNonvoid(Target < XNANDPS8_MAX_TARGETS);
if (InstancePtr->EccCfg.CodeWordSize > 9U) {
PktSize = 1024U;
} else {
PktSize = 512U;
}
PktCount = InstancePtr->Geometry.BytesPerPage/PktSize;
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_RD1, ONFI_CMD_RD2,
1U, 1U, (u8)AddrCycles);
if (InstancePtr->DmaMode == MDMA) {
/*
* Enable DMA boundary Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK |
XNANDPS8_INTR_STS_EN_DMA_INT_STS_EN_MASK);
} else {
/*
* Enable Buffer Read Ready Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
}
/*
* Enable Single bit error and Multi bit error
*/
if (InstancePtr->EccMode == HWECC) {
/*
* Interrupt Status Enable Register
*/
XNandPs8_IntrStsEnable(InstancePtr,
(XNANDPS8_INTR_STS_EN_MUL_BIT_ERR_STS_EN_MASK |
XNANDPS8_INTR_STS_EN_ERR_INTR_STS_EN_MASK));
}
/*
* Program Page Size
*/
XNandPs8_SetPageSize(InstancePtr);
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, Page, (u16)Col);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, PktSize, PktCount);
/*
* Program DMA system address and DMA buffer boundary
*/
if (InstancePtr->DmaMode == MDMA) {
/*
* Invalidate the Data Cache
*/
Xil_DCacheInvalidateRange(Buf, (PktSize * PktCount));
#ifdef __aarch64__
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR1_OFFSET,
(u32) (((INTPTR)(void *)Buf >> 32) &
0xFFFFFFFFU));
#endif
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR0_OFFSET,
(u32) ((INTPTR)(void *)Buf & 0xFFFFFFFFU));
}
/*
* Set Bus Width
*/
XNandPs8_SetBusWidth(InstancePtr);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Set ECC
*/
if (InstancePtr->EccMode == HWECC) {
XNandPs8_SetEccSpareCmd(InstancePtr,
(ONFI_CMD_CHNG_RD_COL1 |
(ONFI_CMD_CHNG_RD_COL2 << (u8)8U)),
InstancePtr->Geometry.ColAddrCycles);
}
/*
* Set Read command in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_RD_MASK);
if (InstancePtr->DmaMode == MDMA) {
goto ReadDmaDone;
}
while (BufRdCnt < PktCount) {
/*
* Poll for Buffer Read Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for buf read ready timeout\r\n",
__func__);
#endif
goto CheckEccError;
}
/*
* Increment Buffer Read Interrupt Count
*/
BufRdCnt++;
if (BufRdCnt == PktCount) {
/*
* Enable Transfer Complete Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
} else {
/*
* Clear Buffer Read Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
}
/*
* Clear Buffer Read Ready Interrupt in Interrupt Status
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK);
/*
* Read Packet Data from Data Port Register
*/
for (Index = 0U; Index < (PktSize/4); Index++) {
BufPtr[Index] = XNandPs8_ReadReg(
InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET);
}
BufPtr += (PktSize/4);
if (BufRdCnt < PktCount) {
/*
* Enable Buffer Read Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
} else {
break;
}
}
ReadDmaDone:
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto CheckEccError;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
CheckEccError:
/*
* Check ECC Errors
*/
if (InstancePtr->EccMode == HWECC) {
/*
* Hamming Multi Bit Errors
*/
if (((u32)XNandPs8_ReadReg(InstancePtr->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET) &
(u32)XNANDPS8_INTR_STS_MUL_BIT_ERR_STS_EN_MASK) != 0U) {
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_MUL_BIT_ERR_STS_EN_MASK);
#ifdef XNANDPS8_DEBUG
xil_printf("%s: ECC Hamming multi bit error\r\n",
__func__);
#endif
Status = XST_FAILURE;
}
/*
* Hamming Single Bit or BCH Errors
*/
if (((u32)XNandPs8_ReadReg(InstancePtr->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET) &
(u32)XNANDPS8_INTR_STS_ERR_INTR_STS_EN_MASK) != 0U) {
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_ERR_INTR_STS_EN_MASK);
if (InstancePtr->EccCfg.IsBCH == 1U) {
InstancePtr->BCH_Error_Status++;
Status = XST_SUCCESS;
}
}
}
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function reads spare bytes from flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_ReadSpareBytes(XNandPs8 *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;
/*
* 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 == MDMA) {
/*
* Enable Transfer Complete Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
} else {
/*
* Enable Buffer Read Ready Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
}
/*
* Program Command
*/
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_RD1, ONFI_CMD_RD2, 0U,
1U, (u8)AddrCycles);
/*
* Program Page Size
*/
XNandPs8_SetPageSize(InstancePtr);
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, PageVar, (u16)Col);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, PktSize, PktCount);
/*
* Program DMA system address and DMA buffer boundary
*/
if (InstancePtr->DmaMode == MDMA) {
/*
* Invalidate the Data Cache
*/
Xil_DCacheInvalidateRange(Buf, (PktSize * PktCount));
#ifdef __aarch64__
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR1_OFFSET,
(u32) (((INTPTR)(void *)Buf >> 32) &
0xFFFFFFFFU));
#endif
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR0_OFFSET,
(u32) ((INTPTR)(void *)Buf & 0xFFFFFFFFU));
}
/*
* Set Bus Width
*/
XNandPs8_SetBusWidth(InstancePtr);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Set Read command in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_RD_MASK);
if (InstancePtr->DmaMode == MDMA) {
goto ReadDmaDone;
}
while (BufRdCnt < PktCount) {
/*
* Poll for Buffer Read Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for buf read ready timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Increment Buffer Read Interrupt Count
*/
BufRdCnt++;
if (BufRdCnt == PktCount) {
/*
* Enable Transfer Complete Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
} else {
/*
* Clear Buffer Read Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
}
/*
* Clear Buffer Read Ready Interrupt in Interrupt Status
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK);
/*
* Read Packet Data from Data Port Register
*/
for (Index = 0U; Index < (PktSize/4); Index++) {
BufPtr[Index] = XNandPs8_ReadReg(
InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET);
}
BufPtr += (PktSize/4);
if (BufRdCnt < PktCount) {
/*
* Enable Buffer Read Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
} else {
break;
}
}
ReadDmaDone:
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function sends ONFI block erase command to the flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_EraseBlock(XNandPs8 *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 < XNANDPS8_MAX_TARGETS);
Xil_AssertNonvoid(Block < InstancePtr->Geometry.NumBlocks);
Page = Block * InstancePtr->Geometry.PagesPerBlock;
ErasePage = (Page >> 16U) & 0xFFFFU;
EraseCol = Page & 0xFFFFU;
#ifdef RTL_3_1_DRIVER_WORKAROUND
/*
* The sequence read, {erase, read status}, {program, read status}
* fails if we don't clear packet register 0x00 before erase.
* The actual reason for the issue is not yet found.
* This issue still needs to be investigated.
*/
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress, 0x00U, 0x0U);
#endif
/*
* Enable Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
/*
* Program Command
*/
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_BLK_ERASE1,
ONFI_CMD_BLK_ERASE2, 0U , 0U, (u8)AddrCycles);
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, ErasePage, (u16)EraseCol);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Set Block Erase in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_BLK_ERASE_MASK);
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function sends ONFI Get Feature command to flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_GetFeature(XNandPs8 *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 == NVDDR) {
PktSize = 8U;
}
/*
* Enable Buffer Read Ready Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
/*
* Program Command
*/
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_GET_FEATURES,
ONFI_CMD_INVALID, 0U, 0U, 1U);
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, 0x0U, Feature);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, PktSize, PktCount);
/*
* Set Read Parameter Page in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_GET_FEATURES_MASK);
/*
* Poll for Buffer Read Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for buf read ready timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Buffer Read Ready Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Buffer Read Ready Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK);
/*
* Enable Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
/*
* Read Data from Data Port Register
*/
for (Index = 0U; Index < (PktSize/4U); Index++) {
BufPtr[Index] = XNandPs8_ReadReg(
InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET);
}
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function sends ONFI Set Feature command to flash.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_SetFeature(XNandPs8 *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 == NVDDR) {
PktSize = 8U;
}
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Enable Buffer Write Ready Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK);
/*
* Program Command
*/
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_SET_FEATURES,
ONFI_CMD_INVALID, 0U , 0U, 1U);
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, 0x0U, Feature);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, PktSize, PktCount);
/*
* Set Read Parameter Page in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_SET_FEATURES_MASK);
/*
* Poll for Buffer Write Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_WR_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for buf write ready timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Buffer Write Ready Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Buffer Write Ready Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_WR_RDY_STS_EN_MASK);
/*
* Enable Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
(XNANDPS8_INTR_STS_EN_OFFSET),
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
/*
* Write Data to Data Port Register
*/
for (Index = 0U; Index < (PktSize/4U); Index++) {
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET,
BufPtr[Index]);
}
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function changes clock frequency of flash controller.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
* @param ClockFreq is the clock frequency to change.
*
* @return
* None
*
* @note None
*
******************************************************************************/
static void XNandPs8_ChangeClockFreq(XNandPs8 *InstancePtr, u32 ClockFreq)
{
/*
* Not implemented
*/
}
/*****************************************************************************/
/**
*
* This function changes the data interface and timing mode.
*
* @param InstancePtr is a pointer to the XNandPs8 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 XNandPs8_ChangeTimingMode(XNandPs8 *InstancePtr,
XNandPs8_DataInterface NewIntf,
XNandPs8_TimingMode NewMode)
{
s32 Status;
u32 Target;
u32 Index;
u32 Found = 0U;
u32 RegVal;
u8 Buf[4] = {0U};
u32 *Feature = (u32 *)(void *)&Buf[0];
const XNandPs8_TimingModeDesc *Desc = NULL;
/*
* Assert the input arguments.
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Get current data interface type and timing mode
*/
XNandPs8_DataInterface CurIntf = InstancePtr->DataInterface;
XNandPs8_TimingMode CurMode = InstancePtr->TimingMode;
/*
* Find the timing mode descriptor
*/
for (Index = 0U; Index <
(sizeof(TimingDesc)/sizeof(XNandPs8_TimingModeDesc)); Index++) {
Desc = &TimingDesc[Index];
if ((Desc->CurDataIntf == CurIntf) &&
(Desc->NewDataIntf == NewIntf) &&
(Desc->NewTimingMode == NewMode)) {
Found = 1U;
break;
}
}
if ((!Found) != 0U) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Timing mode desc not found\r\n",__func__);
#endif
Status = XST_FAILURE;
goto Out;
}
/*
* Check if the flash is in same mode
*/
if ((CurIntf == NewIntf) && (CurMode == NewMode)) {
Status = XST_SUCCESS;
goto Out;
}
if ((CurIntf == NVDDR) && (NewIntf == SDR)) {
/*
* Change the clock frequency
*/
XNandPs8_ChangeClockFreq(InstancePtr, Desc->ClockFreq);
/*
* Issue Reset command
*/
for (Target = 0U; Target < InstancePtr->Geometry.NumTargets;
Target++) {
Status = XNandPs8_OnfiReset(InstancePtr, Target);
if (Status != XST_SUCCESS) {
goto Out;
}
/*
* Get Feature
*/
Status = XNandPs8_GetFeature(InstancePtr, Target,
0x01U, &Buf[0]);
if (Status != XST_SUCCESS) {
goto Out;
}
/*
* Check SDR mode and Timing Mode 0
*/
if (Feature != 0x0U) {
Status = XST_FAILURE;
goto Out;
}
}
InstancePtr->DataInterface = SDR;
InstancePtr->TimingMode = SDR0;
Status = XNandPs8_ChangeTimingMode(InstancePtr, NewIntf,
NewMode);
goto Out;
}
/*
* Set Feature
*/
for (Target = 0U; Target < InstancePtr->Geometry.NumTargets;
Target++) {
Status = XNandPs8_SetFeature(InstancePtr, Target, 0x01U,
(u8 *)&Desc->FeatureVal);
if (Status != XST_SUCCESS) {
goto Out;
}
}
/*
* Change the clock frequency
*/
if (Desc->ClockFreq > 0U) {
XNandPs8_ChangeClockFreq(InstancePtr, Desc->ClockFreq);
}
/*
* Get Feature
*/
for (Target = 0U; Target < InstancePtr->Geometry.NumTargets;
Target++) {
Status = XNandPs8_GetFeature(InstancePtr, Target, 0x01U,
&Buf[0]);
if (Status != XST_SUCCESS) {
goto Out;
}
/*
* Check if set_feature was successful
*/
if (*Feature != Desc->FeatureVal) {
Status = XST_FAILURE;
goto Out;
}
}
InstancePtr->DataInterface = NewIntf;
InstancePtr->TimingMode = NewMode;
/*
* Update Data Interface Register
*/
RegVal = ((NewMode % 6U) << ((NewIntf == NVDDR) ? 3U : 0U)) |
((u32)NewIntf << XNANDPS8_DATA_INTF_DATA_INTF_SHIFT);
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DATA_INTF_OFFSET, RegVal);
Status = XST_SUCCESS;
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 XNandPs8 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 XNandPs8_ChangeReadColumn(XNandPs8 *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;
/*
* Assert the input arguments.
*/
Xil_AssertNonvoid(Target < XNANDPS8_MAX_TARGETS);
Xil_AssertNonvoid(Buf != NULL);
if (InstancePtr->DmaMode == MDMA) {
/*
* Enable DMA boundary Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK |
XNANDPS8_INTR_STS_EN_DMA_INT_STS_EN_MASK);
} else {
/*
* Enable Buffer Read Ready Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
}
/*
* Program Command
*/
XNandPs8_Prepare_Cmd(InstancePtr, ONFI_CMD_CHNG_RD_COL1,
ONFI_CMD_CHNG_RD_COL2, 0U , 1U, (u8)AddrCycles);
/*
* Program Page Size
*/
XNandPs8_SetPageSize(InstancePtr);
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, 0U, (u16)Col);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, PktSize, PktCount);
/*
* Program DMA system address and DMA buffer boundary
*/
if (InstancePtr->DmaMode == MDMA) {
/*
* Invalidate the Data Cache
*/
Xil_DCacheInvalidateRange(Buf, (PktSize * PktCount));
#ifdef __aarch64__
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR1_OFFSET,
(u32) (((INTPTR)Buf >> 32) & 0xFFFFFFFFU));
#endif
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR0_OFFSET,
(u32) ((INTPTR)(void *)Buf & 0xFFFFFFFFU));
}
/*
* Set Bus Width
*/
XNandPs8_SetBusWidth(InstancePtr);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Set Read command in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_RD_MASK);
if (InstancePtr->DmaMode == MDMA) {
goto ReadDmaDone;
}
while (BufRdCnt < PktCount) {
/*
* Poll for Buffer Read Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for buf read ready timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Increment Buffer Read Interrupt Count
*/
BufRdCnt++;
if (BufRdCnt == PktCount) {
/*
* Enable Transfer Complete Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
} else {
/*
* Clear Buffer Read Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
}
/*
* Clear Buffer Read Ready Interrupt in Interrupt Status
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_RD_RDY_STS_EN_MASK);
/*
* Read Packet Data from Data Port Register
*/
for (Index = 0U; Index < (PktSize/4); Index++) {
BufPtr[Index] = XNandPs8_ReadReg(
InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET);
}
BufPtr += (PktSize/4U);
if (BufRdCnt < PktCount) {
/*
* Enable Buffer Read Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_RD_RDY_STS_EN_MASK);
} else {
break;
}
}
ReadDmaDone:
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
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 XNandPs8 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 XNandPs8_ChangeWriteColumn(XNandPs8 *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;
/*
* Assert the input arguments.
*/
Xil_AssertNonvoid(Target < XNANDPS8_MAX_TARGETS);
Xil_AssertNonvoid(Buf != NULL);
if (PktCount == 0U) {
return XST_SUCCESS;
}
if (InstancePtr->DmaMode == MDMA) {
/*
* Enable DMA boundary Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK |
XNANDPS8_INTR_STS_EN_DMA_INT_STS_EN_MASK);
} else {
/*
* Enable Buffer Write Ready Interrupt in Interrupt Status
* Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK);
}
/*
* Change write column hack
*/
OnfiCommand.Command1 = 0x85U;
OnfiCommand.Command2 = 0x10U;
XNandPs8_Prepare_Cmd(InstancePtr, OnfiCommand.Command1,
OnfiCommand.Command2, 0U , 0U, (u8)AddrCycles);
/*
* Program Page Size
*/
XNandPs8_SetPageSize(InstancePtr);
/*
* Program Column, Page, Block address
*/
XNandPs8_SetPageColAddr(InstancePtr, 0U, (u16)Col);
/*
* Program Packet Size and Packet Count
*/
XNandPs8_SetPktSzCnt(InstancePtr, PktSize, PktCount);
/*
* Program DMA system address and DMA buffer boundary
*/
if (InstancePtr->DmaMode == MDMA) {
#ifdef __aarch64__
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR1_OFFSET,
(u32) (((INTPTR)Buf >> 32U) & 0xFFFFFFFFU));
#endif
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_DMA_SYS_ADDR0_OFFSET,
(u32) ((INTPTR)(void *)Buf & 0xFFFFFFFFU));
}
/*
* Set Bus Width
*/
XNandPs8_SetBusWidth(InstancePtr);
/*
* Program Memory Address Register2 for chip select
*/
XNandPs8_SelectChip(InstancePtr, Target);
/*
* Set Page Program in Program Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_PROG_OFFSET,XNANDPS8_PROG_CHNG_ROW_ADDR_END_MASK);
if (InstancePtr->DmaMode == MDMA) {
goto WriteDmaDone;
}
while (BufWrCnt < PktCount) {
/*
* Poll for Buffer Write Ready event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_WR_RDY_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for buf write ready timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Increment Buffer Write Interrupt Count
*/
BufWrCnt++;
if (BufWrCnt == PktCount) {
/*
* Enable Transfer Complete Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_TRANS_COMP_STS_EN_MASK);
} else {
/*
* Clear Buffer Write Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
}
/*
* Clear Buffer Write Ready Interrupt in Interrupt Status
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_BUFF_WR_RDY_STS_EN_MASK);
/*
* Write Packet Data to Data Port Register
*/
for (Index = 0U; Index < (PktSize/4U); Index++) {
XNandPs8_WriteReg(InstancePtr->Config.BaseAddress,
XNANDPS8_BUF_DATA_PORT_OFFSET,
BufPtr[Index]);
}
BufPtr += (PktSize/4U);
if (BufWrCnt < PktCount) {
/*
* Enable Buffer Write Ready Interrupt in Interrupt
* Status Enable Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET,
XNANDPS8_INTR_STS_EN_BUFF_WR_RDY_STS_EN_MASK);
} else {
break;
}
}
WriteDmaDone:
/*
* Poll for Transfer Complete event
*/
Status = XNandPs8_PollRegTimeout(
InstancePtr,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK,
XNANDPS8_INTR_POLL_TIMEOUT);
if (Status != XST_SUCCESS) {
#ifdef XNANDPS8_DEBUG
xil_printf("%s: Poll for xfer complete timeout\r\n",
__func__);
#endif
goto Out;
}
/*
* Clear Transfer Complete Interrupt in Interrupt Status Enable
* Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_EN_OFFSET, 0U);
/*
* Clear Transfer Complete Interrupt in Interrupt Status Register
*/
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_INTR_STS_OFFSET,
XNANDPS8_INTR_STS_TRANS_COMP_STS_EN_MASK);
Out:
return Status;
}
/*****************************************************************************/
/**
*
* This function initializes extended parameter page ECC information.
*
* @param InstancePtr is a pointer to the XNandPs8 instance.
* @param ExtPrm is the Extended parameter page buffer.
*
* @return
* - XST_SUCCESS if successful.
* - XST_FAILURE if failed.
*
* @note None
*
******************************************************************************/
static s32 XNandPs8_InitExtEcc(XNandPs8 *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 XNANDPS8_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->NumBitsEcc;
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 XNandPs8 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 XNandPs8_Prepare_Cmd(XNandPs8 *InstancePtr, u8 Cmd1, u8 Cmd2, u8 EccState,
u8 DmaMode, u8 AddrCycles)
{
u32 RegValue = 0U;
Xil_AssertVoid(InstancePtr != NULL);
RegValue = (u32)Cmd1 | (((u32)Cmd2 << (u32)XNANDPS8_CMD_CMD2_SHIFT) &
(u32)XNANDPS8_CMD_CMD2_MASK);
if ((EccState != 0U) && (InstancePtr->EccMode == HWECC)) {
RegValue |= 1U << XNANDPS8_CMD_ECC_ON_SHIFT;
}
if ((DmaMode != 0U) && (InstancePtr->DmaMode == MDMA)) {
RegValue |= MDMA << XNANDPS8_CMD_DMA_EN_SHIFT;
}
if (AddrCycles != 0U) {
RegValue |= (u32)AddrCycles <<
(u32)XNANDPS8_CMD_ADDR_CYCLES_SHIFT;
}
XNandPs8_WriteReg((InstancePtr)->Config.BaseAddress,
XNANDPS8_CMD_OFFSET, RegValue);
}