/*
 * Realtek Semiconductor Corp.
 *
 * rlxboard.c:
 *	uboot bsp file
 *
 * Jethro Hsu (jethro@realtek.com)
 */

#include <common.h>
#include <command.h>
#include <asm/mipsregs.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <netdev.h>
#include "bspchip.h"
#include "ddr_def.h"
#include <dma.h>

#define get_val(addr)		REG32(addr)
#define set_val(addr, val)	REG32(addr) = (val)
#define clear_bit(addr, val)	set_val((addr), (get_val(addr) & ~(val)))
#define set_bit(addr, val)	set_val((addr), (get_val(addr) | (val)))
#define ONCHIP_SRAM_VADDR	0xb9000010
#define ONCHIP_SRAM_PADDR	0x19000010

void write_mask(u32 reg, u32 bit, u32 mask)
{
	u32 temp;

	temp = get_val(reg);
	set_val(reg, (temp & (~mask)) | bit);
}

/*#define _DDR_SETTING_DEBUG_*/
phys_size_t initdram(int board_type)
{
	u32 dram_size;
	u32 drr_value_temp;
	u16 ddrc_tref_value;
	u16 pctl_period_ps;
	/* Sdram is setup by assembler code */
	/* If memory could be changed, we should return the true value here */
	/*
	 *probe DDR size ,
	 *1. use physical address to probe size ,
	 *if use cachable address , the value
	 *will save in cache , the addess seemed not wrapped,
	 *2. the ddr physical address is from 0x80000000~0xffffffff,
	 *we can use memory wrap method to detect size
	 */

	MEM32(ONCHIP_SRAM_VADDR) = 0x40000000;
	dma_copy(ONCHIP_SRAM_PADDR, 0xBFFFFFF8, 8);

	MEM32(ONCHIP_SRAM_VADDR) = 0x20000000;
	dma_copy(ONCHIP_SRAM_PADDR, 0x9FFFFFF8, 8);

	MEM32(ONCHIP_SRAM_VADDR) = 0x10000000;
	dma_copy(ONCHIP_SRAM_PADDR, 0x8FFFFFF8, 8);

	MEM32(ONCHIP_SRAM_VADDR) = 0x08000000;
	dma_copy(ONCHIP_SRAM_PADDR, 0x87FFFFF8, 8);

	MEM32(ONCHIP_SRAM_VADDR) = 0x04000000;
	dma_copy(ONCHIP_SRAM_PADDR, 0x83FFFFF8, 8);

	mdelay(1);
	dma_copy(0xBFFFFFF8, ONCHIP_SRAM_PADDR, 8);
	printf(" size is 0x%x\n", MEM32(ONCHIP_SRAM_VADDR));

	dram_size = MEM32(ONCHIP_SRAM_VADDR);
	drr_value_temp = MEM32(DDRC_BASE + PCTL_DRR);
	ddrc_tref_value = (u16)(drr_value_temp >> 8);
	pctl_period_ps = 60000000/(ddrc_tref_value + 0x100);

#ifdef CONFIG_RTS3915_DDR_DQ32
	dram_size /= 2;
#endif

	if (dram_size == 0x08000000) {
		MEM32(DDRC_BASE + PCTL_DRR) = (drr_value_temp & 0xffffff00) |
			(135000 / pctl_period_ps + 1);
	}

	if (dram_size == 0x10000000) {
		MEM32(DDRC_BASE + PCTL_DRR) = (drr_value_temp & 0xffffff00) |
			(160000 / pctl_period_ps + 1);
	}

	if (dram_size == 0x20000000) {
		MEM32(DDRC_BASE + PCTL_DRR) = (drr_value_temp & 0xffffff00) |
			(260000 / pctl_period_ps + 1);
	}
	MEM32(DDRC_BASE + PCTL_CCR) = 0x80000000;

#ifdef CONFIG_RTS3915_DDR_DQ32
	dram_size *= 2;
#endif

#ifdef _DDR_SETTING_DEBUG_
	printf("DDR controller setting\n");
	printf("2a0 wrap_idr: %x\n", MEM32(DDRC_BASE + PCTL_WRAP_IDR));
	printf("f4 svn_id: %x\n", MEM32(DDRC_BASE + PCTL_SVN_ID));
	printf("f8 idr: %x\n", MEM32(DDRC_BASE + PCTL_IDR));
	printf("224 misc: %x\n", MEM32(DDRC_BASE + PCTL_MISC));
	printf("4 dcr: %x\n", MEM32(DDRC_BASE + PCTL_DCR));
	printf("08 iocr: %x\n", MEM32(DDRC_BASE + PCTL_IOCR));
	printf("3c mr2: %x\n", MEM32(DDRC_BASE + PCTL_MR2));
	printf("38 mr1: %x\n", MEM32(DDRC_BASE + PCTL_MR1));
	printf("34 mr0: %x\n", MEM32(DDRC_BASE + PCTL_MR0));
	printf("10 drr: %x\n", MEM32(DDRC_BASE + PCTL_DRR));
	printf("14 tpr0: %x\n", MEM32(DDRC_BASE + PCTL_TPR0));
	printf("18 tpr1: %x\n", MEM32(DDRC_BASE + PCTL_TPR1));
	printf("1c tpr2: %x\n", MEM32(DDRC_BASE + PCTL_TPR2));
	printf("0c csr: %x\n", MEM32(DDRC_BASE + PCTL_CSR));
	printf("30 mrinfo: %x\n", MEM32(DDRC_BASE + PCTL_MR_INFO));
#endif

	return dram_size;

}

