All of lore.kernel.org
 help / color / mirror / Atom feed
From: Angus CLARK <angus.clark@st.com>
To: linux-mtd@lists.infradead.org
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Subject: Re: nand_base.c:nand_get_flash_type() test results
Date: Fri, 14 Oct 2011 16:14:51 +0100	[thread overview]
Message-ID: <4E9851EB.8070909@st.com> (raw)
In-Reply-To: <1318600673.12351.131.camel@sauron>

[-- Attachment #1: Type: text/plain, Size: 757 bytes --]

On 10/14/2011 02:57 PM, Artem Bityutskiy wrote:
> On Wed, 2011-10-12 at 16:26 +0100, Angus CLARK wrote:
>> Hi All,
>>
>> While attempting to add support for some of the more recent NAND devices, I
>> ended up refactoring nand_get_flash_type() code.  The refactoring was primarily
>> aimed at simplifying the way in which the growing number 'READID' decoding
>> exceptions could be accommodated.
> 
> Why not to send the refactored code as well?
> 

Well, mostly to avoid criticism of it not being based on the mtd-2.6 tree :-)  I
have attached the file 'nand_id_decode.c' which includes most of the relevant
code.  If there is interest, I am happy to port to mtd-2.6, but testing on this
version would be limited to compilation and nandsim.

Cheers,

Angus

[-- Attachment #2: nand_id_decode.c --]
[-- Type: text/plain, Size: 13346 bytes --]

/*
 *  drivers/mtd/nand/nand_id_decode.c
 *
 *  Determine NAND device properties by decoding the READID string
 *
 *  The following code attempts to encapsulate the knowledge in
 *  mtd-2.6(2011-07-09):nand_base.c:nand_get_flash_type(), refactored according
 *  to three basic schemes:
 *
 *	'ID 2'       : extract properties from device table.  For 2-byte IDs,
 *                     or where device ID gives a non-zero page-size
 *                     (SP devices).
 *
 *	'Extended ID': decode properties from ID string where possible, falling
 *	               back to device table.  For 3/4/5-byte IDs.
 *
 *	'ID 6'       : decode properties from ID string where possible, falling
 *	               back to device table.  For 6-byte IDs.
 *
 *  The refactoring is primarily aimed at simplifying the way in which decoding
 *  exceptions (growing fast!) can be accomodated.
 *
 *  Tested on all devices found in
 *  http://www.linux-mtd.infradead.org/nand-data/nanddata.html (2011-07-09),
 *  EXCLUDING the following:
 *
 *      - 'ONFI-only':  READID is insufficient to decode all properties.
 *
 *      - Toshiba devices: datasheets only present decode table, not actual
 *                         READID strings.
 *
 *
 *  Author: Angus Clark <Angus.Clark@st.com>
 *  Copyright (c) 2012 STMicroelectronics Limited
 * 
 *  ------------------------------------------------------------------------
 *  May be copied or modified under the terms of the GNU General Public
 *  License Version 2.0 only.  See linux/COPYING for more information.
 *  ------------------------------------------------------------------------
 *
 */

#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>

static struct nand_flash_dev *search_nand_flash_dev(uint8_t id);
static int nand_decode_id_2(struct mtd_info *mtd,
			    struct nand_chip *chip,
			    struct nand_flash_dev *type,
			    uint8_t *id, int id_len);
static int nand_decode_id_ext(struct mtd_info *mtd,
			      struct nand_chip *chip,
			      struct nand_flash_dev *type,
			      uint8_t *id, int id_len);
static int nand_decode_id_6(struct mtd_info *mtd,
			    struct nand_chip *chip,
			    struct nand_flash_dev *type,
			    uint8_t *id, int id_len);
static void nand_decode_id_bbm(struct mtd_info *mtd,
			       struct nand_chip *chip,
			       uint8_t *id, int id_len);

int nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
		   uint8_t *id, int id_len)
{
	struct nand_flash_dev *type = NULL;
	int ret = 0;

	/*
	 * Find 'nand_flash_dev' in table
	 */
	type = search_nand_flash_dev(id[1]);
	if (!type) {
		pr_err("[MTD][NAND]:%s: failed to find NAND "
		       "device ID [%02x] in table\n", __func__, id[1]);
		return 1;
	}

	/*
	 * Decode ID string
	 */
	if (id_len == 2 || type->pagesize)
		ret = nand_decode_id_2(mtd, chip, type, id, id_len);
	else if (id_len <= 5)
		ret = nand_decode_id_ext(mtd, chip, type, id, id_len);
	else if (id_len == 6)
		ret = nand_decode_id_6(mtd, chip, type, id, id_len);
	else
		ret = 1;

	if (ret) {
		pr_err("[MTD][NAND]:%s:failed to decode NAND "
		       "device ID\n", __func__);
		return ret;
	}
	
	/*
	 * Determine bad-block marker scheme
	 */
	nand_decode_id_bbm(mtd, chip, id, id_len);
	
	return 0;
}
EXPORT_SYMBOL_GPL(nand_decode_id);

