
/*****************************************************************
**
**  Copyright (C) 2012 Amlogic,Inc.  All rights reserved
**
**        Filename : chip_operation.c
**        Revision : 1.001
**        Author: Benjamin Zhao
**        Description:
**          chip operation function,  contains read/write/erase, and bad block function.
**          mainly init nand phy driver.
**
*****************************************************************/
#include "../include/phynand.h"

static int check_cmdfifo_size(void)
{
    int time_out_cnt = 0, retry_cnt = 0;

RETRY:
    do {
        if (NFC_CMDFIFO_SIZE() <= 0)
            break;
    } while (time_out_cnt++ <= AML_DMA_BUSY_TIMEOUT);

    if (time_out_cnt >= AML_DMA_BUSY_TIMEOUT) {
        if (retry_cnt++ > 3) {
            aml_nand_msg("check cmdfifo size timeout over 3 times !!!!!");
            return -NAND_FAILED;
        }
        else{
            aml_nand_dbg("check cmdfifo size timeout retry_cnt:%d", retry_cnt);
            udelay(10);
            goto RETRY;
        }
    }

    return NAND_SUCCESS;
}
/************************************************************
 * nand_check_wp - [GENERIC] check if the chip is write protected
 * Check, if the chip is write protected.
 * First of all, check controller->option, then  read status, check nand status
 *
 *************************************************************/
static int check_wp(struct amlnand_chip *aml_chip)
{
    struct hw_controller *controller = &(aml_chip->controller);
    int i, time_out_cnt = 0;

    /* force WP for readonly add for shutdown protect */
    if (controller->option & NAND_CTRL_FORCE_WP) {
        aml_nand_msg("check force WP here");
        return -NAND_WP_FAILURE;
    }

    /* Check the WP bit */
    for (i = 0; i < controller->chip_num; i++) {
        controller->select_chip(controller, i);

        /* read status */
        controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE);
        NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWHR_TIME_CYCLE);

        NFC_SEND_CMD_IDLE(controller->chip_selected, 0);
        NFC_SEND_CMD_IDLE(controller->chip_selected, 0);
        do {
            if (NFC_CMDFIFO_SIZE() <= 0)
                break;
            udelay(2);
        } while (time_out_cnt++ <= AML_NAND_READ_BUSY_TIMEOUT);

        if (time_out_cnt >= AML_NAND_READ_BUSY_TIMEOUT) {
            aml_nand_msg("cmd fifo size failed to clear and NFC_CMDFIFO_SIZE():%d", NFC_CMDFIFO_SIZE());
            return -NAND_CMD_FAILURE;
        }

        if (controller->readbyte(controller) & NAND_STATUS_WP) {
            return -NAND_WP_FAILURE;
        }

    }

    return 0;
}


/************************************************************
 * read_page, all parameters saved in aml_chip->ops_para, refer to struct chip_ops_para define.
 * support read way of hwecc/raw, data/oob only, data+oob
 * for opteration mode, contains multi-plane/multi-chip
 *
 *************************************************************/
static int read_page(struct amlnand_chip *aml_chip)
{
    struct hw_controller *controller = &(aml_chip->controller);
    struct nand_flash *flash = &(aml_chip->flash);
    struct chip_ops_para *ops_para = &(aml_chip->ops_para);
    struct read_retry_info *retry_info = &(controller->retry_info);
    struct en_slc_info *slc_info = &(controller->slc_info);
    unsigned char *buf = ops_para->data_buf;
    unsigned char *oob_buf = ops_para->oob_buf;
    unsigned char *tmp_buf = controller->oob_buf;
    unsigned plane_page_addr, plane_blk_addr, pages_per_blk_shift, page_size, tmp_page;
    unsigned plane0_page_addr, plane1_page_addr, column, page_addr;
    unsigned char i, bch_mode, plane_num, chip_num, chipnr, user_byte_num;
    unsigned char with_oob, with_data, plane0_retry_flag = 0, all_ff_flag = 0;
    unsigned char need_retry, retry_cnt, up_page = 0, slc_mode;
    int ooblen, ret = 0;
    int new_oob = 0;
    int retry_op_cnt = retry_info->retry_cnt_lp;
    /* aml_nand_dbg("start"); */
    if ((!buf) && (!oob_buf)) {
        aml_nand_msg("buf & oob_buf should never be NULL");
        ret = -NAND_ARGUMENT_FAILURE;
        goto error_exit0;
    }
    if ((flash->new_type == HYNIX_20NM_8GB) || (flash->new_type == HYNIX_20NM_4GB) || (flash->new_type == HYNIX_1YNM))
         retry_op_cnt = retry_info->retry_cnt_lp * retry_info->retry_cnt_lp;
    if ((flash->new_type == SANDISK_A19NM) || (flash->new_type == SANDISK_A19NM_4G))
         retry_op_cnt = retry_info->retry_cnt_lp * 4;
    user_byte_num = chipnr = 0;
    plane0_page_addr = plane1_page_addr = 0;

    page_addr = ops_para->page_addr;
    controller->page_addr = ops_para->page_addr;
    pages_per_blk_shift =  (controller->block_shift - controller->page_shift);
    if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
        controller->page_addr -= controller->internal_page_nums;
        controller->page_addr |= controller->internal_page_nums * aml_chip->flash.internal_chipnr;
    }

    with_oob = 0;
    with_data = 0;
    if (oob_buf) {
        with_oob = 1;
    }

    if (buf) {
        with_data = 1;
    }

    if (controller->oob_mod && (with_oob == 1) && (with_data == 0)) {
        new_oob = 1;
    }
    /* for multi-chip mode */
    if (ops_para->option & DEV_MULTI_CHIP_MODE) {
        chip_num = controller->chip_num;
    }
    else{
        chip_num = 1;
    }

     if (ops_para->option & DEV_SLC_MODE) {
        /* aml_nand_dbg("enable SLC mode"); */
         if (flash->new_type == SANDISK_19NM) {
             slc_mode = 1;
         }
         else if ((flash->new_type > 0) && (flash->new_type < 10)) {
             slc_mode = 2;
            /* page_addr = slc_info->pagelist[page_addr]; */
         }
         else{
             slc_mode = 0;
         }
     }
     else{
        slc_mode = 0;
         if (flash->new_type == SANDISK_19NM) {
             tmp_page = page_addr % (1 << pages_per_blk_shift);
             if (((tmp_page % 2 == 0) && (tmp_page != 0)) || (tmp_page == ((1 << pages_per_blk_shift)-1))) {
                up_page = 1;
             }
             else{
                up_page = 0;
             }
         }
     }

    /* for multi-plane mode */
    if (ops_para->option & DEV_MULTI_PLANE_MODE) {
        plane_num = 2;
        plane_page_addr = (controller->page_addr & (((1 << pages_per_blk_shift) - 1)));
        plane_blk_addr = (controller->page_addr >> (pages_per_blk_shift));
        plane_blk_addr <<= 1;
        plane0_page_addr = (plane_blk_addr << pages_per_blk_shift) | plane_page_addr;
        plane1_page_addr = ((plane_blk_addr + 1) << pages_per_blk_shift) | plane_page_addr;
    }
    else{
        plane_num = 1;
    }

    /* for ecc mode */
    if ((ops_para->option & DEV_ECC_SOFT_MODE) || (controller->bch_mode == NAND_ECC_NONE)) {
        bch_mode = NAND_ECC_NONE;
        page_size = flash->pagesize + flash->oobsize;
        user_byte_num = flash->oobsize;
    }
    else{
        bch_mode = controller->bch_mode;
        user_byte_num = controller->ecc_steps*controller->user_mode;
        page_size = controller->ecc_steps*controller->ecc_unit;
    }

    if (new_oob) {
        page_size = controller->ecc_unit;
    }
    ooblen = user_byte_num * chip_num;
    ooblen *= plane_num;

    if (ops_para->ooblen > ooblen) {
        aml_nand_msg("check ops_para->ooblen:%d over size to chip ooblen:%d", ops_para->ooblen, ooblen);
    }
    else if (ops_para->ooblen) {
        ooblen = ops_para->ooblen;
    }

    column = 0;
/* send read command */
    for (i = 0; i < chip_num; i++) {

        chipnr = (chip_num > 1) ? i : ops_para->chipnr;
        /* setting retry_cnt to 0 */
        retry_cnt = 0;

        ret = controller->quene_rb(controller, chipnr);
        if (ret) {
            aml_nand_msg("quene rb busy here");
            if (retry_cnt && retry_info->exit) {
                ret = retry_info->exit(controller, chipnr);
                if (ret < 0) {
                    printk("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                }
            }
            goto error_exit0;
        }

        if (plane_num == 2) {

RETRY_PLANE_CMD:
            if ((controller->mfr_type == NAND_MFR_MICRON) || (controller->mfr_type == NAND_MFR_INTEL)) {
                /* plane0 */
                controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE);
                controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane0_page_addr, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane0_page_addr>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane0_page_addr>>16, NAND_CTRL_ALE);

                controller->cmd_ctrl(controller, 0x32, NAND_CTRL_CLE);
                NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);
                ret = controller->quene_rb(controller, chipnr);
                if (ret) {
                    aml_nand_msg("send 0x32 cmd Rb  failed\n");
                }

                /* plane1 */
                controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE);
                controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr>>16, NAND_CTRL_ALE);
                /* read start */
                controller->cmd_ctrl(controller, NAND_CMD_READSTART, NAND_CTRL_CLE);

                /* NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE); */
            }
            else{
                /* plane0 */
                controller->cmd_ctrl(controller, NAND_CMD_TWOPLANE_PREVIOS_READ, NAND_CTRL_CLE);
                controller->cmd_ctrl(controller, plane0_page_addr, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane0_page_addr>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane0_page_addr>>16, NAND_CTRL_ALE);
                /* plane1 */
                controller->cmd_ctrl(controller, NAND_CMD_TWOPLANE_PREVIOS_READ, NAND_CTRL_CLE);
                controller->cmd_ctrl(controller, plane1_page_addr, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr>>16, NAND_CTRL_ALE);
                /* read start */
                controller->cmd_ctrl(controller, NAND_CMD_READSTART, NAND_CTRL_CLE);

                /* NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE); */
            }

        }
        else{
            if (slc_mode == 1) {
                ret = slc_info->enter(controller);
                if (ret < 0) {
                    aml_nand_msg("slc enter failed here");
                }
            }
RETRY_CMD:
            controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE);
            controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, controller->page_addr, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, controller->page_addr>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, controller->page_addr>>16, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, NAND_CMD_READSTART, NAND_CTRL_CLE);

            /* NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE); */
        }

        /* wait twb here */
        NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);

        if (check_cmdfifo_size()) {
            aml_nand_msg("check cmdfifo size timeout");
            BUG();
        }
        if (retry_cnt) {
            goto RETRY_DMA;
        }
    }

