diff --git a/drivers/nxp/drivers.mk b/drivers/nxp/drivers.mk index ab3d75774..d77e98549 100644 --- a/drivers/nxp/drivers.mk +++ b/drivers/nxp/drivers.mk @@ -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 diff --git a/drivers/nxp/ifc/nand/ifc.h b/drivers/nxp/ifc/nand/ifc.h new file mode 100644 index 000000000..56c5f9292 --- /dev/null +++ b/drivers/nxp/ifc/nand/ifc.h @@ -0,0 +1,329 @@ +/* + * Copyright 2022 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IFC_H +#define IFC_H + +#include + +#include + +#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 */ diff --git a/drivers/nxp/ifc/nand/ifc_nand.c b/drivers/nxp/ifc/nand/ifc_nand.c new file mode 100644 index 000000000..1f7092a78 --- /dev/null +++ b/drivers/nxp/ifc/nand/ifc_nand.c @@ -0,0 +1,658 @@ +/* + * Copyright 2022 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include "ifc.h" +#include +#include + +/* 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; +} diff --git a/drivers/nxp/ifc/nand/ifc_nand.mk b/drivers/nxp/ifc/nand/ifc_nand.mk new file mode 100644 index 000000000..890fd23c3 --- /dev/null +++ b/drivers/nxp/ifc/nand/ifc_nand.mk @@ -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 diff --git a/include/drivers/nxp/ifc/ifc_nand.h b/include/drivers/nxp/ifc/ifc_nand.h new file mode 100644 index 000000000..dbcd762ce --- /dev/null +++ b/include/drivers/nxp/ifc/ifc_nand.h @@ -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*/