linuxOS_D21X/source/uboot-2021.10/env/spinand.c
2024-11-29 16:23:11 +08:00

297 lines
6.1 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2020 ArtInChip Technology Co.,Ltd.
* Dehuang Wu <dehuang.wu@artinchip.com>
*/
#include <common.h>
#include <generated/autoconf.h>
#include <command.h>
#include <dm.h>
#include <dm/uclass.h>
#include <dm/device-internal.h>
#include <env_internal.h>
#include <linux/stddef.h>
#include <malloc.h>
#include <memalign.h>
#include <search.h>
#include <errno.h>
#include <mtd.h>
#include <env.h>
#ifdef CONFIG_AUTO_CALCULATE_PART_CONFIG
#include <generated/image_cfg_part_config.h>
#endif
#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
#define CMD_SAVEENV
#endif
#ifndef CONFIG_ENV_RANGE
#define CONFIG_ENV_RANGE CONFIG_ENV_SIZE
#endif
DECLARE_GLOBAL_DATA_PTR;
static int readenv(struct mtd_info *mtd, size_t offset, u_char *buf)
{
size_t end = offset + CONFIG_ENV_RANGE;
size_t rdlen, remain;
u_char *char_ptr;
remain = min(mtd->erasesize, (uint32_t)CONFIG_ENV_SIZE);
char_ptr = buf;
while ((remain > 0) && (offset < end)) {
if (mtd_block_isbad(mtd, offset)) {
offset += mtd->erasesize;
continue;
}
if (mtd_read(mtd, offset, remain, &rdlen, char_ptr))
return 1;
offset += rdlen;
remain -= rdlen;
char_ptr += rdlen;
}
return 0;
}
#if defined(CMD_SAVEENV)
static int writeenv(struct mtd_info *mtd, size_t offset, u_char *buf)
{
size_t end = offset + CONFIG_ENV_RANGE;
size_t wrlen, remain;
u_char *char_ptr;
remain = min(mtd->erasesize, (uint32_t)CONFIG_ENV_SIZE);
char_ptr = buf;
while ((remain > 0) && (offset < end)) {
if (mtd_block_isbad(mtd, offset)) {
offset += mtd->erasesize;
continue;
}
if (mtd_write(mtd, offset, remain, &wrlen, char_ptr))
return 1;
offset += wrlen;
remain -= wrlen;
char_ptr += wrlen;
}
return 0;
}
#endif
#if defined(CONFIG_ENV_OFFSET_REDUND)
static int env_spinand_load(void)
{
struct mtd_info *mtd;
struct udevice *dev;
int ret = -1;
int read1_fail = 0, read2_fail = 0;
ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
ALLOC_CACHE_ALIGN_BUFFER(char, buf_redund, CONFIG_ENV_SIZE);
ret = uclass_first_device(UCLASS_MTD, &dev);
if (ret && !dev) {
pr_err("Find MTD device failed.\n");
return -ENODEV;
}
device_probe(dev);
mtd = get_mtd_device(NULL, 0);
if (!mtd) {
pr_err("Get SPI NAND mtd device failed.\n");
return -ENODEV;
}
read1_fail = readenv(mtd, CONFIG_ENV_OFFSET, (u_char *)buf);
if (read1_fail)
pr_info("\n** Unable to read env from %d **\n",
CONFIG_ENV_OFFSET);
read2_fail = readenv(mtd, CONFIG_ENV_OFFSET_REDUND,
(u_char *)buf_redund);
if (read2_fail)
pr_info("\n** Unable to read redundant env from %d **\n",
CONFIG_ENV_OFFSET_REDUND);
ret = env_import_redund((char *)buf, read1_fail, (char *)buf_redund,
read2_fail, H_EXTERNAL);
return ret;
}
#if defined(CMD_SAVEENV)
static int env_spinand_save(void)
{
struct mtd_info *mtd;
struct udevice *dev;
env_t env_new;
int ret = -1;
struct erase_info instr = {
.callback = NULL,
};
ret = uclass_first_device(UCLASS_MTD, &dev);
if (ret && !dev) {
pr_err("Find MTD device failed.\n");
return -ENODEV;
}
device_probe(dev);
mtd = get_mtd_device(NULL, 0);
if (!mtd) {
pr_err("Get SPI NAND mtd device failed.\n");
return -ENODEV;
}
ret = env_export(&env_new);
if (ret)
return ret;
if (gd->env_valid == ENV_VALID) {
pr_info("Writing to env_redundant mtd... ");
instr.len = CONFIG_ENV_SIZE;
instr.addr = CONFIG_ENV_OFFSET_REDUND;
instr.mtd = mtd;
pr_info("Erasing ...\n");
if (mtd_erase(mtd, &instr)) {
pr_err("SPINAND: erase failed at 0x%08llx\n",
instr.addr);
return 1;
}
pr_info("Writing ...\n");
ret = writeenv(mtd, CONFIG_ENV_OFFSET_REDUND,
(u_char *)&env_new);
if (ret) {
pr_err("SPINAND: save env failed.\n");
return 2;
}
} else {
puts("Writing to env mtd... ");
instr.len = CONFIG_ENV_SIZE;
instr.addr = CONFIG_ENV_OFFSET;
instr.mtd = mtd;
pr_info("Erasing ...\n");
if (mtd_erase(mtd, &instr)) {
pr_err("SPINAND: erase failed at 0x%08llx\n",
instr.addr);
return 1;
}
pr_info("Writing ...\n");
ret = writeenv(mtd, CONFIG_ENV_OFFSET, (u_char *)&env_new);
if (ret) {
pr_err("SPINAND: save env failed.\n");
return 2;
}
}
pr_info("Done ...\n");
gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
return 0;
}
#endif
#else
static int env_spinand_load(void)
{
struct mtd_info *mtd;
struct udevice *dev;
int ret = -1;
ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
ret = uclass_first_device(UCLASS_MTD, &dev);
if (ret && !dev) {
pr_err("Find MTD device failed.\n");
return -ENODEV;
}
device_probe(dev);
mtd = get_mtd_device(NULL, 0);
if (!mtd) {
pr_err("Get SPI NAND mtd device failed.\n");
return -ENODEV;
}
ret = readenv(mtd, CONFIG_ENV_OFFSET, (u_char *)buf);
if (ret) {
env_set_default("readenv() failed", 0);
pr_err("read environment from offset 0x%x to 0x%lx failed.\n",
CONFIG_ENV_OFFSET, (unsigned long)buf);
return -EIO;
}
ret = env_import(buf, 1, H_EXTERNAL);
if (!ret)
gd->env_valid = ENV_VALID;
return ret;
}
#if defined(CMD_SAVEENV)
static int env_spinand_save(void)
{
struct mtd_info *mtd;
struct udevice *dev;
env_t env_new;
int ret = -1;
struct erase_info instr = {
.callback = NULL,
};
ret = uclass_first_device(UCLASS_MTD, &dev);
if (ret && !dev) {
pr_err("Find MTD device failed.\n");
return -ENODEV;
}
device_probe(dev);
mtd = get_mtd_device(NULL, 0);
if (!mtd) {
pr_err("Get SPI NAND mtd device failed.\n");
return -ENODEV;
}
ret = env_export(&env_new);
if (ret)
return ret;
instr.len = CONFIG_ENV_SIZE;
instr.addr = CONFIG_ENV_OFFSET;
instr.mtd = mtd;
pr_info("Erasing ...\n");
if (mtd_erase(mtd, &instr)) {
pr_err("SPINAND: erase failed at 0x%08llx\n", instr.addr);
return 1;
}
pr_info("Writing ...\n");
ret = writeenv(mtd, CONFIG_ENV_OFFSET, (u_char *)&env_new);
if (ret) {
pr_err("SPINAND: save env failed.\n");
return 2;
}
return 0;
}
#endif
#endif
U_BOOT_ENV_LOCATION(spinand) = {
.location = ENVL_SPINAND,
ENV_NAME("SPINAND")
.load = env_spinand_load,
#if defined(CMD_SAVEENV)
.save = env_save_ptr(env_spinand_save),
#endif
};