From a499d97cac84979441d629ac3e76b74a9ad05848 Mon Sep 17 00:00:00 2001 From: "xianjun.liu" Date: Thu, 7 Mar 2019 17:26:42 +0800 Subject: [PATCH] MTD: spin_lock stuck problem about marking bad block for mtd driver [1/1] PD#SWPL-5613 Problem: boot fail because of the UBI write error Solution: fixed nested-calls spin-lock when update bbt Verify: AXG-S420 Change-Id: I56b603ebffa5557ab7949ead2fddd41323346d09 Signed-off-by: xianjun.liu --- drivers/amlogic/mtd/aml_dtb.c | 30 +++++--- drivers/amlogic/mtd/aml_env.c | 25 +++++- drivers/amlogic/mtd/aml_mtd.h | 3 + drivers/amlogic/mtd/aml_nand.c | 26 +++---- drivers/amlogic/mtd/rsv_manage.c | 160 +++++++++++++++++++++------------------ 5 files changed, 141 insertions(+), 103 deletions(-) diff --git a/drivers/amlogic/mtd/aml_dtb.c b/drivers/amlogic/mtd/aml_dtb.c index e8711d8..4259e15 100644 --- a/drivers/amlogic/mtd/aml_dtb.c +++ b/drivers/amlogic/mtd/aml_dtb.c @@ -129,10 +129,12 @@ ssize_t dtb_read(struct file *file, loff_t *ppos) { u8 *dtb_ptr = NULL; - /*struct nand_flash *flash = &aml_chip_dtb->flash;*/ + struct nand_chip *chip = &aml_chip_dtb->chip; struct mtd_info *mtd = aml_chip_dtb->mtd; ssize_t read_size = 0; int ret = 0; + loff_t addr; + int chipnr; if (*ppos == aml_chip_dtb->dtbsize) return 0; @@ -146,8 +148,10 @@ ssize_t dtb_read(struct file *file, if (dtb_ptr == NULL) return -ENOMEM; - /*not need nand_get_device here, mtd->_read_xx will done with it*/ - /*nand_get_device(mtd, FL_READING);*/ + addr = *ppos; + nand_get_device(mtd, FL_READING); + chipnr = (int)(addr >> chip->chip_shift); + chip->select_chip(mtd, chipnr); ret = amlnf_dtb_read((u8 *)dtb_ptr, aml_chip_dtb->dtbsize); if (ret) { pr_info("%s: read failed:%d\n", __func__, ret); @@ -163,8 +167,8 @@ ssize_t dtb_read(struct file *file, ret = copy_to_user(buf, (dtb_ptr + *ppos), read_size); *ppos += read_size; exit: - /*nand_release_device(mtd);*/ - /* kfree(dtb_ptr); */ + chip->select_chip(mtd, -1); + nand_release_device(mtd); vfree(dtb_ptr); return read_size; } @@ -175,9 +179,11 @@ ssize_t dtb_write(struct file *file, { u8 *dtb_ptr = NULL; ssize_t write_size = 0; - /*struct nand_flash *flash = &aml_chip_dtb->flash;*/ - struct mtd_info *mtd = aml_chip_dtb->mtd; int ret = 0; + struct nand_chip *chip = &aml_chip_dtb->chip; + struct mtd_info *mtd = aml_chip_dtb->mtd; + loff_t addr; + int chipnr; if (*ppos == aml_chip_dtb->dtbsize) return 0; @@ -191,8 +197,10 @@ ssize_t dtb_write(struct file *file, if (dtb_ptr == NULL) return -ENOMEM; - /*not need nand_get_device here, mtd->_read_xx will done with it*/ - /*nand_get_device(mtd, FL_WRITING);*/ + addr = *ppos; + nand_get_device(mtd, FL_WRITING); + chipnr = (int)(addr >> chip->chip_shift); + chip->select_chip(mtd, chipnr); ret = amlnf_dtb_read((u8 *)dtb_ptr, aml_chip_dtb->dtbsize); if (ret) { pr_info("%s: read failed\n", __func__); @@ -216,8 +224,8 @@ ssize_t dtb_write(struct file *file, *ppos += write_size; exit: - /*nand_release_device(mtd);*/ - /* kfree(dtb_ptr); */ + chip->select_chip(mtd, -1); + nand_release_device(mtd); vfree(dtb_ptr); return write_size; } diff --git a/drivers/amlogic/mtd/aml_env.c b/drivers/amlogic/mtd/aml_env.c index 0de2242..c56fb11 100644 --- a/drivers/amlogic/mtd/aml_env.c +++ b/drivers/amlogic/mtd/aml_env.c @@ -189,6 +189,10 @@ ssize_t uboot_env_read(struct file *file, u8 *env_ptr = NULL; ssize_t read_size = 0; int ret = 0; + struct nand_chip *chip = &aml_chip_env->chip; + struct mtd_info *mtd = aml_chip_env->mtd; + loff_t addr; + int chipnr; if (*ppos == CONFIG_ENV_SIZE) return 0; @@ -201,7 +205,11 @@ ssize_t uboot_env_read(struct file *file, env_ptr = vmalloc(CONFIG_ENV_SIZE + 2048); if (env_ptr == NULL) return -ENOMEM; - mutex_lock(&env_mutex); + + addr = *ppos; + nand_get_device(mtd, FL_READING); + chipnr = (int)(addr >> chip->chip_shift); + chip->select_chip(mtd, chipnr); /*amlnand_get_device(aml_chip_env, CHIP_READING);*/ ret = amlnf_env_read((u8 *)env_ptr, CONFIG_ENV_SIZE); @@ -220,7 +228,8 @@ ssize_t uboot_env_read(struct file *file, ret = copy_to_user(buf, (env_ptr + *ppos), read_size); *ppos += read_size; exit: - mutex_unlock(&env_mutex); + chip->select_chip(mtd, -1); + nand_release_device(mtd); /*amlnand_release_device(aml_chip_env);*/ vfree(env_ptr); return read_size; @@ -233,6 +242,10 @@ ssize_t uboot_env_write(struct file *file, u8 *env_ptr = NULL; ssize_t write_size = 0; int ret = 0; + struct nand_chip *chip = &aml_chip_env->chip; + struct mtd_info *mtd = aml_chip_env->mtd; + loff_t addr; + int chipnr; if (*ppos == CONFIG_ENV_SIZE) return 0; @@ -246,7 +259,10 @@ ssize_t uboot_env_write(struct file *file, if (env_ptr == NULL) return -ENOMEM; - mutex_lock(&env_mutex); + addr = *ppos; + nand_get_device(mtd, FL_WRITING); + chipnr = (int)(addr >> chip->chip_shift); + chip->select_chip(mtd, chipnr); if ((*ppos + count) > CONFIG_ENV_SIZE) write_size = CONFIG_ENV_SIZE - *ppos; @@ -264,7 +280,8 @@ ssize_t uboot_env_write(struct file *file, *ppos += write_size; exit: - mutex_unlock(&env_mutex); + chip->select_chip(mtd, -1); + nand_release_device(mtd); vfree(env_ptr); return write_size; } diff --git a/drivers/amlogic/mtd/aml_mtd.h b/drivers/amlogic/mtd/aml_mtd.h index a8d475e..3d4cadf 100644 --- a/drivers/amlogic/mtd/aml_mtd.h +++ b/drivers/amlogic/mtd/aml_mtd.h @@ -883,6 +883,9 @@ int boot_device_register(struct aml_nand_chip *aml_chip); int add_mtd_partitions(struct mtd_info *mtd, const struct mtd_partition *part, int num); +int nand_get_device(struct mtd_info *mtd, int new_state); +void nand_release_device(struct mtd_info *mtd); + #ifdef AML_NAND_UBOOT extern int get_partition_from_dts(unsigned char *buffer); #endif diff --git a/drivers/amlogic/mtd/aml_nand.c b/drivers/amlogic/mtd/aml_nand.c index 7251fff..e827a9a 100644 --- a/drivers/amlogic/mtd/aml_nand.c +++ b/drivers/amlogic/mtd/aml_nand.c @@ -17,7 +17,6 @@ #include "aml_mtd.h" - uint8_t nand_boot_flag; /*#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 13)*/ @@ -1913,9 +1912,10 @@ int aml_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); - struct mtd_oob_ops aml_oob_ops; int blk_addr, mtd_erase_shift; int8_t *buf = NULL; + int page, chipnr; + int ret = 0; if (!mtd->erasesize) return -EINVAL; @@ -1935,19 +1935,18 @@ int aml_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) } mark_bad: /*no erase here, fixit*/ - aml_oob_ops.mode = MTD_OPS_AUTO_OOB; - aml_oob_ops.len = mtd->writesize; - aml_oob_ops.ooblen = mtd->oobavail; - /*aml_oob_ops.ooboffs = chip->ecc.layout->oobfree[0].offset;*/ - aml_oob_ops.ooboffs = 0; - aml_oob_ops.datbuf = chip->buffers->databuf; - aml_oob_ops.oobbuf = chip->oob_poi; chip->pagebuf = -1; - memset((unsigned char *)aml_oob_ops.datbuf, 0x0, mtd->writesize); - memset((unsigned char *)aml_oob_ops.oobbuf, 0x0, aml_oob_ops.ooblen); - - return mtd->_write_oob(mtd, ofs, &aml_oob_ops); + memset((unsigned char *)chip->buffers->databuf, 0x0, mtd->writesize); + memset((unsigned char *)chip->oob_poi, 0x0, mtd->oobavail); + chipnr = (int)(ofs >> chip->chip_shift); + page = (int)(ofs >> chip->page_shift); + chip->select_chip(mtd, chipnr); + ret = chip->write_page(mtd, chip, 0, mtd->writesize, + chip->buffers->databuf, + 1, page, 0, 0); + chip->select_chip(mtd, -1); + return ret; } static uint8_t aml_platform_read_byte(struct mtd_info *mtd) @@ -2195,3 +2194,4 @@ exit_error: aml_chip->block_status = NULL; return err; } + diff --git a/drivers/amlogic/mtd/rsv_manage.c b/drivers/amlogic/mtd/rsv_manage.c index f0ddbc4..cb0de55 100644 --- a/drivers/amlogic/mtd/rsv_manage.c +++ b/drivers/amlogic/mtd/rsv_manage.c @@ -418,9 +418,10 @@ int aml_nand_read_rsv_info(struct mtd_info *mtd, 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)]; + int page, realpage, chipnr; + struct nand_chip *chip = mtd_to_nand(mtd); READ_RSV_AGAIN: addr = nandrsv_info->valid_node->phy_blk_addr; @@ -433,20 +434,19 @@ READ_RSV_AGAIN: 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, + memset((unsigned char *)data_buf, 0x0, mtd->writesize); - memset((unsigned char *)aml_oob_ops.oobbuf, - 0x0, aml_oob_ops.ooblen); - - error = mtd->_read_oob(mtd, addr, &aml_oob_ops); + memset((unsigned char *)oob_buf, + 0x0, sizeof(struct oobinfo_t)); + chipnr = (int)(addr >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + realpage = (int)(addr >> chip->page_shift); + page = realpage & chip->pagemask; + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + error = chip->ecc.read_page(mtd, chip, data_buf, + 1, page); + chip->select_chip(mtd, -1); if ((error != 0) && (error != -EUCLEAN)) { pr_info("blk good but read failed: %llx, %d\n", (uint64_t)addr, error); @@ -456,11 +456,12 @@ READ_RSV_AGAIN: goto READ_RSV_AGAIN; } + memcpy(oob_buf, chip->oob_poi, mtd->oobavail); 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; + page++; len = min_t(uint32_t, mtd->writesize, (nandrsv_info->size - amount_loaded)); memcpy(buf + amount_loaded, data_buf, len); @@ -487,8 +488,6 @@ READ_RSV_AGAIN: } } #endif - /* if(data_buf) */ - /* kfree(data_buf); */ return 0; } @@ -544,18 +543,17 @@ 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 nand_chip *chip = mtd_to_nand(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)]; + int page, chipnr; 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); @@ -584,21 +582,21 @@ static int aml_nand_write_rsv(struct mtd_info *mtd, } #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, + memset((unsigned char *)data_buf, 0x0, mtd->writesize); len = min_t(uint32_t, mtd->writesize, nandrsv_info->size - amount_saved); - memcpy((unsigned char *)aml_oob_ops.datbuf, + memcpy((unsigned char *)data_buf, buf + amount_saved, len); - error = mtd->_write_oob(mtd, addr, &aml_oob_ops); + page = (int)(addr >> chip->page_shift); + chipnr = (int)(addr >> chip->chip_shift); + memset(chip->oob_poi, 0xff, mtd->oobsize); + memcpy(chip->oob_poi, oob_buf, mtd->oobsize); + chip->select_chip(mtd, chipnr); + error = chip->write_page(mtd, chip, 0, len, data_buf, + 1, page, 0, 0); + chip->select_chip(mtd, -1); if (error) { pr_info("blk check good but write failed: %llx, %d\n", (uint64_t)addr, error); @@ -609,17 +607,19 @@ static int aml_nand_write_rsv(struct mtd_info *mtd, } 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 nand_chip *chip = mtd_to_nand(mtd); struct free_node_t *free_node = NULL, *tmp_node = NULL; int error = 0, pages_per_blk, valid_page_addr, i = 1; loff_t addr = 0; - struct erase_info erase_info; + int chipnr = 0; + int page; pages_per_blk = mtd->erasesize / mtd->writesize; /*solve these abnormals caused by power off and ecc error*/ @@ -639,13 +639,12 @@ RE_SEARCH: if ((valid_page_addr - i) == pages_per_blk) { 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); + page = (int)(addr >> chip->page_shift); + chipnr = (int)(addr >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + error = chip->erase(mtd, page & chip->pagemask); + chip->select_chip(mtd, -1); _aml_rsv_protect(); nandrsv_info->valid_node->ec++; pr_info("---erase bad env block:%llx\n", addr); @@ -691,7 +690,10 @@ RE_SEARCH: 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); + chipnr = (int)(addr >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + error = chip->block_bad(mtd, addr); + chip->select_chip(mtd, -1); if (error != 0) { /* *bad block here, need fix it @@ -706,16 +708,18 @@ RE_SEARCH: goto RE_SEARCH; } - 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); + page = (int)(addr >> chip->page_shift); + //chipnr = (int)(addr >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + error = chip->erase(mtd, page & chip->pagemask); + chip->select_chip(mtd, -1); _aml_rsv_protect(); if (error) { pr_info("env free blk erase failed %d\n", error); - mtd->_block_markbad(mtd, addr); + chip->select_chip(mtd, chipnr); + chip->block_markbad(mtd, addr); + chip->select_chip(mtd, -1); return error; } nandrsv_info->valid_node->ec++; @@ -941,20 +945,21 @@ int aml_nand_free_rsv_info(struct mtd_info *mtd, struct free_node_t *tmp_node, *next_node = NULL; int error = 0; loff_t addr = 0; - struct erase_info erase_info; + int page, chipnr; + struct nand_chip *chip = mtd_to_nand(mtd); pr_info("free %s:\n", nandrsv_info->name); if (nandrsv_info->valid) { 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; + + page = (int)(addr >> chip->page_shift); + chipnr = (int)(addr >> chip->chip_shift); _aml_rsv_disprotect(); - error = mtd->_erase(mtd, &erase_info); + chip->select_chip(mtd, chipnr); + error = chip->erase(mtd, page & chip->pagemask); + chip->select_chip(mtd, -1); _aml_rsv_protect(); pr_info("erasing valid info block: %llx\n", addr); nandrsv_info->valid_node->phy_blk_addr = -1; @@ -980,7 +985,6 @@ int aml_nand_scan_rsv_info(struct mtd_info *mtd, { 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)]; @@ -990,6 +994,7 @@ int aml_nand_scan_rsv_info(struct mtd_info *mtd, int phys_erase_shift, pages_per_blk, page_num; int error = 0, ret = 0; uint32_t remainder; + int page, realpage, chipnr; data_buf = aml_chip->rsv_data_buf; oobinfo = (struct oobinfo_t *)oob_buf; @@ -1007,19 +1012,20 @@ RE_RSV_INFO_EXT: 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, + memset((unsigned char *)data_buf, 0x0, mtd->writesize); - memset((unsigned char *)aml_oob_ops.oobbuf, - 0x0, aml_oob_ops.ooblen); - - error = mtd->_read_oob(mtd, offset, &aml_oob_ops); + memset((unsigned char *)oob_buf, + 0x0, sizeof(struct oobinfo_t)); + + realpage = (int)(offset >> chip->page_shift); + page = realpage & chip->pagemask; + chipnr = (int)(offset >> chip->chip_shift); + + chip->select_chip(mtd, chipnr); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + error = chip->ecc.read_page(mtd, chip, data_buf, + 1, page); + chip->select_chip(mtd, -1); if ((error != 0) && (error != -EUCLEAN)) { pr_info("blk check good but read failed: %llx, %d\n", (uint64_t)offset, error); @@ -1033,6 +1039,7 @@ RE_RSV_INFO: goto RE_RSV_INFO; } + memcpy(oob_buf, chip->oob_poi, mtd->oobavail); nandrsv_info->init = 1; nandrsv_info->valid_node->status = 0; if (!memcmp(oobinfo->name, nandrsv_info->name, 4)) { @@ -1121,23 +1128,25 @@ RE_RSV_INFO: 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, + memset((unsigned char *)data_buf, 0x0, mtd->writesize); - memset((unsigned char *)aml_oob_ops.oobbuf, - 0x0, aml_oob_ops.ooblen); + memset((unsigned char *)oob_buf, + 0x0, sizeof(struct oobinfo_t)); offset = nandrsv_info->valid_node->phy_blk_addr; offset *= mtd->erasesize; offset += i * mtd->writesize; - error = mtd->_read_oob(mtd, offset, &aml_oob_ops); + + realpage = (int)(offset >> chip->page_shift); + page = realpage & chip->pagemask; + chipnr = (int)(offset >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + error = chip->ecc.read_page(mtd, chip, data_buf, + 1, page); + chip->select_chip(mtd, -1); if ((error != 0) && (error != -EUCLEAN)) { pr_info("blk good but read failed:%llx,%d\n", (uint64_t)offset, error); @@ -1146,6 +1155,7 @@ RE_RSV_INFO: continue; } + memcpy(oob_buf, chip->oob_poi, mtd->oobavail); if (!memcmp(oobinfo->name, nandrsv_info->name, 4)) { good_addr[i] = 1; nandrsv_info->valid_node->phy_page_addr = i; -- 2.7.4