// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include "../include/phynand.h" extern int nand_hardreset(struct amlnand_chip *aml_chip, u8 chipnr); extern int info_disprotect; void amlnf_hardreset(void) { struct amlnand_chip *aml_chip = aml_nand_chip; struct hw_controller *controller = NULL; int i=0; if (check_dev() == STORAGE_DEV_NAND) { controller = &(aml_chip->controller); for (i=0; i < controller->chip_num; i++) { /* per lun */ controller->select_chip(controller, i); nand_get_chip(aml_chip); (void) nand_hardreset(aml_chip, i); nand_release_chip(aml_chip); } } } void amlnf_dump_chipinfo(void) { struct amlnand_chip *aml_chip = aml_nand_chip; amlchip_dumpinfo(aml_chip); return; } /*in bytes*/ void amlnf_get_chip_size(u64 *size) { struct amlnand_chip *aml_chip = aml_nand_chip; struct hw_controller *controller = &(aml_chip->controller); struct nand_flash *flash = &(aml_chip->flash); u64 chipsize = 0; chipsize = ((u64)(flash->chipsize*controller->chip_num))<<20; *size = chipsize; return; } void amlnand_dump_page(struct amlnand_phydev *phydev) { struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv; struct phydev_ops *devops = &(phydev->ops); //struct hw_controller *controller = &(aml_chip->controller); //struct chip_operation *operation = &(aml_chip->operation); struct chip_ops_para *ops_para = &(aml_chip->ops_para); u64 dump_len = 0; u8 *tmp = NULL; int ret = 0; ret = phydev->read(phydev); if (ret < 0) { aml_nand_msg("amlnand_dump_page : read failed page_addr:%d", ops_para->page_addr); } aml_nand_msg("\n show the raw read BUF: "); tmp = devops->datbuf; dump_len = 512; while (dump_len--) { aml_nand_msg("\t%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15]); tmp += 16; } return; } int nand_read_ops(struct amlnand_phydev *phydev) { struct phydev_ops *devops = &(phydev->ops); u64 offset , read_len; u8 *buffer; int ret = 0; u64 nand_read_len = 0; offset = devops->addr; read_len = devops->len; buffer = devops->datbuf; aml_nand_dbg("offset =%llx", offset); aml_nand_dbg("write_len =%llx", read_len); if ((offset & (phydev->writesize - 1)) != 0 || (read_len & (phydev->writesize - 1)) != 0) { printf("Attempt to read non page aligned data\n"); return -NAND_READ_FAILED; } if ((offset + read_len) > phydev->size) { aml_nand_dbg("Attemp to read out side the dev area"); return -NAND_READ_FAILED; } memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = offset; devops->len = phydev->writesize; devops->datbuf = buffer; devops->oobbuf = NULL; devops->mode = NAND_HW_ECC; aml_nand_dbg("phydev->writesize= %x", phydev->writesize); do { if ((devops->addr % phydev->erasesize) == 0) { /* aml_nand_dbg("devops->addr = %llx",devops->addr); */ ret = phydev->block_isbad(phydev); if (ret > 0) { printf("\rSkipping bad block at %llx\n", devops->addr); devops->addr += phydev->erasesize; continue; } else if (ret < 0) { printf("\n:AMLNAND get bad block failed: ret=%d at addr=%llx\n", ret, devops->addr); return -1; } } ret = phydev->read(phydev); if (ret < 0) aml_nand_dbg("nand read failed at %llx", devops->addr); devops->addr += phydev->writesize; nand_read_len += phydev->writesize; //fix by liuxianjun devops->datbuf += phydev->writesize; } while (nand_read_len < ( read_len));//while (devops->addr < (offset + read_len)); return ret; } int nand_write_ops(struct amlnand_phydev *phydev) { struct phydev_ops *devops = &(phydev->ops); u64 offset , write_len; u8 *buffer; int ret = 0; u64 nand_write_len = 0; offset = devops->addr; write_len = devops->len; buffer = devops->datbuf; if ((offset & (phydev->writesize - 1)) != 0 || (write_len & (phydev->writesize - 1)) != 0) { printf("Attempt to write non page aligned data\n"); return -NAND_WRITE_FAILED; } if ((offset + write_len) > phydev->size) { aml_nand_msg("Attemp to write out side the dev area"); return -NAND_WRITE_FAILED; } memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = offset; devops->len = phydev->writesize; devops->datbuf = buffer; devops->oobbuf = NULL; devops->mode = NAND_HW_ECC; aml_nand_msg("phydev->writesize= %x", phydev->writesize); do { if ((devops->addr % phydev->erasesize) == 0) { aml_nand_dbg("devops->addr = %llx",devops->addr); ret = phydev->block_isbad(phydev); if (ret > 0) { printf("\rSkipping bad block at %llx\n", devops->addr); devops->addr += phydev->erasesize; continue; } else if (ret < 0) { printf("\n:AMLNAND get bad block failed: ret=%d at addr=%llx\n", ret, devops->addr); return -1; } } ret = phydev->write(phydev); if (ret < 0) aml_nand_dbg("nand write failed at %llx", devops->addr); devops->addr += phydev->writesize; nand_write_len += phydev->writesize; //fix bug by liuxianjun devops->datbuf += phydev->writesize; } while (nand_write_len < ( write_len)); //while (devops->addr < (offset + write_len)); return ret; } #if 1 static int erase_env_protect(struct amlnand_chip *aml_chip, int blk) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct read_retry_info *retry_info = &(controller->retry_info); struct nand_arg_info *shipped_bbtinfo = &aml_chip->shipped_bbtinfo; struct nand_arg_info *bbtinfo = &aml_chip->nand_bbtinfo; struct nand_arg_info *nand_key = &aml_chip->nand_key; struct nand_arg_info *nand_secure = &aml_chip->nand_secure; unsigned char phys_erase_shift; unsigned short start_blk, nand_boot; unsigned offset; int ret = 0; nand_boot = 1; if (nand_boot) offset = (1024 * flash->pagesize); else { offset = 0; } phys_erase_shift = ffs(flash->blocksize) - 1; start_blk = (int)(offset >> phys_erase_shift); blk -= (controller->chip_num - 1) * start_blk; if (!(info_disprotect & DISPROTECT_FBBT)) { /* do not erase fbbt whenever! */ if (((blk == shipped_bbtinfo->valid_blk_addr)) &&(shipped_bbtinfo->valid_blk_addr >= start_blk)){ aml_nand_msg("protect fbbt at blk %d",blk); ret = -1; }else if((blk == bbtinfo->valid_blk_addr) &&(bbtinfo->valid_blk_addr >= start_blk)){ aml_nand_msg("protect bbt at blk %d",blk); ret = -1; }else if(((blk == 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",blk); //ret = -1; aml_nand_msg("disprotect hynix retry at blk %d",blk); ret = 0; }else if((blk == nand_key->valid_blk_addr) &&(nand_key->valid_blk_addr >= start_blk) &&(!(info_disprotect & DISPROTECT_KEY))){ aml_nand_msg("protect nand_key info at blk %d",blk); ret = -1; }else if((blk == nand_secure->valid_blk_addr) &&(nand_secure->valid_blk_addr >= start_blk) &&(!(info_disprotect & DISPROTECT_SECURE))){ aml_nand_msg("protect nand_secure info at blk %d",blk); ret = -1; }else{ ret = 0; } } return ret; } #else static int erase_env_protect(struct amlnand_chip *aml_chip, int blk) { struct hw_controller *controller = &aml_chip->controller; struct nand_flash *flash = &aml_chip->flash; struct read_retry_info *retry_info = &(controller->retry_info); struct nand_arg_info *shipped_bbtinfo = &aml_chip->shipped_bbtinfo; struct nand_arg_info *nand_key = &aml_chip->nand_key; struct nand_arg_info *nand_secure = &aml_chip->nand_secure; u8 phys_erase_shift; u16 start_blk, nand_boot; u32 offset; int ret = 0; 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; start_blk = (int)(offset >> phys_erase_shift); /* decrease the uboot block; */ blk -= (controller->chip_num - 1) * start_blk; if (!aml_chip->fbbt_protect) { if (((blk == shipped_bbtinfo->valid_blk_addr)) && (shipped_bbtinfo->valid_blk_addr >= start_blk) && (!(aml_chip->protect & 1))) { aml_nand_msg("protect fbbt at blk %d", blk); ret = -1; } else if (((blk == retry_info->info_save_blk) && (retry_info->info_save_blk >= start_blk) && (flash->new_type) && (flash->new_type < 10))) { aml_nand_msg("protect hynix retry info at blk %d", blk); ret = -1; } else if ((blk == nand_key->valid_blk_addr) && (nand_key->valid_blk_addr >= start_blk) && (!(aml_chip->protect & (1<<1)))) { aml_nand_msg("protect nand_key info at blk %d", blk); ret = -1; } else if ((blk == nand_secure->valid_blk_addr) && (nand_secure->valid_blk_addr >= start_blk) && (!(aml_chip->protect & (1<<2)))) { aml_nand_msg("protect nand_secure info at blk %d", blk); ret = -1; } else ret = 0; } return ret; } #endif int amlnf_erase_ops(u64 off, u64 erase_len, u8 scrub_flag) { struct amlnand_chip *aml_chip = aml_nand_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 erase_shift, write_shift, pages_per_blk; int start_blk, total_blk, ret = 0; int percent = 0; int percent_complete = -1; int temp_value,last_reserve_blk; erase_shift = ffs(flash->blocksize) - 1; write_shift = ffs(flash->pagesize) - 1; start_blk = (int)(off >> erase_shift); total_blk = (int)(erase_len >> erase_shift); pages_per_blk = (1 << (erase_shift - write_shift)); if (!scrub_flag) { if (boot_device_flag == 1) { if (start_blk < ((1024 / pages_per_blk))) start_blk = ((1024 / pages_per_blk)); } } aml_nand_msg("%s:start_blk =%d,total_blk=%d", __func__, start_blk, total_blk); last_reserve_blk = get_last_reserve_block(aml_chip); for (; start_blk < total_blk; start_blk++) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); temp_value = start_blk - start_blk % controller->chip_num; temp_value /= controller->chip_num; ops_para->page_addr = temp_value * pages_per_blk; ops_para->chipnr = start_blk % controller->chip_num; /* printk("%04d, ", start_blk); */ controller->select_chip(controller, ops_para->chipnr); ret = erase_env_protect(aml_chip, start_blk); if (ret < 0) continue; if (!scrub_flag) { ret = operation->block_isbad(aml_chip); if (ret) { if (start_blk < last_reserve_blk \ && bad_block_is_dtb_blk(start_blk)) { aml_nand_msg("erase bad dtb block:0x%x",start_blk); } else { //aml_nand_msg("Skiping block:0x%x!!!", start_blk); continue; } } } nand_get_chip(aml_chip); ret = operation->erase_block(aml_chip); nand_release_chip(aml_chip); if (ret < 0) { ret = operation->block_markbad(aml_chip); continue; } 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); } } printk("\n"); return 0; } int dbg_amlnf_erase_ops(u64 off, u64 erase_len, u8 scrub_flag) { struct amlnand_chip *aml_chip = aml_nand_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 erase_shift, write_shift, pages_per_blk; int start_blk, total_blk, ret = 0; int percent = 0; int percent_complete = -1; int temp_value; erase_shift = ffs(flash->blocksize) - 1; write_shift = ffs(flash->pagesize) - 1; start_blk = (int)(off >> erase_shift); total_blk = (int)(erase_len >> erase_shift); pages_per_blk = (1 << (erase_shift - write_shift)); if (!scrub_flag) { if (boot_device_flag == 1) { if (start_blk < ((1024 / pages_per_blk))) start_blk = ((1024 / pages_per_blk)); } } aml_nand_dbg("start_blk =%d,total_blk=%d", start_blk, total_blk); for (; start_blk < total_blk; start_blk++) { memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para)); temp_value = start_blk - start_blk % controller->chip_num; temp_value /= controller->chip_num; ops_para->page_addr = temp_value * pages_per_blk; ops_para->chipnr = start_blk % controller->chip_num; printk("%04d, ", start_blk); controller->select_chip(controller, ops_para->chipnr); #if 0 //fixme, dbg code..... ret = erase_env_protect(aml_chip, start_blk); if (ret < 0) continue; if (!scrub_flag) { ret = operation->block_isbad(aml_chip); if (ret) continue; } #endif //0 nand_get_chip(aml_chip); ret = operation->erase_block(aml_chip); nand_release_chip(aml_chip); #if 0 //dbg code. if (ret < 0) { ret = operation->block_markbad(aml_chip); continue; } #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); } } printk("\n"); return ret; } int amlnf_markbad_reserved_ops(uint32_t start_blk) { struct amlnand_chip *aml_chip = aml_nand_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); unsigned erase_shift, write_shift, pages_per_blk; int ret = 0; erase_shift = ffs(flash->blocksize) - 1; write_shift = ffs(flash->pagesize) - 1; pages_per_blk = (1 << (erase_shift -write_shift)); 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 ); nand_get_chip(aml_chip); ret = operation->block_markbad(aml_chip); nand_release_chip(aml_chip); aml_nand_msg("ops_para->page_addr=%d, ret %d\n",ops_para->page_addr, ret); return 0; }