/*
 * (C) Copyright 2013-2016
* SPDX-License-Identifier:	GPL-2.0+
 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
 *
 */
/*
**********************************************************************************************************************
*											        eGon
*						           the Embedded GO-ON Bootloader System
*									       eGON arm boot sub-system
*
*						  Copyright(C), 2006-2010, SoftWinners Microelectronic Co., Ltd.
*                                           All Rights Reserved
*
* File    : nand_osal_boot0.c
*
* By      : Jerry
*
* Version : V2.00
*
* Date	  :
*
* Descript:
**********************************************************************************************************************
*/
#include "../../nand_common.h"

extern int printf(const char *fmt, ...);
#define NAND_Print(fmt, args...) printf(fmt, ##args)

#if 0
#define get_wvalue(addr) readl(addr)
#define put_wvalue(addr, v) writel(v, addr)
#else
#define get_wvalue(addr) readl((volatile void __iomem *)(ulong)addr)
#define put_wvalue(addr, v) writel(v, (volatile void __iomem *)(ulong)addr)
#endif

#define NAND_CLK_BASE_ADDR (SUNXI_CCM_BASE)
#define NAND_PIO_BASE_ADDRESS (SUNXI_PIO_BASE)

u32 _get_pll4_periph1_clk(void)
{
#if 1
	return 24000000;
#else
	u32 n, div1, div2;
	u32 rval;

	rval = get_wvalue((0x06000000 + 0xC)); /*read pll4-periph1 register*/

	n    = (0xff & (rval >> 8));
	div1 = (0x1 & (rval >> 16));
	div2 = (0x1 & (rval >> 18));

	rval = 24000000 * n / (div1 + 1) / (div2 + 1);
	;

	return rval; /*24000000 * n / (div1+1) / (div2+1);*/
#endif
}
__u32 _Getpll6Clk(void)
{
	return 600;
}

__u32 _Get_PLL_PERI(void)
{
	__u32 reg_val;
	__u32 factor_n;
	__u32 factor_m;
	__u32 factor_p0;
	__u32 clock;

	/* PRCM base addr */
	reg_val   = get_wvalue(SUNXI_RPRCM_BASE + 0x1010);
	factor_p0 = ((reg_val >> 16) & 0x3) + 1;
	factor_n  = ((reg_val >> 8) & 0xFF) + 1;
	factor_m = ((reg_val >> 1) & 0x1) + 1;
	clock     = 24000000 * factor_n / factor_m / factor_p0 / 2;

	return clock;
}

