1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2019-2023, STMicroelectronics - All Rights Reserved 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu #include <assert.h> 8*91f16700Schasinglulu #include <errno.h> 9*91f16700Schasinglulu #include <stddef.h> 10*91f16700Schasinglulu 11*91f16700Schasinglulu #include <common/debug.h> 12*91f16700Schasinglulu #include <drivers/delay_timer.h> 13*91f16700Schasinglulu #include <drivers/spi_nand.h> 14*91f16700Schasinglulu #include <lib/utils.h> 15*91f16700Schasinglulu 16*91f16700Schasinglulu #include <platform_def.h> 17*91f16700Schasinglulu 18*91f16700Schasinglulu #define SPI_NAND_MAX_ID_LEN 4U 19*91f16700Schasinglulu #define DELAY_US_400MS 400000U 20*91f16700Schasinglulu 21*91f16700Schasinglulu static struct spinand_device spinand_dev; 22*91f16700Schasinglulu 23*91f16700Schasinglulu #pragma weak plat_get_spi_nand_data 24*91f16700Schasinglulu int plat_get_spi_nand_data(struct spinand_device *device) 25*91f16700Schasinglulu { 26*91f16700Schasinglulu return 0; 27*91f16700Schasinglulu } 28*91f16700Schasinglulu 29*91f16700Schasinglulu static int spi_nand_reg(bool read_reg, uint8_t reg, uint8_t *val, 30*91f16700Schasinglulu enum spi_mem_data_dir dir) 31*91f16700Schasinglulu { 32*91f16700Schasinglulu struct spi_mem_op op; 33*91f16700Schasinglulu 34*91f16700Schasinglulu zeromem(&op, sizeof(struct spi_mem_op)); 35*91f16700Schasinglulu if (read_reg) { 36*91f16700Schasinglulu op.cmd.opcode = SPI_NAND_OP_GET_FEATURE; 37*91f16700Schasinglulu } else { 38*91f16700Schasinglulu op.cmd.opcode = SPI_NAND_OP_SET_FEATURE; 39*91f16700Schasinglulu } 40*91f16700Schasinglulu 41*91f16700Schasinglulu op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 42*91f16700Schasinglulu op.addr.val = reg; 43*91f16700Schasinglulu op.addr.nbytes = 1U; 44*91f16700Schasinglulu op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 45*91f16700Schasinglulu op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 46*91f16700Schasinglulu op.data.dir = dir; 47*91f16700Schasinglulu op.data.nbytes = 1U; 48*91f16700Schasinglulu op.data.buf = val; 49*91f16700Schasinglulu 50*91f16700Schasinglulu return spi_mem_exec_op(&op); 51*91f16700Schasinglulu } 52*91f16700Schasinglulu 53*91f16700Schasinglulu static int spi_nand_read_reg(uint8_t reg, uint8_t *val) 54*91f16700Schasinglulu { 55*91f16700Schasinglulu return spi_nand_reg(true, reg, val, SPI_MEM_DATA_IN); 56*91f16700Schasinglulu } 57*91f16700Schasinglulu 58*91f16700Schasinglulu static int spi_nand_write_reg(uint8_t reg, uint8_t val) 59*91f16700Schasinglulu { 60*91f16700Schasinglulu return spi_nand_reg(false, reg, &val, SPI_MEM_DATA_OUT); 61*91f16700Schasinglulu } 62*91f16700Schasinglulu 63*91f16700Schasinglulu static int spi_nand_update_cfg(uint8_t mask, uint8_t val) 64*91f16700Schasinglulu { 65*91f16700Schasinglulu int ret; 66*91f16700Schasinglulu uint8_t cfg = spinand_dev.cfg_cache; 67*91f16700Schasinglulu 68*91f16700Schasinglulu cfg &= ~mask; 69*91f16700Schasinglulu cfg |= val; 70*91f16700Schasinglulu 71*91f16700Schasinglulu if (cfg == spinand_dev.cfg_cache) { 72*91f16700Schasinglulu return 0; 73*91f16700Schasinglulu } 74*91f16700Schasinglulu 75*91f16700Schasinglulu ret = spi_nand_write_reg(SPI_NAND_REG_CFG, cfg); 76*91f16700Schasinglulu if (ret == 0) { 77*91f16700Schasinglulu spinand_dev.cfg_cache = cfg; 78*91f16700Schasinglulu } 79*91f16700Schasinglulu 80*91f16700Schasinglulu return ret; 81*91f16700Schasinglulu } 82*91f16700Schasinglulu 83*91f16700Schasinglulu static int spi_nand_ecc_enable(bool enable) 84*91f16700Schasinglulu { 85*91f16700Schasinglulu return spi_nand_update_cfg(SPI_NAND_CFG_ECC_EN, 86*91f16700Schasinglulu enable ? SPI_NAND_CFG_ECC_EN : 0U); 87*91f16700Schasinglulu } 88*91f16700Schasinglulu 89*91f16700Schasinglulu static int spi_nand_quad_enable(uint8_t manufacturer_id) 90*91f16700Schasinglulu { 91*91f16700Schasinglulu bool enable = false; 92*91f16700Schasinglulu 93*91f16700Schasinglulu if ((spinand_dev.flags & SPI_NAND_HAS_QE_BIT) == 0U) { 94*91f16700Schasinglulu return 0; 95*91f16700Schasinglulu } 96*91f16700Schasinglulu 97*91f16700Schasinglulu if (spinand_dev.spi_read_cache_op.data.buswidth == 98*91f16700Schasinglulu SPI_MEM_BUSWIDTH_4_LINE) { 99*91f16700Schasinglulu enable = true; 100*91f16700Schasinglulu } 101*91f16700Schasinglulu 102*91f16700Schasinglulu return spi_nand_update_cfg(SPI_NAND_CFG_QE, 103*91f16700Schasinglulu enable ? SPI_NAND_CFG_QE : 0U); 104*91f16700Schasinglulu } 105*91f16700Schasinglulu 106*91f16700Schasinglulu static int spi_nand_wait_ready(uint8_t *status) 107*91f16700Schasinglulu { 108*91f16700Schasinglulu int ret; 109*91f16700Schasinglulu uint64_t timeout = timeout_init_us(DELAY_US_400MS); 110*91f16700Schasinglulu 111*91f16700Schasinglulu while (!timeout_elapsed(timeout)) { 112*91f16700Schasinglulu ret = spi_nand_read_reg(SPI_NAND_REG_STATUS, status); 113*91f16700Schasinglulu if (ret != 0) { 114*91f16700Schasinglulu return ret; 115*91f16700Schasinglulu } 116*91f16700Schasinglulu 117*91f16700Schasinglulu VERBOSE("%s Status %x\n", __func__, *status); 118*91f16700Schasinglulu if ((*status & SPI_NAND_STATUS_BUSY) == 0U) { 119*91f16700Schasinglulu return 0; 120*91f16700Schasinglulu } 121*91f16700Schasinglulu } 122*91f16700Schasinglulu 123*91f16700Schasinglulu return -ETIMEDOUT; 124*91f16700Schasinglulu } 125*91f16700Schasinglulu 126*91f16700Schasinglulu static int spi_nand_reset(void) 127*91f16700Schasinglulu { 128*91f16700Schasinglulu struct spi_mem_op op; 129*91f16700Schasinglulu uint8_t status; 130*91f16700Schasinglulu int ret; 131*91f16700Schasinglulu 132*91f16700Schasinglulu zeromem(&op, sizeof(struct spi_mem_op)); 133*91f16700Schasinglulu op.cmd.opcode = SPI_NAND_OP_RESET; 134*91f16700Schasinglulu op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 135*91f16700Schasinglulu 136*91f16700Schasinglulu ret = spi_mem_exec_op(&op); 137*91f16700Schasinglulu if (ret != 0) { 138*91f16700Schasinglulu return ret; 139*91f16700Schasinglulu } 140*91f16700Schasinglulu 141*91f16700Schasinglulu return spi_nand_wait_ready(&status); 142*91f16700Schasinglulu } 143*91f16700Schasinglulu 144*91f16700Schasinglulu static int spi_nand_read_id(uint8_t *id) 145*91f16700Schasinglulu { 146*91f16700Schasinglulu struct spi_mem_op op; 147*91f16700Schasinglulu 148*91f16700Schasinglulu zeromem(&op, sizeof(struct spi_mem_op)); 149*91f16700Schasinglulu op.cmd.opcode = SPI_NAND_OP_READ_ID; 150*91f16700Schasinglulu op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 151*91f16700Schasinglulu op.data.dir = SPI_MEM_DATA_IN; 152*91f16700Schasinglulu op.data.nbytes = SPI_NAND_MAX_ID_LEN; 153*91f16700Schasinglulu op.data.buf = id; 154*91f16700Schasinglulu op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 155*91f16700Schasinglulu 156*91f16700Schasinglulu return spi_mem_exec_op(&op); 157*91f16700Schasinglulu } 158*91f16700Schasinglulu 159*91f16700Schasinglulu static int spi_nand_load_page(unsigned int page) 160*91f16700Schasinglulu { 161*91f16700Schasinglulu struct spi_mem_op op; 162*91f16700Schasinglulu uint32_t block_nb = page / spinand_dev.nand_dev->block_size; 163*91f16700Schasinglulu uint32_t page_nb = page - (block_nb * spinand_dev.nand_dev->page_size); 164*91f16700Schasinglulu uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size / 165*91f16700Schasinglulu spinand_dev.nand_dev->page_size; 166*91f16700Schasinglulu uint32_t block_sh = __builtin_ctz(nbpages_per_block) + 1U; 167*91f16700Schasinglulu 168*91f16700Schasinglulu zeromem(&op, sizeof(struct spi_mem_op)); 169*91f16700Schasinglulu op.cmd.opcode = SPI_NAND_OP_LOAD_PAGE; 170*91f16700Schasinglulu op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 171*91f16700Schasinglulu op.addr.val = (block_nb << block_sh) | page_nb; 172*91f16700Schasinglulu op.addr.nbytes = 3U; 173*91f16700Schasinglulu op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 174*91f16700Schasinglulu 175*91f16700Schasinglulu return spi_mem_exec_op(&op); 176*91f16700Schasinglulu } 177*91f16700Schasinglulu 178*91f16700Schasinglulu static int spi_nand_read_from_cache(unsigned int page, unsigned int offset, 179*91f16700Schasinglulu uint8_t *buffer, unsigned int len) 180*91f16700Schasinglulu { 181*91f16700Schasinglulu uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size / 182*91f16700Schasinglulu spinand_dev.nand_dev->page_size; 183*91f16700Schasinglulu uint32_t block_nb = page / nbpages_per_block; 184*91f16700Schasinglulu uint32_t page_sh = __builtin_ctz(spinand_dev.nand_dev->page_size) + 1U; 185*91f16700Schasinglulu 186*91f16700Schasinglulu spinand_dev.spi_read_cache_op.addr.val = offset; 187*91f16700Schasinglulu 188*91f16700Schasinglulu if ((spinand_dev.nand_dev->nb_planes > 1U) && ((block_nb % 2U) == 1U)) { 189*91f16700Schasinglulu spinand_dev.spi_read_cache_op.addr.val |= 1U << page_sh; 190*91f16700Schasinglulu } 191*91f16700Schasinglulu 192*91f16700Schasinglulu spinand_dev.spi_read_cache_op.data.buf = buffer; 193*91f16700Schasinglulu spinand_dev.spi_read_cache_op.data.nbytes = len; 194*91f16700Schasinglulu 195*91f16700Schasinglulu return spi_mem_exec_op(&spinand_dev.spi_read_cache_op); 196*91f16700Schasinglulu } 197*91f16700Schasinglulu 198*91f16700Schasinglulu static int spi_nand_read_page(unsigned int page, unsigned int offset, 199*91f16700Schasinglulu uint8_t *buffer, unsigned int len, 200*91f16700Schasinglulu bool ecc_enabled) 201*91f16700Schasinglulu { 202*91f16700Schasinglulu uint8_t status; 203*91f16700Schasinglulu int ret; 204*91f16700Schasinglulu 205*91f16700Schasinglulu ret = spi_nand_ecc_enable(ecc_enabled); 206*91f16700Schasinglulu if (ret != 0) { 207*91f16700Schasinglulu return ret; 208*91f16700Schasinglulu } 209*91f16700Schasinglulu 210*91f16700Schasinglulu ret = spi_nand_load_page(page); 211*91f16700Schasinglulu if (ret != 0) { 212*91f16700Schasinglulu return ret; 213*91f16700Schasinglulu } 214*91f16700Schasinglulu 215*91f16700Schasinglulu ret = spi_nand_wait_ready(&status); 216*91f16700Schasinglulu if (ret != 0) { 217*91f16700Schasinglulu return ret; 218*91f16700Schasinglulu } 219*91f16700Schasinglulu 220*91f16700Schasinglulu ret = spi_nand_read_from_cache(page, offset, buffer, len); 221*91f16700Schasinglulu if (ret != 0) { 222*91f16700Schasinglulu return ret; 223*91f16700Schasinglulu } 224*91f16700Schasinglulu 225*91f16700Schasinglulu if (ecc_enabled && ((status & SPI_NAND_STATUS_ECC_UNCOR) != 0U)) { 226*91f16700Schasinglulu return -EBADMSG; 227*91f16700Schasinglulu } 228*91f16700Schasinglulu 229*91f16700Schasinglulu return 0; 230*91f16700Schasinglulu } 231*91f16700Schasinglulu 232*91f16700Schasinglulu static int spi_nand_mtd_block_is_bad(unsigned int block) 233*91f16700Schasinglulu { 234*91f16700Schasinglulu unsigned int nbpages_per_block = spinand_dev.nand_dev->block_size / 235*91f16700Schasinglulu spinand_dev.nand_dev->page_size; 236*91f16700Schasinglulu uint8_t bbm_marker[2]; 237*91f16700Schasinglulu int ret; 238*91f16700Schasinglulu 239*91f16700Schasinglulu ret = spi_nand_read_page(block * nbpages_per_block, 240*91f16700Schasinglulu spinand_dev.nand_dev->page_size, 241*91f16700Schasinglulu bbm_marker, sizeof(bbm_marker), false); 242*91f16700Schasinglulu if (ret != 0) { 243*91f16700Schasinglulu return ret; 244*91f16700Schasinglulu } 245*91f16700Schasinglulu 246*91f16700Schasinglulu if ((bbm_marker[0] != GENMASK_32(7, 0)) || 247*91f16700Schasinglulu (bbm_marker[1] != GENMASK_32(7, 0))) { 248*91f16700Schasinglulu WARN("Block %u is bad\n", block); 249*91f16700Schasinglulu return 1; 250*91f16700Schasinglulu } 251*91f16700Schasinglulu 252*91f16700Schasinglulu return 0; 253*91f16700Schasinglulu } 254*91f16700Schasinglulu 255*91f16700Schasinglulu static int spi_nand_mtd_read_page(struct nand_device *nand, unsigned int page, 256*91f16700Schasinglulu uintptr_t buffer) 257*91f16700Schasinglulu { 258*91f16700Schasinglulu return spi_nand_read_page(page, 0, (uint8_t *)buffer, 259*91f16700Schasinglulu spinand_dev.nand_dev->page_size, true); 260*91f16700Schasinglulu } 261*91f16700Schasinglulu 262*91f16700Schasinglulu int spi_nand_init(unsigned long long *size, unsigned int *erase_size) 263*91f16700Schasinglulu { 264*91f16700Schasinglulu uint8_t id[SPI_NAND_MAX_ID_LEN]; 265*91f16700Schasinglulu int ret; 266*91f16700Schasinglulu 267*91f16700Schasinglulu spinand_dev.nand_dev = get_nand_device(); 268*91f16700Schasinglulu if (spinand_dev.nand_dev == NULL) { 269*91f16700Schasinglulu return -EINVAL; 270*91f16700Schasinglulu } 271*91f16700Schasinglulu 272*91f16700Schasinglulu spinand_dev.nand_dev->mtd_block_is_bad = spi_nand_mtd_block_is_bad; 273*91f16700Schasinglulu spinand_dev.nand_dev->mtd_read_page = spi_nand_mtd_read_page; 274*91f16700Schasinglulu spinand_dev.nand_dev->nb_planes = 1; 275*91f16700Schasinglulu 276*91f16700Schasinglulu spinand_dev.spi_read_cache_op.cmd.opcode = SPI_NAND_OP_READ_FROM_CACHE; 277*91f16700Schasinglulu spinand_dev.spi_read_cache_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 278*91f16700Schasinglulu spinand_dev.spi_read_cache_op.addr.nbytes = 2U; 279*91f16700Schasinglulu spinand_dev.spi_read_cache_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 280*91f16700Schasinglulu spinand_dev.spi_read_cache_op.dummy.nbytes = 1U; 281*91f16700Schasinglulu spinand_dev.spi_read_cache_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 282*91f16700Schasinglulu spinand_dev.spi_read_cache_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 283*91f16700Schasinglulu 284*91f16700Schasinglulu if (plat_get_spi_nand_data(&spinand_dev) != 0) { 285*91f16700Schasinglulu return -EINVAL; 286*91f16700Schasinglulu } 287*91f16700Schasinglulu 288*91f16700Schasinglulu assert((spinand_dev.nand_dev->page_size != 0U) && 289*91f16700Schasinglulu (spinand_dev.nand_dev->block_size != 0U) && 290*91f16700Schasinglulu (spinand_dev.nand_dev->size != 0U)); 291*91f16700Schasinglulu 292*91f16700Schasinglulu ret = spi_nand_reset(); 293*91f16700Schasinglulu if (ret != 0) { 294*91f16700Schasinglulu return ret; 295*91f16700Schasinglulu } 296*91f16700Schasinglulu 297*91f16700Schasinglulu ret = spi_nand_read_id(id); 298*91f16700Schasinglulu if (ret != 0) { 299*91f16700Schasinglulu return ret; 300*91f16700Schasinglulu } 301*91f16700Schasinglulu 302*91f16700Schasinglulu ret = spi_nand_read_reg(SPI_NAND_REG_CFG, &spinand_dev.cfg_cache); 303*91f16700Schasinglulu if (ret != 0) { 304*91f16700Schasinglulu return ret; 305*91f16700Schasinglulu } 306*91f16700Schasinglulu 307*91f16700Schasinglulu ret = spi_nand_quad_enable(id[1]); 308*91f16700Schasinglulu if (ret != 0) { 309*91f16700Schasinglulu return ret; 310*91f16700Schasinglulu } 311*91f16700Schasinglulu 312*91f16700Schasinglulu VERBOSE("SPI_NAND Detected ID 0x%x\n", id[1]); 313*91f16700Schasinglulu 314*91f16700Schasinglulu VERBOSE("Page size %u, Block size %u, size %llu\n", 315*91f16700Schasinglulu spinand_dev.nand_dev->page_size, 316*91f16700Schasinglulu spinand_dev.nand_dev->block_size, 317*91f16700Schasinglulu spinand_dev.nand_dev->size); 318*91f16700Schasinglulu 319*91f16700Schasinglulu *size = spinand_dev.nand_dev->size; 320*91f16700Schasinglulu *erase_size = spinand_dev.nand_dev->block_size; 321*91f16700Schasinglulu 322*91f16700Schasinglulu return 0; 323*91f16700Schasinglulu } 324