| /**************************************************************************** |
| * 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); |
| } |