static struct nand_flash_dev *search_nand_flash_dev(uint8_t id)
{
	struct nand_flash_dev *device;

	for (device = nand_flash_ids; device->name != NULL; device++) {
		if (device->id == id)
			return device;
	}
	
	return NULL;
}

static int nand_decode_id_2(struct mtd_info *mtd,
			    struct nand_chip *chip,
			    struct nand_flash_dev *type,
			    uint8_t *id, int id_len)
{
	mtd->writesize = type->pagesize;
	mtd->oobsize = type->pagesize / 32;
	chip->chipsize = ((uint64_t)type->chipsize) << 20;
	
	/* SPANSION/AMD (S30ML-P ORNAND) has non-standard block size */
	if (id[0] == NAND_MFR_AMD)
		mtd->erasesize = 512 * 1024;
	else
		mtd->erasesize = type->erasesize;
	
	/* Get chip options from table */
	chip->options &= ~NAND_CHIPOPTIONS_MSK;
	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
	chip->options |= NAND_NO_AUTOINCR;
	if (mtd->writesize > 512)
		chip->options |= NAND_NO_READRDY;

	/* Assume some defaults */
	chip->bits_per_cell = 1;
	chip->cellinfo = 0;
	chip->planes_per_chip = 1;
	chip->luns_per_chip = 1;

	return 0;
}

