// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include "../include/phynand.h" #ifdef AML_NAND_UBOOT //extern struct amlnf_partition amlnf_partitions; extern struct amlnf_partition * amlnf_partitions; #endif #define DBG_WRITE_VERIFY (0) #if (DBG_WRITE_VERIFY) u8 * glb_verify_buffer = NULL; #endif enum chip_state_t get_chip_state(struct amlnand_chip *aml_chip) { return aml_chip->state; } void set_chip_state(struct amlnand_chip *aml_chip, enum chip_state_t state) { aml_chip->state = state; } static u32 amlnand_slc_addr_trs(struct amlnand_phydev *phydev) { struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv; struct nand_flash *flash = &(aml_chip->flash); struct phydev_ops *devops = &(phydev->ops); struct hw_controller *controller = &(aml_chip->controller); struct en_slc_info *slc_info = &(controller->slc_info); int real_page_per_blk, page_per_blk, blk_num, blk_num_in_dev, page_num; /* u64 addr, readlen = 0; */ u32 page_addr = 0; u32 real_erase_size, real_erase_shift, w_size_shift, e_size_shift; int total_pages_in_dev; real_erase_size = (phydev->erasesize << 1); real_erase_shift = ffs(real_erase_size) - 1; w_size_shift = phydev->writesize_shift; e_size_shift = phydev->erasesize_shift; real_page_per_blk = (1 << ((ffs(real_erase_size) - 1) - w_size_shift)); page_per_blk = (1 << (e_size_shift - w_size_shift)); blk_num = phydev->offset >> real_erase_shift; blk_num_in_dev = devops->addr >> e_size_shift; blk_num += blk_num_in_dev; total_pages_in_dev = blk_num_in_dev * page_per_blk; page_num = ((devops->addr >> w_size_shift) - total_pages_in_dev); if ((flash->new_type > 0) && (flash->new_type < 10)) page_addr = blk_num * real_page_per_blk + slc_info->pagelist[page_num]; else if (flash->new_type == SANDISK_19NM) page_addr = blk_num * real_page_per_blk + (page_num << 1); else { /* not support slc nand */ page_addr = (int)((phydev->offset + devops->addr) >> w_size_shift); aml_nand_msg("nand not support slc "); } #if 0 aml_nand_dbg(" devops->addr =%llx %d", devops->addr, devops->addr); aml_nand_dbg("real_erase_size =%x", real_erase_size); aml_nand_dbg("real_page_per_blk =%d", real_page_per_blk); aml_nand_dbg("page_per_blk =%d", page_per_blk); aml_nand_dbg("blk_num =%d", blk_num); aml_nand_dbg("blk_num_in_dev =%d", blk_num_in_dev); aml_nand_dbg("page_num =%d", page_num); aml_nand_dbg("page_addr =%d", page_addr); #endif return page_addr; } /* * fixme, pls use DBG_WRITE_VERIFY instead(20150918) before fix! yyh. */ static void nand_write_verify(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); int ret = 0; u8 *verify_buf = NULL; verify_buf = aml_nand_malloc(2 * phydev->writesize); if (!verify_buf) { aml_nand_msg("malloc failed for nand_read_verify"); return; } ops_para->data_buf = verify_buf; ret = operation->read_page(aml_chip); if ((ops_para->ecc_err) || (ret < 0)) aml_nand_msg("nand phy read failed at devops->addr : %llx", devops->addr); if (memcmp(verify_buf, devops->datbuf, phydev->writesize)) aml_nand_msg("nand write verify failed"); kfree(verify_buf); return; } /* *read data case: *only data read function, if none ecc mode, buf should be data+oob * operation type as below: oob_mode data_buf oob_buf ooblen *1) read oob hw ecc mode 0 NULL available available *2) read data and oob hw ecc mode 0 available available available *3) read data and oob sw ecc mode 1 available available 0 */ static int nand_read(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 addr, readlen = 0, len = 0; int ret = 0; if ((devops->addr + devops->len) > phydev->size) { aml_nand_msg("out of space and addr:"); aml_nand_msg("%llx len:%llx pdev->offset:%llx pdev->size:%llx", devops->addr, devops->len, phydev->offset, phydev->size); return -NAND_ARGUMENT_FAILURE; } if ((devops->len == 0) && (devops->ooblen == 0)) { aml_nand_msg("len equal zero here"); return NAND_SUCCESS; } amlnand_get_device(aml_chip, CHIP_READING); /* clear ops_para here */ memset(ops_para, 0, sizeof(struct chip_ops_para)); if (devops->len == 0) { len = phydev->writesize; ops_para->ooblen = devops->ooblen; } else { len = devops->len; ops_para->ooblen = devops->ooblen; } addr = phydev->offset + devops->addr; ops_para->data_buf = devops->datbuf; ops_para->option = phydev->option; ops_para->oob_buf = devops->oobbuf; /* aml_nand_dbg("len =%llx",len); */ if (devops->mode == NAND_SOFT_ECC) ops_para->option |= DEV_ECC_SOFT_MODE; while (1) { if (ops_para->option & DEV_SERIAL_CHIP_MODE) { ops_para->chipnr = (addr >> phydev->erasesize) % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); aml_nand_dbg("ops_para->chipnr =%d", ops_para->chipnr); aml_nand_dbg("DEV_SERIAL_CHIP_MODE"); } if (ops_para->option & DEV_SLC_MODE) ops_para->page_addr = amlnand_slc_addr_trs(phydev); else ops_para->page_addr = (int)(addr >> phydev->writesize_shift); //aml_nand_msg("%s() page %x\n", __func__, ops_para->page_addr); ret = operation->read_page(aml_chip); if ((ops_para->ecc_err) || (ret < 0)) { aml_nand_msg("phy read failed at devops->addr: %llx", devops->addr); break; } addr += phydev->writesize; ops_para->data_buf += phydev->writesize; readlen += phydev->writesize; /* aml_nand_dbg("readlen =%llx",readlen); */ if (readlen >= len) break; } devops->retlen = readlen; amlnand_release_device(aml_chip); if (!ret) { if (ops_para->ecc_err) ret = NAND_ECC_FAILURE; else if (ops_para->bit_flip) ret = -EUCLEAN; /* 117 */ } return ret; } #if (DBG_WRITE_VERIFY) static int _nand_write(struct amlnand_phydev *phydev) #else static int nand_write(struct amlnand_phydev *phydev) #endif { 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 addr, writelen = 0, len = 0; int ret = 0; PHYDEV_LINE if ((devops->addr + devops->len) > phydev->size) { aml_nand_msg("out of space and addr:"); aml_nand_msg("%llx len:%llx pdev->offset:%llx pdev->size:%llx", devops->addr, devops->len, phydev->offset, phydev->size); return -NAND_ARGUMENT_FAILURE; } if ((devops->len == 0) && (devops->ooblen == 0)) { aml_nand_msg("len equal zero here"); return NAND_SUCCESS; } #ifndef AML_NAND_UBOOT if (phydev->option & NAND_SHUT_DOWN) { aml_nand_msg("nand is in shut down protect mode"); return NAND_SUCCESS; } #endif amlnand_get_device(aml_chip, CHIP_WRITING); len = devops->len; /* clear ops_para here */ memset(ops_para, 0, sizeof(struct chip_ops_para)); addr = phydev->offset + devops->addr; ops_para->option = phydev->option; //multi-plane is set ops_para->data_buf = devops->datbuf; ops_para->oob_buf = devops->oobbuf; ops_para->ooblen = devops->ooblen; if (devops->mode == NAND_SOFT_ECC) ops_para->option |= DEV_ECC_SOFT_MODE; while (1) { if (ops_para->option & DEV_SERIAL_CHIP_MODE) { ops_para->chipnr = (addr>>phydev->erasesize_shift) % controller->chip_num; controller->select_chip(controller, ops_para->chipnr); aml_nand_dbg("DEV_SERIAL_CHIP_MODE"); } if (ops_para->option & DEV_SLC_MODE) ops_para->page_addr = amlnand_slc_addr_trs(phydev); else ops_para->page_addr = (int)(addr >> phydev->writesize_shift); //aml_nand_msg("%s() page %x\n", __func__, ops_para->page_addr); ret = operation->write_page(aml_chip); if (ret < 0) { aml_nand_msg("Write fail devops->addr:%llx,addr:%llx", devops->addr, addr); break; } addr += phydev->writesize; ops_para->data_buf += phydev->writesize; writelen += phydev->writesize; if (writelen >= len) break; } devops->retlen = writelen; if (aml_chip->debug_flag & NAND_WRITE_VERIFY) nand_write_verify(phydev); amlnand_release_device(aml_chip); return ret; } #if (DBG_WRITE_VERIFY) static int nand_write(struct amlnand_phydev *phydev) { int ret; u8 * tmp; struct phydev_ops *devops = &(phydev->ops); ret = _nand_write(phydev); tmp = devops->datbuf; devops->datbuf = glb_verify_buffer; nand_read(phydev); devops->datbuf = tmp; printf("."); if (memcmp(tmp, glb_verify_buffer, phydev->writesize)) { aml_nand_msg("nand write verify failed"); while (1) ; } return ret; } #endif int nand_erase(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 addr = 0, eraselen = 0; int ret = 0; if ((devops->addr + devops->len) > phydev->size) { aml_nand_msg("out of space and addr:"); aml_nand_msg("%llx len:%llx pdev->offset:%llx pdev->size:%llx", devops->addr, devops->len, phydev->offset, phydev->size); return -NAND_ARGUMENT_FAILURE; } if (devops->len == 0) { aml_nand_msg("len equal zero here"); return NAND_SUCCESS; } if (phydev->option & NAND_SHUT_DOWN) { aml_nand_msg("nand is in shut down protect mod"); return NAND_SUCCESS; } amlnand_get_device(aml_chip, CHIP_ERASING); /* clear ops_para here */ memset(ops_para, 0, sizeof(struct chip_ops_para)); addr = phydev->offset + devops->addr; ops_para->option = phydev->option; while (1) { if (ops_para->option & DEV_SLC_MODE) ops_para->page_addr = amlnand_slc_addr_trs(phydev); else ops_para->page_addr = (int)(addr >> phydev->writesize_shift); if (ops_para->option & DEV_SERIAL_CHIP_MODE) { ops_para->chipnr = (addr>>phydev->erasesize)%controller->chip_num; controller->select_chip(controller, ops_para->chipnr); } ret = operation->erase_block(aml_chip); if (ret < 0) { aml_nand_msg("nand erase fail at addr :%x ", ops_para->page_addr); break; } addr += phydev->erasesize; eraselen += phydev->erasesize; if (eraselen >= devops->len) break; } devops->retlen = eraselen; amlnand_release_device(aml_chip); return ret; } static int nand_block_isbad(struct amlnand_phydev *phydev) { struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv; /* struct nand_flash *flash = &(aml_chip->flash); */ 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 addr = 0; int ret = 0; if (devops->addr > phydev->size) { aml_nand_msg("out of space and addr:"); aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx", devops->addr, phydev->offset, phydev->size); return -NAND_ARGUMENT_FAILURE; } #ifndef AML_NAND_UBOOT if (phydev->option & NAND_SHUT_DOWN) { aml_nand_msg("nand is in shut down protect mod"); return NAND_SUCCESS; } #endif amlnand_get_device(aml_chip, CHIP_READING); /* clear ops_para here */ memset(ops_para, 0, sizeof(struct chip_ops_para)); addr = phydev->offset + devops->addr; ops_para->option = phydev->option; if (ops_para->option & DEV_SLC_MODE) ops_para->page_addr = amlnand_slc_addr_trs(phydev); else ops_para->page_addr = (int)(addr >> phydev->writesize_shift); if ((ops_para->option & DEV_SERIAL_CHIP_MODE)) ops_para->chipnr = (addr >> phydev->erasesize_shift) % controller->chip_num; ret = operation->block_isbad(aml_chip); if (ret < 0) aml_nand_msg("fail page_addr:ret=%d, %x len:%llx", ret, ops_para->page_addr, devops->len); amlnand_release_device(aml_chip); return ret; } static int nand_block_markbad(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 addr = 0; int ret = 0; if (devops->addr > phydev->size) { aml_nand_msg("out of space and addr:"); aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx", devops->addr, phydev->offset, phydev->size); return -NAND_ARGUMENT_FAILURE; } #ifndef AML_NAND_UBOOT if (phydev->option & NAND_SHUT_DOWN) { aml_nand_msg("nand is in shut down protect mod"); return NAND_SUCCESS; } #endif amlnand_get_device(aml_chip, CHIP_READING); /* clear ops_para here */ memset(ops_para, 0, sizeof(struct chip_ops_para)); addr = phydev->offset + devops->addr; ops_para->option = phydev->option; /* ops_para->page_addr = (int)(addr >> phydev->writesize_shift); */ if (ops_para->option & DEV_SLC_MODE) ops_para->page_addr = amlnand_slc_addr_trs(phydev); else ops_para->page_addr = (int)(addr >> phydev->writesize_shift); if (ops_para->option & DEV_SERIAL_CHIP_MODE) { ops_para->chipnr = (addr>>phydev->erasesize)%controller->chip_num; controller->select_chip(controller, ops_para->chipnr); } ret = operation->block_markbad(aml_chip); if (ret < 0) aml_nand_msg("nand mark bad failed at page %d", ops_para->page_addr); amlnand_release_device(aml_chip); return ret; } static int block_modifybbt(struct amlnand_phydev *phydev, int value) { 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 addr = 0; int ret = 0; if (devops->addr > phydev->size) { aml_nand_msg("out of space and addr:"); aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx", devops->addr, phydev->offset, phydev->size); return -NAND_ARGUMENT_FAILURE; } amlnand_get_device(aml_chip, CHIP_READING); /* clear ops_para here */ memset(ops_para, 0, sizeof(struct chip_ops_para)); addr = phydev->offset + devops->addr; aml_nand_msg("addr = %lld", addr); ops_para->option = phydev->option; /* ops_para->page_addr = (int)(addr >> phydev->writesize_shift); */ if (ops_para->option & DEV_SLC_MODE) ops_para->page_addr = amlnand_slc_addr_trs(phydev); else ops_para->page_addr = (int)(addr >> phydev->writesize_shift); if (ops_para->option & DEV_SERIAL_CHIP_MODE) { ops_para->chipnr = (addr>>phydev->erasesize)%controller->chip_num; controller->select_chip(controller, ops_para->chipnr); } ret = operation->blk_modify_bbt_chip_op(aml_chip, value); if (ret < 0) aml_nand_msg("nand mark bad failed at page %d", ops_para->page_addr); amlnand_release_device(aml_chip); return ret; } static int update_bbt(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 addr = 0; int ret = 0; if (devops->addr > phydev->size) { aml_nand_msg("out of space and addr:"); aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx", devops->addr, phydev->offset, phydev->size); return -NAND_ARGUMENT_FAILURE; } amlnand_get_device(aml_chip, CHIP_READING); /* clear ops_para here */ memset(ops_para, 0, sizeof(struct chip_ops_para)); addr = phydev->offset + devops->addr; ops_para->option = phydev->option; /* ops_para->page_addr = (int)(addr >> phydev->writesize_shift); */ if (ops_para->option & DEV_SLC_MODE) ops_para->page_addr = amlnand_slc_addr_trs(phydev); else ops_para->page_addr = (int)(addr >> phydev->writesize_shift); if (ops_para->option & DEV_SERIAL_CHIP_MODE) { ops_para->chipnr = (addr>>phydev->erasesize)%controller->chip_num; controller->select_chip(controller, ops_para->chipnr); } ret = operation->update_bbt_chip_op(aml_chip); if (ret < 0) aml_nand_msg("nand mark bad failed at page %d", ops_para->page_addr); amlnand_release_device(aml_chip); return ret; } static int nand_test_block(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 addr = 0; int ret = 0; u32 tmp_addr = 0; if (devops->addr > phydev->size) { aml_nand_msg("out of space and addr:"); aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx", devops->addr, phydev->offset, phydev->size); return -NAND_ARGUMENT_FAILURE; } if (phydev->option & NAND_SHUT_DOWN) { ml_nand_msg("nand is in shut down protect mod"); return NAND_SUCCESS; } amlnand_get_device(aml_chip, CHIP_READING); /* clear ops_para here */ memset(ops_para, 0, sizeof(struct chip_ops_para)); addr = phydev->offset + devops->addr; aml_nand_msg("addr = %lld", addr); ops_para->option = phydev->option; /* ops_para->page_addr = (int)(addr >> phydev->writesize_shift); */ if (ops_para->option & DEV_SLC_MODE) ops_para->page_addr = amlnand_slc_addr_trs(phydev); else ops_para->page_addr = (int)(addr >> phydev->writesize_shift); if (ops_para->option & DEV_SERIAL_CHIP_MODE) { ops_para->chipnr = (addr>>phydev->erasesize)%controller->chip_num; controller->select_chip(controller, ops_para->chipnr); } tmp_addr = ops_para->page_addr; ops_para->data_buf = devops->datbuf; ret = operation->erase_block(aml_chip); if (ret < 0) { aml_nand_msg("nand erase_block failed at page %d", ops_para->page_addr); goto exit; } ops_para->page_addr = tmp_addr; do { ret = operation->write_page(aml_chip); if (ret < 0) { aml_nand_msg("nand erase_block failed at page %d", ops_para->page_addr); goto exit; } ops_para->page_addr += phydev->writesize; } while (ops_para->page_addr >= devops->len); ops_para->page_addr = tmp_addr; do { ret = operation->read_page(aml_chip); if ((ops_para->ecc_err) || (ret < 0)) { aml_nand_msg("phy read failed at devops->addr: %llx", devops->addr); goto exit; } ops_para->page_addr += phydev->writesize; } while (ops_para->page_addr >= devops->len); ops_para->page_addr = tmp_addr; ret = operation->erase_block(aml_chip); if (ret < 0) { aml_nand_msg("nand erase_block failed at page %d", ops_para->page_addr); goto exit; } aml_nand_msg("nand test OK "); exit: amlnand_release_device(aml_chip); return ret; } int phydev_init_erase(struct amlnand_chip *aml_chip) { int ret =0, percent=0,percent_complete = -1; //uint64_t addr, off, size, chipsize, erase_addr, erase_len, erase_off; uint64_t erase_addr, erase_len, erase_off; struct amlnand_phydev *phydev = NULL; struct phydev_ops *devops = NULL; if (aml_chip->init_flag == NAND_BOOT_ERASE_PROTECT_CACHE) { list_for_each_entry(phydev,&nphy_dev_list,list){ if ((!strncmp((char*)phydev->name, NAND_CODE_NAME, strlen((const char*)NAND_CODE_NAME))) \ ||(!strncmp((char*)phydev->name, NAND_DATA_NAME, strlen((const char*)NAND_DATA_NAME)))){ devops = &phydev->ops; erase_addr = 0; erase_off = erase_addr; erase_len = phydev->size; percent=0;percent_complete = -1; for (; erase_addr erasesize) { memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = erase_addr; devops->len = phydev->erasesize; devops->mode = NAND_HW_ECC; ret = phydev->block_isbad(phydev); if (ret > 0) { printf("\rSkipping bad block at 0x%08llx\n", erase_addr); continue; } else if (ret < 0) { printf("\n:AMLNAND get bad block failed: ret=%d at addr=%llx\n",ret, erase_addr); return -1; } ret = nand_erase(phydev); if (ret < 0) { printf("\nAMLNAND Erase failure: %d %llx\n", ret, erase_addr); ret = phydev->block_markbad(phydev); if (ret < 0) printf("AMLNAND bad block mark failed: %llx\n", erase_addr); continue; } percent = (erase_addr * 100) / (erase_off + erase_len); if ((percent != percent_complete) && ((percent %10) == 0)) { percent_complete = percent; aml_nand_msg("nand erasing %d %% --%d %% complete",percent,percent+10); } } } } } return ret; } void amldev_dumpinfo(struct amlnand_phydev *phydev) { /* flash info */ aml_nand_msg("device info"); aml_nand_msg("name:%s, offset:%llx, size:%llx, option:%x", phydev->name, phydev->offset, phydev->size, phydev->option); aml_nand_msg("esize:%x,wsize:%x,oob:%x,esize_shift:%x,wsize_shift:%d", phydev->erasesize, phydev->writesize, phydev->oobavail, phydev->erasesize_shift, phydev->writesize_shift); } #ifndef AML_NAND_UBOOT ssize_t nand_page_read(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { struct amlnand_phydev *phydev = NULL; struct phydev_ops *devops = NULL; u8 *datbuf, *p; u64 offset , write_len; loff_t off; size_t ret; int i; phydev = container_of(class, struct amlnand_phydev, cls); devops = &(phydev->ops); ret = sscanf(buf, "%llx", &off); datbuf = kmalloc(2*phydev->writesize, GFP_KERNEL); if (!datbuf) { aml_nand_msg("No memory for page buffer"); goto exit_erro; } p = datbuf; memset(datbuf, 0x0, 2*phydev->writesize); aml_nand_dbg("phydev->name =%s", phydev->name); aml_nand_dbg("read page"); offset = 0; write_len = phydev->writesize; memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = offset; devops->len = phydev->writesize; devops->datbuf = datbuf; devops->oobbuf = NULL; devops->mode = NAND_HW_ECC; do { if ((((u32)devops->addr % phydev->erasesize)) == 0) { aml_nand_dbg("devops->addr = %llx", devops->addr); ret = phydev->block_isbad(phydev); if (ret > 0) { aml_nand_dbg("\rSkipping bad block at %llx\n", devops->addr); devops->addr += phydev->erasesize; continue; } else if (ret < 0) { aml_nand_dbg("get bad blk fail:"); aml_nand_dbg("ret=%d 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; datbuf += phydev->writesize; } while (devops->addr < (offset + write_len)); i = 512; aml_nand_dbg("read page"); while (i--) { aml_nand_msg("\t%02x %02x %02x %02x %02x %02x %02x %02x", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); aml_nand_msg("\t%02x %02x %02x %02x %02x %02x %02x %02x", p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); p += 16; } aml_nand_dbg("read page complete"); exit_erro: kfree(datbuf); return count; } void amlchip_resume(struct amlnand_phydev *phydev) { struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv; struct hw_controller *controller = &(aml_chip->controller); struct chip_operation *operation = &(aml_chip->operation); u8 onfi_features[4] = {0}; int i = 0, ret = 0; if (!strncmp((char *)phydev->name, NAND_CODE_NAME, strlen((const char *)NAND_CODE_NAME))) { nand_get_chip(aml_chip); for (i = 0; i < controller->chip_num; i++) { ret = controller->select_chip(controller, i); if (ret < 0) aml_nand_msg("select chip %d failed", i); ret = nand_reset(aml_chip, i); if (ret < 0) aml_nand_dbg("reset failed %d", i); } if (controller->onfi_mode) { operation->set_onfi_para(aml_chip, (u8 *)&(controller->onfi_mode), ONFI_TIMING_ADDR); operation->get_onfi_para(aml_chip, onfi_features, ONFI_TIMING_ADDR); if (onfi_features[0] != controller->onfi_mode) { aml_chip->flash.T_REA = DEFAULT_T_REA; aml_chip->flash.T_RHOH = DEFAULT_T_RHOH; } } /* if (aml_chip->state == CHIP_PM_SUSPENDED) */ amlnand_release_device(aml_chip); aml_nand_dbg("nand resume entered\n"); } return; } ssize_t nand_page_write(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { struct amlnand_phydev *phydev = NULL; struct phydev_ops *devops = NULL; u_char *datbuf; u64 offset , write_len; loff_t off; size_t ret; phydev = container_of(class, struct amlnand_phydev, cls); devops = &(phydev->ops); aml_nand_dbg("phydev->name =%s", phydev->name); aml_nand_dbg("write page"); ret = sscanf(buf, "%llx", &off); datbuf = kmalloc(2*phydev->writesize, GFP_KERNEL); if (!datbuf) { aml_nand_msg("No memory for page buffer"); goto exit_erro; } memset(datbuf, 0xa5, 2*phydev->writesize); offset = 0; write_len = phydev->writesize; memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = offset; devops->len = phydev->writesize; devops->datbuf = datbuf; devops->oobbuf = NULL; devops->mode = NAND_HW_ECC; do { if ((((u32)devops->addr % phydev->erasesize)) == 0) { aml_nand_dbg("devops->addr = %llx", devops->addr); ret = phydev->block_isbad(phydev); if (ret > 0) { aml_nand_msg("\rSkipping bad block at %llx\n", devops->addr); devops->addr += phydev->erasesize; continue; } else if (ret < 0) { aml_nand_msg("get bad blk fail:"); aml_nand_msg("ret=%d addr=%llx\n", ret, devops->addr); return -1; } } ret = phydev->write(phydev); if (ret < 0) aml_nand_msg("nand read failed at %llx", devops->addr); devops->addr += phydev->writesize; datbuf += phydev->writesize; } while (devops->addr < (offset + write_len)); aml_nand_dbg("write page complete"); exit_erro: kfree(datbuf); return count; } /**** *verify_nand_page: *read data immediately when write data, then compare; * *enbale this function: set the first bit of aml_chip->debug_flag to 1; *****/ ssize_t verify_nand_page(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { struct amlnand_phydev *phydev = NULL; struct amlnand_chip *aml_chip = NULL; u8 off; size_t ret; phydev = container_of(class, struct amlnand_phydev, cls); aml_chip = (struct amlnand_chip *)phydev->priv; ret = sscanf(buf, "%d", (int *)&off); aml_chip->debug_flag = off; aml_nand_msg("nand set aml_chip->debug_flag to %d", aml_chip->debug_flag); return count; } ssize_t dump_nand_page(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { struct amlnand_phydev *phydev = NULL; struct phydev_ops *devops = NULL; u8 *datbuf, *p; u64 offset , write_len; u64 off; size_t ret; int i; phydev = container_of(class, struct amlnand_phydev, cls); devops = &(phydev->ops); ret = sscanf(buf, "%llx", &off); aml_nand_msg("dump page %llx", off); datbuf = kmalloc(2*phydev->writesize, GFP_KERNEL); if (!datbuf) { aml_nand_msg("No memory for page buffer"); goto exit_erro; } p = datbuf; memset(datbuf, 0x0, 2*phydev->writesize); aml_nand_msg("phydev->name =%s", phydev->name); offset = off; write_len = phydev->writesize; aml_nand_msg("offset %llx", offset); memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = offset; devops->len = phydev->writesize; devops->datbuf = datbuf; devops->oobbuf = NULL; devops->mode = NAND_SOFT_ECC; do { if ((((u32)devops->addr % phydev->erasesize)) == 0) { aml_nand_dbg("devops->addr = %llx", devops->addr); ret = phydev->block_isbad(phydev); if (ret > 0) { aml_nand_dbg("\rSkipping bad block at %llx\n", devops->addr); devops->addr += phydev->erasesize; continue; } else if (ret < 0) { aml_nand_dbg("get bad blk fail:"); aml_nand_dbg("ret=%d 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; datbuf += phydev->writesize; } while (devops->addr < (offset + write_len)); i = 512; aml_nand_dbg("read page"); while (i--) { aml_nand_msg("\t%02x %02x %02x %02x %02x %02x %02x %02x", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); aml_nand_msg("\t%02x %02x %02x %02x %02x %02x %02x %02x", p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); p += 16; } aml_nand_dbg("read page complete"); exit_erro: kfree(datbuf); return count; } ssize_t show_nand_info(struct class *class, struct class_attribute *attr, char *buf) { struct amlnand_phydev *phydev = NULL; aml_nand_dbg("phydev info:"); phydev = container_of(class, struct amlnand_phydev, cls); amldev_dumpinfo(phydev); return 0; } ssize_t show_bbt_table(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { struct amlnand_phydev *phydev = NULL; struct amlnand_chip *aml_chip = NULL; struct hw_controller *controller = NULL; u16 *tmp_arr; int start_block, chipnr, total_block; int ret; phydev = container_of(class, struct amlnand_phydev, cls); aml_chip = (struct amlnand_chip *)phydev->priv; controller = &(aml_chip->controller); aml_nand_msg("show the block status !!!!"); ret = sscanf(buf, "%d", &total_block); if (ret != 1) return -EINVAL; aml_nand_msg("set total_block %d", total_block); for (chipnr = 0; chipnr < controller->chip_num; chipnr++) { tmp_arr = &aml_chip->block_status->blk_status[chipnr][0]; for (start_block = total_block; start_block < (total_block + 50); start_block++) aml_nand_msg(" aml_chip->block_status[%d][%d]=%d", chipnr, start_block, tmp_arr[start_block]); } return count; } ssize_t change_test_sync_flag(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int num; int ret; ret = sscanf(buf, "%x", &num); if (ret != 1) return -EINVAL; aml_nand_msg("---------------test_flag=%d", num); test_flag = num; return count; } ssize_t show_amlnf_version_info(struct class *class, struct class_attribute *attr, char *buf) { aml_nand_dbg("show_nand_version_info v0.01"); return 0; } #endif /* AML_NAND_UBOOT */ static void show_phydev_info(void) { struct amlnand_phydev *phydev = NULL; //struct amlnf_partition *partition = NULL; //int i = 0; char *config1, *config2; list_for_each_entry(phydev, &nphy_dev_list, list) { if (phydev == NULL) break; #if 0 for (i = 0; i < phydev->nr_partitions; i++) { partition = &phydev->partitions[i]; aml_nand_msg("%s: name=%s,size=%llx", phydev->name, partition->name, partition->size); } #endif if (phydev->option & DEV_MULTI_CHIP_MODE) config1 = "multi_chip"; else config1 = "single_chip"; if (phydev->option & DEV_MULTI_PLANE_MODE) config2 = "multi_plane"; else config2 = "single_plane"; aml_nand_msg("%-10s: 0x%012llx-0x%012llx :partn=%d:%s %s", phydev->name, phydev->offset, phydev->size, phydev->nr_partitions, config1, config2); } } /**********/ /* check list by name 1st, avoiding reinit */ int phydev_add_list(struct amlnand_phydev *new) { int ret = 0; struct amlnand_phydev *dev; list_for_each_entry(dev, &nphy_dev_list, list) { //if (!(memcmp(new->name, dev->name, sizeof(new->name)))) { if (!strncmp((char *)new->name, (char *) dev->name, strlen((char *)new->name))) { ret = -1; goto _out; } } list_add_tail(&new->list, &nphy_dev_list); _out: return ret; } void show_phydev_list(void) { int i = 0; struct amlnand_phydev *dev; printk("%s\n", __func__); list_for_each_entry(dev, &nphy_dev_list, list) { printk("%d: %s\n", i , dev->name); i++; } return; } static int init_phydev_ops(struct amlnand_phydev *phydev) { int ret = 0; phydev->write = nand_write; phydev->read = nand_read; phydev->erase = nand_erase; phydev->block_isbad = nand_block_isbad; phydev->block_markbad = nand_block_markbad; phydev->block_modifybbt = block_modifybbt; phydev->update_bbt = update_bbt; // phydev->phydev_test_block = nand_test_block; return ret; } /*only init dev for u-boot*/ nand_page0_t *p_page0_buf = NULL; int boot_dev_init(struct amlnand_chip *aml_chip) { int ret = 0; struct amlnand_phydev *phydev = NULL; struct nand_flash *flash = &(aml_chip->flash); struct hw_controller *controller = &(aml_chip->controller); PHYDEV_LINE phydev = aml_nand_malloc(sizeof(struct amlnand_phydev)); if (phydev == NULL) { aml_nand_msg("malloc failed need %x here", (u32)(sizeof(struct amlnand_phydev))); ret = -NAND_MALLOC_FAILURE; goto _out; } memset(phydev, 0, sizeof(struct amlnand_phydev)); phydev->priv = aml_chip; PHYDEV_LINE phydev->writesize = flash->pagesize; phydev->erasesize = flash->blocksize; phydev->oobavail = controller->oobavail; p_page0_buf = aml_nand_malloc(flash->pagesize); if (p_page0_buf == NULL) { aml_nand_msg("malloc failed0:%d", flash->pagesize); return ret; } memset(p_page0_buf, 0, flash->pagesize); PHYDEV_LINE //fixme, phy name... memcpy((char *)phydev->name, NAND_BOOT_NAME, strlen(NAND_BOOT_NAME)); PHYDEV_LINE phydev->offset = 0; phydev->size = (BOOT_COPY_NUM*BOOT_PAGES_PER_COPY); phydev->size *= flash->pagesize; /* phydev->size *= chip_num; */ PHYDEV_LINE phydev->writesize_shift = ffs(phydev->writesize) - 1; phydev->erasesize_shift = ffs(phydev->erasesize) - 1; phydev->writesize_mask = (phydev->size>>phydev->writesize_shift) - 1; PHYDEV_LINE init_phydev_ops(phydev); PHYDEV_LINE if ((flash->option & NAND_CHIP_SLC_MODE) && (!(phydev->option & DEV_MULTI_PLANE_MODE))) { phydev->option |= DEV_SLC_MODE; phydev->erasesize >>= 1; phydev->erasesize_shift = ffs(phydev->erasesize) - 1; phydev->size >>= 1; aml_nand_msg(" DEV %s enable SLC mode", phydev->name); } PHYDEV_LINE ret = phydev_add_list(phydev); if (ret) { kfree(phydev); } show_phydev_list(); _out: return ret; } /****** *nand chip usage * all dev size should * uboot(nand boot) reserved code data */ extern struct list_head nphy_dev_list; int aml_alloc_phydev(struct amlnand_phydev **phydev_pp, struct amlnand_chip *aml_chip, struct dev_para **dev_para, int dev_idx) { int ret = 0; struct hw_controller *controller = &(aml_chip->controller); struct nand_flash *flash = &(aml_chip->flash); struct amlnand_phydev* phydev_p = NULL; *phydev_pp = aml_nand_malloc(sizeof(struct amlnand_phydev)); if (*phydev_pp == NULL) { aml_nand_msg("malloc failed need %lx here", sizeof(struct amlnand_phydev)); ret = -NAND_MALLOC_FAILURE; return ret; } phydev_p = *phydev_pp; memset(phydev_p, 0, sizeof(struct amlnand_phydev)); phydev_p->priv = aml_chip; *dev_para = &aml_chip->config_ptr->dev_para[dev_idx]; memcpy((void *)&phydev_p->name, &(*dev_para)->name, MAX_DEVICE_NAME_LEN*sizeof(char)); /*set default parameter*/ phydev_p->writesize = flash->pagesize; phydev_p->erasesize = flash->blocksize; phydev_p->oobavail = controller->oobavail; phydev_p->writesize_shift = ffs(phydev_p->writesize) - 1; phydev_p->erasesize_shift = ffs(phydev_p->erasesize) - 1; phydev_p->write = nand_write; phydev_p->read = nand_read; phydev_p->erase = nand_erase; phydev_p->block_isbad = nand_block_isbad; phydev_p->block_markbad = nand_block_markbad; phydev_p->block_modifybbt = block_modifybbt; phydev_p->update_bbt = update_bbt; phydev_p->phydev_test_block = nand_test_block; return ret; } int amlnand_phydev_init(struct amlnand_chip *aml_chip) { struct amlnand_phydev *phydev = NULL, *phydev_pre = NULL; struct nand_flash *flash = &(aml_chip->flash); 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_config *config = aml_chip->config_ptr; struct nand_arg_info phy_part = aml_chip->nand_phy_partition; struct dev_para *dev_para = NULL; struct amlnf_partition *partition = NULL; u64 offset = 0, dev_size = 0, chip_size = 0, phydev_pre_size = 0; u32 start_blk, total_blk, tmp_write_shift, tmp_erase_shift; u32 tmp_offset = 0, tmp_blk = 0, pages_per_blk; u8 boot_flag = 0, plane_num = 1, chip_num; int i, j, k, ret = 0; u32 adjust_blk = 0, cpu_type; u64 tmp_value; #if 0 /*when opened , need be free bad_blk; */ u64 *bad_blk = NULL; bad_blk = aml_nand_malloc(128*sizeof(u64)); if (bad_blk == NULL) { aml_nand_msg("malloc failed"); ret = -NAND_MALLOC_FAILURE; goto exit_error0; } memset(bad_blk, 0, 128*sizeof(u64)); #endif if (flash->option & NAND_MULTI_PLANE_MODE) plane_num = 2; else plane_num = 1; /* #ifndef AML_NAND_UBOOT */ /* INIT_LIST_HEAD (&nphy_dev_list); */ /* #endif */ chip_num = controller->chip_num; chip_size = (flash->chipsize*chip_num); chip_size = chip_size << 20; if (config->dev_num == 0) { aml_nand_msg("config get invalid: config->dev_num =%d", config->dev_num); ret = -NAND_FAILED; goto exit_error0; } PHYDEV_LINE /* if phy-partition saved is valid, no need to calc and care about driver version. */ if (phy_part.arg_valid == 1) { for (i = 0; i < config->dev_num; i++) { ret = aml_alloc_phydev(&phydev, aml_chip, &dev_para, i); phydev->offset = aml_chip->phy_part_ptr->partition[i].phy_off; phydev->size = aml_chip->phy_part_ptr->partition[i].phy_len; phydev->nr_partitions = dev_para->nr_partitions; phydev->partitions = dev_para->partitions; if ((dev_para->option & DEV_MULTI_CHIP_MODE) && (controller->chip_num > 1)) { phydev->option |= DEV_MULTI_CHIP_MODE; phydev->writesize *= controller->chip_num; phydev->erasesize *= controller->chip_num; phydev->oobavail *= controller->chip_num; } if ((dev_para->option & DEV_MULTI_PLANE_MODE) && (flash->option & NAND_MULTI_PLANE_MODE)) { phydev->option |= DEV_MULTI_PLANE_MODE; phydev->writesize <<= 1; phydev->erasesize <<= 1; phydev->oobavail <<= 1; } if ((dev_para->option & DEV_USE_SHAREPAGE_MODE) && (flash->option & NAND_USE_SHAREPAGE_MODE)) { phydev->option |= DEV_USE_SHAREPAGE_MODE; phydev->writesize <<= 1; phydev->oobavail <<= 1; } phydev->writesize_shift = ffs(phydev->writesize) - 1; phydev->erasesize_shift = ffs(phydev->erasesize) - 1; printk("%s,%d,phydev->offset=%llx,phydev->size=%llx\n", __func__, __LINE__, phydev->offset,phydev->size); phydev_add_list(phydev); } } else { for (i = 0; i < config->dev_num; i++) { ret = aml_alloc_phydev(&phydev, aml_chip, &dev_para, i); dev_size = 0; aml_nand_msg("dev num: %d, name: %s",i,phydev->name); tmp_write_shift = ffs(flash->pagesize) - 1; tmp_erase_shift = ffs(flash->blocksize) - 1; pages_per_blk = (1 << (tmp_erase_shift - tmp_write_shift)); /* set partitions and calculate dev size */ if (dev_para->nr_partitions) { phydev->nr_partitions = dev_para->nr_partitions; phydev->partitions = dev_para->partitions; for (k = 0; k < dev_para->nr_partitions; k++) { partition = &(dev_para->partitions[k]); aml_nand_msg("partition[%d]-name:%s,size:%llx", k, partition->name, partition->size); } PHYDEV_LINE if (i != (config->dev_num - 1)) { for (j = 0; j < dev_para->nr_partitions; j++) { partition = &(dev_para->partitions[j]); dev_size += partition->size; } if (!is_phydev_off_adjust()) { //no go int adjust_shift = ffs(ADJUST_SIZE_NFTL) - 1; dev_size = dev_size + (dev_size >> adjust_shift); } /* adjust dev_size for nftl dev_size = dev_size \ + ((u64)(dev_size)/(u64)(ADJUST_SIZE_NFTL)); */ } else { if ((phydev_pre->option & DEV_SLC_MODE) && (flash->option & NAND_CHIP_SLC_MODE) && (!(phydev->option & DEV_MULTI_PLANE_MODE))) phydev_pre_size = phydev_pre->size << 1; else phydev_pre_size = phydev_pre->size; dev_size = chip_size - phydev_pre->offset - phydev_pre_size; } } else { dev_size = dev_para->size;/*0*/ aml_nand_msg(" dev size: 0x%llx",dev_size); } if ((dev_para->option & DEV_SLC_MODE) && (flash->option & NAND_CHIP_SLC_MODE) && (!(dev_para->option & DEV_MULTI_PLANE_MODE))) { dev_size <<= 1; aml_nand_msg("DEV enable SLC mode"); } if (!strncmp((char *)phydev->name, NAND_BOOT_NAME, strlen((const char *)NAND_BOOT_NAME))) { boot_flag = 1; phydev->offset = 0; phydev->size = (BOOT_COPY_NUM * BOOT_PAGES_PER_COPY); //printk("----------%llx\n", phydev->size); phydev->size *= flash->pagesize; //printk("----------%llx\n", phydev->size); /* phydev->size *= chip_num; */ phydev->writesize_shift = ffs(phydev->writesize) - 1; phydev->erasesize_shift = ffs(phydev->erasesize) - 1; phydev->writesize_mask = (phydev->size>>phydev->writesize_shift) - 1; tmp_blk = phydev->size >> phydev->erasesize_shift; } else { /* not boot phydev[nftl] */ if ((dev_para->option & DEV_MULTI_CHIP_MODE) && (chip_num > 1)) { phydev->option |= DEV_MULTI_CHIP_MODE; phydev->writesize *= chip_num; phydev->erasesize *= chip_num; phydev->oobavail *= chip_num; } if ((dev_para->option & DEV_MULTI_PLANE_MODE) && (flash->option & NAND_MULTI_PLANE_MODE)) { phydev->option |= DEV_MULTI_PLANE_MODE; phydev->writesize <<= 1; phydev->erasesize <<= 1; phydev->oobavail <<= 1; } if ((dev_para->option & DEV_USE_SHAREPAGE_MODE) && (flash->option & NAND_USE_SHAREPAGE_MODE)) { phydev->option |= DEV_USE_SHAREPAGE_MODE; phydev->writesize <<= 1; phydev->oobavail <<= 1; } phydev->writesize_shift = ffs(phydev->writesize) - 1; phydev->erasesize_shift = ffs(phydev->erasesize) - 1; /****cal phydev off**/ if (((boot_flag == 1) && (i == 1)) || ((boot_flag == 0) && (i == 0))) { offset = start_blk = 0; total_blk = RESERVED_BLOCK_CNT; memset(ops_para, 0, sizeof(struct chip_ops_para)); do { tmp_value = offset>>tmp_erase_shift; ops_para->chipnr = ((u32)tmp_value) % chip_num; tmp_value += tmp_blk; tmp_value /= chip_num; tmp_value += tmp_blk - tmp_blk/chip_num; tmp_value *= pages_per_blk; ops_para->page_addr = (u32)tmp_value; ret = operation->block_isbad(aml_chip); if (ret == NAND_BLOCK_FACTORY_BAD) { offset += flash->blocksize; continue; } start_blk++; offset += flash->blocksize; } while (start_blk < total_blk); /*reserve_end blk*/ tmp_value = (offset>>tmp_erase_shift) - 1; tmp_value /= chip_num*plane_num; tmp_value += 1; total_blk = tmp_value * chip_num * plane_num; aml_nand_msg("total_blk =%d", total_blk); aml_nand_msg(" phydev_pre->size =%llx", phydev_pre->size); if (phydev_pre == NULL) phydev->offset = total_blk * flash->blocksize; else phydev->offset = total_blk * flash->blocksize + phydev_pre->size; aml_nand_msg("phydev->offset =%llx", phydev->offset); } else { if ((!(phydev->option & DEV_MULTI_PLANE_MODE) && (flash->option & NAND_CHIP_SLC_MODE) && (phydev_pre->option & DEV_SLC_MODE))) phydev_pre_size = phydev_pre->size << 1; else phydev_pre_size = phydev_pre->size; phydev->offset = phydev_pre->offset + phydev_pre_size; } /****cal phydev size**/ if (i != (config->dev_num - 1)) { start_blk = 0; if (((boot_flag == 1) && (i == 1)) || ((boot_flag == 0) && (i == 0))) offset = phydev->offset; else offset = phydev_pre->offset + phydev_pre->size; tmp_offset = offset; aml_nand_dbg("offset = %llx, %llx", offset, tmp_erase_shift); aml_nand_dbg("adjust phy offset: %d", ADJUST_BLOCK_NUM); total_blk = dev_size >> phydev->erasesize_shift; cpu_type = get_cpu_type(); /* for compatible with history. */ if ((cpu_type == MESON_CPU_MAJOR_ID_M8) && (phydev->erasesize < 0x400000)) adjust_blk = 0; else adjust_blk = ADJUST_PART_SIZE - 1; /*************adjust****************/ tmp_value = total_blk; tmp_value += adjust_blk; tmp_value /= ADJUST_PART_SIZE; tmp_value += total_blk + ADJUST_BLOCK_NUM; total_blk = tmp_value; memset(ops_para, 0, sizeof(struct chip_ops_para)); ops_para->option = phydev->option; do { tmp_value = offset>>phydev->writesize_shift; ops_para->page_addr = (u32)(tmp_value); ret = operation->block_isbad(aml_chip); if (ret == NAND_BLOCK_FACTORY_BAD) { offset += phydev->erasesize; continue; } start_blk++; offset += phydev->erasesize; } while (start_blk < total_blk); tmp_value = offset - tmp_offset; tmp_value = (tmp_value >> phydev->erasesize_shift); tmp_value -= 1; tmp_value /= chip_num * plane_num; tmp_value += 1; total_blk = tmp_value * chip_num * plane_num; //printk("%s() %d, total_blk 0x%x, erase_size 0x%x\n", __func__, __LINE__, total_blk, phydev->erasesize); phydev->size = ((u64)total_blk * (u64)phydev->erasesize); } else phydev->size = dev_size; if ((phydev->offset + phydev->size) > chip_size) { aml_nand_msg("nand dev size is out of space"); ret = -NAND_FAILED; goto exit_error0; } #ifndef AML_NAND_UBOOT phydev->suspend = phydev_suspend; phydev->resume = phydev_resume; #endif } phydev_pre = phydev; if ((dev_para->option & DEV_SLC_MODE) && (flash->option & NAND_CHIP_SLC_MODE) && (!(phydev->option & DEV_MULTI_PLANE_MODE))) { phydev->option |= DEV_SLC_MODE; phydev->erasesize >>= 1; phydev->erasesize_shift = ffs(phydev->erasesize) - 1; phydev->size >>= 1; aml_nand_msg(" DEV %s enable SLC mode", phydev->name); } aml_chip->phy_part_ptr->partition[i].phy_off = phydev->offset; aml_chip->phy_part_ptr->partition[i].phy_len = phydev->size; if (dev_para->nr_partitions) { for (k = 0; k < dev_para->nr_partitions; k++) { partition = &(dev_para->partitions[k]); aml_chip->phy_part_ptr->partition[i].logic_len += partition->size; } aml_nand_msg("logic_len: 0x%llx",aml_chip->phy_part_ptr->partition[i].logic_len); } else { aml_chip->phy_part_ptr->partition[i].logic_len = dev_para->size; aml_nand_msg("logic_len: 0x%llx",dev_para->size); } /* fixme, add new physic device */ //list_add_tail(&phydev->list, &nphy_dev_list); phydev_add_list(phydev); #if (AML_NAND_DBG) amldev_dumpinfo(phydev); #endif aml_nand_dbg("####: %s :phydev->offset = %llx", phydev_pre->name, phydev_pre->offset); aml_nand_dbg("####: %s :phydev->size = %llx", phydev_pre->name, phydev_pre->size); aml_nand_dbg("####: %s :phydev->writesize = %x", phydev_pre->name, phydev_pre->writesize); aml_nand_dbg("####: %s :phydev->erasesize = %x", phydev_pre->name, phydev_pre->erasesize); } /*save phy partitions to nand*/ aml_chip->phy_part_ptr->dev_num = config->dev_num; aml_chip->phy_part_ptr->crc = aml_info_checksum((u8 *)aml_chip->phy_part_ptr->partition, (MAX_DEVICE_NUM * sizeof(struct _phy_partition))); ret = amlnand_save_info_by_name(aml_chip, (u8 *)&(aml_chip->nand_phy_partition), (u8 *)aml_chip->phy_part_ptr, (u8 *)PHY_PARTITION_HEAD_MAGIC, sizeof(struct phy_partition_info)); if (ret < 0) { aml_nand_msg("save nand phy partition failed and ret:%d",ret); goto exit_error0; } } show_phydev_info(); PHYDEV_LINE #if 0 phydev = NULL; list_for_each_entry(phydev, &nphy_dev_list, list) { if (phydev == NULL) break; aml_nand_dbg("-----------------------------\n"); aml_nand_dbg("name:%s,offset:%llx,size:%llx,option:%x", phydev->name, phydev->offset, phydev->size, phydev->option); aml_nand_dbg("es:%x,ws:%x,oob:%x,eshift:%x,wshift:%d", phydev->erasesize, phydev->writesize, phydev->oobavail, phydev->erasesize_shift, phydev->writesize_shift); aml_nand_dbg(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); relative_offset = 0; bad_blk_cnt = 0; devops = &(phydev->ops); memset(bad_blk, 0, 128*sizeof(u64)); do { memset(devops, 0x0, sizeof(struct phydev_ops)); memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = relative_offset; devops->len = phydev->erasesize; devops->datbuf = NULL; devops->oobbuf = NULL; devops->mode = NAND_HW_ECC; ret = nand_block_isbad(phydev); if (ret == NAND_BLOCK_USED_BAD) { if (bad_blk_cnt < 128) { bad_blk[bad_blk_cnt] = relative_offset; bad_blk_cnt++; } } relative_offset += phydev->erasesize; } while (relative_offset < phydev->size); aml_nand_msg("(%s) bad blks %d", phydev->name, bad_blk_cnt); if ((bad_blk_cnt * 32 > (phydev->size >> phydev->erasesize_shift)) || (bad_blk_cnt > 10)) { aml_nand_dbg("Too many new bad blks,try to repair..\n"); /* ret = aml_repair_bbt(phydev,bad_blk,bad_blk_cnt); */ } } kfree(bad_blk); #endif #ifdef AML_NAND_UBOOT /************fixed by liuxianjun********** if (aml_chip->init_flag == NAND_BOOT_ERASE_PROTECT_CACHE) { ret = phydev_init_erase(aml_chip); if (ret < 0) { aml_nand_msg("amlnand_phydev_init : phydev_init_erase failed"); } } **************************/ #endif /* AML_NAND_UBOOT */ return NAND_SUCCESS; exit_error0: return ret; } #ifdef AML_NAND_UBOOT void amlnf_phy_exit(void) { struct amlnand_phydev *phydev = NULL; struct amlnand_chip *aml_chip = NULL; //struct list_head *entry; int time= 0; list_for_each_entry(phydev,&nphy_dev_list,list){ if (phydev) { if (time == 0) { aml_chip = (struct amlnand_chip *)phydev->priv; if (aml_chip) { if (aml_chip->block_status) { kfree(aml_chip->block_status); aml_chip->block_status = NULL; } if (aml_chip->user_page_buf) { kfree(aml_chip->user_page_buf); aml_chip->user_page_buf = NULL; } if (aml_chip->user_oob_buf) { kfree(aml_chip->user_oob_buf); aml_chip->user_oob_buf = 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; } } time++; } } } list_del_init(&nphy_dev_list); } #endif