/* check rb and dma and ecc correct */
    for (i = 0; i < chip_num; i++) {

        chipnr = (chip_num > 1) ? i : ops_para->chipnr;
        retry_cnt = 0;
RETRY_DMA:
        ret = controller->quene_rb(controller, chipnr);
        if (ret) {
            aml_nand_msg("quene rb busy here");
            if (retry_cnt && retry_info->exit) {
                ret = retry_info->exit(controller, chipnr);
                if (ret < 0) {
                    printk("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                }
            }
            goto error_exit0;
        }

         if (controller->option & NAND_CTRL_NONE_RB) {
            controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE);
            NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);
        }

        if (plane_num == 2) {
            if ((controller->mfr_type == NAND_MFR_MICRON) || (controller->mfr_type == NAND_MFR_INTEL)) {
                   if (plane0_retry_flag == 0) {
                    controller->page_addr = plane0_page_addr;
                    ret = controller->dma_read(controller, page_size, bch_mode);
                    if (ret) {
                        aml_nand_msg("quene rb busy here");
                        BUG();
                        goto error_exit0;
                    }

                    all_ff_flag = 0;
                    /* for ecc soft mode */
                    if (bch_mode != NAND_ECC_NONE) {
                        controller->get_usr_byte(controller, tmp_buf, user_byte_num);
                        ret = controller->hwecc_correct(controller, page_size, tmp_buf);
                        if (ret ==  NAND_ECC_FAILURE) {
                            /* check rand_mode and 0xff page */
                            if (controller->zero_cnt < controller->ecc_max) {
                                all_ff_flag = 1;
                                aml_nand_dbg("found all 0xff page here");
                            if (retry_cnt && retry_info->exit) {
                                ret = retry_info->exit(controller, chipnr);
                                if (ret < 0) {
                                    printk("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                                }
                            }
                                goto plane0_ff_m;
                            }
    /*  */
                        need_retry = 0;
                        if (retry_info->flag && (slc_mode == 0)) {
                            if (flash->new_type != SANDISK_19NM) {
                                if (retry_cnt++ < retry_op_cnt)
                                    need_retry = 1;
                            }
                            else if (flash->new_type == SANDISK_19NM) {
                                if (up_page) {
                                    if (retry_cnt++ < retry_info->retry_cnt_up) {
                                        need_retry = 1;
                                    }
                                }
                                else if (retry_cnt++ < retry_info->retry_cnt_lp) {
                                    need_retry = 1;
                                }
                            }
                            if (need_retry) {
                                aml_nand_dbg("read retry at chip %d plane0 page %d and retry_cnt: %d", chipnr, plane0_page_addr, retry_cnt);
                                ret = retry_info->handle(controller, chipnr);
                                if (ret < 0) {
                                    aml_nand_msg("read retry handle failed at plane0 page:%d", plane0_page_addr);
                                }
                                goto RETRY_PLANE_CMD;
                            }
                        }
    /*  */
                            aml_nand_msg("#####NAND uncorrect ecc here at page:%d, plane 0 chip:%d", page_addr, chipnr);
                            ops_para->ecc_err++;
                        }
                        else if (ret < 0) {
                            aml_nand_msg("hwecc_correct failed ");
                            BUG();
                            goto error_exit0;
                        }
                        else {
                            if (retry_cnt) {
                                if (flash->new_type != SANDISK_19NM) {
                                    if (retry_cnt > (retry_op_cnt-2)) {
                                        aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                        ops_para->bit_flip++;
                                    }
                                }
                                else{
                                    if (up_page) {
                                        if (retry_cnt > (retry_info->retry_cnt_up-2)) {
                                            aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                            ops_para->bit_flip++;
                                        }
                                    }
                                    else{
                                        if (retry_cnt > (retry_info->retry_cnt_lp - 2)) {
                                            aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                            ops_para->bit_flip++;
                                        }
                                    }
                                }
                            }
                            else if ((controller->ecc_cnt_cur > controller->ecc_cnt_limit) && (flash->new_type == 0)) {
                                aml_nand_dbg("detect bitflip at  page:%d, chip:%d", page_addr, chipnr);
                                ops_para->bit_flip++;
                            }
                        }
                        if (retry_cnt && retry_info->exit) {
                            ret = retry_info->exit(controller, chipnr);
                            if (ret < 0) {
                                aml_nand_msg("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                            }
                        }
                            plane0_retry_flag = 1;
                            retry_cnt = 0;
                    }
plane0_ff_m:
                    if (with_data) {
                        if (all_ff_flag) {
                            memset(buf, 0xff, page_size);
                        }
                        else{
                            memcpy(buf, controller->data_buf, page_size);
                        }
                        /* aml_nand_dbg("read data %x %x %x %x %x %x %x %x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); */
                        /* aml_nand_dbg("read oob %x %x %x %x", oob_buf[0], oob_buf[1], oob_buf[2], oob_buf[3]); */
                        buf += page_size;
                    }

                    if (with_oob) {
                        if (all_ff_flag) {
                            memset(tmp_buf, 0xff, user_byte_num);
                        }
                        /* memcpy(ops_para->oob_buf, tmp_buf, user_byte_num); */
                        /* aml_nand_dbg("read oob %x %x %x %x", oob_buf[0], oob_buf[1], oob_buf[2], oob_buf[3]); */
                        /* oob_buf += user_byte_num; */
                        /* ops_para->oob_buf += (BYTES_OF_USER_PER_PAGE / (controller->chip_num*plane_num)) ; */
                        tmp_buf += user_byte_num;
                    }

                    all_ff_flag = 0;
                }
                if (new_oob) {
                    if (retry_cnt && retry_info->exit) {
                        ret = retry_info->exit(controller, chipnr);
                        if (ret < 0) {
                            printk("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                        }
                    }
                    goto new_oob_mod;
                }
                controller->cmd_ctrl(controller, NAND_CMD_PLANE2_READ_START, NAND_CTRL_CLE);
                controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr>>16, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE);

                NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWHR_TIME_CYCLE);

                controller->page_addr = plane1_page_addr;
                ret = controller->dma_read(controller, page_size, bch_mode);
                if (ret) {
                    aml_nand_msg("dma error here");
                    BUG();
                    goto error_exit0;
                }

                if (bch_mode != NAND_ECC_NONE) {
                    controller->get_usr_byte(controller, tmp_buf, user_byte_num);
                    ret = controller->hwecc_correct(controller, page_size, tmp_buf);
                    if (ret == NAND_ECC_FAILURE) {
                        /* check rand_mode and 0xff page */
                        if (controller->zero_cnt < controller->ecc_max) {
                            all_ff_flag = 1;
                            aml_nand_dbg("found all 0xff page here");
                        if (retry_cnt && retry_info->exit) {
                            ret = retry_info->exit(controller, chipnr);
                            if (ret < 0) {
                                printk("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                            }
                        }
                            goto page_ff;
                        }
/*  */
                        need_retry = 0;
                        if (retry_info->flag && (slc_mode == 0)) {
                            if (flash->new_type != SANDISK_19NM) {
                                if (retry_cnt++ < retry_op_cnt)
                                    need_retry = 1;
                            }
                            else if (flash->new_type == SANDISK_19NM) {
                                if (up_page) {
                                    if (retry_cnt++ < retry_info->retry_cnt_up) {
                                        need_retry = 1;
                                    }
                                }
                                else if (retry_cnt++ < retry_info->retry_cnt_lp) {
                                    need_retry = 1;
                                }
                            }
                            if (need_retry) {
                                aml_nand_dbg("read retry at chip %d plane1 page %d and retry_cnt: %d", chipnr, plane1_page_addr, retry_cnt);
                                ret = retry_info->handle(controller, chipnr);
                                if (ret < 0) {
                                    aml_nand_msg("read retry handle failed at plane1 page:%d", plane1_page_addr);
                                }
                                goto RETRY_PLANE_CMD;
                            }
                        }
/*  */
                        aml_nand_msg("####uncorrect ecc here at page:%d plane 1 chip:%d", page_addr, chipnr);
                        ops_para->ecc_err++;
                        if (retry_info->flag && (flash->new_type) && (flash->new_type < 10)) {
                            ret = nand_reset(aml_chip, chipnr);
                            if (ret < 0) {
                                   aml_nand_msg("reset failed after retry failed at chip %d", chipnr);
                            }
                        }
                    }
                    else if (ret < 0) {
                        aml_nand_msg("failed ");
                        BUG();
                        goto error_exit0;
                    }
                    else {
                        if (retry_cnt) {
                            if (flash->new_type != SANDISK_19NM) {
                                if (retry_cnt > (retry_op_cnt-2)) {
                                    aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                    ops_para->bit_flip++;
                                }
                            }
                            else{
                                if (up_page) {
                                    if (retry_cnt > (retry_info->retry_cnt_up-2)) {
                                        aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                        ops_para->bit_flip++;
                                    }
                                }
                                else{
                                    if (retry_cnt > (retry_info->retry_cnt_lp - 2)) {
                                        aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                        ops_para->bit_flip++;
                                    }
                                }
                            }
                        }
                        else if ((controller->ecc_cnt_cur > controller->ecc_cnt_limit) && (flash->new_type == 0)) {
                            aml_nand_dbg("detect bitflip at  page:%d, chip:%d", page_addr, chipnr);
                            ops_para->bit_flip++;
                        }
                    }
                    if (retry_cnt && retry_info->exit) {
                        ret = retry_info->exit(controller, chipnr);
                        if (ret < 0) {
                            aml_nand_msg("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                        }
                    }
                    plane0_retry_flag = 0;
                    retry_cnt = 0;
                }
            }
            else{
                if (plane0_retry_flag == 0) {
                    controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE);
                    controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
                    controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
                    controller->cmd_ctrl(controller, plane0_page_addr, NAND_CTRL_ALE);
                    controller->cmd_ctrl(controller, plane0_page_addr>>8, NAND_CTRL_ALE);
                    controller->cmd_ctrl(controller, plane0_page_addr>>16, NAND_CTRL_ALE);
                    controller->cmd_ctrl(controller, NAND_CMD_RNDOUT, NAND_CTRL_CLE);
                    controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
                    controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
                    controller->cmd_ctrl(controller, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE);

                    NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWHR_TIME_CYCLE);

                    controller->page_addr = plane0_page_addr;
                    ret = controller->dma_read(controller, page_size, bch_mode);
                    if (ret) {
                        aml_nand_msg("dma error here");
                        BUG();
                        goto error_exit0;
                    }

                    if (bch_mode != NAND_ECC_NONE) {
                        controller->get_usr_byte(controller, tmp_buf, user_byte_num);
                        ret = controller->hwecc_correct(controller, page_size, tmp_buf);
                        if (ret == NAND_ECC_FAILURE) {
                            /* check rand_mode and 0xff page */
                            if (controller->zero_cnt < controller->ecc_max) {
                                all_ff_flag = 1;
                                aml_nand_dbg("found all 0xff page here");
                            if (retry_cnt && retry_info->exit) {
                                ret = retry_info->exit(controller, chipnr);
                                if (ret < 0) {
                                    printk("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                                }
                            }
                                goto plane0_ff_h;
                            }
    /*  */
                            need_retry = 0;
                            if (retry_info->flag && (slc_mode == 0)) {
                                if (flash->new_type != SANDISK_19NM) {
                                    if (retry_cnt++ < retry_op_cnt)
                                        need_retry = 1;
                                }
                                else if (flash->new_type == SANDISK_19NM) {
                                    if (up_page) {
                                        if (retry_cnt++ < retry_info->retry_cnt_up) {
                                            need_retry = 1;
                                        }
                                    }
                                    else if (retry_cnt++ < retry_info->retry_cnt_lp) {
                                        need_retry = 1;
                                    }
                                }
                                if (need_retry) {
                                    aml_nand_dbg("read retry at chip %d plane0 page %d and retry_cnt: %d", chipnr, plane0_page_addr, retry_cnt);
                                    ret = retry_info->handle(controller, chipnr);
                                    if (ret < 0) {
                                        aml_nand_msg("read retry handle failed at plane0 page:%d", plane0_page_addr);
                                    }
                                    goto RETRY_PLANE_CMD;
                                }
                            }
    /*  */
                            aml_nand_msg("####uncorrect ecc here at page:%d chip:%d", page_addr, chipnr);
                            ops_para->ecc_err++;
                        }
                        else if (ret < 0) {
                            aml_nand_msg("failed");
                            BUG();
                            goto error_exit0;
                        }
                        else {
                            if (retry_cnt) {
                                if (flash->new_type != SANDISK_19NM) {
                                    if (retry_cnt > (retry_op_cnt-2)) {
                                        aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                        ops_para->bit_flip++;
                                    }
                                }
                                else{
                                    if (up_page) {
                                        if (retry_cnt > (retry_info->retry_cnt_up-2)) {
                                            aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                            ops_para->bit_flip++;
                                        }
                                    }
                                    else{
                                        if (retry_cnt > (retry_info->retry_cnt_lp - 2)) {
                                            aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                            ops_para->bit_flip++;
                                        }
                                    }
                                }
                            }
                            else if ((controller->ecc_cnt_cur > controller->ecc_cnt_limit) && (flash->new_type == 0)) {
                                aml_nand_dbg("detect bitflip at  page:%d, chip:%d", page_addr, chipnr);
                                ops_para->bit_flip++;
                            }
                        }
                        if (retry_cnt && retry_info->exit) {
                            ret = retry_info->exit(controller, chipnr);
                            if (ret < 0) {
                                aml_nand_msg("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                            }
                        }
                            plane0_retry_flag = 1;
                            retry_cnt = 0;
                    }
plane0_ff_h:
                    if (with_data) {
                        if (all_ff_flag) {
                            memset(buf, 0xff, page_size);
                        }
                        else{
                            memcpy(buf, controller->data_buf, page_size);
                        }
                        /* aml_nand_dbg("read data %x %x %x %x", buf[0], buf[1], buf[2], buf[3]); */
                        /* aml_nand_dbg("read oob %x %x %x %x", oob_buf[0], oob_buf[1], oob_buf[2], oob_buf[3]); */
                        buf += page_size;
                    }

                    if (with_oob) {
                        if (all_ff_flag) {
                            memset(tmp_buf, 0xff, user_byte_num);
                        }
                        /* memcpy(ops_para->oob_buf, tmp_buf, BYTES_OF_USER_PER_PAGE); */
                        /* aml_nand_dbg("read oob %x %x %x %x", oob_buf[0], oob_buf[1], oob_buf[2], oob_buf[3]); */
                        /* oob_buf += user_byte_num; */
                        tmp_buf += user_byte_num;
                        /* ops_para->oob_buf += (BYTES_OF_USER_PER_PAGE / (controller->chip_num*plane_num)) ; */
                    }

                    all_ff_flag = 0;
                }

                controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE);
                controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, plane1_page_addr>>16, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, NAND_CMD_RNDOUT, NAND_CTRL_CLE);
                controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
                controller->cmd_ctrl(controller, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE);

                NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWHR_TIME_CYCLE);

                controller->page_addr = plane1_page_addr;
                ret = controller->dma_read(controller, page_size, bch_mode);
                if (ret) {
                    aml_nand_msg("dma error here");
                    BUG();
                    goto error_exit0;
                }

                if (bch_mode != NAND_ECC_NONE) {
                    controller->get_usr_byte(controller, tmp_buf, user_byte_num);
                    ret = controller->hwecc_correct(controller, page_size, tmp_buf);
                    if (ret == NAND_ECC_FAILURE) {
                        /* check rand_mode and 0xff page */
                        if (controller->zero_cnt < controller->ecc_max) {
                            all_ff_flag = 1;
                            aml_nand_dbg("found all 0xff page here");
                        if (retry_cnt && retry_info->exit) {
                            ret = retry_info->exit(controller, chipnr);
                            if (ret < 0) {
                                printk("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                            }
                        }
                            goto page_ff;
                        }
/*  */
                        need_retry = 0;
                        if (retry_info->flag && (slc_mode == 0)) {
                            if (flash->new_type != SANDISK_19NM) {
                                if (retry_cnt++ < retry_op_cnt)
                                    need_retry = 1;
                            }
                            else if (flash->new_type == SANDISK_19NM) {
                                if (up_page) {
                                    if (retry_cnt++ < retry_info->retry_cnt_up) {
                                        need_retry = 1;
                                    }
                                }
                                else if (retry_cnt++ < retry_info->retry_cnt_lp) {
                                    need_retry = 1;
                                }
                            }
                            if (need_retry) {
                                aml_nand_dbg("read retry at chip %d plane1 page %d and retry_cnt: %d", chipnr, plane1_page_addr, retry_cnt);
                                ret = retry_info->handle(controller, chipnr);
                                if (ret < 0) {
                                    aml_nand_msg("read retry handle failed at plane1 page:%d", plane1_page_addr);
                                }
                                aml_nand_dbg("plane0_retry_flag %d", plane0_retry_flag);
                                goto RETRY_PLANE_CMD;
                            }
                        }
/*  */
                        aml_nand_msg("uncorrect ecc here at page:%d", page_addr);
                        ops_para->ecc_err++;
                        if (retry_info->flag && (flash->new_type) && (flash->new_type < 10)) {
                            ret = nand_reset(aml_chip, chipnr);
                            if (ret < 0) {
                                   aml_nand_msg("reset failed after retry failed at chip %d", chipnr);
                            }
                        }
                    }
                    else if (ret) {
                        aml_nand_msg("failed ");
                        BUG();
                        goto error_exit0;
                    }
                    else {
                        if (retry_cnt) {
                            if (flash->new_type != SANDISK_19NM) {
                                if (retry_cnt > (retry_op_cnt-2)) {
                                    aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                    ops_para->bit_flip++;
                                }
                            }
                            else{
                                if (up_page) {
                                    if (retry_cnt > (retry_info->retry_cnt_up-2)) {
                                        aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                        ops_para->bit_flip++;
                                    }
                                }
                                else{
                                    if (retry_cnt > (retry_info->retry_cnt_lp - 2)) {
                                        aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                        ops_para->bit_flip++;
                                    }
                                }
                            }
                        }
                        else if ((controller->ecc_cnt_cur > controller->ecc_cnt_limit) && (flash->new_type == 0)) {
                            aml_nand_dbg("detect bitflip at  page:%d, chip:%d", page_addr, chipnr);
                            ops_para->bit_flip++;
                        }
                    }
                }
                if (retry_cnt && retry_info->exit) {
                    ret = retry_info->exit(controller, chipnr);
                    if (ret < 0) {
                        aml_nand_msg("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                    }
                }
                plane0_retry_flag = 0;
                retry_cnt = 0;
            }
        }
        else{
            /* aml_nand_dbg("page_addr:%d", ops_para->page_addr); */
            ret = controller->dma_read(controller, page_size, bch_mode);
            if (ret) {
                aml_nand_msg("dma error here");
                BUG();
                goto error_exit0;
            }

            if (bch_mode != NAND_ECC_NONE) {
                controller->get_usr_byte(controller, tmp_buf, user_byte_num);
                ret = controller->hwecc_correct(controller, page_size, tmp_buf);
                if (ret == NAND_ECC_FAILURE) {
                    /* check rand_mode and 0xff page */
                    if (controller->zero_cnt < controller->ecc_max) {
                        all_ff_flag = 1;
                        aml_nand_dbg("found all 0xff page here");
            if (retry_cnt && retry_info->exit) {
                ret = retry_info->exit(controller, chipnr);
                if (ret < 0) {
                printk("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                }
            }
                        goto page_ff;
                    }
                    need_retry = 0;
                    if (retry_info->flag && (slc_mode == 0)) {
                        if (flash->new_type != SANDISK_19NM) {
                            if (retry_cnt++ < retry_op_cnt)
                                need_retry = 1;
                        }
                        else if (flash->new_type == SANDISK_19NM) {
                            if (up_page) {
                                if (retry_cnt++ < retry_info->retry_cnt_up) {
                                    need_retry = 1;
                                }
                            }
                            else if (retry_cnt++ < retry_info->retry_cnt_lp) {
                                need_retry = 1;
                            }
                        }

                        if (need_retry) {
                            aml_nand_dbg("read retry at chip %d page %d and retry_cnt: %d", chipnr, page_addr, retry_cnt);
                            ret = retry_info->handle(controller, chipnr);
                            if (ret < 0) {
                                aml_nand_msg("read retry handle failed at page:%d", page_addr);
                            }
                            goto RETRY_CMD;
                        }
                    }
                    aml_nand_msg("uncorrect ecc here at page:%d, chip:%d", page_addr, chipnr);
                    ops_para->ecc_err++;
                    if (retry_info->flag && (flash->new_type) && (flash->new_type < 10)) {
                        ret = nand_reset(aml_chip, chipnr);
                        if (ret < 0) {
                               aml_nand_msg("reset failed after retry failed at chip %d", chipnr);
                        }
                    }

                }
                else if (ret < 0) {
                    aml_nand_msg("failed ");
                    BUG();
                    goto error_exit0;
                }
                else{
                    if (retry_cnt) {
                        if (flash->new_type != SANDISK_19NM) {
                            if (retry_cnt > (retry_op_cnt-2)) {
                                aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                ops_para->bit_flip++;
                            }
                        }
                        else{
                            if (up_page) {
                                if (retry_cnt > (retry_info->retry_cnt_up-2)) {
                                    aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                    ops_para->bit_flip++;
                                }
                            }
                            else{
                                if (retry_cnt > (retry_info->retry_cnt_lp - 2)) {
                                    aml_nand_dbg("detect bitflip at page:%d", page_addr);
                                    ops_para->bit_flip++;
                                }
                            }
                        }
                    }
                    else if ((controller->ecc_cnt_cur > controller->ecc_cnt_limit) && (flash->new_type == 0)) {
                        aml_nand_dbg("detect bitflip at  page:%d, chip:%d , controller->ecc_cnt_cur %d ,controller->ecc_cnt_limit %d", page_addr, chipnr, controller->ecc_cnt_cur , controller->ecc_cnt_limit);
                        ops_para->bit_flip++;
                    }
                }
                if (retry_cnt && retry_info->exit) {
                    ret = retry_info->exit(controller, chipnr);
                    if (ret < 0) {
                        aml_nand_msg("retry exit failed flash->new_type:%d, retry_cnt:%d", flash->new_type, retry_cnt);
                    }
                }
            }
        }
page_ff:
        if (with_data) {
            if (all_ff_flag) {
                memset(buf, 0xff, page_size);
            }
            else{
                memcpy(buf, controller->data_buf, page_size);
            }
            /* aml_nand_dbg("read data %x %x %x %x %x %x %x %x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); */
            /* aml_nand_dbg("read oob %x %x %x %x", oob_buf[0], oob_buf[1], oob_buf[2], oob_buf[3]); */
            buf += page_size;
        }

        if (with_oob) {
            if (all_ff_flag) {
                memset(tmp_buf, 0xff, user_byte_num);
            }
            /* memcpy( ops_para->oob_buf, tmp_buf, BYTES_OF_USER_PER_PAGE); */
        /* aml_nand_dbg("read oob %x %x %x %x", oob_buf[0], oob_buf[1], oob_buf[2], oob_buf[3]); */
            /* oob_buf += user_byte_num; */

            tmp_buf += user_byte_num;
        }

        all_ff_flag = 0;

        if (check_cmdfifo_size()) {
            aml_nand_msg("check cmdfifo size timeout");
            BUG();
        }

    }
new_oob_mod:
    if (with_oob) {
        memcpy(ops_para->oob_buf, controller->oob_buf, BYTES_OF_USER_PER_PAGE);
        if (ops_para->ecc_err)
            memset(ops_para->oob_buf, 0x22, BYTES_OF_USER_PER_PAGE);
    }


    return NAND_SUCCESS;

error_exit0:
    return ret;
}


/************************************************************
 * write_page, all parameters saved in aml_chip->ops_para.
 * support read way of hwecc/raw, data/oob only, data+oob
 * for opteration mode, contains multi-plane/multi-chip
 *
 *************************************************************/
static int write_page(struct amlnand_chip *aml_chip)
{
    struct hw_controller *controller = &(aml_chip->controller);
    struct nand_flash *flash = &(aml_chip->flash);
    struct chip_ops_para *ops_para = &(aml_chip->ops_para);
    struct en_slc_info *slc_info = &(controller->slc_info);
    unsigned char *buf = ops_para->data_buf;
    unsigned char *oob_buf = ops_para->oob_buf;
    unsigned plane_page_addr, plane_blk_addr, pages_per_blk_shift, page_size;
    unsigned plane0_page_addr, plane1_page_addr, column, page_addr;
    unsigned char i, bch_mode, plane_num, chip_num, chipnr, user_byte_num;
    unsigned char slc_mode, status, st_cnt;
    int ret = 0;

    if (!buf) {
        aml_nand_msg("buf should never be NULL");
        ret = -NAND_ARGUMENT_FAILURE;
        goto error_exit0;
    }
    if (aml_chip->nand_status  != NAND_STATUS_NORMAL) {
        aml_nand_msg("nand status unusal: do not write anything!!!!!");
        return NAND_SUCCESS;
    }

    user_byte_num = chipnr = 0;
    plane0_page_addr = plane1_page_addr = 0;

    /* for multi-chip mode */
    if (ops_para->option & DEV_MULTI_CHIP_MODE) {
        chip_num = controller->chip_num;
        /* aml_nand_dbg("DEV_MULTI_CHIP_MODE"); */
    }
    else{
        chip_num = 1;
    }

    if (ops_para->option & DEV_SLC_MODE) {
        /* aml_nand_dbg("enable SLC mode"); */
        if (flash->new_type == SANDISK_19NM) {
            slc_mode = 1;
        }
        else if ((flash->new_type > 0) && (flash->new_type < 10)) {
            slc_mode = 2;
            /* page_addr = slc_info->pagelist[page_addr]; */
        }
        else{
            slc_mode = 0;
        }
    }
    else{
        slc_mode = 0;
    }

    /* for multi-plane mode */
    page_addr = ops_para->page_addr;
    controller->page_addr = ops_para->page_addr;
    pages_per_blk_shift =  (controller->block_shift - controller->page_shift);
    if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
        controller->page_addr -= controller->internal_page_nums;
        controller->page_addr |= controller->internal_page_nums * aml_chip->flash.internal_chipnr;
    }
    if (ops_para->option & DEV_MULTI_PLANE_MODE) {
        plane_num = 2;
        plane_page_addr = (controller->page_addr & (((1 << pages_per_blk_shift) - 1)));
        plane_blk_addr = (controller->page_addr >> (pages_per_blk_shift));
        plane_blk_addr <<= 1;
        plane0_page_addr = (plane_blk_addr << pages_per_blk_shift) | plane_page_addr;
        plane1_page_addr = ((plane_blk_addr + 1) << pages_per_blk_shift) | plane_page_addr;

#if 0
        aml_nand_dbg("ops_para->page_addr =%d", ops_para->page_addr);
        aml_nand_dbg("controller->page_addr =%d", controller->page_addr);
        aml_nand_dbg("plane_page_addr =%d", plane_page_addr);
        aml_nand_dbg("plane_blk_addr =%d", plane_blk_addr);
        aml_nand_dbg("plane0_page_addr =%d", plane0_page_addr);
        aml_nand_dbg("plane1_page_addr =%d", plane1_page_addr);
#endif
    }
    else{
        plane_num = 1;
    }

    /* for ecc mode */
    if ((ops_para->option & DEV_ECC_SOFT_MODE) || (controller->bch_mode == NAND_ECC_NONE)) {
        bch_mode = NAND_ECC_NONE;
        page_size = flash->pagesize + flash->oobsize;
    }
    else{
        bch_mode = controller->bch_mode;
        user_byte_num = controller->ecc_steps*controller->user_mode;
        page_size = controller->ecc_steps*controller->ecc_unit;

        /*if (controller->bch_mode == NAND_ECC_BCH_SHORT){
            page_size = (flash->pagesize / 512) * NAND_ECC_UNIT_SHORT;
        }*/

        if (!oob_buf) {
            memset(controller->oob_buf, 0xa5, (user_byte_num*(plane_num*chip_num)));
            controller->oob_buf[0] = 0xff;
            oob_buf = controller->oob_buf;
        } else{
            memcpy(controller->oob_buf,  ops_para->oob_buf, (user_byte_num*(plane_num*chip_num)));
            oob_buf = controller->oob_buf;
        }

    }
#if 0
    aml_nand_dbg("page_addr:%d, buf: %x %x %x %x %x %x %x %x", \
        ops_para->page_addr, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);

    aml_nand_dbg("page_addr:%d, oob_buf: %x %x %x %x %x %x %x %x", \
        ops_para->page_addr, oob_buf[0], oob_buf[1], oob_buf[2], oob_buf[3], oob_buf[4], oob_buf[5], oob_buf[6], oob_buf[7]);
#endif
    column = 0;

    for (i = 0; i < chip_num; i++) {

        chipnr = (chip_num > 1) ? i : ops_para->chipnr;

        ret = controller->quene_rb(controller, chipnr);
        if (ret) {
            aml_nand_msg("quene rb busy here");
            goto error_exit0;
        }
#if 0
        if (aml_chip->debug_num == 1)
            aml_nand_dbg("chipnr =%d, chip_num  =%d", chipnr, chip_num);
            aml_nand_dbg("controller->chip_selected =%d", controller->chip_selected);
#endif

        if (plane_num == 2) {
            /* plane0 */
            controller->cmd_ctrl(controller, NAND_CMD_SEQIN, NAND_CTRL_CLE);
            controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane0_page_addr, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane0_page_addr>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane0_page_addr>>16, NAND_CTRL_ALE);

            if (bch_mode != NAND_ECC_NONE) {
                controller->set_usr_byte(controller, oob_buf, user_byte_num);
            }

            controller->page_addr = plane0_page_addr;
            ret = controller->dma_write(controller, buf, page_size, bch_mode);
            if (ret) {
                aml_nand_msg("dma error here");
                goto error_exit0;
            }

#if  0
                    if (aml_chip->debug_num == 1) {
                        aml_nand_dbg("ops_para->page_addr =%d", ops_para->page_addr);
                        aml_nand_dbg("controller->page_addr =%d", controller->page_addr);
                        aml_nand_dbg("plane_page_addr =%d", plane_page_addr);
                        aml_nand_dbg("plane_blk_addr =%d", plane_blk_addr);
                        aml_nand_dbg("plane0_page_addr =%d", plane0_page_addr);
                        aml_nand_dbg("plane1_page_addr =%d", plane1_page_addr);

                    }
#endif


            controller->cmd_ctrl(controller, NAND_CMD_DUMMY_PROGRAM, NAND_CTRL_CLE);

            NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TADL_TIME_CYCLE);

            ret = controller->quene_rb(controller, chipnr);
            if (ret) {
                aml_nand_msg("quene rb busy here");
                goto error_exit0;
            }

            oob_buf += user_byte_num;

            /* oob_buf += (BYTES_OF_USER_PER_PAGE /(controller->chip_num * plane_num)); */
            buf += page_size;

            if ((controller->mfr_type == NAND_MFR_HYNIX) || (controller->mfr_type == NAND_MFR_SAMSUNG)) {
                controller->cmd_ctrl(controller, NAND_CMD_TWOPLANE_WRITE2, NAND_CTRL_CLE);
            }
            else{
                controller->cmd_ctrl(controller, NAND_CMD_TWOPLANE_WRITE2_MICRO, NAND_CTRL_CLE);
            }

            controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane1_page_addr, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane1_page_addr>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane1_page_addr>>16, NAND_CTRL_ALE);

            NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TADL_TIME_CYCLE);

            if (bch_mode != NAND_ECC_NONE) {
                controller->set_usr_byte(controller, oob_buf, user_byte_num);
            }

            controller->page_addr = plane1_page_addr;
            ret = controller->dma_write(controller, buf, page_size, bch_mode);
            if (ret) {
                aml_nand_msg("dma error here");
                goto error_exit0;
            }

        }
        else{
            if (slc_mode) {
                ret = slc_info->enter(controller);
                if (ret < 0) {
                    aml_nand_msg("slc enter failed here");
                }
            }

            /* NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TADL_TIME_CYCLE); */
            ret = controller->quene_rb(controller, chipnr);
            if (ret) {
                aml_nand_msg("quene rb busy here");
            }

            controller->cmd_ctrl(controller, NAND_CMD_SEQIN, NAND_CTRL_CLE);
            controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, column>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, controller->page_addr, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, controller->page_addr>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, controller->page_addr>>16, NAND_CTRL_ALE);
            /* controller->cmd_ctrl(controller, NAND_CMD_PAGEPROG, NAND_CTRL_CLE); */

            NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TADL_TIME_CYCLE);

            if (bch_mode != NAND_ECC_NONE) {
                controller->set_usr_byte(controller, oob_buf, user_byte_num);
            }
#if 0
            if (aml_chip->debug_num == 1) {
                aml_nand_dbg("chipnr =%d", chipnr);
                aml_nand_dbg("controller->page_addr =%d", controller->page_addr);
            }
#endif
            ret = controller->dma_write(controller, buf, page_size, bch_mode);
            if (ret) {
                aml_nand_msg("dma error here");
                goto error_exit0;
            }
#if  0
            if (1) {
                aml_nand_dbg("ops_para->page_addr =%d", ops_para->page_addr);
                aml_nand_dbg("controller->page_addr =%d", controller->page_addr);
                aml_nand_dbg("plane_page_addr =%d", plane_page_addr);
                aml_nand_dbg("plane_blk_addr =%d", plane_blk_addr);
                int tt;
                aml_nand_dbg("page_size=%d", page_size);
                for (tt = 0; tt < 200; tt++) {
                    aml_nand_dbg("buf[%d]=%d", tt, buf[tt]);
                }
            }
#endif
        }
        /* start */
        controller->cmd_ctrl(controller, NAND_CMD_PAGEPROG, NAND_CTRL_CLE);
        NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);
        oob_buf += user_byte_num;

        /* oob_buf += (BYTES_OF_USER_PER_PAGE /(controller->chip_num * plane_num)); */
        buf += page_size;

        if (check_cmdfifo_size()) {
            aml_nand_msg("check cmdfifo size timeout");
            BUG();
        }
    }

    for (i = 0; i < chip_num; i++) {

        chipnr = (chip_num > 1) ? i : ops_para->chipnr;
        st_cnt = 0;
        ret = controller->quene_rb(controller, chipnr);
        if (ret) {
            aml_nand_msg("quene rb busy here");
            goto error_exit0;
        }

        controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE);
        NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWHR_TIME_CYCLE);
WSTATUS_TRY:
        status = controller->readbyte(controller);
        if ((status == NAND_STATUS_FAILED) || (status == NAND_STATUS_FAILED1)) {
            aml_nand_msg("write falied at page:%d, status:0x%x", page_addr, status);
            ret = -NAND_WRITE_FAILED;
            goto error_exit0;
        }
		else if ((status != NAND_STATUS_RIGHT) && (status != NAND_STATUS_RIGHT1)) {
            aml_nand_msg("check status failed at page:%d, status:0x%x and try:%d", page_addr, status, st_cnt);
            if (st_cnt++ < NAND_STATUS_MAX_TRY) {
            NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);
            goto WSTATUS_TRY;
        }

        aml_nand_msg("######check status still failed at page:%d, status:0x%x and try:%d", page_addr, status, st_cnt);
        }

        if (slc_mode == 2) {   /* for hynix nand, reset reg def value */
            ret = slc_info->exit(controller);
            if (ret < 0) {
                aml_nand_msg("slc enter failed here");
            }
        }
    }

    return NAND_SUCCESS;

error_exit0:
    return ret;
}

static int set_blcok_status(struct amlnand_chip *aml_chip, unsigned char chipnr, unsigned addr, int value)
{
    /* struct hw_controller *controller = &aml_chip->controller; */
    /* struct nand_flash *flash = &aml_chip->flash; */
    unsigned blk_addr = addr;
    unsigned short *tmp_status = &aml_chip->block_status->blk_status[chipnr][0];

    tmp_status[blk_addr] = value;
    aml_nand_msg(" NAND bbt set Bad block at %d\n", blk_addr);

    return 0;
}

static int get_blcok_status(struct amlnand_chip *aml_chip, unsigned char chipnr, unsigned addr)
{
    /* struct hw_controller *controller = &aml_chip->controller; */
    /* struct nand_flash *flash = &aml_chip->flash; */
    unsigned blk_addr = addr;
    unsigned short *tmp_status = &aml_chip->block_status->blk_status[chipnr][0];
#if 0
    aml_nand_dbg("!!!!!show the block statusXXX ");
    int chip = 0, start_block, total_blk = 50;
    unsigned short *tmp_arr;
    for (chip = 0; chip < controller->chip_num; chip++) {
        tmp_arr = &aml_chip->block_status->blk_status[chip][0];
            for (start_block = 0; start_block < total_blk; start_block++) {
                aml_nand_msg(" tmp_arr[%d][%d]=%d", chip, start_block, tmp_arr[start_block]);
            }
    }
#endif

    if (tmp_status[blk_addr] == NAND_BLOCK_USED_BAD) {
        aml_nand_dbg(" NAND bbt detect Bad block at chip %d blk %d ", chipnr, blk_addr);
        return  NAND_BLOCK_USED_BAD;
    }
    else if (tmp_status[blk_addr] == NAND_BLOCK_FACTORY_BAD) {
        /* aml_nand_dbg(" NAND bbt detect factory Bad block at chip %d blk %d",chipnr, blk_addr); */
        return  NAND_BLOCK_FACTORY_BAD;
    }
    else if (tmp_status[blk_addr] == NAND_BLOCK_GOOD) {
        return  NAND_BLOCK_GOOD;
    }
    else{
        aml_nand_msg("block status is wrong at chip %d blk %d tmp_status[%d] = %d", chipnr, blk_addr, blk_addr, tmp_status[blk_addr]);
        return -1;
    }
}

/************************************************************
 * block_isbad, all parameters saved in aml_chip->ops_para.
 * for opteration mode, contains multi-plane/multi-chip
 * supposed chip bbt has been installed
 *
 *************************************************************/
static int block_isbad(struct amlnand_chip *aml_chip)
{
    struct hw_controller *controller = &aml_chip->controller;
    struct nand_flash *flash = &aml_chip->flash;
    struct chip_ops_para *ops_para = &aml_chip->ops_para;
    struct chip_operation *operation = &aml_chip->operation;

    unsigned blk_addr, page_per_blk_shift;
    unsigned char chip_num = 1, chipnr;
    unsigned char  oob_buf[8];
    int ret = 0;

    if (ops_para->option & DEV_MULTI_CHIP_MODE) {
        chip_num = controller->chip_num;
        /* aml_nand_dbg(" chip_num =%d ",chip_num); */
    }

    page_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize);
    blk_addr = ops_para->page_addr >> page_per_blk_shift;
    chipnr = ops_para->chipnr;
    if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
        controller->page_addr -= controller->internal_page_nums;
        controller->page_addr |= controller->internal_page_nums * aml_chip->flash.internal_chipnr;
    }
    /* aml_nand_dbg("ops_para->page_addr = %d",ops_para->page_addr); */
    /* aml_nand_dbg("chipnr= %d",chipnr); */
    /* aml_nand_dbg("blk_addr= %d",blk_addr); */

    if (/*(aml_chip->nand_bbtinfo.arg_valid) &&*/ (aml_chip->block_status != NULL)) {

        if ((ops_para->option  & DEV_MULTI_PLANE_MODE) && (!(ops_para->option & DEV_MULTI_CHIP_MODE))) {

            blk_addr <<= 1;
            /* aml_nand_dbg(" DEV_MULTI_PLANE_MODE  &&  !DEV_MULTI_CHIP_MODE"); */
            ret = get_blcok_status(aml_chip, chipnr, blk_addr);
            if (ret == NAND_BLOCK_GOOD) {
                if ((blk_addr % 2) == 0) {  /* plane 0 is good , check plane 1 */
                    ret = get_blcok_status(aml_chip, chipnr, (blk_addr+1));
                }
                else {              /* plane 1 is good, check plane 0 */
                    ret = get_blcok_status(aml_chip, chipnr, (blk_addr - 1));
                    }
                }
        }
        else if ((!(ops_para->option  & DEV_MULTI_PLANE_MODE)) && ((ops_para->option & DEV_MULTI_CHIP_MODE))) {

            /* aml_nand_dbg(" !DEV_MULTI_PLANE_MODE  &&  DEV_MULTI_CHIP_MODE"); */
            for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
                ret = get_blcok_status(aml_chip, chipnr, blk_addr);
                if (ret != NAND_BLOCK_GOOD) {
                    break;
                }
            }
        }
        else if ((ops_para->option  & DEV_MULTI_PLANE_MODE) && (ops_para->option & DEV_MULTI_CHIP_MODE)) {

            /* aml_nand_dbg(" DEV_MULTI_PLANE_MODE  &&  DEV_MULTI_CHIP_MODE"); */
            blk_addr <<= 1;
            ret = get_blcok_status(aml_chip, chipnr, blk_addr);
            if (ret == 0) {
                for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
                    ret = get_blcok_status(aml_chip, chipnr, blk_addr);
                    if (ret != NAND_BLOCK_GOOD)
                        break;
                    if ((blk_addr % 2) == 0) { /* plane 0 is good , check plane 1 */
                        ret = get_blcok_status(aml_chip, chipnr, (blk_addr+1));
                        if (ret != NAND_BLOCK_GOOD)
                            break;
                    }
                    else {          /* plane 1 is good, check plane 0 */
                        ret = get_blcok_status(aml_chip, chipnr, (blk_addr - 1));
                        if (ret != NAND_BLOCK_GOOD)
                            break;
                    }
                }
            }
        }
        else{
            ret = get_blcok_status(aml_chip, chipnr, blk_addr);
        }
    }
    else{

        ops_para->data_buf = controller->page_buf;
        ops_para->oob_buf = controller->oob_buf;
        ops_para->ooblen = sizeof(oob_buf);

        memset(ops_para->data_buf, 0x0, flash->pagesize);
        memset(ops_para->oob_buf, 0x0, sizeof(oob_buf));

#ifdef AML_NAND_UBOOT
        nand_get_chip();
#else
        nand_get_chip(aml_chip);
#endif
        ret = operation->read_page(aml_chip);
#ifdef AML_NAND_UBOOT
        nand_release_chip();
#else
        nand_release_chip(aml_chip);
#endif
         if ((ret < 0) || (ops_para->ecc_err)) {
            aml_nand_msg("nand read page failed at %d chip %d", ops_para->page_addr, ops_para->chipnr);
            ret = -NAND_READ_FAILED;
            goto exit_error0;
         }

         if (ops_para->oob_buf[0] == 0) {
            aml_nand_msg("nand detect bad blk at %d chip %d", blk_addr, ops_para->chipnr);
            ret = -NAND_BAD_BLCOK_FAILURE;
            goto exit_error0;
         }

    }

    return ret;