void change_dram_clk(void)
{
	/*set DRAM to 400MHz*/
	set_val(CLK_CHANGE, DRAM_CLK_CHANGE);
	set_val(DRAM_OCP_BUS_CLK_CFG_REG,
		CLK_ENABLE | DRAMC_SELECT_GPLL1_2);
	set_val(CLK_CHANGE, 0);
}

void dump_work_mode(void)
{
	u8 mode = 0;

	mode = (REG32(RST_MODE_ADDR) & RST_MASK);

	switch (mode) {
	case NOR:
	case NOR_JTAG0:
	case NOR_JTAG1:
		printf("spi nor boot mode\n");
		break;
	case NOR_SECURE:
	case NOR_ROM_DEBUG:
		printf("spi nor secure mode\n");
		break;
	case NOR_ROM_DOWNLOAD:
		printf("spi nor recure mode\n");
		break;
	case NAND:
	case NAND_JTAG0:
	case NAND_JTAG1:
		printf("spi nand boot mode\n");
		break;
	case NAND_SECURE:
	case NAND_ROM_DEBUG:
		printf("spi nand secure mode\n");
		break;
	case NAND_ROM_DOWNLOAD:
		printf("spi nand recure mode\n");
		break;
	case EMMC:
		printf("emmc boot mode\n");
		break;
	case EMMC_SECURE:
		printf("emmc secure boot mode\n");
		change_dram_clk();
		break;
	default:
		printf("not known mode\n");
		break;
	}
}

void load_efuse(void)
{
	u32 temp;

	/*set XB2 to 60MHz*/
	set_val(CLK_CHANGE, XB2_CLK_CHANGE);
	set_val(CLK_CHANGE, CLK_ENABLE|CLOCK_SELECT_DIV4|XB2_SELECT_USBPLL_2);
	set_val(CLK_CHANGE, 0);

	/*reset and enable clk*/
	set_bit(FORCE_REG_ASYNC_RST, FORCE_EFUSE_RST);
	set_val(EFUSE_CLK_CFG_REG, CLK_ENABLE);
	clear_bit(FORCE_REG_ASYNC_RST, FORCE_EFUSE_RST);
	/*enable HW control*/
	set_val(SF_HW_CTRL, HW_ENABLE);
	/*enable double bit and ecc*/
	set_val(HW_CTRL, DOUBLE_BIT_ECC);

	/*clear efuse data*/
	set_val(CLEAR_1024_REG, CLEAR);
	/*power on*/
	clear_bit(SF_CTRL, RG_PD_SF);
	/*disable redundancy*/
	set_bit(SF_CTRL, RG_RSB_SF);
	/*set delay counter*/
	write_mask(READ_DELAY_COUNTER,
			PD_SETUP_VALUE << PD_SETUP_DELAY_SHIFT,
			PD_SETUP_DELAY_MASK);
	write_mask(READ_DELAY_COUNTER,
			STROBE_READ_VALUE << STROBE_READ_DELAY_SHIFT,
			STROBE_READ_DELAY_MASK);

	/*set burst length*/
	set_val(BURST_CTRL, BURST_LENGTH);
	/*set address*/
	set_val(ADDR_CTRL, ADDR_START);
	/*set read mode*/
	write_mask(HW_CTRL, A_READ << MODE_SELECT_SHIFT, MODE_SELECT_MASK);
	/*start read efuse*/
	set_val(HW_START_REG, HW_START);

	while (1) {
		temp = get_val(HW_START_REG);
		if (temp == 0)
			break;
	}
}

