// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include "../include/phynand.h" extern int block_markbad(struct amlnand_chip *aml_chip); extern int amlnand_save_info_by_name(struct amlnand_chip *aml_chip,unsigned char * info,unsigned char * buf,unsigned char * name,unsigned size); extern int aml_sys_info_error_handle(struct amlnand_chip *aml_chip); extern int aml_sys_info_init(struct amlnand_chip *aml_chip); extern int aml_nand_update_ubootenv(struct amlnand_chip * aml_chip, char *env_ptr); extern int amlnand_get_partition_table(struct amlnand_chip *aml_chip); extern void amlnf_get_chip_size(u64 *size); extern int amlnf_erase_ops(uint64_t off, uint64_t erase_len, unsigned char scrub_flag); /* fixme, */ extern int info_disprotect; extern int amlnf_dtb_read(u8 *buf, int len); extern int amlnf_key_read(u8 * buf, int len, uint32_t *actual_length); extern int amlnf_env_read(u8 *buf, int len); extern int amlnf_dtb_save(u8 *buf, int len); extern int amlnf_key_write(u8 *buf, int len, uint32_t *actual_length); extern int amlnf_env_save(u8 *buf, int len); extern int amlnf_dtb_erase(void); extern int amlnf_key_erase(void); extern int amlnf_env_erase(void); #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif /* MAX */ #ifdef AML_NAND_UBOOT //extern struct amlnf_partition amlnand_config; extern struct amlnf_partition * amlnand_config; int get_last_reserve_block(struct amlnand_chip *aml_chip); int repair_reserved_bad_block(struct amlnand_chip *aml_chip); void show_data_buf(unsigned char * buf); void amlnand_config_buf_free(struct amlnand_chip *aml_chip); int chipenv_init_erase_protect(struct amlnand_chip *aml_chip, int flag,int block_num) { int ret = 0,start_blk = 0; struct nand_flash *flash = &aml_chip->flash; struct hw_controller *controller = &aml_chip->controller; struct read_retry_info *retry_info = &(controller->retry_info); int phys_erase_shift = ffs(flash->blocksize) - 1; start_blk = (1024 * flash->pagesize) >> phys_erase_shift; block_num -= (controller->chip_num - 1) * start_blk; if ((flag > NAND_BOOT_UPGRATE) && (flag <= NAND_BOOT_SCRUB_ALL)) { /*liang:make sure fbbt and bbt are ok, don't erase forever!!!*/ if ((block_num == aml_chip->shipped_bbtinfo.valid_blk_addr) && (aml_chip->shipped_bbtinfo.valid_blk_addr >= start_blk)) { aml_nand_msg("protect fbbt at blk %d",block_num); ret = -1; }else if((block_num == aml_chip->nand_bbtinfo.valid_blk_addr)&&(aml_chip->nand_bbtinfo.valid_blk_addr >= start_blk)){ aml_nand_msg("protect nand_bbt info at blk %d",block_num); ret = -1; }else if(((block_num == retry_info->info_save_blk)&&(retry_info->info_save_blk >= start_blk)&&(flash->new_type)&&(flash->new_type < 10))&&(!(info_disprotect & DISPROTECT_HYNIX))){ //aml_nand_msg("protect hynix retry info at blk %d", block_num); ret = -1; /* do not protect hynix retry info anymore*/ aml_nand_msg("disprotect hynix retry info at blk %d",block_num); ret = 0; }else if((block_num == aml_chip->nand_key.valid_blk_addr)&&(aml_chip->nand_key.valid_blk_addr >= start_blk)&&(!(info_disprotect & DISPROTECT_KEY))){ aml_nand_msg("protect nand_key info at blk %d",block_num); ret = -1; }else if((block_num == aml_chip->nand_secure.valid_blk_addr)&&(aml_chip->nand_secure.valid_blk_addr >= start_blk)&&(!(info_disprotect & DISPROTECT_SECURE))){ aml_nand_msg("protect nand_secure info at blk %d",block_num); ret = -1; }else{ ret = 0; } } return ret; } #if (AML_CFG_DTB_RSV_EN) extern int dtb_erase_blk; extern struct amlnand_chip *aml_chip_dtb; int bad_block_is_dtb_blk(const int blk_addr) { /*load dtb form ram*/ if (dtb_erase_blk == blk_addr && dtb_erase_blk != -1) { return 1; } /*load dtb form flash*/ if (aml_chip_dtb != NULL) { if (aml_chip_dtb->amlnf_dtb.arg_valid == 1 &&\ aml_chip_dtb->amlnf_dtb.valid_blk_addr == blk_addr) { return 1; } } return 0; } #endif /*** *erase whole nand as scrub * start_blk = 0; total_blk; ***/ /* todo need to add bbt here !!!!!! */ static int amlnand_oops_handle(struct amlnand_chip *aml_chip, int flag) { 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); uint64_t erase_len; unsigned erase_shift, write_shift, pages_per_blk; int start_blk,total_blk, ret = 0; int percent=0, percent_complete = -1; unsigned char *buf = NULL; unsigned int buf_size; int last_reserve_blk; buf_size = 0x40000; /*rsv item max size is 256KB*/ buf = aml_nand_malloc(buf_size); if (!buf) { aml_nand_msg("%s() %d: malloc failed", __FUNCTION__, __LINE__); } memset(buf, 0x0, buf_size); /* fixme, should not exit here, 20150801 */ ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->nand_key),buf,(unsigned char *)KEY_INFO_HEAD_MAGIC, aml_chip->keysize); if (ret < 0) { aml_nand_msg("%s() %d invalid nand key\n", __FUNCTION__, __LINE__); goto exit_error0; } #ifdef CONFIG_SECURE_NAND ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->nand_secure),buf,(unsigned char *)SECURE_INFO_HEAD_MAGIC, CONFIG_SECURE_SIZE); if (ret < 0) { aml_nand_msg("invalid nand secure_ptr\n"); goto exit_error0; } #endif erase_shift = ffs(flash->blocksize) - 1; write_shift = ffs(flash->pagesize) - 1; erase_len = ((uint64_t)(flash->chipsize*controller->chip_num))<<20; start_blk = 0; total_blk = (int)(erase_len >> erase_shift); pages_per_blk = (1 << (erase_shift -write_shift)); aml_nand_msg("start_blk =%d,total_blk=%d",start_blk, total_blk); if (flag == NAND_BOOT_ERASE_PROTECT_CACHE) { start_blk = (1024 * flash->pagesize) >> erase_shift; total_blk = get_last_reserve_block(aml_chip); aml_nand_msg("start_blk =%d,total_blk=%d",start_blk, total_blk); } last_reserve_blk = get_last_reserve_block(aml_chip); for (;start_blk< total_blk; start_blk++) { memset((unsigned char *)ops_para, 0x0, sizeof(struct chip_ops_para)); ops_para->page_addr =(((start_blk - start_blk % controller->chip_num) /controller->chip_num)) * pages_per_blk; ops_para->chipnr = start_blk % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ret = operation->block_isbad(aml_chip); if (ret ) { aml_nand_msg("bad block skipping!!!!0x%x",start_blk); //fixme, check is dtb, if dtb ,erase it! if (start_blk < last_reserve_blk && bad_block_is_dtb_blk(start_blk)) { aml_nand_msg("bad block dtb is dtb block:0x%x,not skipping",start_blk); } else { continue; } } //check bbt ret = chipenv_init_erase_protect(aml_chip,flag,start_blk); if (ret) { aml_nand_msg("chipenv block skipping!!!!!!!0x%x", start_blk); continue; } nand_get_chip(aml_chip); ret = operation->erase_block(aml_chip); nand_release_chip(aml_chip); /*need to mark bad block*/ if (ret) { aml_nand_msg("erase fail, marking badblock!!!!!!!0x%x", start_blk); ret = operation->block_markbad(aml_chip); if (ret < 0) { /* todo need to add bbt here !!!!!! */ } //continue; }else{ if (aml_chip->init_flag > 3) { #ifdef SORTING_BAD_BLOCK_D if (flash->new_type == HYNIX_1YNM) { memset((unsigned char *)ops_para, 0x0, sizeof(struct chip_ops_para)); ops_para->page_addr =(((start_blk - start_blk % controller->chip_num) /controller->chip_num)) * pages_per_blk; ops_para->chipnr = start_blk % controller->chip_num; if (start_blk > 4) { nand_get_chip(aml_chip); //aml_nand_msg("test block starting!!!!!!!0x%x", ops_para->page_addr); ret = operation->test_block(aml_chip); nand_release_chip(aml_chip); if (ret < 0) { memset((unsigned char *)ops_para, 0x0, sizeof(struct chip_ops_para)); ops_para->page_addr =(((start_blk - start_blk % controller->chip_num) /controller->chip_num)) * pages_per_blk; ops_para->chipnr = start_blk % controller->chip_num; aml_nand_msg("test block fail, marking badblock!!!!!!!0x%x", start_blk); ret = operation->block_markbad(aml_chip); } } } #endif } } percent = (start_blk * 100) / total_blk; if ((percent != percent_complete) && ((percent %10) == 0)) { percent_complete = percent; aml_nand_msg("nand erasing %d %% --%d %% complete",percent,percent+10); } } exit_error0: if (buf) { kfree(buf); buf = NULL; } return ret; } int phrase_driver_version(unsigned int cp, unsigned int cmp) { int ret=0; #if 0 if (((cp >> 24)&0xff) != ((cp >> 24)&0xff)) { ret = -1; } if (((cp >> 16)&0xff) != ((cp >> 16)&0xff)) { ret = -1; } #endif return ret; } void reset_amlchip_member(struct amlnand_chip *aml_chip) { memset(aml_chip->reserved_blk, 0xff, RESERVED_BLOCK_CNT); memset(&aml_chip->nand_bbtinfo,0x0,sizeof(struct nand_arg_info)); memset(&aml_chip->shipped_bbtinfo,0x0,sizeof(struct nand_arg_info)); memset(&aml_chip->nand_key,0x0,sizeof(struct nand_arg_info)); memset(&aml_chip->nand_secure,0x0,sizeof(struct nand_arg_info)); memset(&aml_chip->config_msg,0x0,sizeof(struct nand_arg_info)); } #endif /* AML_NAND_UBOOT */ u32 aml_info_checksum(u8 *data, int length) { u32 checksum; u8 *pdata; int i; checksum = 0; pdata = (u8 *)data; for (i = 0; i < length; i++) checksum += pdata[i]; return checksum; } static int aml_info_check_datasum(void *data, u8 *name) { int ret = 0; u32 crc = 0; struct block_status *blk_status = NULL; struct shipped_bbt *bbt = NULL; struct nand_config *config = NULL; struct phy_partition_info *phy_part = NULL; if (!memcmp(name, BBT_HEAD_MAGIC, 4)) { blk_status = (struct block_status *)data; crc = blk_status->crc; if (aml_info_checksum((u8 *)(blk_status->blk_status), (MAX_CHIP_NUM*MAX_BLK_NUM)) != crc) { aml_nand_msg("%s :nand bbt bad crc error", __func__); ret = -NAND_READ_FAILED; } } if (!memcmp(name, SHIPPED_BBT_HEAD_MAGIC, 4)) { bbt = (struct shipped_bbt *)data; crc = bbt->crc; if (aml_info_checksum((u8 *)(bbt->shipped_bbt), (MAX_CHIP_NUM*MAX_BAD_BLK_NUM)) != crc) { aml_nand_msg("%s : nand shipped bbt bad crc error", __func__); ret = -NAND_READ_FAILED; } } if (!memcmp(name, CONFIG_HEAD_MAGIC, 4)) { config = (struct nand_config *)data; crc = config->crc; if (aml_info_checksum((u8 *)(config->dev_para), (MAX_DEVICE_NUM*sizeof(struct dev_para))) != crc) { aml_nand_msg("%s : nand check config crc error", __func__); ret = -NAND_READ_FAILED; } } if (!memcmp(name, PHY_PARTITION_HEAD_MAGIC, 4)) { phy_part = (struct phy_partition_info *)data; crc = phy_part->crc; if (aml_info_checksum((u8 *)(phy_part->partition), (MAX_DEVICE_NUM*sizeof(struct _phy_partition))) != crc) { aml_nand_msg("%s : nand check phy partition crc error", __func__); ret = -NAND_READ_FAILED; } } /* others do not checksum at all. */ return ret; } int amlnand_free_block_test(struct amlnand_chip *aml_chip, int start_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); char block_invalid = 0; 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; dat_buf = aml_nand_malloc(flash->pagesize); if (!dat_buf) { aml_nand_msg("amlnand_free_block_test : malloc failed"); block_invalid = 1; ret = -1; goto exit; } memset(dat_buf, 0xa5, flash->pagesize); nand_boot = 1; /* if (boot_device_flag == 0) nand_boot = 0; */ 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(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif /* erase */ memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); tmp_value = start_blk - start_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 = start_blk % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ret = operation->erase_block(aml_chip); if (ret < 0) { aml_nand_msg("nand blk %d check good but erase failed", start_blk); block_invalid = 1; 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 = start_blk - start_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 = start_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 = operation->write_page(aml_chip); if (ret < 0) { aml_nand_msg("%s() %d: nand write failed", __func__, __LINE__); block_invalid = 1; 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 = start_blk - start_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 = start_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 = operation->read_page(aml_chip); if (ret < 0) { aml_nand_msg("nand write failed, %d", block_invalid); block_invalid = 1; ret = -1; goto exit; } aml_nand_dbg("start_blk %d aml_chip->user_page_buf: ", start_blk); /* show_data_buf(aml_chip->user_page_buf); */ aml_nand_dbg("start_blk %d dat_buf: ", start_blk); /* show_data_buf(dat_buf); */ if (memcmp(aml_chip->user_page_buf, dat_buf, flash->pagesize)) { block_invalid = 1; ret = -1; aml_nand_msg("free blk %d, page %d : test failed", start_blk, t); goto exit; } } exit: #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif if (dat_buf) { aml_nand_free(dat_buf); dat_buf = NULL; } if (!ret) aml_nand_msg("free blk start_blk %d test OK", start_blk); return ret; } int get_last_reserve_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; u32 offset, start_blk, blk_addr, tmp_blk, pages_per_blk; u8 phys_erase_shift, phys_page_shift; int ret = 0; u32 tmp_value; static u32 total_blk = 0, scan_flag = 0; if ((total_blk > RESERVED_BLOCK_CNT) && (scan_flag == 1)) { aml_nand_dbg("total_blk:%d",total_blk); return total_blk; } if (aml_chip->nand_bbtinfo.arg_valid) { scan_flag = 1; } 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)); memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); start_blk = (offset >> phys_erase_shift); tmp_blk = total_blk = start_blk; blk_addr = 0; /* decide the total block_addr */ while (blk_addr < RESERVED_BLOCK_CNT) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); tmp_value = total_blk - total_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 = total_blk % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ret = operation->block_isbad(aml_chip); if (ret == NAND_BLOCK_FACTORY_BAD) { aml_nand_dbg("blk %d is shipped bad block ", total_blk); total_blk++; continue; } total_blk++; blk_addr++; } return total_blk; } int repair_reserved_bad_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; u32 offset, start_blk, total_blk, blk_addr, tmp_blk; u32 pages_per_blk, blk_used_bad_cnt = 0; u8 phys_erase_shift, phys_page_shift; int ret = 0, i = 0, j = 0; u32 bad_blk[128]; u8 *dat_buf = NULL; u8 *oob_buf = NULL; u32 tmp_value; memset(bad_blk, 0, 128*sizeof(u32)); 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)); memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); start_blk = (offset >> phys_erase_shift); tmp_blk = total_blk = start_blk; dat_buf = aml_nand_malloc(flash->pagesize); if (!dat_buf) { aml_nand_msg("amlnand_free_block_test : malloc failed"); ret = -1; return ret; } memset(dat_buf, 0, flash->pagesize); oob_buf = aml_nand_malloc(flash->oobsize); if (!oob_buf) { aml_nand_msg("amlnand_free_block_test : malloc failed"); ret = -1; kfree(dat_buf); return ret; } memset(oob_buf, 0, flash->oobsize); blk_addr = 0; /* decide the total block_addr */ while (blk_addr < RESERVED_BLOCK_CNT) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); tmp_value = total_blk - total_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 = total_blk % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ret = operation->block_isbad(aml_chip); if (ret == NAND_BLOCK_FACTORY_BAD) { aml_nand_msg("blk %d is shipped bad block ", total_blk); total_blk++; continue; } if (ret == NAND_BLOCK_USED_BAD) { if (blk_used_bad_cnt < 128) { bad_blk[blk_used_bad_cnt] = total_blk; blk_used_bad_cnt++; } } total_blk++; blk_addr++; } if (blk_used_bad_cnt > 6) { nand_get_chip(aml_chip); aml_nand_msg("repair badblk of reserved,blk_used_bad_cnt=%d\n", blk_used_bad_cnt); for (i = 0; i < blk_used_bad_cnt; i++) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); tmp_value = bad_blk[i]-bad_blk[i]%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 = bad_blk[i] % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ret = operation->blk_modify_bbt_chip_op(aml_chip, 0); /* erase */ ret = operation->erase_block(aml_chip); if (ret) { ret = operation->blk_modify_bbt_chip_op( aml_chip, 1); aml_nand_msg("test blk %d fail\n", bad_blk[i]); continue; } /* write */ ops_para->page_addr = tmp_value * pages_per_blk; ops_para->data_buf = dat_buf; ops_para->oob_buf = oob_buf; for (j = 0; j < pages_per_blk; j++) { ops_para->page_addr += 1; memset(dat_buf, 0, flash->pagesize); memset(oob_buf, 0, flash->oobsize); ret = operation->write_page(aml_chip); if (ret) { ops_para->page_addr = tmp_value * pages_per_blk; ret = operation->blk_modify_bbt_chip_op( aml_chip, 1); aml_nand_msg("test blk %d fail\n", bad_blk[i]); goto write_read_fail; } } /* read */ ops_para->page_addr = tmp_value * pages_per_blk; ops_para->data_buf = dat_buf; ops_para->oob_buf = oob_buf; for (j = 0; j < pages_per_blk; j++) { ops_para->page_addr += 1; memset(dat_buf, 0, flash->pagesize); memset(oob_buf, 0, flash->oobsize); ret = operation->read_page(aml_chip); if ((ops_para->ecc_err) || (ret < 0)) { ops_para->page_addr = tmp_value * pages_per_blk; ret = operation->blk_modify_bbt_chip_op( aml_chip, 1); aml_nand_msg("test blk %d fail\n", bad_blk[i]); goto write_read_fail; } } /* erase */ ops_para->page_addr = tmp_value * pages_per_blk; ret = operation->erase_block(aml_chip); if (ret) { ret = operation->blk_modify_bbt_chip_op( aml_chip, 1); aml_nand_msg("test blk %d fail\n", bad_blk[i]); continue; } aml_nand_msg("test blk %d OK\n", bad_blk[i]); write_read_fail: ; } nand_release_chip(aml_chip); amlnand_update_bbt(aml_chip); } kfree(dat_buf); kfree(oob_buf); return total_blk; } /***************************************************************************** *Name :amlnand_get_free_block *Description :search a good block by skip the shipped bad block *Parameter : *Return : *Note : *****************************************************************************/ int amlnand_get_free_block(struct amlnand_chip *aml_chip, u32 block) { 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; u32 offset, nand_boot, start_blk, total_blk; u32 blk_addr, tmp_blk, pages_per_blk; u8 phys_erase_shift, phys_page_shift; u8 blk_used_flag = 0; int ret = 0, i; u32 tmp_value; nand_boot = 1; /*if(boot_device_flag == 0) { nand_boot = 0; }*/ 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)); memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); start_blk = (offset >> phys_erase_shift); tmp_blk = total_blk = start_blk; total_blk = get_last_reserve_block(aml_chip); blk_addr = 0; while ((blk_addr < 1) && (start_blk < total_blk)) { for (i = 0; i < RESERVED_BLOCK_CNT; i++) { if (aml_chip->reserved_blk[i] == start_blk) { /* aml_nand_msg("nand blk %d is used", start_blk); */ blk_used_flag = 1; break; } else blk_used_flag = 0; } if (blk_used_flag) { start_blk++; continue; } if (block == start_blk) { start_blk++; continue; } memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); tmp_value = start_blk - start_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 = start_blk % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ret = operation->block_isbad(aml_chip); if (ret == NAND_BLOCK_FACTORY_BAD) { aml_nand_msg("blk %d is shipped bad block ", start_blk); start_blk++; continue; } if (ret == NAND_BLOCK_USED_BAD) { aml_nand_msg("blk %d is used bad block ", start_blk); start_blk++; continue; } /* ret = amlnand_free_block_test(aml_chip, start_blk); if (ret) { aml_nand_msg("nand get free block %d invalid", start_blk); start_blk++; continue; }*/ if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); ret = operation->erase_block(aml_chip); if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); if (ret < 0) { aml_nand_msg("nand blk %d check good but erase failed", start_blk); ret = operation->block_markbad(aml_chip); start_blk++; continue; } else aml_nand_dbg("nand get free block at %d", start_blk); blk_addr++; } if (start_blk >= total_blk) { ret = -NAND_BAD_BLOCK_FAILURE; aml_nand_msg("nand can not find free block"); } return (ret == 0) ? start_blk : ret; } void amlnand_info_error_handle(struct amlnand_chip *aml_chip) { struct nand_arg_info *nand_bbt = &aml_chip->nand_bbtinfo; struct nand_arg_info *nand_config = &aml_chip->config_msg; int ret = 0; if ((nand_bbt->arg_valid) && (nand_bbt->update_flag)) { /* aml_nand_msg("amlnand_info_error_handle : update bbt"); */ ret = amlnand_update_bbt(aml_chip); nand_bbt->update_flag = 0; aml_nand_msg("NAND UPDATE CKECK : arg %s:", "bbt"); aml_nand_msg("arg_valid=%d,valid_blk_addr=%d,valid_pg_addr=%d", nand_bbt->arg_valid, nand_bbt->valid_blk_addr, nand_bbt->valid_page_addr); if (ret) { aml_nand_msg("%s: nand update bbt failed", __func__); return; } } if ((nand_config->arg_valid) && (nand_config->update_flag)) { /* aml_nand_msg("amlnand_info_error_handle : update nand config"); */ aml_chip->config_ptr->crc = aml_info_checksum( (u8 *)(aml_chip->config_ptr->dev_para), (MAX_DEVICE_NUM*sizeof(struct dev_para))); ret = amlnand_save_info_by_name(aml_chip, (u8 *) &(aml_chip->config_msg), (u8 *)aml_chip->config_ptr, (u8 *)CONFIG_HEAD_MAGIC, sizeof(struct nand_config)); nand_config->update_flag = 0; aml_nand_msg("NAND UPDATE CKECK: arg %s:", "config"); aml_nand_msg("arg_valid=%d,valid_blk_addr=%d,valid_pg_addr=%d", nand_config->arg_valid, nand_config->valid_blk_addr, nand_config->valid_page_addr); if (ret < 0) { aml_nand_msg("save nand dev_configs failed and ret:%d", ret); return; } } return; } int amlnand_erase_info_by_name(struct amlnand_chip *aml_chip, u8 *info, u8 *name) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct chip_operation *operation = &aml_chip->operation; struct chip_ops_para *ops_para = &aml_chip->ops_para; struct nand_arg_info *arg_info = (struct nand_arg_info *)info; struct en_slc_info *slc_info = &(controller->slc_info); u8 phys_erase_shift, phys_page_shift, nand_boot; u32 offset; u32 pages_per_blk; u8 oob_buf[sizeof(struct nand_arg_oobinfo)]; u16 start_blk, tmp_blk; int ret = 0; u32 tmp_value; nand_boot = 1; if (nand_boot) offset = (1024 * flash->pagesize); else offset = 0; memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); phys_erase_shift = ffs(flash->blocksize) - 1; phys_page_shift = ffs(flash->pagesize) - 1; pages_per_blk = (1 << (phys_erase_shift - phys_page_shift)); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) ops_para->option |= DEV_SLC_MODE; start_blk = (offset >> phys_erase_shift); tmp_blk = start_blk; if (arg_info->arg_valid == 1) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) ops_para->option |= DEV_SLC_MODE; ops_para->data_buf = aml_chip->user_page_buf; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); memset((u8 *)ops_para->data_buf, 0x0, flash->pagesize); memset((u8 *)ops_para->oob_buf, 0x0, sizeof(oob_buf)); /* calculate address */ tmp_value = arg_info->valid_blk_addr; tmp_value = tmp_value-tmp_value % 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 = arg_info->valid_blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif /* AML_NAND_UBOOT */ /* erase block ! */ ret = operation->erase_block(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif if (ret < 0) { aml_nand_msg("erase arg %s fail,chip%d page=%d", name, ops_para->chipnr, ops_para->page_addr); } arg_info->arg_valid = 0; } return ret; } int amlnand_read_info_by_name(struct amlnand_chip *aml_chip, u8 *info, u8 *buf, u8 *name, u32 size) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct chip_operation *operation = &aml_chip->operation; struct chip_ops_para *ops_para = &aml_chip->ops_para; struct en_slc_info *slc_info = &(controller->slc_info); struct nand_arg_info *arg_info = (struct nand_arg_info *)info; struct nand_arg_oobinfo *arg_oob_info; u8 phys_erase_shift, phys_page_shift, nand_boot; u32 offset, offset_tmp; u32 pages_per_blk, amount_loaded = 0; u8 oob_buf[sizeof(struct nand_arg_oobinfo)]; u16 start_blk, tmp_blk; int ret = 0, len; u32 tmp_value, tmp_index; nand_boot = 1; if (nand_boot) offset = (1024 * flash->pagesize); else offset = 0; arg_oob_info = (struct nand_arg_oobinfo *)oob_buf; memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); phys_erase_shift = ffs(flash->blocksize) - 1; phys_page_shift = ffs(flash->pagesize) - 1; pages_per_blk = (1 << (phys_erase_shift - phys_page_shift)); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) ops_para->option |= DEV_SLC_MODE; #if 0 if (ops_para->option & DEV_SLC_MODE) pages_read = pages_per_blk >> 1; else pages_read = pages_per_blk; #endif //0 start_blk = (offset >> phys_erase_shift); tmp_blk = start_blk; /*total_blk = (offset >> phys_erase_shift) + RESERVED_BLOCK_CNT; */ if (arg_info->arg_valid == 1) { /* load bbt */ offset_tmp = 0; while (amount_loaded < size) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) ops_para->option |= DEV_SLC_MODE; ops_para->data_buf = aml_chip->user_page_buf; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); memset((u8 *)ops_para->data_buf, 0x0, flash->pagesize); memset((u8 *)ops_para->oob_buf, 0x0, sizeof(oob_buf)); tmp_value = arg_info->valid_blk_addr; tmp_value = tmp_value-tmp_value % controller->chip_num; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; ops_para->page_addr = (arg_info->valid_page_addr + tmp_value * pages_per_blk) + offset_tmp; /* aml_nand_msg("read reserve info arg_info->valid_page_addr:%d",arg_info->valid_page_addr); */ 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)) || (slc_info->micron_l0l3_mode == 1)) { tmp_index = ops_para->page_addr % 256; ops_para->page_addr = tmp_value | slc_info->pagelist[tmp_index]; } if (flash->new_type == SANDISK_19NM) ops_para->page_addr = tmp_value | ((ops_para->page_addr % pages_per_blk) << 1); } ops_para->chipnr = arg_info->valid_blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif /* AML_NAND_UBOOT */ /* aml_nand_msg("read reserve info ops_para->page_addr:%d",ops_para->page_addr); */ ret = operation->read_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif if ((ops_para->ecc_err) || (ret < 0)) { aml_nand_msg("read arg %s fail,chip%d page=%d", name, ops_para->chipnr, ops_para->page_addr); goto exit_error0; } memcpy((u8 *)arg_oob_info, aml_chip->user_oob_buf, sizeof(oob_buf)); if (!memcmp(arg_oob_info->name, name, 4)) { len = min(flash->pagesize, size-amount_loaded); memcpy(((u8 *)(buf)+amount_loaded), (u8 *)aml_chip->user_page_buf, len); } offset_tmp += 1; amount_loaded += flash->pagesize; } } return ret; exit_error0: return ret; } int amlnand_save_info_by_name(struct amlnand_chip *aml_chip, u8 *info, u8 *buf, u8 *name, u32 size) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct chip_operation *operation = &aml_chip->operation; struct chip_ops_para *ops_para = &aml_chip->ops_para; struct en_slc_info *slc_info = &(controller->slc_info); struct nand_arg_info *arg_info = (struct nand_arg_info *)info; struct nand_arg_oobinfo *arg_oob_info; u32 len, offset, offset_tmp, nand_boot, blk_addr = 0; u32 tmp_blk_addr, amount_saved = 0; u32 pages_per_blk, arg_pages, pages_read, tmp_pages; u8 phys_erase_shift, phys_page_shift; u8 oob_buf[sizeof(struct nand_arg_oobinfo)]; u16 tmp_blk; u32 tmp_addr, temp_option; u8 temp_ran_mode; int full_page_flag = 0, ret = 0, i = 0, test_cnt = 0, j; int extra_page = 0, write_page_cnt = 0, temp_page_num = 0; u32 index,tmp_value = 0; unsigned char rand_val[4] = {0, 0, 0, 0}, rand_flag = 0; ENV_NAND_LINE nand_boot = 1; /*if (strcmp((char*)name, (char*)SHIPPED_BBT_HEAD_MAGIC) == 0) { aml_nand_msg("fbbt size: %d",size); }*/ if (nand_boot) offset = (1024 * flash->pagesize); else offset = 0; ENV_NAND_LINE phys_erase_shift = ffs(flash->blocksize) - 1; phys_page_shift = ffs(flash->pagesize) - 1; pages_per_blk = (1 << (phys_erase_shift - phys_page_shift)); aml_nand_msg("name %s, size:%d", name, size); arg_pages = ((size>>phys_page_shift) + 1); tmp_pages = arg_pages; if ((size%flash->pagesize) == 0) extra_page = 1; else extra_page = 0; if (flash->option & NAND_USE_SHAREPAGE_MODE) { if ((arg_pages % 2) != 0) { arg_pages += 1; extra_page += 1; } } /* aml_nand_msg("extra_page:%d",extra_page); //1 */ tmp_blk = (offset >> phys_erase_shift); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) 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; write_again: arg_oob_info = (struct nand_arg_oobinfo *) oob_buf; arg_info->timestamp += 1; arg_oob_info->timestamp = arg_info->timestamp; memcpy(arg_oob_info->name, name, 4); memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); get_free_blk: /* get new block according to arg_type or update_flag */ /* aml_nand_msg("arg_info->arg_valid:%d,arg_info->update_flag:%d,arg_info->valid_page_addr:%x",arg_info->arg_valid,arg_info->update_flag,arg_info->valid_page_addr); */ if ((arg_info->arg_valid) && (!arg_info->update_flag) && (arg_info->arg_type == FULL_PAGE)) { if ((arg_info->valid_page_addr + 2 * arg_pages) > pages_read) { ret = amlnand_get_free_block(aml_chip, blk_addr); blk_addr = ret; if (ret < 0) { aml_nand_msg("nand get free block failed"); ret = -NAND_BAD_BLOCK_FAILURE; goto exit_error0; } aml_nand_msg("nand get free block0 at %d", blk_addr); full_page_flag = 1; } else blk_addr = arg_info->valid_blk_addr; } else { ret = amlnand_get_free_block(aml_chip, blk_addr); blk_addr = ret; aml_nand_msg("%s, %d: new blk %d", __func__, __LINE__, blk_addr); if (ret < 0) { aml_nand_msg("nand get free block failed"); ret = -NAND_BAD_BLOCK_FAILURE; goto exit_error0; } aml_nand_msg("nand get free block1 at %d", blk_addr); } /* show_data_buf(buf); */ if (arg_info->arg_type == FULL_BLK) { for (i = 0; i < pages_read;) { if ((pages_read - i) < arg_pages) { if (flash->new_type == HYNIX_1YNM) { /* for slc mode, if not full block write, need write dummy random data to lock data */ memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); ops_para->option |= DEV_SLC_MODE; tmp_value = blk_addr; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; tmp_value *= pages_per_blk; ops_para->page_addr = tmp_value + i; tmp_value = ~(pages_per_blk - 1); tmp_value &= ops_para->page_addr; index = ops_para->page_addr % 256; ops_para->page_addr = tmp_value | (slc_info->pagelist[index]); ops_para->chipnr = blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ops_para->data_buf = aml_chip->user_page_buf; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); memset(aml_chip->user_oob_buf, 0x5a, sizeof(oob_buf)); memset(aml_chip->user_page_buf, 0x5a, flash->pagesize); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else nand_get_chip(aml_chip); #endif aml_nand_msg("dummy random data,i=%d pg_addr=%x", i, ops_para->page_addr); ret = operation->write_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif /* write dummy page paried */ tmp_addr = ops_para->page_addr; temp_ran_mode = controller->ran_mode; temp_option = ops_para->option; controller->ran_mode = 0; ops_para->page_addr += 1; aml_nand_msg("dummy random paired data,i=%d pg_addr=%x", i, ops_para->page_addr); ops_para->option |= DEV_ECC_SOFT_MODE; ops_para->option &= DEV_SERIAL_CHIP_MODE; memset(aml_chip->user_page_buf, 0xff, flash->pagesize); memset(aml_chip->user_oob_buf, 0xff, sizeof(oob_buf)); #ifdef AML_NAND_UBOOT nand_get_chip(aml_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(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif controller->ran_mode = temp_ran_mode; ops_para->page_addr = tmp_addr; ops_para->option = temp_option; } break; } offset_tmp = 0; amount_saved = 0; while (amount_saved < (size+extra_page*flash->pagesize)) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) ops_para->option |= DEV_SLC_MODE; tmp_value = blk_addr; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; tmp_value *= pages_per_blk; tmp_value += offset_tmp + i; ops_para->page_addr = tmp_value; if ((ops_para->option & DEV_SLC_MODE)) { tmp_value = ops_para->page_addr; tmp_value &= (~(pages_per_blk - 1)); if (((flash->new_type > 0) && (flash->new_type < 10)) || (slc_info->micron_l0l3_mode == 1)) { index = ops_para->page_addr % 256; ops_para->page_addr = tmp_value | (slc_info->pagelist[index]); } if (flash->new_type == SANDISK_19NM) { index = ops_para->page_addr; index %= pages_per_blk; index <<= 0x01; ops_para->page_addr = tmp_value | index; } } ops_para->chipnr = blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ops_para->data_buf = aml_chip->user_page_buf; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); len = min(flash->pagesize, size + extra_page*flash->pagesize - amount_saved); memset(aml_chip->user_page_buf, 0x0, flash->pagesize); memset(aml_chip->user_oob_buf, 0x0, sizeof(oob_buf)); memcpy((u8 *)aml_chip->user_page_buf, ((u8 *)(buf) + amount_saved), len); memcpy(aml_chip->user_oob_buf, (u8 *)arg_oob_info, sizeof(oob_buf)); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif /* aml_nand_msg("ops_para->page_addr:%d",ops_para->page_addr); */ ret = operation->write_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif if (ret < 0) { aml_nand_msg("%s %d: nand write failed", __func__, __LINE__); if (test_cnt >= 3) { aml_nand_msg("test 3 times"); break; } ret = operation->test_block_reserved(aml_chip, blk_addr); test_cnt++; if (ret) { ret = operation->block_markbad(aml_chip); if (ret < 0) aml_nand_msg("nand mark bad blk=%d", blk_addr); } aml_nand_msg("rewrite!"); goto get_free_blk; } /*for slc mode*/ if (flash->new_type == HYNIX_1YNM) { temp_page_num = offset_tmp + i; if (temp_page_num >= 1) { ops_para->chipnr = blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); tmp_addr = ops_para->page_addr; temp_ran_mode = controller->ran_mode; temp_option = ops_para->option; controller->ran_mode = 0; ops_para->page_addr += 1; ops_para->option |= DEV_ECC_SOFT_MODE; ops_para->option &= DEV_SERIAL_CHIP_MODE; memset(aml_chip->user_page_buf, 0xff, flash->pagesize); memset(aml_chip->user_oob_buf, 0xff, sizeof(oob_buf)); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif /* aml_nand_msg("write pg=%d pgaddr=%x", temp_page_num, ops_para->page_addr); */ ret = operation->write_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif controller->ran_mode = temp_ran_mode; ops_para->page_addr = tmp_addr; ops_para->option = temp_option; } } else if (slc_info->micron_l0l3_mode == 1) { temp_page_num = offset_tmp + i; if ((temp_page_num >= 5) && ((ops_para->page_addr % 2)!=0)) { ops_para->chipnr = blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); tmp_addr = ops_para->page_addr; temp_ran_mode = controller->ran_mode; temp_option = ops_para->option; controller->ran_mode = 0; ops_para->option |= DEV_ECC_SOFT_MODE; ops_para->option &= DEV_SERIAL_CHIP_MODE; /*disable micron internal scram*/ if (flash->new_type == MICRON_20NM) { rand_val[0] = 0; rand_flag = 1; operation->set_onfi_para( aml_chip, rand_val, 0x92); } for (j=1; j<=2; j++) { ops_para->page_addr += 1; memset(aml_chip->user_page_buf, 0xff, flash->pagesize); memset(aml_chip->user_oob_buf, 0xff, sizeof(oob_buf)); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif /* aml_nand_msg("write micron temp%d pg%x", temp_page_num, ops_para->page_addr); */ ret = operation->write_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif } /*enable micron internal scram */ if (rand_flag == 1) { rand_val[0] = 1; operation->set_onfi_para( aml_chip, rand_val, 0x92); } controller->ran_mode = temp_ran_mode; ops_para->page_addr = tmp_addr; ops_para->option = temp_option; } } offset_tmp += 1; amount_saved += flash->pagesize; } i += arg_pages; if (ret < 0) break; } } else if (arg_info->arg_type == FULL_PAGE) { offset_tmp = 0; amount_saved = 0; while (amount_saved < size+extra_page*flash->pagesize) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) ops_para->option |= DEV_SLC_MODE; tmp_value = blk_addr; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; tmp_value *= pages_per_blk; ops_para->page_addr = tmp_value + offset_tmp; /* aml_nand_msg("ops_para->page_addraaa:%d",ops_para->page_addr); */ if (arg_info->arg_valid && (!full_page_flag) && (!arg_info->update_flag)){ ops_para->page_addr +=(arg_info->valid_page_addr + arg_pages); /* aml_nand_msg("ops_para->page_addrbb:%d,arg_info->valid_page_addr:%d",ops_para->page_addr,arg_info->valid_page_addr); */ } /*for slc mode*/ if (flash->new_type == HYNIX_1YNM) { if (arg_info->arg_valid && (!full_page_flag) && (!arg_info->update_flag)) { ops_para->page_addr += 1; temp_page_num = ops_para->page_addr % 256; } } if ((ops_para->option & DEV_SLC_MODE)) { tmp_value = ops_para->page_addr; tmp_value &= (~(pages_per_blk - 1)); if (((flash->new_type > 0) && (flash->new_type < 10)) || (slc_info->micron_l0l3_mode == 1)) { index = ops_para->page_addr % 256; ops_para->page_addr = tmp_value | (slc_info->pagelist[index]); } if (flash->new_type == SANDISK_19NM) { index = ops_para->page_addr; index %= pages_per_blk; index <<= 0x01; ops_para->page_addr = tmp_value | index; } } ops_para->chipnr = blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ops_para->data_buf = aml_chip->user_page_buf; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); len = min(flash->pagesize, size + extra_page*flash->pagesize - amount_saved); memset(aml_chip->user_page_buf, 0x0, flash->pagesize); memset(aml_chip->user_oob_buf, 0x0, sizeof(oob_buf)); if (offset_tmp == tmp_pages) { memset(aml_chip->user_page_buf, 0xa5, flash->pagesize); }else{ memcpy((u8 *)aml_chip->user_page_buf, ((u8 *)(buf) + amount_saved), len); } memcpy(aml_chip->user_oob_buf, (u8 *)arg_oob_info, sizeof(oob_buf)); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif /* aml_nand_msg("ops_para->page_addr:%d",ops_para->page_addr); */ ret = operation->write_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif if (ret < 0) { aml_nand_msg("%s() %d: nand write failed", __func__, __LINE__); if (test_cnt >= 3) { aml_nand_msg("test blk 3times"); break; } ret = operation->test_block_reserved(aml_chip, blk_addr); test_cnt++; if (ret) { ret = operation->block_markbad(aml_chip); if (ret < 0) aml_nand_msg("mark bad blk %d", blk_addr); } goto get_free_blk; } /*for slc mode*/ if (flash->new_type == HYNIX_1YNM) { if (temp_page_num >= 1) { ops_para->chipnr = blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); tmp_addr = ops_para->page_addr; temp_ran_mode = controller->ran_mode; temp_option = ops_para->option; controller->ran_mode = 0; ops_para->page_addr += 1; ops_para->option |= DEV_ECC_SOFT_MODE; ops_para->option &= DEV_SERIAL_CHIP_MODE; memset(aml_chip->user_page_buf, 0xff, flash->pagesize); memset(aml_chip->user_oob_buf, 0xff, sizeof(oob_buf)); #ifndef AML_UBOOT_NAND nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif aml_nand_msg("pgnum=%d pgaddr=%x", temp_page_num, ops_para->page_addr); ret = operation->write_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif controller->ran_mode = temp_ran_mode; ops_para->page_addr = tmp_addr; ops_para->option = temp_option; } } else if (slc_info->micron_l0l3_mode == 1) { if (((ops_para->page_addr%pages_per_blk) >= 5) &&((ops_para->page_addr%2)!=0)) { ops_para->chipnr = blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); tmp_addr = ops_para->page_addr; temp_ran_mode = controller->ran_mode; temp_option = ops_para->option; controller->ran_mode = 0; /*disable micron internal scram*/ if (flash->new_type == MICRON_20NM) { rand_val[0] = 0; rand_flag = 1; operation->set_onfi_para( aml_chip, rand_val, 0x92); } for (j=1; j<=2; j++) { ops_para->page_addr += 1; ops_para->option |= DEV_ECC_SOFT_MODE; ops_para->option &= DEV_SERIAL_CHIP_MODE; memset(aml_chip->user_page_buf, 0xff, flash->pagesize); memset(aml_chip->user_oob_buf, 0xff, sizeof(oob_buf)); #ifndef AML_UBOOT_NAND nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif /* aml_nand_msg("write micron pg%x", ops_para->page_addr); */ ret = operation->write_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif } /*enable micron internal scram */ if (rand_flag == 1) { rand_val[0] = 1; operation->set_onfi_para( aml_chip, rand_val, 0x92); } controller->ran_mode = temp_ran_mode; ops_para->page_addr = tmp_addr; ops_para->option = temp_option; } } offset_tmp += 1; amount_saved += flash->pagesize; if (amount_saved >= size+ extra_page*flash->pagesize) { if (flash->new_type == HYNIX_1YNM) { /* for slc mode, if not full block write, need write dummy random data to lock data */ memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); ops_para->option |= DEV_SLC_MODE; tmp_value = blk_addr; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; tmp_value *= pages_per_blk; ops_para->page_addr = tmp_value + temp_page_num + 1; tmp_value = ops_para->page_addr; tmp_value &= (~(pages_per_blk - 1)); index = ops_para->page_addr % 256; ops_para->page_addr = tmp_value | (slc_info->pagelist[index]); ops_para->chipnr = blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ops_para->data_buf = aml_chip->user_page_buf; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); memset(aml_chip->user_page_buf, 0x5a, flash->pagesize); memset(aml_chip->user_oob_buf, 0x5a, sizeof(oob_buf)); if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); aml_nand_dbg("dummy rand data:pg%d pg%x", temp_page_num, ops_para->page_addr); ret = operation->write_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif /* write dummy page paried */ tmp_addr = ops_para->page_addr; temp_ran_mode = controller->ran_mode; temp_option = ops_para->option; controller->ran_mode = 0; ops_para->page_addr += 1; ops_para->option |= DEV_ECC_SOFT_MODE; ops_para->option &= DEV_SERIAL_CHIP_MODE; memset(aml_chip->user_page_buf, 0xff, flash->pagesize); memset(aml_chip->user_oob_buf, 0xff, sizeof(oob_buf)); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif aml_nand_msg("dummy rand paired data:pg%d pg%x", temp_page_num, ops_para->page_addr); ret = operation->write_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif controller->ran_mode = temp_ran_mode; ops_para->page_addr = tmp_addr; ops_para->option = temp_option; } } } } if (ret < 0) { /* write failed */ aml_nand_msg(" NAND SAVE :arg %s faild at blk:%d", name, blk_addr); ret = -NAND_WRITE_FAILED; goto exit_error0; } else { /* write success and update reserved_blk table */ if ((arg_info->arg_valid == 0) || (arg_info->arg_type == FULL_BLK) || ((arg_info->arg_type == FULL_PAGE) && (full_page_flag || arg_info->update_flag))) { for (i = 0; i < RESERVED_BLOCK_CNT; i++) { if (aml_chip->reserved_blk[i] == 0xff) { aml_nand_dbg("update blk %s blk %d", name, blk_addr); aml_chip->reserved_blk[i] = blk_addr; break; } } } #if 0 for (i = 0; i < RESERVED_BLOCK_CNT; i++) aml_nand_dbg("aml_chip->reserved_blk[%d]=%d ", i, aml_chip->reserved_blk[i]); #endif /* write success and update arg_info */ tmp_blk_addr = arg_info->valid_blk_addr; arg_info->valid_blk_addr = blk_addr; if ((arg_info->arg_type == FULL_PAGE) && (arg_info->arg_valid)) { if (full_page_flag || arg_info->update_flag) arg_info->valid_page_addr = 0; else{ arg_info->valid_page_addr += arg_pages; if (flash->new_type == HYNIX_1YNM) arg_info->valid_page_addr += 1; } } else if ((arg_info->arg_type == FULL_BLK) && (arg_info->arg_valid)) arg_info->valid_page_addr = 0; aml_nand_dbg("NAND SAVE : arg %s success at blk:%d", name, blk_addr); /* erase old info block */ if ((arg_info->arg_type == FULL_BLK) || ((arg_info->arg_type == FULL_PAGE) && (full_page_flag || arg_info->update_flag))) { if ((arg_info->arg_valid) && (tmp_blk_addr != 0)) { aml_nand_dbg("nand erase old arg %s blk at %d", name, tmp_blk_addr); tmp_value = tmp_blk_addr; 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 = tmp_blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif ret = operation->erase_block(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif if (ret < 0) { aml_nand_msg("blk %d erase failed", tmp_blk_addr); ret = operation->test_block_reserved(aml_chip, tmp_blk_addr); if (ret) { ret = operation->block_markbad(aml_chip); if (ret < 0) { aml_nand_msg("mark failed,blk=%d", tmp_blk_addr); goto exit_error0; } } } for (i = 0; i < RESERVED_BLOCK_CNT; i++) { if (aml_chip->reserved_blk[i] == tmp_blk_addr) { aml_chip->reserved_blk[i] = 0xff; break; } } #if 0 for (i = 0; i < RESERVED_BLOCK_CNT; i++) aml_nand_dbg("reserved_blk[%d]=%d ", i, aml_chip->reserved_blk[i]); #endif } } /* add 'flash->blocksize > 0x40000' here,nand flash which blocksize is smaller than 256KB(slc flash) shouldn't write again. */ if ((arg_info->arg_type == FULL_PAGE) && (flash->blocksize > 0x40000)) { if (write_page_cnt == 0) { arg_info->arg_valid = 1; full_page_flag = 0; arg_info->update_flag = 0; write_page_cnt = 1; goto write_again; } } arg_info->arg_valid = 1; /* SAVE SET VALID */ full_page_flag = 0; } exit_error0: return ret; } void show_data_buf(u8 *buf) { int i = 0; for (i = 0; i < 10; i++) aml_nand_dbg("buf[%d]= %d", i, buf[i]); return; } int amlnand_check_info_by_name(struct amlnand_chip *aml_chip, u8 *info, u8 *name, u32 size) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct chip_operation *operation = &aml_chip->operation; struct chip_ops_para *ops_para = &aml_chip->ops_para; struct en_slc_info *slc_info = &(controller->slc_info); struct nand_arg_info *arg_info = (struct nand_arg_info *)info; struct nand_arg_oobinfo *arg_oob_info; u8 phys_erase_shift, phys_page_shift, nand_boot; u32 i, offset, pages_per_blk, pages_read; u8 oob_buf[sizeof(struct nand_arg_oobinfo)]; u16 start_blk, total_blk, tmp_blk; int ret = 0, read_failed_page = 0, read_middle_page_failed = 0; u32 tmp_value, index; u32 arg_pages; nand_boot = 1; if (nand_boot) offset = (1024 * flash->pagesize); else offset = 0; ENV_NAND_LINE arg_oob_info = (struct nand_arg_oobinfo *)oob_buf; memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); phys_erase_shift = ffs(flash->blocksize) - 1; phys_page_shift = ffs(flash->pagesize) - 1; pages_per_blk = (1 << (phys_erase_shift - phys_page_shift)); arg_pages = ((size>>phys_page_shift) + 1); if (flash->option & NAND_USE_SHAREPAGE_MODE) { if ((arg_pages % 2) != 0) { arg_pages += 1; } } if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) 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; start_blk = (offset >> phys_erase_shift); tmp_blk = start_blk; #if 0 total_blk = (offset >> phys_erase_shift) + RESERVED_BLOCK_CNT; #else total_blk = get_last_reserve_block(aml_chip); #endif ENV_NAND_LINE for (; start_blk < total_blk; start_blk++) { read_failed_page = 0; read_middle_page_failed = 0; memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); tmp_value = start_blk; 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 = start_blk % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); ret = operation->block_isbad(aml_chip); if (ret) { aml_nand_dbg("blk %d is bad ", start_blk); continue; } for (i = 0; i < pages_read;) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) ops_para->option |= DEV_SLC_MODE; ops_para->page_addr = (i + tmp_value * pages_per_blk); ops_para->chipnr = start_blk % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); if ((ops_para->option & DEV_SLC_MODE)) { index = ops_para->page_addr; index &= (~(pages_per_blk - 1)); if (((flash->new_type > 0) && (flash->new_type < 10)) || (slc_info->micron_l0l3_mode == 1)) ops_para->page_addr = index | (slc_info->pagelist[ops_para->page_addr % 256]); if (flash->new_type == SANDISK_19NM) ops_para->page_addr = index | ((ops_para->page_addr % pages_per_blk) << 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); memset(ops_para->data_buf, 0x0, flash->pagesize); memset(ops_para->oob_buf, 0x0, sizeof(oob_buf)); ENV_NAND_LINE nand_get_chip(aml_chip); ENV_NAND_LINE ret = operation->read_page(aml_chip); ENV_NAND_LINE nand_release_chip(aml_chip); if ((ops_para->ecc_err) || (ret < 0)) { aml_nand_msg("blk check good but read failed"); aml_nand_msg("chip%d page=%d", ops_para->chipnr, ops_para->page_addr); read_failed_page++; //i += (size >> phys_page_shift) + 1; ///////////////////////////// i += arg_pages; if ((read_failed_page > 8) && (read_middle_page_failed == 0)) { aml_nand_msg("failed_pg=%d", read_failed_page); i = arg_pages * //////////////////////// ((pages_per_blk/arg_pages)>>1); //////////////////////// read_middle_page_failed = -1; } else if (read_middle_page_failed == -1) { aml_nand_msg("failed_page=%d", read_failed_page); i = arg_pages * ///////////////////////////// (pages_per_blk/arg_pages);/////////////////////////////////// read_middle_page_failed = -2; } continue; } memcpy((u8 *)arg_oob_info, aml_chip->user_oob_buf, sizeof(oob_buf)); if ((!memcmp(arg_oob_info->name, name, 4))) { if (arg_info->arg_valid == 1) { if (arg_oob_info->timestamp > arg_info->timestamp) { arg_info->free_blk_addr = arg_info->valid_blk_addr; arg_info->valid_blk_addr = start_blk; arg_info->timestamp = arg_oob_info->timestamp; } else arg_info->free_blk_addr = start_blk; break; } else { arg_info->arg_valid = 1; arg_info->valid_blk_addr = start_blk; arg_info->timestamp = arg_oob_info->timestamp; } } break; } if ((arg_info->arg_type == FULL_BLK) && (arg_info->arg_valid == 1) && (arg_info->valid_blk_addr != 0)) break; } if (arg_info->arg_valid == 1) { if (arg_info->arg_type == FULL_BLK) { for (i = 0; i < pages_read;) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); ops_para->data_buf = NULL; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); memset((u8 *)ops_para->oob_buf, 0x0, sizeof(oob_buf)); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) ops_para->option |= DEV_SLC_MODE; tmp_value = arg_info->valid_blk_addr; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; tmp_value *= pages_per_blk; ops_para->page_addr = i + tmp_value; if ((ops_para->option & DEV_SLC_MODE)) { index = ops_para->page_addr; index &= (~(pages_per_blk - 1)); if (((flash->new_type > 0) && (flash->new_type < 10)) || (slc_info->micron_l0l3_mode == 1)) ops_para->page_addr = index | (slc_info->pagelist[ops_para->page_addr % 256]); if (flash->new_type == SANDISK_19NM) ops_para->page_addr = index | ((ops_para->page_addr % pages_per_blk) << 1); } ops_para->chipnr = arg_info->valid_blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); ret = operation->read_page(aml_chip); if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); if ((ops_para->ecc_err) || (ret < 0)) { aml_nand_msg("read %s failed chip%d page=%d", name, ops_para->chipnr, ops_para->page_addr); i += arg_pages;////////////////////////////////// arg_info->update_flag = 1; continue; } memcpy((u8 *)arg_oob_info, aml_chip->user_oob_buf, sizeof(oob_buf)); if (!memcmp(arg_oob_info->name, name, 4)) { arg_info->valid_page_addr = i; arg_info->timestamp = arg_oob_info->timestamp; break; } i += arg_pages;///////////////////////////////////////// } } else if (arg_info->arg_type == FULL_PAGE) { for (i = 0; i < pages_read;) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) || (slc_info->micron_l0l3_mode == 1))) ops_para->option |= DEV_SLC_MODE; ops_para->data_buf = aml_chip->user_page_buf; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); memset((u8 *)ops_para->data_buf, 0x0, flash->pagesize); memset((u8 *)ops_para->oob_buf, 0x0, sizeof(oob_buf)); tmp_value = arg_info->valid_blk_addr; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; tmp_value *= pages_per_blk; ops_para->page_addr = i + tmp_value; if ((ops_para->option & DEV_SLC_MODE)) { index = ops_para->page_addr; index &= (~(pages_per_blk - 1)); if (((flash->new_type > 0) && (flash->new_type < 10)) || (slc_info->micron_l0l3_mode == 1)) ops_para->page_addr = index | (slc_info->pagelist[ops_para->page_addr % 256]); if (flash->new_type == SANDISK_19NM) ops_para->page_addr = index | ((ops_para->page_addr % pages_per_blk) << 1); } ops_para->chipnr = arg_info->valid_blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); ret = operation->read_page(aml_chip); if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); if ((ops_para->ecc_err) || (ret < 0)) { aml_nand_msg("read %s failed at chip%d page %d", name, ops_para->chipnr, ops_para->page_addr); i += arg_pages;//////////////////////////////// arg_info->update_flag = 1; continue; } memcpy((u8 *)arg_oob_info, aml_chip->user_oob_buf, sizeof(oob_buf)); if (!memcmp(arg_oob_info->name, name, 4)) { arg_info->valid_page_addr = i; arg_info->timestamp = arg_oob_info->timestamp; } else break; i += arg_pages;/////////////////////////////////// /*for hynix slc mode*/ if (flash->new_type == HYNIX_1YNM) i += 1; } } for (i = 0; i < RESERVED_BLOCK_CNT; i++) { if (aml_chip->reserved_blk[i] == 0xff) { aml_chip->reserved_blk[i] = arg_info->valid_blk_addr; break; } } aml_nand_dbg("NAND CKECK: %s success at blk:%d page %d", name, arg_info->valid_blk_addr, arg_info->valid_page_addr); } aml_nand_msg("NAND CKECK:arg %s: valid=%d, blk=%d, page=%d", name, arg_info->arg_valid, arg_info->valid_blk_addr, arg_info->valid_page_addr); aml_nand_dbg(" complete "); return ret; } int amlnand_info_init(struct amlnand_chip *aml_chip, u8 *info, u8 *buf, u8 *name, u32 size) { struct nand_arg_info *arg_info = (struct nand_arg_info *)info; int ret = 0; aml_nand_dbg("NAME : %s", name); ret = amlnand_check_info_by_name(aml_chip, (u8 *) arg_info , name, size); if (ret < 0) { aml_nand_msg("nand check info failed"); goto exit_error; } if (arg_info->arg_valid == 1) { ret = amlnand_read_info_by_name(aml_chip, (u8 *)arg_info, buf, name, size); if (ret < 0) { aml_nand_msg("nand check info success but read failed"); goto exit_error; } ret = aml_info_check_datasum(buf, name); if (ret < 0) { aml_nand_msg("amlnand_info_init:check read %s failed", name); /* ret = 0; */ goto exit_error; } } else aml_nand_msg("found NO arg : %s info", name); exit_error: return ret; } /***************************************************************************** *Name :amlnand_update_bbt *Description :update bbt by block status *Parameter : *Return : *Note : *****************************************************************************/ int amlnand_update_bbt(struct amlnand_chip *aml_chip) { int ret = 0; aml_nand_dbg("amlnand_update_bbt :here!!"); #if 0 /* show the bbt */ aml_nand_dbg("show the bbt"); for (chipnr = 0; chipnr < controller->chip_num; chipnr++) { tmp_arr = &aml_chip->bbt_ptr->nand_bbt[chipnr][0]; for (start_block = 0; start_block < 200; start_block++) aml_nand_msg(" tmp_arr[%d][%d]=%d", chipnr, start_block, tmp_arr[start_block]); } #endif aml_chip->block_status->crc = aml_info_checksum((u8 *)aml_chip->block_status->blk_status, (MAX_CHIP_NUM*MAX_BLK_NUM)); ret = amlnand_save_info_by_name(aml_chip, (u8 *)&(aml_chip->nand_bbtinfo), (u8 *)aml_chip->block_status, (unsigned char *)BBT_HEAD_MAGIC, sizeof(struct block_status)); if (ret < 0) { aml_nand_msg("nand update bbt failed"); goto exit_error0; } return ret; exit_error0: return ret; } int amlnand_recover_fbbt(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; u32 total_blk, chipnr, start_block, factory_badblock_cnt=0; u16 *tmp_bbt, *tmp_status; u8 phys_erase_shift; u64 chip_size; aml_nand_dbg("%s : start",__func__); phys_erase_shift = ffs(flash->blocksize) - 1; chip_size = flash->chipsize; total_blk = (int) ((chip_size<<20) >> phys_erase_shift); factory_badblock_cnt = 0; for (chipnr = 0; chipnr < controller->chip_num; chipnr++) { tmp_bbt = &aml_chip->shipped_bbt_ptr->shipped_bbt[chipnr][0]; tmp_status = &aml_chip->block_status->blk_status[chipnr][0]; for (start_block = 0; start_block < total_blk; start_block++) { if (tmp_status[start_block] == NAND_BLOCK_FACTORY_BAD) { tmp_bbt[factory_badblock_cnt++] = (0x8000 | (u16)start_block); if ((controller->flash_type == NAND_TYPE_MLC) && (flash->option & NAND_MULTI_PLANE_MODE)) { // if plane 0 is bad block,just set plane 1 to bad if ((start_block % 2) == 0 ) { start_block += 1; tmp_bbt[factory_badblock_cnt++] = (u16)start_block |0x8000; } // if plane 1 is bad block, just set plane 0 to bad else{ tmp_bbt[factory_badblock_cnt++] = (u16)(start_block -1) |0x8000; } if (factory_badblock_cnt >= MAX_BAD_BLK_NUM) { aml_nand_dbg("%s : error too many bad blocks",__func__); return -1; } } } } } return 0; } /***************************************************************************** *Name :amlnand_init_block_status *Description :init the block_status table by bbt; *Parameter : *Return : *Note : *****************************************************************************/ int amlnand_init_block_status(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &aml_chip->controller; /* struct chip_operation *operation = &aml_chip->operation; */ struct nand_flash *flash = &aml_chip->flash; /* struct shipped_bbt * shipped_bbt_ptr = aml_chip->shipped_bbt_ptr; */ u32 total_blk, chipnr; u16 *tmp_bbt, *tmp_status; u8 phys_erase_shift; u64 chip_size; int ret = 0, i, j; aml_nand_dbg("amlnand_init_block_status : start"); phys_erase_shift = ffs(flash->blocksize) - 1; chip_size = flash->chipsize; total_blk = (int) ((chip_size<<20) >> phys_erase_shift); #if 0 aml_nand_dbg("show the bbt"); for (chipnr = 0; chipnr < controller->chip_num; chipnr++) { tmp_arr = &aml_chip->bbt_ptr->nand_bbt[chipnr][0]; for (start_block = 0; start_block < 200; start_block++) aml_nand_msg(" tmp_arr[%d][%d]=%d", chipnr, start_block, tmp_arr[start_block]); } #endif for (chipnr = 0; chipnr < controller->chip_num; chipnr++) { tmp_bbt = &aml_chip->shipped_bbt_ptr->shipped_bbt[chipnr][0]; tmp_status = &aml_chip->block_status->blk_status[chipnr][0]; for (i = 0; i < total_blk; i++) { tmp_status[i] = NAND_BLOCK_GOOD; for (j = 0; j < MAX_BAD_BLK_NUM; j++) { if ((tmp_bbt[j] & 0x7fff) == i) { if ((tmp_bbt[j] & 0x8000)) { tmp_status[i] = NAND_BLOCK_FACTORY_BAD; aml_nand_dbg("s[%d][%d]=%d", chipnr, i, tmp_status[i]); } else { if (i == 0) tmp_status[i] = NAND_BLOCK_GOOD; else{ tmp_status[i] = NAND_BLOCK_USED_BAD; aml_nand_dbg("s[%d][%d]=%d", chipnr, i, tmp_status[i]); } } break; } } } } #if 0 aml_nand_dbg("show the block status "); u16 *tmp_arr; int start_block; for (chipnr = 0; chipnr < controller->chip_num; chipnr++) { tmp_arr = &aml_chip->block_status->blk_status[chipnr][0]; for (start_block = 0; start_block < 50; start_block++) aml_nand_msg(" tmp_arr[%d][%d]=%d", chipnr, start_block, tmp_arr[start_block]); } #endif return ret; } #ifdef AML_NAND_UBOOT static int confirm_dev_para(struct dev_para*dev_para_cmp,struct amlnf_partition *config_init,int dev_flag) { int ret =0, j=0,partition_num=0; struct amlnf_partition *partition = NULL; struct amlnf_partition * partition_ptr =NULL; for (j = 0; j < MAX_NAND_PART_NUM; j++) { partition = &(config_init[j]); partition_ptr =& (dev_para_cmp->partitions[partition_num]); if (partition->mask_flags == dev_flag) { if (memcmp(partition_ptr->name, partition->name, strlen(partition->name))) { aml_nand_msg("nand partition table changed: partition->name: from %s to %s",partition_ptr->name,partition->name); ret = -1; break; } if (partition->size != partition_ptr->size) { aml_nand_msg("nand partition table changed: %s partition->size: from %llx to %llx",partition->name,partition_ptr->size,partition->size); ret = -1; break; } if (partition_ptr->mask_flags != dev_flag) { aml_nand_msg("nand partition table %s : mask_flag changed from %d to %d",partition->name,partition_ptr->mask_flags,partition->mask_flags); ret = -1; break; } partition_num ++; }else if(partition == NULL){ break; } } if (dev_para_cmp->nr_partitions != partition_num) { aml_nand_msg("nand dev %s : nr_partitions num changed from %d to %d",dev_para_cmp->name,dev_para_cmp->nr_partitions,partition_num); ret = -1; } return ret ; } static void init_dev_para(struct dev_para*dev_para_ptr,struct amlnf_partition *config_init, int dev_flag) { int j=0,partition_num=0; struct amlnf_partition *partition = NULL; struct amlnf_partition * partition_ptr =NULL; for (j = 0; j < MAX_NAND_PART_NUM; j++) { //printf("%s, j = %d\n", __func__, j); partition = &(config_init[j]); partition_ptr =& (dev_para_ptr->partitions[partition_num]); if (partition->mask_flags == dev_flag) { memcpy(partition_ptr->name, partition->name, strlen( partition->name)); partition_ptr->size = partition->size; partition_ptr->mask_flags = partition->mask_flags; partition_num ++; aml_nand_dbg("init_dev_para : partition->name %s ", partition->name); aml_nand_dbg("init_dev_para : partition->size %llx", partition->size); aml_nand_dbg("init_dev_para : partition->mask_flags %d", partition->mask_flags); }else if(partition == NULL){ break; } } dev_para_ptr->nr_partitions = partition_num; aml_nand_msg("partition-> partition_num %d",partition_num); return; } static void amlnand_get_dev_num(struct amlnand_chip *aml_chip,struct amlnf_partition *config_init) { struct dev_para *dev_para_ptr =NULL; int i , tmp_num=0; int device_num; device_num = (aml_chip->h_cache_dev)? 3 : 2; if (boot_device_flag == 1) { memcpy((void *)(aml_chip->config_ptr->dev_para[tmp_num].name), NAND_BOOT_NAME, strlen(NAND_BOOT_NAME)); aml_chip->config_ptr->dev_para[tmp_num].nr_partitions = 0; aml_chip->config_ptr->dev_para[tmp_num].option = 0; tmp_num++; device_num += 1; } aml_chip->config_ptr->dev_num = device_num; for (i=0; tmp_num < device_num; tmp_num++, i++) { int dev_flag; u32 option; dev_para_ptr = &(aml_chip->config_ptr->dev_para[tmp_num]); if (i == 0) { if (aml_chip->h_cache_dev) { memcpy((void *)(dev_para_ptr->name), NAND_CACHE_NAME, strlen(NAND_CACHE_NAME)); dev_flag = STORE_CACHE; option = NAND_DATA_OPTION; } else { memcpy((void *)(dev_para_ptr->name), NAND_CODE_NAME, strlen(NAND_CODE_NAME)); dev_flag = STORE_CODE; option = NAND_CODE_OPTION; } init_dev_para(dev_para_ptr, config_init, dev_flag); dev_para_ptr->option = option; } else if (i == 1) { if (aml_chip->h_cache_dev) { memcpy((void *)(dev_para_ptr->name), NAND_CODE_NAME, strlen(NAND_CODE_NAME)); dev_flag = STORE_CODE; option = NAND_CODE_OPTION; } else { memcpy((void *)(dev_para_ptr->name), NAND_DATA_NAME, strlen(NAND_DATA_NAME)); dev_flag = STORE_DATA; option = NAND_DATA_OPTION; } init_dev_para(dev_para_ptr, config_init, dev_flag); dev_para_ptr->option = option; } else if (i == 2) { if (aml_chip->h_cache_dev) { memcpy((void *)(dev_para_ptr->name), NAND_DATA_NAME, strlen(NAND_DATA_NAME)); dev_flag = STORE_DATA; option = NAND_DATA_OPTION; init_dev_para(dev_para_ptr, config_init, dev_flag); dev_para_ptr->option = option; } } else { aml_nand_msg("%s:something wrong here", __func__); break; } } return ; } int amlnand_configs_confirm(struct amlnand_chip *aml_chip) { //nand_arg_info * config_msg = &aml_chip->config_msg; #ifdef AML_NAND_UBOOT struct amlnf_partition * configs_init = (struct amlnf_partition *) amlnand_config; #endif struct nand_config * config_ptr = aml_chip->config_ptr; struct dev_para *dev_para_cmp = NULL; unsigned char confirm_flag=0; int i, tmp_num=0,ret = 0; int device_num; ENV_NAND_LINE device_num = (aml_chip->h_cache_dev)? 3 : 2; ret = phrase_driver_version(config_ptr->driver_version,DRV_PHY_VERSION); if (ret) { aml_nand_msg("driver_version in nand %d.%02d.%03d.%04d ", (config_ptr->driver_version >> 24)&0xff, (config_ptr->driver_version >> 16)&0xff, (config_ptr->driver_version >> 8)&0xff, (config_ptr->driver_version)&0xff); /*confirm_flag = 1;*/ } ENV_NAND_LINE if (boot_device_flag == 1) { tmp_num++; device_num += 1; } ENV_NAND_LINE //check device num if (device_num != config_ptr->dev_num) { //aml_nand_msg("nand device num changed from %d to %d %s", config_ptr->dev_num,device_num); aml_nand_msg("nand device num changed from %d to %d", config_ptr->dev_num,device_num); confirm_flag = 1; } ENV_NAND_LINE for (i=0; tmp_num < device_num; tmp_num++, i++) { ENV_NAND_LINE dev_para_cmp = &(aml_chip->config_ptr->dev_para[tmp_num]); if (i == 0) { if (aml_chip->h_cache_dev) ret = confirm_dev_para(dev_para_cmp, configs_init, STORE_CACHE); else ret = confirm_dev_para(dev_para_cmp, configs_init, STORE_CODE); if (ret) { confirm_flag = 1; break; } } else if (i == 1) { if (aml_chip->h_cache_dev) ret = confirm_dev_para(dev_para_cmp, configs_init, STORE_CODE); else ret = confirm_dev_para(dev_para_cmp, configs_init, STORE_DATA); if (ret) { confirm_flag = 1; break; } } else if (i == 2) { if (aml_chip->h_cache_dev) { ret = confirm_dev_para(dev_para_cmp, configs_init, STORE_DATA); if (ret) { confirm_flag = 1; break; } } else { aml_nand_msg("%s %d: something wrong here!!", __func__, __LINE__); confirm_flag = 1; break; } } else { aml_nand_msg("%s %d: something wrong here!!", __func__, __LINE__); confirm_flag = 1; break; } } ENV_NAND_LINE if (confirm_flag == 0) { aml_nand_dbg("nand configs confirm all right"); aml_chip->shipped_bbtinfo.valid_blk_addr = config_ptr->fbbt_blk_addr; for (i=0; ireserved_blk[i] == 0xff) { aml_chip->reserved_blk[i] = aml_chip->config_ptr->fbbt_blk_addr; break; } } aml_nand_dbg("nand shipped bbt at blk:%d, nand config at blk:%d", \ aml_chip->shipped_bbtinfo.valid_blk_addr, aml_chip->config_msg.valid_blk_addr); } else{ aml_nand_msg("nand configs confirm failed"); reset_amlchip_member(aml_chip); ret = -1; } ENV_NAND_LINE return ret; //exit_error0: //return ret; } #endif /* AML_NAND_UBOOT */ int aml_nand_save_hynix_info(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct chip_operation *operation = &aml_chip->operation; 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); u8 phys_erase_shift, phys_page_shift; u16 blk_addr = 0, tmp_blk, nand_boot; u32 i, j, offset, pages_per_blk, pages_read; u8 oob_buf[8]; int ret = 0; u32 tmp_addr; int test_cnt = 0; u8 tmp_rand; u32 tmp_value; #ifdef DEBUG_HYINX_DEF int save_cnt = 20; if (retry_info->retry_cnt_lp > 20) save_cnt = retry_info->retry_cnt_lp; #endif if ((flash->new_type == 0) || (flash->new_type > 10)) return NAND_SUCCESS; printf("aml_nand_save_hynix_info : here!! \n"); nand_boot = 1; /*if(boot_device_flag == 0){ nand_boot = 0; }*/ 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)) 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; memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); memcpy(oob_buf, HYNIX_DEV_HEAD_MAGIC, strlen(HYNIX_DEV_HEAD_MAGIC)); get_free_blk: ret = amlnand_get_free_block(aml_chip, blk_addr); blk_addr = ret; if (ret < 0) { aml_nand_msg("nand get free block failed"); ret = -NAND_BAD_BLOCK_FAILURE; goto exit_error0; } printf("nand get free block for hynix readretry info at %d\n", blk_addr); for (i = 0; i < pages_read; i++) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); if ((flash->new_type) && (flash->new_type < 10)) ops_para->option |= DEV_SLC_MODE; tmp_value = blk_addr; tmp_value /= controller->chip_num; tmp_value += tmp_blk - tmp_blk/controller->chip_num; ops_para->page_addr = i + (tmp_value * pages_per_blk); ops_para->chipnr = blk_addr % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); if ((ops_para->option & DEV_SLC_MODE) && ((flash->new_type > 0) && (flash->new_type < 10))) { tmp_value = ops_para->page_addr; tmp_value &= (~(pages_per_blk - 1)); ops_para->page_addr = tmp_value | (slc_info->pagelist[ops_para->page_addr % 256]); } ops_para->data_buf = aml_chip->user_page_buf; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); if (flash->new_type == HYNIX_1YNM) { if ((i > 1) && ((i%2) == 0)) { tmp_addr = ops_para->page_addr; tmp_rand = controller->ran_mode; ops_para->page_addr -= 1; controller->ran_mode = 0; ops_para->option |= DEV_ECC_SOFT_MODE; ops_para->option &= DEV_SERIAL_CHIP_MODE; memset(aml_chip->user_page_buf, 0xff, flash->pagesize); #ifdef AML_NAND_UBOOT nand_get_chip(aml_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(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif ops_para->page_addr = tmp_addr; controller->ran_mode = tmp_rand; ops_para->option &= DEV_ECC_HW_MODE; ops_para->option |=DEV_SLC_MODE; } } memset(aml_chip->user_page_buf, 0x0, flash->pagesize); memset(aml_chip->user_oob_buf, 0x0, sizeof(oob_buf)); memcpy((u8 *)aml_chip->user_page_buf, &retry_info->reg_def_val[0][0], MAX_CHIP_NUM*READ_RETRY_REG_NUM); memcpy(aml_chip->user_oob_buf, (u8 *)oob_buf, 4); #ifdef DEBUG_HYINX_DEF for (k = 0; k < controller->chip_num; k++) for (j = 0; j < save_cnt; j++) memcpy((u8 *)(aml_chip->user_page_buf + MAX_CHIP_NUM*READ_RETRY_REG_NUM + j*READ_RETRY_REG_NUM+k*READ_RETRY_CNT), &retry_info->reg_offs_val_lp[k][j][0], READ_RETRY_REG_NUM); #else memcpy((u8 *)(aml_chip->user_page_buf + MAX_CHIP_NUM*READ_RETRY_REG_NUM), &retry_info->reg_offs_val_lp[0][0][0], MAX_CHIP_NUM*READ_RETRY_CNT*READ_RETRY_REG_NUM); #endif #ifdef AML_NAND_UBOOT nand_get_chip(aml_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(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif if (ret < 0) { aml_nand_msg("%s:nand write failed", __func__); if (test_cnt >= 3) { aml_nand_msg("test blk 3times,"); break; } ret = operation->test_block_reserved(aml_chip, blk_addr); test_cnt++; if (ret) { ret = operation->block_markbad(aml_chip); if (ret < 0) aml_nand_dbg("mark badblk failed %d", blk_addr); } goto get_free_blk; } } if (ret < 0) { aml_nand_msg("hynix nand save readretry info failed"); goto exit_error0; } else { for (j = 0; j < RESERVED_BLOCK_CNT; j++) { if (aml_chip->reserved_blk[j] == 0xff) { aml_chip->reserved_blk[j] = blk_addr; break; } } retry_info->info_save_blk = blk_addr; printf("save hynix readretry info success at blk %d\n", blk_addr); } exit_error0: return ret; } int aml_nand_scan_hynix_info(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct chip_operation *operation = &aml_chip->operation; 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); u8 phys_erase_shift, phys_page_shift; u16 start_blk, tmp_blk, total_blk, nand_boot; u32 i, j, k, n, offset, pages_per_blk, pages_read; u8 oob_buf[8]; int nand_type, ret = 0; u32 tmp_value, index; #ifdef DEBUG_HYINX_DEF int save_cnt = 20; if (retry_info->retry_cnt_lp > 20) save_cnt = retry_info->retry_cnt_lp; #endif if ((flash->new_type == 0) || (flash->new_type > 10)) return NAND_SUCCESS; nand_boot = 1; /*if(boot_device_flag == 0){ nand_boot = 0; }*/ if (nand_boot) offset = (1024 * flash->pagesize); else offset = 0; memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); phys_erase_shift = ffs(flash->blocksize) - 1; phys_page_shift = ffs(flash->pagesize) - 1; if ((flash->new_type) && (flash->new_type < 10)) ops_para->option |= DEV_SLC_MODE; pages_per_blk = (1 << (phys_erase_shift - phys_page_shift)); if (ops_para->option & DEV_SLC_MODE) pages_read = pages_per_blk >> 1; else pages_read = pages_per_blk; retry_info->default_flag = 0; retry_info->flag = 0; start_blk = (int)(offset >> phys_erase_shift); tmp_blk = start_blk; total_blk = (offset >> phys_erase_shift)+RESERVED_BLOCK_CNT; do { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); tmp_value = start_blk; 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 = start_blk % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); nand_type = flash->new_type; flash->new_type = 0; ret = operation->block_isbad(aml_chip); flash->new_type = nand_type; if (ret == NAND_BLOCK_FACTORY_BAD) { aml_nand_msg("blk %d is bad ", start_blk); continue; } for (i = 0; i < pages_read; i++) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); if ((flash->new_type) && (flash->new_type < 10)) ops_para->option |= DEV_SLC_MODE; ops_para->data_buf = aml_chip->user_page_buf; ops_para->oob_buf = aml_chip->user_oob_buf; ops_para->ooblen = sizeof(oob_buf); ops_para->page_addr = (i + tmp_value * pages_per_blk); if ((ops_para->option & DEV_SLC_MODE) && ((flash->new_type > 0) && (flash->new_type < 10))) { index = ops_para->page_addr; index &= (~(pages_per_blk - 1)); ops_para->page_addr = index | (slc_info->pagelist[ops_para->page_addr % 256]); } ops_para->chipnr = start_blk % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); memset((u8 *)ops_para->data_buf, 0x0, flash->pagesize); memset((u8 *)ops_para->oob_buf, 0x0, sizeof(oob_buf)); nand_type = flash->new_type; flash->new_type = 0; #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_get_chip(aml_chip); #endif ret = operation->read_page(aml_chip); #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else if (aml_chip->state == CHIP_READY) nand_release_chip(aml_chip); #endif flash->new_type = nand_type; if ((ops_para->ecc_err) || (ret < 0)) { aml_nand_msg("blk check good but read failed "); aml_nand_msg("chip %d page %d", ops_para->chipnr, ops_para->page_addr); continue; } memcpy(oob_buf, aml_chip->user_oob_buf, sizeof(oob_buf)); if (!memcmp(oob_buf, HYNIX_DEV_HEAD_MAGIC, strlen(HYNIX_DEV_HEAD_MAGIC))) { memcpy(&retry_info->reg_def_val[0][0], (u8 *)aml_chip->user_page_buf, MAX_CHIP_NUM*READ_RETRY_REG_NUM); printf("get def value at blk:%d,page:%d\n", start_blk, ops_para->page_addr); for (k = 0; k < controller->chip_num; k++) for (j = 0; j < retry_info->reg_cnt_lp; j++) aml_nand_dbg("REG(0x%x):val:0x%x,for chip%d", retry_info->reg_addr_lp[j], retry_info->reg_def_val[k][j], k); if ((flash->new_type == HYNIX_20NM_8GB) || (flash->new_type == HYNIX_20NM_4GB) || (flash->new_type == HYNIX_1YNM)) { #ifdef DEBUG_HYINX_DEF for (n = 0; n < controller->chip_num; n++) for (j = 0; j < save_cnt; j++) memcpy(&retry_info->reg_offs_val_lp[n][j][0], (u8 *)(aml_chip->user_page_buf + MAX_CHIP_NUM*READ_RETRY_REG_NUM + j*READ_RETRY_REG_NUM+n*save_cnt), READ_RETRY_REG_NUM); #else memcpy(&retry_info->reg_offs_val_lp[0][0][0], (u8 *)(aml_chip->user_page_buf + MAX_CHIP_NUM*READ_RETRY_REG_NUM), MAX_CHIP_NUM*READ_RETRY_CNT*READ_RETRY_REG_NUM); #endif } for (n = 0; n < controller->chip_num; n++) for (j = 0; j < retry_info->retry_cnt_lp; j++) for (k = 0; k < retry_info->reg_cnt_lp; k++) { aml_nand_dbg("Retry%dst,REG(0x%x):val:0x%2x,for chip%d", k, retry_info->reg_addr_lp[k], retry_info->reg_offs_val_lp[n][j][k], n); } retry_info->default_flag = 1; retry_info->flag = 1; break; } else { aml_nand_dbg("read OK but magic is not hynix"); break; } } if (retry_info->default_flag && flash->new_type) break; } while ((++start_blk) < total_blk); if (retry_info->default_flag && flash->new_type) { retry_info->info_save_blk = start_blk; for (i = 0; i < RESERVED_BLOCK_CNT; i++) { if (aml_chip->reserved_blk[i] == 0xff) { aml_chip->reserved_blk[i] = start_blk; break; } } } return ret; } #ifdef AML_NAND_UBOOT /***************************************************************************** *Name :shipped_badblock_detect *Description :Detect factory badblock once using the nand flash first time *Parameter : *Return : *Note : *****************************************************************************/ static int shipped_badblock_detect(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct shipped_bbt * shipped_bbt_ptr = aml_chip->shipped_bbt_ptr; unsigned short * tmp_arr; int pages_per_blk, start_block, total_block, nand_boot, start_blk; int chipnr, offset, offset_tmp, read_cnt, page_addr, col0_data = 0, col_data_sandisk[6], col0_oob = 0xff; unsigned char phys_erase_shift, phys_page_shift; int i, ret = 0, factory_badblock_cnt; uint64_t tmp_blk; aml_nand_dbg("here!! "); nand_boot = 1; if (boot_device_flag == 0) { nand_boot = 0; } if (nand_boot) offset_tmp = (1024 * flash->pagesize); else { offset_tmp = 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)); start_blk = offset_tmp >> phys_erase_shift; tmp_blk = flash->chipsize; total_block = (int) ((tmp_blk<<20) >> phys_erase_shift); aml_nand_dbg("phys_erase_shift=%d ",phys_erase_shift); aml_nand_dbg("flash->chipsize =%d ",flash->chipsize); aml_nand_dbg("flash->blocksize =%d ",flash->blocksize); aml_nand_dbg("start_blk =%d ",start_blk); aml_nand_dbg("total_block =%d ",total_block); aml_nand_dbg("pages_per_blk =%d ",pages_per_blk); //block 0 never bad block, so init table into 0 memset((unsigned char *)shipped_bbt_ptr, 0x0, (sizeof(struct shipped_bbt))); #ifdef AML_NAND_UBOOT nand_get_chip(aml_chip); #else nand_get_chip(aml_chip); #endif for (chipnr=0; chipnr < controller->chip_num; chipnr++) { aml_nand_dbg("chipnr=%d",chipnr); factory_badblock_cnt = 0; tmp_arr = &aml_chip->shipped_bbt_ptr->shipped_bbt[chipnr][0]; controller->select_chip(controller, chipnr); for (start_block=start_blk; start_block < total_block; start_block++) { //for(start_block = 0; start_block < total_block; start_block++){ if (((start_block == ((aml_chip->nand_key.valid_blk_addr +(controller->chip_num -1)*4)/controller->chip_num)) && (aml_chip->nand_key.arg_valid)) ||((start_block == ((aml_chip->nand_secure.valid_blk_addr +(controller->chip_num -1)*4)/controller->chip_num))&&(aml_chip->nand_secure.arg_valid))){ aml_nand_msg("shipped_badblock_detect skip block %d,chipnr %d",start_block,chipnr); continue; } offset = pages_per_blk*start_block; for (read_cnt=0; read_cnt<2; read_cnt++) { if ((controller->mfr_type == NAND_MFR_SANDISK )) page_addr = offset + read_cnt; // page0 page1 else page_addr = offset + read_cnt*(pages_per_blk-1); // page_num if (unlikely(page_addr >= controller->internal_page_nums)) { page_addr -= controller->internal_page_nums; page_addr |= controller->internal_page_nums *aml_chip->flash.internal_chipnr; } controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE); controller->cmd_ctrl(controller, 0, NAND_CTRL_ALE); controller->cmd_ctrl(controller, 0, 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); ret = controller->quene_rb(controller, chipnr); if (ret) { aml_nand_msg("quene rb busy here"); ret = -NAND_BUSY_FAILURE; 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, NAND_TWB_TIME_CYCLE); } if ((controller->mfr_type == NAND_MFR_SANDISK )) { for (i = 0; i < 6; i++) { col_data_sandisk[i] = controller->readbyte(controller); if (col_data_sandisk[i] == 0x0) { col0_oob = 0x0; break; } } }else { col0_data = controller->readbyte(controller); //TRHW NFC_SEND_CMD_IDLE(controller, NAND_TRHW_TIME_CYCLE); controller->cmd_ctrl(controller, NAND_CMD_RNDOUT, NAND_CTRL_CLE); controller->cmd_ctrl(controller, flash->pagesize, NAND_CTRL_ALE); controller->cmd_ctrl(controller, flash->pagesize>>8, NAND_CTRL_ALE); controller->cmd_ctrl(controller, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE); //TCCS NFC_SEND_CMD_IDLE(controller, NAND_TCCS_TIME_CYCLE); col0_oob = controller->readbyte(controller); } if (((controller->mfr_type == NAND_MFR_SAMSUNG ) && ((col0_oob != 0xFF) || (col0_data != 0xFF))) \ || ((controller->mfr_type == NAND_MFR_TOSHIBA ) && ((col0_oob != 0xFF) || (col0_data != 0xFF))) \ ||((controller->mfr_type == NAND_MFR_MICRON ) && (col0_oob == 0x0)) \ ||((controller->mfr_type == NAND_MFR_HYNIX ) && (col0_oob != 0xFF)) \ ||((controller->mfr_type == NAND_MFR_SANDISK ) && (col0_oob != 0xFF))){ col0_oob = 0xff; aml_nand_msg("mfr_type:%x detect factory Bad block at read_cnt:%d and block:%d and chip:%d", \ controller->mfr_type, read_cnt, start_block, chipnr); tmp_arr[factory_badblock_cnt] = start_block |0x8000; //aml_nand_msg("start_block is bad block = %d, tmp_arr[factory_badblock_cnt] = %d", //start_block, tmp_arr[factory_badblock_cnt]); if (start_block < start_blk) { aml_nand_msg("WARNING: UBOOT AREA BLOCK %d IS BAD BLOCK",start_block); } if ((controller->flash_type == NAND_TYPE_MLC) && (flash->option & NAND_MULTI_PLANE_MODE)) { if ((start_block % 2) == 0 ) { // if plane 0 is bad block,just set plane 1 to bad start_block+=1; tmp_arr[++factory_badblock_cnt] = start_block |0x8000; //aml_nand_msg(" plane 0 is bad block,just set plane 1 to bad:"); }else{ // if plane 1 is bad block, just set plane 0 to bad tmp_arr[++factory_badblock_cnt]= (start_block -1) |0x8000; //aml_nand_msg(" plane 1 is bad block,just set plane 0 to bad:"); } } //bad block should less than 6% of total blocks if ((factory_badblock_cnt++ >= (total_block/2))) { aml_nand_msg("detect factory bad block over 50%%, hardware problem and factory_badblock_cnt:%d, total_block:%d, chipnr:%d !!!", \ factory_badblock_cnt, total_block, chipnr); if (aml_chip->shipped_retry_flag) { ret = -NAND_STATUS_FAILURE; }else{ aml_chip->shipped_retry_flag = 1; reset_amlchip_member(aml_chip); amlnand_config_buf_free(aml_chip); ret= -NAND_SHIPPED_BADBLOCK_FAILED; } goto error_exit0; } break; } } } aml_nand_msg("shipped bad block scan complete: chip %d, factory_badblock_cnt =%d (total_block/10) =%d",chipnr, factory_badblock_cnt,(total_block/10)); } #if 0 //display the fbbt for (chipnr= 0; chipnr < controller->chip_num; chipnr++) { tmp_arr = &aml_chip->shipped_bbt_ptr->shipped_bbt[chipnr][0]; for (start_block=0; start_block < 200; start_block++) { aml_nand_msg(" tmp_arr[%d][%d]=%d", chipnr, start_block, tmp_arr[start_block]); } } #endif #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else nand_release_chip(aml_chip); #endif return ret; error_exit0: #ifdef AML_NAND_UBOOT nand_release_chip(aml_chip); #else nand_release_chip(aml_chip); #endif return ret; } #endif /** * @usage: get the bbt info * * @buf: pointer of the target buffer * @len: bbt len * * @return: the amount bytes of the rsv info */ int amlnf_bbt_read(u8 *buf, int len) { struct amlnand_chip *aml_chip = aml_nand_chip; u8 *bbt_buf = NULL; int ret = 0; u32 size = 0; aml_nand_msg("%s: ####", __func__); size= (sizeof(struct block_status)); if (aml_chip == NULL) { aml_nand_msg("bbt not ready yet!"); ret = -1; goto exit_err; } if (len > size) { aml_nand_msg("warning!!! %s bbt length too much", __func__); len = size; } if (aml_chip->nand_bbtinfo.arg_valid == 0) { memset(buf, 0x0, len); aml_nand_msg("%s arg_valid = 0 invalid", __func__); ret = -1; goto exit_err; } bbt_buf = aml_nand_malloc(size); if (bbt_buf == NULL) { aml_nand_msg("%s: malloc failed", __func__); ret = -1; goto exit_err; } memset(bbt_buf, 0, size); ret = amlnand_read_info_by_name(aml_chip, (u8 *)&(aml_chip->nand_bbtinfo), (u8 *)bbt_buf, (u8 *)BBT_HEAD_MAGIC, size); if (ret) { aml_nand_msg("bbt error,%s", __func__); ret = -EFAULT; goto exit_err; } memcpy(buf, bbt_buf, len); exit_err: if (bbt_buf) { /* kfree(dtb_buf); */ kfree(bbt_buf); bbt_buf = NULL; } return ret; } int amlnf_bbt_erase(void) { int ret = 0; struct amlnand_chip *aml_chip = aml_nand_chip; if (aml_chip == NULL) { printk("%s amlnf not ready yet!\n", __func__); return -1; } ret = amlnand_erase_info_by_name(aml_chip, (u8 *)&(aml_chip->nand_bbtinfo), (u8 *)BBT_HEAD_MAGIC); if (ret) { printk("%s erase bbt error\n", __func__); ret = -EFAULT; } else { aml_nand_msg("bbt erase success"); } return ret; } /** * @usage: get the rsv info size * * @name: rsv info name, please refer to * RSV_KEY "key" * RSV_ENV "env" * RSV_DTB "dtb" * RSV_BBT "bbt" * * @return: the amount bytes of the rsv info */ uint32_t amlnf_get_rsv_size(const char *name) { struct amlnand_chip *aml_chip = aml_nand_chip; u32 size = 0; if (strcmp(name, "key") == 0) { size = aml_chip->keysize; } else if (strcmp(name, "dtb") == 0) { size = aml_chip->dtbsize; } else if (strcmp(name, "env") == 0) { size = CONFIG_ENV_SIZE; } else if (strcmp(name, "bbt") == 0) { //size = (sizeof(struct shipped_bbt)); size= (sizeof(struct block_status)); } else aml_nand_msg("error: no such reserv name"); aml_nand_msg("name %s, size: 0x%x", name, size); return size; } /** * @usage: read the rsv info from NAND * * @name: rsv info name, please refer to * RSV_KEY "key" * RSV_ENV "env" * RSV_DTB "dtb" * RSV_BBT "bbt" * @size: the amount of bytes to read * @buf: pointer of the target buffer * * @return: result of the operation * 0 = success * other = fail */ int amlnf_read_rsv(const char *name, size_t size, void *buf) { char ret = 0; uint32_t actual_length = 0; if (strcmp(name, "dtb") == 0) { ret = amlnf_dtb_read((u8 *)buf, (int)size); if (ret < 0) aml_nand_msg("nand read dtd failed"); } else if (strcmp(name, "key") == 0) { ret = amlnf_key_read((u8 *)buf, (int)size, &actual_length); if (ret < 0) aml_nand_msg("nand read key failed"); aml_nand_msg("key real size: %d",(u32)actual_length); } else if (strcmp(name, "env") == 0) { ret = amlnf_env_read((u8 *)buf, (int)size); if (ret < 0) aml_nand_msg("nand read env failed"); } else if (strcmp(name, "bbt") == 0) { ret = amlnf_bbt_read((u8 *)buf, (int)size); if (ret < 0) aml_nand_msg("nand read bbt failed"); } else { aml_nand_msg("error: no such reserv name"); ret = -1; } return ret; } /** * @usage: write the rsv info to the nand * * @name: rsv info name, please refer to * RSV_KEY "key" * RSV_ENV "env" * RSV_DTB "dtb" * RSV_BBT "bbt" prohibited * @size: the amount of bytes to write * @buf: pointer of the source buffer * * @return: result of the operation * 0 = success * other = fail */ int amlnf_write_rsv(const char *name, size_t size, void *buf) { char ret = 0; uint32_t actual_length = 0; if (strcmp(name, "dtb") == 0) { ret = amlnf_dtb_save((u8 *)buf, (int)size); if (ret < 0) aml_nand_msg("nand write dtd failed"); } else if (strcmp(name, "key") == 0) { ret = amlnf_key_write((u8 *)buf, (int)size, &actual_length); if (ret < 0) aml_nand_msg("nand write key failed"); aml_nand_msg("key real size: %d",(u32)actual_length); } else if (strcmp(name, "env") == 0) { ret = amlnf_env_save((u8 *)buf, (int)size); if (ret < 0) aml_nand_msg("nand write env failed"); } else if (strcmp(name, "bbt") == 0) { aml_nand_msg("writing bbt is prohibited" ); } else { aml_nand_msg("error: no such reserv name"); ret = -1; } return ret; } /** * @usage: erase the rsv info * * @name: rsv info name, please refer to * RSV_KEY "key" * RSV_ENV "env" * RSV_DTB "dtb" * RSV_BBT "bbt" * * @return: result of the operation * 0 = success * other = fail */ int amlnf_erase_rsv(const char *name) { char ret = 0; if (!name) { aml_nand_msg("part name null"); ret = amlnf_dtb_erase(); if (ret) aml_nand_msg("nand erase dtd failed"); ret = amlnf_key_erase(); if (ret < 0) aml_nand_msg("nand erase key failed"); ret = amlnf_env_erase(); if (ret < 0) aml_nand_msg("nand erase env failed"); ret = amlnf_bbt_erase(); if (ret < 0) aml_nand_msg("nand erase env failed"); return ret; } if (strcmp(name, "dtb") == 0) { ret = amlnf_dtb_erase(); if (ret) aml_nand_msg("nand erase dtd failed"); } else if (strcmp(name, "key") == 0) { ret = amlnf_key_erase(); if (ret < 0) aml_nand_msg("nand erase key failed"); } else if (strcmp(name, "env") == 0) { ret = amlnf_env_erase(); if (ret < 0) aml_nand_msg("nand erase env failed"); } else if (strcmp(name, "bbt") == 0) { ret = amlnf_bbt_erase(); if (ret < 0) aml_nand_msg("nand erase env failed"); } else { aml_nand_msg("error: no such reserv name"); ret = -1; } return ret; } void amlnand_config_buf_free(struct amlnand_chip *aml_chip) { if (aml_chip->block_status) { kfree(aml_chip->block_status); aml_chip->block_status = NULL; } if (aml_chip->shipped_bbt_ptr) { kfree(aml_chip->shipped_bbt_ptr); aml_chip->shipped_bbt_ptr = NULL; } if (aml_chip->config_ptr) { kfree(aml_chip->config_ptr); aml_chip->config_ptr = NULL; } if (aml_chip->phy_part_ptr) { kfree(aml_chip->phy_part_ptr); aml_chip->phy_part_ptr = NULL; } if (aml_chip->user_oob_buf) { kfree(aml_chip->user_oob_buf); aml_chip->user_oob_buf = NULL; } if (aml_chip->user_page_buf) { kfree(aml_chip->user_page_buf); aml_chip->user_page_buf = NULL; } if (amlnand_config) { kfree(amlnand_config); amlnand_config = NULL; } } static int amlnand_config_buf_malloc(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; /* struct chip_operation *operation = & aml_chip->operation; */ /* struct chip_ops_para *ops_para = & aml_chip->ops_para; */ u32 ret = 0, buf_size; buf_size = 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; if (aml_chip->user_oob_buf == NULL) { aml_chip->user_oob_buf = aml_nand_malloc(buf_size); /* aml_chip->user_oob_buf = (u8 *)0x13000000; */ if (aml_chip->user_oob_buf == NULL) { aml_nand_msg("malloc failed for user_oob_buf "); ret = -NAND_MALLOC_FAILURE; goto exit_error0; } } memset(aml_chip->user_oob_buf, 0x0, buf_size); 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; if (aml_chip->user_page_buf == NULL) { aml_chip->user_page_buf = aml_nand_malloc(buf_size); /* aml_chip->user_page_buf = (u8 *)0x12004000;*/ if (aml_chip->user_page_buf == NULL) { aml_nand_msg("malloc failed for user_page_buf "); ret = -NAND_MALLOC_FAILURE; goto exit_error0; } } memset(aml_chip->user_page_buf, 0x0, buf_size); /* using to record each block status.*/ if (aml_chip->block_status == NULL) { aml_chip->block_status = (struct block_status *)aml_nand_malloc(sizeof(struct block_status)); if (aml_chip->block_status == NULL) { aml_nand_msg("malloc failed for block_status and size:%x", (u32)sizeof(struct block_status)); ret = -NAND_MALLOC_FAILURE; goto exit_error0; } } memset(aml_chip->block_status, 0x0, (sizeof(struct block_status))); if (aml_chip->shipped_bbt_ptr == NULL) { aml_chip->shipped_bbt_ptr = aml_nand_malloc(sizeof(struct shipped_bbt)); if (aml_chip->shipped_bbt_ptr == NULL) { aml_nand_msg("malloc failed for shipped_bbt_ptr "); ret = -NAND_MALLOC_FAILURE; goto exit_error0; } } memset(aml_chip->shipped_bbt_ptr, 0x0, (sizeof(struct shipped_bbt))); if (aml_chip->config_ptr == NULL) { aml_chip->config_ptr = aml_nand_malloc(sizeof(struct nand_config)); if (aml_chip->config_ptr == NULL) { aml_nand_msg("malloc failed for config_ptr "); ret = -NAND_MALLOC_FAILURE; goto exit_error0; } } memset(aml_chip->config_ptr, 0x0, (sizeof(struct nand_config))); aml_chip->phy_part_ptr = aml_nand_malloc(sizeof(struct phy_partition_info)); if (aml_chip->phy_part_ptr == NULL) { aml_nand_msg("malloc failed for phy_part_ptr "); ret = -NAND_MALLOC_FAILURE; goto exit_error0; } memset(aml_chip->phy_part_ptr, 0x0, (sizeof(struct phy_partition_info))); return ret; exit_error0: amlnand_config_buf_free(aml_chip); return ret ; } /* * set attribute of each configs. * FULL_BLK: write the whole block once. * FULL_PAGE: write full pages once. */ void amlnand_set_config_attribute(struct amlnand_chip *aml_chip) { aml_chip->nand_bbtinfo.arg_type = FULL_BLK; aml_chip->shipped_bbtinfo.arg_type = FULL_BLK; aml_chip->config_msg.arg_type = FULL_BLK; aml_chip->nand_secure.arg_type = FULL_PAGE; aml_chip->nand_key.arg_type = FULL_PAGE; aml_chip->uboot_env.arg_type = FULL_PAGE; aml_chip->nand_phy_partition.arg_type = FULL_PAGE; #if (AML_CFG_DTB_RSV_EN) aml_chip->amlnf_dtb.arg_type = FULL_PAGE; #endif return; } /* bbt is valid. */ int bbt_valid_ops(struct amlnand_chip *aml_chip) { int ret = 0; ENV_NAND_LINE; PRINT("%s\n", __func__); ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->config_msg),(unsigned char *)(aml_chip->config_ptr),(unsigned char *)CONFIG_HEAD_MAGIC, sizeof(struct nand_config)); if (ret < 0) { aml_nand_msg("nand scan config failed and ret:%d",ret); goto exit_error0; } ENV_NAND_LINE /* aml_nand_msg("aml_chip->detect_dtb_flag:%d,aml_chip->config_msg.arg_valid:%d",aml_chip->detect_dtb_flag,aml_chip->config_msg.arg_valid);*/ ret = aml_sys_info_init(aml_chip); //key and storage and env if (ret < 0) { aml_nand_msg("nand init sys_info failed and ret:%d", ret); goto exit_error0; } if (aml_chip->detect_dtb_flag) { ret = -NAND_DETECT_DTB_FAILED; aml_nand_msg("%s()%d:Now we must stop init !!!",__func__, __LINE__); goto exit_error0; } ENV_NAND_LINE #ifdef AML_NAND_UBOOT if (aml_chip->config_msg.arg_valid == 1) { ENV_NAND_LINE ret = amlnand_configs_confirm(aml_chip); ENV_NAND_LINE if (ret < 0) { if ((aml_chip->init_flag > NAND_BOOT_UPGRATE) && (aml_chip->init_flag < NAND_BOOT_SCRUB_ALL)) { ret =0; } else { aml_nand_msg("nand configs confirm failed"); ret = -NAND_CONFIGS_FAILED; goto exit_error0; } } } else { ENV_NAND_LINE // do nothing.... aml_nand_msg("%s: do nothing!", __func__); } #endif ENV_NAND_LINE exit_error0: return ret; } int shipped_bbt_invalid_ops(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; uint64_t chipsize; unsigned char *buf = NULL; int ret = 0, pre_erase = 0; /* clean nand case!!!!! */ #if 1 //clean nand case /* need erase */ if (aml_chip->init_flag > NAND_BOOT_ERASE_PROTECT_CACHE) { /* sandisk flash can't promise all the blocks are clean and need to be erased at the first time using. need be sure that factory bad block can't be erased. */ if (flash->id[0] == NAND_MFR_SANDISK) { /* set info_disprotect variant with DISPROTECT_FBBT to skip env_protect erea. */ info_disprotect |= DISPROTECT_FBBT; amlnf_get_chip_size(&chipsize); /*background for carrying out, bbt table is all zero*/ /*make all blocks are erased*/ amlnf_erase_ops(0, chipsize, 1); pre_erase = 1; } /*check factory bad block by reading bad flags.*/ ret = shipped_badblock_detect(aml_chip); if (ret < 0 ) { aml_nand_msg("nand detect factory bbt failed and ret:%d", ret); goto exit_error0; } /* fill block status according to shipped bad block. */ ret = amlnand_init_block_status(aml_chip); if (ret < 0) { aml_nand_msg("nand init block status failed and ret:%d", ret); goto exit_error0; } aml_chip->nand_bbtinfo.arg_valid =1;//risking?it is need here. /* erasing the whole chip then! */ if (!pre_erase) amlnand_oops_handle(aml_chip,aml_chip->init_flag); /* save bbt info.*/ aml_chip->nand_bbtinfo.arg_valid =0; aml_chip->block_status->crc = aml_info_checksum((unsigned char *)aml_chip->block_status->blk_status,(MAX_CHIP_NUM*MAX_BLK_NUM)); ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->nand_bbtinfo),(unsigned char *)aml_chip->block_status,(unsigned char *)BBT_HEAD_MAGIC, sizeof(struct block_status)); if (ret < 0) { aml_nand_msg("nand save bbt failed and ret:%d", ret); goto exit_error0; } /* save fbbt info.*/ aml_chip->shipped_bbt_ptr->crc = aml_info_checksum((unsigned char *)aml_chip->shipped_bbt_ptr->shipped_bbt,(MAX_CHIP_NUM*MAX_BAD_BLK_NUM)); aml_chip->shipped_bbt_ptr->chipnum = controller->chip_num; ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->shipped_bbtinfo),(unsigned char *)aml_chip->shipped_bbt_ptr,(unsigned char *)SHIPPED_BBT_HEAD_MAGIC, sizeof(struct shipped_bbt)); if (ret < 0) { aml_nand_msg("nand save shipped bbt failed and ret:%d",ret); goto exit_error0; } } else { /* normal boot or upgrade, no need to erase the whole chip! */ /* init key info here!*/ ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->nand_key),buf,(unsigned char *)KEY_INFO_HEAD_MAGIC, aml_chip->keysize); if (ret < 0) { aml_nand_msg("invalid nand key\n"); goto exit_error0; } #ifdef CONFIG_SECURE_NAND ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->nand_secure),buf,(unsigned char *)SECURE_INFO_HEAD_MAGIC, CONFIG_SECURE_SIZE); if (ret < 0) { aml_nand_msg("invalid nand secure_ptr\n"); goto exit_error0; } #endif ret = shipped_badblock_detect(aml_chip); if (ret < 0 ) { aml_nand_msg("%s() %d: nand detect factory bbt failed and ret:%d", __FUNCTION__, __LINE__, ret); goto exit_error0; } ret = amlnand_init_block_status(aml_chip); if (ret < 0) { aml_nand_msg("%s() %d: nand init block status failed and ret:%d", __FUNCTION__, __LINE__, ret); goto exit_error0; } ret = aml_sys_info_init(aml_chip); //key and storage if (ret < 0) { aml_nand_msg("%s() %d: nand init sys_info failed and ret:%d", __FUNCTION__, __LINE__, ret); goto exit_error0; } aml_chip->block_status->crc = aml_info_checksum((unsigned char *)aml_chip->block_status->blk_status,(MAX_CHIP_NUM*MAX_BLK_NUM));/*liang:if bbt is valid, never be here! so do this!*/ ret = amlnand_save_info_by_name(aml_chip,(unsigned char *)&(aml_chip->nand_bbtinfo),(unsigned char *)aml_chip->block_status,(unsigned char *)BBT_HEAD_MAGIC, sizeof(struct block_status)); if (ret < 0) { aml_nand_msg("nand save bbt failed and ret:%d", ret); goto exit_error0; } aml_chip->shipped_bbt_ptr->crc = aml_info_checksum((unsigned char *)aml_chip->shipped_bbt_ptr->shipped_bbt,(MAX_CHIP_NUM*MAX_BAD_BLK_NUM)); aml_chip->shipped_bbt_ptr->chipnum = controller->chip_num; ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->shipped_bbtinfo),(unsigned char *)(aml_chip->shipped_bbt_ptr),(unsigned char *)SHIPPED_BBT_HEAD_MAGIC, sizeof(struct shipped_bbt)); if (ret < 0) { aml_nand_msg("nand save shipped bbt failed and ret:%d",ret); goto exit_error0; } if (aml_chip->detect_dtb_flag) { ret = -NAND_DETECT_DTB_FAILED; aml_nand_msg("%s()%d:Now we must stop init !!!",__func__, __LINE__); goto exit_error0; } //save config aml_chip->config_ptr->driver_version = DRV_PHY_VERSION; aml_chip->config_ptr->fbbt_blk_addr = aml_chip->shipped_bbtinfo.valid_blk_addr; amlnand_get_dev_num(aml_chip,(struct amlnf_partition *)amlnand_config); aml_chip->config_ptr->crc = aml_info_checksum((unsigned char *)(aml_chip->config_ptr->dev_para),(MAX_DEVICE_NUM*sizeof(struct dev_para))); ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->config_msg),(unsigned char *)(aml_chip->config_ptr),(unsigned char *)CONFIG_HEAD_MAGIC, sizeof(struct nand_config)); if (ret < 0) { aml_nand_msg("save nand dev_configs failed and ret:%d",ret); goto exit_error0; } } #else ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->nand_key),buf,(unsigned char *)KEY_INFO_HEAD_MAGIC, aml_chip->keysize); if (ret < 0) { aml_nand_msg("invalid nand key\n"); goto exit_error0; } #ifdef CONFIG_SECURE_NAND ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->nand_secure),buf,(unsigned char *)SECURE_INFO_HEAD_MAGIC, CONFIG_SECURE_SIZE); if (ret < 0) { aml_nand_msg("invalid nand secure_ptr\n"); goto exit_error0; } #endif if (aml_chip->init_flag > NAND_BOOT_ERASE_PROTECT_CACHE) { amlnand_oops_handle(aml_chip,aml_chip->init_flag); } ret = shipped_badblock_detect(aml_chip); if (ret < 0 ) { aml_nand_msg("nand detect factory bbt failed and ret:%d", ret); goto exit_error0; } ret = amlnand_init_block_status(aml_chip); if (ret < 0) { aml_nand_msg("nand init block status failed and ret:%d", ret); goto exit_error0; } //if((aml_chip->init_flag == NAND_BOOT_ERASE_ALL)) // amlnand_oops_handle(aml_chip,aml_chip->init_flag); ret = aml_sys_info_init(aml_chip); //key and storage if (ret < 0) { aml_nand_msg("nand init sys_info failed and ret:%d", ret); goto exit_error0; } aml_chip->block_status->crc = aml_info_checksum((unsigned char *)(aml_chip->block_status->blk_status),(MAX_CHIP_NUM*MAX_BLK_NUM)); ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->nand_bbtinfo),(unsigned char *)(aml_chip->block_status),(unsigned char *)BBT_HEAD_MAGIC, sizeof(struct block_status)); if (ret < 0) { aml_nand_msg("nand save bbt failed and ret:%d", ret); goto exit_error0; } aml_chip->shipped_bbt_ptr->crc = aml_info_checksum((unsigned char *)(aml_chip->shipped_bbt_ptr->shipped_bbt),(MAX_CHIP_NUM*MAX_BAD_BLK_NUM)); aml_chip->shipped_bbt_ptr->chipnum = controller->chip_num; ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->shipped_bbtinfo),(unsigned char *)(aml_chip->shipped_bbt_ptr),(unsigned char *)SHIPPED_BBT_HEAD_MAGIC, sizeof(struct shipped_bbt)); if (ret < 0) { aml_nand_msg("nand save shipped bbt failed and ret:%d",ret); goto exit_error0; } //save config aml_chip->config_ptr->driver_version = DRV_PHY_VERSION; aml_chip->config_ptr->fbbt_blk_addr = aml_chip->shipped_bbtinfo.valid_blk_addr; amlnand_get_dev_num(aml_chip,(struct amlnf_partition *)amlnand_config); aml_chip->config_ptr->crc = aml_info_checksum((unsigned char *)(aml_chip->config_ptr->dev_para),(MAX_DEVICE_NUM*sizeof(struct dev_para))); ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->config_msg),(unsigned char *)(aml_chip->config_ptr),(unsigned char *)CONFIG_HEAD_MAGIC, sizeof(struct nand_config)); if (ret < 0) { aml_nand_msg("save nand dev_configs failed and ret:%d",ret); goto exit_error0; } if (aml_chip->init_flag > NAND_BOOT_ERASE_PROTECT_CACHE) { aml_chip->uboot_env.update_flag = 1; if ((aml_chip->uboot_env.arg_valid == 1) && (aml_chip->uboot_env.update_flag)) { aml_nand_update_ubootenv(aml_chip,NULL); aml_chip->uboot_env.update_flag = 0; aml_nand_msg("NAND UPDATE CKECK : arg %s: arg_valid= %d, valid_blk_addr = %d, valid_page_addr = %d",\ "ubootenv",aml_chip->uboot_env.arg_valid, aml_chip->uboot_env.valid_blk_addr, aml_chip->uboot_env.valid_page_addr); } } #endif exit_error0: if (buf) { kfree(buf); buf = NULL; } return ret; } int shipped_bbt_valid_ops(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &aml_chip->controller; //struct nand_flash *flash = &aml_chip->flash; //struct chip_operation *operation = & aml_chip->operation; //struct chip_ops_para *ops_para = & aml_chip->ops_para; // nand_arg_info * nand_key = &aml_chip->nand_key; //nand_arg_info * nand_secure= &aml_chip->nand_secure; int ret = 0; ENV_NAND_LINE if (aml_chip->shipped_bbt_ptr->chipnum != controller->chip_num) { aml_nand_msg("nand read chipnum in config %d,controller->chip_num:%d",aml_chip->shipped_bbt_ptr->chipnum,controller->chip_num); ret = -NAND_SHIPPED_BADBLOCK_FAILED ; goto exit_error0; } ret = amlnand_init_block_status(aml_chip); if (ret < 0 ) { aml_nand_msg("nand init block status failed and ret:%d", ret); goto exit_error0; } if (aml_chip->init_flag < NAND_BOOT_ERASE_PROTECT_CACHE) { ENV_NAND_LINE ret = aml_sys_info_init(aml_chip); //key and storage if (ret < 0) { aml_nand_msg("nand init sys_info failed and ret:%d", ret); goto exit_error0; } ENV_NAND_LINE ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->config_msg),(unsigned char *)(aml_chip->config_ptr),(unsigned char *)CONFIG_HEAD_MAGIC, sizeof(struct nand_config)); if (ret < 0) { aml_nand_msg("nand scan config failed and ret:%d",ret); goto exit_error0; } ENV_NAND_LINE aml_chip->block_status->crc = aml_info_checksum((unsigned char *)aml_chip->block_status->blk_status,(MAX_CHIP_NUM*MAX_BLK_NUM)); ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->nand_bbtinfo),(unsigned char *)(aml_chip->block_status),(unsigned char *)BBT_HEAD_MAGIC, sizeof(struct block_status)); if (ret < 0) { aml_nand_msg("nand save bbt failed and ret:%d", ret); goto exit_error0; } if (aml_chip->detect_dtb_flag) { ret = -NAND_DETECT_DTB_FAILED; aml_nand_msg("%s()%d:Now we must stop init !!!",__func__, __LINE__); goto exit_error0; } ENV_NAND_LINE //save config aml_chip->config_ptr->driver_version = DRV_PHY_VERSION; aml_chip->config_ptr->fbbt_blk_addr = aml_chip->shipped_bbtinfo.valid_blk_addr; amlnand_get_dev_num(aml_chip,(struct amlnf_partition *)amlnand_config); ENV_NAND_LINE aml_chip->config_ptr->crc = aml_info_checksum((unsigned char *)(aml_chip->config_ptr->dev_para),(MAX_DEVICE_NUM*sizeof(struct dev_para))); ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->config_msg),(unsigned char *)(aml_chip->config_ptr),(unsigned char *)CONFIG_HEAD_MAGIC, sizeof(struct nand_config)); if (ret < 0) { aml_nand_msg("nand save config failed and ret:%d",ret); goto exit_error0; } ENV_NAND_LINE ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->config_msg),(unsigned char *)(aml_chip->config_ptr),(unsigned char *)CONFIG_HEAD_MAGIC, sizeof(struct nand_config)); if (ret < 0) { aml_nand_msg("nand scan config failed and ret:%d",ret); goto exit_error0; } ENV_NAND_LINE /*liang: check if config is exist,if it is, use old config which is saved in nandflash*/ if ((aml_chip->config_msg.arg_valid == 1)) { ENV_NAND_LINE ret = amlnand_configs_confirm(aml_chip); ENV_NAND_LINE if (ret < 0) { if ((aml_chip->init_flag > NAND_BOOT_UPGRATE) && (aml_chip->init_flag < NAND_BOOT_SCRUB_ALL)) { ret =0; }else{ aml_nand_msg("nand configs confirm failed"); ret = -NAND_CONFIGS_FAILED; goto exit_error0; } } }else{ //save config aml_chip->config_ptr->driver_version = DRV_PHY_VERSION; aml_chip->config_ptr->fbbt_blk_addr = aml_chip->shipped_bbtinfo.valid_blk_addr; amlnand_get_dev_num(aml_chip,(struct amlnf_partition *)amlnand_config); ENV_NAND_LINE aml_chip->config_ptr->crc = aml_info_checksum((unsigned char *)aml_chip->config_ptr->dev_para,(MAX_DEVICE_NUM*sizeof(struct dev_para))); ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->config_msg),(unsigned char *)(aml_chip->config_ptr),(unsigned char *)CONFIG_HEAD_MAGIC, sizeof(struct nand_config)); if (ret < 0) { aml_nand_msg("nand save config failed and ret:%d",ret); goto exit_error0; } ENV_NAND_LINE } /* fixme, can not reach here! */ if (aml_chip->init_flag > NAND_BOOT_ERASE_PROTECT_CACHE) { aml_chip->uboot_env.update_flag = 1; if ((aml_chip->uboot_env.arg_valid == 1) && (aml_chip->uboot_env.update_flag)) { aml_nand_update_ubootenv(aml_chip,NULL); aml_chip->uboot_env.update_flag = 0; aml_nand_msg("NAND UPDATE CKECK : arg %s: arg_valid= %d, valid_blk_addr = %d, valid_page_addr = %d",\ "ubootenv",aml_chip->uboot_env.arg_valid, aml_chip->uboot_env.valid_blk_addr, aml_chip->uboot_env.valid_page_addr); } } } else { aml_chip->nand_bbtinfo.arg_valid = 1;//risking?it is need here. amlnand_oops_handle(aml_chip,aml_chip->init_flag); aml_chip->nand_bbtinfo.arg_valid = 0; aml_chip->block_status->crc = aml_info_checksum((unsigned char *)aml_chip->block_status->blk_status,(MAX_CHIP_NUM*MAX_BLK_NUM)); ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->nand_bbtinfo),(unsigned char *)(aml_chip->block_status),(unsigned char *)BBT_HEAD_MAGIC, sizeof(struct block_status)); if (ret < 0) { aml_nand_msg("nand save bbt failed and ret:%d", ret); goto exit_error0; } } exit_error0: return ret; } /* fixme, */ #if 0 static int _get_bbt_fbbt(struct amlnand_chip *aml_chip, int flag) { int ret = 0; /* 3.1 get bbt info 1st*/ ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->nand_bbtinfo),(unsigned char *)(aml_chip->block_status),(unsigned char *)BBT_HEAD_MAGIC, sizeof(struct block_status)); if (ret < 0) { aml_nand_msg("%s() %d: nand scan bbt info failed and ret:%d", __FUNCTION__, __LINE__, ret); } /* 3.2 get fbbt info if bbt info is not exist.*/ if (aml_chip->nand_bbtinfo.arg_valid == 0) { ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->shipped_bbtinfo),(unsigned char *)aml_chip->shipped_bbt_ptr,(unsigned char *)SHIPPED_BBT_HEAD_MAGIC, sizeof(struct shipped_bbt)); if (ret < 0) { aml_nand_msg("%s() %d: nand scan shipped bbt info failed and ret:%d", __FUNCTION__, __LINE__, ret); } /* 3.3 ship bbt invalid, rebuild it */ if (aml_chip->shipped_bbtinfo.arg_valid == 0) { /*todo one, !!!!!! fbbt lost or clean nand. how to check that fbbt is lost or it only a clean nand. if a clean nand, first scan factory bad block and then created a bad block table. else fbbt is lost ,need to do some test-patterns and search bad blocks. */ ret = shipped_bbt_invalid_ops(aml_chip); if (ret < 0) { aml_nand_msg("shipped_bbt_invalid_ops and ret:%d", ret); goto _out; //fixme, may need free buffer } } else { /* 3.4 ship bbt valid, */ ret = shipped_bbt_valid_ops(aml_chip); if (ret < 0) { aml_nand_msg("shipped_bbt_valid_ops and ret:%d", ret); goto _out; //fixme, may need free buffer } } } else { /* bbt is valid */ /*liang: for amlnand_oops_handle,it will not erase fbbt if exist. fixme, may need to check fbbt which may need a refresh. */ ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->shipped_bbtinfo),(unsigned char *)(aml_chip->shipped_bbt_ptr),(unsigned char *)SHIPPED_BBT_HEAD_MAGIC, sizeof(struct shipped_bbt)); if (ret < 0) { aml_nand_msg("nand scan shipped info failed and ret:%d",ret); } amlnand_oops_handle(aml_chip,aml_chip->init_flag); } _out: return ret; } static int get_bbt_fbbt(struct amlnand_chip *aml_chip, int flag) { int ret = 0; switch (flag) { case NAND_BOOT_NORMAL: case NAND_BOOT_UPGRATE: break; case NAND_BOOT_ERASE_PROTECT_CACHE: break; case NAND_BOOT_ERASE_ALL: case NAND_BOOT_SCRUB_ALL: case NAND_SCAN_ID_INIT: //fixme, this flag should already return earlier. break; default: aml_nand_msg("%s() %d: no such flag(%d) while phy dev init.", __FUNCTION__, __LINE__, flag); } return ret; } #endif /***************************************************************************** *Name :amlnand_get_dev_configs *Description :search bbt /fbbt /config /key; *Parameter : *Return : *Note : *****************************************************************************/ int amlnand_get_dev_configs(struct amlnand_chip *aml_chip) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct read_retry_info *retry_info = &(controller->retry_info); int ret = 0; if (flash->blocksize < 0x40000) { aml_chip->keysize = flash->blocksize; aml_chip->dtbsize = flash->blocksize; } else { /* fix size key/dtb!!! max key/dtb size is 256KB */ aml_chip->keysize = 0x40000; aml_chip->dtbsize = 0x40000; } /* 1. setting config attribute.*/ ENV_NAND_LINE; amlnand_set_config_attribute(aml_chip); ENV_NAND_LINE; ret = amlnand_config_buf_malloc(aml_chip); if (ret < 0) { aml_nand_msg("nand malloc buf failed"); goto exit_error0; } ENV_NAND_LINE; /* get retry infos on the otp area.*/ if (aml_chip->flash.new_type) { ENV_NAND_LINE; aml_nand_msg("detect new nand here and new_type:%d", aml_chip->flash.new_type); ret = amlnand_set_readretry_slc_para(aml_chip); if (ret<0) { aml_nand_msg("setting new nand para failed and ret:0x%x", ret); goto exit_error0; } } #ifdef AML_NAND_UBOOT ENV_NAND_LINE; /* 2. search fbbt & nbbt in flash.*/ /* 2.1 search fbbt & nbbt in flash.*/ ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->nand_bbtinfo), (unsigned char *)(aml_chip->block_status), (unsigned char *)BBT_HEAD_MAGIC, sizeof(struct block_status)); if (ret < 0) { aml_nand_msg("%s() %d: nand scan bbt info failed and ret:%d", __FUNCTION__, __LINE__, ret); } ENV_NAND_LINE; ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->shipped_bbtinfo), (unsigned char *)aml_chip->shipped_bbt_ptr, (unsigned char *)SHIPPED_BBT_HEAD_MAGIC, sizeof(struct shipped_bbt)); if (ret < 0) { aml_nand_msg("%s() %d: nand scan shipped bbt info failed and ret:%d", __FUNCTION__, __LINE__, ret); } ENV_NAND_LINE; /* 2.1 get partition table from outsides, maybe sram.*/ ret = amlnand_get_partition_table(aml_chip); if (ret < 0 && ret != -NAND_DETECT_DTB_FAILED) { aml_nand_msg("amlnand_get_partition_table failed:ret:%d",ret); goto exit_error0; } #endif /* 3. with erase flags*/ /* aml_nand_msg("aml_chip->init_flag:%d,aml_chip->nand_bbtinfo.arg_valid:%d,aml_chip->shipped_bbtinfo.arg_valid:%d", aml_chip->init_flag,aml_chip->nand_bbtinfo.arg_valid,aml_chip->shipped_bbtinfo.arg_valid); */ if ((aml_chip->init_flag > NAND_BOOT_ERASE_PROTECT_CACHE)) { ENV_NAND_LINE; /* 3.2 get fbbt info if bbt info is not exist.*/ if (aml_chip->nand_bbtinfo.arg_valid == 0) { /* 3.3 ship bbt invalid, rebuild it */ if (aml_chip->shipped_bbtinfo.arg_valid == 0) { /*todo one, !!!!!!fbbt lost or clean nand. how to check that fbbt is lost or it only a clean nand. if a clean nand, first scan factory bad block and then created a bad block table. else fbbt is lost ,need to do some test-patterns and search bad blocks. */ ENV_NAND_LINE; ret = shipped_bbt_invalid_ops(aml_chip); if (ret < 0) { aml_nand_msg("shipped_bbt_invalid_ops and ret:%d", ret); goto exit_error0; } } else { /* 3.4 ship bbt valid, */ ENV_NAND_LINE; ret = shipped_bbt_valid_ops(aml_chip); if (ret < 0) { aml_nand_msg("shipped_bbt_valid_ops and ret:%d", ret); goto exit_error0; } } } else { /*liang: for amlnand_oops_handle,it will not erase fbbt if exist. fixme, may need to check fbbt which may need a refresh. */ ENV_NAND_LINE; amlnand_oops_handle(aml_chip,aml_chip->init_flag); if (aml_chip->shipped_bbtinfo.arg_valid == 0) { amlnand_recover_fbbt(aml_chip); /*recover fbbt table*/ /* save fbbt info.*/ aml_chip->shipped_bbt_ptr->crc = aml_info_checksum( (unsigned char *)aml_chip->shipped_bbt_ptr->shipped_bbt, (MAX_CHIP_NUM*MAX_BAD_BLK_NUM) ); aml_chip->shipped_bbt_ptr->chipnum = controller->chip_num; ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->shipped_bbtinfo), (unsigned char *)aml_chip->shipped_bbt_ptr, (unsigned char *)SHIPPED_BBT_HEAD_MAGIC, sizeof(struct shipped_bbt)); if (ret < 0) { aml_nand_msg("error:%s(),%d,ret=%d", __func__, __LINE__, ret); goto exit_error0; } } } ENV_NAND_LINE; }else if(aml_chip->init_flag == NAND_BOOT_ERASE_PROTECT_CACHE) { /* 4. erase protect cache only!*/ ENV_NAND_LINE; amlnand_oops_handle(aml_chip, aml_chip->init_flag); } else { /* 5. without erase, normal boot or upgrade */ if (aml_chip->nand_bbtinfo.arg_valid == 0) { // bbt invalid #ifdef AML_NAND_UBOOT ENV_NAND_LINE if (aml_chip->shipped_bbtinfo.arg_valid == 0) { // ship bbt invalid ret = shipped_bbt_invalid_ops(aml_chip); if (ret < 0) { aml_nand_msg("shipped_bbt_invalid_ops and ret:%d", ret); goto exit_error0; } } else { // ship bbt valid ENV_NAND_LINE ret = shipped_bbt_valid_ops(aml_chip); if (ret < 0) { aml_nand_msg("shipped_bbt_valid_ops and ret:%d", ret); goto exit_error0; } } #else aml_nand_msg("nand scan bbt failed"); ret = -NAND_READ_FAILED; goto exit_error0; #endif } else { // bbt valid if (aml_chip->shipped_bbtinfo.arg_valid == 0) { amlnand_recover_fbbt(aml_chip); /*recover fbbt table*/ /* save fbbt info.*/ aml_chip->shipped_bbt_ptr->crc = aml_info_checksum( (unsigned char *)aml_chip->shipped_bbt_ptr->shipped_bbt, (MAX_CHIP_NUM*MAX_BAD_BLK_NUM) ); aml_chip->shipped_bbt_ptr->chipnum = controller->chip_num; ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->shipped_bbtinfo), (unsigned char *)aml_chip->shipped_bbt_ptr, (unsigned char *)SHIPPED_BBT_HEAD_MAGIC, sizeof(struct shipped_bbt)); if (ret < 0) { aml_nand_msg("error:%s(),%d,ret=%d", __func__, __LINE__, ret); goto exit_error0; } } ENV_NAND_LINE ret = bbt_valid_ops(aml_chip); if (ret < 0) { aml_nand_msg("shipped_bbt_valid_ops and ret:%d", ret); goto exit_error0; } } } /* normal boot or upgrade */ ENV_NAND_LINE if ((aml_chip->init_flag < NAND_BOOT_ERASE_PROTECT_CACHE)) { #ifdef AML_NAND_UBOOT if (aml_chip->config_msg.arg_valid == 0) { // if no config,just save ENV_NAND_LINE aml_chip->config_ptr->driver_version = DRV_PHY_VERSION; ENV_NAND_LINE aml_chip->config_ptr->fbbt_blk_addr = aml_chip->shipped_bbtinfo.valid_blk_addr; ENV_NAND_LINE /* fixme, debug code. */ aml_nand_msg("%s() %d", __func__, __LINE__); amlnand_get_dev_num(aml_chip,(struct amlnf_partition *)amlnand_config); ENV_NAND_LINE aml_chip->config_ptr->crc = aml_info_checksum((unsigned char *)aml_chip->config_ptr->dev_para,(MAX_DEVICE_NUM*sizeof(struct dev_para))); ENV_NAND_LINE ret = amlnand_save_info_by_name(aml_chip, (unsigned char *)&(aml_chip->config_msg),(unsigned char *)(aml_chip->config_ptr),(unsigned char *)CONFIG_HEAD_MAGIC, sizeof(struct nand_config)); if (ret < 0) { aml_nand_msg("nand save config failed and ret:%d",ret); goto exit_error0; } } /* scan phy partition info here, if we can't find phy partition, we will calc and save it in phydev init stage. */ ret = amlnand_info_init(aml_chip, (unsigned char *)&(aml_chip->nand_phy_partition), (unsigned char *)aml_chip->phy_part_ptr, (unsigned char *)PHY_PARTITION_HEAD_MAGIC, sizeof(struct phy_partition_info)); if (ret < 0) aml_nand_msg("scan phy partition failed and ret:%d", ret); ENV_NAND_LINE if (flash->new_type && (flash->new_type < 10) && (retry_info->default_flag == 0)) { ENV_NAND_LINE ret = aml_nand_save_hynix_info(aml_chip); if (ret < 0) { aml_nand_msg("hynix nand save readretry info failed and ret:%d", ret); goto exit_error0; } } if (aml_chip->shipped_bbt_ptr) { kfree(aml_chip->shipped_bbt_ptr); aml_chip->shipped_bbt_ptr = NULL; } #endif ENV_NAND_LINE amlnand_info_error_handle(aml_chip); /*liang:sure? is it need?*/ /* fixme, yyh */ repair_reserved_bad_block(aml_chip); /* fixme, do not free buffers....*/ return ret ; } /*fixme, should not return here?*/ //return ret; ENV_NAND_LINE; exit_error0: /* fixme, debug code*/ /* free this for bad block detect! */ ENV_NAND_LINE; if (ret != -NAND_DETECT_DTB_FAILED) { kfree(aml_chip->block_status); aml_chip->block_status = NULL; } #if 0 kfree(aml_chip->shipped_bbt_ptr); aml_chip->shipped_bbt_ptr = NULL; kfree(aml_chip->config_ptr); aml_chip->config_ptr = NULL; /* fixme, user_page_buf */ kfree(aml_chip->user_oob_buf); aml_chip->user_oob_buf = NULL; kfree(aml_chip->user_page_buf); aml_chip->user_page_buf = NULL; #endif return ret; }