mtd: nand: add generic READ ID length calculation functions

Linux-MTD Mailing List linux-mtd at lists.infradead.org
Sat Sep 29 10:59:55 EDT 2012


Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=e3b88bd604283ef83ae6e8f53622d5b1ffe9d43a
Commit:     e3b88bd604283ef83ae6e8f53622d5b1ffe9d43a
Parent:     f23a481c4e0ccb006470b1c890cc7236ba634e67
Author:     Brian Norris <computersforpeace at gmail.com>
AuthorDate: Mon Sep 24 20:40:52 2012 -0700
Committer:  David Woodhouse <David.Woodhouse at intel.com>
CommitDate: Sat Sep 29 15:57:58 2012 +0100

    mtd: nand: add generic READ ID length calculation functions
    
    When decoding the extended ID bytes of a NAND chip, we have to calculate the ID
    length according to some heuristic patterns (e.g., Does the ID wrap around?
    Does it end in trailing zeros?). Currently, these heuristics are built into
    complicated if/else blocks that can be hard to understand.
    
    Now, these checks can be done generically in a function, making them more
    robust and reusable. In fact, this sort of calculation is needed in future
    additions to nand_base.c. And with this advancement, we get the added benefit
    of a more readable "extended ID decode".
    
    Signed-off-by: Brian Norris <computersforpeace at gmail.com>
    Signed-off-by: Artem Bityutskiy <artem.bityutskiy at linux.intel.com>
    Signed-off-by: David Woodhouse <David.Woodhouse at intel.com>
---
 drivers/mtd/nand/nand_base.c |   72 +++++++++++++++++++++++++++++++++++++----
 1 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 4e1ea72..5365ad5 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2906,6 +2906,65 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
 }
 
 /*
+ * nand_id_has_period - Check if an ID string has a given wraparound period
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+ * @period: the period of repitition
+ *
+ * Check if an ID string is repeated within a given sequence of bytes at
+ * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a
+ * period of 2). This is a helper function for nand_id_len(). Returns non-zero
+ * if the repetition has a period of @period; otherwise, returns zero.
+ */
+static int nand_id_has_period(u8 *id_data, int arrlen, int period)
+{
+	int i, j;
+	for (i = 0; i < period; i++)
+		for (j = i + period; j < arrlen; j += period)
+			if (id_data[i] != id_data[j])
+				return 0;
+	return 1;
+}
+
+/*
+ * nand_id_len - Get the length of an ID string returned by CMD_READID
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+
+ * Returns the length of the ID string, according to known wraparound/trailing
+ * zero patterns. If no pattern exists, returns the length of the array.
+ */
+static int nand_id_len(u8 *id_data, int arrlen)
+{
+	int last_nonzero, period;
+
+	/* Find last non-zero byte */
+	for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--)
+		if (id_data[last_nonzero])
+			break;
+
+	/* All zeros */
+	if (last_nonzero < 0)
+		return 0;
+
+	/* Calculate wraparound period */
+	for (period = 1; period < arrlen; period++)
+		if (nand_id_has_period(id_data, arrlen, period))
+			break;
+
+	/* There's a repeated pattern */
+	if (period < arrlen)
+		return period;
+
+	/* There are trailing zeros */
+	if (last_nonzero < arrlen - 1)
+		return last_nonzero + 1;
+
+	/* No pattern detected */
+	return arrlen;
+}
+
+/*
  * Many new NAND share similar device ID codes, which represent the size of the
  * chip. The rest of the parameters must be decoded according to generic or
  * manufacturer-specific "extended ID" decoding patterns.
@@ -2913,24 +2972,23 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
 static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
 				u8 id_data[8], int *busw)
 {
-	int extid;
+	int extid, id_len;
 	/* The 3rd id byte holds MLC / multichip data */
 	chip->cellinfo = id_data[2];
 	/* The 4th id byte is the important one */
 	extid = id_data[3];
 
+	id_len = nand_id_len(id_data, 8);
+
 	/*
 	 * Field definitions are in the following datasheets:
 	 * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
 	 * New style   (6 byte ID): Samsung K9GBG08U0M (p.40)
 	 *
-	 * Check for wraparound + Samsung ID + nonzero 6th byte
-	 * to decide what to do.
+	 * Check for ID length + Samsung ID to decide what to do.
 	 */
-	if (id_data[0] == id_data[6] && id_data[1] == id_data[7] &&
-			id_data[0] == NAND_MFR_SAMSUNG &&
-			(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
-			id_data[5] != 0x00) {
+	if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG &&
+			(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
 		/* Calc pagesize */
 		mtd->writesize = 2048 << (extid & 0x03);
 		extid >>= 2;



More information about the linux-mtd-cvs mailing list