static int nand_decode_id_ext(struct mtd_info *mtd,
			      struct nand_chip *chip,
			      struct nand_flash_dev *type,
			      uint8_t *id, int id_len)
{
	uint8_t data;

	if (id_len < 3 || id_len > 5) {
		pr_err("[MTD][NAND]:%s: invalid ID length [%d]\n",
		       __func__, id_len);
		return 1;
	}

	/* Clear chip options */
	chip->options &= ~NAND_CHIPOPTIONS_MSK;

	/* ID4: Planes/Chip Size */
	if (id[0] == NAND_MFR_HYNIX && id_len == 5 && id[4] == 0 &&
	    (id[1] == 0xDA || id[1] == 0xCA)) {
		/* Non-standard decode: HY27UF082G2A, HY27UF162G2A */
		chip->planes_per_chip = 2;
		chip->chipsize = (128 * 1024 * 1024) * chip->planes_per_chip;
	} else if (id_len == 5) {
		/*   - Planes per chip: ID4[3:2] */
		data = (id[4] >> 2) & 0x3;
		chip->planes_per_chip = 1 << data;
		
		/*   - Plane size: ID4[6:4], multiples of 8MiB */
		data = (id[4] >> 4) & 0x7;
		chip->chipsize = (8 * 1024 * 1024) << data;	/* plane size */
		chip->chipsize *= chip->planes_per_chip;	/* chip size */
	} else {
		/* Fall-back to table */
		chip->planes_per_chip = 1;
		chip->chipsize = (((uint64_t)type->chipsize) << 20);
	}

	/* ID3: Page/OOB/Block Size */
	if (id_len >= 4) {
		/*   - Page Size: ID3[1:0] */
		data = id[3] & 0x3;
		mtd->writesize = 1024 << data; /* multiples of 1k */

		/*   - OOB Size: ID3[2] */
		data = (id[3] >> 2) & 0x1;
		mtd->oobsize = 8 << data;		/* per 512 */
		mtd->oobsize *= mtd->writesize / 512;	/* per page */

		/*   - Block Size: ID3[5:4] */
		data = (id[3] >> 4) & 0x3;
		mtd->erasesize = (64 * 1024) << data; /* multiples of 64k */

		/*   - Bus Width; ID3[6] */
		if ((id[3] >> 6) & 0x1)
			chip->options |= NAND_BUSWIDTH_16;
	} else {
		/* Fall-back to table */
		mtd->writesize = type->pagesize;
		mtd->oobsize = type->pagesize / 32;
		if (type->options & NAND_BUSWIDTH_16)
			chip->options |= NAND_BUSWIDTH_16;
	}

	/* Some default 'chip' options */
	chip->options |= NAND_NO_AUTOINCR;
	if (chip->planes_per_chip > 1)
		chip->options |= NAND_MULTIPLANE_READ;
	
	if (mtd->writesize > 512)
		chip->options |= NAND_NO_READRDY;

	if (id[0] == NAND_MFR_SAMSUNG && mtd->writesize > 512)
		chip->options |= NAND_SAMSUNG_LP_OPTIONS;
	
	/* ID2: Package/Cell/Features */
	/*   Note, ID2 invalid, or documented as "don't care" on certain devices
	 *   (assume some defaults)
	 */
	if (id_len == 4 && id[0] == NAND_MFR_HYNIX &&
	    (id[1] == 0xF1 || id[1] == 0xC1 || id[1] == 0xA1 || id[1] == 0xAD ||
	     id[1] == 0xDA || id[1] == 0xCA)) {
		/* HY27{U,S}F{08,16}1G2M;
		 * HY27UF{08,16}2G2M
		 */
		chip->luns_per_chip = 1;
		chip->bits_per_cell = 1;
		chip->cellinfo = 0;
		chip->options |= (NAND_CACHEPRG |
				  NAND_CACHERD |
				  NAND_COPYBACK);
	} else if (id_len == 4 && id[0] == NAND_MFR_MICRON &&
		   (id[1] == 0xDA || id[1] == 0xCA || id[1] == 0xDC ||
		    id[1] == 0xCC || id[1] == 0xAA || id[1] == 0xBA)) {
		/* MT29F2G{08,16}AAB;
		 * MT29F4G{08,16}BAB;
		 * MT29F2G{08,16}A{A,B}C;
		 * MT29F4G08BAC
		 */
		chip->luns_per_chip = 1;
		chip->bits_per_cell = 1;
		chip->cellinfo = 0;
		chip->options |= (NAND_CACHEPRG |
				  NAND_CACHERD |
				  NAND_COPYBACK);
	} else if (id_len == 4 && id[0] == NAND_MFR_SAMSUNG &&
		   (id[1] == 0xF1 || id[1] == 0xA1)) {
		/* K9F1G08{U,Q}A */
		chip->luns_per_chip = 1;
		chip->bits_per_cell = 1;
		chip->cellinfo = 0;
		chip->options |= (NAND_CACHEPRG |
				  NAND_CACHERD |
				  NAND_COPYBACK);
	} else {
		/*   - LUNs: ID2[1:0] */
		data = id[2] & 0x3;
		chip->luns_per_chip = 0x1 << data;

		/*   - Cell levels: ID2[3:2] */
		data = (id[2] >> 2) & 0x3;
		chip->bits_per_cell = data + 1;

		/*   - Interleave: ID2[6] */
		if ((id[2] >> 6) & 0x1)
			chip->options |= NAND_MULTILUN;

		/*   - Cache Program: ID2[7] */
		if ((id[2] >> 7) & 0x1)
			chip->options |= NAND_CACHEPRG;

		/*   - Copy to 'cellinfo' */
		chip->cellinfo = id[2];
	}

	return 0;
}