exit_error0:

    return ret;
}

/************************************************************
 * block_isbad, all parameters saved in aml_chip->ops_para.
 * for opteration mode, contains multi-plane/multi-chip
 * supposed chip bbt has been installed
 *
 *************************************************************/
static int block_markbad(struct amlnand_chip *aml_chip)
{
    struct hw_controller *controller = &aml_chip->controller;
    struct nand_flash *flash = &aml_chip->flash;
    struct chip_ops_para *ops_para = &aml_chip->ops_para;
    struct chip_operation *operation = &aml_chip->operation;

    unsigned blk_addr, pages_per_blk_shift;
    unsigned char chip_num = 1, chipnr;
    unsigned short *tmp_status;
    int ret;

    if (ops_para->option & DEV_MULTI_CHIP_MODE) {
        chip_num = controller->chip_num;
    }

    pages_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize);
    blk_addr = ops_para->page_addr >> pages_per_blk_shift;
    chipnr = ops_para->chipnr;
    if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
        controller->page_addr -= controller->internal_page_nums;
        controller->page_addr |= controller->internal_page_nums * aml_chip->flash.internal_chipnr;
    }
    aml_nand_dbg("blk_addr =%d", blk_addr);

    if ((aml_chip->nand_bbtinfo.arg_valid) && (aml_chip->block_status != NULL)) {

        tmp_status = &aml_chip->block_status->blk_status[chipnr][0];
        if ((tmp_status[blk_addr] == NAND_BLOCK_USED_BAD) || (tmp_status[blk_addr] == NAND_BLOCK_FACTORY_BAD)) {
            return 0;
        }
        else if ((tmp_status[blk_addr] == NAND_BLOCK_GOOD)) {
            if ((ops_para->option  & DEV_MULTI_PLANE_MODE) && (!(ops_para->option & DEV_MULTI_CHIP_MODE))) {
                blk_addr <<= 1;
                if ((blk_addr % 2) == 0) { /* plane 0 is good , set plane 1 */
                    ret = set_blcok_status(aml_chip, chipnr, (blk_addr), NAND_BLOCK_USED_BAD);
                    ret = set_blcok_status(aml_chip, chipnr, (blk_addr+1), NAND_BLOCK_USED_BAD);
                    aml_nand_dbg("set blk bad at chip %d blk %d", chipnr, blk_addr);
                    aml_nand_dbg("set blk bad at chip %d blk %d", chipnr, (blk_addr+1));
                }
                else {          /* plane 1 is good, set plane 0 */
                    ret = set_blcok_status(aml_chip, chipnr, (blk_addr), NAND_BLOCK_USED_BAD);
                    ret = set_blcok_status(aml_chip, chipnr, (blk_addr - 1), NAND_BLOCK_USED_BAD);
                    aml_nand_dbg("set blk bad at chip %d blk %d",  chipnr, blk_addr);
                    aml_nand_dbg("set blk bad at chip %d blk %d", chipnr, (blk_addr-1));
                    }
            }
            else if ((!(ops_para->option  & DEV_MULTI_PLANE_MODE)) && ((ops_para->option & DEV_MULTI_CHIP_MODE))) {

                for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
                    ret = set_blcok_status(aml_chip, chipnr, blk_addr, NAND_BLOCK_USED_BAD);
                    aml_nand_dbg(" set blk bad at chip %d blk %d",  chipnr, blk_addr);
                }
            }
            else if ((ops_para->option  & DEV_MULTI_PLANE_MODE) && (ops_para->option & DEV_MULTI_CHIP_MODE)) {
                blk_addr <<= 1;
                for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
                    if ((blk_addr % 2) == 0) {  /* plane 0 is good , set plane 1 */
                        ret = set_blcok_status(aml_chip, chipnr, (blk_addr+1), NAND_BLOCK_USED_BAD);
                        aml_nand_dbg("set blk bad at chip %d blk %d", chipnr, (blk_addr+1));
                    }
                    else {          /* plane 1 is good, set plane 0 */
                        ret = set_blcok_status(aml_chip, chipnr, (blk_addr - 1), NAND_BLOCK_USED_BAD);
                        aml_nand_dbg(" set blk bad at chip %d blk %d", chipnr, (blk_addr-1));
                    }
                                    /* multi_chip , set every chip_blk */
                        ret = set_blcok_status(aml_chip, chipnr, blk_addr, NAND_BLOCK_USED_BAD);
                        aml_nand_dbg(" set blk bad at chip %d blk %d", chipnr, blk_addr);
                    }
            }
            else{
                ret = set_blcok_status(aml_chip, chipnr, blk_addr, NAND_BLOCK_USED_BAD);
                aml_nand_dbg(" set blk bad at chip %d blk %d", chipnr, blk_addr);
            }

            ret = amlnand_update_bbt(aml_chip);
            if (ret < 0) {
                aml_nand_msg("nand update bbt failed");
            }

        }
    }

