mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-08-08 00:06:59 +02:00
env: Introduce support for MTD
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@gmail.com>
This commit is contained in:
parent
f717d798be
commit
03fb08d4ae
33
env/Kconfig
vendored
33
env/Kconfig
vendored
@ -74,7 +74,7 @@ config ENV_IS_DEFAULT
|
|||||||
!ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \
|
!ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \
|
||||||
!ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \
|
!ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \
|
||||||
!ENV_IS_IN_REMOTE && !ENV_IS_IN_SPI_FLASH && \
|
!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
|
select ENV_IS_NOWHERE
|
||||||
|
|
||||||
config 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
|
during a "saveenv" operation. CONFIG_ENV_OFFSET_REDUND must be
|
||||||
aligned to an erase sector boundary.
|
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
|
config ENV_SECT_SIZE_AUTO
|
||||||
bool "Use automatically detected sector size"
|
bool "Use automatically detected sector size"
|
||||||
depends on ENV_IS_IN_SPI_FLASH
|
depends on ENV_IS_IN_SPI_FLASH
|
||||||
@ -562,8 +581,8 @@ config ENV_EXT4_FILE
|
|||||||
config ENV_ADDR
|
config ENV_ADDR
|
||||||
hex "Environment address"
|
hex "Environment address"
|
||||||
depends on ENV_IS_IN_FLASH || ENV_IS_IN_NVRAM || ENV_IS_IN_ONENAND || \
|
depends on ENV_IS_IN_FLASH || ENV_IS_IN_NVRAM || ENV_IS_IN_ONENAND || \
|
||||||
ENV_IS_IN_REMOTE || 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
|
default 0x0 if ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD
|
||||||
help
|
help
|
||||||
Offset from the start of the device (or partition)
|
Offset from the start of the device (or partition)
|
||||||
|
|
||||||
@ -577,7 +596,7 @@ config ENV_ADDR_REDUND
|
|||||||
config ENV_OFFSET
|
config ENV_OFFSET
|
||||||
hex "Environment offset"
|
hex "Environment offset"
|
||||||
depends on ENV_IS_IN_EEPROM || ENV_IS_IN_MMC || ENV_IS_IN_NAND || \
|
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 0x3f8000 if ARCH_ROCKCHIP && ENV_IS_IN_MMC
|
||||||
default 0x140000 if ARCH_ROCKCHIP && ENV_IS_IN_SPI_FLASH
|
default 0x140000 if ARCH_ROCKCHIP && ENV_IS_IN_SPI_FLASH
|
||||||
default 0xF0000 if ARCH_SUNXI
|
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
|
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.
|
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
|
config SYS_MMC_ENV_DEV
|
||||||
int "mmc device number"
|
int "mmc device number"
|
||||||
depends on ENV_IS_IN_MMC || ENV_IS_IN_FAT || ENV_IS_IN_EXT4 || \
|
depends on ENV_IS_IN_MMC || ENV_IS_IN_FAT || ENV_IS_IN_EXT4 || \
|
||||||
|
1
env/Makefile
vendored
1
env/Makefile
vendored
@ -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_EXT4) += ext4.o
|
||||||
obj-$(CONFIG_$(PHASE_)ENV_IS_IN_NAND) += nand.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_SPI_FLASH) += sf.o
|
||||||
|
obj-$(CONFIG_$(PHASE_)ENV_IS_IN_MTD) += mtd.o
|
||||||
obj-$(CONFIG_$(PHASE_)ENV_IS_IN_FLASH) += flash.o
|
obj-$(CONFIG_$(PHASE_)ENV_IS_IN_FLASH) += flash.o
|
||||||
|
|
||||||
CFLAGS_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null)
|
CFLAGS_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null)
|
||||||
|
3
env/env.c
vendored
3
env/env.c
vendored
@ -58,6 +58,9 @@ static enum env_location env_locations[] = {
|
|||||||
#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
|
#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
|
||||||
ENVL_SPI_FLASH,
|
ENVL_SPI_FLASH,
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_ENV_IS_IN_MTD
|
||||||
|
ENVL_MTD,
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_ENV_IS_IN_UBI
|
#ifdef CONFIG_ENV_IS_IN_UBI
|
||||||
ENVL_UBI,
|
ENVL_UBI,
|
||||||
#endif
|
#endif
|
||||||
|
338
env/mtd.c
vendored
Normal file
338
env/mtd.c
vendored
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Author: Christian Marangi <ansuelsmth@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,
|
||||||
|
};
|
@ -113,6 +113,7 @@ enum env_location {
|
|||||||
ENVL_ONENAND,
|
ENVL_ONENAND,
|
||||||
ENVL_REMOTE,
|
ENVL_REMOTE,
|
||||||
ENVL_SPI_FLASH,
|
ENVL_SPI_FLASH,
|
||||||
|
ENVL_MTD,
|
||||||
ENVL_UBI,
|
ENVL_UBI,
|
||||||
ENVL_NOWHERE,
|
ENVL_NOWHERE,
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user