/* * Copyright (c) 2019-2021, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include /* * Define a single nand_device used by specific NAND frameworks. */ static struct nand_device nand_dev; static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE]; int nand_read(unsigned int offset, uintptr_t buffer, size_t length, size_t *length_read) { unsigned int block = offset / nand_dev.block_size; unsigned int end_block = (offset + length - 1U) / nand_dev.block_size; unsigned int page_start = (offset % nand_dev.block_size) / nand_dev.page_size; unsigned int nb_pages = nand_dev.block_size / nand_dev.page_size; unsigned int start_offset = offset % nand_dev.page_size; unsigned int page; unsigned int bytes_read; int is_bad; int ret; VERBOSE("Block %u - %u, page_start %u, nb %u, length %zu, offset %u\n", block, end_block, page_start, nb_pages, length, offset); *length_read = 0UL; if (((start_offset != 0U) || (length % nand_dev.page_size) != 0U) && (sizeof(scratch_buff) < nand_dev.page_size)) { return -EINVAL; } while (block <= end_block) { is_bad = nand_dev.mtd_block_is_bad(block); if (is_bad < 0) { return is_bad; } if (is_bad == 1) { /* Skip the block */ uint32_t max_block = nand_dev.size / nand_dev.block_size; block++; end_block++; if ((block < max_block) && (end_block < max_block)) { continue; } return -EIO; } for (page = page_start; page < nb_pages; page++) { if ((start_offset != 0U) || (length < nand_dev.page_size)) { ret = nand_dev.mtd_read_page( &nand_dev, (block * nb_pages) + page, (uintptr_t)scratch_buff); if (ret != 0) { return ret; } bytes_read = MIN((size_t)(nand_dev.page_size - start_offset), length); memcpy((uint8_t *)buffer, scratch_buff + start_offset, bytes_read); start_offset = 0U; } else { ret = nand_dev.mtd_read_page(&nand_dev, (block * nb_pages) + page, buffer); if (ret != 0) { return ret; } bytes_read = nand_dev.page_size; } length -= bytes_read; buffer += bytes_read; *length_read += bytes_read; if (length == 0U) { break; } } page_start = 0U; block++; } return 0; } int nand_seek_bb(uintptr_t base, unsigned int offset, size_t *extra_offset) { unsigned int block; unsigned int offset_block; unsigned int max_block; int is_bad; size_t count_bb = 0U; block = base / nand_dev.block_size; if (offset != 0U) { offset_block = (base + offset - 1U) / nand_dev.block_size; } else { offset_block = block; } max_block = nand_dev.size / nand_dev.block_size; while (block <= offset_block) { if (offset_block >= max_block) { return -EIO; } is_bad = nand_dev.mtd_block_is_bad(block); if (is_bad < 0) { return is_bad; } if (is_bad == 1) { count_bb++; offset_block++; } block++; } *extra_offset = count_bb * nand_dev.block_size; return 0; } struct nand_device *get_nand_device(void) { return &nand_dev; }