[PATCH] env: Introduce support for MTD
Christian Marangi (Ansuel)
ansuelsmth at gmail.com
Wed Apr 30 09:45:27 CEST 2025
Il giorno lun 7 apr 2025 alle ore 21:00 Christian Marangi
<ansuelsmth at gmail.com> ha scritto:
>
> Introduce support for env in generic MTD. Currently we only support SPI
> flash based on the lagacy sf cmd that assume SPI flash are always NOR.
> This is not the case as to SPI controller also NAND can be attached.
>
> To support also these flash scenario, add support for storing and
> reading ENV from generic MTD device by adding an env driver that
> base entirely on the MTD api.
>
> Introduce a new kconfig CONFIG_ENV_IS_IN_MTD and
> CONFIG_ENV_MTD_DEV to define the name of the MTD device as exposed
> by mtd list.
>
> Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
> ---
> env/Kconfig | 33 +++-
> env/Makefile | 1 +
> env/env.c | 3 +
> env/mtd.c | 338 +++++++++++++++++++++++++++++++++++++++++
> include/env_internal.h | 1 +
> 5 files changed, 372 insertions(+), 4 deletions(-)
> create mode 100644 env/mtd.c
>
> diff --git a/env/Kconfig b/env/Kconfig
> index 4438f0b392c..74678c43334 100644
> --- a/env/Kconfig
> +++ b/env/Kconfig
> @@ -74,7 +74,7 @@ config ENV_IS_DEFAULT
> !ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \
> !ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \
> !ENV_IS_IN_REMOTE && !ENV_IS_IN_SPI_FLASH && \
> - !ENV_IS_IN_UBI
> + !ENV_IS_IN_UBI && !ENV_IS_IN_MTD
> select ENV_IS_NOWHERE
>
> config ENV_IS_NOWHERE
> @@ -387,6 +387,25 @@ config ENV_IS_IN_SPI_FLASH
> during a "saveenv" operation. CONFIG_ENV_OFFSET_REDUND must be
> aligned to an erase sector boundary.
>
> +config ENV_IS_IN_MTD
> + bool "Environment is in MTD flash"
> + depends on !CHAIN_OF_TRUST && (SPI_FLASH || DM_SPI_FLASH)
> + default y if ARCH_AIROHA
> + help
> + Define this if you have a MTD Flash memory device which you
> + want to use for the environment.
> +
> + - CONFIG_ENV_MTD_DEV:
> +
> + Specifies which SPI NAND device the environment is stored in.
> +
> + - CONFIG_ENV_OFFSET:
> + - CONFIG_ENV_SIZE:
> +
> + These two #defines specify the offset and size of the
> + environment area within the MTD Flash.
> + CONFIG_ENV_OFFSET must be aligned to an erase sector boundary.
> +
> config ENV_SECT_SIZE_AUTO
> bool "Use automatically detected sector size"
> depends on ENV_IS_IN_SPI_FLASH
> @@ -562,8 +581,8 @@ config ENV_EXT4_FILE
> config ENV_ADDR
> hex "Environment address"
> depends on ENV_IS_IN_FLASH || ENV_IS_IN_NVRAM || ENV_IS_IN_ONENAND || \
> - ENV_IS_IN_REMOTE || ENV_IS_IN_SPI_FLASH
> - default 0x0 if ENV_IS_IN_SPI_FLASH
> + ENV_IS_IN_REMOTE || ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD
> + default 0x0 if ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD
> help
> Offset from the start of the device (or partition)
>
> @@ -577,7 +596,7 @@ config ENV_ADDR_REDUND
> config ENV_OFFSET
> hex "Environment offset"
> depends on ENV_IS_IN_EEPROM || ENV_IS_IN_MMC || ENV_IS_IN_NAND || \
> - ENV_IS_IN_SPI_FLASH
> + ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD
> default 0x3f8000 if ARCH_ROCKCHIP && ENV_IS_IN_MMC
> default 0x140000 if ARCH_ROCKCHIP && ENV_IS_IN_SPI_FLASH
> default 0xF0000 if ARCH_SUNXI
> @@ -666,6 +685,12 @@ config SYS_RELOC_GD_ENV_ADDR
> Relocate the early env_addr pointer so we know it is not inside
> the binary. Some systems need this and for the rest, it doesn't hurt.
>
> +config ENV_MTD_DEV
> + string "mtd device name"
> + depends on ENV_IS_IN_MTD
> + help
> + MTD device name on the platform where the environment is stored.
> +
> config SYS_MMC_ENV_DEV
> int "mmc device number"
> depends on ENV_IS_IN_MMC || ENV_IS_IN_FAT || ENV_IS_IN_EXT4 || \
> diff --git a/env/Makefile b/env/Makefile
> index a54e924d419..3b9c71d5681 100644
> --- a/env/Makefile
> +++ b/env/Makefile
> @@ -26,6 +26,7 @@ obj-$(CONFIG_$(PHASE_)ENV_IS_IN_FAT) += fat.o
> obj-$(CONFIG_$(PHASE_)ENV_IS_IN_EXT4) += ext4.o
> obj-$(CONFIG_$(PHASE_)ENV_IS_IN_NAND) += nand.o
> obj-$(CONFIG_$(PHASE_)ENV_IS_IN_SPI_FLASH) += sf.o
> +obj-$(CONFIG_$(PHASE_)ENV_IS_IN_MTD) += mtd.o
> obj-$(CONFIG_$(PHASE_)ENV_IS_IN_FLASH) += flash.o
>
> CFLAGS_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null)
> diff --git a/env/env.c b/env/env.c
> index bcc189e14db..dbaeedc3c3b 100644
> --- a/env/env.c
> +++ b/env/env.c
> @@ -58,6 +58,9 @@ static enum env_location env_locations[] = {
> #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
> ENVL_SPI_FLASH,
> #endif
> +#ifdef CONFIG_ENV_IS_IN_MTD
> + ENVL_MTD,
> +#endif
> #ifdef CONFIG_ENV_IS_IN_UBI
> ENVL_UBI,
> #endif
> diff --git a/env/mtd.c b/env/mtd.c
> new file mode 100644
> index 00000000000..721faebd8f2
> --- /dev/null
> +++ b/env/mtd.c
> @@ -0,0 +1,338 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Author: Christian Marangi <ansuelsmth at gmail.com>
> + */
> +#include <env_internal.h>
> +#include <errno.h>
> +#include <malloc.h>
> +#include <mtd.h>
> +#include <asm/cache.h>
> +#include <asm/global_data.h>
> +#include <linux/mtd/mtd.h>
> +#include <u-boot/crc.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +static int setup_mtd_device(struct mtd_info **mtd_env)
> +{
> + struct mtd_info *mtd;
> +
> + mtd_probe_devices();
> +
> + mtd = get_mtd_device_nm(CONFIG_ENV_MTD_DEV);
> + if (IS_ERR_OR_NULL(mtd)) {
> + env_set_default("get_mtd_device_nm() failed", 0);
> + return mtd ? PTR_ERR(mtd) : -EINVAL;
> + }
> +
> + *mtd_env = mtd;
> +
> + return 0;
> +}
> +
> +static int env_mtd_save(void)
> +{
> + char *saved_buf, *write_buf, *tmp;
> + struct erase_info ei = { };
> + struct mtd_info *mtd_env;
> + u32 sect_size, sect_num;
> + size_t ret_len = 0;
> + u32 write_size;
> + env_t env_new;
> + int remaining;
> + u32 offset;
> + int ret;
> +
> + ret = setup_mtd_device(&mtd_env);
> + if (ret)
> + return ret;
> +
> + sect_size = mtd_env->erasesize;
> +
> + /* Is the sector larger than the env (i.e. embedded) */
> + if (sect_size > CONFIG_ENV_SIZE) {
> + saved_buf = malloc(sect_size);
> + if (!saved_buf) {
> + ret = -ENOMEM;
> + goto done;
> + }
> +
> + offset = CONFIG_ENV_OFFSET;
> + remaining = sect_size;
> + tmp = saved_buf;
> +
> + while (remaining) {
> + /* Skip the block if it is bad */
> + if (!(offset % sect_size) &&
> + mtd_block_isbad(mtd_env, offset)) {
> + offset += sect_size;
> + continue;
> + }
> +
> + ret = mtd_read(mtd_env, offset, mtd_env->writesize,
> + &ret_len, tmp);
> + if (ret)
> + goto done;
> +
> + tmp += ret_len;
> + offset += ret_len;
> + remaining -= ret_len;
> + }
> + }
> +
> + ret = env_export(&env_new);
> + if (ret)
> + goto done;
> +
> + sect_num = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
> +
> + ei.mtd = mtd_env;
> + ei.addr = CONFIG_ENV_OFFSET;
> + ei.len = sect_num * sect_size;
> +
> + puts("Erasing MTD...");
> + ret = mtd_erase(mtd_env, &ei);
> + if (ret)
> + goto done;
> +
> + if (sect_size > CONFIG_ENV_SIZE) {
> + memcpy(saved_buf, &env_new, CONFIG_ENV_SIZE);
> + write_size = sect_size;
> + write_buf = saved_buf;
> + } else {
> + write_size = sect_num * sect_size;
> + write_buf = (char *)&env_new;
> + }
> +
> + offset = CONFIG_ENV_OFFSET;
> + remaining = sect_size;
> + tmp = write_buf;
> +
> + puts("Writing to MTD...");
> + while (remaining) {
> + /* Skip the block if it is bad */
> + if (!(offset % sect_size) &&
> + mtd_block_isbad(mtd_env, offset)) {
> + offset += sect_size;
> + continue;
> + }
> +
> + ret = mtd_write(mtd_env, offset, mtd_env->writesize,
> + &ret_len, tmp);
> + if (ret)
> + goto done;
> +
> + offset += mtd_env->writesize;
> + remaining -= ret_len;
> + tmp += ret_len;
> + }
> +
> + ret = 0;
> + puts("done\n");
> +
> +done:
> + if (saved_buf)
> + free(saved_buf);
> +
> + return ret;
> +}
> +
> +static int env_mtd_load(void)
> +{
> + struct mtd_info *mtd_env;
> + char *buf, *tmp;
> + size_t ret_len;
> + int remaining;
> + u32 sect_size;
> + u32 offset;
> + int ret;
> +
> + buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
> + if (!buf) {
> + env_set_default("memalign() failed", 0);
> + return -EIO;
> + }
> +
> + ret = setup_mtd_device(&mtd_env);
> + if (ret)
> + goto out;
> +
> + sect_size = mtd_env->erasesize;
> +
> + offset = CONFIG_ENV_OFFSET;
> + remaining = CONFIG_ENV_SIZE;
> + tmp = buf;
> +
> + while (remaining) {
> + /* Skip the block if it is bad */
> + if (!(offset % sect_size) &&
> + mtd_block_isbad(mtd_env, offset)) {
> + offset += sect_size;
> + continue;
> + }
> +
> + ret = mtd_read(mtd_env, offset, mtd_env->writesize,
> + &ret_len, tmp);
> + if (ret) {
> + env_set_default("mtd_read() failed", 1);
> + goto out;
> + }
> +
> + tmp += ret_len;
> + offset += ret_len;
> + remaining -= ret_len;
> + }
> +
> + ret = env_import(buf, 1, H_EXTERNAL);
> + if (!ret)
> + gd->env_valid = ENV_VALID;
> +
> +out:
> + free(buf);
> +
> + return ret;
> +}
> +
> +static int env_mtd_erase(void)
> +{
> + struct mtd_info *mtd_env;
> + u32 sect_size, sect_num;
> + char *saved_buf, *tmp;
> + struct erase_info ei;
> + size_t ret_len;
> + int remaining;
> + u32 offset;
> + int ret;
> +
> + ret = setup_mtd_device(&mtd_env);
> + if (ret)
> + return ret;
> +
> + sect_size = mtd_env->erasesize;
> +
> + /* Is the sector larger than the env (i.e. embedded) */
> + if (sect_size > CONFIG_ENV_SIZE) {
> + saved_buf = malloc(sect_size);
> + if (!saved_buf) {
> + ret = -ENOMEM;
> + goto done;
> + }
> +
> + offset = CONFIG_ENV_OFFSET;
> + remaining = sect_size;
> + tmp = saved_buf;
> +
> + while (remaining) {
> + /* Skip the block if it is bad */
> + if (!(offset % sect_size) &&
> + mtd_block_isbad(mtd_env, offset)) {
> + offset += sect_size;
> + continue;
> + }
> +
> + ret = mtd_read(mtd_env, offset, mtd_env->writesize,
> + &ret_len, tmp);
> + if (ret)
> + goto done;
> +
> + tmp += ret_len;
> + offset += ret_len;
> + remaining -= ret_len;
> + }
> + }
> +
> + sect_num = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
> +
> + ei.mtd = mtd_env;
> + ei.addr = CONFIG_ENV_OFFSET;
> + ei.len = sect_num * sect_size;
> +
> + ret = mtd_erase(mtd_env, &ei);
> + if (ret)
> + goto done;
> +
> + if (sect_size > CONFIG_ENV_SIZE) {
> + memset(saved_buf, 0, CONFIG_ENV_SIZE);
> +
> + offset = CONFIG_ENV_OFFSET;
> + remaining = sect_size;
> + tmp = saved_buf;
> +
> + while (remaining) {
> + /* Skip the block if it is bad */
> + if (!(offset % sect_size) &&
> + mtd_block_isbad(mtd_env, offset)) {
> + offset += sect_size;
> + continue;
> + }
> +
> + ret = mtd_write(mtd_env, offset, mtd_env->writesize,
> + &ret_len, tmp);
> + if (ret)
> + goto done;
> +
> + offset += mtd_env->writesize;
> + remaining -= ret_len;
> + tmp += ret_len;
> + }
> + }
> +
> + ret = 0;
> +
> +done:
> + if (saved_buf)
> + free(saved_buf);
> +
> + return ret;
> +}
> +
> +__weak void *env_mtd_get_env_addr(void)
> +{
> + return (void *)CONFIG_ENV_ADDR;
> +}
> +
> +/*
> + * Check if Environment on CONFIG_ENV_ADDR is valid.
> + */
> +static int env_mtd_init_addr(void)
> +{
> + env_t *env_ptr = (env_t *)env_mtd_get_env_addr();
> +
> + if (!env_ptr)
> + return -ENOENT;
> +
> + if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
> + gd->env_addr = (ulong)&env_ptr->data;
> + gd->env_valid = ENV_VALID;
> + } else {
> + gd->env_valid = ENV_INVALID;
> + }
> +
> + return 0;
> +}
> +
> +static int env_mtd_init(void)
> +{
> + int ret;
> +
> + ret = env_mtd_init_addr();
> + if (ret != -ENOENT)
> + return ret;
> +
> + /*
> + * return here -ENOENT, so env_init()
> + * can set the init bit and later if no
> + * other Environment storage is defined
> + * can set the default environment
> + */
> + return -ENOENT;
> +}
> +
> +U_BOOT_ENV_LOCATION(mtd) = {
> + .location = ENVL_MTD,
> + ENV_NAME("MTD")
> + .load = env_mtd_load,
> + .save = ENV_SAVE_PTR(env_mtd_save),
> + .erase = ENV_ERASE_PTR(env_mtd_erase),
> + .init = env_mtd_init,
> +};
> diff --git a/include/env_internal.h b/include/env_internal.h
> index c1c0727e4d0..ee939ba4293 100644
> --- a/include/env_internal.h
> +++ b/include/env_internal.h
> @@ -113,6 +113,7 @@ enum env_location {
> ENVL_ONENAND,
> ENVL_REMOTE,
> ENVL_SPI_FLASH,
> + ENVL_MTD,
> ENVL_UBI,
> ENVL_NOWHERE,
>
> --
> 2.48.1
>
Any news for this?
More information about the U-Boot
mailing list