#if 0
/* show the changed block status */
    aml_nand_dbg("show the changed block status ");
    for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
        tmp_arr = &aml_chip->block_status->blk_status[chipnr][0];
            for (start_block = 0; start_block < 100; start_block++) {
                aml_nand_msg(" tmp_arr[%d][%d]=%d", chipnr, start_block, tmp_arr[start_block]);
            }
    }
#endif

    /* memset((unsigned char *)ops_para, 0x0, sizeof(struct chip_ops_para)); */
    /* ops_para->page_addr = blk_addr << pages_per_blk_shift; */
    /* ops_para->chipnr = blk_addr % controller->chip_num; */
    controller->select_chip(controller, ops_para->chipnr);
    ops_para->data_buf = controller->page_buf;
    ops_para->oob_buf = controller->oob_buf;
    memset((unsigned char *)ops_para->data_buf, 0x0, flash->pagesize);
    memset((unsigned char *)ops_para->oob_buf, 0x0, flash->oobsize);

#ifdef AML_NAND_UBOOT
        nand_get_chip();
#else
    if (aml_chip->state == CHIP_READY)
        nand_get_chip(aml_chip);
#endif
    ret = operation->write_page(aml_chip);
#ifdef AML_NAND_UBOOT
        nand_release_chip();
#else
    if (aml_chip->state == CHIP_READY)
        nand_release_chip(aml_chip);
