| /* |
| * drivers/amlogic/mtd/rsv_manage.c |
| * |
| * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| */ |
| |
| #include "aml_mtd.h" |
| |
| /* protect flag inside */ |
| static int rsv_protect = 1; |
| |
| static inline void _aml_rsv_disprotect(void) |
| { |
| rsv_protect = 0; |
| } |
| |
| static inline void _aml_rsv_protect(void) |
| { |
| rsv_protect = 1; |
| } |
| |
| static inline int _aml_rsv_isprotect(void) |
| { |
| return rsv_protect; |
| } |
| static struct free_node_t *get_free_node(struct mtd_info *mtd) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| unsigned int index; |
| |
| pr_info("%s %d: bitmap=%llx\n", __func__, __LINE__, |
| aml_chip->freeNodeBitmask); |
| |
| index = find_first_zero_bit((void *)&aml_chip->freeNodeBitmask, 64); |
| if (index > RESERVED_BLOCK_NUM) |
| pr_info("%s %d: index=%d is greater than max! error", |
| __func__, __LINE__, index); |
| test_and_set_bit(index, (void *)&aml_chip->freeNodeBitmask); |
| |
| pr_info("%s %d: bitmap=%llx\n", __func__, __LINE__, |
| aml_chip->freeNodeBitmask); |
| |
| return aml_chip->free_node[index]; |
| |
| } |
| |
| static void release_free_node(struct mtd_info *mtd, |
| struct free_node_t *free_node) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| unsigned int index_save = free_node->index; |
| |
| pr_info("%s %d: bitmap=%llx\n", __func__, __LINE__, |
| aml_chip->freeNodeBitmask); |
| |
| if (free_node->index > RESERVED_BLOCK_NUM) |
| pr_info("%s %d: index=%d is greater than max! error", |
| __func__, __LINE__, free_node->index); |
| |
| test_and_clear_bit(free_node->index, |
| (void *)&aml_chip->freeNodeBitmask); |
| |
| /*memset zero to protect from dead-loop*/ |
| memset(free_node, 0, sizeof(struct free_node_t)); |
| free_node->index = index_save; |
| pr_info("%s %d: bitmap=%llx\n", __func__, __LINE__, |
| aml_chip->freeNodeBitmask); |
| } |
| |
| |
| int aml_nand_rsv_erase_protect(struct mtd_info *mtd, unsigned int block_addr) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| |
| if (!_aml_rsv_isprotect()) |
| return 0; |
| |
| #ifdef AML_NAND_UBOOT |
| if (aml_chip->aml_nandkey_info != NULL) { |
| if (aml_chip->aml_nandkey_info->valid) |
| if ((!(info_disprotect & DISPROTECT_KEY)) |
| && ((block_addr >= aml_chip->aml_nandkey_info->start_block) |
| && (block_addr < aml_chip->aml_nandkey_info->end_block))) |
| return -1; /*need skip key blocks*/ |
| } |
| if (aml_chip->aml_nandbbt_info != NULL) { |
| if (aml_chip->aml_nandbbt_info->valid) |
| if ((block_addr >= aml_chip->aml_nandbbt_info->start_block) |
| && (block_addr < aml_chip->aml_nandbbt_info->end_block)) |
| return -1; /*need skip bbt blocks*/ |
| } |
| #else |
| if (aml_chip->aml_nandkey_info != NULL) { |
| if (aml_chip->aml_nandkey_info->valid) |
| if ((block_addr >= aml_chip->aml_nandkey_info->start_block) |
| && (block_addr < aml_chip->aml_nandkey_info->end_block)) |
| return -1; /*need skip key blocks*/ |
| } |
| if (aml_chip->aml_nandbbt_info != NULL) { |
| if (aml_chip->aml_nandbbt_info->valid) |
| if ((block_addr >= aml_chip->aml_nandbbt_info->start_block) |
| && (block_addr < aml_chip->aml_nandbbt_info->end_block)) |
| return -1; /*need skip bbt blocks*/ |
| } |
| #endif |
| return 0; |
| } |
| |
| |
| /* only read bad block labeled ops */ |
| int aml_nand_scan_shipped_bbt(struct mtd_info *mtd) |
| { |
| struct nand_chip *chip = mtd->priv; |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| struct aml_nand_platform *plat = aml_chip->platform; |
| unsigned char *data_buf; |
| int32_t read_cnt, page, pages_per_blk; |
| loff_t addr, offset; |
| int start_blk = 0, total_blk = 0, bad_blk_cnt = 0, phys_erase_shift; |
| int realpage, col0_data = 0, col0_oob = 0, valid_page_num = 1; |
| int col_data_sandisk[6], bad_sandisk_flag = 0; |
| int i, j; |
| |
| phys_erase_shift = fls(mtd->erasesize) - 1; |
| chip->pagebuf = -1; |
| pages_per_blk = (1 << (chip->phys_erase_shift - chip->page_shift)); |
| |
| data_buf = kzalloc(mtd->writesize, GFP_KERNEL); |
| if (data_buf == NULL) |
| return -ENOMEM; |
| |
| if ((strncmp((char *)plat->name, |
| NAND_BOOT_NAME, strlen((const char *)NAND_BOOT_NAME)))) { |
| memset(&aml_chip->nand_bbt_info->nand_bbt[0], |
| 0, MAX_BAD_BLK_NUM); |
| if (nand_boot_flag) |
| offset = (1024 * mtd->writesize / aml_chip->plane_num); |
| else |
| offset = 0; |
| |
| start_blk = (int)(offset >> phys_erase_shift); |
| total_blk = (int)(mtd->size >> phys_erase_shift); |
| } |
| |
| pr_info("scanning flash chip_num=%d %d %d %d\n", controller->chip_num, |
| aml_chip->valid_chip[0], aml_chip->valid_chip[1], |
| aml_chip->valid_chip[2]); |
| do { |
| offset = mtd->erasesize; |
| offset *= start_blk; |
| for (i = 0; i < controller->chip_num; i++) { |
| /* if (aml_chip->valid_chip[i]) { */ |
| for (read_cnt = 0; read_cnt < 2; read_cnt++) { |
| if (aml_chip->mfr_type == NAND_MFR_SANDISK) { |
| addr = offset + read_cnt*mtd->writesize; |
| } else |
| addr = offset + |
| (pages_per_blk - 1) * read_cnt * mtd->writesize; |
| |
| realpage = (int)(addr >> chip->page_shift); |
| page = realpage & chip->pagemask; |
| |
| if (page != -1) { |
| valid_page_num = |
| mtd->writesize>>chip->page_shift; |
| valid_page_num /= aml_chip->plane_num; |
| |
| aml_chip->page_addr = page / valid_page_num; |
| if (unlikely(aml_chip->page_addr >= aml_chip->internal_page_nums)) { |
| aml_chip->page_addr -= aml_chip->internal_page_nums; |
| aml_chip->page_addr |= |
| (1 << aml_chip->internal_chip_shift)*aml_chip->internal_chipnr; |
| } |
| } |
| if (aml_chip->plane_num == 2) { |
| chip->select_chip(mtd, i); |
| aml_chip->aml_nand_wait_devready(aml_chip, i); |
| aml_chip->aml_nand_command(aml_chip, |
| NAND_CMD_READ0, |
| 0x00, aml_chip->page_addr, i); |
| |
| if (!aml_chip->aml_nand_wait_devready(aml_chip, i)) |
| pr_info("%s, %d,selected chip%d not ready\n", |
| __func__, __LINE__, i); |
| |
| if (aml_chip->ops_mode & AML_CHIP_NONE_RB) |
| chip->cmd_ctrl(mtd, |
| NAND_CMD_READ0 & 0xff, |
| NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); |
| udelay(2); |
| aml_chip->aml_nand_command(aml_chip, |
| NAND_CMD_TWOPLANE_READ1, |
| 0x00, aml_chip->page_addr, i); |
| udelay(2); |
| |
| if (aml_chip->mfr_type == NAND_MFR_SANDISK) { |
| for (j = 0; j < 6; j++) |
| col_data_sandisk[j] = chip->read_byte(mtd); |
| } else |
| col0_data = chip->read_byte(mtd); |
| |
| aml_chip->aml_nand_command(aml_chip, |
| NAND_CMD_TWOPLANE_READ2, |
| aml_chip->page_size, |
| aml_chip->page_addr, i); |
| udelay(2); |
| |
| if (aml_chip->mfr_type == NAND_MFR_SANDISK) |
| col0_oob = 0x0; |
| else |
| col0_oob = chip->read_byte(mtd); |
| /* pr_info("col0_oob=%x\n",col0_oob); */ |
| |
| } else if (aml_chip->plane_num == 1) { |
| chip->select_chip(mtd, i); |
| /* nand_get_chip(); */ |
| /*aml_chip->aml_nand_select_chip(aml_chip, i);*/ |
| aml_chip->aml_nand_command(aml_chip, |
| NAND_CMD_READ0, 0x00, |
| aml_chip->page_addr, i); |
| udelay(2); |
| |
| if (!aml_chip->aml_nand_wait_devready(aml_chip, i)) |
| pr_info("%s, %d,selected chip%d not ready\n", |
| __func__, __LINE__, i); |
| |
| if (aml_chip->ops_mode & AML_CHIP_NONE_RB) |
| chip->cmd_ctrl(mtd, |
| NAND_CMD_READ0 & 0xff, |
| NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); |
| udelay(2); |
| |
| if (aml_chip->mfr_type == NAND_MFR_SANDISK) { |
| for (j = 0; j < 6; j++) |
| col_data_sandisk[j] = |
| chip->read_byte(mtd); |
| } else |
| col0_data = chip->read_byte(mtd); |
| |
| /* pr_info("col0_data =%x\n",col0_data); */ |
| |
| if (aml_chip->mfr_type != NAND_MFR_SANDISK) |
| aml_chip->aml_nand_command(aml_chip, |
| NAND_CMD_RNDOUT, |
| aml_chip->page_size, -1, i); |
| udelay(2); |
| |
| if (aml_chip->mfr_type == NAND_MFR_SANDISK) |
| col0_oob = 0x0; |
| else |
| col0_oob = chip->read_byte(mtd); |
| /* pr_info("col0_oob =%x\n",col0_oob); */ |
| } |
| |
| if ((aml_chip->mfr_type == 0xC8)) { |
| if ((col0_oob != 0xFF) || (col0_data != 0xFF)) { |
| pr_info("detect factory Bad blk:%llx blk:%d chip:%d\n", |
| (uint64_t)addr, start_blk, i); |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| start_blk | 0x8000; |
| aml_chip->block_status[start_blk] = NAND_FACTORY_BAD; |
| break; |
| } |
| } |
| |
| if ((col0_oob == 0xFF)) |
| continue; |
| |
| if (col0_oob != 0xFF) { |
| pr_info("%s:%d factory ship bbt found\n", __func__, __LINE__); |
| if (aml_chip->mfr_type == 0xc2) { |
| if (col0_oob != 0xFF) { |
| pr_info("factory Bad blk:%llx blk=%d chip=%d\n", |
| (uint64_t)addr, start_blk, i); |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| start_blk|0x8000; |
| aml_chip->block_status[start_blk] = NAND_FACTORY_BAD; |
| break; |
| } |
| } |
| |
| if (aml_chip->mfr_type == 0xef) { |
| if (col0_oob != 0xFF) { |
| pr_info("factory Bad blk:%llx blk=%d chip=%d\n", |
| (uint64_t)addr, start_blk, i); |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| start_blk|0x8000; |
| aml_chip->block_status[start_blk] = NAND_FACTORY_BAD; |
| break; |
| } |
| } |
| |
| if (aml_chip->mfr_type == NAND_MFR_SANDISK) { |
| for (j = 0; j < 6; j++) { |
| if (col_data_sandisk[j] == 0x0) { |
| bad_sandisk_flag = 1; |
| break; |
| } |
| } |
| if (bad_sandisk_flag) { |
| pr_info("factory Bad blk:%llx blk=%d chip=%d\n", |
| (uint64_t)addr, start_blk, i); |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| start_blk|0x8000; |
| aml_chip->block_status[start_blk] = NAND_FACTORY_BAD; |
| bad_sandisk_flag = 0; |
| break; |
| } |
| } |
| |
| if (aml_chip->mfr_type == NAND_MFR_SAMSUNG) { |
| if ((col0_oob != 0xFF) && (col0_data != 0xFF)) { |
| pr_info("factory Bad blk:%llx blk=%d chip=%d\n", |
| (uint64_t)addr, start_blk, i); |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| start_blk|0x8000; |
| aml_chip->block_status[start_blk] = NAND_FACTORY_BAD; |
| break; |
| } |
| } |
| |
| if (aml_chip->mfr_type == NAND_MFR_TOSHIBA) { |
| if ((col0_oob != 0xFF) && (col0_data != 0xFF)) { |
| pr_info("factory Bad blk:%llx blk=%d chip=%d\n", |
| (uint64_t)addr, start_blk, i); |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| start_blk|0x8000; |
| aml_chip->block_status[start_blk] = NAND_FACTORY_BAD; |
| break; |
| } |
| } |
| |
| if (aml_chip->mfr_type == NAND_MFR_MICRON) { |
| if (col0_oob == 0x0) { |
| pr_info("factory Bad blk:%llx blk=%d chip=%d\n", |
| (uint64_t)addr, start_blk, i); |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| start_blk|0x8000; |
| aml_chip->block_status[start_blk] = NAND_FACTORY_BAD; |
| break; |
| } |
| } |
| |
| if (aml_chip->mfr_type == NAND_MFR_HYNIX) { |
| if (col0_oob != 0xFF) { |
| pr_info("factory Bad blk:%llx blk=%d chip=%d\n", |
| (uint64_t)addr, start_blk, i); |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| start_blk|0x8000; |
| aml_chip->block_status[start_blk] = NAND_FACTORY_BAD; |
| /* if plane 0 is bad block,just set plane 1 to bad */ |
| if ((start_blk % 2) == 0) { |
| start_blk += 1; |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| start_blk|0x8000; |
| aml_chip->block_status[start_blk] = NAND_FACTORY_BAD; |
| pr_info(" pl0 is bad block,just set plane 1 to bad:\n"); |
| } else { |
| aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = |
| (start_blk - 1)|0x8000; |
| aml_chip->block_status[start_blk - 1] = |
| NAND_FACTORY_BAD; |
| pr_info(" pl1 is bad block,just set plane 0 to bad:\n"); |
| } |
| break; |
| } |
| } |
| } |
| } |
| /* } */ |
| } |
| } while ((++start_blk) < total_blk); |
| |
| pr_info("aml_nand_scan_bbt: factory Bad block bad_blk_cnt=%d\n", |
| bad_blk_cnt); |
| kfree(data_buf); |
| return 0; |
| } |
| |
| int aml_nand_read_rsv_info(struct mtd_info *mtd, |
| struct aml_nandrsv_info_t *nandrsv_info, size_t offset, u_char *buf) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| struct oobinfo_t *oobinfo; |
| int error = 0; |
| loff_t addr = 0; |
| size_t amount_loaded = 0; |
| size_t len; |
| struct mtd_oob_ops aml_oob_ops; |
| unsigned char *data_buf; |
| unsigned char oob_buf[sizeof(struct oobinfo_t)]; |
| |
| addr = nandrsv_info->valid_node->phy_blk_addr; |
| addr *= mtd->erasesize; |
| addr += nandrsv_info->valid_node->phy_page_addr * mtd->writesize; |
| pr_info("%s:%d,read %s info to %llx\n", __func__, __LINE__, |
| nandrsv_info->name, addr); |
| |
| data_buf = aml_chip->rsv_data_buf; |
| |
| oobinfo = (struct oobinfo_t *)oob_buf; |
| while (amount_loaded < nandrsv_info->size) { |
| aml_oob_ops.mode = MTD_OPS_AUTO_OOB; |
| aml_oob_ops.len = mtd->writesize; |
| aml_oob_ops.ooblen = sizeof(struct oobinfo_t); |
| /*aml_oob_ops.ooboffs = mtd->ecclayout->oobfree[0].offset;*/ |
| aml_oob_ops.ooboffs = 0; |
| aml_oob_ops.datbuf = data_buf; |
| aml_oob_ops.oobbuf = oob_buf; |
| |
| memset((unsigned char *)aml_oob_ops.datbuf, |
| 0x0, mtd->writesize); |
| memset((unsigned char *)aml_oob_ops.oobbuf, |
| 0x0, aml_oob_ops.ooblen); |
| |
| error = mtd->_read_oob(mtd, addr, &aml_oob_ops); |
| if ((error != 0) && (error != -EUCLEAN)) { |
| pr_info("blk good but read failed: %llx, %d\n", |
| (uint64_t)addr, error); |
| return 1; |
| } |
| |
| if (memcmp(oobinfo->name, nandrsv_info->name, 4)) |
| pr_info("invalid nand info %s magic: %llx\n", |
| nandrsv_info->name, (uint64_t)addr); |
| |
| addr += mtd->writesize; |
| len = min_t(uint32_t, mtd->writesize, |
| (nandrsv_info->size - amount_loaded)); |
| memcpy(buf + amount_loaded, data_buf, len); |
| amount_loaded += mtd->writesize; |
| } |
| if (amount_loaded < nandrsv_info->size) |
| return 1; |
| #if 0 |
| uint64_t dump_len = 0; |
| unsigned char *tmp = NULL; |
| |
| if (!strncmp(nandrsv_info->name, |
| KEY_NAND_MAGIC, strlen(nandrsv_info->name))) { |
| tmp = buf; |
| dump_len = nandrsv_info->size / 16; |
| while (dump_len--) { |
| pr_info("\t%02x %02x %02x %02x %02x %02x %02x %02x", |
| tmp[0], tmp[1], tmp[2], tmp[3], |
| tmp[4], tmp[5], tmp[6], tmp[7]); |
| pr_info(" %02x %02x %02x %02x %02x %02x %02x %02x\n", |
| tmp[8], tmp[9], tmp[10], tmp[11], |
| tmp[12], tmp[13], tmp[14], tmp[15]); |
| tmp += 16; |
| } |
| } |
| #endif |
| /* if(data_buf) */ |
| /* kfree(data_buf); */ |
| return 0; |
| } |
| |
| int aml_nand_read_env(struct mtd_info *mtd, size_t offset, u_char *buf) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| |
| if (!aml_chip->aml_nandenv_info->valid) { |
| pr_info("%s %d: %s is invalid! read exit!\n", |
| __func__, __LINE__, |
| aml_chip->aml_nandenv_info->name); |
| return 1; |
| } |
| if (aml_nand_read_rsv_info(mtd, |
| aml_chip->aml_nandenv_info, offset, (u_char *)buf)) |
| return 1; |
| return 0; |
| } |
| |
| int aml_nand_read_key(struct mtd_info *mtd, size_t offset, u_char *buf) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| |
| if (!aml_chip->aml_nandkey_info->valid) { |
| pr_info("%s %d: %s is invalid! read exit!\n", |
| __func__, __LINE__, |
| aml_chip->aml_nandkey_info->name); |
| return 1; |
| } |
| if (aml_nand_read_rsv_info(mtd, |
| aml_chip->aml_nandkey_info, offset, (u_char *)buf)) |
| return 1; |
| return 0; |
| } |
| |
| int aml_nand_read_dtb(struct mtd_info *mtd, size_t offset, u_char *buf) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| |
| if (!aml_chip->aml_nanddtb_info->valid) { |
| pr_info("%s %d: %s is invalid! read exit!", |
| __func__, __LINE__, |
| aml_chip->aml_nanddtb_info->name); |
| return 1; |
| } |
| if (aml_nand_read_rsv_info(mtd, |
| aml_chip->aml_nanddtb_info, offset, (u_char *)buf)) |
| return 1; |
| return 0; |
| } |
| |
| static int aml_nand_write_rsv(struct mtd_info *mtd, |
| struct aml_nandrsv_info_t *nandrsv_info, loff_t offset, u_char *buf) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| struct oobinfo_t *oobinfo; |
| int error = 0; |
| loff_t addr = 0; |
| size_t amount_saved = 0; |
| size_t len; |
| struct mtd_oob_ops aml_oob_ops; |
| unsigned char *data_buf; |
| unsigned char oob_buf[sizeof(struct oobinfo_t)]; |
| |
| data_buf = aml_chip->rsv_data_buf; |
| /* if (data_buf == NULL) */ |
| /* return -ENOMEM; */ |
| |
| addr = offset; |
| pr_info("%s:%d,write info to %llx\n", __func__, __LINE__, addr); |
| oobinfo = (struct oobinfo_t *)oob_buf; |
| memcpy(oobinfo->name, nandrsv_info->name, 4); |
| oobinfo->ec = nandrsv_info->valid_node->ec; |
| oobinfo->timestamp = nandrsv_info->valid_node->timestamp; |
| |
| #if 0 |
| uint64_t dump_len = 0; |
| unsigned char *tmp = NULL; |
| |
| if (!strncmp(nandrsv_info->name, |
| KEY_NAND_MAGIC, strlen(nandrsv_info->name))) { |
| tmp = buf; |
| dump_len = nandrsv_info->size / 16; |
| while (dump_len--) { |
| pr_info("\t%02x %02x %02x %02x %02x %02x %02x %02x", |
| tmp[0], tmp[1], tmp[2], tmp[3], |
| tmp[4], tmp[5], tmp[6], tmp[7]); |
| pr_info(" %02x %02x %02x %02x %02x %02x %02x %02x\n", |
| tmp[8], tmp[9], tmp[10], tmp[11], |
| tmp[12], tmp[13], tmp[14], tmp[15]); |
| tmp += 16; |
| } |
| } |
| #endif |
| while (amount_saved < nandrsv_info->size) { |
| aml_oob_ops.mode = MTD_OPS_AUTO_OOB; |
| aml_oob_ops.len = mtd->writesize; |
| aml_oob_ops.ooblen = sizeof(struct oobinfo_t); |
| aml_oob_ops.ooboffs = 0;/*mtd->ecclayout->oobfree[0].offset;*/ |
| aml_oob_ops.datbuf = data_buf; |
| aml_oob_ops.oobbuf = oob_buf; |
| |
| memset((unsigned char *)aml_oob_ops.datbuf, |
| 0x0, mtd->writesize); |
| len = min_t(uint32_t, mtd->writesize, |
| nandrsv_info->size - amount_saved); |
| memcpy((unsigned char *)aml_oob_ops.datbuf, |
| buf + amount_saved, len); |
| |
| error = mtd->_write_oob(mtd, addr, &aml_oob_ops); |
| if (error) { |
| pr_info("blk check good but write failed: %llx, %d\n", |
| (uint64_t)addr, error); |
| return 1; |
| } |
| addr += mtd->writesize; |
| amount_saved += mtd->writesize; |
| } |
| if (amount_saved < nandrsv_info->size) |
| return 1; |
| /* kfree(data_buf); */ |
| return 0; |
| } |
| |
| int aml_nand_save_rsv_info(struct mtd_info *mtd, |
| struct aml_nandrsv_info_t *nandrsv_info, u_char *buf) |
| { |
| struct free_node_t *free_node = NULL, *tmp_node = NULL; |
| int error = 0, pages_per_blk, i = 1; |
| loff_t addr = 0; |
| struct erase_info erase_info; |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| |
| pages_per_blk = mtd->erasesize / mtd->writesize; |
| if ((mtd->writesize < nandrsv_info->size) |
| && (aml_chip->aml_nandenv_info->valid == 1)) |
| i = (nandrsv_info->size + mtd->writesize - 1) / mtd->writesize; |
| pr_info("%s:%d,valid=%d, pages=%d\n", __func__, __LINE__, |
| aml_chip->aml_nandenv_info->valid, i); |
| RE_SEARCH: |
| if (nandrsv_info->valid) { |
| /*pr_info("%s:%d,phy_page_addr=%d,pages=%d\n", |
| * __func__, __LINE__, |
| * nandrsv_info->valid_node->phy_page_addr, i); |
| **/ |
| nandrsv_info->valid_node->phy_page_addr += i; |
| if ((nandrsv_info->valid_node->phy_page_addr+i) > pages_per_blk) { |
| if ((nandrsv_info->valid_node->phy_page_addr - i) == pages_per_blk) { |
| nandrsv_info->valid_node->status = 0; |
| addr = nandrsv_info->valid_node->phy_blk_addr; |
| addr *= mtd->erasesize; |
| memset(&erase_info, |
| 0, sizeof(struct erase_info)); |
| erase_info.mtd = mtd; |
| erase_info.addr = addr; |
| erase_info.len = mtd->erasesize; |
| _aml_rsv_disprotect(); |
| error = mtd->_erase(mtd, &erase_info); |
| _aml_rsv_protect(); |
| nandrsv_info->valid_node->status = 1; |
| nandrsv_info->valid_node->ec++; |
| pr_info("---erase bad env block:%llx\n", addr); |
| } |
| /* free_node = kzalloc(sizeof(struct free_node_t), */ |
| /* GFP_KERNEL); */ |
| free_node = get_free_node(mtd); |
| if (free_node == NULL) |
| return -ENOMEM; |
| |
| free_node->phy_blk_addr = |
| nandrsv_info->valid_node->phy_blk_addr; |
| free_node->ec = nandrsv_info->valid_node->ec; |
| tmp_node = nandrsv_info->free_node; |
| while (tmp_node->next != NULL) |
| tmp_node = tmp_node->next; |
| |
| tmp_node->next = free_node; |
| |
| tmp_node = nandrsv_info->free_node; |
| nandrsv_info->valid_node->phy_blk_addr = |
| tmp_node->phy_blk_addr; |
| nandrsv_info->valid_node->phy_page_addr = 0; |
| nandrsv_info->valid_node->ec = tmp_node->ec; |
| nandrsv_info->valid_node->timestamp += 1; |
| nandrsv_info->free_node = tmp_node->next; |
| release_free_node(mtd, tmp_node); |
| } |
| } else { |
| tmp_node = nandrsv_info->free_node; |
| nandrsv_info->valid_node->phy_blk_addr = tmp_node->phy_blk_addr; |
| nandrsv_info->valid_node->phy_page_addr = 0; |
| nandrsv_info->valid_node->ec = tmp_node->ec; |
| nandrsv_info->valid_node->timestamp += 1; |
| nandrsv_info->free_node = tmp_node->next; |
| release_free_node(mtd, tmp_node); |
| } |
| |
| addr = nandrsv_info->valid_node->phy_blk_addr; |
| addr *= mtd->erasesize; |
| addr += nandrsv_info->valid_node->phy_page_addr * mtd->writesize; |
| |
| pr_info("%s:%d,save info to %llx\n", __func__, __LINE__, addr); |
| |
| if (nandrsv_info->valid_node->phy_page_addr == 0) { |
| error = mtd->_block_isbad(mtd, addr); |
| if (error != 0) { |
| /* |
| *bad block here, need fix it |
| *because of info_blk list may be include bad block, |
| *so we need check it and done here. if don't, |
| *some bad blocks may be erase here |
| *and env will lost or too much ecc error |
| **/ |
| pr_info("have bad block in info_blk list!!!!\n"); |
| nandrsv_info->valid_node->phy_page_addr = |
| pages_per_blk - i; |
| goto RE_SEARCH; |
| } |
| |
| memset(&erase_info, 0, sizeof(struct erase_info)); |
| erase_info.mtd = mtd; |
| erase_info.addr = addr; |
| erase_info.len = mtd->erasesize; |
| nandrsv_info->valid_node->status = 0; |
| _aml_rsv_disprotect(); |
| error = mtd->_erase(mtd, &erase_info); |
| _aml_rsv_protect(); |
| if (error) { |
| pr_info("env free blk erase failed %d\n", error); |
| mtd->_block_markbad(mtd, addr); |
| return error; |
| } |
| nandrsv_info->valid_node->ec++; |
| nandrsv_info->valid_node->status = 1; |
| } |
| |
| if (aml_nand_write_rsv(mtd, nandrsv_info, addr, (u_char *) buf)) { |
| pr_info("update nand env FAILED!\n"); |
| return 1; |
| } |
| |
| return error; |
| } |
| |
| int aml_nand_save_env(struct mtd_info *mtd, u_char *buf) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| /* env_t *env_ptr = (env_t *)buf; */ |
| |
| if (!aml_chip->aml_nandenv_info->init) { |
| pr_info("%s %d %s not init\n", |
| __func__, __LINE__, |
| aml_chip->aml_nandenv_info->name); |
| return 1; |
| } |
| /*fixit*/ |
| /* env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE); */ |
| if (aml_nand_save_rsv_info(mtd, aml_chip->aml_nandenv_info, buf)) |
| return 1; |
| return 0; |
| } |
| |
| int aml_nand_save_key(struct mtd_info *mtd, u_char *buf) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| /* env_t *env_ptr = (env_t *)buf; */ |
| |
| if (!aml_chip->aml_nandkey_info->init) { |
| pr_info("%s %d %s not init\n", |
| __func__, __LINE__, |
| aml_chip->aml_nandkey_info->name); |
| return 1; |
| } |
| /*fixit*/ |
| /* env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE); */ |
| if (aml_nand_save_rsv_info(mtd, aml_chip->aml_nandkey_info, buf)) |
| return 1; |
| return 0; |
| } |
| |
| int aml_nand_save_dtb(struct mtd_info *mtd, u_char *buf) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| /* env_t *env_ptr = (env_t *)buf; */ |
| |
| if (!aml_chip->aml_nanddtb_info->init) { |
| pr_info("%s %d %s not init\n", |
| __func__, __LINE__, |
| aml_chip->aml_nanddtb_info->name); |
| return 1; |
| } |
| /*fixit*/ |
| /* env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE); */ |
| if (aml_nand_save_rsv_info(mtd, aml_chip->aml_nanddtb_info, buf)) |
| return 1; |
| return 0; |
| } |
| |
| int aml_nand_save_bbt(struct mtd_info *mtd, u_char *buf) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| |
| if (!aml_chip->aml_nandbbt_info->init) { |
| pr_info("%s %d %s not init\n", |
| __func__, __LINE__, |
| aml_chip->aml_nandbbt_info->name); |
| return 1; |
| } |
| if (aml_nand_save_rsv_info(mtd, aml_chip->aml_nandbbt_info, buf)) |
| return 1; |
| return 0; |
| } |
| |
| int aml_nand_rsv_info_init(struct mtd_info *mtd) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| struct nand_chip *chip = mtd->priv; |
| unsigned int pages_per_blk_shift, bbt_start_block; |
| int phys_erase_shift, i; |
| |
| phys_erase_shift = fls(mtd->erasesize) - 1; |
| pages_per_blk_shift = (chip->phys_erase_shift - chip->page_shift); |
| |
| /*bootloader occupy 1024 pages*/ |
| bbt_start_block = BOOT_TOTAL_PAGES >> pages_per_blk_shift; |
| bbt_start_block += 4; /*gap occupy 4 blocks*/ |
| |
| aml_chip->rsv_data_buf = kzalloc(mtd->writesize, GFP_KERNEL); |
| if (aml_chip->rsv_data_buf == NULL) |
| return -ENOMEM; |
| |
| aml_chip->freeNodeBitmask = 0; |
| for (i = 0; i < RESERVED_BLOCK_NUM; i++) { |
| aml_chip->free_node[i] = |
| kzalloc(sizeof(struct free_node_t), GFP_KERNEL); |
| aml_chip->free_node[i]->index = i; |
| } |
| |
| /*bbt info init*/ |
| aml_chip->aml_nandbbt_info = |
| kzalloc(sizeof(struct aml_nandrsv_info_t), GFP_KERNEL); |
| if (aml_chip->aml_nandbbt_info == NULL) |
| return -ENOMEM; |
| |
| aml_chip->aml_nandbbt_info->mtd = mtd; |
| aml_chip->aml_nandbbt_info->valid_node = |
| kzalloc(sizeof(struct valid_node_t), GFP_KERNEL); |
| if (aml_chip->aml_nandbbt_info->valid_node == NULL) |
| return -ENOMEM; |
| |
| aml_chip->aml_nandbbt_info->valid_node->phy_blk_addr = -1; |
| aml_chip->aml_nandbbt_info->valid_node->status = 1; |
| aml_chip->aml_nandbbt_info->start_block = bbt_start_block; |
| aml_chip->aml_nandbbt_info->end_block = |
| aml_chip->aml_nandbbt_info->start_block + 4; |
| aml_chip->aml_nandbbt_info->size = mtd->size >> phys_erase_shift; |
| memcpy(aml_chip->aml_nandbbt_info->name, BBT_NAND_MAGIC, 4); |
| |
| /*block status*/ |
| aml_chip->block_status = |
| kzalloc((mtd->size >> phys_erase_shift), GFP_KERNEL); |
| if (aml_chip->block_status == NULL) { |
| pr_info("no memory for flash block status\n"); |
| return -ENOMEM; |
| } |
| memset(aml_chip->block_status, 0, (mtd->size >> phys_erase_shift)); |
| |
| /*env info init*/ |
| aml_chip->aml_nandenv_info = |
| kzalloc(sizeof(struct aml_nandrsv_info_t), GFP_KERNEL); |
| if (aml_chip->aml_nandenv_info == NULL) |
| return -ENOMEM; |
| |
| aml_chip->aml_nandenv_info->mtd = mtd; |
| aml_chip->aml_nandenv_info->valid_node = |
| kzalloc(sizeof(struct valid_node_t), GFP_KERNEL); |
| if (aml_chip->aml_nandenv_info->valid_node == NULL) |
| return -ENOMEM; |
| |
| aml_chip->aml_nandenv_info->valid_node->phy_blk_addr = -1; |
| aml_chip->aml_nandenv_info->valid_node->status = 1; |
| aml_chip->aml_nandenv_info->start_block = |
| aml_chip->aml_nandbbt_info->end_block; |
| aml_chip->aml_nandenv_info->end_block = |
| aml_chip->aml_nandbbt_info->end_block + 8; |
| aml_chip->aml_nandenv_info->size = CONFIG_ENV_SIZE; |
| memcpy(aml_chip->aml_nandenv_info->name, ENV_NAND_MAGIC, 4); |
| |
| aml_chip->aml_nandkey_info = |
| kzalloc(sizeof(struct aml_nandrsv_info_t), GFP_KERNEL); |
| if (aml_chip->aml_nandkey_info == NULL) |
| return -ENOMEM; |
| |
| /*key init*/ |
| aml_chip->aml_nandkey_info->mtd = mtd; |
| aml_chip->aml_nandkey_info->valid_node = |
| kzalloc(sizeof(struct valid_node_t), GFP_KERNEL); |
| if (aml_chip->aml_nandkey_info->valid_node == NULL) |
| return -ENOMEM; |
| |
| aml_chip->aml_nandkey_info->valid_node->phy_blk_addr = -1; |
| aml_chip->aml_nandkey_info->valid_node->status = 1; |
| aml_chip->aml_nandkey_info->start_block = |
| aml_chip->aml_nandenv_info->end_block; |
| aml_chip->aml_nandkey_info->end_block = |
| aml_chip->aml_nandenv_info->end_block + 8; |
| aml_chip->aml_nandkey_info->size = aml_chip->keysize; |
| memcpy(aml_chip->aml_nandkey_info->name, KEY_NAND_MAGIC, 4); |
| |
| aml_chip->aml_nanddtb_info = |
| kzalloc(sizeof(struct aml_nandrsv_info_t), GFP_KERNEL); |
| if (aml_chip->aml_nanddtb_info == NULL) |
| return -ENOMEM; |
| |
| /*key init*/ |
| aml_chip->aml_nanddtb_info->mtd = mtd; |
| aml_chip->aml_nanddtb_info->valid_node = |
| kzalloc(sizeof(struct valid_node_t), GFP_KERNEL); |
| if (aml_chip->aml_nanddtb_info->valid_node == NULL) |
| return -ENOMEM; |
| |
| aml_chip->aml_nanddtb_info->valid_node->phy_blk_addr = -1; |
| aml_chip->aml_nanddtb_info->valid_node->status = 1; |
| aml_chip->aml_nanddtb_info->start_block = |
| aml_chip->aml_nandkey_info->end_block; |
| aml_chip->aml_nanddtb_info->end_block = |
| aml_chip->aml_nandkey_info->end_block + 4; |
| aml_chip->aml_nanddtb_info->size = aml_chip->dtbsize; |
| memcpy(aml_chip->aml_nanddtb_info->name, DTB_NAND_MAGIC, 4); |
| |
| pr_info("bbt_start=%d env_start=%d key_start=%d dtb_start=%d\n", |
| aml_chip->aml_nandbbt_info->start_block, |
| aml_chip->aml_nandenv_info->start_block, |
| aml_chip->aml_nandkey_info->start_block, |
| aml_chip->aml_nanddtb_info->start_block); |
| |
| return 0; |
| } |
| |
| int aml_nand_scan_rsv_info(struct mtd_info *mtd, |
| struct aml_nandrsv_info_t *nandrsv_info) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| struct nand_chip *chip = &aml_chip->chip; |
| struct mtd_oob_ops aml_oob_ops; |
| struct oobinfo_t *oobinfo; |
| struct free_node_t *free_node, *tmp_node = NULL; |
| unsigned char oob_buf[sizeof(struct oobinfo_t)]; |
| loff_t offset; |
| unsigned char *data_buf, good_addr[256] = {0}; |
| int start_blk, max_scan_blk, i, k, scan_status = 0, env_status = 0; |
| int phys_erase_shift, pages_per_blk, page_num; |
| int error = 0, ret = 0; |
| |
| data_buf = aml_chip->rsv_data_buf; |
| /* |
| *good_addr = kzalloc(256, GFP_KERNEL); |
| *if (good_addr == NULL) |
| * return -ENOMEM; |
| *memset(good_addr, 0 , 256); |
| **/ |
| |
| oobinfo = (struct oobinfo_t *)oob_buf; |
| |
| max_scan_blk = nandrsv_info->end_block; |
| start_blk = nandrsv_info->start_block; |
| pr_info("%s: info size=0x%x max_scan_blk=%d, start_blk=%d\n", |
| nandrsv_info->name, nandrsv_info->size, |
| max_scan_blk, start_blk); |
| |
| do { |
| offset = mtd->erasesize; |
| offset *= start_blk; |
| scan_status = 0; |
| RE_RSV_INFO: |
| aml_oob_ops.mode = MTD_OPS_AUTO_OOB; |
| aml_oob_ops.len = mtd->writesize; |
| aml_oob_ops.ooblen = sizeof(struct oobinfo_t); |
| aml_oob_ops.ooboffs = 0;/*mtd->ecclayout->oobfree[0].offset;*/ |
| aml_oob_ops.datbuf = data_buf; |
| aml_oob_ops.oobbuf = oob_buf; |
| |
| memset((unsigned char *)aml_oob_ops.datbuf, |
| 0x0, mtd->writesize); |
| memset((unsigned char *)aml_oob_ops.oobbuf, |
| 0x0, aml_oob_ops.ooblen); |
| /* |
| *fixit, need or not; |
| *when list bad block, it will be check in save rsv info. |
| *when not,it is not list. |
| **/ |
| /* |
| *error = mtd->_block_isbad(mtd, offset); |
| *if (error == FACTORY_BAD_BLOCK_ERROR) { |
| * pr_info("%s:%d factory bad addr =%llx\n", |
| * __func__,__LINE__, |
| * (uint64_t)(offset >> phys_erase_shift)); |
| * goto SCAN_LOOP; |
| *} |
| **/ |
| error = mtd->_read_oob(mtd, offset, &aml_oob_ops); |
| if ((error != 0) && (error != -EUCLEAN)) { |
| pr_info("blk check good but read failed: %llx, %d\n", |
| (uint64_t)offset, error); |
| offset += nandrsv_info->size; |
| if ((scan_status++ > 6) || (!(offset % mtd->erasesize))) { |
| pr_info("ECC error, scan ONE block exit\n"); |
| scan_status = 0; |
| continue; |
| } |
| goto RE_RSV_INFO; |
| } |
| |
| /* pr_info("%s %d\n", __func__, __LINE__); */ |
| nandrsv_info->init = 1; |
| if (!memcmp(oobinfo->name, nandrsv_info->name, 4)) { |
| /* pr_info("%s %d\n", __func__, __LINE__); */ |
| nandrsv_info->valid = 1; |
| if (nandrsv_info->valid_node->phy_blk_addr >= 0) { |
| /* |
| *free_node = |
| *kzalloc(sizeof(struct free_node_t), GFP_KERNEL); |
| **/ |
| free_node = get_free_node(mtd); |
| if (free_node == NULL) |
| return -ENOMEM; |
| |
| free_node->dirty_flag = 1; |
| if (oobinfo->timestamp > nandrsv_info->valid_node->timestamp) { |
| free_node->phy_blk_addr = |
| nandrsv_info->valid_node->phy_blk_addr; |
| free_node->ec = |
| nandrsv_info->valid_node->ec; |
| nandrsv_info->valid_node->phy_blk_addr = |
| start_blk; |
| nandrsv_info->valid_node->phy_page_addr = 0; |
| nandrsv_info->valid_node->ec = oobinfo->ec; |
| nandrsv_info->valid_node->timestamp = |
| oobinfo->timestamp; |
| } else { |
| free_node->phy_blk_addr = start_blk; |
| free_node->ec = oobinfo->ec; |
| } |
| if (nandrsv_info->free_node == NULL) |
| nandrsv_info->free_node = free_node; |
| else { |
| tmp_node = nandrsv_info->free_node; |
| while (tmp_node->next != NULL) |
| tmp_node = tmp_node->next; |
| |
| tmp_node->next = free_node; |
| } |
| } else { |
| nandrsv_info->valid_node->phy_blk_addr = |
| start_blk; |
| nandrsv_info->valid_node->phy_page_addr = 0; |
| nandrsv_info->valid_node->ec = oobinfo->ec; |
| nandrsv_info->valid_node->timestamp = |
| oobinfo->timestamp; |
| } |
| } else { |
| /* |
| *free_node = |
| * kzalloc(sizeof(struct free_node_t), GFP_KERNEL); |
| **/ |
| free_node = get_free_node(mtd); |
| if (free_node == NULL) |
| return -ENOMEM; |
| free_node->phy_blk_addr = start_blk; |
| free_node->ec = oobinfo->ec; |
| if (nandrsv_info->free_node == NULL) |
| nandrsv_info->free_node = free_node; |
| else { |
| tmp_node = nandrsv_info->free_node; |
| while (tmp_node->next != NULL) |
| tmp_node = tmp_node->next; |
| |
| tmp_node->next = free_node; |
| } |
| } |
| /* SCAN_LOOP: */ |
| } while ((++start_blk) < max_scan_blk); |
| |
| pr_info("%s : phy_blk_addr=%d, ec=%d, phy_page_addr=%d, timestamp=%d\n", |
| nandrsv_info->name, |
| nandrsv_info->valid_node->phy_blk_addr, |
| nandrsv_info->valid_node->ec, |
| nandrsv_info->valid_node->phy_page_addr, |
| nandrsv_info->valid_node->timestamp); |
| pr_info("%s free list:\n", nandrsv_info->name); |
| tmp_node = nandrsv_info->free_node; |
| while (tmp_node != NULL) { |
| pr_info("blockN=%d, ec=%d, dirty_flag=%d\n", |
| tmp_node->phy_blk_addr, |
| tmp_node->ec, |
| tmp_node->dirty_flag); |
| tmp_node = tmp_node->next; |
| } |
| |
| /*second stage*/ |
| |
| phys_erase_shift = fls(mtd->erasesize) - 1; |
| pages_per_blk = (1 << (phys_erase_shift - chip->page_shift)); |
| page_num = nandrsv_info->size / mtd->writesize; |
| if (page_num == 0) |
| page_num++; |
| |
| pr_info("%s %d: page_num=%d\n", __func__, __LINE__, page_num); |
| |
| if (nandrsv_info->valid == 1) { |
| pr_info("%s %d\n", __func__, __LINE__); |
| aml_oob_ops.mode = MTD_OPS_AUTO_OOB; |
| aml_oob_ops.len = mtd->writesize; |
| aml_oob_ops.ooblen = sizeof(struct oobinfo_t); |
| aml_oob_ops.ooboffs = 0;/*mtd->ecclayout->oobfree[0].offset;*/ |
| aml_oob_ops.datbuf = data_buf; |
| aml_oob_ops.oobbuf = oob_buf; |
| |
| for (i = 0; i < pages_per_blk; i++) { |
| memset((unsigned char *)aml_oob_ops.datbuf, |
| 0x0, mtd->writesize); |
| memset((unsigned char *)aml_oob_ops.oobbuf, |
| 0x0, aml_oob_ops.ooblen); |
| |
| offset = nandrsv_info->valid_node->phy_blk_addr; |
| offset *= mtd->erasesize; |
| offset += i * mtd->writesize; |
| error = mtd->_read_oob(mtd, offset, &aml_oob_ops); |
| if ((error != 0) && (error != -EUCLEAN)) { |
| pr_info("blk good but read failed:%llx,%d\n", |
| (uint64_t)offset, error); |
| ret = -1; |
| continue; |
| } |
| |
| if (!memcmp(oobinfo->name, nandrsv_info->name, 4)) { |
| /* pr_info("%d page find info:%llx\n", */ |
| /* i, (uint64_t)offset); */ |
| good_addr[i] = 1; |
| nandrsv_info->valid_node->phy_page_addr = i; |
| } else |
| break; |
| } |
| } |
| |
| if ((mtd->writesize < nandrsv_info->size) |
| && (nandrsv_info->valid == 1)) { |
| i = nandrsv_info->valid_node->phy_page_addr; |
| if (((i + 1) % page_num) != 0) { |
| ret = -1; |
| pr_info("find %s incomplete\n", nandrsv_info->name); |
| } |
| if (ret == -1) { |
| for (i = 0; i < (pages_per_blk / page_num); i++) { |
| env_status = 0; |
| for (k = 0; k < page_num; k++) { |
| if (!good_addr[k + i * page_num]) { |
| env_status = 1; |
| break; |
| } |
| } |
| if (!env_status) { |
| pr_info("find %d page ok\n", |
| i*page_num); |
| nandrsv_info->valid_node->phy_page_addr = |
| k + i * page_num - 1; |
| ret = 0; |
| } |
| } |
| } |
| i = (nandrsv_info->size + mtd->writesize - 1) / mtd->writesize; |
| nandrsv_info->valid_node->phy_page_addr -= (i - 1); |
| } |
| |
| offset = nandrsv_info->valid_node->phy_blk_addr; |
| offset *= mtd->erasesize; |
| offset += nandrsv_info->valid_node->phy_page_addr * mtd->writesize; |
| pr_info("%s valid addr: %llx\n", nandrsv_info->name, (uint64_t)offset); |
| return ret; |
| } |
| |
| int aml_nand_env_check(struct mtd_info *mtd) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| int ret = 0; |
| |
| /* pr_info("%s %d\n", __func__, __LINE__); */ |
| |
| ret = aml_nand_scan_rsv_info(mtd, aml_chip->aml_nandenv_info); |
| if ((ret != 0) && ((ret != (-1)))) |
| pr_info("%s %d\n", __func__, __LINE__); |
| |
| if (aml_chip->aml_nandenv_info->valid == 0) |
| pr_info("%s %d NO env exist\n", __func__, __LINE__); |
| |
| return ret; |
| } |
| |
| int aml_nand_key_check(struct mtd_info *mtd) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| int ret = 0; |
| |
| ret = aml_nand_scan_rsv_info(mtd, aml_chip->aml_nandkey_info); |
| if ((ret != 0) && ((ret != (-1)))) |
| pr_info("%s %d\n", __func__, __LINE__); |
| |
| if (aml_chip->aml_nandkey_info->valid == 0) |
| pr_info("%s %d NO key exist\n", __func__, __LINE__); |
| |
| return ret; |
| } |
| |
| int aml_nand_dtb_check(struct mtd_info *mtd) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| int ret = 0; |
| |
| ret = aml_nand_scan_rsv_info(mtd, aml_chip->aml_nanddtb_info); |
| if ((ret != 0) && ((ret != (-1)))) |
| pr_info("%s %d\n", __func__, __LINE__); |
| |
| if (aml_chip->aml_nanddtb_info->valid == 0) |
| pr_info("%s %d NO dtb exist\n", __func__, __LINE__); |
| |
| return ret; |
| } |
| |
| int aml_nand_bbt_check(struct mtd_info *mtd) |
| { |
| struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); |
| int phys_erase_shift; |
| int ret = 0; |
| int8_t *buf = NULL; |
| |
| phys_erase_shift = fls(mtd->erasesize) - 1; |
| |
| ret = aml_nand_scan_rsv_info(mtd, aml_chip->aml_nandbbt_info); |
| if ((ret != 0) && ((ret != (-1)))) { |
| pr_info("%s %d\n", __func__, __LINE__); |
| return ret; |
| } |
| |
| buf = aml_chip->block_status; |
| if (aml_chip->aml_nandbbt_info->valid == 1) { |
| /*read bbt*/ |
| pr_info("%s %d bbt is valid, reading.\n", |
| __func__, __LINE__); |
| aml_nand_read_rsv_info(mtd, |
| aml_chip->aml_nandbbt_info, 0, (u_char *)buf); |
| ret = 0; |
| goto exit_error1; |
| } else { |
| pr_info("%s %d bbt is invalid, scanning.\n", |
| __func__, __LINE__); |
| /*no bbt haven't been found, abnormal or clean nand! rebuild*/ |
| aml_chip->nand_bbt_info = |
| kzalloc(sizeof(struct aml_nand_bbt_info), GFP_KERNEL); |
| if (!aml_chip->nand_bbt_info) { |
| ret = -ENOMEM; |
| goto exit_error; |
| } |
| memset(aml_chip->block_status, |
| 0, (mtd->size >> phys_erase_shift)); |
| aml_nand_scan_shipped_bbt(mtd); |
| aml_nand_save_bbt(mtd, (u_char *)buf); |
| } |
| exit_error: |
| kfree(aml_chip->nand_bbt_info); |
| exit_error1: |
| return ret; |
| } |
| |
| |
| int aml_nand_scan_bbt(struct mtd_info *mtd) |
| { |
| return 0; |
| } |
| |