mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-16 09:34:18 +00:00
feat(nxp/driver/ifc_nand): add IFC NAND flash driver
Support IFC NAND flash as boot device. Signed-off-by: Ruchika Gupta <ruchika.gupta@nxp.com> Signed-off-by: Jiafei Pan <Jiafei.Pan@nxp.com> Change-Id: I1aba7035ff70b179915e181c04e7b00be2066abe
This commit is contained in:
parent
e2fdc77ba4
commit
28279cf2c1
5 changed files with 1039 additions and 0 deletions
|
@ -93,3 +93,7 @@ endif
|
|||
ifeq (${IFC_NOR_NEEDED},yes)
|
||||
include ${PLAT_DRIVERS_PATH}/ifc/nor/ifc_nor.mk
|
||||
endif
|
||||
|
||||
ifeq (${IFC_NAND_NEEDED},yes)
|
||||
include ${PLAT_DRIVERS_PATH}/ifc/nand/ifc_nand.mk
|
||||
endif
|
||||
|
|
329
drivers/nxp/ifc/nand/ifc.h
Normal file
329
drivers/nxp/ifc/nand/ifc.h
Normal file
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* Copyright 2022 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef IFC_H
|
||||
#define IFC_H
|
||||
|
||||
#include <endian.h>
|
||||
|
||||
#include <mmio.h>
|
||||
|
||||
#define NXP_IFC_RUN_TIME_ADDR U(0x1000)
|
||||
|
||||
/* CPSR - Chip Select Property Register Offset */
|
||||
#define EXT_CSPR(n) (U(0x000C) + (n * 0xC))
|
||||
#define CSPR(n) (U(0x0010) + (n * 0xC))
|
||||
#define CSOR(n) (U(0x0130) + (n * 0xC))
|
||||
#define EXT_CSOR(n) (U(0x0134) + (n * 0xC))
|
||||
#define IFC_AMASK_CS0 U(0x00A0)
|
||||
|
||||
/* NAND specific Registers Offset */
|
||||
#define NCFGR (NXP_IFC_RUN_TIME_ADDR + U(0x0000))
|
||||
#define NAND_FCR0 (NXP_IFC_RUN_TIME_ADDR + U(0x0014))
|
||||
|
||||
#define ROW0 (NXP_IFC_RUN_TIME_ADDR + U(0x003C))
|
||||
#define ROW1 (NXP_IFC_RUN_TIME_ADDR + U(0x004C))
|
||||
#define COL0 (NXP_IFC_RUN_TIME_ADDR + U(0x0044))
|
||||
#define COL1 (NXP_IFC_RUN_TIME_ADDR + U(0x0054))
|
||||
|
||||
#define NAND_BC (NXP_IFC_RUN_TIME_ADDR + U(0x0108))
|
||||
#define NAND_FIR0 (NXP_IFC_RUN_TIME_ADDR + U(0x0110))
|
||||
#define NAND_FIR1 (NXP_IFC_RUN_TIME_ADDR + U(0x0114))
|
||||
#define NAND_FIR2 (NXP_IFC_RUN_TIME_ADDR + U(0x0118))
|
||||
#define NAND_CSEL (NXP_IFC_RUN_TIME_ADDR + U(0x015C))
|
||||
#define NANDSEQ_STRT (NXP_IFC_RUN_TIME_ADDR + U(0x0164))
|
||||
#define NAND_EVTER_STAT (NXP_IFC_RUN_TIME_ADDR + U(0x016C))
|
||||
#define NAND_AUTOBOOT_TRGR (NXP_IFC_RUN_TIME_ADDR + U(0x0284))
|
||||
|
||||
/* Size of SRAM Buffer */
|
||||
#define CSPR_PS U(0x00000180)
|
||||
#define CSPR_PS_SHIFT 7
|
||||
#define CSPR_PS_8 0x1 // Port Size 8 bit
|
||||
#define CSPR_PS_16 0x2 // Port Size 16 bit
|
||||
#define CSPR_PS_32 0x3 // Port Size 32 bit
|
||||
|
||||
/* Chip Select Option Register NAND Machine */
|
||||
#define CSOR_NAND_PGS U(0x00380000)
|
||||
#define CSOR_NAND_PGS_SHIFT 19
|
||||
#define CSOR_NAND_PGS_512 U(0x00000000)
|
||||
#define CSOR_NAND_PGS_2K U(0x00080000)
|
||||
#define CSOR_NAND_PGS_4K U(0x00100000)
|
||||
#define CSOR_NAND_PGS_8K U(0x00180000)
|
||||
#define CSOR_NAND_PGS_16K U(0x00200000)
|
||||
|
||||
|
||||
#define CSOR_NAND_PB U(0x00000700)
|
||||
#define CSOR_NAND_PB_32 U(0x00000000)
|
||||
#define CSOR_NAND_PB_64 U(0x00000100)
|
||||
#define CSOR_NAND_PB_128 U(0x00000200)
|
||||
#define CSOR_NAND_PB_256 U(0x00000300)
|
||||
#define CSOR_NAND_PB_512 U(0x00000400)
|
||||
#define CSOR_NAND_PB_1024 U(0x00000500)
|
||||
#define CSOR_NAND_PB_2048 U(0x00000600)
|
||||
#define CSOR_NAND_PPB_32 32
|
||||
#define CSOR_NAND_PPB_64 64
|
||||
#define CSOR_NAND_PPB_128 128
|
||||
#define CSOR_NAND_PPB_256 256
|
||||
#define CSOR_NAND_PPB_512 512
|
||||
#define CSOR_NAND_PPB_1024 1024
|
||||
#define CSOR_NAND_PPB_2048 2048
|
||||
|
||||
/* NAND Chip select register */
|
||||
#define NAND_CSEL_SHIFT 26
|
||||
#define NAND_COL_MS_SHIFT 31
|
||||
|
||||
/* FCR - Flash Command Register */
|
||||
#define FCR_CMD0 U(0xFF000000)
|
||||
#define FCR_CMD0_SHIFT 24
|
||||
#define FCR_CMD1 U(0x00FF0000)
|
||||
#define FCR_CMD1_SHIFT 16
|
||||
#define FCR_CMD2 U(0x0000FF00)
|
||||
#define FCR_CMD2_SHIFT 8
|
||||
#define FCR_CMD3 U(0x000000FF)
|
||||
#define FCR_CMD3_SHIFT 0
|
||||
|
||||
/* FIR - Flash Instruction Register Opcode */
|
||||
#define FIR_OP0 U(0xFC000000)
|
||||
#define FIR_OP0_SHIFT 26
|
||||
#define FIR_OP1 U(0x03F00000)
|
||||
#define FIR_OP1_SHIFT 20
|
||||
#define FIR_OP2 U(0x000FC000)
|
||||
#define FIR_OP2_SHIFT 14
|
||||
#define FIR_OP3 U(0x00003F00)
|
||||
#define FIR_OP3_SHIFT 8
|
||||
#define FIR_OP4 U(0x000000FC)
|
||||
#define FIR_OP4_SHIFT 2
|
||||
#define FIR_OP5 U(0xFC000000)
|
||||
#define FIR_OP5_SHIFT 26
|
||||
#define FIR_OP6 U(0x03F00000)
|
||||
#define FIR_OP6_SHIFT 20
|
||||
|
||||
/* Instruction Opcode - 6 bits */
|
||||
#define FIR_OP_NOP 0x00
|
||||
#define FIR_OP_CA0 0x01 /* Issue current column address */
|
||||
#define FIR_OP_CA1 0x02 /* Issue current column address */
|
||||
#define FIR_OP_RA0 0x05 /* Issue current column address */
|
||||
#define FIR_OP_RA1 0x06 /* Issue current column address */
|
||||
#define FIR_OP_CMD0 0x09 /* Issue command from FCR[CMD0] */
|
||||
#define FIR_OP_CMD1 0x0a /* Issue command from FCR[CMD1] */
|
||||
#define FIR_OP_CMD2 0x0b /* Issue command from FCR[CMD2] */
|
||||
#define FIR_OP_CMD3 0x0c /* Issue command from FCR[CMD3] */
|
||||
#define FIR_OP_CW0 0x11 /* Wait then issue FCR[CMD0] */
|
||||
#define FIR_OP_CW1 0x12 /* Wait then issue FCR[CMD1] */
|
||||
#define FIR_OP_CW2 0x13 /* Wait then issue FCR[CMD1] */
|
||||
#define FIR_OP_CW3 0x14 /* Wait then issue FCR[CMD1] */
|
||||
#define FIR_OP_WBCD 0x19 /* Wait then read FBCR bytes */
|
||||
#define FIR_OP_RBCD 0x1a /* Wait then read 1 or 2 bytes */
|
||||
#define FIR_OP_BTRD 0x1b /* Wait then read 1 or 2 bytes */
|
||||
#define FIR_OP_RDSTAT 0x1c /* Wait then read 1 or 2 bytes */
|
||||
#define FIR_OP_NWAIT 0x1d /* Wait then read 1 or 2 bytes */
|
||||
#define FIR_OP_WFR 0x1e /* Wait then read 1 or 2 bytes */
|
||||
|
||||
#define NAND_SEQ_STRT_FIR_STRT U(0x80000000)
|
||||
#define NAND_SEQ_STRT_FIR_STRT_SHIFT 31
|
||||
|
||||
#define NAND_EVTER_STAT_FTOER U(0x08000000)
|
||||
#define NAND_EVTER_STAT_WPER U(0x04000000)
|
||||
#define NAND_EVTER_STAT_ECCER U(0x02000000)
|
||||
#define NAND_EVTER_STAT_DQSER U(0x01000000)
|
||||
#define NAND_EVTER_STAT_RCW_DN U(0x00008000)
|
||||
#define NAND_EVTER_STAT_BOOT_DN U(0x00004000)
|
||||
#define NAND_EVTER_STAT_RCW_DN U(0x00008000)
|
||||
#define NAND_EVTER_STAT_OPC_DN U(0x80000000)
|
||||
#define NAND_EVTER_STAT_BBI_SRCH_SEL U(0x00000800)
|
||||
#define NCFGR_BOOT U(0x80000000)
|
||||
#define NAND_AUTOBOOT_TRGR_RCW_LD U(0x80000000)
|
||||
#define NAND_AUTOBOOT_TRGR_BOOT_LD U(0x20000000)
|
||||
|
||||
/* ECC ERROR STATUS Registers */
|
||||
#define NAND_RCW_LD U(0x80000000)
|
||||
#define NAND_BOOT_LD U(0x20000000)
|
||||
|
||||
/*Other Temp Defines */
|
||||
/*256 bad Blocks supported */
|
||||
#define BBT_SIZE 256
|
||||
|
||||
/*Standard NAND flash commands */
|
||||
#define NAND_CMD_READ0 0
|
||||
#define NAND_CMD_READ1 1
|
||||
#define NAND_CMD_READOOB 0x50
|
||||
|
||||
/*Extended commands for large page devices */
|
||||
#define NAND_CMD_READSTART 0x30
|
||||
|
||||
#define NAND_TIMEOUT_MS 40
|
||||
|
||||
#define EMPTY_VAL_CHECK U(0xFFFFFFFF)
|
||||
#define EMPTY_VAL 0xFF
|
||||
|
||||
|
||||
#define MAIN 0
|
||||
#define SPARE 1
|
||||
|
||||
#define GOOD_BLK 1
|
||||
#define BAD_BLK 0
|
||||
#define DIV_2 2
|
||||
|
||||
#define ATTRIBUTE_PGSZ 0xa
|
||||
#define ATTRIBUTE_PPB 0xb
|
||||
|
||||
#define CSPR_PORT_SIZE_8 (0x1 << 7)
|
||||
#define CSPR_PORT_SIZE_16 (0x2 << 7)
|
||||
#define CSPR_PORT_SIZE_32 (0x3 << 7)
|
||||
|
||||
/* NAND specific */
|
||||
#define RCW_SRC_NAND_PORT_MASK U(0x00000080)
|
||||
|
||||
#define NAND_DEFAULT_CSPR U(0x00000053)
|
||||
#define NAND_DEFAULT_CSOR U(0x0180C00C)
|
||||
#define NAND_DEFAULT_EXT_CSPR U(0x00000000)
|
||||
#define NAND_DEFAULT_EXT_CSOR U(0x00000000)
|
||||
#define NAND_DEFAULT_FTIM0 U(0x181c0c10)
|
||||
#define NAND_DEFAULT_FTIM1 U(0x5454141e)
|
||||
#define NAND_DEFAULT_FTIM2 U(0x03808034)
|
||||
#define NAND_DEFAULT_FTIM3 U(0x2c000000)
|
||||
|
||||
#define NAND_CSOR_ECC_MODE_DISABLE U(0x00000000)
|
||||
#define NAND_CSOR_ECC_MODE0 U(0x84000000)
|
||||
#define NAND_CSOR_ECC_MODE1 U(0x94000000)
|
||||
#define NAND_CSOR_ECC_MODE2 U(0xa4000000)
|
||||
#define NAND_CSOR_ECC_MODE3 U(0xb4000000)
|
||||
#define NAND_CSOR_PAGE_SIZE_2K (0x1 << 19)
|
||||
#define NAND_CSOR_PAGE_SIZE_4K (0x2 << 19)
|
||||
#define NAND_CSOR_PAGE_SIZE_8K (0x3 << 19)
|
||||
#define NAND_CSOR_PAGE_SIZE_16K (0x4 << 19)
|
||||
#define NAND_CSOR_PPB_64 (0x1 << 8)
|
||||
#define NAND_CSOR_PPB_128 (0x2 << 8)
|
||||
#define NAND_CSOR_PPB_256 (0x3 << 8)
|
||||
#define NAND_CSOR_PPB_512 (0x4 << 8)
|
||||
|
||||
/* BBI INDICATOR for NAND_2K(CFG_RCW_SRC[1]) for
|
||||
* devices greater than 2K page size(CFG_RCW_SRC[3])
|
||||
*/
|
||||
#define RCW_SRC_NAND_BBI_MASK U(0x00000008)
|
||||
#define RCW_SRC_NAND_BBI_MASK_NAND_2K U(0x00000002)
|
||||
#define NAND_BBI_ONFI_2K (0x1 << 1)
|
||||
#define NAND_BBI_ONFI (0x1 << 3)
|
||||
|
||||
#define RCW_SRC_NAND_PAGE_MASK U(0x00000070)
|
||||
#define RCW_SRC_NAND_PAGE_MASK_NAND_2K U(0x0000000C)
|
||||
#define NAND_2K_XXX 0x00
|
||||
#define NAND_2K_64 0x04
|
||||
#define NAND_2K_128 0x08
|
||||
#define NAND_4K_128 0x10
|
||||
#define NAND_4K_256 0x20
|
||||
#define NAND_4K_512 0x30
|
||||
#define NAND_8K_128 0x40
|
||||
#define NAND_8K_256 0x50
|
||||
#define NAND_8K_512 0x60
|
||||
#define NAND_16K_512 0x70
|
||||
#define BLOCK_LEN_2K 2048
|
||||
|
||||
#define RCW_SRC_NAND_ECC_MASK U(0x00000007)
|
||||
#define RCW_SRC_NAND_ECC_MASK_NAND_2K U(0x00000001)
|
||||
#define NAND_ECC_DISABLE 0x0
|
||||
#define NAND_ECC_4_520 0x1
|
||||
#define NAND_ECC_8_528 0x5
|
||||
#define NAND_ECC_24_1K 0x6
|
||||
#define NAND_ECC_40_1K 0x7
|
||||
|
||||
#define NAND_SPARE_2K U(0x00000040)
|
||||
#define NAND_SPARE_4K_ECC_M0 U(0x00000080)
|
||||
#define NAND_SPARE_4K_ECC_M1 U(0x000000D2)
|
||||
#define NAND_SPARE_4K_ECC_M2 U(0x000000B0)
|
||||
#define NAND_SPARE_4K_ECC_M3 U(0x00000120)
|
||||
#define NAND_SPARE_8K_ECC_M0 U(0x00000088)
|
||||
#define NAND_SPARE_8K_ECC_M1 U(0x00000108)
|
||||
#define NAND_SPARE_8K_ECC_M2 U(0x00000158)
|
||||
#define NAND_SPARE_8K_ECC_M3 U(0x00000238)
|
||||
#define NAND_SPARE_16K_ECC_M0 U(0x00000108)
|
||||
#define NAND_SPARE_16K_ECC_M1 U(0x00000208)
|
||||
#define NAND_SPARE_16K_ECC_M2 U(0x000002A8)
|
||||
#define NAND_SPARE_16K_ECC_M3 U(0x00000468)
|
||||
|
||||
struct nand_info {
|
||||
uintptr_t ifc_register_addr;
|
||||
uintptr_t ifc_region_addr;
|
||||
uint32_t page_size;
|
||||
uint32_t port_size;
|
||||
uint32_t blk_size;
|
||||
uint32_t ppb;
|
||||
uint32_t pi_width; /* Bits Required to index a page in block */
|
||||
uint32_t ral;
|
||||
uint32_t ibr_flow;
|
||||
uint32_t bbt[BBT_SIZE];
|
||||
uint32_t lgb; /* Last Good Block */
|
||||
uint32_t bbt_max; /* Total entries in bbt */
|
||||
uint32_t bzero_good;
|
||||
uint8_t bbs;
|
||||
uint8_t bad_marker_loc;
|
||||
uint8_t onfi_dev_flag;
|
||||
uint8_t init_time_boot_flag;
|
||||
uint8_t *buf;
|
||||
};
|
||||
|
||||
struct ifc_regs {
|
||||
uint32_t ext_cspr;
|
||||
uint32_t cspr;
|
||||
uint32_t csor;
|
||||
uint32_t ext_csor;
|
||||
};
|
||||
|
||||
struct sec_nand_info {
|
||||
uint32_t cspr_port_size;
|
||||
uint32_t csor_ecc_mode;
|
||||
uint32_t csor_page_size;
|
||||
uint32_t csor_ppb;
|
||||
uint32_t ext_csor_spare_size;
|
||||
uint32_t onfi_flag;
|
||||
};
|
||||
|
||||
struct sec_nor_info {
|
||||
uint32_t cspr_port_size;
|
||||
uint32_t csor_nor_mode;
|
||||
uint32_t csor_adm_shift;
|
||||
uint32_t port_size;
|
||||
uint32_t addr_bits;
|
||||
};
|
||||
|
||||
enum ifc_chip_sel {
|
||||
IFC_CS0,
|
||||
IFC_CS1,
|
||||
IFC_CS2,
|
||||
IFC_CS3,
|
||||
IFC_CS4,
|
||||
IFC_CS5,
|
||||
IFC_CS6,
|
||||
IFC_CS7,
|
||||
};
|
||||
|
||||
enum ifc_ftims {
|
||||
IFC_FTIM0,
|
||||
IFC_FTIM1,
|
||||
IFC_FTIM2,
|
||||
IFC_FTIM3,
|
||||
};
|
||||
|
||||
#ifdef NXP_IFC_BE
|
||||
#define nand_in32(a) bswap32(mmio_read_32((uintptr_t)a))
|
||||
#define nand_out32(a, v) mmio_write_32((uintptr_t)a, bswap32(v))
|
||||
#else
|
||||
#define nand_in32(a) mmio_read_32((uintptr_t)a)
|
||||
#define nand_out32(a, v) mmio_write_32((uintptr_t)a, v)
|
||||
#endif
|
||||
|
||||
/* Read Write on IFC registers */
|
||||
static inline void write_reg(struct nand_info *nand, uint32_t reg, uint32_t val)
|
||||
{
|
||||
nand_out32(nand->ifc_register_addr + reg, val);
|
||||
}
|
||||
|
||||
static inline uint32_t read_reg(struct nand_info *nand, uint32_t reg)
|
||||
{
|
||||
return nand_in32(nand->ifc_register_addr + reg);
|
||||
}
|
||||
|
||||
#endif /* IFC_H */
|
658
drivers/nxp/ifc/nand/ifc_nand.c
Normal file
658
drivers/nxp/ifc/nand/ifc_nand.c
Normal file
|
@ -0,0 +1,658 @@
|
|||
/*
|
||||
* Copyright 2022 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <drivers/io/io_block.h>
|
||||
#include "ifc.h"
|
||||
#include <lib/xlat_tables/xlat_tables_v2.h>
|
||||
#include <nxp_timer.h>
|
||||
|
||||
/* Private structure for NAND driver data */
|
||||
static struct nand_info nand_drv_data;
|
||||
|
||||
static int update_bbt(uint32_t idx, uint32_t blk, uint32_t *updated,
|
||||
struct nand_info *nand);
|
||||
|
||||
static int nand_wait(struct nand_info *nand)
|
||||
{
|
||||
int timeout = 1;
|
||||
uint32_t neesr;
|
||||
unsigned long start_time;
|
||||
|
||||
start_time = get_timer_val(0);
|
||||
|
||||
while (get_timer_val(start_time) < NAND_TIMEOUT_MS) {
|
||||
/* clear the OPC event */
|
||||
neesr = read_reg(nand, NAND_EVTER_STAT);
|
||||
if (neesr & NAND_EVTER_STAT_OPC_DN) {
|
||||
write_reg(nand, NAND_EVTER_STAT, neesr);
|
||||
timeout = 0;
|
||||
|
||||
/* check for other errors */
|
||||
if (neesr & NAND_EVTER_STAT_FTOER) {
|
||||
ERROR("%s NAND_EVTER_STAT_FTOER occurs\n",
|
||||
__func__);
|
||||
return -1;
|
||||
} else if (neesr & NAND_EVTER_STAT_ECCER) {
|
||||
ERROR("%s NAND_EVTER_STAT_ECCER occurs\n",
|
||||
__func__);
|
||||
return -1;
|
||||
} else if (neesr & NAND_EVTER_STAT_DQSER) {
|
||||
ERROR("%s NAND_EVTER_STAT_DQSER occurs\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
ERROR("%s ERROR_NAND_TIMEOUT occurs\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t nand_get_port_size(struct nand_info *nand)
|
||||
{
|
||||
uint32_t port_size = U(0);
|
||||
uint32_t cs_reg;
|
||||
uint32_t cur_cs;
|
||||
|
||||
cur_cs = U(0);
|
||||
cs_reg = CSPR(cur_cs);
|
||||
port_size = (read_reg(nand, cs_reg) & CSPR_PS) >> CSPR_PS_SHIFT;
|
||||
switch (port_size) {
|
||||
case CSPR_PS_8:
|
||||
port_size = U(8);
|
||||
break;
|
||||
case CSPR_PS_16:
|
||||
port_size = U(16);
|
||||
break;
|
||||
case CSPR_PS_32:
|
||||
port_size = U(32);
|
||||
break;
|
||||
default:
|
||||
port_size = U(8);
|
||||
}
|
||||
|
||||
return port_size;
|
||||
}
|
||||
|
||||
static uint32_t nand_get_page_size(struct nand_info *nand)
|
||||
{
|
||||
uint32_t pg_size;
|
||||
uint32_t cs_reg;
|
||||
uint32_t cur_cs;
|
||||
|
||||
cur_cs = 0;
|
||||
cs_reg = CSOR(cur_cs);
|
||||
pg_size = read_reg(nand, cs_reg) & CSOR_NAND_PGS;
|
||||
switch (pg_size) {
|
||||
case CSOR_NAND_PGS_2K:
|
||||
pg_size = U(2048);
|
||||
break;
|
||||
case CSOR_NAND_PGS_4K:
|
||||
pg_size = U(4096);
|
||||
break;
|
||||
case CSOR_NAND_PGS_8K:
|
||||
pg_size = U(8192);
|
||||
break;
|
||||
case CSOR_NAND_PGS_16K:
|
||||
pg_size = U(16384);
|
||||
break;
|
||||
default:
|
||||
pg_size = U(512);
|
||||
}
|
||||
|
||||
return pg_size;
|
||||
}
|
||||
|
||||
static uint32_t nand_get_pages_per_blk(struct nand_info *nand)
|
||||
{
|
||||
uint32_t pages_per_blk;
|
||||
uint32_t cs_reg;
|
||||
uint32_t cur_cs;
|
||||
|
||||
cur_cs = 0;
|
||||
cs_reg = CSOR(cur_cs);
|
||||
pages_per_blk = (read_reg(nand, cs_reg) & CSOR_NAND_PB);
|
||||
switch (pages_per_blk) {
|
||||
case CSOR_NAND_PB_32:
|
||||
pages_per_blk = U(32);
|
||||
break;
|
||||
case CSOR_NAND_PB_64:
|
||||
pages_per_blk = U(64);
|
||||
break;
|
||||
case CSOR_NAND_PB_128:
|
||||
pages_per_blk = U(128);
|
||||
break;
|
||||
case CSOR_NAND_PB_256:
|
||||
pages_per_blk = U(256);
|
||||
break;
|
||||
case CSOR_NAND_PB_512:
|
||||
pages_per_blk = U(512);
|
||||
break;
|
||||
case CSOR_NAND_PB_1024:
|
||||
pages_per_blk = U(1024);
|
||||
break;
|
||||
case CSOR_NAND_PB_2048:
|
||||
pages_per_blk = U(2048);
|
||||
break;
|
||||
default:
|
||||
pages_per_blk = U(0);
|
||||
}
|
||||
|
||||
return pages_per_blk;
|
||||
}
|
||||
|
||||
static uint32_t get_page_index_width(uint32_t ppb)
|
||||
{
|
||||
switch (ppb) {
|
||||
case CSOR_NAND_PPB_32:
|
||||
return U(5);
|
||||
case CSOR_NAND_PPB_64:
|
||||
return U(6);
|
||||
case CSOR_NAND_PPB_128:
|
||||
return U(7);
|
||||
case CSOR_NAND_PPB_256:
|
||||
return U(8);
|
||||
case CSOR_NAND_PPB_512:
|
||||
return U(9);
|
||||
case CSOR_NAND_PPB_1024:
|
||||
return U(10);
|
||||
case CSOR_NAND_PPB_2048:
|
||||
return U(11);
|
||||
default:
|
||||
return U(5);
|
||||
}
|
||||
}
|
||||
|
||||
static void nand_get_params(struct nand_info *nand)
|
||||
{
|
||||
nand->port_size = nand_get_port_size(nand);
|
||||
|
||||
nand->page_size = nand_get_page_size(nand);
|
||||
|
||||
/*
|
||||
* Set Bad marker Location for LP / SP
|
||||
* Small Page : 8 Bit : 0x5
|
||||
* Small Page : 16 Bit : 0xa
|
||||
* Large Page : 8 /16 Bit : 0x0
|
||||
*/
|
||||
nand->bad_marker_loc = (nand->page_size == 512) ?
|
||||
((nand->port_size == 8) ? 0x5 : 0xa) : 0;
|
||||
|
||||
/* check for the device is ONFI compliant or not */
|
||||
nand->onfi_dev_flag =
|
||||
(read_reg(nand, NAND_EVTER_STAT) & NAND_EVTER_STAT_BBI_SRCH_SEL)
|
||||
? 1 : 0;
|
||||
|
||||
/* NAND Blk serached count for incremental Bad block search cnt */
|
||||
nand->bbs = 0;
|
||||
|
||||
/* pages per Block */
|
||||
nand->ppb = nand_get_pages_per_blk(nand);
|
||||
|
||||
/* Blk size */
|
||||
nand->blk_size = nand->page_size * nand->ppb;
|
||||
|
||||
/* get_page_index_width */
|
||||
nand->pi_width = get_page_index_width(nand->ppb);
|
||||
|
||||
/* bad block table init */
|
||||
nand->lgb = 0;
|
||||
nand->bbt_max = 0;
|
||||
nand->bzero_good = 0;
|
||||
memset(nand->bbt, EMPTY_VAL, BBT_SIZE * sizeof(nand->bbt[0]));
|
||||
}
|
||||
|
||||
static int nand_init(struct nand_info *nand)
|
||||
{
|
||||
uint32_t ncfgr = 0;
|
||||
|
||||
/* Get nand Parameters from IFC */
|
||||
nand_get_params(nand);
|
||||
|
||||
/* Clear all errors */
|
||||
write_reg(nand, NAND_EVTER_STAT, U(0xffffffff));
|
||||
|
||||
/*
|
||||
* Disable autoboot in NCFGR. Mapping will change from
|
||||
* physical to logical for SRAM buffer
|
||||
*/
|
||||
ncfgr = read_reg(nand, NCFGR);
|
||||
write_reg(nand, NCFGR, (ncfgr & ~NCFGR_BOOT));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_read_data(
|
||||
uintptr_t ifc_region_addr,
|
||||
uint32_t row_add,
|
||||
uint32_t col_add,
|
||||
uint32_t byte_cnt,
|
||||
uint8_t *data,
|
||||
uint32_t main_spare,
|
||||
struct nand_info *nand)
|
||||
{
|
||||
uint32_t page_size_add_bits = U(0);
|
||||
uint32_t page_add_in_actual, page_add;
|
||||
uintptr_t sram_addr_calc;
|
||||
int ret;
|
||||
uint32_t col_val;
|
||||
|
||||
/* Programming MS bit to read from spare area.*/
|
||||
col_val = (main_spare << NAND_COL_MS_SHIFT) | col_add;
|
||||
|
||||
write_reg(nand, NAND_BC, byte_cnt);
|
||||
|
||||
write_reg(nand, ROW0, row_add);
|
||||
write_reg(nand, COL0, col_val);
|
||||
|
||||
/* Program FCR for small Page */
|
||||
if (nand->page_size == U(512)) {
|
||||
if (byte_cnt == 0 ||
|
||||
(byte_cnt != 0 && main_spare == 0 && col_add <= 255)) {
|
||||
write_reg(nand, NAND_FCR0,
|
||||
(NAND_CMD_READ0 << FCR_CMD0_SHIFT));
|
||||
} else if (main_spare == 0) {
|
||||
write_reg(nand, NAND_FCR0,
|
||||
(NAND_CMD_READ1 << FCR_CMD0_SHIFT));
|
||||
} else {
|
||||
write_reg(nand, NAND_FCR0,
|
||||
(NAND_CMD_READOOB << FCR_CMD0_SHIFT));
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Program FCR for Large Page */
|
||||
write_reg(nand, NAND_FCR0, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
|
||||
(NAND_CMD_READSTART << FCR_CMD1_SHIFT));
|
||||
}
|
||||
if (nand->page_size == U(512)) {
|
||||
write_reg(nand, NAND_FIR0, ((FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CA0 << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_RA0 << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_BTRD << FIR_OP3_SHIFT) |
|
||||
(FIR_OP_NOP << FIR_OP4_SHIFT)));
|
||||
write_reg(nand, NAND_FIR1, U(0x00000000));
|
||||
} else {
|
||||
write_reg(nand, NAND_FIR0, ((FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CA0 << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_RA0 << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_CMD1 << FIR_OP3_SHIFT) |
|
||||
(FIR_OP_BTRD << FIR_OP4_SHIFT)));
|
||||
|
||||
write_reg(nand, NAND_FIR1, (FIR_OP_NOP << FIR_OP5_SHIFT));
|
||||
}
|
||||
write_reg(nand, NANDSEQ_STRT, NAND_SEQ_STRT_FIR_STRT);
|
||||
|
||||
ret = nand_wait(nand);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* calculate page_size_add_bits i.e bits
|
||||
* in sram address corresponding to area
|
||||
* within a page for sram
|
||||
*/
|
||||
if (nand->page_size == U(512))
|
||||
page_size_add_bits = U(10);
|
||||
else if (nand->page_size == U(2048))
|
||||
page_size_add_bits = U(12);
|
||||
else if (nand->page_size == U(4096))
|
||||
page_size_add_bits = U(13);
|
||||
else if (nand->page_size == U(8192))
|
||||
page_size_add_bits = U(14);
|
||||
else if (nand->page_size == U(16384))
|
||||
page_size_add_bits = U(15);
|
||||
|
||||
page_add = row_add;
|
||||
|
||||
page_add_in_actual = (page_add << page_size_add_bits) & U(0x0000FFFF);
|
||||
|
||||
if (byte_cnt == 0)
|
||||
col_add = U(0);
|
||||
|
||||
/* Calculate SRAM address for main and spare area */
|
||||
if (main_spare == 0)
|
||||
sram_addr_calc = ifc_region_addr | page_add_in_actual | col_add;
|
||||
else
|
||||
sram_addr_calc = ifc_region_addr | page_add_in_actual |
|
||||
(col_add + nand->page_size);
|
||||
|
||||
/* Depending Byte_count copy full page or partial page from SRAM */
|
||||
if (byte_cnt == 0)
|
||||
memcpy(data, (void *)sram_addr_calc,
|
||||
nand->page_size);
|
||||
else
|
||||
memcpy(data, (void *)sram_addr_calc, byte_cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_read(struct nand_info *nand, int32_t src_addr,
|
||||
uintptr_t dst, uint32_t size)
|
||||
{
|
||||
uint32_t log_blk = U(0);
|
||||
uint32_t pg_no = U(0);
|
||||
uint32_t col_off = U(0);
|
||||
uint32_t row_off = U(0);
|
||||
uint32_t byte_cnt = U(0);
|
||||
uint32_t read_cnt = U(0);
|
||||
uint32_t i = U(0);
|
||||
uint32_t updated = U(0);
|
||||
|
||||
int ret = 0;
|
||||
uint8_t *out = (uint8_t *)dst;
|
||||
|
||||
uint32_t pblk;
|
||||
|
||||
/* loop till size */
|
||||
while (size) {
|
||||
log_blk = (src_addr / nand->blk_size);
|
||||
pg_no = ((src_addr - (log_blk * nand->blk_size)) /
|
||||
nand->page_size);
|
||||
pblk = log_blk;
|
||||
|
||||
// iterate the bbt to find the block
|
||||
for (i = 0; i <= nand->bbt_max; i++) {
|
||||
if (nand->bbt[i] == EMPTY_VAL_CHECK) {
|
||||
ret = update_bbt(i, pblk, &updated, nand);
|
||||
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
/*
|
||||
* if table not updated and we reached
|
||||
* end of table
|
||||
*/
|
||||
if (!updated)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pblk < nand->bbt[i])
|
||||
break;
|
||||
else if (pblk >= nand->bbt[i])
|
||||
pblk++;
|
||||
}
|
||||
|
||||
col_off = (src_addr % nand->page_size);
|
||||
if (col_off) {
|
||||
if ((col_off + size) < nand->page_size)
|
||||
byte_cnt = size;
|
||||
else
|
||||
byte_cnt = nand->page_size - col_off;
|
||||
|
||||
row_off = (pblk << nand->pi_width) | pg_no;
|
||||
|
||||
ret = nand_read_data(
|
||||
nand->ifc_region_addr,
|
||||
row_off,
|
||||
col_off,
|
||||
byte_cnt, out, MAIN, nand);
|
||||
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
} else {
|
||||
/*
|
||||
* fullpage/Partial Page
|
||||
* if byte_cnt = 0 full page
|
||||
* else partial page
|
||||
*/
|
||||
if (size < nand->page_size) {
|
||||
byte_cnt = size;
|
||||
read_cnt = size;
|
||||
} else {
|
||||
byte_cnt = nand->page_size;
|
||||
read_cnt = 0;
|
||||
}
|
||||
row_off = (pblk << nand->pi_width) | pg_no;
|
||||
|
||||
ret = nand_read_data(
|
||||
nand->ifc_region_addr,
|
||||
row_off,
|
||||
0,
|
||||
read_cnt, out, MAIN, nand);
|
||||
|
||||
if (ret != 0) {
|
||||
ERROR("Error from nand-read_data %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
src_addr += byte_cnt;
|
||||
out += byte_cnt;
|
||||
size -= byte_cnt;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isgoodblock(uint32_t blk, uint32_t *gb, struct nand_info *nand)
|
||||
{
|
||||
uint8_t buf[2];
|
||||
int ret;
|
||||
uint32_t row_add;
|
||||
|
||||
*gb = 0;
|
||||
|
||||
/* read Page 0 of blk */
|
||||
ret = nand_read_data(
|
||||
nand->ifc_region_addr,
|
||||
blk << nand->pi_width,
|
||||
nand->bad_marker_loc,
|
||||
0x2, buf, 1, nand);
|
||||
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* For ONFI devices check Page 0 and Last page of block for
|
||||
* Bad Marker and for NON-ONFI Page 0 and 1 for Bad Marker
|
||||
*/
|
||||
row_add = (blk << nand->pi_width);
|
||||
if (nand->port_size == 8) {
|
||||
/* port size is 8 Bit */
|
||||
/* check if page 0 has 0xff */
|
||||
if (buf[0] == 0xff) {
|
||||
/* check page 1 */
|
||||
if (nand->onfi_dev_flag)
|
||||
ret = nand_read_data(
|
||||
nand->ifc_region_addr,
|
||||
row_add | (nand->ppb - 1),
|
||||
nand->bad_marker_loc,
|
||||
0x2, buf, SPARE, nand);
|
||||
else
|
||||
ret = nand_read_data(
|
||||
nand->ifc_region_addr,
|
||||
row_add | 1,
|
||||
nand->bad_marker_loc,
|
||||
0x2, buf, SPARE, nand);
|
||||
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (buf[0] == 0xff)
|
||||
*gb = GOOD_BLK;
|
||||
else
|
||||
*gb = BAD_BLK;
|
||||
} else {
|
||||
/* no, so it is bad blk */
|
||||
*gb = BAD_BLK;
|
||||
}
|
||||
} else {
|
||||
/* Port size 16-Bit */
|
||||
/* check if page 0 has 0xffff */
|
||||
if ((buf[0] == 0xff) &&
|
||||
(buf[1] == 0xff)) {
|
||||
/* check page 1 for 0xffff */
|
||||
if (nand->onfi_dev_flag) {
|
||||
ret = nand_read_data(
|
||||
nand->ifc_region_addr,
|
||||
row_add | (nand->ppb - 1),
|
||||
nand->bad_marker_loc,
|
||||
0x2, buf, SPARE, nand);
|
||||
} else {
|
||||
ret = nand_read_data(
|
||||
nand->ifc_region_addr,
|
||||
row_add | 1,
|
||||
nand->bad_marker_loc,
|
||||
0x2, buf, SPARE, nand);
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if ((buf[0] == 0xff) &&
|
||||
(buf[1] == 0xff)) {
|
||||
*gb = GOOD_BLK;
|
||||
} else {
|
||||
*gb = BAD_BLK;
|
||||
}
|
||||
} else {
|
||||
/* no, so it is bad blk */
|
||||
*gb = BAD_BLK;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_bbt(uint32_t idx, uint32_t blk,
|
||||
uint32_t *updated, struct nand_info *nand)
|
||||
{
|
||||
uint32_t sblk;
|
||||
uint32_t lgb;
|
||||
int ret;
|
||||
|
||||
if (nand->bzero_good && blk == 0)
|
||||
return 0;
|
||||
|
||||
/* special case for lgb == 0 */
|
||||
/* if blk <= lgb retrun */
|
||||
if (nand->lgb != 0 && blk <= nand->lgb)
|
||||
return 0;
|
||||
|
||||
*updated = 0;
|
||||
|
||||
/* if blk is more than lgb, iterate from lgb till a good block
|
||||
* is found for blk
|
||||
*/
|
||||
|
||||
if (nand->lgb < blk)
|
||||
sblk = nand->lgb;
|
||||
else
|
||||
/* this is when lgb = 0 */
|
||||
sblk = blk;
|
||||
|
||||
|
||||
lgb = nand->lgb;
|
||||
|
||||
/* loop from blk to find a good block */
|
||||
while (1) {
|
||||
while (lgb <= sblk) {
|
||||
uint32_t gb = 0;
|
||||
|
||||
ret = isgoodblock(lgb, &gb, nand);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* special case block 0 is good then set this flag */
|
||||
if (lgb == 0 && gb == GOOD_BLK)
|
||||
nand->bzero_good = 1;
|
||||
|
||||
if (gb == BAD_BLK) {
|
||||
if (idx >= BBT_SIZE) {
|
||||
ERROR("NAND BBT Table full\n");
|
||||
return -1;
|
||||
}
|
||||
*updated = 1;
|
||||
nand->bbt[idx] = lgb;
|
||||
idx++;
|
||||
blk++;
|
||||
sblk++;
|
||||
if (idx > nand->bbt_max)
|
||||
nand->bbt_max = idx;
|
||||
}
|
||||
lgb++;
|
||||
}
|
||||
/* the access block found */
|
||||
if (sblk == blk) {
|
||||
/* when good block found update lgb */
|
||||
nand->lgb = blk;
|
||||
break;
|
||||
}
|
||||
sblk++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t ifc_nand_read(int lba, uintptr_t buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
uint32_t page_size;
|
||||
uint32_t src_addr;
|
||||
struct nand_info *nand = &nand_drv_data;
|
||||
|
||||
page_size = nand_get_page_size(nand);
|
||||
src_addr = lba * page_size;
|
||||
ret = nand_read(nand, src_addr, buf, size);
|
||||
return ret ? 0 : size;
|
||||
}
|
||||
|
||||
static struct io_block_dev_spec ifc_nand_spec = {
|
||||
.buffer = {
|
||||
.offset = 0,
|
||||
.length = 0,
|
||||
},
|
||||
.ops = {
|
||||
.read = ifc_nand_read,
|
||||
},
|
||||
/*
|
||||
* Default block size assumed as 2K
|
||||
* Would be updated based on actual size
|
||||
*/
|
||||
.block_size = UL(2048),
|
||||
};
|
||||
|
||||
int ifc_nand_init(uintptr_t *block_dev_spec,
|
||||
uintptr_t ifc_region_addr,
|
||||
uintptr_t ifc_register_addr,
|
||||
size_t ifc_sram_size,
|
||||
uintptr_t ifc_nand_blk_offset,
|
||||
size_t ifc_nand_blk_size)
|
||||
{
|
||||
struct nand_info *nand = NULL;
|
||||
int ret;
|
||||
|
||||
nand = &nand_drv_data;
|
||||
memset(nand, 0, sizeof(struct nand_info));
|
||||
|
||||
nand->ifc_region_addr = ifc_region_addr;
|
||||
nand->ifc_register_addr = ifc_register_addr;
|
||||
|
||||
VERBOSE("nand_init\n");
|
||||
ret = nand_init(nand);
|
||||
if (ret) {
|
||||
ERROR("nand init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ifc_nand_spec.buffer.offset = ifc_nand_blk_offset;
|
||||
ifc_nand_spec.buffer.length = ifc_nand_blk_size;
|
||||
|
||||
ifc_nand_spec.block_size = nand_get_page_size(nand);
|
||||
|
||||
VERBOSE("Page size is %ld\n", ifc_nand_spec.block_size);
|
||||
|
||||
*block_dev_spec = (uintptr_t)&ifc_nand_spec;
|
||||
|
||||
/* Adding NAND SRAM< Buffer in XLAT Table */
|
||||
mmap_add_region(ifc_region_addr, ifc_region_addr,
|
||||
ifc_sram_size, MT_DEVICE | MT_RW);
|
||||
|
||||
return 0;
|
||||
}
|
29
drivers/nxp/ifc/nand/ifc_nand.mk
Normal file
29
drivers/nxp/ifc/nand/ifc_nand.mk
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# Copyright 2022 NXP
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
ifeq (${NAND_ADDED},)
|
||||
|
||||
NAND_ADDED := 1
|
||||
|
||||
NAND_DRIVERS_PATH := ${PLAT_DRIVERS_PATH}/ifc/nand
|
||||
|
||||
NAND_SOURCES := $(NAND_DRIVERS_PATH)/ifc_nand.c \
|
||||
drivers/io/io_block.c
|
||||
|
||||
PLAT_INCLUDES += -I$(PLAT_DRIVERS_INCLUDE_PATH)/ifc
|
||||
|
||||
ifeq (${BL_COMM_IFC_NAND_NEEDED},yes)
|
||||
BL_COMMON_SOURCES += ${NAND_SOURCES}
|
||||
else
|
||||
ifeq (${BL2_IFC_NAND_NEEDED},yes)
|
||||
BL2_SOURCES += ${NAND_SOURCES}
|
||||
endif
|
||||
ifeq (${BL31_IFC_NAND_NEEDED},yes)
|
||||
BL31_SOURCES += ${NAND_SOURCES}
|
||||
endif
|
||||
endif
|
||||
|
||||
endif
|
19
include/drivers/nxp/ifc/ifc_nand.h
Normal file
19
include/drivers/nxp/ifc/ifc_nand.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2022 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef IFC_NAND_H
|
||||
#define IFC_NAND_H
|
||||
|
||||
#define NXP_IFC_SRAM_BUFFER_SIZE UL(0x100000) /* 1M */
|
||||
|
||||
int ifc_nand_init(uintptr_t *block_dev_spec,
|
||||
uintptr_t ifc_region_addr,
|
||||
uintptr_t ifc_register_addr,
|
||||
size_t ifc_sram_size,
|
||||
uintptr_t ifc_nand_blk_offset,
|
||||
size_t ifc_nand_blk_size);
|
||||
|
||||
#endif /*IFC_NAND_H*/
|
Loading…
Add table
Reference in a new issue