123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- From efc3e6f5d29f87a433b42f15a0b87e04b7cd498d Mon Sep 17 00:00:00 2001
- From: Weijie Gao <[email protected]>
- Date: Wed, 3 Mar 2021 10:11:32 +0800
- Subject: [PATCH 38/71] env: add support for generic MTD device
- Add an env driver for generic MTD device.
- Signed-off-by: Weijie Gao <[email protected]>
- ---
- cmd/nvedit.c | 3 +-
- env/Kconfig | 37 +++++-
- env/Makefile | 1 +
- env/env.c | 3 +
- env/mtd.c | 256 +++++++++++++++++++++++++++++++++++++++++
- include/env_internal.h | 1 +
- tools/Makefile | 1 +
- 7 files changed, 299 insertions(+), 3 deletions(-)
- create mode 100644 env/mtd.c
- --- a/env/Kconfig
- +++ b/env/Kconfig
- @@ -61,7 +61,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
- @@ -254,6 +254,27 @@ config ENV_IS_IN_MMC
- offset: "u-boot,mmc-env-offset", "u-boot,mmc-env-offset-redundant".
- CONFIG_ENV_OFFSET and CONFIG_ENV_OFFSET_REDUND are not used.
-
- +config ENV_IS_IN_MTD
- + bool "Environment in a MTD device"
- + depends on !CHAIN_OF_TRUST
- + depends on MTD
- + help
- + Define this if you have a MTD device which you want to use for
- + the environment.
- +
- + - CONFIG_ENV_MTD_NAME:
- + - CONFIG_ENV_OFFSET:
- + - CONFIG_ENV_SIZE:
- +
- + These three #defines specify the MTD device where the environment
- + is stored, offset and size of the environment area within the MTD
- + device. CONFIG_ENV_OFFSET must be aligned to an erase block boundary.
- +
- + - CONFIG_ENV_SIZE_REDUND:
- +
- + This #define specify the maximum size allowed for read/write/erase
- + with skipped bad blocks starting from ENV_OFFSET.
- +
- config ENV_IS_IN_NAND
- bool "Environment in a NAND device"
- depends on !CHAIN_OF_TRUST
- @@ -561,10 +582,16 @@ config ENV_ADDR_REDUND
- Offset from the start of the device (or partition) of the redundant
- environment location.
-
- +config ENV_MTD_NAME
- + string "Name of the MTD device storing the environment"
- + depends on ENV_IS_IN_MTD
- + help
- + Name of the MTD device that stores the environment
- +
- 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
- @@ -622,6 +649,12 @@ config ENV_SECT_SIZE
- help
- Size of the sector containing the environment.
-
- +config ENV_SIZE_REDUND
- + hex "Redundant environment size"
- + depends on ENV_IS_IN_MTD
- + help
- + The maximum size allowed for read/write/erase with skipped bad blocks.
- +
- config ENV_UBI_PART
- string "UBI partition name"
- depends on ENV_IS_IN_UBI
- --- a/env/Makefile
- +++ b/env/Makefile
- @@ -24,6 +24,7 @@ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_NOWHERE)
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MMC) += mmc.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) += fat.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_EXT4) += ext4.o
- +obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MTD) += mtd.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NAND) += nand.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_SPI_FLASH) += sf.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FLASH) += flash.o
- --- a/env/env.c
- +++ b/env/env.c
- @@ -46,6 +46,9 @@ static enum env_location env_locations[]
- #ifdef CONFIG_ENV_IS_IN_MMC
- ENVL_MMC,
- #endif
- +#ifdef CONFIG_ENV_IS_IN_MTD
- + ENVL_MTD,
- +#endif
- #ifdef CONFIG_ENV_IS_IN_NAND
- ENVL_NAND,
- #endif
- --- /dev/null
- +++ b/env/mtd.c
- @@ -0,0 +1,256 @@
- +/* SPDX-License-Identifier: GPL-2.0 */
- +/*
- + * Copyright (C) 2021 MediaTek Inc. All Rights Reserved.
- + *
- + * Author: Weijie Gao <[email protected]>
- + */
- +
- +#include <command.h>
- +#include <env.h>
- +#include <env_internal.h>
- +#include <errno.h>
- +#include <linux/kernel.h>
- +#include <linux/stddef.h>
- +#include <linux/types.h>
- +#include <linux/mtd/mtd.h>
- +#include <malloc.h>
- +#include <memalign.h>
- +#include <mtd.h>
- +#include <search.h>
- +
- +#if CONFIG_ENV_SIZE_REDUND < CONFIG_ENV_SIZE
- +#undef CONFIG_ENV_SIZE_REDUND
- +#define CONFIG_ENV_SIZE_REDUND CONFIG_ENV_SIZE
- +#endif
- +
- +#if defined(ENV_IS_EMBEDDED)
- +env_t *env_ptr = &environment;
- +#else /* ! ENV_IS_EMBEDDED */
- +env_t *env_ptr;
- +#endif /* ENV_IS_EMBEDDED */
- +
- +DECLARE_GLOBAL_DATA_PTR;
- +
- +static int env_mtd_init(void)
- +{
- +#if defined(ENV_IS_EMBEDDED)
- + int crc1_ok = 0, crc2_ok = 0;
- + env_t *tmp_env1;
- +
- + tmp_env1 = env_ptr;
- + crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
- +
- + if (!crc1_ok && !crc2_ok) {
- + gd->env_addr = 0;
- + gd->env_valid = ENV_INVALID;
- +
- + return 0;
- + } else if (crc1_ok && !crc2_ok) {
- + gd->env_valid = ENV_VALID;
- + }
- +
- + if (gd->env_valid == ENV_VALID)
- + env_ptr = tmp_env1;
- +
- + gd->env_addr = (ulong)env_ptr->data;
- +
- +#else /* ENV_IS_EMBEDDED */
- + gd->env_addr = (ulong)&default_environment[0];
- + gd->env_valid = ENV_VALID;
- +#endif /* ENV_IS_EMBEDDED */
- +
- + return 0;
- +}
- +
- +static struct mtd_info *env_mtd_get_dev(void)
- +{
- + struct mtd_info *mtd;
- +
- + mtd_probe_devices();
- +
- + mtd = get_mtd_device_nm(CONFIG_ENV_MTD_NAME);
- + if (IS_ERR(mtd) || !mtd) {
- + printf("MTD device '%s' not found\n", CONFIG_ENV_MTD_NAME);
- + return NULL;
- + }
- +
- + return mtd;
- +}
- +
- +static inline bool mtd_addr_is_block_aligned(struct mtd_info *mtd, u64 addr)
- +{
- + return (addr & mtd->erasesize_mask) == 0;
- +}
- +
- +static int mtd_io_skip_bad(struct mtd_info *mtd, bool read, loff_t offset,
- + size_t length, size_t redund, u8 *buffer)
- +{
- + struct mtd_oob_ops io_op = {};
- + size_t remaining = length;
- + loff_t off, end;
- + int ret;
- +
- + io_op.mode = MTD_OPS_PLACE_OOB;
- + io_op.len = mtd->writesize;
- + io_op.datbuf = (void *)buffer;
- +
- + /* Search for the first good block after the given offset */
- + off = offset;
- + end = (off + redund) | (mtd->erasesize - 1);
- + while (mtd_block_isbad(mtd, off) && off < end)
- + off += mtd->erasesize;
- +
- + /* Reached end position */
- + if (off >= end)
- + return -EIO;
- +
- + /* Loop over the pages to do the actual read/write */
- + while (remaining) {
- + /* Skip the block if it is bad */
- + if (mtd_addr_is_block_aligned(mtd, off) &&
- + mtd_block_isbad(mtd, off)) {
- + off += mtd->erasesize;
- + continue;
- + }
- +
- + if (read)
- + ret = mtd_read_oob(mtd, off, &io_op);
- + else
- + ret = mtd_write_oob(mtd, off, &io_op);
- +
- + if (ret) {
- + printf("Failure while %s at offset 0x%llx\n",
- + read ? "reading" : "writing", off);
- + break;
- + }
- +
- + off += io_op.retlen;
- + remaining -= io_op.retlen;
- + io_op.datbuf += io_op.retlen;
- + io_op.oobbuf += io_op.oobretlen;
- +
- + /* Reached end position */
- + if (off >= end)
- + return -EIO;
- + }
- +
- + return 0;
- +}
- +
- +#ifdef CONFIG_CMD_SAVEENV
- +static int mtd_erase_skip_bad(struct mtd_info *mtd, loff_t offset,
- + size_t length, size_t redund)
- +{
- + struct erase_info erase_op = {};
- + loff_t end = (offset + redund) | (mtd->erasesize - 1);
- + int ret;
- +
- + erase_op.mtd = mtd;
- + erase_op.addr = offset;
- + erase_op.len = length;
- +
- + while (erase_op.len) {
- + ret = mtd_erase(mtd, &erase_op);
- +
- + /* Abort if its not a bad block error */
- + if (ret != -EIO)
- + return ret;
- +
- + printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr);
- +
- + /* Skip bad block and continue behind it */
- + erase_op.len -= erase_op.fail_addr - erase_op.addr;
- + erase_op.len -= mtd->erasesize;
- + erase_op.addr = erase_op.fail_addr + mtd->erasesize;
- +
- + /* Reached end position */
- + if (erase_op.addr >= end)
- + return -EIO;
- + }
- +
- + return 0;
- +}
- +
- +static int env_mtd_save(void)
- +{
- + ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
- + struct mtd_info *mtd;
- + int ret = 0;
- +
- + ret = env_export(env_new);
- + if (ret)
- + return ret;
- +
- + mtd = env_mtd_get_dev();
- + if (!mtd)
- + return 1;
- +
- + printf("Erasing on MTD device '%s'... ", mtd->name);
- +
- + ret = mtd_erase_skip_bad(mtd, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
- + CONFIG_ENV_SIZE_REDUND);
- +
- + puts(ret ? "FAILED\n" : "OK\n");
- +
- + if (ret) {
- + put_mtd_device(mtd);
- + return 1;
- + }
- +
- + printf("Writing to MTD device '%s'... ", mtd->name);
- +
- + ret = mtd_io_skip_bad(mtd, false, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
- + CONFIG_ENV_SIZE_REDUND, (u8 *)env_new);
- +
- + puts(ret ? "FAILED\n" : "OK\n");
- +
- + put_mtd_device(mtd);
- +
- + return !!ret;
- +}
- +#endif /* CONFIG_CMD_SAVEENV */
- +
- +static int readenv(size_t offset, u_char *buf)
- +{
- + struct mtd_info *mtd;
- + int ret;
- +
- + mtd = env_mtd_get_dev();
- + if (!mtd)
- + return 1;
- +
- + ret = mtd_io_skip_bad(mtd, true, offset, CONFIG_ENV_SIZE,
- + CONFIG_ENV_SIZE_REDUND, buf);
- +
- + put_mtd_device(mtd);
- +
- + return !!ret;
- +}
- +
- +static int env_mtd_load(void)
- +{
- +#if !defined(ENV_IS_EMBEDDED)
- + ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
- + int ret;
- +
- + ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);
- + if (ret) {
- + env_set_default("readenv() failed", 0);
- + return -EIO;
- + }
- +
- + return env_import(buf, 1, H_EXTERNAL);
- +#endif /* ! ENV_IS_EMBEDDED */
- +
- + return 0;
- +}
- +
- +U_BOOT_ENV_LOCATION(mtd) = {
- + .location = ENVL_MTD,
- + ENV_NAME("MTD")
- + .load = env_mtd_load,
- +#if defined(CONFIG_CMD_SAVEENV)
- + .save = env_save_ptr(env_mtd_save),
- +#endif
- + .init = env_mtd_init,
- +};
- --- a/include/env_internal.h
- +++ b/include/env_internal.h
- @@ -109,6 +109,7 @@ enum env_location {
- ENVL_FAT,
- ENVL_FLASH,
- ENVL_MMC,
- + ENVL_MTD,
- ENVL_NAND,
- ENVL_NVRAM,
- ENVL_ONENAND,
- --- a/tools/Makefile
- +++ b/tools/Makefile
- @@ -37,6 +37,7 @@ subdir-$(HOST_TOOLS_ALL) += gdb
- ENVCRC-$(CONFIG_ENV_IS_IN_EEPROM) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_FLASH) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_ONENAND) = y
- +ENVCRC-$(CONFIG_ENV_IS_IN_MTD) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_NAND) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_NVRAM) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_SPI_FLASH) = y
|