mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2024-12-22 14:43:30 +00:00
145d896e0e
Tested on BananaPi R2 (SD, eMMC), BananaPi R64 (SD, eMMC, SPI-NAND) and UniElec U7623-02 (eMMC). Signed-off-by: Daniel Golle <daniel@makrotopia.org>
410 lines
10 KiB
Diff
410 lines
10 KiB
Diff
From a26620ec83fa3077f0c261046e82091f7455736f Mon Sep 17 00:00:00 2001
|
|
From: Weijie Gao <weijie.gao@mediatek.com>
|
|
Date: Wed, 3 Mar 2021 10:11:32 +0800
|
|
Subject: [PATCH 06/12] env: add support for generic MTD device
|
|
|
|
Add an env driver for generic MTD device.
|
|
|
|
Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
|
|
---
|
|
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/cmd/nvedit.c
|
|
+++ b/cmd/nvedit.c
|
|
@@ -48,6 +48,7 @@ DECLARE_GLOBAL_DATA_PTR;
|
|
defined(CONFIG_ENV_IS_IN_MMC) || \
|
|
defined(CONFIG_ENV_IS_IN_FAT) || \
|
|
defined(CONFIG_ENV_IS_IN_EXT4) || \
|
|
+ defined(CONFIG_ENV_IS_IN_MTD) || \
|
|
defined(CONFIG_ENV_IS_IN_NAND) || \
|
|
defined(CONFIG_ENV_IS_IN_NVRAM) || \
|
|
defined(CONFIG_ENV_IS_IN_ONENAND) || \
|
|
@@ -62,7 +63,7 @@ DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#if !defined(ENV_IS_IN_DEVICE) && \
|
|
!defined(CONFIG_ENV_IS_NOWHERE)
|
|
-# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|\
|
|
+# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|MTD|\
|
|
NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE
|
|
#endif
|
|
|
|
--- a/env/Kconfig
|
|
+++ b/env/Kconfig
|
|
@@ -19,7 +19,7 @@ config ENV_IS_NOWHERE
|
|
!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
|
|
help
|
|
Define this if you don't want to or can't have an environment stored
|
|
on a storage medium. In this case the environment will still exist
|
|
@@ -208,6 +208,27 @@ config ENV_IS_IN_MMC
|
|
This value is also in units of bytes, but must also be aligned to
|
|
an MMC sector boundary.
|
|
|
|
+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
|
|
@@ -535,10 +556,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 0x88000 if ARCH_SUNXI
|
|
@@ -583,6 +610,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
|
|
@@ -26,6 +26,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
|
|
@@ -69,6 +69,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 <weijie.gao@mediatek.com>
|
|
+ */
|
|
+
|
|
+#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
|
|
@@ -130,6 +130,7 @@ enum env_location {
|
|
ENVL_FAT,
|
|
ENVL_FLASH,
|
|
ENVL_MMC,
|
|
+ ENVL_MTD,
|
|
ENVL_NAND,
|
|
ENVL_NVRAM,
|
|
ENVL_ONENAND,
|
|
--- a/tools/Makefile
|
|
+++ b/tools/Makefile
|
|
@@ -41,6 +41,7 @@ ENVCRC-$(CONFIG_ENV_IS_EMBEDDED) = y
|
|
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
|