int _change_ndfc_clk_v1(__u32 nand_index, __u32 dclk_src_sel, __u32 dclk,
			__u32 cclk_src_sel, __u32 cclk)
{
	u32 reg_val;
	u32 sclk0_src_sel, sclk0, sclk0_src, sclk0_pre_ratio_n, sclk0_src_t,
		sclk0_ratio_m;
	u32 sclk1_src_sel, sclk1, sclk1_src, sclk1_pre_ratio_n, sclk1_src_t,
		sclk1_ratio_m;
	u32 sclk0_reg_adr, sclk1_reg_adr;

	if (nand_index == 0) {
		sclk0_reg_adr =
			(NAND_CLK_BASE_ADDR + 0x0810); /*CCM_NAND0_CLK0_REG;*/
		sclk1_reg_adr =
			(NAND_CLK_BASE_ADDR + 0x0814); /*CCM_NAND0_CLK1_REG;*/
	} else {
		printf("_change_ndfc_clk_v1 error, wrong nand index: %d\n",
		       nand_index);
		return -1;
	}

	/*close dclk and cclk*/
	if ((dclk == 0) && (cclk == 0)) {
		reg_val = get_wvalue(sclk0_reg_adr);
		reg_val &= (~(0x1U << 31));
		put_wvalue(sclk0_reg_adr, reg_val);

		reg_val = get_wvalue(sclk1_reg_adr);
		reg_val &= (~(0x1U << 31));
		put_wvalue(sclk1_reg_adr, reg_val);

		printf("_change_ndfc_clk_v1, close sclk0 and sclk1\n");
		return 0;
	}

	sclk0_src_sel = dclk_src_sel;
	sclk0	 = dclk;
	sclk1_src_sel = cclk_src_sel;
	sclk1	 = cclk;

	if (sclk0_src_sel == 0x0) {
		/*osc pll*/
		sclk0_src = 24;
	} else if (sclk0_src_sel < 0x3)
		sclk0_src = _Get_PLL_PERI() / 1000000;
	else
		sclk0_src = 2 * _Get_PLL_PERI() / 1000000;

	if (sclk1_src_sel == 0x0) {
		/*osc pll*/
		sclk1_src = 24;
	} else if (sclk1_src_sel < 0x3)
		sclk1_src = _Get_PLL_PERI() / 1000000;
	else
		sclk1_src = 2 * _Get_PLL_PERI() / 1000000;

	/*////////////////// sclk0: 2*dclk*/
	/*sclk0_pre_ratio_n*/
	sclk0_pre_ratio_n = 3;
	if (sclk0_src > 4 * 16 * sclk0)
		sclk0_pre_ratio_n = 3;
	else if (sclk0_src > 2 * 16 * sclk0)
		sclk0_pre_ratio_n = 2;
	else if (sclk0_src > 1 * 16 * sclk0)
		sclk0_pre_ratio_n = 1;
	else
		sclk0_pre_ratio_n = 0;

	sclk0_src_t = sclk0_src >> sclk0_pre_ratio_n;

	/*sclk0_ratio_m*/
	sclk0_ratio_m = (sclk0_src_t / (sclk0)) - 1;
	if (sclk0_src_t % (sclk0))
		sclk0_ratio_m += 1;

	/*////////////// sclk1: cclk*/
	/*sclk1_pre_ratio_n*/
	sclk1_pre_ratio_n = 3;
	if (sclk1_src > 4 * 16 * sclk1)
		sclk1_pre_ratio_n = 3;
	else if (sclk1_src > 2 * 16 * sclk1)
		sclk1_pre_ratio_n = 2;
	else if (sclk1_src > 1 * 16 * sclk1)
		sclk1_pre_ratio_n = 1;
	else
		sclk1_pre_ratio_n = 0;

	sclk1_src_t = sclk1_src >> sclk1_pre_ratio_n;

	/*sclk1_ratio_m*/
	sclk1_ratio_m = (sclk1_src_t / (sclk1)) - 1;
	if (sclk1_src_t % (sclk1))
		sclk1_ratio_m += 1;

	/*///////////////////////////// close clock*/
	reg_val = get_wvalue(sclk0_reg_adr);
	reg_val &= (~(0x1U << 31));
	put_wvalue(sclk0_reg_adr, reg_val);

	reg_val = get_wvalue(sclk1_reg_adr);
	reg_val &= (~(0x1U << 31));
	put_wvalue(sclk1_reg_adr, reg_val);

	/*/////////////////////////////configure*/
	/*sclk0 <--> 2*dclk*/
	reg_val = get_wvalue(sclk0_reg_adr);
	/*clock source select*/
	reg_val &= (~(0x7 << 24));
	reg_val |= (sclk0_src_sel & 0x7) << 24;
	/*clock pre-divide ratio(N)*/
	reg_val &= (~(0x3 << 8));
	reg_val |= (sclk0_pre_ratio_n & 0x3) << 8;
	/*clock divide ratio(M)*/
	reg_val &= ~(0xf << 0);
	reg_val |= (sclk0_ratio_m & 0xf) << 0;
	put_wvalue(sclk0_reg_adr, reg_val);

	/*sclk1 <--> cclk*/
	reg_val = get_wvalue(sclk1_reg_adr);
	/*clock source select*/
	reg_val &= (~(0x7 << 24));
	reg_val |= (sclk1_src_sel & 0x7) << 24;
	/*clock pre-divide ratio(N)*/
	reg_val &= (~(0x3 << 8));
	reg_val |= (sclk1_pre_ratio_n & 0x3) << 8;
	/*clock divide ratio(M)*/
	reg_val &= ~(0xf << 0);
	reg_val |= (sclk1_ratio_m & 0xf) << 0;
	put_wvalue(sclk1_reg_adr, reg_val);

	/*///////////////////////////// open clock*/
	reg_val = get_wvalue(sclk0_reg_adr);
	reg_val |= 0x1U << 31;
	put_wvalue(sclk0_reg_adr, reg_val);

	reg_val = get_wvalue(sclk1_reg_adr);
	reg_val |= 0x1U << 31;
	put_wvalue(sclk1_reg_adr, reg_val);

	if (nand_index == 0) {
		sclk0_ratio_m     = (get_wvalue(sclk0_reg_adr) & 0xf) + 1;
		sclk0_pre_ratio_n = 1
				    << ((get_wvalue(sclk0_reg_adr) >> 8) & 0x3);

		sclk1_ratio_m     = (get_wvalue(sclk1_reg_adr) & 0xf) + 1;
		sclk1_pre_ratio_n = 1
				    << ((get_wvalue(sclk1_reg_adr) >> 8) & 0x3);
		NAND_Print("boot0 Nand0 clk: %dMHz,PERI=%d,N=%d,M=%d,T=%d\n",
			   sclk0_src / sclk0_ratio_m / sclk0_pre_ratio_n,
			   sclk0_src, sclk0_pre_ratio_n, sclk0_ratio_m, dclk);
		NAND_Print("boot0 Nand Ecc clk: %dMHz,PERI=%d,N=%d,M=%d,T=%d\n",
			   sclk1_src / sclk1_ratio_m / sclk1_pre_ratio_n,
			   sclk1_src, sclk1_pre_ratio_n, sclk1_ratio_m, cclk);

	} else {
		printf("change nfdc clk error, wrong nand index: %d\n",
		       nand_index);
	}

	return 0;
}

