diff --git a/boards/xtensa/esp32/common/src/esp32_board_spiflash.c b/boards/xtensa/esp32/common/src/esp32_board_spiflash.c index 9c388b0f6c..3b86557ded 100644 --- a/boards/xtensa/esp32/common/src/esp32_board_spiflash.c +++ b/boards/xtensa/esp32/common/src/esp32_board_spiflash.c @@ -397,6 +397,18 @@ static int init_storage_partition(void) return ret; } +#elif defined(CONFIG_MTD_NVBLK) + + ret = nvblk_initialize("/dev/mtdblock0", mtd, + CONFIG_MTD_NVBLK_DEFAULT_LBS, + CONFIG_MTD_NVBLK_DEFAULT_IOBS, + CONFIG_MTD_NVBLK_DEFAULT_SPEB); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to setup nvblk\n"); + return ret; + } + #else ret = register_mtddriver("/dev/esp32flash", mtd, 0755, NULL); diff --git a/boards/xtensa/esp32/esp32-devkitc/configs/nvblk/defconfig b/boards/xtensa/esp32/esp32-devkitc/configs/nvblk/defconfig new file mode 100644 index 0000000000..f388114b89 --- /dev/null +++ b/boards/xtensa/esp32/esp32-devkitc/configs/nvblk/defconfig @@ -0,0 +1,65 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_LEDS is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="xtensa" +CONFIG_ARCH_BOARD="esp32-devkitc" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_ESP32_DEVKITC=y +CONFIG_ARCH_CHIP="esp32" +CONFIG_ARCH_CHIP_ESP32=y +CONFIG_ARCH_CHIP_ESP32WROVER=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARCH_USE_TEXT_HEAP=y +CONFIG_ARCH_XTENSA=y +CONFIG_BOARDCTL_ROMDISK=y +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILTIN=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_ESP32_IRAM_HEAP=y +CONFIG_ESP32_SPIFLASH=y +CONFIG_ESP32_UART0=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_EXECFUNCS_HAVE_SYMTAB=y +CONFIG_EXECFUNCS_SYSTEM_SYMTAB=y +CONFIG_FS_FAT=y +CONFIG_FS_PROCFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_IDLETHREAD_STACKSIZE=3072 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=3072 +CONFIG_INTELHEX_BINARY=y +CONFIG_LIBC_EXECFUNCS=y +CONFIG_LIBC_PERROR_STDOUT=y +CONFIG_LIBC_STRERROR=y +CONFIG_LINE_MAX=64 +CONFIG_MM_REGIONS=3 +CONFIG_MTD_NVBLK=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_LPWORK=y +CONFIG_SCHED_WAITPID=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_SYMTAB_ORDEREDBYNAME=y +CONFIG_SYSLOG_BUFFER=y +CONFIG_SYSTEM_NSH=y +CONFIG_TLS_NELEM=4 +CONFIG_UART0_SERIAL_CONSOLE=y diff --git a/drivers/mtd/.gitignore b/drivers/mtd/.gitignore index 87e3b49806..ee59a39219 100644 --- a/drivers/mtd/.gitignore +++ b/drivers/mtd/.gitignore @@ -1 +1,2 @@ /dhara +/nvblk diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 38a364f58e..0104ae2177 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1459,6 +1459,25 @@ config DHARA_READ_NCACHES default 4 endif +config MTD_NVBLK + bool "MTD using Non Volatile BLock driver" + default n + +if MTD_NVBLK + +config MTD_NVBLK_DEFAULT_LBS + int "nvblk (logical) block size" + default 256 + +config MTD_NVBLK_DEFAULT_IOBS + int "nvblk io block size (block device sector size)" + default 512 + +config MTD_NVBLK_DEFAULT_SPEB + int "nvblk spare erase blocks" + default 1 +endif + config MTD_CFI bool "CFI(common flash interface) NOR FLASH" default n diff --git a/drivers/mtd/Make.defs b/drivers/mtd/Make.defs index e357e8c84c..11a3b2e595 100644 --- a/drivers/mtd/Make.defs +++ b/drivers/mtd/Make.defs @@ -208,6 +208,29 @@ CSRCS += mtd/dhara/dhara/journal.c CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)drivers$(DELIM)mtd$(DELIM)dhara endif +ifeq ($(CONFIG_MTD_NVBLK),y) + +main.zip: + $(call DOWNLOAD,https://github.com/Laczen/nvblk/archive/refs/heads,main.zip,mtd/nvblk-main.zip) + +.nvblkunpack: main.zip + $(Q) unzip mtd/nvblk-main.zip + $(call DELFILE, mtd/nvblk-main.zip) + $(Q) mv nvblk-main mtd/nvblk + $(Q) touch mtd/nvblk/.nvblkunpack + +ifeq ($(wildcard mtd/nvblk/.gitignore),) +context:: .nvblkunpack +endif + +distclean:: + $(call DELDIR, mtd/nvblk) + +CSRCS += nvblk.c +CSRCS += mtd/nvblk/src/nvblk.c +CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)drivers$(DELIM)mtd$(DELIM)nvblk$(DELIM)include +endif + ifeq ($(CONFIG_MTD_CFI),y) CSRCS += mtd_cfi.c CSRCS += cfi.c diff --git a/drivers/mtd/nvblk.c b/drivers/mtd/nvblk.c new file mode 100644 index 0000000000..236eaa58df --- /dev/null +++ b/drivers/mtd/nvblk.c @@ -0,0 +1,559 @@ +/**************************************************************************** + * drivers/mtd/nvblk.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 + +#include +#include +#include + +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NVBLK_MIN_LBS 128 /* Minimal logical block size */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nvblk_dev_s +{ + struct nvb_config cfg; /* nvblk configuration */ + struct nvb_info info; /* nvblk data */ + mutex_t lock; + + FAR struct mtd_dev_s *mtd; /* Contained MTD interface */ + struct mtd_geometry_s geo; /* Device geometry */ + uint16_t refs; /* Number of references */ + uint8_t log2_bpiob; /* (logical) blocks per IO block */ + uint8_t log2_ppb; /* pages per (logical) block */ + bool unlinked; /* The driver has been unlinked */ + + /* Two pagesize buffer first is for working temp buffer + * second is for journel use + */ + + FAR uint8_t *pagebuf; +}; + +typedef struct nvblk_dev_s nvblk_dev_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int nvblk_open(FAR struct inode *inode); +static int nvblk_close(FAR struct inode *inode); +static ssize_t nvblk_read(FAR struct inode *inode, + FAR unsigned char *buffer, + blkcnt_t start_sector, + unsigned int nsectors); +static ssize_t nvblk_write(FAR struct inode *inode, + FAR const unsigned char *buffer, + blkcnt_t start_sector, + unsigned int nsectors); +static int nvblk_geometry(FAR struct inode *inode, + FAR struct geometry *geometry); +static int nvblk_ioctl(FAR struct inode *inode, + int cmd, + unsigned long arg); +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int nvblk_unlink(FAR struct inode *inode); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct block_operations g_nvblk_bops = +{ + nvblk_open, /* open */ + nvblk_close, /* close */ + nvblk_read, /* read */ + nvblk_write, /* write */ + nvblk_geometry, /* geometry */ + nvblk_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , nvblk_unlink /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int nvblk_convert_result(int err) +{ + switch (err) + { + case 0: + return 0; + case -NVB_ENOENT: + return -ENOENT; + case -NVB_EINVAL: + return -EINVAL; + case -NVB_EROFS: + return -EROFS; + case -NVB_EAGAIN: + return -EAGAIN; + case -NVB_ENOSPC: + return -ENOSPC; + default: + return -EFAULT; + } +} + +/**************************************************************************** + * Name: nvblk_open + * + * Description: Open the block device + * + ****************************************************************************/ + +static int nvblk_open(FAR struct inode *inode) +{ + FAR nvblk_dev_t *dev; + + DEBUGASSERT(inode->i_private); + dev = inode->i_private; + nxmutex_lock(&dev->lock); + dev->refs++; + nxmutex_unlock(&dev->lock); + + return 0; +} + +/**************************************************************************** + * Name: nvblk_close + * + * Description: close the block device + * + ****************************************************************************/ + +static int nvblk_close(FAR struct inode *inode) +{ + FAR nvblk_dev_t *dev; + + DEBUGASSERT(inode->i_private); + dev = inode->i_private; + nxmutex_lock(&dev->lock); + (void)nvb_sync(&dev->info); + dev->refs--; + nxmutex_unlock(&dev->lock); + + if (dev->refs == 0 && dev->unlinked) + { + nxmutex_destroy(&dev->lock); + kmm_free(dev->pagebuf); + kmm_free(dev); + } + + return 0; +} + +/**************************************************************************** + * Name: nvblk_read + * + * Description: Read the specified number of sectors + * + ****************************************************************************/ + +static ssize_t nvblk_read(FAR struct inode *inode, + FAR unsigned char *buffer, + blkcnt_t start_sector, + unsigned int nsectors) +{ + FAR nvblk_dev_t *dev; + uint16_t bstart; + uint16_t bcnt; + uint16_t n; + int ret = 0; + + finfo("Read %lld %zd\n", (long long)start_sector, nsectors); + + DEBUGASSERT(inode->i_private); + dev = inode->i_private; + nxmutex_lock(&dev->lock); + bstart = start_sector << dev->log2_bpiob; + bcnt = nsectors << dev->log2_bpiob; + + for (n = 0; n < bcnt; n++) + { + ret = nvb_read(&dev->info, buffer, bstart, 1); + if (ret == -NVB_ENOENT) + { + memset(buffer, 0xff, (1 << dev->cfg.log2_bs)); + ret = 0; + } + + if (ret < 0) + { + break; + } + + buffer += (1 << dev->cfg.log2_bs); + bstart++; + } + + if (ret < 0) + { + ret = nvblk_convert_result(ret); + ferr("Read startblock %lld failed nsectors %zd err: %d\n", + (long long)start_sector, nsectors, ret); + } + + nxmutex_unlock(&dev->lock); + return ret < 0 ? ret : nsectors; +} + +/**************************************************************************** + * Name: nvblk_write + * + * Description: Write the specified number of sectors + * + ****************************************************************************/ + +static ssize_t nvblk_write(FAR struct inode *inode, + FAR const unsigned char *buffer, + blkcnt_t start_sector, + unsigned int nsectors) +{ + FAR nvblk_dev_t *dev; + uint16_t bstart; + uint16_t bcnt; + int ret = 0; + + finfo("Write %lld %zd\n", (long long)start_sector, nsectors); + + DEBUGASSERT(inode->i_private); + dev = inode->i_private; + nxmutex_lock(&dev->lock); + bstart = start_sector << dev->log2_bpiob; + bcnt = nsectors << dev->log2_bpiob; + ret = nvblk_convert_result(nvb_write(&dev->info, buffer, bstart, bcnt)); + if (ret < 0) + { + ferr("Write startblock %lld failed nsectors %zd err: %d\n", + (long long)start_sector, nsectors, ret); + } + + nxmutex_unlock(&dev->lock); + return ret < 0 ? ret : nsectors; +} + +/**************************************************************************** + * Name: nvblk_geometry + * + * Description: Return device geometry + * + ****************************************************************************/ + +static int nvblk_geometry(FAR struct inode *inode, + FAR struct geometry *geometry) +{ + FAR nvblk_dev_t *dev; + uint32_t blkcnt; + int ret = -EINVAL; + + DEBUGASSERT(inode->i_private); + dev = inode->i_private; + + if (!geometry) + { + return ret; + } + + nxmutex_lock(&dev->lock); + ret = nvb_ioctl(&dev->info, NVB_CMD_GET_BLK_COUNT, &blkcnt); + + if (ret < 0) + { + ret = nvblk_convert_result(ret); + goto end; + } + + geometry->geo_available = true; + geometry->geo_mediachanged = false; + geometry->geo_writeenabled = true; + geometry->geo_nsectors = blkcnt >> dev->log2_bpiob; + geometry->geo_sectorsize = (1 << (dev->log2_bpiob + dev->cfg.log2_bs)); + strlcpy(geometry->geo_model, dev->geo.model, sizeof(geometry->geo_model)); + +end: + nxmutex_unlock(&dev->lock); + return ret; +} + +/**************************************************************************** + * Name: nvblk_ioctl + * + * Description: + * Set/Get option to/from block device. + * + * No ioctl commands are supported. + * + ****************************************************************************/ + +static int nvblk_ioctl(FAR struct inode *inode, + int cmd, + unsigned long arg) +{ + FAR nvblk_dev_t *dev; + int ret; + + DEBUGASSERT(inode->i_private); + dev = inode->i_private; + + nxmutex_lock(&dev->lock); + switch (cmd) + { + case BIOC_FLUSH: + { + ret = nvb_sync(&dev->info); + if (ret < 0) + { + ferr("sync failed: %d\n", ret); + } + } + break; + + default: + ret = -ENOTTY; + break; + } + + nxmutex_unlock(&dev->lock); + return ret; +} + +/**************************************************************************** + * Name: nvblk_unlink + * + * Description: Unlink the device + * + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int nvblk_unlink(FAR struct inode *inode) +{ + FAR nvblk_dev_t *dev; + + DEBUGASSERT(inode->i_private); + dev = inode->i_private; + nxmutex_lock(&dev->lock); + dev->unlinked = true; + nxmutex_unlock(&dev->lock); + + if (dev->refs == 0) + { + nxmutex_destroy(&dev->lock); + kmm_free(dev->pagebuf); + kmm_free(dev); + } + + return 0; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* nvblk cfg interface implementation */ + +int nvblk_cfg_read(FAR const struct nvb_config *cfg, + uint32_t p, void *buf) +{ + FAR nvblk_dev_t *dev = container_of(cfg, FAR nvblk_dev_t, cfg); + + if (MTD_BREAD(dev->mtd, p << dev->log2_ppb, 1 << dev->log2_ppb, buf) < 0) + { + return -NVB_EFAULT; + } + + return 0; +} + +int nvblk_cfg_prog(FAR const struct nvb_config *cfg, + uint32_t p, const void *buf) +{ + FAR nvblk_dev_t *dev = container_of(cfg, FAR nvblk_dev_t, cfg); + + if ((p % (1 << cfg->log2_bpeb)) == 0) + { + if (MTD_ERASE(dev->mtd, p >> cfg->log2_bpeb, 1U) < 0) + { + return -NVB_EFAULT; + } + } + + if (MTD_BWRITE(dev->mtd, p << dev->log2_ppb, 1 << dev->log2_ppb, buf) < 0) + { + return -NVB_EFAULT; + } + + return 0; +} + +/**************************************************************************** + * Name: nvblk_initialize_by_path + * + * Description: + * Initialize to provide a block driver wrapper around an MTD interface + * + * Input Parameters: + * path - The block device path. + * mtd - The MTD device that supports the FLASH interface. + * lbs - The logical blocksize (size of the nvblk blocks). + * iobs - The input output blocksize (multiple of lbs). + * speb - The number of spare erase blocks. + * + ****************************************************************************/ + +int nvblk_initialize(FAR const char *path, + FAR struct mtd_dev_s *mtd, + uint32_t lbs, + uint32_t iobs, + uint32_t speb) +{ + FAR nvblk_dev_t *dev; + int ret; + + /* Sanity check */ + + if (path == NULL || mtd == NULL || lbs < NVBLK_MIN_LBS || lbs > iobs || + iobs == 0U || (iobs & (iobs - 1U)) != 0U || + lbs == 0U || (lbs & (lbs - 1U)) != 0U) + { + return -EINVAL; + } + + finfo("path=\"%s\"\n", path); + + /* Allocate a NVBLK_MTDBLOCK device structure */ + + dev = (FAR nvblk_dev_t *)kmm_zalloc(sizeof(nvblk_dev_t)); + if (dev == NULL) + { + return -ENOMEM; + } + + nxmutex_init(&dev->lock); + + /* Initialize the NVBLK_MTDBLOCK device structure */ + + dev->mtd = mtd; + + /* Get the device geometry. (casting to uintptr_t first + * eliminates complaints on some architectures where the + * sizeof long is different + * from the size of a pointer). + */ + + ret = MTD_IOCTL(mtd, + MTDIOC_GEOMETRY, + (unsigned long)((uintptr_t)&dev->geo)); + if (ret < 0) + { + ferr("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret); + goto err; + } + + /* Validate the number of R/W blocks per erase block */ + + if ((dev->geo.erasesize % lbs) != 0 || (lbs % dev->geo.blocksize) != 0 || + (dev->geo.neraseblocks <= speb)) + { + ferr("NVBLK bad config\n"); + ret = -EINVAL; + goto err; + } + + /* Init nvblk */ + + dev->log2_bpiob = fls(iobs / lbs) - 1; + dev->log2_ppb = fls(lbs / dev->geo.blocksize) - 1; + dev->cfg.log2_bs = fls(lbs) - 1; + dev->cfg.log2_bpeb = fls(dev->geo.erasesize / lbs) - 1; + dev->cfg.eb = dev->geo.neraseblocks; + dev->cfg.sp_eb = speb; + + dev->pagebuf = kmm_zalloc(lbs * 2); + if (!dev->pagebuf) + { + ret = -ENOMEM; + goto err; + } + + dev->cfg.mb = &dev->pagebuf[0]; + dev->cfg.gb = &dev->pagebuf[lbs]; + dev->cfg.read = nvblk_cfg_read; + dev->cfg.prog = nvblk_cfg_prog; + + finfo("Initializing nvblk\n"); + ret = nvb_init(&dev->info, &dev->cfg); + if (ret < 0) + { + ferr("failed to initialize nvblk: %d\n", ret); + goto err; + } + + finfo("succeeded initializing nvblk\n"); + finfo("Physical Size: ebcnt [%d] bcnt [%d]", dev->cfg.eb, + dev->cfg.eb << dev->cfg.log2_bpeb); + finfo("Head at mblock [%d], Tail at mblock [%d]\n", dev->info.head, + dev->info.tail); + finfo("Root at mblock [%d]\n", dev->info.root); + finfo("cpe [%d], tail_cpe [%d]\n", dev->info.cpe, dev->info.tail_cpe); + finfo("used [%d], pass [%x]\n", dev->info.used, dev->info.pass); + + /* Inode private data is a reference to the + * NVBLK_MTDBLOCK device structure + */ + + ret = register_blockdriver(path, &g_nvblk_bops, 0666, dev); + if (ret < 0) + { + ferr("register_blockdriver failed: %d\n", ret); + goto err; + } + + return ret; + +err: + nxmutex_destroy(&dev->lock); + kmm_free(dev->pagebuf); + kmm_free(dev); + return ret; +} diff --git a/include/nuttx/mtd/mtd.h b/include/nuttx/mtd/mtd.h index a1c791dbf5..73a3b529f1 100644 --- a/include/nuttx/mtd/mtd.h +++ b/include/nuttx/mtd/mtd.h @@ -820,6 +820,29 @@ int dhara_initialize_by_path(FAR const char *path, FAR struct mtd_dev_s *mtd); #endif +/**************************************************************************** + * Name: nvblk_initialize + * + * Description: + * Initialize to provide a block driver wrapper around an MTD interface + * + * Input Parameters: + * path - The block device path. + * mtd - The MTD device that supports the FLASH interface. + * lbs - The logical blocksize (size of the nvblk blocks). + * iobs - The input output blocksize (multiple of lbs). + * speb - The number of spare erase blocks. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_NVBLK +int nvblk_initialize(FAR const char *path, + FAR struct mtd_dev_s *mtd, + uint32_t lbs, + uint32_t iobs, + uint32_t speb); +#endif + /**************************************************************************** * Name: register_cfi_driver *