/* SPDX-License-Identifier: GPL-2.0 */
/*
 ************************************************************************************************************************
 *                                                      eNand
 *                                           Nand flash driver scan module
 *
 *                             Copyright(C), 2008-2009, SoftWinners Microelectronic Co., Ltd.
 *                                                  All Rights Reserved
 *
 * File Name : nand_chip_function.c
 *
 * Author :
 *
 * Version : v0.1
 *
 * Date : 2013-11-20
 *
 * Description :
 *
 * Others : None at present.
 *
 *
 *
 ************************************************************************************************************************
 */

#define _NCTRIC_C_
/* #define TEST_ONFI_DDR_ON_FPGA_PLATFORM */

#include "ndfc_timings.h"
#include "../../../nfd/nand_osal_for_linux.h"
#include "../../nand_errno.h"
#include "../rawnand_chip.h"
#include "../rawnand_cfg.h"
#include "../rawnand_debug.h"
#include "ndfc_base.h"
#include "ndfc_ops.h"
static inline struct nand_controller_info *to_sunxi_nctri(struct nand_chip_info *chip)
{
	return chip->nctri;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int add_to_nctri(struct nand_controller_info *node)
{
	struct nand_controller_info *head = g_nctri;

	if (head == NULL) {
		node->channel_id = 0;
		g_nctri = node;
	} else {
		node->channel_id = 1;
		while (head->next != 0) {
			node->channel_id++;
			head = head->next;
		}
		head->next = node;
	}

	/*ndfc_df_ops.fill_nctri(node);*/

	return 0;
}

void delete_from_nctri_by_channel(int channel)
{
	struct nand_controller_info *head = g_nctri;

	struct nand_controller_info *tail = NULL;
	struct nand_controller_info *prev = NULL;

	for (tail = head; tail != NULL; tail = tail->next) {
		if (channel == 0) {
			g_nctri = tail->next;
			break;
		} else if (tail->channel_id  == channel) {
			prev->next = tail->next;
			break;
		}
		prev = tail;
	}
}

void delete_nctri(void)
{
	struct nand_controller_info *head = g_nctri;

	struct nand_controller_info *next = NULL;
	struct nand_controller_info *tail = NULL;
	void *nreg_base = NULL;

	for (tail = head; tail != NULL; tail = next) {
		nci_delete_from_nctri(tail);
		nreg_base = tail->nreg_base;
		memset(tail, 0, sizeof(struct nand_controller_info));
		tail->nreg_base = nreg_base;
		next = tail->next;
		tail->next = NULL;
	}

	g_nctri = NULL;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
struct nand_controller_info *nctri_get(struct nand_controller_info *head, unsigned int num)
{
	int i;
	struct nand_controller_info *nctri;

	for (i = 0, nctri = head; i < num; i++)
		nctri = nctri->next;

	return nctri;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int before_update_nctri_interface(struct nand_controller_info *nctri)
{
	struct nand_chip_info *nci = nctri->nci;
	s32 ret = 0;
	u32 ddr_type, sdr_edo, ddr_edo, ddr_delay;

	if (nctri->nci->support_toggle_only) {
		/* init toggle ddr interface with classic clk cfg(20MHz) */
		RAWNAND_DBG("%s: ch %d, init toggle interface!\n", __func__, nctri->channel_id);
		if ((nctri->nci->interface_type == TOG_DDR) || (nctri->nci->interface_type == TOG_DDR2)) {
			ret = nand_set_clk(&aw_ndfc, nctri->channel_id, 20, 20 * 2);
			if (ret) {
				RAWNAND_ERR("%s: init toggle interface with classic clk cfg(%d, %d) failed!\n", __func__, 20, 20 * 2);
				return ERR_NO_48;
			}

			ddr_type = TOG_DDR;
			sdr_edo = 0;
			ddr_edo = 0x2;
			ddr_delay = 0x1f;
			ndfc_change_nand_interface(nctri, ddr_type, sdr_edo, ddr_edo, ddr_delay);
		}

		while (nci != NULL) {
			nctri->ddr_timing_ctl[nci->nctri_chip_no] = (ddr_edo << 8) | ddr_delay;
			nci = nci->nctri_next;
		}
	}

	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int start_update_nctri_interface(struct nand_controller_info *nctri, u32 input_timing_para, u32 ddr_type, u32 ddr_type_old, u32 sclk0, u32 sclk1, u32 sdr_edo_i, u32 ddr_edo_i, u32 ddr_delay_i)
{
	s32 ret;
	struct nand_chip_info *nci;
	u32 sdr_edo, ddr_edo, ddr_delay, tmp, i;
	u32 ddr_type_bak, ddr_type_old_bak;

	ddr_type_bak = ddr_type;
	ddr_type_old_bak = ddr_type_old;

	ret = _change_all_nand_parameter(nctri, ddr_type_bak, ddr_type_old_bak, sclk0);
	if (ret) {
		RAWNAND_ERR("_change nand parameter error when use clk %dMhz!\n", sclk0);
		return ret;
	}

	ret = nand_set_clk(&aw_ndfc, nctri->channel_id, sclk0, sclk1);
	if (ret) {
		RAWNAND_ERR("change clk to cfg(%d, %d) failed!\n", sclk0, sclk0 * 2);
		return ERR_NO_47;
	}

	if (input_timing_para) {
		sdr_edo = sdr_edo_i;
		ddr_edo = ddr_edo_i;
		ddr_delay = ddr_delay_i;
		ndfc_change_nand_interface(nctri, ddr_type_bak, sdr_edo, ddr_edo, ddr_delay);
	} else {
		/* get right timing parameter. only get timing parameter for first chip */
		ret = _get_right_timing_para(nctri, ddr_type_bak, &sdr_edo, &ddr_edo, &ddr_delay);
#ifndef TEST_ONFI_DDR_ON_FPGA_PLATFORM
		if (ret < 0) {
			if ((nctri->nci->support_toggle_only == 0) && (nctri->nci->interface_type != SDR)) {
				nctri->nci->interface_type = SDR;
				ddr_type_bak = nctri->nci->interface_type;
				ddr_type_old_bak = ddr_type;

				for (i = 0; i < 3; i++) {
					//nctri->nci->frequency = 40;
					nctri->nci->frequency = 15; //for debug A50 IC by zzm
					RAWNAND_DBG("try timing %d clk %dMHz!\n", i, nctri->nci->frequency);
					ret = _change_all_nand_parameter(nctri, ddr_type_bak, ddr_type_old_bak, nctri->nci->frequency);
					if (ret) {
						RAWNAND_ERR("1 _change nand parameter error when use clk %dMhz!\n", nctri->nci->frequency);
						return ret;
					}

					ret = nand_set_clk(&aw_ndfc, nctri->channel_id, nctri->nci->frequency, 2 * nctri->nci->frequency);
					if (ret) {
						RAWNAND_ERR("1 change clk to cfg(%d, %d) failed!\n", nctri->nci->frequency, nctri->nci->frequency * 2);
						return ERR_NO_47;
					}
					ret = _get_right_timing_para(nctri, ddr_type_bak, &sdr_edo, &ddr_edo, &ddr_delay);
					if (ret >= 0) {
						break;
					}

					nctri->nci->frequency -= 10;
				}
				if (ret < 0) {
					/*failed, */
					RAWNAND_ERR("get timing para failed 0!\n");
					return ret;
				} else {
					/* successful, set parameter*/
					ndfc_change_nand_interface(nctri, ddr_type_bak, sdr_edo, ddr_edo, ddr_delay);
				}
			} else {
				/*failed, */
				RAWNAND_ERR("get timing para failed! 1\n");
				return ret;
			}
		} else {
			/* successful, set parameter*/
			ndfc_change_nand_interface(nctri, ddr_type_bak, sdr_edo, ddr_edo, ddr_delay);
		}
	}
#else
		ddr_edo = 2;
		ddr_delay = 0x1f;
		ndfc_change_nand_interface(nctri, ONFI_DDR, sdr_edo, 2, 0x1f);
#endif
	if (ddr_type_bak == SDR) {
		tmp = sdr_edo << 8;
	} else {
		tmp = (ddr_edo << 8) | ddr_delay;
	}

	nci = nctri->nci;
	while (nci) {
		nctri->ddr_timing_ctl[nci->nctri_chip_no] = tmp;
		nci = nci->nctri_next;
	}

	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int ddr_mode_to_sdr(struct nand_controller_info *nctri)
{
	struct nand_chip_info *nci;
	nci = nctri->nci;

	if (!nci->itf_cfg.toggle_cfg.support_vendor_specific_setting) {
		return 0;
	}

	while (nci != NULL) {
		_setup_ddr_nand_force_to_sdr_para(nci);
		nci = nci->nctri_next;
	}

	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int update_1st_nctri_interface(struct nand_controller_info *nctri)
{
	int ret = 0;
	//struct nand_chip_info *nci;
	unsigned int ddr_type_bak, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak;
	unsigned int ddr_type;
	unsigned int sclk0, sclk1, sclk0_bak, sclk1_bak;

	if (nctri->channel_id != 0) {
		RAWNAND_ERR("wrong nctri %d\n", nctri->channel_id);
		return ERR_NO_47;
	}

	ddr_mode_to_sdr(nctri);

	/* get current interface & access frequency */
	ndfc_get_nand_interface(nctri, &ddr_type_bak, &sdr_edo_bak, &ddr_edo_bak, &ddr_delay_bak);
	nand_get_clk(&aw_ndfc, nctri->channel_id, &sclk0_bak, &sclk1_bak);

	/* new interface type & access frequency */
	ddr_type = nctri->nci->interface_type;
	sclk0 = nctri->nci->frequency;
	sclk1 = 2 * sclk0;

	ret = _check_scan_data(1, 0, nctri->ddr_scan_blk_no, NULL);
	if ((ret < 0) || (ret == 1)) {
		if ((ddr_type == SDR) && (sclk0 < 50)) {
			RAWNAND_DBG("%d interface change ddr_type:%x sclk0:%d sclk1:%d sdr_edo_bak:%x ddr_edo_bak:%x ddr_delay_bak:%x !\n", __LINE__, ddr_type, sclk0, sclk1, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak);
			ret = start_update_nctri_interface(nctri, 0, ddr_type, ddr_type_bak, sclk0, sclk1, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak);
		} else {
			RAWNAND_DBG("interface  not change ddr_type:%x sclk0:%d sclk1:%d sdr_edo:%x ddr_edo:%x ddr_delay:%x !\n", ddr_type_bak, sclk0_bak, sclk1_bak, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak);
			ret = start_update_nctri_interface(nctri, 1, ddr_type_bak, ddr_type_bak, sclk0_bak, sclk1_bak, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak);
		}
	} else {
		RAWNAND_DBG("%d interface change ddr_type:%x sclk0:%d sclk1:%d sdr_edo_bak:%x ddr_edo_bak:%x ddr_delay_bak:%x !\n", __LINE__, ddr_type, sclk0, sclk1, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak);
		ret = start_update_nctri_interface(nctri, 0, ddr_type, ddr_type_bak, sclk0, sclk1, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak);
	}

	if (ret) {
		RAWNAND_ERR("start update nctri interface  fail --1!\n");
	}

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int update_other_nctri_interface(struct nand_controller_info *nctri)
{
	int ret = 0;
	unsigned int timing_change = 0;
	unsigned int ddr_type_bak, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak;
	unsigned int ddr_type, sdr_edo, ddr_edo, ddr_delay;
	unsigned int sclk0, sclk1, sclk0_bak, sclk1_bak;

	if (nctri->channel_id == 0) {
		RAWNAND_ERR("%s: wrong nctri %u\n", __func__, nctri->channel_id);
		return ERR_NO_46;
	}

	ddr_mode_to_sdr(nctri);

	//get current configuration
	ndfc_get_nand_interface(nctri, &ddr_type_bak, &sdr_edo_bak, &ddr_edo_bak, &ddr_delay_bak);
	nand_get_clk(&aw_ndfc, nctri->channel_id, &sclk0_bak, &sclk1_bak);

	//get new configuration from channel 0
	ndfc_get_nand_interface(g_nctri, &ddr_type, &sdr_edo, &ddr_edo, &ddr_delay);
	nand_get_clk(&aw_ndfc, g_nctri->channel_id, &sclk0, &sclk1);

	/*  change ? */
	if (ddr_type_bak != ddr_type) {
		timing_change = 1;
		RAWNAND_DBG("%s: nand interface will be changed! %d -> %d\n", __func__, ddr_type_bak, ddr_type);
	}

	if (sclk0_bak != sclk0) {
		timing_change = 1;
		RAWNAND_DBG("%s: sclk0 will be changed! %d -> %d\n", __func__, sclk0_bak, sclk0);
	}

	if ((nctri->type == NDFC_VERSION_V2) && (sclk1_bak != sclk1)) {
		timing_change = 1;
		RAWNAND_DBG("%s: sclk1 will be changed! %d -> %d\n", __func__, sclk1_bak, sclk1);
	}

	RAWNAND_DBG("%s: timing_change %d, sclk0_bak %d, sclk0 %d!\n", __func__, timing_change, sclk0_bak, sclk0);

	if (timing_change) {
		ret = start_update_nctri_interface(nctri, 1, ddr_type, ddr_type_bak, sclk0, sclk1, sdr_edo, ddr_edo, ddr_delay);
		if (ret) {
			RAWNAND_ERR("start update nctri_interface fail --0!\n");
		}
	}
	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
s32 _update_ext_accessfreq_para(struct nand_controller_info *nctri)
{
	u32 id_number_ctl;
	u32 script_frequence, para;
	struct nand_chip_info *nci;

	id_number_ctl = nand_get_nand_id_number_ctrl(&aw_ndfc);

	if ((id_number_ctl & 0x01) != 0) {
		/*bit 0, set freq para*/
		para = nand_get_nand_extern_para(&aw_ndfc, 0);
		if (para == 0xffffffff) {
			/*get script fail*/
			RAWNAND_ERR("_update_ext accessfreq_para: wrong freq para, 0x%x\n", para);
			return ERR_NO_43;
		}

		if (((para & 0xffffff) == nctri->nci->npi->id_number) || ((para & 0xffffff) == 0xeeeeee)) {
			script_frequence = (para >> 24) & 0xff;
			if ((script_frequence > 10) && (script_frequence < 100)) {
				nci = nctri->nci;
				while (nci) {
					nci->frequency = script_frequence;
					nci = nci->nctri_next;
				}
				RAWNAND_DBG("_update_ext accessfreq_para: update freq from script, %d\n", script_frequence);
			} else {
				RAWNAND_ERR("_update_ext accessfreq_para: wrong freq, %d\n", script_frequence);
				return ERR_NO_42;
			}
		} else {
			RAWNAND_ERR("_update_ext accessfreq_para: wrong id number, 0x%x/0x%x\n", (para & 0xffffff), nctri->nci->npi->id_number);
			return ERR_NO_41;
		}
	} else if ((id_number_ctl & 0x10) != 0) {
		script_frequence = nctri->nci->npi->access_high_freq;
		if ((script_frequence > 10) && (script_frequence < 100)) {
			nci = nctri->nci;
			while (nci) {
				nci->frequency = script_frequence;
				nci = nci->nctri_next;
			}
			RAWNAND_DBG("_update_ext accessfreq_para: update freq high, %d\n", script_frequence);
		} else {
			RAWNAND_ERR("_update_ext accessfreq_para: wrong freq, %d\n", script_frequence);
			return ERR_NO_42;
		}
	} else {
		return 0;
	}
	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int set_nand_script_frequence(void)
{
	int ret;
	struct nand_controller_info *nctri = g_nctri;

	if (SUPPORT_UPDATE_EXTERNAL_ACCESS_FREQ) {
		while (nctri != NULL) {
			ret = _update_ext_accessfreq_para(nctri);
			if (ret != 0) {
				RAWNAND_ERR("update ext accessfreq para fail, using internal frequency!\n");
			}
			nctri = nctri->next;
		}
	}
	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int update_nctri(struct nand_controller_info *nctri)
{
	s32 i, ret;
	u32 rb_connect = 0xff;
	u32 chip_cnt = nctri->chip_cnt;
	u32 chip_connect = nctri->chip_connect_info;

	while (nctri != NULL) {
		// change ndfc configuration
		ndfc_change_page_size(nctri);
		ndfc_set_ecc_mode(nctri, nctri->nci->ecc_mode);

		// build physical connection
		memset(nctri->ce, 0xff, MAX_CHIP_PER_CHANNEL);
		memset(nctri->rb, 0xff, MAX_CHIP_PER_CHANNEL);

		if ((chip_cnt == 1) && (chip_connect & (1 << 0))) {
			rb_connect = 1;

			nctri->ce[0] = 0;
			nctri->rb[0] = 0;
		} else if (chip_cnt == 2) {
			if ((chip_connect & (1 << 0)) && (chip_connect & (1 << 1))) {
				rb_connect = 2;

				nctri->ce[0] = 0;
				nctri->rb[0] = 0;
				nctri->ce[1] = 1;
				nctri->rb[1] = 1;
			} else if ((chip_connect & (1 << 0)) && (chip_connect & (1 << 2))) {
				rb_connect = 3;

				nctri->ce[0] = 0;
				nctri->rb[0] = 0;
				nctri->ce[1] = 2;
				nctri->rb[1] = 1;
			} else if ((chip_connect & (1 << 0)) && (chip_connect & (1 << 7))) {
				//special use, only one rb
				rb_connect = 0;

				nctri->ce[0] = 0;
				nctri->rb[0] = 0;
				nctri->ce[1] = 7;
				nctri->rb[1] = 0;
			}
		} else if (chip_cnt == 4) {
			if ((chip_connect & (1 << 0)) && (chip_connect & (1 << 1)) && (chip_connect & (1 << 2)) && (chip_connect & (1 << 3))) {
				rb_connect = 4;

				nctri->ce[0] = 0;
				nctri->rb[0] = 0;
				nctri->ce[1] = 1;
				nctri->rb[1] = 0;
				nctri->ce[2] = 2;
				nctri->rb[2] = 1;
				nctri->ce[3] = 3;
				nctri->rb[3] = 1;
			} else if ((chip_connect & (1 << 0)) && (chip_connect & (1 << 2)) && (chip_connect & (1 << 4)) && (chip_connect & (1 << 6))) {
				rb_connect = 5;

				nctri->ce[0] = 0;
				nctri->rb[0] = 0;
				nctri->ce[1] = 2;
				nctri->rb[1] = 1;
				nctri->ce[2] = 4;
				nctri->rb[2] = 0;
				nctri->ce[3] = 6;
				nctri->rb[3] = 1;
			}
		} else if (chip_cnt == 8) {
			rb_connect = 8;

			nctri->ce[0] = 0;
			nctri->rb[0] = 0;
			nctri->ce[1] = 1;
			nctri->rb[1] = 0;
			nctri->ce[2] = 2;
			nctri->rb[2] = 1;
			nctri->ce[3] = 3;
			nctri->rb[3] = 1;

			nctri->ce[4] = 4;
			nctri->rb[4] = 0;
			nctri->ce[5] = 5;
			nctri->rb[5] = 0;
			nctri->ce[6] = 6;
			nctri->rb[6] = 1;
			nctri->ce[7] = 7;
			nctri->rb[7] = 1;
		} else {
			return ERR_NO_40;
		}

		if (rb_connect == 0xff) {
			RAWNAND_ERR("update_nctri: build nand rb connect fail, chip_cnt=%d, chip_connect=0x%x\n", chip_cnt, chip_connect);
			return ERR_NO_39;
		} else {
			nctri->rb_connect_info = rb_connect;
			RAWNAND_DBG("build nand physical connection ok...\n");
			RAWNAND_DBG("CE: ");
			for (i = 0; i < chip_cnt; i++)
				RAWNAND_DBG("%d ", nctri->ce[i]);

			RAWNAND_DBG("\nRB: ");
			for (i = 0; i < chip_cnt; i++)
				RAWNAND_DBG("%d ", nctri->rb[i]);
			RAWNAND_DBG("\n");
		}

		ret = before_update_nctri_interface(nctri);
		if (ret) {
			RAWNAND_ERR("before_update nctri nand interface fail!\n");
			return ret;
		}
		if (nctri->channel_id == 0) {
			ret = update_1st_nctri_interface(nctri);
			if (ret) {
				RAWNAND_ERR("update 1st nctri interface fail!\n");
				return ret;
			}
		} else {
			ret = update_other_nctri_interface(nctri);
			if (ret) {
				RAWNAND_ERR("update other nctri interface fail!\n");
				return ret;
			}
		}
		nctri = nctri->next;
	}

	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int check_nctri(struct nand_controller_info *nctri)
{
	int ch;
	unsigned int id_number, chip_connect;
	struct nand_controller_info *nctri_tmp;
	struct nand_chip_info *nci;

	for (ch = MAX_CHANNEL - 1; ch >= 0; ch--) {
		nctri_tmp = nctri_get(nctri, ch);
		if ((nctri_tmp != NULL) && (nctri_tmp->chip_cnt == 0)) {
			nand_free(nctri_tmp);
			if (ch >= 1) {
				nctri_tmp = nctri_get(nctri, ch - 1);
				nctri_tmp->next = NULL;
				RAWNAND_DBG("null channel %d\n", ch);
			} else {
				RAWNAND_ERR("error, channel 0 is null!!!!\n");
				return ERR_NO_38;
			}
		}
	}

	while (nctri != NULL) {
		if (nctri->channel_id == 0) {
			id_number = nctri->nci->npi->id_number;
			chip_connect = nctri->chip_connect_info;
		} else {
			id_number = g_nctri->nci->npi->id_number;
			chip_connect = g_nctri->chip_connect_info;
		}

		if ((nctri->chip_connect_info != chip_connect) && (nctri->chip_cnt != 0)) {
			RAWNAND_ERR("check_nctri, different chip_connect %d %d\n", chip_connect, nctri->chip_connect_info);
			return ERR_NO_37;
		}

		// all chips are same ?
		nci = nctri->nci;
		while (nci != NULL) {
			if (nci->npi->id_number != id_number) {
				RAWNAND_ERR("check_nctri, different id number\n");
				return ERR_NO_36;
			}
			nci = nci->nctri_next;
		}

		nctri = nctri->next;
	}

	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int update_boot0_nctri_interface(struct nand_controller_info *nctri)
{
	int ret = 0;
	//struct nand_chip_info *nci;
	unsigned int ddr_type_bak, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak;
	unsigned int ddr_type;
	unsigned int sclk0, sclk1, sclk0_bak, sclk1_bak;

	if (nctri->channel_id != 0) {
		RAWNAND_ERR("wrong nctri %d\n", nctri->channel_id);
		return ERR_NO_47;
	}

	/* get current interface & access frequency */
	ndfc_get_nand_interface(nctri, &ddr_type_bak, &sdr_edo_bak, &ddr_edo_bak, &ddr_delay_bak);
	nand_get_clk(&aw_ndfc, nctri->channel_id, &sclk0_bak, &sclk1_bak);

	/* new interface type & access frequency */
	ddr_type = 0x0; //SDR interface
	sclk0 = 15;     //30MHz
	sclk1 = 2 * sclk0;

	ret = start_update_nctri_interface(nctri, 0, ddr_type, ddr_type_bak, sclk0, sclk1, sdr_edo_bak, ddr_edo_bak, ddr_delay_bak);
	if (ret) {
		RAWNAND_ERR("update write boot0 nctri interface  fail --1!\n");
	}

	return ret;
}
#if 0
static int sunxi_ndfc_normal_exec_op(struct nand_chip_info *chip,
				     const struct nand_operation *op, bool check_only)
{
	struct nand_controller_info *nctri = chip->nctri;
	u32 cmd = 0, extcmd = 0, cnt = 0, addrs[2] = {};
	unsigned int i, j, remaining, start;
	void *inbuf = NULL;
	int ret;

	for (i = 0; i < op->ninstrs; i++) {
		const struct nand_op_instr *instr = &op->instrs[i];
		switch (instr->type) {
		case NAND_OP_CMD_INSTR:
			if (cmd & NDFC_SEND_CMD1) {
				if (WARN_ON(cmd & NDFC_SEND_CMD2))
					return -EINVAL;

				cmd |= NDFC_SEND_CMD2;
				extcmd |= instr->ctx.cmd.opcode;
			} else {
				cmd |= NDFC_SEND_CMD1 |
				       NDFC_CMD(instr->ctx.cmd.opcode);
			}
			break;

		case NAND_OP_ADDR_INSTR:
			for (j = 0; j < 8 && j < instr->ctx.addr.naddrs; j++) {
				u8 addr = instr->ctx.addr.addrs[j];

				addrs[j / 4] |= addr << ((j % 4) * 8);
			}

			if (j)
				cmd |= NDFC_SEND_ADR | NDFC_ADR_NUM(j);

			break;

		case NAND_OP_DATA_IN_INSTR:
		case NAND_OP_DATA_OUT_INSTR:
			start = nand_subop_get_data_start_off(subop, i);
			remaining = nand_subop_get_data_len(subop, i);
			cnt = min_t(u32, remaining, NFC_SRAM_SIZE);
			cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;

			if (instr->type == NAND_OP_DATA_OUT_INSTR) {
				cmd |= NDFC_ACCESS_DIR;
				memcpy_toio(nfc->regs + NFC_RAM0_BASE,
					    instr->ctx.data.buf.out + start,
					    cnt);
			} else {
				inbuf = instr->ctx.data.buf.in + start;
			}

			break;

		case NAND_OP_WAITRDY_INSTR:
			cmd |= NDFC_WAIT_FLAG;
			break;
		}
	}

	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
	if (ret)
		return ret;

	if (cmd & NFC_SEND_ADR) {
		writel(addrs[0], nfc->regs + NFC_REG_ADDR_LOW);
		writel(addrs[1], nfc->regs + NFC_REG_ADDR_HIGH);
	}

	if (cmd & NFC_SEND_CMD2)
		writel(extcmd,
		       nfc->regs +
			   (cmd & NFC_ACCESS_DIR ? NFC_REG_WCMD_SET : NFC_REG_RCMD_SET));

	if (cmd & NFC_DATA_TRANS)
		writel(cnt, nfc->regs + NFC_REG_CNT);

	writel(cmd, nfc->regs + NFC_REG_CMD);

	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG,
				    !(cmd & NFC_WAIT_FLAG) && cnt < 64,
				    0);
	if (ret)
		return ret;

	if (inbuf)
		memcpy_fromio(inbuf, nfc->regs + NFC_RAM0_BASE, cnt);

	return 0;
}

static int sunxi_ndfc_exec_op(struct nand_chip_info *chip,
			      const struct nand_operation *op, bool check_only)
{
	struct nand_controller_info *nctri = chip->nctri;
	u32 cmd = 0, extcmd = 0;
	u32 cnt = 0, addrs[2] = {};
	unsigned int i, j, n, cmdcnt, remaining, start;
	void *inbuf = NULL;
	int ret;
	for (n = 0; n < op->nopsets; n++) {
		const struct nand_opset *opset = &op->opset[i];
		for (i = 0; i < opset->ninstrs; i++) {
			const struct nand_op_instr *instr = &opset->instrs[i];
			switch (instr->type) {
			case NAND_OP_CMD_INSTR:
				if (cmd & NDFC_SEND_CMD1) {
					if (WARN_ON(cmd & NDFC_SEND_CMD2))
						return -EINVAL;

					cmd |= NDFC_SEND_CMD2;
					extcmd = instr->ctx.cmd.opcode;
				} else {
					cmd |= NDFC_SEND_CMD1 |
					       NDFC_CMD(instr->ctx.cmd.opcode);
				}
				break;

			case NAND_OP_ADDR_INSTR:
				for (j = 0; j < 8 && j < instr->ctx.addr.naddrs; j++) {
					u8 addr = instr->ctx.addr.addrs[j];

					addrs[j / 4] |= addr << ((j % 4) * 8);
				}

				if (j)
					cmd |= NDFC_SEND_ADR | NDFC_ADR_NUM(j);

				break;

			case NAND_OP_DATA_IN_INSTR:
			case NAND_OP_DATA_OUT_INSTR:

				cmd |= NDFC_DATA_TRANS;

				if (instr->type == NAND_OP_DATA_OUT_INSTR) {
					cmd |= NDFC_ACCESS_DIR;
				}

				if (instr->ctx.data.layout.type == ECC_LAYOUT_SEQUENCE) {
					cmd |= NDFC_SEQ;
				}

				if (instr->ctx.data.main.len != 0) {
					cmd |= NDFC_DATA_SWAP_METHOD;
					sunxi_ndfc_tsfer_data(); //wait ot achieve
				} else if (instr->ctx.data.user.len != 0) {
					cmd |= NDFC_DATA_SWAP_METHOD;
					sunxi_ndfc_tsfer_data();
				} else if (instr->ctx.data.other.len != 0) {
					cnt = instr->ctx.data.other.len;
				}
				break;

			case NAND_OP_WAITRDY_INSTR:
				cmd |= NDFC_WAIT_FLAG;
				break;
			}
		}
		if (opset->type == OP_CMD_NORMAL) {
			if ((ndfc_wait_cmdfifo_free(nctri) != 0) || (ndfc_wait_fsm_ready(nctri) != 0)) {
				rawnand_pr_err("normal op cmd send, wait cmd fifo free timeout!\n");
				return ERR_TIMEOUT;
			}
			cmd |= NDFC_CMD_NORMAL;
		} else if (opset->type == OP_CMD_BATCH) {
			cmd |= NDFC_CMD_BATCH;
			cmd |= ((nctri->random_addr_num << 8) & NDFC_ADR_NUM_IN_PAGECMD);
		}

		if (cmd & NDFC_SEND_ADR) {
			aw_writel(addrs[0], nctri->nreg.reg_addr_low);
			aw_writel(addrs[0], nctri->nreg.reg_addr_high);
		}

		if (cmd & NDFC_SEND_CMD2) {
			unsigned int val = 0;
			aw_readl(&val, (cmd & NDFC_ACCESS_DIR) ? nctri->nreg.reg_write_cmd_set : nctri->nreg.reg_read_cmd_set);

			val |= extcmd;
			aw_writel(&val, (cmd & NDFC_ACCESS_DIR) ? nctri->nreg.reg_write_cmd_set : nctri->nreg.reg_read_cmd_set);
		}

		if (cmd & NDFC_DATA_TRANS) {
			aw_writel(cnt, nctri->nreg.reg_ndfc_cnt)
		}

		if (!ndfc_wait_cmd_finish(nctri))
			aw_writel(cmd, nctri->nreg.reg_cmd);

		if (n == opset->ninstrs - 1) {
		}
	}

	if (cmd & NFC_SEND_CMD2)
		writel(extcmd,
		       nfc->regs +
			   (cmd & NFC_ACCESS_DIR ? NFC_REG_WCMD_SET : NFC_REG_RCMD_SET));

	if (cmd & NFC_DATA_TRANS)
		writel(cnt, nfc->regs + NFC_REG_CNT);

	writel(cmd, nfc->regs + NFC_REG_CMD);

	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG,
				    !(cmd & NFC_WAIT_FLAG) && cnt < 64,
				    0);
	if (ret)
		return ret;

	if (inbuf)
		memcpy_fromio(inbuf, nfc->regs + NFC_RAM0_BASE, cnt);

	return 0;
}

#endif
