
Modified XNandPs8_FlashInit API present in xnanps8.c Added Macro for Onfi Read ID check. Signed-off-by: Shakti Bhatnagar <shaktib@xilinx.com>
4125 lines
107 KiB
C
Executable file
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);
|
|
}
|