#endif
    if (ret < 0) {
        aml_nand_msg(" nand write failed");
        ret = -NAND_WRITE_FAILED;
    }

    return ret;
}


/************************************************************
 * erase_block, all parameters saved in aml_chip->ops_para.
 * for opteration mode, contains multi-plane/multi-chip
 * supposed chip bbt has been installed
 *
 *************************************************************/
static int erase_block(struct amlnand_chip *aml_chip)
{
    struct hw_controller *controller = &(aml_chip->controller);
    struct nand_flash *flash = &(aml_chip->flash);
    struct chip_ops_para *ops_para = &(aml_chip->ops_para);
    struct en_slc_info *slc_info = &(controller->slc_info);
    unsigned plane_page_addr, plane_blk_addr, pages_per_blk_shift;
    unsigned plane0_page_addr, plane1_page_addr, chipnr;
    unsigned char slc_mode, chip_num = 1, plane_num = 1, status, st_cnt;
    int i, ret = 0;

    /* aml_nand_dbg("page_addr:%d", ops_para->page_addr); */
    if (aml_chip->nand_status  != NAND_STATUS_NORMAL) {
        aml_nand_msg("nand status unusal: do not erase anything!!!!!");
        return NAND_SUCCESS;
    }

    if (ops_para->option & DEV_MULTI_CHIP_MODE) {
        chip_num = controller->chip_num;
    }

    if (ops_para->option & DEV_SLC_MODE) {
        aml_nand_dbg("enable SLC mode");
        if (flash->new_type == SANDISK_19NM) {
            slc_mode = 1;
        }
        else{
            slc_mode = 0;
        }
    }
    else{
       slc_mode = 0;
    }

    controller->page_addr = ops_para->page_addr;

    pages_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize);
    if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
        controller->page_addr -= controller->internal_page_nums;
        controller->page_addr |= controller->internal_page_nums * aml_chip->flash.internal_chipnr;
    }
    plane0_page_addr = plane1_page_addr = 0;
    if (ops_para->option & DEV_MULTI_PLANE_MODE) {
        plane_num = 2;
        plane_page_addr = (controller->page_addr & ((1 << pages_per_blk_shift) - 1));
        plane_blk_addr = (controller->page_addr >> pages_per_blk_shift);
        plane_blk_addr <<= 1;
        plane0_page_addr = (plane_blk_addr << pages_per_blk_shift) | plane_page_addr;
        plane1_page_addr = ((plane_blk_addr + 1) << pages_per_blk_shift) | plane_page_addr;
    }

    for (i = 0; i < chip_num; i++) {

        chipnr = (chip_num > 1) ? i : ops_para->chipnr;

        ret = controller->quene_rb(controller, chipnr);
        if (ret) {
            aml_nand_msg("quene rb busy here, chipnr:%d, page_addr:%d", chipnr, controller->page_addr);
            goto error_exit0;
        }

        if (plane_num == 2) {
            controller->cmd_ctrl(controller, NAND_CMD_ERASE1, NAND_CTRL_CLE);
            controller->cmd_ctrl(controller, plane0_page_addr, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane0_page_addr>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane0_page_addr>>16, NAND_CTRL_ALE);
            if ((controller->mfr_type == NAND_MFR_MICRON) || (controller->mfr_type == NAND_MFR_MICRON)) {
                controller->cmd_ctrl(controller, NAND_CMD_ERASE1_END, NAND_CTRL_CLE);
                NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);
                ret = controller->quene_rb(controller, chipnr);
                if (ret) {
                    aml_nand_msg("quene rb busy here, chipnr:%d, page_addr:%d", chipnr, controller->page_addr);
                    goto error_exit0;
                }
            }

            controller->cmd_ctrl(controller, NAND_CMD_ERASE1, NAND_CTRL_CLE);
            controller->cmd_ctrl(controller, plane1_page_addr, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane1_page_addr>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, plane1_page_addr>>16, NAND_CTRL_ALE);

            controller->cmd_ctrl(controller, NAND_CMD_ERASE2, NAND_CTRL_CLE);

            NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);

        }
        else{
            if (slc_mode) {
                ret = slc_info->enter(controller);
                if (ret < 0) {
                    aml_nand_msg("slc enter failed here");
                }
            }
            controller->cmd_ctrl(controller, NAND_CMD_ERASE1, NAND_CTRL_CLE);
            controller->cmd_ctrl(controller, controller->page_addr, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, controller->page_addr>>8, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, controller->page_addr>>16, NAND_CTRL_ALE);
            controller->cmd_ctrl(controller, NAND_CMD_ERASE2, NAND_CTRL_CLE);

            NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);
        }

        if (check_cmdfifo_size()) {
            aml_nand_msg("check cmdfifo size timeout");
            BUG();
        }

    }

    for (i = 0; i < chip_num; i++) {

        chipnr = (chip_num > 1) ? i : ops_para->chipnr;
    st_cnt = 0;
        ret = controller->quene_rb(controller, chipnr);
        if (ret) {
            aml_nand_msg("quene rb busy here, chipnr:%d, page_addr:%d", chipnr, controller->page_addr);
            goto error_exit0;
        }

        controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE);
        NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWHR_TIME_CYCLE);

