// SPDX-License-Identifier: GPL-2.0+
/*
 * (c) Copyright 2016 by VRT Technology
 *
 * Author:
 *  Stuart Longland <stuartl@vrt.com.au>
 *
 * Based on FAT environment driver
 * (c) Copyright 2011 by Tigris Elektronik GmbH
 *
 * Author:
 *  Maximilian Schwerin <mvs@tigris.de>
 *
 * and EXT4 filesystem implementation
 * (C) Copyright 2011 - 2012 Samsung Electronics
 * EXT4 filesystem implementation in Uboot by
 * Uma Shankar <uma.shankar@samsung.com>
 * Manjunatha C Achar <a.manjunatha@samsung.com>
 */

#include <common.h>

#include <command.h>
#include <environment.h>
#include <linux/stddef.h>
#include <malloc.h>
#include <memalign.h>
#include <search.h>
#include <errno.h>
#include <partition.h>
#include <ext4fs.h>
#include <mmc.h>

#include <mtk_ab.h>
#include <asm/io.h>

#define AB_SLOT_BUF_SIZE			2
#define ENV_EXT4_PART				"uenv"

#if defined(CONFIG_AB_SIDELOAD) && defined(CONFIG_TARGET_MT5896)
#define ROMCODE_AB_SLOT_NUM	0x1C040480

static uint32_t ab_slot_num(void)
{
	return (readb(ROMCODE_AB_SLOT_NUM) & 0x1); // if 0, it's slot A; if 1, it's slot B
}
#else
static uint32_t ab_slot_num(void)
{
	return 0; // return always slot A
}
#endif

static int convert_ab_name(char *target_name)
{
	uint32_t slot;
	char slot_char[AB_SLOT_BUF_SIZE][3] = {"_a", "_b"};

	slot = get_ab_selection_result();
	if (slot == AB_SLOT_BUF_SIZE) {
		slot = ab_slot_num();
		printf("use slot from ROM code: %d\n", slot);
	}
	if (slot < AB_SLOT_BUF_SIZE) {
		strncat(target_name, slot_char[slot], PART_NAME_LEN - 1);
		printf("uenv part: %s\n", target_name);
		return CMD_RET_SUCCESS;
	}

	strncat(target_name, slot_char[0], PART_NAME_LEN - 1);
	printf("unknown slot (%d): use default part %s\n", slot, target_name);
	return CMD_RET_FAILURE;
}

#ifdef CONFIG_CMD_SAVEENV
extern bool in_cli_loop;

static int env_ext4_save(void)
{
	env_t	env_new;
	struct blk_desc *dev_desc = NULL;
	disk_partition_t info;
	int dev;
	unsigned int part;
	int err;
	unsigned int device = 0;
	char partition_name[PART_NAME_LEN];

	if (!in_cli_loop) {
		printf("\n** Skip saveenv during boot. Please do it on console. **\n");
		return 1;
	}

	memset(partition_name, 0, PART_NAME_LEN);
	strncat(partition_name, ENV_EXT4_PART, (PART_NAME_LEN - 1));
	convert_ab_name(partition_name);

	err = env_export(&env_new);
	if (err)
		return err;

	dev_desc = blk_get_dev(CONFIG_ENV_EXT4_INTERFACE, device);
	if (get_partition_info(dev_desc, partition_name, &info, &part) != CMD_RET_SUCCESS)
		return 1;

	dev = dev_desc->devnum;
	ext4fs_set_blk_dev(dev_desc, &info);

	if (!ext4fs_mount(info.size)) {
		printf("\n** Unable to use %s %s for saveenv **\n",
		       CONFIG_ENV_EXT4_INTERFACE,
		       partition_name);
		return 1;
	}

	err = ext4fs_write(CONFIG_ENV_EXT4_FILE, (void *)&env_new,
			   sizeof(env_t), FILETYPE_REG);
	ext4fs_close();

	if (err == -1) {
		printf("\n** Unable to write \"%s\" from %s%d:%d **\n",
			CONFIG_ENV_EXT4_FILE, CONFIG_ENV_EXT4_INTERFACE, dev,
			part);
		return 1;
	}

	puts("done\n");
	return 0;
}
#endif /* CONFIG_CMD_SAVEENV */

static int env_ext4_load(void)
{
	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
	struct blk_desc *dev_desc = NULL;
	disk_partition_t info;
	int dev;
	unsigned int part;
	int err;
	loff_t off;
	unsigned int device = 0;
	char partition_name[PART_NAME_LEN];

	memset(partition_name, 0, PART_NAME_LEN);
	strncat(partition_name, ENV_EXT4_PART, (PART_NAME_LEN - 1));
	convert_ab_name(partition_name);

#ifdef CONFIG_MMC
	if (!strcmp(CONFIG_ENV_EXT4_INTERFACE, "mmc"))
		mmc_initialize(NULL);
#endif

	dev_desc = blk_get_dev(CONFIG_ENV_EXT4_INTERFACE, device);
	if (get_partition_info(dev_desc, partition_name, &info, &part) != CMD_RET_SUCCESS)
		goto err_env_relocate;

	dev = dev_desc->devnum;
	ext4fs_set_blk_dev(dev_desc, &info);

	if (!ext4fs_mount(info.size)) {
		printf("\n** Unable to use %s %s for loading the env **\n",
		       CONFIG_ENV_EXT4_INTERFACE,
		       partition_name);
		goto err_env_relocate;
	}

	err = ext4_read_file(CONFIG_ENV_EXT4_FILE, buf, 0, CONFIG_ENV_SIZE,
			     &off);
	ext4fs_close();

	if (err == -1) {
		printf("\n** Unable to read \"%s\" from %s%d:%d **\n",
			CONFIG_ENV_EXT4_FILE, CONFIG_ENV_EXT4_INTERFACE, dev,
			part);
		goto err_env_relocate;
	}

	return env_import(buf, 1);

err_env_relocate:
	set_default_env(NULL, 0);

	return -EIO;
}

U_BOOT_ENV_LOCATION(ext4) = {
	.location	= ENVL_CUST_EXT4,
	ENV_NAME("Custom EXT4")
	.load		= env_ext4_load,
	.save		= env_save_ptr(env_ext4_save),
};
