/*
 * (C) Copyright 2000-2010
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
 * Andreas Heppel <aheppel@sysgo.de>

 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <command.h>
#include <environment.h>
#include <linux/stddef.h>
#include <spi.h>
#include <spi_flash.h>
#include <nand.h>
#include "../board/realtek/rlxboard/bspchip.h"

DECLARE_GLOBAL_DATA_PTR;

#ifndef CONFIG_ENV_SPI_BUS
# define CONFIG_ENV_SPI_BUS	0
#endif
#ifndef CONFIG_ENV_SPI_CS
# define CONFIG_ENV_SPI_CS	0
#endif
#ifndef CONFIG_ENV_SPI_MAX_HZ
# define CONFIG_ENV_SPI_MAX_HZ	1000000
#endif
#ifndef CONFIG_ENV_SPI_MODE
# define CONFIG_ENV_SPI_MODE	SPI_MODE_3
#endif

static void self_copy(void);

env_t *env_ptr;

#ifdef CONFIG_RTS_QSPI
extern struct spi_flash *flash;
#endif
void env_relocate_spec(void)
{
	u8 board_reset_mode = get_rst_mode();
#ifdef CONFIG_RTS_QSPI
	struct spi_flash *new;
#ifdef CONFIG_QFN88_PACKAGE
	/*When config dual channel,use UART2 RX pin connect flash WP# to
	 *enable hardware write protect or disable hardware write protect.
	 *QFN88 package startup UART2 RX is pull down,so enter hardware write
	 *protect.When do not config hardware write protect,before probe
	 *flash,need exit hardware write protect.
	*/
#if defined CONFIG_BOARD_RTS3913 || defined CONFIG_BOARD_RTS3903
	REG32(XB2_SHARE_GPIO_SELECT) = REG32(XB2_SHARE_GPIO_SELECT) |
		(GPIO_SELECT << GPIO_UART2_RXD_BIT);
	REG32(XB2_SHARE_GPIO_OE) = REG32(XB2_SHARE_GPIO_OE) |
		(GPIO_OUTPUT_ENABLE << GPIO_UART2_RXD_BIT);
	REG32(XB2_SHARE_GPIO_O) = REG32(XB2_SHARE_GPIO_O) |
		(GPIO_OUTPUT_VALUE << GPIO_UART2_RXD_BIT);
#endif
#endif
	if (board_reset_mode == NOR_MODE || board_reset_mode == NOR_SEC_MODE) {
		new = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
				CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);

		if (flash)
			spi_flash_free(flash);
		flash = new;
	} else if (board_reset_mode == NOR_RES_MODE) {
#if defined CONFIG_BOARD_RTS3913 || defined CONFIG_BOARD_RTS3915
		/*set user mode for spi nor download mode*/
		REG32(PLATFORM_CON) = REG32(PLATFORM_CON) | ROM_SEL_NOR;
#endif
		new = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
				CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);

		if (flash)
			spi_flash_free(flash);
		flash = new;
		self_copy();
	}
#endif
#ifdef CONFIG_RTS_SPI_NAND_FLASH
	if (board_reset_mode == NAND_MODE) {
		puts("NAND:  ");
		nand_init();
	} else if (board_reset_mode == NAND_RES_MODE) {
#ifdef CONFIG_BOARD_RTS3915
		/*set user mode for spi nand download mode*/
		REG32(PLATFORM_CON) = REG32(PLATFORM_CON) | ROM_SEL_NAND;
#endif
		nand_init();
		self_copy();
	}
#endif
}

void self_copy(void)
{
	u8 ret;
	u8 board_reset_mode = get_rst_mode();

	printf("user write uboot to flash\n");
#ifdef CONFIG_RTS_QSPI
	if (board_reset_mode == NOR_RES_MODE) {
		ret = spi_flash_erase(flash, RESCUE_FLASH_OFFSET,
						RESCUE_BIN_LENGTH);
		printf("SF: %zu bytes @ %#x Erased: %s\n",
			(size_t)RESCUE_BIN_LENGTH,
		    (u32)RESCUE_FLASH_OFFSET, ret ? "ERROR" : "OK");

		ret = spi_flash_write(flash, RESCUE_FLASH_OFFSET,
				RESCUE_BIN_LENGTH,
				(void *)RESCUE_LOAD_ADDR);

		printf("SF: %zu bytes @ %#x %s: %s\n",
			(size_t)RESCUE_BIN_LENGTH,
			(u32)RESCUE_FLASH_OFFSET,
			"Written", ret ? "ERROR" : "OK"); }
#endif
#ifdef CONFIG_RTS_SPI_NAND_FLASH
	if (board_reset_mode == NAND_RES_MODE) {
		ret = update_image_to_nand(RESCUE_FLASH_OFFSET,
				RESCUE_BIN_LENGTH,
				(unsigned char *)RESCUE_LOAD_ADDR);
		}
#endif
	if (ret < 0)
		printf("self copy fail\n");
}
/*
 * Initialize Environment use
 *
 * We are still running from ROM, so data use is limited
 */
int env_init(void)
{
	gd->env_addr	= (ulong)&default_environment[0];
	gd->env_valid	= 0;

	return 0;
}

unsigned int read_spi_flash(unsigned int offset)
{
	u8 board_reset_mode = get_rst_mode();

	if (board_reset_mode == NOR_MODE) {
#ifdef CONFIG_RTS_QSPI
	if (flash)
		return spi_flash_read_word(flash, offset);
	else
		return 0xDEADBEAF;
#endif
	} else {
#ifdef CONFIG_RTS_SPI_NAND_FLASH
	u32 data;
	copy_word_data_to_ram(offset - FLASHBASEADDR, (ulong)(&data));
	return data;
#endif
	}
}