ESTATUS_TRY:
        status = controller->readbyte(controller);
        if ((status == NAND_STATUS_FAILED) || (status == NAND_STATUS_FAILED1)) {
            aml_nand_msg("erase falied at chipnr:%d page:%d, status:0x%x", \
                     chipnr, controller->page_addr, status);
            ret = -NAND_WRITE_FAILED;
            goto error_exit0;
        }
        else if ((status != NAND_STATUS_RIGHT) && (status != NAND_STATUS_RIGHT1)) {
            aml_nand_msg("check erase status failed at chipnr:%d page:%d, status:0x%x and try:%d", \
                     chipnr, controller->page_addr, status, st_cnt);
            if (st_cnt++ < NAND_STATUS_MAX_TRY) {
            NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);
            goto ESTATUS_TRY;
        }

        aml_nand_msg("######check erase status still failed at chipnr:%d page:%d, status:0x%x and try:%d", \
                chipnr, controller->page_addr, status, st_cnt);
        }
    }

    return NAND_SUCCESS;

error_exit0:
    return ret;
}
/************************************************************
 * test_block, all parameters saved in aml_chip->ops_para.
 * for opteration mode, contains multi-plane/multi-chip
 * supposed chip bbt has been installed
 * something wrong, don't work well!!! Do not use it.
 *************************************************************/