int _close_ndfc_clk_v1(__u32 nand_index)
{
	u32 reg_val;
	u32 sclk0_reg_adr, sclk1_reg_adr;

	if (nand_index == 0) {
		/*disable nand sclock gating*/
		sclk0_reg_adr =
			(NAND_CLK_BASE_ADDR + 0x0810); /*CCM_NAND0_CLK0_REG;*/
		sclk1_reg_adr =
			(NAND_CLK_BASE_ADDR + 0x0814); /*CCM_NAND0_CLK1_REG;*/

		reg_val = get_wvalue(sclk0_reg_adr);
		reg_val &= (~(0x1U << 31));
		put_wvalue(sclk0_reg_adr, reg_val);

		reg_val = get_wvalue(sclk1_reg_adr);
		reg_val &= (~(0x1U << 31));
		put_wvalue(sclk1_reg_adr, reg_val);
	} else {
		printf("close_ndfc_clk error, wrong nand index: %d\n",
		       nand_index);
		return -1;
	}

	return 0;
}

int _open_ndfc_ahb_gate_and_reset_v1(__u32 nand_index)
{
	u32 reg_val = 0;

	/*
	1. release ahb reset and open ahb clock gate for ndfc version 1.
	*/
	if (nand_index == 0) {
		/* reset*/
		reg_val = get_wvalue(NAND_CLK_BASE_ADDR + 0x082C);
		reg_val &= (~(0x1U << 16));
		reg_val |= (0x1U << 16);
		put_wvalue((NAND_CLK_BASE_ADDR + 0x082C), reg_val);
		/* ahb clock gate*/
		reg_val = get_wvalue(NAND_CLK_BASE_ADDR + 0x082C);
		reg_val &= (~(0x1U << 0));
		reg_val |= (0x1U << 0);
		put_wvalue((NAND_CLK_BASE_ADDR + 0x082C), reg_val);

		/* enable nand mbus gate*/
		reg_val = get_wvalue(NAND_CLK_BASE_ADDR + 0x0804);
		reg_val &= (~(0x1U << 5));
		reg_val |= (0x1U << 5);
		put_wvalue((NAND_CLK_BASE_ADDR + 0x0804), reg_val);
	} else {
		printf("open ahb gate and reset, wrong nand index: %d\n",
		       nand_index);
		return -1;
	}

	return 0;
}

int _close_ndfc_ahb_gate_and_reset_v1(__u32 nand_index)
{
	u32 reg_val = 0;
	/*
	1. release ahb reset and open ahb clock gate for ndfc version 1.
	*/
	if (nand_index == 0) {
		/* reset*/
		reg_val = get_wvalue(NAND_CLK_BASE_ADDR + 0x082C);
		reg_val &= (~(0x1U << 16));
		put_wvalue((NAND_CLK_BASE_ADDR + 0x082C), reg_val);
		/* ahb clock gate*/
		reg_val = get_wvalue(NAND_CLK_BASE_ADDR + 0x082C);
		reg_val &= (~(0x1U << 0));
		put_wvalue((NAND_CLK_BASE_ADDR + 0x082C), reg_val);

		/* disable nand mbus gate*/
		reg_val = get_wvalue(NAND_CLK_BASE_ADDR + 0x0804);
		reg_val &= (~(0x1U << 5));
		put_wvalue((NAND_CLK_BASE_ADDR + 0x0804), reg_val);

	} else {
		NAND_Print("close ahb gate and reset, wrong nand index:%d\n",
			   nand_index);
		return -1;
	}
	return 0;
}