int checkboard(void)
{
	u32 prid;
	u32 cpufreq;
	prid = read_c0_prid();
	prid &= 0xffff;
	cpufreq = CPU_FREQUCNCY;

	/*CPU 500M  PLL0/2 div1
	 *ew 0xb8861000 0x20
	 *ew 0xb8861010 0x104
	 *ew 0xb8861000 0x00
	 */
	REG32(CLK_CHANGE) = CPU_CLK_CHANGE;
#if (defined CONFIG_CPU_800M)
	REG32(CPU_CLK_CFG_REG) = CLK_ENABLE|CPU_SELECT_GPLL1_1;
#elif (defined CONFIG_CPU_500M)
	REG32(CPU_CLK_CFG_REG) = CLK_ENABLE|CPU_SELECT_GPLL0_1;
#elif (defined CONFIG_CPU_480M)
	REG32(CPU_CLK_CFG_REG) = CLK_ENABLE|CPU_SELECT_USBPLL_1;
#elif (defined CONFIG_CPU_400M)
	REG32(CPU_CLK_CFG_REG) = CLK_ENABLE|CPU_SELECT_GPLL1_1|CLOCK_SELECT_DIV2;
#elif (defined CONFIG_CPU_250M)
	REG32(CPU_CLK_CFG_REG) = CLK_ENABLE | CPU_SELECT_GPLL0_1 | CLOCK_SELECT_DIV2;
#elif (defined CONFIG_CPU_160M)
	REG32(CPU_CLK_CFG_REG) = CLK_ENABLE | CPU_SELECT_USBPLL_3;
#endif
	REG32(CLK_CHANGE) = 0;

#ifdef CONFIG_BOARD_rle0745
	printf("Board: IPCAM RLE0745 CPU: ");
#endif

#ifdef DDR2_MCM
	printf("Board: IPCAM RTS3915 CPU: %dM :", cpufreq / 1000000);
#else
	printf("Board: IPCAM RTS3916 CPU: %dM :", cpufreq / 1000000);
#endif

	switch (prid) {
	case 0x0000dc06:
		printf("rx5381");
		break;
	case 0x0000dc05:
		printf("rx4381");
		break;
	case 0x0000dc04:
		printf("rx5271");
		break;
	case 0x0000dc03:
		printf("rx4271");
		break;
	case 0x0000dc02:
		printf("rx5281");
		break;
	case 0x0000dc01:
		printf("rx4281");
		break;
	default:
		printf("unknown");
	}
	printf(" prid=0x%x\n", prid);

	dump_work_mode();
	load_efuse();
	return 0;
}

u8 get_rst_mode(void)
{
	u8 mode = 0;

	mode = (REG32(RST_MODE_ADDR) & RST_MASK);

	switch (mode) {
	case NOR:
	case NOR_JTAG0:
	case NOR_JTAG1:
		mode = NOR_MODE;
		break;
	case NOR_ROM_DOWNLOAD:
		mode = NOR_RES_MODE;
		break;
	case NOR_SECURE:
	case NOR_ROM_DEBUG:
		mode = NOR_SEC_MODE;
		break;
	case NAND:
	case NAND_JTAG0:
	case NAND_JTAG1:
		mode = NAND_MODE;
		break;
	case NAND_ROM_DOWNLOAD:
		mode = NAND_RES_MODE;
		break;
	case NAND_SECURE:
	case NAND_ROM_DEBUG:
		mode = NAND_SEC_MODE;
		break;
	case EMMC:
	case EMMC_SECURE:
		mode = EMMC_MODE;
		break;
	default:
		printf("not known mode\n");
		break;
	}
	return mode;
}