static int test_block(struct amlnand_chip *aml_chip)
{
    /* struct hw_controller *controller = &aml_chip->controller; */
    /* struct chip_operation *operation = & aml_chip->operation; */
    struct chip_ops_para  *ops_para = &aml_chip->ops_para;
    struct nand_flash *flash = &aml_chip->flash;
    /* struct en_slc_info *slc_info = &(controller->slc_info); */

    /* unsigned char phys_erase_shift, phys_page_shift, nand_boot; */
    unsigned char phys_erase_shift, phys_page_shift;
    /* unsigned i, offset,pages_per_blk, pages_read, amount_loaded =0,blk_addr = 0; */
    unsigned pages_per_blk, pages_read, blk_addr = 0;
    unsigned char  oob_buf[8];
    /* unsigned short total_blk; */
    /* int  ret = 0, len,t=0; */
    int  ret = 0, t = 0;

    /* unsigned char *dat_buf =NULL; */
    blk_addr = ops_para->page_addr;
/* dat_buf  = aml_nand_malloc(flash->pagesize); */
/* if(!dat_buf){ */
/* aml_nand_msg("test_block: malloc failed"); */
/* ret =  -1; */
/* goto exit; */
/* } */
/* memset(dat_buf, 0xa5, flash->pagesize); */


    phys_erase_shift = ffs(flash->blocksize) - 1;
    phys_page_shift =  ffs(flash->pagesize) - 1;
    pages_per_blk = (1 << (phys_erase_shift - phys_page_shift));

    pages_read = pages_per_blk;


#if 1
    /* read */
/* memset((unsigned char *)ops_para, 0x0, sizeof(struct chip_ops_para)); */
/* ops_para->page_addr = blk_addr; */
/* for(t = 0; t < pages_read; t++){ */
/* memset(aml_chip->user_page_buf, 0x0, flash->pagesize); */
/* ops_para->page_addr += t; */
/* ops_para->data_buf = aml_chip->user_page_buf; */
/* ops_para->oob_buf = aml_chip->user_oob_buf; */
/* ops_para->ooblen = sizeof(oob_buf); */
/*  */
/* ret = read_page(aml_chip); */
/* if(ret < 0){ */
/* aml_nand_msg("nand read %ld failed",blk_addr); */
/* ret =  -1; */
/* goto exit; */
/* } */
/* //aml_nand_dbg("aml_chip->user_page_buf: "); */
/* //show_data_buf(aml_chip->user_page_buf); */
/* //aml_nand_dbg("dat_buf: "); */
/* //show_data_buf(dat_buf); */
/* } */
/* aml_nand_msg("nand blk read OK"); */

    /* write */
    memset((unsigned char *)ops_para, 0x0, sizeof(struct chip_ops_para));
    ops_para->page_addr = blk_addr;
        memset(aml_chip->user_page_buf, 0xa5, flash->pagesize);
    for (t = 0; t < pages_read; t++) {
    ops_para->page_addr += 1;
    ops_para->data_buf = aml_chip->user_page_buf;
    ops_para->oob_buf = aml_chip->user_oob_buf;
    ops_para->ooblen = sizeof(oob_buf);

    ret = write_page(aml_chip);
    if (ret < 0) {
        aml_nand_msg("nand write failed");
        ret =  -1;
        goto exit;
    }
    }
    /* aml_nand_msg("nand blk %d write OK",blk_addr); */
    /* read */
        memset((unsigned char *)ops_para, 0x0, sizeof(struct chip_ops_para));
    ops_para->page_addr = blk_addr;
        /* memset(aml_chip->user_page_buf, 0x0, flash->pagesize); */
     /* aml_nand_msg("test block read!!!!!!!0x%x", ops_para->page_addr); */
        for (t = 0; t < pages_read; t++) {
        ops_para->page_addr += 1;
            ops_para->data_buf = aml_chip->user_page_buf;
            ops_para->oob_buf = aml_chip->user_oob_buf;
            ops_para->ooblen = sizeof(oob_buf);
            ret = read_page(aml_chip);
            if ((ret < 0) || (ops_para->ecc_err)) {
                aml_nand_msg("nand read failed");
                ret =  -1;
                /* goto exit; */
                break;
            }
            /* aml_nand_dbg("aml_chip->user_page_buf: "); */
            /* show_data_buf(aml_chip->user_page_buf); */
            /* aml_nand_dbg("dat_buf: "); */
            /* show_data_buf(dat_buf); */
        }
   /* aml_nand_msg("nand blk %d read OK",blk_addr); */
    /* erase */
        ops_para->page_addr = blk_addr;
        /* erase_block(aml_chip); */
        ret = erase_block(aml_chip);
        if (ret < 0) {
            aml_nand_msg("nand blk erase failed");
            ret =  -1;
            goto exit;
        }
#endif
exit:
        if (ops_para->ecc_err) {
            ret = -1;
        }

/* if(dat_buf){ */
/* aml_nand_free(dat_buf); */
/* dat_buf=NULL; */
/* } */

    if (!ret) {
    /* aml_nand_msg("blk test OK"); */
    }

    ops_para->page_addr = blk_addr;/* !!!!!!!! */
    return ret;
}

/************************************************************
 * test_block, all parameters saved in aml_chip->ops_para.
 * for opteration mode, contains multi-plane/multi-chip
 * supposed chip bbt has been installed
 *
 *************************************************************/
static int test_block_reserved(struct amlnand_chip *aml_chip, int tst_blk)
{
    struct hw_controller *controller = &aml_chip->controller;
    /* struct chip_operation *operation = & aml_chip->operation; */
    struct chip_ops_para  *ops_para = &aml_chip->ops_para;
    struct nand_flash *flash = &aml_chip->flash;
    struct en_slc_info *slc_info = &(controller->slc_info);

    unsigned char phys_erase_shift, phys_page_shift, nand_boot;
    /* unsigned i, offset,pages_per_blk, pages_read, amount_loaded =0; */
    unsigned offset, pages_per_blk, pages_read;
    unsigned char  oob_buf[8];
    /* unsigned short total_blk, tmp_blk; */
    unsigned short tmp_blk;
    /* int  ret = 0, len,t=0; */
    int  ret = 0, t = 0;

    unsigned char *dat_buf = NULL;

    dat_buf  = aml_nand_malloc(flash->pagesize);
    if (!dat_buf) {
        aml_nand_msg("test_block: malloc failed");
        ret =  -1;
        goto exit;
    }
    memset(dat_buf, 0xa5, flash->pagesize);

    nand_boot = 1;

    if (nand_boot)
        offset = (1024 * flash->pagesize);
    else {
        offset = 0;
    }

    phys_erase_shift = ffs(flash->blocksize) - 1;
    phys_page_shift =  ffs(flash->pagesize) - 1;
    pages_per_blk = (1 << (phys_erase_shift - phys_page_shift));

    tmp_blk = (offset >> phys_erase_shift);

    if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM)))
        ops_para->option |= DEV_SLC_MODE;

    if (ops_para->option & DEV_SLC_MODE) {
        pages_read = pages_per_blk >> 1;
    } else{
        pages_read = pages_per_blk;
    }

#ifdef AML_NAND_UBOOT
    nand_get_chip();
#else
    nand_get_chip(aml_chip);
#endif
    /* erase */
    ops_para->page_addr = (((tst_blk - tst_blk % controller->chip_num) / controller->chip_num) + tmp_blk - tmp_blk/controller->chip_num) * pages_per_blk;
    ops_para->chipnr = tst_blk % controller->chip_num;
    controller->select_chip(controller, ops_para->chipnr);
    ret = erase_block(aml_chip);
    if (ret < 0) {
        aml_nand_msg("nand blk %d erase failed", tst_blk);
        ret =  -1;
        goto exit;
    }
    /* write */
    for (t = 0; t < pages_read; t++) {
    memset((unsigned char *)ops_para, 0x0, sizeof(struct chip_ops_para));
    if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM)))
        ops_para->option |= DEV_SLC_MODE;
    ops_para->page_addr = (t+(((tst_blk - tst_blk % controller->chip_num) / controller->chip_num) + tmp_blk - tmp_blk/controller->chip_num) * pages_per_blk);
    ops_para->chipnr = tst_blk % controller->chip_num;
    controller->select_chip(controller, ops_para->chipnr);

    if ((ops_para->option & DEV_SLC_MODE)) {
        if ((flash->new_type > 0) && (flash->new_type < 10))
        ops_para->page_addr = (ops_para->page_addr & (~(pages_per_blk - 1))) | (slc_info->pagelist[ops_para->page_addr % 256]);
        if (flash->new_type == SANDISK_19NM)
        ops_para->page_addr = (ops_para->page_addr & (~(pages_per_blk - 1))) | ((ops_para->page_addr % pages_per_blk) << 1);
    }

    memset(aml_chip->user_page_buf, 0xa5, flash->pagesize);
    ops_para->data_buf = aml_chip->user_page_buf;
    ops_para->oob_buf = aml_chip->user_oob_buf;
    ops_para->ooblen = sizeof(oob_buf);

    ret = write_page(aml_chip);
    if (ret < 0) {
        aml_nand_msg("nand write failed");
        ret =  -1;
        goto exit;
    }
    }
    /* read */
    for (t = 0; t < pages_read; t++) {
        memset((unsigned char *)ops_para, 0x0, sizeof(struct chip_ops_para));
        if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM)))
            ops_para->option |= DEV_SLC_MODE;
        ops_para->page_addr = (t+(((tst_blk - tst_blk % controller->chip_num) / controller->chip_num) + tmp_blk - tmp_blk/controller->chip_num) * pages_per_blk);
        ops_para->chipnr = tst_blk % controller->chip_num;
        controller->select_chip(controller, ops_para->chipnr);

        if ((ops_para->option & DEV_SLC_MODE)) {
            if ((flash->new_type > 0) && (flash->new_type < 10))
                ops_para->page_addr = (ops_para->page_addr & (~(pages_per_blk - 1))) | (slc_info->pagelist[ops_para->page_addr % 256]);
            if (flash->new_type == SANDISK_19NM)
                ops_para->page_addr = (ops_para->page_addr & (~(pages_per_blk - 1))) | ((ops_para->page_addr % pages_per_blk) << 1);
        }


        memset(aml_chip->user_page_buf, 0x0, flash->pagesize);
        ops_para->data_buf = aml_chip->user_page_buf;
        ops_para->oob_buf = aml_chip->user_oob_buf;
        ops_para->ooblen = sizeof(oob_buf);

        ret = read_page(aml_chip);
        if (ret < 0) {
            aml_nand_msg("nand read failed");
            ret =  -1;
            goto exit;
        }
        /* aml_nand_dbg("tst_blk %d aml_chip->user_page_buf: ",tst_blk); */
        /* show_data_buf(aml_chip->user_page_buf); */
        /* aml_nand_dbg("tst_blk %d dat_buf: ",tst_blk); */
        /* show_data_buf(dat_buf); */
        if (memcmp(aml_chip->user_page_buf, dat_buf, flash->pagesize)) {
            ret =  -1;
            aml_nand_msg("blk  %d,  page %d : test failed", tst_blk, t);
            goto exit;
        }
    }
    /* erase */
    ops_para->page_addr = (((tst_blk - tst_blk % controller->chip_num) / controller->chip_num) + tmp_blk - tmp_blk/controller->chip_num) * pages_per_blk;
    ops_para->chipnr = tst_blk % controller->chip_num;
    controller->select_chip(controller, ops_para->chipnr);
    ret = erase_block(aml_chip);
    if (ret < 0) {
        aml_nand_msg("nand blk %d erase failed", tst_blk);
        ret =  -1;
        goto exit;
    }

exit:
#ifdef AML_NAND_UBOOT
    nand_release_chip();
#else
    nand_release_chip(aml_chip);
