From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1D2AEC433E0 for ; Thu, 14 May 2020 21:24:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DAC4A20709 for ; Thu, 14 May 2020 21:24:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Efi7MgMQ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728243AbgENVYH (ORCPT ); Thu, 14 May 2020 17:24:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43892 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728043AbgENVX6 (ORCPT ); Thu, 14 May 2020 17:23:58 -0400 Received: from mail-wm1-x341.google.com (mail-wm1-x341.google.com [IPv6:2a00:1450:4864:20::341]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2DDB5C061A0C for ; Thu, 14 May 2020 14:23:58 -0700 (PDT) Received: by mail-wm1-x341.google.com with SMTP id z72so25361027wmc.2 for ; Thu, 14 May 2020 14:23:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=BgJlONPQ+8Wy/hHv7eukI06o8qynIDksuk6QpOQmekU=; b=Efi7MgMQHoOtsPGdUpr4qirQWBXjvBgRNem3oNBzPIdxnPfEPuIm7oMun+hK+gOlvr ZgEJdLj/vny6ZxXu0uvuEmXG3zHYrxW7p3AjK46C5ChYlMEaxMZfqytY27nAkzewLkQn 4MTWk2csoECKbYqIpwQ4Uj16WqPNRAFEbKI95Rpq2ROVUYNYy6cS9POTPlbTgdJbkN2f d9mRHUmnh1VFPb1os5fDvbCeU4ymXEDvPKvrW+PnDX0H9m6Y/2FLJm5+xb/4OEUV9lbt ExuCgejpHOuPrSIwqpbMLweWWCtTS8YJHm/XcUF6fiFulD1Hp27BMrqxC6lNsdyob2qA VYIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=BgJlONPQ+8Wy/hHv7eukI06o8qynIDksuk6QpOQmekU=; b=Bp+aA77Lsx+bxntjWmR+9fHhSWAnYm+4c1cc8DyM/ZokMlEoOgT9i79ZQCwCfE1l6W Jgto7/gewGKWLsvaudNKsQ/qZygNxWMRbn/Lql28SuPREctykf7CtID2peaGR2p9mEwh c/HZFoRsDr56Lj6Us8mnbVCLHK83DNvX2MIZdvaGDxcnWTFZ/EXD1hHgsQOP1AYizMID GB2E745EMXLz48SC/q4Z+Nb/0v5G7aWz/deegH1JMsA7lhlA79l/xMu4M0t+vA3jXWx2 Soy+JeeyeiPRdW+h8Ld2FHYzvzsDSB10e50CijQ/8qShU7489+EgG7ogy2NbGkAMsoI0 orxg== X-Gm-Message-State: AOAM532TtKw9LJxCgRoZaml0tLLGWlLOedq4GSMBY39d+e1q7NB8bhCG MCiZWTskqGBUyv+9uoxm67I= X-Google-Smtp-Source: ABdhPJxb3MMcVKjg7lUD9RN+Jjag/+B/b+9ipQIsMT5BV/RQN2DoVBwXBgnj53O3bOkuQK5PNZROyw== X-Received: by 2002:a1c:b354:: with SMTP id c81mr320665wmf.136.1589491436912; Thu, 14 May 2020 14:23:56 -0700 (PDT) Received: from localhost.localdomain (ip5f5bfcc8.dynamic.kabel-deutschland.de. [95.91.252.200]) by smtp.gmail.com with ESMTPSA id 128sm491210wme.39.2020.05.14.14.23.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 14 May 2020 14:23:56 -0700 (PDT) From: huobean@gmail.com X-Google-Original-From: beanhuo@micron.com To: miquel.raynal@bootlin.com, richard@nod.at, vigneshr@ti.com, s.hauer@pengutronix.de, boris.brezillon@collabora.com, derosier@gmail.com Cc: linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org, Bean Huo Subject: [PATCH v3 5/5] mtd: rawnand: micron: Micron SLC NAND filling block Date: Thu, 14 May 2020 23:23:33 +0200 Message-Id: <20200514212333.28692-6-beanhuo@micron.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200514212333.28692-1-beanhuo@micron.com> References: <20200514212333.28692-1-beanhuo@micron.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Bean Huo On some legacy planar 2D Micron NAND devices when a block erase command is issued, occasionally even though a block erase operation completes and returns a pass status, the flash block may not be completely erased. Subsequent operations to this block on very rare cases can result in subtle failures or corruption. These extremely rare cases should nevertheless be considered. These rare occurrences have been observed on partially written blocks. To avoid this rare occurrence, we should make sure that at least 15 pages have been programmed to a block before it is erased. In case we find that less than 15 pages have been programmed, we will rewrite first 15 pages of block. Signed-off-by: Bean Huo --- drivers/mtd/nand/raw/nand_micron.c | 102 +++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index d30bc944f729..c4fbe1eada18 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -36,6 +36,9 @@ #define NAND_ECC_STATUS_1_3_CORRECTED BIT(4) #define NAND_ECC_STATUS_7_8_CORRECTED (BIT(4) | BIT(3)) +#define MICRON_SHALLOW_ERASE_MIN_PAGE 15 +#define MICRON_PAGE_MASK_TRIGGER GENMASK(MICRON_SHALLOW_ERASE_MIN_PAGE, 0) + struct nand_onfi_vendor_micron { u8 two_plane_read; u8 read_cache; @@ -64,6 +67,7 @@ struct micron_on_die_ecc { struct micron_nand { struct micron_on_die_ecc ecc; + u16 *writtenp; }; static int micron_nand_setup_read_retry(struct nand_chip *chip, int retry_mode) @@ -429,6 +433,93 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) return MICRON_ON_DIE_SUPPORTED; } +static int micron_nand_pre_erase(struct nand_chip *chip, u32 eraseblock) +{ + struct micron_nand *micron = nand_get_manufacturer_data(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + u8 last_page = MICRON_SHALLOW_ERASE_MIN_PAGE - 1; + u32 page; + u8 *data_buf; + int ret, i; + + data_buf = nand_get_data_buf(chip); + WARN_ON(!data_buf); + + if (likely(micron->writtenp[eraseblock] & BIT(last_page))) + return 0; + + page = eraseblock << (chip->phys_erase_shift - chip->page_shift); + + if (unlikely(micron->writtenp[eraseblock] == 0)) { + ret = nand_read_page_raw(chip, data_buf, 1, page + last_page); + if (ret) + return ret; /* Read error */ + ret = nand_check_is_erased_page(chip, data_buf, true); + if (!ret) + return 0; + } + + memset(data_buf, 0x00, mtd->writesize); + + for (i = 0; i < MICRON_SHALLOW_ERASE_MIN_PAGE; i++) { + ret = nand_write_page_raw(chip, data_buf, false, page + i); + if (ret) + return ret; + } + + return 0; +} + +static int micron_nand_post_erase(struct nand_chip *chip, u32 eraseblock) +{ + struct micron_nand *micron = nand_get_manufacturer_data(chip); + + if (!micron) + return -EINVAL; + + micron->writtenp[eraseblock] = 0; + + return 0; +} + +static int micron_nand_write_oob(struct nand_chip *chip, loff_t to, + struct mtd_oob_ops *ops) +{ + struct micron_nand *micron = nand_get_manufacturer_data(chip); + u32 eb_sz = nanddev_eraseblock_size(&chip->base); + u32 p_sz = nanddev_page_size(&chip->base); + u32 ppeb = nanddev_pages_per_eraseblock(&chip->base); + u32 nb_p_tot = ops->len / p_sz; + u32 first_eb = DIV_ROUND_DOWN_ULL(to, eb_sz); + u32 first_p = DIV_ROUND_UP_ULL(to - (first_eb * eb_sz), p_sz); + u32 nb_eb = DIV_ROUND_UP_ULL(first_p + nb_p_tot, ppeb); + u32 remaining_p, eb, nb_p; + int ret; + + ret = nand_write_oob_nand(chip, to, ops); + + if (ret || ops->len != ops->retlen) + return ret; + + /* Mark the last pages of the first erase block to write */ + nb_p = min(nb_p_tot, ppeb - first_p); + micron->writtenp[first_eb] |= GENMASK(first_p + nb_p, first_p) & + MICRON_PAGE_MASK_TRIGGER; + remaining_p = nb_p_tot - nb_p; + + /* Mark all the pages of all "in-the-middle" erase blocks */ + for (eb = first_eb + 1; eb < first_eb + nb_eb - 1; eb++) { + micron->writtenp[eb] |= MICRON_PAGE_MASK_TRIGGER; + remaining_p -= ppeb; + } + + /* Mark the first pages of the last erase block to write */ + if (remaining_p) + micron->writtenp[eb] |= GENMASK(remaining_p - 1, 0) & + MICRON_PAGE_MASK_TRIGGER; + return 0; +} + static int micron_nand_init(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); @@ -513,6 +604,17 @@ static int micron_nand_init(struct nand_chip *chip) } } + if (nand_is_slc(chip)) { + micron->writtenp = kcalloc(nanddev_neraseblocks(&chip->base), + sizeof(u16), GFP_KERNEL); + if (!micron->writtenp) + goto err_free_manuf_data; + + chip->ops.write_oob = micron_nand_write_oob; + chip->ops.pre_erase = micron_nand_pre_erase; + chip->ops.post_erase = micron_nand_post_erase; + } + return 0; err_free_manuf_data: -- 2.17.1