From patchwork Fri Jun 5 11:52:37 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roy Spliet X-Patchwork-Id: 481158 X-Patchwork-Delegate: scottwood@freescale.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 75FF7140218 for ; Fri, 5 Jun 2015 21:53:45 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 9C5364B6CB; Fri, 5 Jun 2015 13:53:25 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id FHDkT-f_RfeN; Fri, 5 Jun 2015 13:53:25 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4B2744B6CD; Fri, 5 Jun 2015 13:53:11 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 114AB4B660 for ; Fri, 5 Jun 2015 13:53:08 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id l2LttoN7kaDn for ; Fri, 5 Jun 2015 13:53:07 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-wi0-f169.google.com (mail-wi0-f169.google.com [209.85.212.169]) by theia.denx.de (Postfix) with ESMTPS id 8B1D74B66A for ; Fri, 5 Jun 2015 13:52:58 +0200 (CEST) Received: by wiwd19 with SMTP id d19so18631212wiw.0 for ; Fri, 05 Jun 2015 04:52:58 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:to:cc:subject:date:message-id :in-reply-to:references:content-type; bh=XTGi3qt/Mu7b8YFii88zMCL7bXPTo1i1duXa6j/OkHA=; b=S6i5cN5l24NHzqZooBMuynp46xkF9at0Lm2KUKjw9T9d902au+1rTGxlBc77mqn+e+ 4KYWbE+vCd/vR8t1VjRTqo8STy2W/ZOjlfH/jGxCi3ryggjKMVK6M6kKY09YJ5hST16H +MSl4/pADi9Wa71VyG/QuTomvzxIxyDH+nuUaojD5wmzp/h3Z+Aama/MiJ1QR/GCWQsm U3O6tdEYVaVBRgs7y1ADkugtEkQcSpxNq4IMOAyX3O2Zpk/Ey8S60ieNY01JnB8OQtnC mQGGo5pnO72BZk0+F0eshFJpNky++52Ujch/2stnEEtuNM1j6g0kfnimj5xohUwOk3Es RApQ== X-Gm-Message-State: ALoCoQkee5PdUzsne/n8SBGlJfczQZbS9+VADWoiR6nM6KANrrWd2Av61quCjjRv3T9Zct3rwCnIqP7V3FofmKuYerq0TJpxMk33759Cf+VHk5cjFcQkEpJ1Dj0Bds4uXzSUsXTStB9b MIME-Version: 1.0 X-Received: by 10.180.91.17 with SMTP id ca17mr63090535wib.9.1433505178656; Fri, 05 Jun 2015 04:52:58 -0700 (PDT) Received: from Seven.fritz.box (a83-163-237-212.adsl.xs4all.nl. [83.163.237.212]) by mx.google.com with ESMTPSA id d3sm2949692wic.1.2015.06.05.04.52.57 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 05 Jun 2015 04:52:57 -0700 (PDT) From: Roy Spliet To: U-Boot Mailinglist , Sunxi Mailinglist Date: Fri, 5 Jun 2015 13:52:37 +0200 Message-Id: <1433505164-24112-5-git-send-email-r.spliet@ultimaker.com> X-Mailer: git-send-email 2.4.2 In-Reply-To: <1433505164-24112-1-git-send-email-r.spliet@ultimaker.com> References: <1433505164-24112-1-git-send-email-r.spliet@ultimaker.com> Cc: Daniel Kochmanski , Roy Spliet , Ian Campbell Subject: [U-Boot] [RFC 04/11] mtd/nand: add page status table (pst) X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: yassin Signed-off-by: Roy Spliet --- drivers/mtd/nand/nand_base.c | 154 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 21 ++++++ 2 files changed, 175 insertions(+) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dbeb092..1c514a0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1185,6 +1185,138 @@ EXPORT_SYMBOL(nand_lock); #endif /** + * nand_page_is_empty - check wether a NAND page contains only FFs + * @mtd: mtd info + * @data: data buffer + * @oob: oob buffer + * + * Reads the data stored in the databuf buffer and check if it contains only + * FFs. + * + * Return true if it does else return false. + */ +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob) +{ + u8 *buf; + int length; + u32 pattern = 0xffffffff; + int bitflips = 0; + int cnt; + + buf = data; + length = mtd->writesize; + while (length) { + cnt = length < sizeof(pattern) ? length : sizeof(pattern); + if (memcmp(&pattern, buf, cnt)) { + int i; + for (i = 0; i < cnt * 8; i++) { + if (!(buf[i / 8] & + (1 << (i % 8)))) { + bitflips++; + if (bitflips > mtd->ecc_strength) + return false; + } + } + } + + buf += sizeof(pattern); + length -= sizeof(pattern); + } + + buf = oob; + length = mtd->oobsize; + while (length) { + cnt = length < sizeof(pattern) ? length : sizeof(pattern); + if (memcmp(&pattern, buf, cnt)) { + int i; + for (i = 0; i < cnt * 8; i++) { + if (!(buf[i / 8] & + (1 << (i % 8)))) { + bitflips++; + if (bitflips > mtd->ecc_strength) + return false; + } + } + } + + buf += sizeof(pattern); + length -= sizeof(pattern); + } + + return true; +} +EXPORT_SYMBOL(nand_page_is_empty); + +/** + * nand_page_get_status - retrieve page status from the page status table (pst) + * @mtd: mtd info + * @page: page you want to get status on + * + * Return the page status. + */ +int nand_page_get_status(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + u8 shift = (page % 4) * 2; + uint64_t offset = page / 4; + int ret = NAND_PAGE_STATUS_UNKNOWN; + + if (chip->pst) + ret = (chip->pst[offset] >> shift) & 0x3; + + return ret; +} +EXPORT_SYMBOL(nand_page_get_status); + +/** + * nand_page_set_status - assign page status from in the page status table + * @mtd: mtd info + * @page: page you want to get status on + * @status: new status to assign + */ +void nand_page_set_status(struct mtd_info *mtd, int page, + enum nand_page_status status) +{ + struct nand_chip *chip = mtd->priv; + u8 shift; + uint64_t offset; + + if (!chip->pst) + return; + + shift = (page % 4) * 2; + offset = page / 4; + chip->pst[offset] &= ~(0x3 << shift); + chip->pst[offset] |= (status & 0x3) << shift; +} +EXPORT_SYMBOL(nand_page_set_status); + +/** + * nand_pst_create - create a page status table + * @mtd: mtd info + * + * Allocate a page status table and assign it to the mtd device. + * + * Returns 0 in case of success or -ERRNO in case of error. + */ +int nand_pst_create(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->pst) + return 0; + + chip->pst = kzalloc(mtd->size >> + (chip->page_shift + mtd->subpage_sft + 2), + GFP_KERNEL); + if (!chip->pst) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(nand_pst_create); + +/** * nand_read_page_raw - [INTERN] read raw page data without ecc * @mtd: mtd info structure * @chip: nand chip info structure @@ -2521,6 +2653,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; uint8_t *wbuf = buf; + int subpage; WATCHDOG_RESET(); /* Partial page write? */ @@ -2547,6 +2680,14 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (ret) break; + for (subpage = column / chip->subpagesize; + subpage < (column + writelen) / chip->subpagesize; + subpage++) + nand_page_set_status(mtd, + (page << mtd->subpage_sft) + + subpage, + NAND_PAGE_FILLED); + writelen -= bytes; if (!writelen) break; @@ -2804,6 +2945,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; loff_t len; + int i; pr_debug("%s: start = 0x%012llx, len = %llu\n", __func__, (unsigned long long)instr->addr, @@ -2880,6 +3022,18 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, goto erase_exit; } + for (i = 0; i < pages_per_block; i++) { + int subpage; + for (subpage = 0; + subpage < 1 << mtd->subpage_sft; + subpage++) { + nand_page_set_status(mtd, + ((page + i) << mtd->subpage_sft) + + subpage, + NAND_PAGE_EMPTY); + } + } + /* Increment page address and decrement length */ len -= (1ULL << chip->phys_erase_shift); page += pages_per_block; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0cdb3b9..ef6a783 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -547,6 +547,24 @@ struct nand_ecc_ctrl { int page); }; +/* + * Constants for page status + */ +enum nand_page_status { + NAND_PAGE_STATUS_UNKNOWN, + NAND_PAGE_EMPTY, + NAND_PAGE_FILLED, +}; + +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob); + +int nand_page_get_status(struct mtd_info *mtd, int page); + +void nand_page_set_status(struct mtd_info *mtd, int page, + enum nand_page_status status); + +int nand_pst_create(struct mtd_info *mtd); + /** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize. @@ -660,6 +678,7 @@ struct nand_buffers { * @bbt_md: [REPLACEABLE] bad block table mirror descriptor * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial * bad block scan. + * @pst: [INTERN] page status table * @controller: [REPLACEABLE] a pointer to a hardware controller * structure which is shared among multiple independent * devices. @@ -750,6 +769,8 @@ struct nand_chip { struct nand_bbt_descr *badblock_pattern; + uint8_t *pst; + void *priv; };