/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ /* * drivers/nand/phy/chip_operation.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ #include "../include/phynand.h" extern int mt_L04A_nand_check(struct amlnand_chip *aml_chip); extern int mt_L05B_nand_check(struct amlnand_chip *aml_chip); static int _read_page_single_plane(struct amlnand_chip *aml_chip, u8 chipnr, u8 *buf, u8 *oob_buf, u32 page_addr); static int write_page_two_plane(struct amlnand_chip *aml_chip, int chipnr, u8 *buf, u8 *oob_buf, u32 plane0_page_addr, u32 plane1_page_addr); static int write_page_single_plane(struct amlnand_chip *aml_chip, u8 chipnr, u8 *buf, u8 *oob_buf, u32 page_addr); static int check_cmdfifo_size(struct hw_controller *controller) { int time_out_cnt = 0, retry_cnt = 0; RETRY: do { if (NFC_CMDFIFO_SIZE(controller) <= 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!"); 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, NAND_TWHR_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); NFC_SEND_CMD_IDLE(controller, 0); do { if (NFC_CMDFIFO_SIZE(controller) <= 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!"); aml_nand_msg("NFC_CMDFIFO_SIZE():%d" , NFC_CMDFIFO_SIZE(controller)); return -NAND_CMD_FAILURE; } if (controller->readbyte(controller) & NAND_STATUS_WP) return -NAND_WP_FAILURE; } return 0; } static int ecc_read_retry_handle(struct amlnand_chip *aml_chip, u8 chipnr, u8 *tmp_buf, u8 user_byte_num, u32 page_size, u8 slc_mode, u8 up_page) { 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); u8 need_retry; u8 *retry_cnt; int retry_op_cnt, retry_cnt_lp, retry_cnt_up, ret; retry_op_cnt = retry_cnt_lp = retry_info->retry_cnt_lp; retry_cnt_up = retry_info->retry_cnt_up; retry_cnt = &aml_chip->g_retry_cnt; 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; /* get usr bytes */ 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) return RETURN_PAGE_ALL_0XFF; /* ecc fail */ need_retry = 0; if (retry_info->flag && (slc_mode == 0)) { if ((flash->new_type != SANDISK_19NM) && ((*retry_cnt)++ < retry_op_cnt)) { need_retry = 1; } else if (flash->new_type == SANDISK_19NM) { if (up_page) { if ((*retry_cnt)++ < retry_cnt_up) need_retry = 1; } else if ((*retry_cnt)++ < retry_cnt_lp) need_retry = 1; } if (need_retry) { aml_nand_dbg("read retry"); aml_nand_dbg("chip:%d cnt:%d", chipnr, *retry_cnt); ret = retry_info->handle(controller, chipnr); if (ret < 0) { aml_nand_msg("read retry"); aml_nand_msg("fail at plane0_page"); } /* next round */ return RETURN_PAGE_NEED_READRETRY; } } ret = -1; } else { if ((*retry_cnt)) { if (flash->new_type != SANDISK_19NM) { if ((*retry_cnt) > (retry_op_cnt-2)) { aml_nand_dbg("detect bitflip page:%d", controller->page_addr); ops_para->bit_flip++; } } else { if (up_page) { if ((*retry_cnt) > (retry_cnt_up-2)) { aml_nand_dbg("detect bitflip page:%d", controller->page_addr); ops_para->bit_flip++; } } else { if ((*retry_cnt) > (retry_cnt_lp-2)) { aml_nand_dbg("detect bitflip page:%d", controller->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 page:%d, chip:%d", controller->page_addr, chipnr); ops_para->bit_flip++; } ret = 0; } /* exit after all retry effort */ if ((*retry_cnt) && retry_info->exit) { ret |= retry_info->exit(controller, chipnr); if (ret < 0) { aml_nand_msg("retry exit failed"); aml_nand_msg("flash->new_type:%d, cnt:%d", flash->new_type, *retry_cnt); } } /* zero retry... */ *retry_cnt = 0; return ret; } #if (AML_CFG_2PLANE_READ_EN) static int read_page_two_plane(struct amlnand_chip *aml_chip, u8 chipnr, u8 *buf, u8 *oob_buf, u32 plane0_page_addr, u32 plane1_page_addr) { struct hw_controller *controller = &(aml_chip->controller); struct nand_flash *flash = &(aml_chip->flash); struct chip_ops_para *ops_para = &(aml_chip->ops_para); //u8 *buf = ops_para->data_buf; u8 *tmp_buf = controller->oob_buf; u32 pages_per_blk_shift; u32 page_size, page_addr, tmp_page; u32 page0_addr, page1_addr; u8 plane0_retry_flag = 0, all_ff_flag = 0; u8 bch_mode, user_byte_num, slc_mode, up_page = 0; int ret = 0; int new_oob = 0; PHY_NAND_LINE page0_addr = plane0_page_addr; page1_addr = plane1_page_addr; page_addr = ops_para->page_addr; pages_per_blk_shift = (controller->block_shift - controller->page_shift); if ((controller->oob_mod) && (oob_buf) && (!ops_para->data_buf)) new_oob = 1; PHY_NAND_LINE /* 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; 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; 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; } } /* setting global retry_cnt to 0 */ aml_chip->g_retry_cnt = 0; PHY_NAND_LINE ret = controller->quene_rb(controller, chipnr); if (ret) { aml_nand_msg("quene rb busy here"); goto error_exit0; } PHY_NAND_LINE RETRY_PLANE_CMD: if ((controller->mfr_type == NAND_MFR_MICRON) || (controller->mfr_type == NAND_MFR_INTEL)) { /* plane0 */ controller->cmd_ctrl(controller, 0x00, NAND_CTRL_CLE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page0_addr, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page0_addr>>8, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page0_addr>>16, NAND_CTRL_ALE); PHY_NAND_LINE controller->cmd_ctrl(controller, 0x32, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); ret = controller->quene_rb(controller, chipnr); if (ret) aml_nand_msg("send 0x32 cmd Rb failed\n"); PHY_NAND_LINE /* plane1 */ controller->cmd_ctrl(controller, 0x00, NAND_CTRL_CLE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr>>8, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr>>16, NAND_CTRL_ALE); /* read start */ controller->cmd_ctrl(controller, 0x30, NAND_CTRL_CLE); } else { PHY_NAND_LINE /* plane0 */ controller->cmd_ctrl(controller, 0x60, NAND_CTRL_CLE); controller->cmd_ctrl(controller, page0_addr, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page0_addr>>8, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page0_addr>>16, NAND_CTRL_ALE); /* plane1 */ controller->cmd_ctrl(controller, 0x60, NAND_CTRL_CLE); controller->cmd_ctrl(controller, page1_addr, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr>>8, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr>>16, NAND_CTRL_ALE); /* read start */ controller->cmd_ctrl(controller, 0x30, NAND_CTRL_CLE); /* NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); */ } PHY_NAND_LINE /* wait twb here */ NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); if (check_cmdfifo_size(controller)) { aml_nand_msg("check cmdfifo size timeout"); BUG(); } PHY_NAND_LINE ret = controller->quene_rb(controller, chipnr); if (ret) { aml_nand_msg("quene rb busy here"); goto error_exit0; } PHY_NAND_LINE if (controller->option & NAND_CTRL_NONE_RB) { controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); } PHY_NAND_LINE if ((controller->mfr_type == NAND_MFR_MICRON) || (controller->mfr_type == NAND_MFR_INTEL)) { PHY_NAND_LINE if (plane0_retry_flag == 0) { PHY_NAND_LINE controller->page_addr = page0_addr; ret = controller->dma_read(controller, page_size, bch_mode); if (ret) { aml_nand_msg("quene dma busy here"); BUG(); goto error_exit0; } PHY_NAND_LINE all_ff_flag = 0; /* for ecc soft mode */ if (bch_mode != NAND_ECC_NONE) { PHY_NAND_LINE ret = ecc_read_retry_handle(aml_chip, chipnr, tmp_buf, user_byte_num, page_size, slc_mode, up_page); if (ret == RETURN_PAGE_ALL_0XFF) { PHY_NAND_LINE all_ff_flag = 1; } else if (ret == RETURN_PAGE_NEED_READRETRY) { PHY_NAND_LINE goto RETRY_PLANE_CMD; } else if (ret < 0) { aml_nand_msg("uncorrect ecc here!"); aml_nand_msg("at page:%d,pl0 chip:%d", page_addr, chipnr); ops_para->ecc_err++; } plane0_retry_flag = 1; } if (ops_para->data_buf) { if (all_ff_flag) { memset(buf, 0xff, page_size); } else { memcpy(buf, controller->data_buf, page_size); } buf += page_size; } if (oob_buf) { if (all_ff_flag) memset(tmp_buf, 0xff, user_byte_num); tmp_buf += user_byte_num; } all_ff_flag = 0; } if (new_oob) goto new_oob_mod; controller->cmd_ctrl(controller, 0x06, NAND_CTRL_CLE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr>>8, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr>>16, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0xe0, NAND_CTRL_CLE); PHY_NAND_LINE NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE); PHY_NAND_LINE controller->page_addr = page1_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) { PHY_NAND_LINE ret = ecc_read_retry_handle(aml_chip, chipnr, tmp_buf, user_byte_num, page_size, slc_mode, up_page); if (ret == RETURN_PAGE_ALL_0XFF) { all_ff_flag = 1; } else if (ret == RETURN_PAGE_NEED_READRETRY) { goto RETRY_PLANE_CMD; } else if (ret < 0) { aml_nand_msg("uncorrect ecc here"); aml_nand_msg("at page:%d, pl1 chip:%d", page_addr, chipnr); ops_para->ecc_err++; } plane0_retry_flag = 0; } } else { /* not micron intel below */ if (plane0_retry_flag == 0) { PHY_NAND_LINE controller->cmd_ctrl(controller, 0x00, NAND_CTRL_CLE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page0_addr, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page0_addr>>8, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page0_addr>>16, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x05, NAND_CTRL_CLE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0xe0, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE); PHY_NAND_LINE controller->page_addr = page0_addr; ret = controller->dma_read(controller, page_size, bch_mode); PHY_NAND_LINE if (ret) { aml_nand_msg("dma error here"); BUG(); goto error_exit0; } if (bch_mode != NAND_ECC_NONE) { ret = ecc_read_retry_handle(aml_chip, chipnr, tmp_buf, user_byte_num, page_size, slc_mode, up_page); if (ret == RETURN_PAGE_ALL_0XFF) { all_ff_flag = 1; } else if (ret == RETURN_PAGE_NEED_READRETRY) { goto RETRY_PLANE_CMD; } else if (ret < 0) { aml_nand_msg("NAND uncorrect ecc here"); aml_nand_msg("at page:%d, pl0 chip:%d", page_addr, chipnr); ops_para->ecc_err++; } plane0_retry_flag = 1; } if (buf) { if (all_ff_flag) memset(buf, 0xff, page_size); else memcpy(buf, controller->data_buf, page_size); buf += page_size; } if (oob_buf) { if (all_ff_flag) memset(tmp_buf, 0xff, user_byte_num); tmp_buf += user_byte_num; } all_ff_flag = 0; } controller->cmd_ctrl(controller, 0x00, NAND_CTRL_CLE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr>>8, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page1_addr>>16, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x05, NAND_CTRL_CLE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0xe0, NAND_CTRL_CLE); PHY_NAND_LINE NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE); PHY_NAND_LINE controller->page_addr = page1_addr; ret = controller->dma_read(controller, page_size, bch_mode); if (ret) { aml_nand_msg("dma error here"); BUG(); goto error_exit0; } PHY_NAND_LINE if (bch_mode != NAND_ECC_NONE) { ret = ecc_read_retry_handle(aml_chip, chipnr, tmp_buf, user_byte_num, page_size, slc_mode, up_page); if (ret == RETURN_PAGE_ALL_0XFF) { all_ff_flag = 1; } else if (ret == RETURN_PAGE_NEED_READRETRY) { goto RETRY_PLANE_CMD; } else if (ret < 0) { aml_nand_msg("NAND uncorrect ecc here"); aml_nand_msg("at page:%d, pl1 chip:%d", page_addr, chipnr); ops_para->ecc_err++; } plane0_retry_flag = 0; } } if (ops_para->data_buf) { if (all_ff_flag) memset(buf, 0xff, page_size); else memcpy(buf, controller->data_buf, page_size); //buf += page_size; /* fixme, why buffer plus here? */ } if (oob_buf) { if (all_ff_flag) memset(tmp_buf, 0xff, user_byte_num); //tmp_buf += user_byte_num; } all_ff_flag = 0; if (check_cmdfifo_size(controller)) { aml_nand_msg("check cmdfifo size timeout"); BUG(); } PHY_NAND_LINE new_oob_mod: if (oob_buf) { memcpy(oob_buf, controller->oob_buf, BYTES_OF_USER_PER_PAGE); if (ops_para->ecc_err) memset(oob_buf, 0x22, BYTES_OF_USER_PER_PAGE); } PHY_NAND_LINE return NAND_SUCCESS; error_exit0: return ret; } /* AML_CFG_2PLANE_READ_EN */ #else /* using 2 single plane read simulate 2plane */ extern void _dump_mem_u8(uint8_t * buf, uint32_t len); static int read_page_two_plane(struct amlnand_chip *aml_chip, u8 chipnr, u32 p0_addr, u32 p1_addr) { int ret = 0; u32 page_size, user_byte_num, new_oob; struct hw_controller *controller = &(aml_chip->controller); struct nand_flash *flash = &(aml_chip->flash); struct chip_ops_para *ops_para = &(aml_chip->ops_para); /* pointer for restore */ u8* oob_buffer = controller->oob_buf; u8* data_buffer = ops_para->data_buf; new_oob = 0; /* for ecc mode */ if ((ops_para->option & DEV_ECC_SOFT_MODE) || (controller->bch_mode == NAND_ECC_NONE)) { page_size = flash->pagesize + flash->oobsize; user_byte_num = flash->oobsize; } else { user_byte_num = controller->ecc_steps * controller->user_mode; page_size = controller->ecc_steps * controller->ecc_unit; } if ((controller->oob_mod) && (ops_para->oob_buf) && (!ops_para->data_buf)) new_oob = 1; //printf("p0, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf); ret = _read_page_single_plane(aml_chip, chipnr, p0_addr) ; if (new_oob) goto _out; //_dump_mem_u8(ops_para->data_buf, 128); /* move buffer ahead. */ //printf("p0-, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf); controller->oob_buf += user_byte_num; /* fixme, maybe not right!*/ if (ops_para->data_buf) ops_para->data_buf += page_size; //printf("p1, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf); ret |= _read_page_single_plane(aml_chip, chipnr, p1_addr) ; //printf("p1-, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf); //_dump_mem_u8(ops_para->data_buf, 128); /* restore buffer location */ controller->oob_buf = oob_buffer; ops_para->data_buf = data_buffer; //printf("p1=, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf); _out: /* fill oob buffer */ if (ops_para->oob_buf) { 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 ret; } /* AML_CFG_2PLANE_READ_EN */ #endif static int _read_page_single_plane(struct amlnand_chip *aml_chip, u8 chipnr, u8 *buf, u8 *oob_buf, u32 page_addr) { 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); u8 *tmp_buf = controller->oob_buf; u32 pages_per_blk_shift; u32 page_size, tmp_page; u8 all_ff_flag = 0; u8 bch_mode, user_byte_num, slc_mode, up_page = 0; int ret = 0; int new_oob = 0; pages_per_blk_shift = (controller->block_shift - controller->page_shift); if (controller->oob_mod && (oob_buf) && (!buf)) new_oob = 1; PHY_NAND_LINE /* 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; 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; 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; } } PHY_NAND_LINE /* setting global retry_cnt to 0 */ aml_chip->g_retry_cnt = 0; ret = controller->quene_rb(controller, chipnr); if (ret) { aml_nand_msg("quene rb busy here"); goto error_exit0; } if (slc_mode == 1) { ret = slc_info->enter(controller); if (ret < 0) aml_nand_msg("slc enter failed here"); } RETRY_CMD: PHY_NAND_LINE controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE); controller->cmd_ctrl(controller, 0x0, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0x0, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page_addr, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page_addr>>8, NAND_CTRL_ALE); controller->cmd_ctrl(controller, page_addr>>16, NAND_CTRL_ALE); controller->cmd_ctrl(controller, NAND_CMD_READSTART, NAND_CTRL_CLE); /* NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); */ /* wait twb here */ NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); if (check_cmdfifo_size(controller)) { aml_nand_msg("check cmdfifo size timeout"); BUG(); } PHY_NAND_LINE ret = controller->quene_rb(controller, chipnr); if (ret) { aml_nand_msg("quene rb busy here"); goto error_exit0; } PHY_NAND_LINE if (controller->option & NAND_CTRL_NONE_RB) { controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); } PHY_NAND_LINE /* aml_nand_dbg("page_addr:%d", ops_para->page_addr); */ /* transfer random seed. */ controller->page_addr = page_addr; ret = controller->dma_read(controller, page_size, bch_mode); if (ret) { aml_nand_msg("dma error here"); BUG(); goto error_exit0; } PHY_NAND_LINE if (bch_mode != NAND_ECC_NONE) { PHY_NAND_LINE ret = ecc_read_retry_handle(aml_chip, chipnr, tmp_buf, user_byte_num, page_size, slc_mode, up_page); if (ret == RETURN_PAGE_ALL_0XFF) { PHY_NAND_LINE all_ff_flag = 1; } else if (ret == RETURN_PAGE_NEED_READRETRY) { PHY_NAND_LINE goto RETRY_CMD; } else if (ret < 0) { PHY_NAND_LINE aml_nand_msg("uncorrect ecc here at page:%d, chip:%d", page_addr, chipnr); ops_para->ecc_err++; } } if (buf) { if (all_ff_flag) memset(buf, 0xff, page_size); else memcpy(buf, controller->data_buf, page_size); //buf += page_size; } PHY_NAND_LINE if (oob_buf) { if (all_ff_flag) memset(tmp_buf, 0xff, user_byte_num); //tmp_buf += user_byte_num; } all_ff_flag = 0; if (check_cmdfifo_size(controller)) { aml_nand_msg("check cmdfifo size timeout"); BUG(); } #if 0 /*move this outsides */ if (oob_buf) { memcpy(oob_buf, controller->oob_buf, BYTES_OF_USER_PER_PAGE); if (ops_para->ecc_err) memset(oob_buf, 0x22, BYTES_OF_USER_PER_PAGE); } #endif // return NAND_SUCCESS; error_exit0: return ret; } static int read_page_single_plane(struct amlnand_chip *aml_chip, u8 chipnr, u8 *buf, u8 *oob_buf, u32 page_addr) { int ret; struct hw_controller *controller = &(aml_chip->controller); struct chip_ops_para *ops_para = &(aml_chip->ops_para); ret = _read_page_single_plane( aml_chip, chipnr, buf, oob_buf, page_addr); if (oob_buf) { memcpy(oob_buf, controller->oob_buf, BYTES_OF_USER_PER_PAGE); if (ops_para->ecc_err) memset(oob_buf, 0x22, BYTES_OF_USER_PER_PAGE); } return ret; } /************************************************************ * 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 operation mode, contains multi-plane/multi-chip *************************************************************/ static int read_page(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &(aml_chip->controller); struct chip_ops_para *ops_para = &(aml_chip->ops_para); u32 plane0_page_addr, plane1_page_addr, page_addr; u32 plane_page_addr, plane_blk_addr, pages_per_blk_shift; u8 i, chip_num , plane_num; int ret = 0; #if (__DEBUG_L04__) struct nand_flash *flash = &(aml_chip->flash); u8 *buf = ops_para->data_buf; u8 *oob_buf = ops_para->oob_buf; #endif PHY_NAND_LINE if ((!ops_para->oob_buf) && (!ops_para->data_buf)) { aml_nand_msg("buf & oob_buf should never be NULL"); ret = -NAND_ARGUMENT_FAILURE; goto error_exit0; } /* 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_USE_SHAREPAGE_MODE) ops_para->page_addr <<= 1; plane0_page_addr = plane1_page_addr = 0; page_addr = ops_para->page_addr; /* aml_nand_msg("ops_para->option:%d,ops_para->page_addr:%x",ops_para->option,ops_para->page_addr); */ controller->page_addr = ops_para->page_addr; pages_per_blk_shift = (controller->block_shift - controller->page_shift); if (unlikely(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; page_addr = controller->page_addr; } PHY_NAND_LINE /* for multi-plane mode */ if (ops_para->option & DEV_MULTI_PLANE_MODE) { plane_num = 2; plane_page_addr = page_addr & ((1 << pages_per_blk_shift)-1); plane_blk_addr = (page_addr >> (pages_per_blk_shift)); plane_blk_addr <<= 1; plane0_page_addr = (plane_blk_addr << pages_per_blk_shift) | plane_page_addr; plane_blk_addr += 1; plane1_page_addr = (plane_blk_addr << pages_per_blk_shift) | plane_page_addr; } else plane_num = 1; PHY_NAND_LINE /* aml_nand_msg("chip_num:%d,plane_num:%x",chip_num,plane_num); */ if (chip_num > 1) { for (i = 0; i < chip_num; i++) { if (plane_num == 2) { ret = read_page_two_plane(aml_chip, i, buf, oob_buf, plane0_page_addr, plane1_page_addr); #if (__DEBUG_L04__) if (ops_para->option & DEV_USE_SHAREPAGE_MODE) { buf += flash->pagesize * 2; oob_buf += BYTES_OF_USER_PER_PAGE; ret = read_page_two_plane(aml_chip, i, buf, oob_buf, plane0_page_addr + 1, plane1_page_addr + 1); } #endif } else { ret = read_page_single_plane(aml_chip, i, buf, oob_buf, page_addr); if (ops_para->option & DEV_USE_SHAREPAGE_MODE) { if (buf) buf += flash->pagesize; if (oob_buf) oob_buf += BYTES_OF_USER_PER_PAGE; ret = read_page_single_plane(aml_chip, i, buf, NULL, page_addr + 1); } } } } else { PHY_NAND_LINE if (plane_num == 2) { PHY_NAND_LINE ret = read_page_two_plane(aml_chip, ops_para->chipnr, buf, oob_buf, plane0_page_addr, plane1_page_addr); #if (__DEBUG_L04__) if (ops_para->option & DEV_USE_SHAREPAGE_MODE) { buf += flash->pagesize * 2; oob_buf += BYTES_OF_USER_PER_PAGE; ret = read_page_two_plane(aml_chip, ops_para->chipnr, buf, oob_buf, plane0_page_addr + 1, plane1_page_addr + 1); } #endif } else { PHY_NAND_LINE ret = read_page_single_plane(aml_chip, ops_para->chipnr, buf, oob_buf, page_addr); if (ops_para->option & DEV_USE_SHAREPAGE_MODE) { if (buf) buf += flash->pagesize; if (oob_buf) oob_buf += BYTES_OF_USER_PER_PAGE; ret = read_page_single_plane(aml_chip, ops_para->chipnr, buf, NULL, page_addr + 1); } } } 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 operation mode, contains multi-plane/multi-chip * *************************************************************/ static int write_page(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &(aml_chip->controller); struct chip_ops_para *ops_para = &(aml_chip->ops_para); u32 plane0_page_addr, plane1_page_addr, page_addr; u32 plane_page_addr, plane_blk_addr, pages_per_blk_shift; u8 i, chip_num , plane_num; int ret = 0; #if (__DEBUG_L04__) struct nand_flash *flash = &(aml_chip->flash); u8 *buf = ops_para->data_buf; u8 *oob_buf = ops_para->oob_buf; #endif PHY_NAND_LINE if (!ops_para->data_buf) { aml_nand_msg("buf & oob_buf should never be NULL"); ret = -NAND_ARGUMENT_FAILURE; goto error_exit0; } /* for multi-chip mode */ if (ops_para->option & DEV_MULTI_CHIP_MODE) chip_num = controller->chip_num; else chip_num = 1; plane0_page_addr = plane1_page_addr = 0; if (ops_para->option & DEV_USE_SHAREPAGE_MODE) ops_para->page_addr <<= 1; 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(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; page_addr = controller->page_addr; } PHY_NAND_LINE /* for multi-plane mode */ if (ops_para->option & DEV_MULTI_PLANE_MODE) { plane_num = 2; plane_page_addr = page_addr & ((1 << pages_per_blk_shift)-1); plane_blk_addr = (page_addr >> (pages_per_blk_shift)); plane_blk_addr <<= 1; plane0_page_addr = (plane_blk_addr << pages_per_blk_shift) | plane_page_addr; plane_blk_addr += 1; plane1_page_addr = (plane_blk_addr << pages_per_blk_shift) | plane_page_addr; } else plane_num = 1; PHY_NAND_LINE if (chip_num > 1) { for (i = 0; i < chip_num; i++) { if (plane_num == 2) { ret = write_page_two_plane(aml_chip, i, buf, oob_buf, plane0_page_addr, plane1_page_addr); #if (__DEBUG_L04__) if (ops_para->option & DEV_USE_SHAREPAGE_MODE) { if (buf) buf += flash->pagesize*2; if (oob_buf) oob_buf += BYTES_OF_USER_PER_PAGE; ret = write_page_two_plane(aml_chip, i, buf, oob_buf, plane0_page_addr + 1, plane1_page_addr + 1); } #endif } else { ret = write_page_single_plane(aml_chip, i, buf, oob_buf, page_addr); if (ops_para->option & DEV_USE_SHAREPAGE_MODE) { if (buf) buf += flash->pagesize; if (oob_buf) oob_buf += BYTES_OF_USER_PER_PAGE; ret = write_page_single_plane(aml_chip, i, buf, oob_buf, page_addr + 1); } } } } else { PHY_NAND_LINE if (plane_num == 2) { PHY_NAND_LINE ret = write_page_two_plane(aml_chip, ops_para->chipnr, buf, oob_buf, plane0_page_addr, plane1_page_addr); #if (__DEBUG_L04__) if (ops_para->option & DEV_USE_SHAREPAGE_MODE) { if (buf) buf += flash->pagesize*2; if (oob_buf) oob_buf += BYTES_OF_USER_PER_PAGE; ret = write_page_two_plane(aml_chip, ops_para->chipnr, buf, oob_buf, plane0_page_addr + 1, plane1_page_addr + 1); } #endif } else { PHY_NAND_LINE ret = write_page_single_plane(aml_chip, ops_para->chipnr, buf, oob_buf, page_addr); if (ops_para->option & DEV_USE_SHAREPAGE_MODE) { if (buf) buf += flash->pagesize; if (oob_buf) oob_buf += BYTES_OF_USER_PER_PAGE; ret = write_page_single_plane(aml_chip, ops_para->chipnr, buf, oob_buf, page_addr + 1); } } } error_exit0: return ret; } static int write_page_two_plane(struct amlnand_chip *aml_chip, int chipnr, u8 *buf, u8 *oob_buf, u32 plane0_page_addr, u32 plane1_page_addr) { 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); struct chip_operation *operation = &(aml_chip->operation); unsigned char retry_val[4] ={0, 0, 0, 0}; unsigned char addr = 0x89; u32 column; u32 page_size; u8 bch_mode, user_byte_num = 0; u8 slc_mode, status, st_cnt; int ret = 0; if ((mt_L04A_nand_check(aml_chip) == 0) || (mt_L05B_nand_check(aml_chip) == 0)) { operation->get_onfi_para(aml_chip, retry_val, addr); if (retry_val[0] != 0) { aml_nand_msg("!!!read retry value:%d,when write", retry_val[0]); retry_val[0] = 0; operation->set_onfi_para(aml_chip, retry_val, addr); operation->get_onfi_para(aml_chip, retry_val, addr); aml_nand_msg("!!!set read retry value:%d,when write", retry_val[0]); } } /* 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 (!oob_buf) { /* empty oob buffer, set defaut */ memset(controller->oob_buf, 0xa5, user_byte_num); controller->oob_buf[0] = 0xff; oob_buf = controller->oob_buf; } else { memcpy(controller->oob_buf, oob_buf, user_byte_num); //oob_buf = controller->oob_buf; } } 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; else slc_mode = 0; } else slc_mode = 0; column = 0; ret = controller->quene_rb(controller, chipnr); if (ret) { aml_nand_msg("quene rb busy here"); goto error_exit0; } /* 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; } controller->cmd_ctrl(controller, NAND_CMD_DUMMY_PROGRAM, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, 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; 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, 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; } /* start */ controller->cmd_ctrl(controller, NAND_CMD_PAGEPROG, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); oob_buf += user_byte_num; buf += page_size; if (check_cmdfifo_size(controller)) { aml_nand_msg("check cmdfifo size timeout"); BUG(); } st_cnt = 0; #ifdef AML_NAND_RB_IRQ if (i == 0) ret = controller->quene_rb_irq(controller, chipnr); else ret = controller->quene_rb(controller, chipnr); #else ret = controller->quene_rb(controller, chipnr); #endif 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, NAND_TWHR_TIME_CYCLE); WSTATUS_TRY: status = controller->readbyte(controller); if (status == NAND_STATUS_FAILED) { aml_nand_msg("write falied at page:%d, status:0x%x", plane0_page_addr, status); ret = -NAND_WRITE_FAILED; goto error_exit0; } else if (status != NAND_STATUS_RIGHT) { aml_nand_msg("status failed page:%d,status:0x%x try:%d", plane0_page_addr, status, st_cnt); if (st_cnt++ < NAND_STATUS_MAX_TRY) { NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); goto WSTATUS_TRY; } aml_nand_msg("status still failed"); } /* for hynix nand, reset reg def value */ if (slc_mode == 2) { ret = slc_info->exit(controller); if (ret < 0) aml_nand_msg("slc enter failed here"); } return NAND_SUCCESS; error_exit0: return ret; } static int write_page_single_plane(struct amlnand_chip *aml_chip, u8 chipnr, u8 *buf, u8 *oob_buf, u32 page_addr) { 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); u32 column; u32 page_size; u8 bch_mode, user_byte_num; u8 slc_mode, status, st_cnt; int ret = 0; struct chip_operation *operation = &(aml_chip->operation); unsigned char retry_val[4] ={0, 0, 0, 0}; unsigned char addr = 0x89; 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 unusual: do not write anything!!!!!"); return NAND_SUCCESS; } if ((mt_L04A_nand_check(aml_chip) == 0) || (mt_L05B_nand_check(aml_chip) == 0)) { operation->get_onfi_para(aml_chip, retry_val, addr); if (retry_val[0] != 0) { aml_nand_msg("!!!read retry value1:%d,when write", retry_val[0]); retry_val[0] = 0; operation->set_onfi_para(aml_chip, retry_val, addr); operation->get_onfi_para(aml_chip, retry_val, addr); aml_nand_msg("!!!set read retry value1:%d,when write", retry_val[0]); } } user_byte_num = 0; 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; else slc_mode = 0; } else slc_mode = 0; controller->page_addr = page_addr; 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; } /* 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 (!oob_buf) { /* empty oob buffer, set defaut */ memset(controller->oob_buf, 0xa5, user_byte_num); controller->oob_buf[0] = 0xff; oob_buf = controller->oob_buf; } else { memcpy(controller->oob_buf, oob_buf, user_byte_num); //oob_buf = controller->oob_buf; } } column = 0; ret = controller->quene_rb(controller, chipnr); if (ret) { aml_nand_msg("quene rb busy here"); goto error_exit0; } if (slc_mode) { ret = slc_info->enter(controller); if (ret < 0) aml_nand_msg("slc enter failed here"); } 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); NFC_SEND_CMD_IDLE(controller, NAND_TADL_TIME_CYCLE); if (bch_mode != NAND_ECC_NONE) controller->set_usr_byte(controller, oob_buf, user_byte_num); ret = controller->dma_write(controller, buf, page_size, bch_mode); if (ret) { aml_nand_msg("dma error here"); goto error_exit0; } /* start */ controller->cmd_ctrl(controller, NAND_CMD_PAGEPROG, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); oob_buf += user_byte_num; buf += page_size; if (check_cmdfifo_size(controller)) { aml_nand_msg("check cmdfifo size timeout"); BUG(); } st_cnt = 0; #ifdef AML_NAND_RB_IRQ if (i == 0) ret = controller->quene_rb_irq(controller, chipnr); else ret = controller->quene_rb(controller, chipnr); #else ret = controller->quene_rb(controller, chipnr); #endif 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, NAND_TWHR_TIME_CYCLE); WSTATUS_TRY: status = controller->readbyte(controller); if (status == NAND_STATUS_FAILED) { 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) { aml_nand_msg("status failed page:%d,status:0x%x try:%d", page_addr, status, st_cnt); if (st_cnt++ < NAND_STATUS_MAX_TRY) { NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); goto WSTATUS_TRY; } aml_nand_msg("status still failed"); } /* for hynix nand, reset reg def value */ if (slc_mode == 2) { 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_block_status(struct amlnand_chip *aml_chip, u8 chipnr, u32 addr, int value) { /* struct hw_controller *controller = &aml_chip->controller; */ /* struct nand_flash *flash = &aml_chip->flash; */ u32 blk_addr = addr; u16 *tmp_status = NULL; 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_block_status(struct amlnand_chip *aml_chip, u8 chipnr, u32 addr) { /* struct hw_controller *controller = &aml_chip->controller; */ /* struct nand_flash *flash = &aml_chip->flash; */ u32 blk_addr = addr; u16 *tmp_status = NULL; 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; u16 *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("blk status is wrong at chip%d blk=%d tmp[%d]=%d", chipnr, blk_addr, blk_addr, tmp_status[blk_addr]); return -1; } } /************************************************************ * block_isbad, all parameters saved in aml_chip->ops_para. * for operation 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; u32 blk_addr, page_per_blk_shift; u8 chipnr; //u8 chip_num = 1; u8 oob_buf[8]; int ret = 0; u32 buf_size = 0; u8* page_buf; #if 0 if (ops_para->option & DEV_MULTI_CHIP_MODE) { chip_num = controller->chip_num; /* aml_nand_dbg(" chip_num =%d ",chip_num); */ } #endif //0 page_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize); if (ops_para->option & DEV_USE_SHAREPAGE_MODE) ops_para->page_addr <<= 1; 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->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_block_status(aml_chip, chipnr, blk_addr); if (ret == NAND_BLOCK_GOOD) { /* plane 0 is good , check plane 1 */ if ((blk_addr % 2) == 0) ret = get_block_status(aml_chip, chipnr, (blk_addr+1)); else/* plane 1 is good, check plane 0 */ ret = get_block_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_block_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_block_status(aml_chip, chipnr, blk_addr); if (ret == 0) { for (chipnr = 0; chipnr < controller->chip_num; chipnr++) { ret = get_block_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_block_status(aml_chip, chipnr, (blk_addr+1)); if (ret != NAND_BLOCK_GOOD) break; } else { /* plane 1 is good, check plane 0 */ ret = get_block_status(aml_chip, chipnr, (blk_addr - 1)); if (ret != NAND_BLOCK_GOOD) break; } } } } else ret = get_block_status(aml_chip, chipnr, blk_addr); } else { buf_size = (flash->pagesize + flash->oobsize) * controller->chip_num; if (flash->option & NAND_MULTI_PLANE_MODE) buf_size <<= 1; if (flash->option & NAND_USE_SHAREPAGE_MODE) buf_size <<= 1; page_buf = aml_nand_malloc(buf_size); if (!page_buf) { aml_nand_msg("no memory for data buf, and need %x", buf_size); printf( "%s: line:%d\n", __func__, __LINE__); while (1) ; ret = -NAND_MALLOC_FAILURE; return 0; } /*ops_para->data_buf = controller->page_buf; */ ops_para->data_buf = 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)); nand_get_chip(aml_chip); ret = operation->read_page(aml_chip); nand_release_chip(aml_chip); aml_nand_free(page_buf); page_buf = NULL; 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_BLOCK_FAILURE; goto exit_error0; } } return ret; exit_error0: return ret; } /************************************************************ * block_isbad, all parameters saved in aml_chip->ops_para. * for operation 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; u32 blk_addr, pages_per_blk_shift; u8 chipnr; //u8 chip_num = 1; u16 *tmp_status; int ret; u32 buf_size = 0; u8* page_buf; #if 0 if (ops_para->option & DEV_MULTI_CHIP_MODE) chip_num = controller->chip_num; #endif pages_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize); if (ops_para->option & DEV_USE_SHAREPAGE_MODE) ops_para->page_addr <<= 1; 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_block_status(aml_chip, chipnr, (blk_addr), NAND_BLOCK_USED_BAD); ret = set_block_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_block_status(aml_chip, chipnr, (blk_addr), NAND_BLOCK_USED_BAD); ret = set_block_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_block_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_block_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_block_status(aml_chip, chipnr, (blk_addr - 1), NAND_BLOCK_USED_BAD); aml_nand_dbg("set blk bad,chip%d blk %d", chipnr, (blk_addr-1)); } /* multi_chip , set every chip_blk */ ret = set_block_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_block_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((u8 *)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); buf_size = (flash->pagesize + flash->oobsize) * controller->chip_num; if (flash->option & NAND_MULTI_PLANE_MODE) buf_size <<= 1; if (flash->option & NAND_USE_SHAREPAGE_MODE) buf_size <<= 1; page_buf = aml_nand_malloc(buf_size); if (!page_buf) { aml_nand_msg("no memory for data buf, and need %x", buf_size); printf( "%s: line:%d\n", __func__, __LINE__); while (1) ; ret = -NAND_MALLOC_FAILURE; return 0; } //ops_para->data_buf = controller->page_buf; ops_para->data_buf = page_buf; ops_para->oob_buf = controller->oob_buf; memset((u8 *)ops_para->data_buf, 0x0, flash->pagesize); memset((u8 *)ops_para->oob_buf, 0x0, flash->oobsize); if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); ret = operation->write_page(aml_chip); if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); aml_nand_free(page_buf); page_buf = NULL; if (ret < 0) { aml_nand_msg("%s() %d: nand write failed", __func__, __LINE__); ret = -NAND_WRITE_FAILED; } return ret; } /************************************************************ * erase_block, all parameters saved in aml_chip->ops_para. * for operation 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); u32 plane_page_addr, plane_blk_addr, pages_per_blk_shift; u32 plane0_page_addr, plane1_page_addr, chipnr; u8 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 unusual: 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; if (ops_para->option & DEV_USE_SHAREPAGE_MODE) ops_para->page_addr <<= 1; 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,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, NAND_TWB_TIME_CYCLE); ret = controller->quene_rb(controller, chipnr); if (ret) { aml_nand_msg("rb busy,chip%d,paddr:%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, 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, NAND_TWB_TIME_CYCLE); } if (check_cmdfifo_size(controller)) { 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; #ifdef AML_NAND_RB_IRQ if (i == 0) ret = controller->quene_rb_irq(controller, chipnr); else ret = controller->quene_rb(controller, chipnr); #else ret = controller->quene_rb(controller, chipnr); #endif if (ret) { aml_nand_msg("quene rb busy,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, NAND_TWHR_TIME_CYCLE); ESTATUS_TRY: status = controller->readbyte(controller); if (status == NAND_STATUS_FAILED) { aml_nand_msg("erase failed, chip%d page:0x%x,status:0x%x", chipnr, controller->page_addr, status); ret = -NAND_WRITE_FAILED; goto error_exit0; } else if (status != NAND_STATUS_RIGHT) { aml_nand_msg("erase status failed"); aml_nand_msg("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, NAND_TWB_TIME_CYCLE); goto ESTATUS_TRY; } aml_nand_msg("erase status still failed "); } } return NAND_SUCCESS; error_exit0: return ret; } /************************************************************ * test_block, all parameters saved in aml_chip->ops_para. * for operation 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_chip_op(struct amlnand_chip *aml_chip) { struct chip_ops_para *ops_para = &aml_chip->ops_para; struct nand_flash *flash = &aml_chip->flash; u8 phys_erase_shift, phys_page_shift; u32 pages_per_blk, pages_read, blk_addr = 0; u8 oob_buf[8]; int ret = 0, t = 0; u8 *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; /* erase */ aml_nand_msg("erase addr = %d", ops_para->page_addr); ret = erase_block(aml_chip); if (ret < 0) { aml_nand_msg("nand blk erase failed"); ret = -1; goto exit; } aml_nand_msg("nand blk %d erase OK", blk_addr); #if 1 /* read */ memset((u8 *)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 %d failed", blk_addr); ret = -1; goto exit; } } aml_nand_msg("nand blk read OK"); /* write */ memset((u8 *)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, 0xa5, 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 = write_page(aml_chip); if (ret < 0) { aml_nand_msg("%s() %d: nand write failed", __func__, __LINE__); ret = -1; goto exit; } } aml_nand_msg("nand blk %d write OK", blk_addr); /* read */ memset((u8 *)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 failed"); ret = -1; goto exit; } } aml_nand_msg("nand blk %d read OK", blk_addr); /* erase */ ops_para->page_addr = blk_addr; ret = erase_block(aml_chip); if (ret < 0) { aml_nand_msg("nand blk erase failed"); ret = -1; goto exit; } aml_nand_msg("nand blk %d erase OK", blk_addr); #endif exit: /* nand_release_chip(aml_chip); */ if (dat_buf) { aml_nand_free(dat_buf); dat_buf = NULL; } if (!ret) aml_nand_msg("blk test OK"); return ret; } /************************************************************ * test_block, all parameters saved in aml_chip->ops_para. * for operation 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); u8 phys_erase_shift, phys_page_shift, nand_boot; u32 offset, pages_per_blk, pages_read; u8 oob_buf[8]; u16 tmp_blk; int ret = 0, t = 0; u32 tmp_value; u8 *dat_buf = NULL; if (!flash->blocksize || !flash->pagesize) return -1; 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); 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; nand_get_chip(aml_chip); /* erase */ tmp_value = tst_blk - tst_blk % controller->chip_num; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; ops_para->page_addr = tmp_value * 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((u8 *)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; tmp_value = tst_blk - tst_blk % controller->chip_num; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; ops_para->page_addr = t + tmp_value * 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)) { tmp_value = ~(pages_per_blk - 1); tmp_value &= ops_para->page_addr; if ((flash->new_type > 0) && (flash->new_type < 10)) ops_para->page_addr = tmp_value | (slc_info->pagelist[ops_para->page_addr % 256]); if (flash->new_type == SANDISK_19NM) ops_para->page_addr = tmp_value | ((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("%s() %d: nand write failed", __func__, __LINE__); ret = -1; goto exit; } } /* read */ for (t = 0; t < pages_read; t++) { memset((u8 *)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; tmp_value = tst_blk - tst_blk % controller->chip_num; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; ops_para->page_addr = (t + tmp_value * 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)) { tmp_value = ~(pages_per_blk - 1); tmp_value &= ops_para->page_addr; if ((flash->new_type > 0) && (flash->new_type < 10)) ops_para->page_addr = tmp_value | (slc_info->pagelist[ops_para->page_addr % 256]); if (flash->new_type == SANDISK_19NM) ops_para->page_addr = tmp_value | ((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 */ tmp_value = tst_blk - tst_blk % controller->chip_num; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; ops_para->page_addr = tmp_value * 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: nand_release_chip(aml_chip); 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 operation 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; */ u32 blk_addr, page_per_blk_shift; u8 chipnr; /* u8 oob_buf[8]; */ int ret = 0; #if 0 u8 chip_num = 1; if (ops_para->option & DEV_MULTI_CHIP_MODE) chip_num = controller->chip_num; #endif page_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize); if (ops_para->option & DEV_USE_SHAREPAGE_MODE) ops_para->page_addr <<= 1; 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_block_status(aml_chip, chipnr, blk_addr, value); if (ret == 0) { if ((blk_addr % 2) == 0) /* plane 0 is good , check plane 1 */ ret = set_block_status(aml_chip, chipnr, (blk_addr+1), value); else /* plane 1 is good, check plane 0 */ ret = set_block_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_block_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_block_status(aml_chip, chipnr, blk_addr, value); if (ret == 0) { for (chipnr = 0; chipnr < controller->chip_num; chipnr++) { ret = set_block_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_block_status(aml_chip, chipnr, (blk_addr+1), value); if (ret != 0) break; } else {/* plane 1 is good, check plane 0 */ ret = set_block_status(aml_chip, chipnr, (blk_addr - 1), value); if (ret != 0) break; } } } } else ret = set_block_status(aml_chip, chipnr, blk_addr, value); return ret; } /************************************************************ * all parameters saved in aml_chip->ops_para. * for operation 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 chip_ops_para *ops_para = &aml_chip->ops_para; /* struct chip_operation *operation = & aml_chip->operation; */ /* u8 chip_num = 1; */ /* u16 * tmp_status; */ int ret; #if 0 if (ops_para->option & DEV_MULTI_CHIP_MODE) chip_num = controller->chip_num; #endif if (ops_para->option & DEV_USE_SHAREPAGE_MODE) ops_para->page_addr <<= 1; //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, u8 *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); if (controller->quene_rb(controller, i) < 0) { aml_nand_dbg("Get features quene rb failed here"); return -NAND_BUSY_FAILURE; } controller->cmd_ctrl(controller, NAND_CMD_GET_FEATURES, NAND_CTRL_CLE); controller->cmd_ctrl(controller, addr, NAND_CTRL_ALE); NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); do { if (NFC_CMDFIFO_SIZE(controller) <= 0) break; udelay(2); } while (time_out_cnt++ <= AML_NAND_READ_BUSY_TIMEOUT); udelay(1); //wait 1us 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, u8 *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); if (controller->quene_rb(controller, i) < 0) { aml_nand_dbg("Set features quene rb failed here"); return -NAND_BUSY_FAILURE; } controller->cmd_ctrl(controller, NAND_CMD_SET_FEATURES, NAND_CTRL_CLE); controller->cmd_ctrl(controller, addr, NAND_CTRL_ALE); NFC_SEND_CMD_IDLE(controller, NAND_TADL_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); for (j = 0; j < 4; j++) controller->writebyte(controller, buf[j]); /* NFC_SEND_CMD_RB(controller, controller->chip_selected, 20); */ do { if (NFC_CMDFIFO_SIZE(controller) <= 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_hardreset(struct amlnand_chip *aml_chip, u8 chipnr) { struct hw_controller *controller = &(aml_chip->controller); int status; if (controller->quene_rb(controller, chipnr) < 0) { aml_nand_msg("%s(): quene rb failed here", __func__); return -NAND_BUSY_FAILURE; } //Delay mdelay(5); controller->cmd_ctrl(controller, 0x78, NAND_CTRL_CLE); controller->cmd_ctrl(controller, chipnr>> 0, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0, NAND_CTRL_ALE); NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); NFC_SEND_CMD_IDLE(controller, 0); while (NFC_CMDFIFO_SIZE(controller)); status = (int)controller->readbyte(controller); while ((status & 0x60) != 0x60) { udelay(1); status = (int)controller->readbyte(controller); } /* hardreset */ NFC_SEND_CMD_IDLE(controller, 0); controller->cmd_ctrl(controller, 0xFD, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); /* DataSheet says 3 ms of tPOR */ mdelay(3); return NAND_SUCCESS; } int nand_reset(struct amlnand_chip *aml_chip, u8 chipnr) { struct hw_controller *controller = &(aml_chip->controller); int status; /* fixme, read back controller status. */ /* reset */ NFC_SEND_CMD_IDLE(controller, 0); controller->cmd_ctrl(controller, NAND_CMD_RESET, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); /* read status */ /* controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE); */ /* NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE); */ /* NFC_SEND_CMD_IDLE(controller, 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, NAND_TWHR_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); NFC_SEND_CMD_IDLE(controller, 0); while (NFC_CMDFIFO_SIZE(controller)); 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, u8 chipnr, u8 id_addr, u8 *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, NAND_TWHR_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); /* Read manufacturer and device IDs */ for (i = 0; i < MAX_ID_LEN; i++) buf[i] = controller->readbyte(controller); error_exit0: return ret; } static int nand_read_param(struct amlnand_chip *aml_chip, u8 chip_nr, u8 param_addr, u8 *param) { struct hw_controller *controller = &(aml_chip->controller); int ret = 0, i = 0, j = 0; int status = 0; int chip_num = 1; memset(param, 0, 512); if ( controller->chip_num ) chip_num = 1; // TBD: controller->chip_num; /* Read From one chip now */ for (i=0; i < chip_num; i++) { controller->select_chip(controller, i); controller->cmd_ctrl(controller, NAND_CMD_PARAM, NAND_CTRL_CLE); controller->cmd_ctrl(controller, param_addr, NAND_CTRL_ALE); NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); ndelay(600); if (controller->quene_rb(controller, i) < 0) { aml_nand_msg("%s: quene rb failed here", __func__); return -NAND_BUSY_FAILURE; } controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE); NFC_SEND_CMD_IDLE(controller, 0); NFC_SEND_CMD_IDLE(controller, 0); ndelay(600); while (NFC_CMDFIFO_SIZE(controller) > 0); status = (int)controller->readbyte(controller); while (!(status & NAND_STATUS_READY)) { status = (int)controller->readbyte(controller); } controller->cmd_ctrl(controller, 0x0, NAND_CTRL_CLE); NFC_SEND_CMD_IDLE(controller, 0); for (j=0; j<512; j++) param[j] = controller->readbyte(controller); } aml_nand_dbg("param data0[32~43]: %x, %x, %x, %x,%x, %x",param[0],param[1], param[2],param[3],param[4],param[5]); aml_nand_dbg("param data0[32~43]: %x, %x, %x, %x,%x, %x",param[32],param[33], param[34],param[35],param[36],param[37]); /* TBD: NEED to add crc to validate JEDEC */ 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->nand_read_param) operation->nand_read_param = nand_read_param; 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_chip_op) operation->test_block_chip_op = test_block_chip_op; 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; }