int NAND_ClkRequest(__u32 nand_index)
{
	int ret = 0;
	if (nand_index != 0) {
		NAND_Print("NAND_ClkRequest, wrong nand index %d \n",
			   nand_index);
		return -1;
	}
	/* 1. release ahb reset and open ahb clock gate*/
	_open_ndfc_ahb_gate_and_reset_v1(nand_index);
	/* 2. configure ndfc's sclk0*/
	ret = _change_ndfc_clk_v1(nand_index, 3, 10, 3, 10 * 2);
	if (ret < 0) {
		NAND_Print("NAND_ClkRequest, set dclk failed!\n");
		return -1;
	}

	return 0;
}

void NAND_ClkRelease(__u32 nand_index)
{
	return;
}

void NAND_PIORequest(__u32 nand_index)
{
	if (nand_index == 0) {
		/* lsb ------------------------------------------------------------> msb
		* pc0-rb,	pc1-re, 	pc2-ce, 	pc3-cle, 	pc4-ale, 	pc5-we, 	pc6-dq0, 	pc7-dq1
		*/
		*(volatile __u32 *)(NAND_PIO_BASE_ADDRESS + 0x48) = 0x22222222;
		/* lsb ------------------------------------------------------------> msb
		* pf0-dq7, 	pf1-dq6, pf2-dq5, 	pf3-dq4, pf4-dqs,	pf5-dq3, 	pf6-dq2
		*/
		*(volatile __u32 *)(NAND_PIO_BASE_ADDRESS + 0xb4) = 0x02222222;
		/* setting driver level 1 @ PC & PF */
		*(volatile __u32 *)(NAND_PIO_BASE_ADDRESS + 0x5c) = 0x00005555;
		*(volatile __u32 *)(NAND_PIO_BASE_ADDRESS + 0xc8) = 0x00001555;
		/* setting RB & CE @ PC pin pull-up */
		*(volatile __u32 *)(NAND_PIO_BASE_ADDRESS + 0x64) = 0x0011;
	} else if (nand_index == 1) {
		NAND_Print("NAND_PIORequest error, wrong nand_index: 0x%x\n",
			   nand_index);
	} else {
		NAND_Print("NAND_PIORequest error, wrong nand_index: 0x%x\n",
			   nand_index);
	}
}

void NAND_PIORelease(__u32 nand_index)
{
	return;
}

void NAND_EnRbInt(void)
{
	return;
}

void NAND_ClearRbInt(void)
{
	return;
}

int NAND_WaitRbReady(void)
{
	return 0;
}

int NAND_WaitDmaFinish(void)
{
	return 0;
}

void NAND_RbInterrupt(void)
{
	return;
}
/*
************************************************************************************************************
*
*                                             OSAL_malloc
*
*    函数名称：
*
*    参数列表：
*
*    返回值  ：
*
*    说明    ： 这是一个虚假的malloc函数，目的只是提供这样一个函数，避免编译不通过
*               本身不提供任何的函数功能
*
************************************************************************************************************
*/
void *NAND_Malloc(unsigned int Size)
{
	/*return (void *)malloc(Size);*/
	return (void *)CONFIG_SYS_DRAM_BASE;
}
/*
************************************************************************************************************
*
*                                             OSAL_free
*
*    函数名称：
*
*    参数列表：
*
*    返回值  ：
*
*    说明    ： 这是一个虚假的free函数，目的只是提供这样一个函数，避免编译不通过
*               本身不提供任何的函数功能
*
************************************************************************************************************
*/
void NAND_Free(void *pAddr, unsigned int Size)
{
	/*return free(pAddr);*/
}

void *NAND_IORemap(unsigned int base_addr, unsigned int size)
{
	return (void *)(ulong)base_addr;
}