void rts_sys_mem_up(int model)
{
	switch (model) {
	case SYS_MEM_SD_ETH:
		clear_bit(SYS_MEM_SD, ETH_MEM_SD);
		break;

	case SYS_MEM_SD_CIPHER:
		clear_bit(SYS_MEM_SD, CIPHER_MEM_SD);
		break;

	case SYS_MEM_SD_SDIO:
		clear_bit(SYS_MEM_SD, SDIO_MEM_SD);
		break;

	case SYS_MEM_SD_SDIO2:
		clear_bit(SYS_MEM_SD, SDIO2_MEM_SD);
		break;

	case SYS_MEM_SD_CPU:
		clear_bit(SYS_MEM_SD, CPU_MEM_SD);
		break;

	case SYS_MEM_SD_ONCHIP:
		clear_bit(SYS_MEM_SD, ONCHIP_MEM_SD);
		break;

	default:
		printf("ERROR: invalid sys mem model %d\n", model);
		break;
	}
}

void reset_eth_phy(void)
{
	/*FEPHY_ISO_EN set to 1*/
	set_val(ETH_PDWN_CONTROL, 0x2);

	/*close ldo*/
	set_val(FEPHY_LDO_CTRL, 0);
}

void rts_sys_mem_down(int model)
{
	switch (model) {
	case SYS_MEM_SD_ETH:
		set_bit(SYS_MEM_SD, ETH_MEM_SD);
		break;

	case SYS_MEM_SD_CIPHER:
		set_bit(SYS_MEM_SD, CIPHER_MEM_SD);
		break;

	case SYS_MEM_SD_SDIO:
		set_bit(SYS_MEM_SD, SDIO_MEM_SD);
		break;

	case SYS_MEM_SD_SDIO2:
		set_bit(SYS_MEM_SD, SDIO2_MEM_SD);
		break;

	case SYS_MEM_SD_CPU:
		set_bit(SYS_MEM_SD, CPU_MEM_SD);
		break;

	case SYS_MEM_SD_ONCHIP:
		set_bit(SYS_MEM_SD, ONCHIP_MEM_SD);
		break;

	default:
		printf("ERROR: invalid sys mem model %d\n", model);
		break;
	}
}

void _machine_restart(void)
{
	/* set watchdog */
	/* watchdog time: 1s, reset system when timeout, enable watchdog */
	writel(WATCH_DOG_CFG_DATA, (u32 *)WATCHDOG_CFG_REG);
	printf("set watchdog, resetting...");
	while (1)
		;
}

#if defined(CONFIG_HW_WATCHDOG)
void hw_watchdog_reset(void)
{
}

void hw_watchdog_init(void)
{
	int timeout = CONFIG_RTS_WATCHDOG_TIMEOUT;
	int i = 0;
	u32 data;

	//printf("watchdog timeout %d\n", timeout);
	/* timeout = 2^i */
	while (timeout > 1) {
		i++;
		timeout = timeout / 2;
	}
	if (i > 6)
		i = 6;
	//printf("timeout i %d\n", i);
	data = (WATCH_DOG_CFG_DATA & ~WATCH_DOG_TIMEOUT_MASK) | (i << 16);
	/* reset system when timeout, enable watchdog */
	writel(data, (u32 *)WATCHDOG_CFG_REG);
}
#endif

#ifdef CONFIG_TARGET_ASIC
static s64 integer_div(s64 dividend, s64 divisor)
{
	s64 res = 0;

	if (divisor == 0) {
		printf("wrong divisor\n");
		return 0;
	}

	if (dividend < divisor)
		return 0;

	while (dividend > divisor) {
		dividend -= divisor;
		res++;
	}

	if (dividend == divisor)
		res++;

	return res;
}
#endif
int get_dram_clock(void)
{
#ifdef CONFIG_TARGET_ASIC
	u64 dpi_n_code, dpi_f_code;

	dpi_f_code = (MEM32(DDR_PHY_SSC2) & 0x7ff0000) >> 16;
	dpi_n_code = (MEM32(DDR_PHY_SSC3) & 0xff00) >> 8;
	s64 clock = integer_div(REF_PLL * dpi_f_code * 2, 2048);

	clock = integer_div((clock + REF_PLL * (dpi_n_code + 3) * 2 + 500000),
			    1000000);
	return (int)clock;
#endif
#ifdef CONFIG_TARGET_FPGA
	return 224;
#endif
	return 0;
}

#if defined(CONFIG_BOARD_EARLY_INIT_R)
/*
 * init spi flash
 */
int board_early_init_r(void)
{
#if defined(CONFIG_DM_SPI_FLASH) && !defined(CONFIG_NO_SYS_FLASH)
	return init_dm_spi_flash_sheipa();
#endif
	return 0;
}

#endif
