From patchwork Thu Mar 16 06:47:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?UGV0ZXIgUGFuIOa9mOagiyAocGV0ZXJwYW5kb25nKQ==?= X-Patchwork-Id: 739573 X-Patchwork-Delegate: boris.brezillon@free-electrons.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3vkJhC2tkFz9s03 for ; Thu, 16 Mar 2017 17:38:19 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="aZ6DxSwN"; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=H6y66RpWuwDcrH2yiAU8KOGOi5oMwqMnt+bnAdON1mA=; b=aZ6DxSwNVkzi1b AI0X8/QCANDq6+Prw2JH9tmOrrWRgdywd4ZonkcTQjYrYM3oMOjqddVdq/VWE7BNXEW8A1ySz6Q3U 9SxP5+kZS4hJh21OVzm6Y7j98thSEC+kIlHZ9VMZe7w3jHgyRPBAXN+PNOTIwwiHuxOHjhfsIz3FO nalpDP95KwfytUuExxqZk25BPYoDwSoYxN+UUFWxqNltyzpO+3PtMqHRTtsC7gVCTqgaH68ufaTr8 9tITFdMSZWBLrw+pPvozqSnXVhckYeIDJDapN32CLRbbZMHF/tdY6ur5LJLS83FaBXmMuOuVgmyL+ cmofDHs68y7HtDnenLLQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1coP35-0001MC-6a; Thu, 16 Mar 2017 06:38:11 +0000 Received: from mailout.micron.com ([137.201.242.129]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1coP1V-0008Su-SN for linux-mtd@lists.infradead.org; Thu, 16 Mar 2017 06:36:36 +0000 Received: from mail.micron.com (bowex17h.micron.com [137.201.20.32]) by mailout.micron.com (8.14.4/8.14.6) with ESMTP id v2G6a7Qg012030; Thu, 16 Mar 2017 00:36:07 -0600 Received: from SIWEX5C.sing.micron.com (10.160.29.61) by BOWEX17H.micron.com (137.201.20.32) with Microsoft SMTP Server (TLS) id 15.0.1263.5; Thu, 16 Mar 2017 00:36:06 -0600 Received: from BOWEX17H.micron.com (137.201.20.32) by SIWEX5C.sing.micron.com (10.160.29.61) with Microsoft SMTP Server (TLS) id 15.0.1263.5; Thu, 16 Mar 2017 14:36:02 +0800 Received: from peterpan-Linux-Desktop.micron.com (10.66.12.56) by BOWEX17H.micron.com (137.201.20.32) with Microsoft SMTP Server id 15.0.1263.5 via Frontend Transport; Thu, 16 Mar 2017 00:36:00 -0600 From: Peter Pan To: , , , , , Subject: [PATCH v3 5/8] nand: spi: Add bad block support Date: Thu, 16 Mar 2017 14:47:34 +0800 Message-ID: <1489646857-10112-6-git-send-email-peterpandong@micron.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1489646857-10112-1-git-send-email-peterpandong@micron.com> References: <1489646857-10112-1-git-send-email-peterpandong@micron.com> MIME-Version: 1.0 X-TM-AS-Product-Ver: SMEX-12.0.0.1464-8.100.1062-22944.005 X-TM-AS-Result: No--11.622000-0.000000-31 X-TM-AS-MatchedID: 704421-863432-188019-700648-706290-709755-700057-700811-3 00010-860493-708712-700398-105250-703529-703399-709908-704568-121367-708797 -105640-712058-105040-702143-188198-851458-700264-188199-705313-709137-7028 98-700838-707451-863828-702171-701403-704425-701927-703371-700324-703788-70 3523-702358-700901-700251-105700-709859-121336-148004-148036-42000-42003-52 000 X-TM-AS-User-Approved-Sender: Yes X-TM-AS-User-Blocked-Sender: No X-MT-CheckInternalSenderRule: True X-Scanned-By: MIMEDefang 2.78 on 137.201.82.98 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170315_233634_102945_3E3FCBF1 X-CRM114-Status: GOOD ( 19.28 ) X-Spam-Score: -4.2 (----) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-4.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [137.201.242.129 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -0.0 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peterpansjtu@gmail.com, linshunquan1@hisilicon.com, peterpandong@micron.com Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Add isbad and markbad support for SPI NAND. And do not erase bad blocks in spi_nand_erase. BBT is also enabled in this patch. Signed-off-by: Peter Pan --- drivers/mtd/nand/spi/core.c | 293 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 289 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 7834c3d..a880a18 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -21,6 +21,9 @@ #include #include +static int spinand_erase_skip_bbt(struct mtd_info *mtd, + struct erase_info *einfo); + /* * spinand_exec_op - execute SPI NAND operation by controller ->exec_op() hook * @chip: SPI NAND device structure @@ -871,11 +874,154 @@ static int spinand_write_oob(struct mtd_info *mtd, loff_t to, } /* - * spinand_erase - [MTD Interface] erase block(s) + * spinand_block_bad - check if block at offset is bad by bad block marker + * @mtd: MTD device structure + * @offs: offset from device start + */ +static int spinand_block_bad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_device *nand = mtd_to_nand(mtd); + struct mtd_oob_ops ops = {0}; + u32 block_addr; + u8 bad[2] = {0, 0}; + u8 ret = 0; + unsigned int max_bitflips; + + block_addr = nand_offs_to_eraseblock(nand, offs); + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooblen = 2; + ops.oobbuf = bad; + spinand_read_pages(mtd, nand_eraseblock_to_offs(nand, block_addr), + &ops, &max_bitflips); + if (bad[0] != 0xFF || bad[1] != 0xFF) + ret = 1; + + return ret; +} + +/* + * spinand_block_checkbad - check if a block is marked bad + * @mtd: MTD device structure + * @offs: offset from device start + * @allowbbt: 1, if allowe to access the bbt area + * Description: + * Check, if the block is bad. Either by reading the bad block table or + * reading bad block marker. + */ +static int spinand_block_checkbad(struct mtd_info *mtd, loff_t offs, + int allowbbt) +{ + struct nand_device *nand = mtd_to_nand(mtd); + int ret; + + if (nand_bbt_is_initialized(nand)) + ret = nand_isbad_bbt(nand, offs, 0); + else + ret = spinand_block_bad(mtd, offs); + + return ret; +} + +/* + * spinand_block_isbad - [MTD Interface] check if block at offset is bad + * @mtd: MTD device structure + * @offs: offset from device start + */ +static int spinand_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct spinand_device *chip = mtd_to_spinand(mtd); + int ret; + + mutex_lock(&chip->lock); + ret = spinand_block_checkbad(mtd, offs, 0); + mutex_unlock(&chip->lock); + + return ret; +} + +/* + * spinand_block_markbad_lowlevel - mark a block bad + * @mtd: MTD device structure + * @offs: offset from device start + * + * This function performs the generic bad block marking steps (i.e., bad + * block table(s) and/or marker(s)). + * + * We try operations in the following order: + * (1) erase the affected block, to allow OOB marker to be written cleanly + * (2) write bad block marker to OOB area of affected block (unless flag + * NAND_BBT_NO_OOB_BBM is present) + * (3) update the BBT + */ +static int spinand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t offs) +{ + struct nand_device *nand = mtd_to_nand(mtd); + struct mtd_oob_ops ops = {0}; + struct erase_info einfo = {0}; + u32 block_addr; + u8 buf[2] = {0, 0}; + int res, ret = 0; + + if (!nand_bbt_is_initialized(nand) || + !(nand->bbt.options & NAND_BBT_NO_OOB_BBM)) { + /*erase bad block before mark bad block*/ + einfo.mtd = mtd; + einfo.addr = offs; + einfo.len = nand_eraseblock_size(nand); + spinand_erase_skip_bbt(mtd, &einfo); + + block_addr = nand_offs_to_eraseblock(nand, offs); + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooblen = 2; + ops.oobbuf = buf; + ret = spinand_do_write_ops(mtd, + nand_eraseblock_to_offs(nand, + block_addr), + &ops); + } + + /* Mark block bad in BBT */ + if (nand_bbt_is_initialized(nand)) { + res = nand_markbad_bbt(nand, offs); + if (!ret) + ret = res; + } + + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} + +/* + * spinand_block_markbad - [MTD Interface] mark block at the given offset + * as bad + * @mtd: MTD device structure + * @offs: offset relative to mtd start + */ +static int spinand_block_markbad(struct mtd_info *mtd, loff_t offs) +{ + int ret; + + ret = spinand_block_isbad(mtd, offs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + return spinand_block_markbad_lowlevel(mtd, offs); +} + +/* + * spinand_erase - erase block(s) * @mtd: MTD device structure * @einfo: erase instruction + * @allowbbt: allow to access bbt */ -static int spinand_erase(struct mtd_info *mtd, struct erase_info *einfo) +static int spinand_erase(struct mtd_info *mtd, struct erase_info *einfo, + int allowbbt) { struct spinand_device *chip = mtd_to_spinand(mtd); struct nand_device *nand = mtd_to_nand(mtd); @@ -893,6 +1039,13 @@ static int spinand_erase(struct mtd_info *mtd, struct erase_info *einfo) einfo->state = MTD_ERASING; while (len) { + /* Check if we have a bad block, we do not erase bad blocks! */ + if (spinand_block_checkbad(mtd, offs, allowbbt)) { + pr_warn("%s: attempt to erase a bad block at 0x%012llx\n", + __func__, offs); + einfo->state = MTD_ERASE_FAILED; + goto erase_exit; + } spinand_write_enable(chip); spinand_erase_block(chip, nand_offs_to_page(nand, offs)); ret = spinand_wait(chip, &status); @@ -929,6 +1082,33 @@ static int spinand_erase(struct mtd_info *mtd, struct erase_info *einfo) } /* + * spinand_erase_skip_bbt - [MTD Interface] erase block(s) except BBT + * @mtd: MTD device structure + * @einfo: erase instruction + */ +static int spinand_erase_skip_bbt(struct mtd_info *mtd, + struct erase_info *einfo) +{ + return spinand_erase(mtd, einfo, 0); +} + +/* + * spinand_block_isreserved - [MTD Interface] check if a block is + * marked reserved. + * @mtd: MTD device structure + * @offs: offset from device start + */ +static int spinand_block_isreserved(struct mtd_info *mtd, loff_t offs) +{ + struct nand_device *nand = mtd_to_nand(mtd); + + if (!nand_bbt_is_initialized(nand)) + return 0; + /* Return info from the table */ + return nand_isreserved_bbt(nand, offs); +} + +/* * spinand_set_rd_wr_op - choose the best read write command * @chip: SPI NAND device structure * Description: @@ -963,6 +1143,100 @@ static void spinand_set_rd_wr_op(struct spinand_device *chip) } /* + * spinand_erase_bbt - erase block(s) including BBT + * @nand: nand device structure + * @einfo: erase instruction + */ +static int spinand_erase_bbt(struct nand_device *nand, + struct erase_info *einfo) +{ + return spinand_erase(nand_to_mtd(nand), einfo, 1); +} + +/* + * spinand_erase_bbt - write bad block marker to certain block + * @nand: nand device structure + * @block: block to mark bad + */ +static int spinand_markbad(struct nand_device *nand, int block) +{ + struct mtd_oob_ops ops = {0}; + u8 buf[2] = {0, 0}; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = 2; + ops.oobbuf = buf; + + return spinand_do_write_ops(nand_to_mtd(nand), + nand_eraseblock_to_offs(nand, block), + &ops); +} + +static const struct nand_ops spinand_ops = { + .erase = spinand_erase_bbt, + .markbad = spinand_markbad, +}; + +/* + * Define some generic bad/good block scan pattern which are used + * while scanning a device for factory marked good/bad blocks. + */ +static u8 scan_ff_pattern[] = { 0xff, 0xff }; + +#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) + +/* + * spinand_create_badblock_pattern - creates a BBT descriptor structure + * @chip: SPI NAND device structure + * + * This function allocates and initializes a nand_bbt_descr for BBM detection. + * The new descriptor is stored in nand->bbt.bbp. Thus, nand->bbt.bbp should + * be NULL when passed to this function. + */ +static int spinand_create_badblock_pattern(struct spinand_device *chip) +{ + struct nand_device *nand = &chip->base; + struct nand_bbt_descr *bd; + + if (nand->bbt.bbp) { + pr_warn("Bad block pattern already allocated; not replacing\n"); + return -EINVAL; + } + bd = kzalloc(sizeof(*bd), GFP_KERNEL); + if (!bd) + return -ENOMEM; + bd->options = nand->bbt.options & BADBLOCK_SCAN_MASK; + bd->offs = 0; + bd->len = 2; + bd->pattern = scan_ff_pattern; + bd->options |= NAND_BBT_DYNAMICSTRUCT; + nand->bbt.bbp = bd; + + return 0; +} + +/* + * spinand_scan_bbt - scan BBT in SPI NAND device + * @chip: SPI NAND device structure + */ +static int spinand_scan_bbt(struct spinand_device *chip) +{ + struct nand_device *nand = &chip->base; + int ret; + + nand->bbt.options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + nand->bbt.td = NULL; + nand->bbt.md = NULL; + + ret = spinand_create_badblock_pattern(chip); + if (ret) + return ret; + + return nand_scan_bbt(nand); +} + +/* * spinand_manufacturer_detect - detect SPI NAND device by each manufacturer * @chip: SPI NAND device structure * @raw_id: raw id buffer. raw id is read by spinand_read_id(), should be @@ -1120,6 +1394,7 @@ int spinand_init(struct spinand_device *chip) return -ENOMEM; chip->oobbuf = chip->buf + nand_page_size(nand); + nand->ops = &spinand_ops; mtd->name = chip->name; mtd->size = nand_size(nand); mtd->erasesize = nand_eraseblock_size(nand); @@ -1137,11 +1412,14 @@ int spinand_init(struct spinand_device *chip) if (ret < 0) ret = 0; mtd->oobavail = ret; - mtd->_erase = spinand_erase; + mtd->_erase = spinand_erase_skip_bbt; mtd->_read = spinand_read; mtd->_write = spinand_write; mtd->_read_oob = spinand_read_oob; mtd->_write_oob = spinand_write_oob; + mtd->_block_isbad = spinand_block_isbad; + mtd->_block_markbad = spinand_block_markbad; + mtd->_block_isreserved = spinand_block_isreserved; if (!mtd->bitflip_threshold) mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, @@ -1149,7 +1427,8 @@ int spinand_init(struct spinand_device *chip) /* After power up, all blocks are locked, so unlock it here. */ spinand_lock_block(chip, BL_ALL_UNLOCKED); - return 0; + /* Build bad block table */ + return spinand_scan_bbt(chip); } EXPORT_SYMBOL_GPL(spinand_init); @@ -1159,8 +1438,14 @@ int spinand_init(struct spinand_device *chip) */ int spinand_cleanup(struct spinand_device *chip) { + struct nand_device *nand = &chip->base; + struct nand_bbt_descr *bd = nand->bbt.bbp; + spinand_manufacturer_cleanup(chip); kfree(chip->buf); + kfree(nand->bbt.bbt); + if (bd->options & NAND_BBT_DYNAMICSTRUCT) + kfree(bd); return 0; }