#endif

    if (dat_buf) {
        aml_nand_free(dat_buf);
        dat_buf = NULL;
    }

    if (!ret) {
        aml_nand_msg("blk %d test OK", tst_blk);
    }

    return ret;
}
/************************************************************
 * all parameters saved in aml_chip->ops_para.
 * for opteration mode, contains multi-plane/multi-chip
 * supposed chip bbt has been installed
 *
 *************************************************************/
static int blk_modify_bbt_chip_op(struct amlnand_chip *aml_chip, int value)
{
    struct hw_controller *controller = &aml_chip->controller;
    struct nand_flash *flash = &aml_chip->flash;
    struct chip_ops_para *ops_para = &aml_chip->ops_para;
    /* struct chip_operation *operation = & aml_chip->operation; */

    unsigned blk_addr, page_per_blk_shift;
    unsigned char chip_num = 1, chipnr;
    /* unsigned char  oob_buf[8]; */
    int ret = 0;

    if (ops_para->option & DEV_MULTI_CHIP_MODE) {
        chip_num = controller->chip_num;
        /* aml_nand_dbg(" chip_num =%d ",chip_num); */
    }

    page_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize);
    blk_addr = ops_para->page_addr >> page_per_blk_shift;
    chipnr = ops_para->chipnr;
    if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
        controller->page_addr -= controller->internal_page_nums;
        controller->page_addr |= controller->internal_page_nums * aml_chip->flash.internal_chipnr;
    }

        if ((ops_para->option  & DEV_MULTI_PLANE_MODE) && (!(ops_para->option & DEV_MULTI_CHIP_MODE))) {

            blk_addr <<= 1;
            /* aml_nand_dbg(" DEV_MULTI_PLANE_MODE  &&  !DEV_MULTI_CHIP_MODE"); */
            ret = set_blcok_status(aml_chip, chipnr, blk_addr, value);
            if (ret == 0) {
                if ((blk_addr % 2) == 0) {  /* plane 0 is good , check plane 1 */
                    ret = set_blcok_status(aml_chip, chipnr, (blk_addr+1), value);
                }
                else {              /* plane 1 is good, check plane 0 */
                    ret = set_blcok_status(aml_chip, chipnr, (blk_addr - 1), value);
                    }
                }
        }
        else if ((!(ops_para->option  & DEV_MULTI_PLANE_MODE)) && ((ops_para->option & value))) {

            /* aml_nand_dbg(" !DEV_MULTI_PLANE_MODE  &&  DEV_MULTI_CHIP_MODE"); */
            for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
                ret = set_blcok_status(aml_chip, chipnr, blk_addr, value);
                if (ret != 0) {
                    break;
                }
            }
        }
        else if ((ops_para->option  & DEV_MULTI_PLANE_MODE) && (ops_para->option & DEV_MULTI_CHIP_MODE)) {

            /* aml_nand_dbg(" DEV_MULTI_PLANE_MODE  &&  DEV_MULTI_CHIP_MODE"); */
            blk_addr <<= 1;
            ret = set_blcok_status(aml_chip, chipnr, blk_addr, value);
            if (ret == 0) {
                for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
                    ret = set_blcok_status(aml_chip, chipnr, blk_addr, value);
                    if (ret != 0)
                        break;
                    if ((blk_addr % 2) == 0) { /* plane 0 is good , check plane 1 */
                        ret = set_blcok_status(aml_chip, chipnr, (blk_addr+1), value);
                        if (ret != 0)
                            break;
                    }
                    else {          /* plane 1 is good, check plane 0 */
                        ret = set_blcok_status(aml_chip, chipnr, (blk_addr - 1), value);
                        if (ret != 0)
                            break;
                    }
                }
            }
        }
        else{
            ret = set_blcok_status(aml_chip, chipnr, blk_addr, value);
        }



    return ret;

/* exit_error0: */

    /* return ret; */
}


/************************************************************
 *  all parameters saved in aml_chip->ops_para.
 * for opteration mode, contains multi-plane/multi-chip
 * supposed chip bbt has been installed
 *
 *************************************************************/
static int update_bbt_chip_op(struct amlnand_chip *aml_chip)
{
    struct hw_controller *controller = &aml_chip->controller;
    struct nand_flash *flash = &aml_chip->flash;
    struct chip_ops_para *ops_para = &aml_chip->ops_para;
    /* struct chip_operation *operation = & aml_chip->operation; */

    unsigned blk_addr, pages_per_blk_shift;
    unsigned char chip_num = 1, chipnr;
    /* unsigned short * tmp_status; */
    int ret;

    if (ops_para->option & DEV_MULTI_CHIP_MODE) {
        chip_num = controller->chip_num;
    }

    pages_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize);
    blk_addr = ops_para->page_addr >> pages_per_blk_shift;
    chipnr = ops_para->chipnr;
    if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
        controller->page_addr -= controller->internal_page_nums;
        controller->page_addr |= controller->internal_page_nums * aml_chip->flash.internal_chipnr;
    }

    aml_nand_msg("###nand update start!!!!\n");


    ret = amlnand_update_bbt(aml_chip);
    if (ret < 0) {
        aml_nand_msg("nand update bbt failed");
    }


    return ret;
}


/************************************************************
 * get_onfi_features, for onfi feature operation
 *
 *************************************************************/
static int get_onfi_features(struct amlnand_chip *aml_chip,  unsigned char *buf, int addr)
{
    struct hw_controller *controller = &(aml_chip->controller);
    int i, j;

    for (i = 0; i < controller->chip_num; i++) {
        controller->select_chip(controller, i);
        controller->cmd_ctrl(controller, NAND_CMD_GET_FEATURES, NAND_CTRL_CLE);
        controller->cmd_ctrl(controller, addr, NAND_CTRL_ALE);
        NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);
        NFC_SEND_CMD_IDLE(controller->chip_selected, 0);

        for (j = 0; j < 4; j++)
            buf[j] = controller->readbyte(controller);
    }

    return 0;
}

/************************************************************
 * set_onfi_features, for onfi feature operation
 *
 *************************************************************/
static int set_onfi_features(struct amlnand_chip *aml_chip,  unsigned char *buf, int addr)
{
    struct hw_controller *controller = &(aml_chip->controller);
    int i, j;
    int time_out_cnt = 0;

    for (i = 0; i < controller->chip_num; i++) {
        controller->select_chip(controller, i);
        controller->cmd_ctrl(controller, NAND_CMD_SET_FEATURES, NAND_CTRL_CLE);
        controller->cmd_ctrl(controller, addr, NAND_CTRL_ALE);
        NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TADL_TIME_CYCLE);
        NFC_SEND_CMD_IDLE(controller->chip_selected, 0);

        for (j = 0; j < 4; j++)
            controller->writebyte(controller, buf[j]);

        /* NFC_SEND_CMD_RB(controller->chip_selected, 20); */
        do {
            if (NFC_CMDFIFO_SIZE() <= 0)
                break;
            udelay(2);
        } while (time_out_cnt++ <= AML_NAND_READ_BUSY_TIMEOUT);

        if (time_out_cnt >= AML_NAND_READ_BUSY_TIMEOUT)
            return -NAND_BUSY_FAILURE;
    }

    return NAND_SUCCESS;
}

/************************************************************
 * nand_reset, send reset command, and assume nand selected here
 *
 *************************************************************/
int nand_reset(struct amlnand_chip *aml_chip, unsigned char chipnr)
{
    struct hw_controller *controller = &(aml_chip->controller);
    int status;
    /* reset */
    NFC_SEND_CMD_IDLE(controller->chip_selected, 0);
    controller->cmd_ctrl(controller, NAND_CMD_RESET, NAND_CTRL_CLE);
    NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWB_TIME_CYCLE);
    NFC_SEND_CMD_IDLE(controller->chip_selected, 0);

    /* read status */
    /* controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE); */
    /* NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWHR_TIME_CYCLE); */

    /* NFC_SEND_CMD_IDLE(controller->chip_selected, 0); */

    if (controller->quene_rb(controller, chipnr) < 0) {
        aml_nand_dbg("quene rb failed here");
        return -NAND_BUSY_FAILURE;
    }

    controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE);
    NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWHR_TIME_CYCLE);
    NFC_SEND_CMD_IDLE(controller->chip_selected, 0);
    NFC_SEND_CMD_IDLE(controller->chip_selected, 0);

    while (NFC_CMDFIFO_SIZE());

    status = (int)controller->readbyte(controller);
    if (status & NAND_STATUS_READY) {
        return NAND_SUCCESS;
    }

    aml_nand_dbg("check status failed and status:0x%x", status);

    return -NAND_BUSY_FAILURE;
}

/************************************************************
 * read_id
 * @id_addr: for 00H and 20H;
 * @buf: chip id stored into buf;
 *************************************************************/
static int read_id(struct amlnand_chip *aml_chip, unsigned char chipnr, unsigned char id_addr, unsigned char *buf)
{
    struct hw_controller *controller = &(aml_chip->controller);
    int i, ret = 0;

    if (buf == NULL) {
        aml_nand_msg("buf must not be NULL here");
        ret = -NAND_ARGUMENT_FAILURE;
        goto error_exit0;
    }

    ret = controller->select_chip(controller, chipnr);
    if (ret < 0) {
        aml_nand_msg("select chip %d failed", chipnr);
        goto error_exit0;
    }

    ret = nand_reset(aml_chip, chipnr);
    if (ret < 0) {
        if (chipnr == 0)
           aml_nand_msg("reset failed");
        else
        aml_nand_dbg("reset failed");
        goto error_exit0;
    }

    /* send id cmd */
    controller->cmd_ctrl(controller, NAND_CMD_READID, NAND_CTRL_CLE);
    controller->cmd_ctrl(controller, id_addr, NAND_CTRL_ALE);
    NFC_SEND_CMD_IDLE(controller->chip_selected, NAND_TWHR_TIME_CYCLE);
    NFC_SEND_CMD_IDLE(controller->chip_selected, 0);

    /* Read manufacturer and device IDs */
    for (i = 0; i < MAX_ID_LEN; i++) {
        buf[i] = controller->readbyte(controller);
    }

error_exit0:
    return ret;

}

int amlnand_init_operation(struct amlnand_chip *aml_chip)
{
    struct chip_operation *operation = &(aml_chip->operation);

    if (!operation->reset)
        operation->reset = nand_reset;
    if (!operation->read_id)
        operation->read_id = read_id;
    if (!operation->set_onfi_para)
        operation->set_onfi_para = set_onfi_features;
    if (!operation->get_onfi_para)
        operation->get_onfi_para = get_onfi_features;

    if (!operation->check_wp)
        operation->check_wp = check_wp;

    if (!operation->erase_block)
        operation->erase_block = erase_block;

    if (!operation->test_block)
        operation->test_block = test_block;

    if (!operation->test_block_reserved)
        operation->test_block_reserved = test_block_reserved;

    if (!operation->block_isbad)
        operation->block_isbad = block_isbad;

    if (!operation->block_markbad)
        operation->block_markbad = block_markbad;

    if (!operation->read_page)
        operation->read_page = read_page;

    if (!operation->write_page)
        operation->write_page = write_page;

    if (!operation->blk_modify_bbt_chip_op)
        operation->blk_modify_bbt_chip_op = blk_modify_bbt_chip_op;

    if (!operation->update_bbt_chip_op)
        operation->update_bbt_chip_op = update_bbt_chip_op;

    return NAND_SUCCESS;

}

