You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
3.6 KiB
176 lines
3.6 KiB
/*
|
|
* Copyright (c) 2019-2022, STMicroelectronics - All Rights Reserved
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
|
|
#include <common/debug.h>
|
|
#include <drivers/delay_timer.h>
|
|
#include <drivers/nand.h>
|
|
#include <lib/utils.h>
|
|
|
|
#include <platform_def.h>
|
|
|
|
/*
|
|
* Define a single nand_device used by specific NAND frameworks.
|
|
*/
|
|
static struct nand_device nand_dev;
|
|
|
|
#pragma weak plat_get_scratch_buffer
|
|
void plat_get_scratch_buffer(void **buffer_addr, size_t *buf_size)
|
|
{
|
|
static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE];
|
|
|
|
assert(buffer_addr != NULL);
|
|
assert(buf_size != NULL);
|
|
|
|
*buffer_addr = (void *)scratch_buff;
|
|
*buf_size = sizeof(scratch_buff);
|
|
}
|
|
|
|
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;
|
|
uint8_t *scratch_buff;
|
|
size_t scratch_buff_size;
|
|
|
|
plat_get_scratch_buffer((void **)&scratch_buff, &scratch_buff_size);
|
|
|
|
assert(scratch_buff != NULL);
|
|
|
|
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) &&
|
|
(scratch_buff_size < 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;
|
|
}
|
|
|