mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-23 13:36:05 +00:00

The raw NAND framework supports SLC NAND devices. It introduces a new high level interface (io_mtd) that defines operations a driver can register to the NAND framework. This interface will fill in the io_mtd device specification: - device_size - erase_size that could be used by the io_storage interface. NAND core source file integrates the standard read loop that performs NAND device read operations using a skip bad block strategy. A platform buffer must be defined in case of unaligned data. This buffer must fit to the maximum device page size defined by PLATFORM_MTD_MAX_PAGE_SIZE. The raw_nand.c source file embeds the specific NAND operations to read data. The read command is a raw page read without any ECC correction. This can be overridden by a low level driver. No generic support for write or erase command or software ECC correction. NAND ONFI detection is available and can be enabled using NAND_ONFI_DETECT=1. For non-ONFI NAND management, platform can define required information. Change-Id: Id80e9864456cf47f02b74938cf25d99261da8e82 Signed-off-by: Lionel Debieve <lionel.debieve@st.com> Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
118 lines
2.5 KiB
C
118 lines
2.5 KiB
C
/*
|
|
* Copyright (c) 2019, STMicroelectronics - All Rights Reserved
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
|
|
#include <platform_def.h>
|
|
|
|
#include <common/debug.h>
|
|
#include <drivers/delay_timer.h>
|
|
#include <drivers/nand.h>
|
|
#include <lib/utils.h>
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
struct nand_device *get_nand_device(void)
|
|
{
|
|
return &nand_dev;
|
|
}
|