Changelog: * https://www.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.1.5 Signed-off-by: Jonas Gorski <jogo@openwrt.org> SVN-Revision: 46598
		
			
				
	
	
		
			262 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From bec69bb8e85151729014d859106dcc3fe652b1d4 Mon Sep 17 00:00:00 2001
 | 
						|
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
 | 
						|
Date: Mon, 28 Jul 2014 14:45:40 +0200
 | 
						|
Subject: [PATCH] mtd: nand: Add page status table (pst)
 | 
						|
 | 
						|
Page status table is an byte array storing pages status.
 | 
						|
It defines 3 status:
 | 
						|
 - unknown: the page has not been read yet and we do not know its current
 | 
						|
   state
 | 
						|
 - empty: the page contains only FFs
 | 
						|
 - filled: the page has been filled with data
 | 
						|
 | 
						|
Care must be taken: an empty page does not mean it can be written, because
 | 
						|
it might have already been written with only FFs.
 | 
						|
 | 
						|
These page status are useful to check wether the controller should try to
 | 
						|
correct errors (using ECC) or a derandomize data (using a randomizer
 | 
						|
block).
 | 
						|
 | 
						|
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
 | 
						|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
						|
---
 | 
						|
 drivers/mtd/nand/nand_base.c | 154 +++++++++++++++++++++++++++++++++++++++++++
 | 
						|
 include/linux/mtd/nand.h     |  21 ++++++
 | 
						|
 2 files changed, 175 insertions(+)
 | 
						|
 | 
						|
--- a/drivers/mtd/nand/nand_base.c
 | 
						|
+++ b/drivers/mtd/nand/nand_base.c
 | 
						|
@@ -1102,6 +1102,138 @@ out:
 | 
						|
 EXPORT_SYMBOL(nand_lock);
 | 
						|
 
 | 
						|
 /**
 | 
						|
+ * 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 * BITS_PER_BYTE; i++) {
 | 
						|
+				if (!(buf[i / BITS_PER_BYTE] &
 | 
						|
+				      (1 << (i % BITS_PER_BYTE)))) {
 | 
						|
+					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 * BITS_PER_BYTE; i++) {
 | 
						|
+				if (!(buf[i / BITS_PER_BYTE] &
 | 
						|
+				      (1 << (i % BITS_PER_BYTE)))) {
 | 
						|
+					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
 | 
						|
@@ -2539,6 +2671,7 @@ static int nand_do_write_ops(struct mtd_
 | 
						|
 		uint8_t *wbuf = buf;
 | 
						|
 		int use_bufpoi;
 | 
						|
 		int part_pagewr = (column || writelen < (mtd->writesize - 1));
 | 
						|
+		int subpage;
 | 
						|
 
 | 
						|
 		if (part_pagewr)
 | 
						|
 			use_bufpoi = 1;
 | 
						|
@@ -2574,6 +2707,14 @@ static int nand_do_write_ops(struct mtd_
 | 
						|
 		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;
 | 
						|
@@ -2979,6 +3120,7 @@ int nand_erase_nand(struct mtd_info *mtd
 | 
						|
 	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,
 | 
						|
@@ -3051,6 +3193,18 @@ int nand_erase_nand(struct mtd_info *mtd
 | 
						|
 			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;
 | 
						|
--- a/include/linux/mtd/nand.h
 | 
						|
+++ b/include/linux/mtd/nand.h
 | 
						|
@@ -521,6 +521,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.
 | 
						|
@@ -630,6 +648,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.
 | 
						|
@@ -718,6 +737,8 @@ struct nand_chip {
 | 
						|
 
 | 
						|
 	struct nand_bbt_descr *badblock_pattern;
 | 
						|
 
 | 
						|
+	uint8_t *pst;
 | 
						|
+
 | 
						|
 	struct list_head partitions;
 | 
						|
 	struct mutex part_lock;
 | 
						|
 
 |