static int nand_decode_id_6(struct mtd_info *mtd,
			    struct nand_chip *chip,
			    struct nand_flash_dev *type,
			    uint8_t *id, int id_len)
{
	uint8_t data;

	if (id_len != 6) {
		pr_err("[MTD][NAND]:%s: invalid ID length [%d]\n",
		       __func__, id_len);
		return 1;
	}

	chip->chipsize = (((uint64_t)type->chipsize) << 20);

	/* ID4: Planes */
	/*   - Number: ID4[3:2] */
	data = (id[4] >> 2) & 0x3;
	chip->planes_per_chip = 1 << data;

	/* ID3: Page/OOB/Block Size */
	/*   - Page Size:  ID3[1:0] */
	data = id[3] & 0x3;
	mtd->writesize = 2048 << data; /* multiples of 2k */

	/*   - OOB Size: ID3[6,3:2] */
	data = ((id[3] >> 4) & 0x4) | ((id[3] >> 2) & 0x3);
	if (id[0] == NAND_MFR_SAMSUNG) {
		switch (data) {
		case 1:
			mtd->oobsize = 128;
			break;
		case 2:
			mtd->oobsize = 218;
			break;
		case 3:
			mtd->oobsize = 400;
			break;
		case 4:
			mtd->oobsize = 436;
			break;
		case 5:
			mtd->oobsize = 640;
			break;
		default:
			pr_err("[MTD][NAND]:%s: unknown OOB size\n",
			       __func__);
			return 1;
			break;
		}
	} else {
		switch (data) {
		case 0:
			mtd->oobsize = 128;
			break;
		case 1:
			mtd->oobsize = 224;
			break;
		case 2:
			mtd->oobsize = 448;
			break;
		default:
			pr_err("[MTD][NAND]:%s: unknown OOB size\n",
			       __func__);
			break;
		}
	}

	/*   - Block Size: ID3[7,5:4] */
	data = ((id[3] >> 5) & 0x4) | ((id[3] >> 4) & 0x3);
	switch (data) {
	case 0:
	case 1:
	case 2:
		mtd->erasesize = (128 * 1024) << data;
		break;
	case 3:
		if (id[0] == NAND_MFR_SAMSUNG)
			mtd->erasesize = (1024 * 1024);
		else
			mtd->erasesize = (768 * 1024);
		break;
	case 4:
	case 5:
		mtd->erasesize = (1024 * 1024) << (data - 4);
		break;
	default:
		pr_err("[MTD][NAND]:%s: unknown block size\n",
		       __func__);
		return 1;
		break;
	}

	/* Some default 'chip' options */
	chip->options &= ~NAND_CHIPOPTIONS_MSK;
	chip->options |= NAND_NO_AUTOINCR;
	if (chip->planes_per_chip > 1)
		chip->options |= NAND_MULTIPLANE_READ;
	
	if (mtd->writesize > 512)
		chip->options |= NAND_NO_READRDY;
	
	if (id[0] == NAND_MFR_SAMSUNG && mtd->writesize > 512)
		chip->options |= NAND_SAMSUNG_LP_OPTIONS;

	/* ID2: Package/Cell/Features */
	/*   - LUNs: ID2[1:0] */
	data = id[2] & 0x3;
	chip->luns_per_chip = 0x1 << data;

	/*   - Cell levels: ID2[3:2] */
	data = (id[2] >> 2) & 0x3;
	chip->bits_per_cell = data + 1;

	/*   - Interleave: ID2[6] */
	if ((id[2] >> 6) & 0x1)
		chip->options |= NAND_MULTILUN;

	/*   - Cache Program: ID2[7] */
	if ((id[2] >> 7) & 0x1)
		chip->options |= NAND_CACHEPRG;

	/*   - Copy to 'cellinfo' */
	chip->cellinfo = id[2];

	/* Bus Width, from table */
	chip->options |= (type->options & NAND_BUSWIDTH_16);

	return 0;
}


/*
 * Heuristics for factory-programmed bad-block marker (BBM) schemes, largely
 * based on code in mtd-2.6 (2011-07-09)
 * nand_base.c:nand_get_flash_type(), and datasheets for devices in
 * http://www.linux-mtd.infradead.org/nand-data/nanddata.html (2011-07-09).
 */
