1bec133385
Adds virtual NAND Flash device simulator, NAND flash log wrapper, and documentation for it. Signed-off-by: Saurav Pal <resyfer.dev@gmail.com>
476 lines
14 KiB
C
476 lines
14 KiB
C
/****************************************************************************
|
|
* drivers/mtd/mtd_nandram.c
|
|
* This file deals with the raw lower half of the device driver, and manages
|
|
* reading and writing to the actual NAND Flash device that has been emulated
|
|
* from RAM.
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <nuttx/compiler.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/mtd/nand_ram.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_MTD_NAND_RAM_DEBUG
|
|
|
|
#define NAND_RAM_DEBUG_1 1
|
|
#define NAND_RAM_DEBUG_2 5
|
|
#define NAND_RAM_DEBUG_3 10
|
|
|
|
#define NAND_RAM_STATUS_1 1
|
|
#define NAND_RAM_STATUS_2 5
|
|
#define NAND_RAM_STATUS_3 10
|
|
#define NAND_RAM_STATUS_4 50
|
|
#define NAND_RAM_STATUS_5 100
|
|
#define NAND_RAM_STATUS_6 500
|
|
#define NAND_RAM_STATUS_7 1000
|
|
#define NAND_RAM_STATUS_8 5000
|
|
|
|
#if CONFIG_MTD_NAND_RAM_DEBUG_LEVEL == 1
|
|
#define NAND_RAM_DEBUG_LEVEL NAND_RAM_DEBUG_1
|
|
#elif CONFIG_MTD_NAND_RAM_DEBUG_LEVEL == 2
|
|
#define NAND_RAM_DEBUG_LEVEL NAND_RAM_DEBUG_2
|
|
#elif CONFIG_MTD_NAND_RAM_DEBUG_LEVEL == 3
|
|
#define NAND_RAM_DEBUG_LEVEL NAND_RAM_DEBUG_3
|
|
#endif /* CONFIG_MTD_NAND_RAM_DEBUG_LEVEL */
|
|
|
|
#if CONFIG_MTD_NAND_RAM_STATUS == 1
|
|
#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_1
|
|
#elif CONFIG_MTD_NAND_RAM_STATUS == 2
|
|
#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_2
|
|
#elif CONFIG_MTD_NAND_RAM_STATUS == 3
|
|
#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_3
|
|
#elif CONFIG_MTD_NAND_RAM_STATUS == 4
|
|
#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_4
|
|
#elif CONFIG_MTD_NAND_RAM_STATUS == 5
|
|
#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_5
|
|
#elif CONFIG_MTD_NAND_RAM_STATUS == 6
|
|
#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_6
|
|
#elif CONFIG_MTD_NAND_RAM_STATUS == 7
|
|
#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_7
|
|
#elif CONFIG_MTD_NAND_RAM_STATUS == 8
|
|
#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_8
|
|
#endif /* CONFIG_MTD_NAND_RAM_STATUS */
|
|
|
|
#define NAND_RAM_LOG(str, ...) \
|
|
{ \
|
|
if (nand_ram_ins_i % NAND_RAM_DEBUG_LEVEL == 0) \
|
|
{ \
|
|
syslog(LOG_DEBUG, "nand_ram: " str, __VA_ARGS__); \
|
|
} \
|
|
} \
|
|
|
|
#define NAND_RAM_STATUS_LOG(str, ...) \
|
|
syslog(LOG_DEBUG, "nand_ram_status: " str, __VA_ARGS__);
|
|
|
|
#else
|
|
|
|
#define NAND_RAM_LOG
|
|
#define NAND_RAM_STATUS_LOG
|
|
|
|
#endif /* CONFIG_MTD_NAND_RAM_DEBUG */
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct nand_ram_data_s
|
|
{
|
|
uint8_t page[NAND_RAM_PAGE_SIZE / 8];
|
|
};
|
|
|
|
/* 512 B page spare scheme */
|
|
|
|
struct nand_ram_spare_s
|
|
{
|
|
uint8_t ecc_0; /* 0 */
|
|
uint8_t ecc_1;
|
|
uint8_t ecc_2;
|
|
uint8_t ecc_3;
|
|
uint8_t __res1;
|
|
uint8_t bad; /* 5 */ /* NAND_RAM_BLOCK_* */
|
|
uint8_t ecc_4;
|
|
uint8_t ecc_5;
|
|
|
|
/* Using reserved (8 bytes) */
|
|
|
|
uint16_t n_read;
|
|
uint16_t n_write; /* 10 */
|
|
uint16_t n_erase;
|
|
uint8_t free; /* Erased page: NAND_RAM_PAGE_* */
|
|
uint8_t __res2;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static uint64_t nand_ram_ins_i = 0; /* Instruction counter */
|
|
static mutex_t nand_ram_dev_mut;
|
|
static struct nand_ram_data_s nand_ram_flash_data[NAND_RAM_N_PAGES];
|
|
static struct nand_ram_spare_s nand_ram_flash_spare[NAND_RAM_N_PAGES];
|
|
|
|
/* Hard coded array for bad block indexes */
|
|
|
|
static int g_nand_ram_rand_bad_blk_indx[] =
|
|
{
|
|
4, 14, 19, 21, 28, 30, 107,
|
|
108, 164, 173, 179, 229, 268,
|
|
362, 377, 382, 396, 410, 412,
|
|
419, 428, 456, 500, 0
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* External Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: nand_ram_storage_status
|
|
*
|
|
* Description:
|
|
* Writes per-page status of virtual NAND Flash.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void nand_ram_storage_status(void)
|
|
{
|
|
uint32_t i;
|
|
uint16_t reads;
|
|
uint16_t writes;
|
|
uint16_t erases;
|
|
uint8_t bad;
|
|
|
|
/* Wear */
|
|
|
|
for (i = 0; i < NAND_RAM_N_PAGES; i++)
|
|
{
|
|
reads = nand_ram_flash_spare[i].n_read;
|
|
writes = nand_ram_flash_spare[i].n_write;
|
|
erases = nand_ram_flash_spare[i].n_erase;
|
|
bad = (nand_ram_flash_spare[i].bad != NAND_RAM_BLOCK_GOOD);
|
|
|
|
NAND_RAM_STATUS_LOG(
|
|
"Block %3d, Page %6d, Bad: %1d |"
|
|
" Reads: %6d, Writes: %6d, Erases: %6d\n",
|
|
i >> NAND_RAM_LOG_PAGES_PER_BLOCK, i, bad,
|
|
reads, writes, erases);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static inline void nand_ram_status(void)
|
|
{
|
|
if (nand_ram_ins_i % NAND_RAM_STATUS_LEVEL == 0)
|
|
{
|
|
nand_ram_storage_status();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nand_ram_storage_init
|
|
*
|
|
* Description:
|
|
* Initializes the actual NAND Device that is emulated from RAM.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void nand_ram_storage_init(void)
|
|
{
|
|
int i;
|
|
|
|
memset(nand_ram_flash_data, 0xff,
|
|
sizeof(struct nand_ram_data_s) * NAND_RAM_N_PAGES);
|
|
memset(nand_ram_flash_spare, 0,
|
|
sizeof(struct nand_ram_spare_s) * NAND_RAM_N_PAGES);
|
|
|
|
for (i = 0; i < NAND_RAM_N_PAGES; i++)
|
|
{
|
|
nand_ram_flash_spare[i].free = NAND_RAM_PAGE_FREE;
|
|
nand_ram_flash_spare[i].bad = NAND_RAM_BLOCK_GOOD;
|
|
}
|
|
|
|
/* Bad blocks */
|
|
|
|
for (i = 0;
|
|
g_nand_ram_rand_bad_blk_indx[i] != 0 &&
|
|
g_nand_ram_rand_bad_blk_indx[i] < NAND_RAM_N_BLOCKS;
|
|
i++)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < NAND_RAM_PAGES_PER_BLOCK; j++)
|
|
{
|
|
int page = (g_nand_ram_rand_bad_blk_indx[i] <<
|
|
NAND_RAM_LOG_PAGES_PER_BLOCK)+j;
|
|
|
|
/* Set bad block marker to Anything but NAND_RAM_BLOCK_GOOD */
|
|
|
|
nand_ram_flash_spare[page].bad = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: nand_ram_eraseblock
|
|
*
|
|
* Description:
|
|
* Erases a block on the device.
|
|
*
|
|
* Input Parameters:
|
|
* raw: NAND MTD Device raw structure.
|
|
* block: Block number (0 indexing) to erase
|
|
*
|
|
* Returned Value:
|
|
* 0: Successful
|
|
* < 0: Error
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nand_ram_eraseblock(FAR struct nand_raw_s *raw, off_t block)
|
|
{
|
|
int i;
|
|
uint32_t start_page;
|
|
uint32_t end_page;
|
|
|
|
start_page = block << NAND_RAM_LOG_PAGES_PER_BLOCK;
|
|
end_page = start_page + NAND_RAM_PAGES_PER_BLOCK;
|
|
|
|
nxmutex_lock(&nand_ram_dev_mut);
|
|
nand_ram_ins_i++;
|
|
|
|
NAND_RAM_LOG(
|
|
"[LOWER %lu | %s] Block %d, Start Page: %d, Last Page: %d",
|
|
nand_ram_ins_i, "eraseblock", block, start_page, end_page - 1
|
|
);
|
|
nand_ram_status();
|
|
|
|
/* [start_page, end_page) is cleared (all bits are set) */
|
|
|
|
memset(nand_ram_flash_data + start_page, 0xff,
|
|
(end_page - start_page) * sizeof(struct nand_ram_data_s));
|
|
for (i = start_page; i < end_page; i++)
|
|
{
|
|
nand_ram_flash_spare[i].n_erase++;
|
|
nand_ram_flash_spare[i].free = 1;
|
|
}
|
|
|
|
NAND_RAM_LOG("[LOWER %lu | %s] Done\n", nand_ram_ins_i, "eraseblock");
|
|
|
|
nxmutex_unlock(&nand_ram_dev_mut);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nand_ram_rawread
|
|
*
|
|
* Description:
|
|
* Reads a page from the device.
|
|
*
|
|
* Input Parameters:
|
|
* raw: NAND MTD Device raw structure.
|
|
* block: Block number (0 indexing) to erase
|
|
* page: Page number (0 indexing) in (relative to) that block
|
|
* data: Preallocated memory where the data will be copied to
|
|
* spare: Preallocated memory where the spare data will be copied to
|
|
*
|
|
* Returned Value:
|
|
* 0: Successful
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nand_ram_rawread(FAR struct nand_raw_s *raw, off_t block,
|
|
unsigned int page, FAR void *data, FAR void *spare)
|
|
{
|
|
int ret;
|
|
uint32_t read_page;
|
|
struct nand_ram_data_s *read_page_data;
|
|
struct nand_ram_spare_s *read_page_spare;
|
|
|
|
read_page = (block << NAND_RAM_LOG_PAGES_PER_BLOCK) + page;
|
|
read_page_data = nand_ram_flash_data + read_page;
|
|
read_page_spare = nand_ram_flash_spare + read_page;
|
|
|
|
ret = OK;
|
|
|
|
nxmutex_lock(&nand_ram_dev_mut);
|
|
nand_ram_ins_i++;
|
|
|
|
NAND_RAM_LOG("[LOWER %lu | %s] Page %d\n",
|
|
nand_ram_ins_i, "rawread", read_page);
|
|
nand_ram_status();
|
|
|
|
if (nand_ram_flash_spare[read_page].bad != NAND_RAM_BLOCK_GOOD)
|
|
{
|
|
ret = -EFAULT;
|
|
NAND_RAM_LOG("[LOWER %lu | %s] Failed: %s\n",
|
|
nand_ram_ins_i, "rawread", EFAULT_STR);
|
|
goto errout;
|
|
}
|
|
|
|
nand_ram_flash_spare[read_page].n_read++;
|
|
|
|
if (data != NULL)
|
|
{
|
|
memcpy(data, (const void *)read_page_data, NAND_RAM_PAGE_SIZE);
|
|
}
|
|
|
|
if (spare != NULL)
|
|
{
|
|
memcpy(spare, (const void *)read_page_spare, NAND_RAM_PAGE_SIZE);
|
|
}
|
|
|
|
NAND_RAM_LOG("[LOWER %lu | %s] Done\n", nand_ram_ins_i, "rawread");
|
|
|
|
errout:
|
|
nxmutex_unlock(&nand_ram_dev_mut);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nand_ram_rawread
|
|
*
|
|
* Description:
|
|
* Writes a page to the device.
|
|
*
|
|
* Input Parameters:
|
|
* raw: NAND MTD Device raw structure.
|
|
* block: Block number (0 indexing) to erase
|
|
* page: Page number (0 indexing) in (relative to) that block
|
|
* data: Preallocated memory where the data will be copied to
|
|
* spare: Preallocated memory where the spare data will be copied to
|
|
*
|
|
* Returned Value:
|
|
* 0: Successful
|
|
* -EACCESS: The page's block needs to be erased first before writing to it
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nand_ram_rawwrite(FAR struct nand_raw_s *raw, off_t block,
|
|
unsigned int page, FAR const void *data,
|
|
FAR const void *spare)
|
|
{
|
|
int ret;
|
|
uint32_t write_page;
|
|
struct nand_ram_data_s *write_page_data;
|
|
struct nand_ram_spare_s *write_page_spare;
|
|
|
|
write_page = (block << NAND_RAM_LOG_PAGES_PER_BLOCK) + page;
|
|
write_page_data = nand_ram_flash_data + write_page;
|
|
write_page_spare = nand_ram_flash_spare + write_page;
|
|
|
|
ret = OK;
|
|
|
|
nxmutex_lock(&nand_ram_dev_mut);
|
|
nand_ram_ins_i++;
|
|
|
|
NAND_RAM_LOG("[LOWER %lu | %s] Page %d\n",
|
|
nand_ram_ins_i, "rawwrite", write_page);
|
|
nand_ram_status();
|
|
|
|
if (nand_ram_flash_spare[write_page].free != NAND_RAM_PAGE_FREE)
|
|
{
|
|
ret = -EACCES;
|
|
NAND_RAM_LOG("[LOWER %lu | %s] Failed: %s\n",
|
|
nand_ram_ins_i, "rawwrite", EACCES_STR);
|
|
goto errout;
|
|
}
|
|
|
|
nand_ram_flash_spare[write_page].n_write++;
|
|
|
|
if (data != NULL)
|
|
{
|
|
memcpy((void *)write_page_data, data, NAND_RAM_PAGE_SIZE);
|
|
}
|
|
|
|
if (spare != NULL)
|
|
{
|
|
memcpy((void *)write_page_spare, data, NAND_RAM_PAGE_SIZE);
|
|
}
|
|
|
|
NAND_RAM_LOG("[LOWER %lu | %s] Done\n", nand_ram_ins_i, "rawwrite");
|
|
|
|
errout:
|
|
nxmutex_unlock(&nand_ram_dev_mut);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nand_ram_init
|
|
*
|
|
* Description:
|
|
* Driver init.
|
|
*
|
|
* Input Parameters:
|
|
* raw: NAND MTD Device raw structure.
|
|
*
|
|
* Returned Value:
|
|
* A non-NULL MTD driver instance is returned on success. NULL is
|
|
* returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
FAR struct mtd_dev_s *nand_ram_initialize(struct nand_raw_s *raw)
|
|
{
|
|
NAND_RAM_LOG("[LOWER | %s]\n", "initialize");
|
|
|
|
nand_ram_storage_init();
|
|
nxmutex_init(&nand_ram_dev_mut);
|
|
|
|
raw->model.devid = 123;
|
|
raw->model.pagesize = NAND_RAM_PAGE_SIZE;
|
|
raw->model.sparesize = NAND_RAM_SPARE_SIZE;
|
|
raw->model.devsize = NAND_RAM_SIZE / (1024 * 1024);
|
|
raw->model.blocksize = NAND_RAM_BLOCK_SIZE / 1024;
|
|
raw->model.scheme = &g_nand_sparescheme512;
|
|
|
|
raw->eraseblock = nand_ram_eraseblock;
|
|
raw->rawread = nand_ram_rawread;
|
|
raw->rawwrite = nand_ram_rawwrite;
|
|
|
|
return nand_raw_initialize(raw);
|
|
}
|