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