static void nand_decode_id_bbm(struct mtd_info *mtd,
			       struct nand_chip *chip,
			       uint8_t *id, int id_len)
{
	/*
	 * Some special cases first
	 */
	
	/* Hynix HY27US1612{1,2}B: 3rd word for x16 device! */
	if (id[0] == NAND_MFR_HYNIX && id[1] == 0x56) {
		chip->bbt_options = (NAND_BBM_PAGE_0 |
				     NAND_BBM_PAGE_1 |
				     NAND_BBM_BYTE_OOB_5);
				     
		return;
	}
	
	/* Hynix H27UAG8T2A: last and last-2 pages, byte 0 */
	if (id[0] == NAND_MFR_HYNIX && id_len == 6 &&
	    id[1] == 0xD5 && id[2] == 0x94 && id[3] == 0x25 &&
	    id[4] == 0x44 && id[5] == 0x41) {
		chip->bbt_options = (NAND_BBM_PAGE_LAST |
				     NAND_BBM_PAGE_LMIN2 |
				     NAND_BBM_BYTE_OOB_0);
		return;
	}
	
	/* Numonyx/ST 2K/4K pages, x8 bus use BOTH byte 0 and 5 (drivers may
	 * need to disable 'byte 5' depending on ECC layout)
	 */
	if (!(chip->options & NAND_BUSWIDTH_16) &&
	    mtd->writesize >= 2048 && id[0] == NAND_MFR_STMICRO) {
	 	chip->bbt_options =  (NAND_BBM_PAGE_0 |
				      NAND_BBM_BYTE_OOB_0 |
				      NAND_BBM_BYTE_OOB_5);
		return;
	}

	/* Samsung and Hynix MLC NAND: last page, byte 0; and 1st page for 8KiB
	 * page devices */
	if ((id[0] == NAND_MFR_SAMSUNG || id[0] == NAND_MFR_HYNIX) &&
	    chip->bits_per_cell == 2) {
		chip->bbt_options = NAND_BBM_PAGE_LAST | NAND_BBM_BYTE_OOB_0;
		if (mtd->writesize == 8192)
			chip->bbt_options |= NAND_BBM_PAGE_0;
		return;
	}
	
	/* Micron 2KiB page devices use 1st and 2nd page, byte 0 */
	if (id[0] == NAND_MFR_MICRON && mtd->writesize == 2048) {
		chip->bbt_options = NAND_BBM_PAGE_0 | NAND_BBM_PAGE_1 |
			NAND_BBM_BYTE_OOB_0;
		return;
	}

	/*
	 * For the rest...
	 */
	
	/* Scan at least the first page */
	chip->bbt_options = NAND_BBM_PAGE_0;
	/* Also 2nd page for SLC Samsung, Hynix, Toshia, and AMD/Spansion */
	if (chip->bits_per_cell == 1 &&
	    (id[0] == NAND_MFR_SAMSUNG ||
	     id[0] == NAND_MFR_HYNIX ||
	     id[0] == NAND_MFR_TOSHIBA ||
	     id[0] == NAND_MFR_AMD))
		chip->bbt_options |= NAND_BBM_PAGE_1;

	/* SP x8 devices use 6th byte OOB; everything else uses 1st byte OOB */
	if (mtd->writesize == 512 && !(chip->options & NAND_BUSWIDTH_16))
		chip->bbt_options |= NAND_BBM_BYTE_OOB_5;
	else
		chip->bbt_options |= NAND_BBM_BYTE_OOB_0;
	
	return;
}

  reply	other threads:[~2011-10-14 15:15 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-10-12 15:26 nand_base.c:nand_get_flash_type() test results Angus CLARK
2011-10-14 13:57 ` Artem Bityutskiy
2011-10-14 15:14   ` Angus CLARK [this message]
2011-10-16 12:03     ` Artem Bityutskiy
2011-10-16 14:33 ` Mike Dunn
2011-10-18  7:58   ` Angus CLARK
2011-10-18 14:05     ` Mike Dunn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4E9851EB.8070909@st.com \
    --to=angus.clark@st.com \
    --cc=dedekind1@gmail.com \
    --cc=linux-mtd@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.