All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 1/2] NAND: add NAND_CMD_PARAM (0xec) definition
@ 2010-12-10 22:16 Florian Fainelli
  2010-12-10 22:16 ` [U-Boot] [PATCH 2/2] NAND: add support for reading ONFI page table Florian Fainelli
  2010-12-10 23:47 ` [U-Boot] [U-Boot, 1/2] NAND: add NAND_CMD_PARAM (0xec) definition Scott Wood
  0 siblings, 2 replies; 15+ messages in thread
From: Florian Fainelli @ 2010-12-10 22:16 UTC (permalink / raw)
  To: u-boot

From: Florian Fainelli <florian@openwrt.org>

This command is used to read the device ONFI parameters page.

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
 include/linux/mtd/nand.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 94ad0c0..7db87e1 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -84,6 +84,7 @@ extern void nand_wait_ready(struct mtd_info *mtd);
 #define NAND_CMD_SEQIN		0x80
 #define NAND_CMD_RNDIN		0x85
 #define NAND_CMD_READID		0x90
+#define NAND_CMD_PARAM		0xec
 #define NAND_CMD_ERASE2		0xd0
 #define NAND_CMD_RESET		0xff
 
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [U-Boot] [PATCH 2/2] NAND: add support for reading ONFI page table
  2010-12-10 22:16 [U-Boot] [PATCH 1/2] NAND: add NAND_CMD_PARAM (0xec) definition Florian Fainelli
@ 2010-12-10 22:16 ` Florian Fainelli
  2010-12-11  0:15   ` [U-Boot] [U-Boot, " Scott Wood
  2010-12-28  0:21   ` [U-Boot] [PATCH v2] " Florian Fainelli
  2010-12-10 23:47 ` [U-Boot] [U-Boot, 1/2] NAND: add NAND_CMD_PARAM (0xec) definition Scott Wood
  1 sibling, 2 replies; 15+ messages in thread
From: Florian Fainelli @ 2010-12-10 22:16 UTC (permalink / raw)
  To: u-boot

From: Florian Fainelli <florian@openwrt.org>

This patch adds support for reading an ONFI page parameter from a NAND device
supporting it. If this is the case, struct nand_chip onfi_version member
contains the supported ONFI version, 0 otherwise.

This allows NAND drivers past nand_scan_ident to set the best timings for the
NAND chip.

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
 drivers/mtd/nand/nand_base.c |  129 ++++++++++++++++++++++++++++++++++-------
 include/linux/mtd/nand.h     |   68 ++++++++++++++++++++++
 2 files changed, 175 insertions(+), 22 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 21cc5a3..a3a0507 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2406,15 +2406,94 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
 		chip->controller = &chip->hwcontrol;
 }
 
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+	int i;
+
+	while (len--) {
+		crc ^= *p++ << 8;
+		for (i = 0; i < 8; i++)
+			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+	}
+
+	return crc;
+}
+
+/*
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
+ */
+static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
+								int busw)
+{
+	struct nand_onfi_params *p = &chip->onfi_params;
+	int i;
+	int val;
+
+	/* try ONFI for unknow chip or LP */
+	chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
+	if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
+		chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
+		return 0;
+
+	printk(KERN_INFO "ONFI flash detected\n");
+	chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+	for (i = 0; i < 3; i++) {
+		chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+		if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+						le16_to_cpu(p->crc)) {
+			printk(KERN_INFO "ONFI param page %d valid\n", i);
+			break;
+		}
+	}
+
+	if (i == 3)
+		return 0;
+
+	/* check version */
+	val = le16_to_cpu(p->revision);
+	if (val == 1 || val > (1 << 4)) {
+		printk(KERN_INFO "%s: unsupported ONFI version: %d\n",
+								__func__, val);
+		return 0;
+	}
+
+	if (val & (1 << 4))
+		chip->onfi_version = 22;
+	else if (val & (1 << 3))
+		chip->onfi_version = 21;
+	else if (val & (1 << 2))
+		chip->onfi_version = 20;
+	else
+		chip->onfi_version = 10;
+
+	if (!mtd->name)
+		mtd->name = p->model;
+
+	mtd->writesize = le32_to_cpu(p->byte_per_page);
+	mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+	mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+	chip->chipsize = le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+	busw = 0;
+	if (le16_to_cpu(p->features) & 1)
+		busw = NAND_BUSWIDTH_16;
+
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= (NAND_NO_READRDY |
+			NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
+
+	return 1;
+}
+
 /*
  * Get the flash and manufacturer id and lookup if the type is supported
  */
 static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 						  struct nand_chip *chip,
-						  int busw, int *maf_id)
+						  int busw,
+						  int *maf_id, int *dev_id)
 {
 	struct nand_flash_dev *type = NULL;
-	int i, dev_id, maf_idx;
+	int i, ret, maf_idx;
 	int tmp_id, tmp_manf;
 
 	/* Select the device */
@@ -2431,7 +2510,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 
 	/* Read manufacturer and device IDs */
 	*maf_id = chip->read_byte(mtd);
-	dev_id = chip->read_byte(mtd);
+	*dev_id = chip->read_byte(mtd);
 
 	/* Try again to make sure, as some systems the bus-hold or other
 	 * interface concerns can cause random data which looks like a
@@ -2446,16 +2525,16 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	tmp_manf = chip->read_byte(mtd);
 	tmp_id = chip->read_byte(mtd);
 
-	if (tmp_manf != *maf_id || tmp_id != dev_id) {
+	if (tmp_manf != *maf_id || tmp_id != *dev_id) {
 		printk(KERN_INFO "%s: second ID read did not match "
 		       "%02x,%02x against %02x,%02x\n", __func__,
-		       *maf_id, dev_id, tmp_manf, tmp_id);
+		       *maf_id, *dev_id, tmp_manf, tmp_id);
 		return ERR_PTR(-ENODEV);
 	}
 
 	/* Lookup the flash id */
 	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
-		if (dev_id == nand_flash_ids[i].id) {
+		if (*dev_id == nand_flash_ids[i].id) {
 			type =  &nand_flash_ids[i];
 			break;
 		}
@@ -2464,10 +2543,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	if (!type) {
 		/* supress warning if there is no nand */
 		if (*maf_id != 0x00 && *maf_id != 0xff &&
-		    dev_id  != 0x00 && dev_id  != 0xff)
+		    *dev_id  != 0x00 && *dev_id  != 0xff)
 			printk(KERN_INFO "%s: unknown NAND device: "
 				"Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
-				__func__, *maf_id, dev_id);
+				__func__, *maf_id, *dev_id);
 		return ERR_PTR(-ENODEV);
 	}
 
@@ -2475,6 +2554,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		mtd->name = type->name;
 
 	chip->chipsize = (uint64_t)type->chipsize << 20;
+	chip->onfi_version = 0;
+
+	ret = nand_flash_detect_onfi(mtd, chip, busw);
+	if (ret)
+		goto ident_done;
 
 	/* Newer devices have all the information in additional id bytes */
 	if (!type->pagesize) {
@@ -2505,6 +2589,16 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		busw = type->options & NAND_BUSWIDTH_16;
 	}
 
+	/* Get chip options, preserve non chip based options */
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+
+	/*
+	 * Set chip as a default. Board drivers can override it, if necessary
+	 */
+ident_done:
+	chip->options |= NAND_NO_AUTOINCR;
+
 	/* Try to identify manufacturer */
 	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
 		if (nand_manuf_ids[maf_idx].id == *maf_id)
@@ -2518,7 +2612,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	if (busw != (chip->options & NAND_BUSWIDTH_16)) {
 		printk(KERN_INFO "NAND device: Manufacturer ID:"
 		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
-		       dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
+		       *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
 		printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
 		       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
 		       busw ? 16 : 8);
@@ -2541,15 +2635,6 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	chip->badblockpos = mtd->writesize > 512 ?
 		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
 
-	/* Get chip options, preserve non chip based options */
-	chip->options &= ~NAND_CHIPOPTIONS_MSK;
-	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
-
-	/*
-	 * Set chip as a default. Board drivers can override it, if necessary
-	 */
-	chip->options |= NAND_NO_AUTOINCR;
-
 	/* Check if chip is a not a samsung device. Do not clear the
 	 * options for chips which are not having an extended id.
 	 */
@@ -2567,7 +2652,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		chip->cmdfunc = nand_command_lp;
 
 	MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
-	          " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
+	          " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
 	          nand_manuf_ids[maf_idx].name, type->name);
 
 	return type;
@@ -2585,7 +2670,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
  */
 int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 {
-	int i, busw, nand_maf_id;
+	int i, busw, nand_maf_id, nand_dev_id;
 	struct nand_chip *chip = mtd->priv;
 	struct nand_flash_dev *type;
 
@@ -2595,7 +2680,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 	nand_set_defaults(chip, busw);
 
 	/* Read the flash type */
-	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
+	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id);
 
 	if (IS_ERR(type)) {
 #ifndef CONFIG_SYS_NAND_QUIET_TEST
@@ -2614,7 +2699,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 		/* Read manufacturer and device IDs */
 		if (nand_maf_id != chip->read_byte(mtd) ||
-		    type->id != chip->read_byte(mtd))
+		    nand_dev_id != chip->read_byte(mtd))
 			break;
 	}
 #ifdef DEBUG
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 7db87e1..beb35c6 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -215,6 +215,71 @@ typedef enum {
 /* Keep gcc happy */
 struct nand_chip;
 
+struct nand_onfi_params {
+	/* rev info and features block */
+	/* 'O' 'N' 'F' 'I'  */
+	u8 sig[4];
+	__le16 revision;
+	__le16 features;
+	__le16 opt_cmd;
+	u8 reserved[22];
+
+	/* manufacturer information block */
+	char manufacturer[12];
+	char model[20];
+	u8 jedec_id;
+	__le16 date_code;
+	u8 reserved2[13];
+
+	/* memory organization block */
+	__le32 byte_per_page;
+	__le16 spare_bytes_per_page;
+	__le32 data_bytes_per_ppage;
+	__le16 spare_bytes_per_ppage;
+	__le32 pages_per_block;
+	__le32 blocks_per_lun;
+	u8 lun_count;
+	u8 addr_cycles;
+	u8 bits_per_cell;
+	__le16 bb_per_lun;
+	__le16 block_endurance;
+	u8 guaranteed_good_blocks;
+	__le16 guaranteed_block_endurance;
+	u8 programs_per_page;
+	u8 ppage_attr;
+	u8 ecc_bits;
+	u8 interleaved_bits;
+	u8 interleaved_ops;
+	u8 reserved3[13];
+
+	/* electrical parameter block */
+	u8 io_pin_capacitance_max;
+	__le16 async_timing_mode;
+	__le16 program_cache_timing_mode;
+	__le16 t_prog;
+	__le16 t_bers;
+	__le16 t_r;
+	__le16 t_ccs;
+	__le16 src_sync_timing_mode;
+	__le16 src_ssync_features;
+	__le16 clk_pin_capacitance_typ;
+	__le16 io_pin_capacitance_typ;
+	__le16 input_pin_capacitance_typ;
+	u8 input_pin_capacitance_max;
+	u8 driver_strenght_support;
+	__le16 t_int_r;
+	__le16 t_ald;
+	u8 reserved4[7];
+
+	/* vendor */
+	u8 reserved5[90];
+
+	 __le16 crc;
+} __attribute__((packed));
+
+#define ONFI_CRC_BASE	0x4F4E
+
+
 /**
  * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
  * @lock:               protection lock
@@ -405,6 +470,9 @@ struct nand_chip {
 	uint8_t		cellinfo;
 	int		badblockpos;
 
+	int		onfi_version;
+	struct nand_onfi_params onfi_params;
+
 	int 		state;
 
 	uint8_t		*oob_poi;
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [U-Boot] [U-Boot, 1/2] NAND: add NAND_CMD_PARAM (0xec) definition
  2010-12-10 22:16 [U-Boot] [PATCH 1/2] NAND: add NAND_CMD_PARAM (0xec) definition Florian Fainelli
  2010-12-10 22:16 ` [U-Boot] [PATCH 2/2] NAND: add support for reading ONFI page table Florian Fainelli
@ 2010-12-10 23:47 ` Scott Wood
  1 sibling, 0 replies; 15+ messages in thread
From: Scott Wood @ 2010-12-10 23:47 UTC (permalink / raw)
  To: u-boot

On Fri, Dec 10, 2010 at 12:16:41PM -0000, Florian Fainelli wrote:
> From: Florian Fainelli <florian@openwrt.org>
> 
> This command is used to read the device ONFI parameters page.
> 
> Signed-off-by: Florian Fainelli <florian@openwrt.org>
> 
> ---
> include/linux/mtd/nand.h |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)

Applied to u-boot-nand-flash next

-Scott

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [U-Boot] [U-Boot, 2/2] NAND: add support for reading ONFI page table
  2010-12-10 22:16 ` [U-Boot] [PATCH 2/2] NAND: add support for reading ONFI page table Florian Fainelli
@ 2010-12-11  0:15   ` Scott Wood
  2010-12-11 17:08     ` Florian Fainelli
  2010-12-28  0:21   ` [U-Boot] [PATCH v2] " Florian Fainelli
  1 sibling, 1 reply; 15+ messages in thread
From: Scott Wood @ 2010-12-11  0:15 UTC (permalink / raw)
  To: u-boot

On Fri, Dec 10, 2010 at 12:16:42PM -0000, Florian Fainelli wrote:
> From: Florian Fainelli <florian@openwrt.org>
> 
> This patch adds support for reading an ONFI page parameter from a NAND device
> supporting it. If this is the case, struct nand_chip onfi_version member
> contains the supported ONFI version, 0 otherwise.
> 
> This allows NAND drivers past nand_scan_ident to set the best timings for the
> NAND chip.

Wrap changelogs around 72 characters, as git log indents them a bit.

> 
> Signed-off-by: Florian Fainelli <florian@openwrt.org>
> 
> ---
> drivers/mtd/nand/nand_base.c |  129 ++++++++++++++++++++++++++++++++++-------
>  include/linux/mtd/nand.h     |   68 ++++++++++++++++++++++
>  2 files changed, 175 insertions(+), 22 deletions(-)

Tested on what I assume is a non-ONFI chip, no breakage.

Please wrap this in a CONFIG option, so that we don't increase the
image size of boards that don't need to support this.

> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index 21cc5a3..a3a0507 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -2406,15 +2406,94 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
>  		chip->controller = &chip->hwcontrol;
>  }
>  
> +static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
> +{
> +	int i;
> +
> +	while (len--) {
> +		crc ^= *p++ << 8;
> +		for (i = 0; i < 8; i++)
> +			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
> +	}
> +
> +	return crc;
> +}
> +
> +/*
> + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
> + */
> +static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
> +								int busw)
> +{

Keep lines under 80 columns.

> +
> +	/* try ONFI for unknow chip or LP */

"unknown".

> +	chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
> +	if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
> +		chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
> +		return 0;

I'm not sure how this comment matches the code -- it looks like it's
checking for an explicit ONFI ID.  I don't see anything about unknown
chips or large pages.

> +	/* check version */
> +	val = le16_to_cpu(p->revision);
> +	if (val == 1 || val > (1 << 4)) {
> +		printk(KERN_INFO "%s: unsupported ONFI version: %d\n",
> +								__func__, val);
> +		return 0;
> +	}

Ideally I'd like to see continuation lines lined up nicely with
the start of arguments on the previous line, but at least
don't tab it all the way over to the right edge of the screen.

>  	chip->chipsize = (uint64_t)type->chipsize << 20;
> +	chip->onfi_version = 0;
> +
> +	ret = nand_flash_detect_onfi(mtd, chip, busw);
> +	if (ret)
> +		goto ident_done;

Move the non-ONFI code out into its own function, instead of using
goto.

> +	__le32 blocks_per_lun;
> +	u8 lun_count;
> +	u8 addr_cycles;
> +	u8 bits_per_cell;
> +	__le16 bb_per_lun;
> +	__le16 block_endurance;
> +	u8 guaranteed_good_blocks;
> +	__le16 guaranteed_block_endurance;
[snip]
> +} __attribute__((packed));

Sigh.  Someone on the standards body needs to learn about alignment.

-Scott

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [U-Boot] [U-Boot, 2/2] NAND: add support for reading ONFI page table
  2010-12-11  0:15   ` [U-Boot] [U-Boot, " Scott Wood
@ 2010-12-11 17:08     ` Florian Fainelli
  0 siblings, 0 replies; 15+ messages in thread
From: Florian Fainelli @ 2010-12-11 17:08 UTC (permalink / raw)
  To: u-boot

On Saturday 11 December 2010 01:15:13 Scott Wood wrote:
> On Fri, Dec 10, 2010 at 12:16:42PM -0000, Florian Fainelli wrote:
> > From: Florian Fainelli <florian@openwrt.org>
> > 
> > This patch adds support for reading an ONFI page parameter from a NAND
> > device supporting it. If this is the case, struct nand_chip onfi_version
> > member contains the supported ONFI version, 0 otherwise.
> > 
> > This allows NAND drivers past nand_scan_ident to set the best timings for
> > the NAND chip.
> 
> Wrap changelogs around 72 characters, as git log indents them a bit.
> 
> > Signed-off-by: Florian Fainelli <florian@openwrt.org>
> > 
> > ---
> > drivers/mtd/nand/nand_base.c |  129
> > ++++++++++++++++++++++++++++++++++-------
> > 
> >  include/linux/mtd/nand.h     |   68 ++++++++++++++++++++++
> >  2 files changed, 175 insertions(+), 22 deletions(-)
> 
> Tested on what I assume is a non-ONFI chip, no breakage.
> 
> Please wrap this in a CONFIG option, so that we don't increase the
> image size of boards that don't need to support this.

Makes sense, I will do this

> 
> > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > index 21cc5a3..a3a0507 100644
> > --- a/drivers/mtd/nand/nand_base.c
> > +++ b/drivers/mtd/nand/nand_base.c
> > @@ -2406,15 +2406,94 @@ static void nand_set_defaults(struct nand_chip
> > *chip, int busw)
> > 
> >  		chip->controller = &chip->hwcontrol;
> >  
> >  }
> > 
> > +static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
> > +{
> > +	int i;
> > +
> > +	while (len--) {
> > +		crc ^= *p++ << 8;
> > +		for (i = 0; i < 8; i++)
> > +			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
> > +	}
> > +
> > +	return crc;
> > +}
> > +
> > +/*
> > + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0
> > otherwise + */
> > +static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip
> > *chip, +								int busw)
> > +{
> 
> Keep lines under 80 columns.
> 
> > +
> > +	/* try ONFI for unknow chip or LP */
> 
> "unknown".
> 
> > +	chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
> > +	if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
> > +		chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
> > +		return 0;
> 
> I'm not sure how this comment matches the code -- it looks like it's
> checking for an explicit ONFI ID.  I don't see anything about unknown
> chips or large pages.

It does not actually, I did not think about checking it when porting this from Linux.

> 
> > +	/* check version */
> > +	val = le16_to_cpu(p->revision);
> > +	if (val == 1 || val > (1 << 4)) {
> > +		printk(KERN_INFO "%s: unsupported ONFI version: %d\n",
> > +								__func__, val);
> > +		return 0;
> > +	}
> 
> Ideally I'd like to see continuation lines lined up nicely with
> the start of arguments on the previous line, but at least
> don't tab it all the way over to the right edge of the screen.
> 
> >  	chip->chipsize = (uint64_t)type->chipsize << 20;
> > 
> > +	chip->onfi_version = 0;
> > +
> > +	ret = nand_flash_detect_onfi(mtd, chip, busw);
> > +	if (ret)
> > +		goto ident_done;
> 
> Move the non-ONFI code out into its own function, instead of using
> goto.
> 
> > +	__le32 blocks_per_lun;
> > +	u8 lun_count;
> > +	u8 addr_cycles;
> > +	u8 bits_per_cell;
> > +	__le16 bb_per_lun;
> > +	__le16 block_endurance;
> > +	u8 guaranteed_good_blocks;
> > +	__le16 guaranteed_block_endurance;
> 
> [snip]
> 
> > +} __attribute__((packed));
> 
> Sigh.  Someone on the standards body needs to learn about alignment.
> 
> -Scott
> 
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [U-Boot] [PATCH v2] NAND: add support for reading ONFI page table
  2010-12-10 22:16 ` [U-Boot] [PATCH 2/2] NAND: add support for reading ONFI page table Florian Fainelli
  2010-12-11  0:15   ` [U-Boot] [U-Boot, " Scott Wood
@ 2010-12-28  0:21   ` Florian Fainelli
  2010-12-28  0:23     ` Florian Fainelli
  2011-01-04 20:42     ` Scott Wood
  1 sibling, 2 replies; 15+ messages in thread
From: Florian Fainelli @ 2010-12-28  0:21 UTC (permalink / raw)
  To: u-boot

From: Florian Fainelli <florian@openwrt.org>

This patch adds support for reading an ONFI page parameter from a NAND
device supporting it. If this is the case, struct nand_chip onfi_version
member contains the supported ONFI version, 0 otherwise.

This allows NAND drivers past nand_scan_ident to set the best timings for the
NAND chip.

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
Changes since v1:
- ifdef out ONFI detection code around CONFIG_SYS_NAND_ONFI_DETECTION
- removed bogus comment
- fixed busw variable usage
- move non-ONFI detection code to its own function
- fixed stylistic issues spotted by Scott

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 5239c1f..7f9d69a 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2409,15 +2409,141 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
 		chip->controller = &chip->hwcontrol;
 }
 
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+	int i;
+
+	while (len--) {
+		crc ^= *p++ << 8;
+		for (i = 0; i < 8; i++)
+			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+	}
+
+	return crc;
+}
+
+/*
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
+ */
+static int nand_flash_detect_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					int *busw)
+{
+	struct nand_onfi_params *p = &chip->onfi_params;
+	int i;
+	int val;
+
+	chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
+	if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
+		chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
+		return 0;
+
+	printk(KERN_INFO "ONFI flash detected\n");
+	chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+	for (i = 0; i < 3; i++) {
+		chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+		if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+						le16_to_cpu(p->crc)) {
+			printk(KERN_INFO "ONFI param page %d valid\n", i);
+			break;
+		}
+	}
+
+	if (i == 3)
+		return 0;
+
+	/* check version */
+	val = le16_to_cpu(p->revision);
+	if (val == 1 || val > (1 << 4)) {
+		printk(KERN_INFO "%s: unsupported ONFI version: %d\n", __func__, val);
+		return 0;
+	}
+
+	if (val & (1 << 4))
+		chip->onfi_version = 22;
+	else if (val & (1 << 3))
+		chip->onfi_version = 21;
+	else if (val & (1 << 2))
+		chip->onfi_version = 20;
+	else
+		chip->onfi_version = 10;
+
+	if (!mtd->name)
+		mtd->name = p->model;
+
+	mtd->writesize = le32_to_cpu(p->byte_per_page);
+	mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+	mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+	chip->chipsize = le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+	*busw = 0;
+	if (le16_to_cpu(p->features) & 1)
+		*busw = NAND_BUSWIDTH_16;
+
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= (NAND_NO_READRDY |
+			NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
+
+	return 1;
+}
+#else
+static inline int nand_flash_detect_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					int *busw)
+{
+	return 0;
+}
+#endif
+
+static void nand_flash_detect_non_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					const struct nand_flash_dev *type,
+					int *busw)
+{
+	/* Newer devices have all the information in additional id bytes */
+	if (!type->pagesize) {
+		int extid;
+		/* The 3rd id byte holds MLC / multichip data */
+		chip->cellinfo = chip->read_byte(mtd);
+		/* The 4th id byte is the important one */
+		extid = chip->read_byte(mtd);
+		/* Calc pagesize */
+		mtd->writesize = 1024 << (extid & 0x3);
+		extid >>= 2;
+		/* Calc oobsize */
+		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
+		extid >>= 2;
+		/* Calc blocksize. Blocksize is multiples of 64KiB */
+		mtd->erasesize = (64 * 1024) << (extid & 0x03);
+		extid >>= 2;
+		/* Get buswidth information */
+		*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+
+	} else {
+		/*
+		 * Old devices have chip data hardcoded in the device id table
+		 */
+		mtd->erasesize = type->erasesize;
+		mtd->writesize = type->pagesize;
+		mtd->oobsize = mtd->writesize / 32;
+		*busw = type->options & NAND_BUSWIDTH_16;
+	}
+
+	/* Get chip options, preserve non chip based options */
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+}
+
 /*
  * Get the flash and manufacturer id and lookup if the type is supported
  */
 static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 						  struct nand_chip *chip,
-						  int busw, int *maf_id)
+						  int busw,
+						  int *maf_id, int *dev_id)
 {
 	const struct nand_flash_dev *type = NULL;
-	int i, dev_id, maf_idx;
+	int i, ret, maf_idx;
 	int tmp_id, tmp_manf;
 
 	/* Select the device */
@@ -2434,7 +2560,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 
 	/* Read manufacturer and device IDs */
 	*maf_id = chip->read_byte(mtd);
-	dev_id = chip->read_byte(mtd);
+	*dev_id = chip->read_byte(mtd);
 
 	/* Try again to make sure, as some systems the bus-hold or other
 	 * interface concerns can cause random data which looks like a
@@ -2449,16 +2575,16 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	tmp_manf = chip->read_byte(mtd);
 	tmp_id = chip->read_byte(mtd);
 
-	if (tmp_manf != *maf_id || tmp_id != dev_id) {
+	if (tmp_manf != *maf_id || tmp_id != *dev_id) {
 		printk(KERN_INFO "%s: second ID read did not match "
 		       "%02x,%02x against %02x,%02x\n", __func__,
-		       *maf_id, dev_id, tmp_manf, tmp_id);
+		       *maf_id, *dev_id, tmp_manf, tmp_id);
 		return ERR_PTR(-ENODEV);
 	}
 
 	/* Lookup the flash id */
 	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
-		if (dev_id == nand_flash_ids[i].id) {
+		if (*dev_id == nand_flash_ids[i].id) {
 			type =  &nand_flash_ids[i];
 			break;
 		}
@@ -2467,10 +2593,10 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	if (!type) {
 		/* supress warning if there is no nand */
 		if (*maf_id != 0x00 && *maf_id != 0xff &&
-		    dev_id  != 0x00 && dev_id  != 0xff)
+		    *dev_id  != 0x00 && *dev_id  != 0xff)
 			printk(KERN_INFO "%s: unknown NAND device: "
 				"Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
-				__func__, *maf_id, dev_id);
+				__func__, *maf_id, *dev_id);
 		return ERR_PTR(-ENODEV);
 	}
 
@@ -2478,35 +2604,20 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		mtd->name = type->name;
 
 	chip->chipsize = (uint64_t)type->chipsize << 20;
+	chip->onfi_version = 0;
 
-	/* Newer devices have all the information in additional id bytes */
-	if (!type->pagesize) {
-		int extid;
-		/* The 3rd id byte holds MLC / multichip data */
-		chip->cellinfo = chip->read_byte(mtd);
-		/* The 4th id byte is the important one */
-		extid = chip->read_byte(mtd);
-		/* Calc pagesize */
-		mtd->writesize = 1024 << (extid & 0x3);
-		extid >>= 2;
-		/* Calc oobsize */
-		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
-		extid >>= 2;
-		/* Calc blocksize. Blocksize is multiples of 64KiB */
-		mtd->erasesize = (64 * 1024) << (extid & 0x03);
-		extid >>= 2;
-		/* Get buswidth information */
-		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+	ret = nand_flash_detect_onfi(mtd, chip, &busw);
+	if (!ret)
+		nand_flash_detect_non_onfi(mtd, chip, type, &busw);
 
-	} else {
-		/*
-		 * Old devices have chip data hardcoded in the device id table
-		 */
-		mtd->erasesize = type->erasesize;
-		mtd->writesize = type->pagesize;
-		mtd->oobsize = mtd->writesize / 32;
-		busw = type->options & NAND_BUSWIDTH_16;
-	}
+	/* Get chip options, preserve non chip based options */
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+
+	/*
+	 * Set chip as a default. Board drivers can override it, if necessary
+	 */
+	chip->options |= NAND_NO_AUTOINCR;
 
 	/* Try to identify manufacturer */
 	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
@@ -2521,7 +2632,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	if (busw != (chip->options & NAND_BUSWIDTH_16)) {
 		printk(KERN_INFO "NAND device: Manufacturer ID:"
 		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
-		       dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
+		       *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
 		printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
 		       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
 		       busw ? 16 : 8);
@@ -2544,15 +2655,6 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	chip->badblockpos = mtd->writesize > 512 ?
 		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
 
-	/* Get chip options, preserve non chip based options */
-	chip->options &= ~NAND_CHIPOPTIONS_MSK;
-	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
-
-	/*
-	 * Set chip as a default. Board drivers can override it, if necessary
-	 */
-	chip->options |= NAND_NO_AUTOINCR;
-
 	/* Check if chip is a not a samsung device. Do not clear the
 	 * options for chips which are not having an extended id.
 	 */
@@ -2570,7 +2672,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		chip->cmdfunc = nand_command_lp;
 
 	MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
-	          " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
+	          " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
 	          nand_manuf_ids[maf_idx].name, type->name);
 
 	return type;
@@ -2588,7 +2690,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
  */
 int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 {
-	int i, busw, nand_maf_id;
+	int i, busw, nand_maf_id, nand_dev_id;
 	struct nand_chip *chip = mtd->priv;
 	const struct nand_flash_dev *type;
 
@@ -2598,7 +2700,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 	nand_set_defaults(chip, busw);
 
 	/* Read the flash type */
-	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
+	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id);
 
 	if (IS_ERR(type)) {
 #ifndef CONFIG_SYS_NAND_QUIET_TEST
@@ -2617,7 +2719,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 		/* Read manufacturer and device IDs */
 		if (nand_maf_id != chip->read_byte(mtd) ||
-		    type->id != chip->read_byte(mtd))
+		    nand_dev_id != chip->read_byte(mtd))
 			break;
 	}
 #ifdef DEBUG
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 1128f5a..f19909c 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -215,6 +215,71 @@ typedef enum {
 /* Keep gcc happy */
 struct nand_chip;
 
+struct nand_onfi_params {
+	/* rev info and features block */
+	/* 'O' 'N' 'F' 'I'  */
+	u8 sig[4];
+	__le16 revision;
+	__le16 features;
+	__le16 opt_cmd;
+	u8 reserved[22];
+
+	/* manufacturer information block */
+	char manufacturer[12];
+	char model[20];
+	u8 jedec_id;
+	__le16 date_code;
+	u8 reserved2[13];
+
+	/* memory organization block */
+	__le32 byte_per_page;
+	__le16 spare_bytes_per_page;
+	__le32 data_bytes_per_ppage;
+	__le16 spare_bytes_per_ppage;
+	__le32 pages_per_block;
+	__le32 blocks_per_lun;
+	u8 lun_count;
+	u8 addr_cycles;
+	u8 bits_per_cell;
+	__le16 bb_per_lun;
+	__le16 block_endurance;
+	u8 guaranteed_good_blocks;
+	__le16 guaranteed_block_endurance;
+	u8 programs_per_page;
+	u8 ppage_attr;
+	u8 ecc_bits;
+	u8 interleaved_bits;
+	u8 interleaved_ops;
+	u8 reserved3[13];
+
+	/* electrical parameter block */
+	u8 io_pin_capacitance_max;
+	__le16 async_timing_mode;
+	__le16 program_cache_timing_mode;
+	__le16 t_prog;
+	__le16 t_bers;
+	__le16 t_r;
+	__le16 t_ccs;
+	__le16 src_sync_timing_mode;
+	__le16 src_ssync_features;
+	__le16 clk_pin_capacitance_typ;
+	__le16 io_pin_capacitance_typ;
+	__le16 input_pin_capacitance_typ;
+	u8 input_pin_capacitance_max;
+	u8 driver_strenght_support;
+	__le16 t_int_r;
+	__le16 t_ald;
+	u8 reserved4[7];
+
+	/* vendor */
+	u8 reserved5[90];
+
+	 __le16 crc;
+} __attribute__((packed));
+
+#define ONFI_CRC_BASE	0x4F4E
+
+
 /**
  * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
  * @lock:               protection lock
@@ -404,6 +469,10 @@ struct nand_chip {
 	int		subpagesize;
 	uint8_t		cellinfo;
 	int		badblockpos;
+	int		onfi_version;
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+	struct nand_onfi_params onfi_params;
+#endif
 
 	int 		state;
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [U-Boot] [PATCH v2] NAND: add support for reading ONFI page table
  2010-12-28  0:21   ` [U-Boot] [PATCH v2] " Florian Fainelli
@ 2010-12-28  0:23     ` Florian Fainelli
  2011-01-04 20:42     ` Scott Wood
  1 sibling, 0 replies; 15+ messages in thread
From: Florian Fainelli @ 2010-12-28  0:23 UTC (permalink / raw)
  To: u-boot

Hello Scott,

Forgot to mention that this patch is against your 'next' branch.
--
Florian

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [U-Boot] [PATCH v2] NAND: add support for reading ONFI page table
  2010-12-28  0:21   ` [U-Boot] [PATCH v2] " Florian Fainelli
  2010-12-28  0:23     ` Florian Fainelli
@ 2011-01-04 20:42     ` Scott Wood
  2011-02-14 15:48       ` [U-Boot] [PATCH v3] " Florian Fainelli
  1 sibling, 1 reply; 15+ messages in thread
From: Scott Wood @ 2011-01-04 20:42 UTC (permalink / raw)
  To: u-boot

On Tue, Dec 28, 2010 at 01:21:06AM +0100, Florian Fainelli wrote:
> +       /* check version */
> +       val = le16_to_cpu(p->revision);
> +       if (val == 1 || val > (1 << 4)) {
> +               printk(KERN_INFO "%s: unsupported ONFI version: %d\n", __func__, val);

Line length.

> +       chip->options &= ~NAND_CHIPOPTIONS_MSK;
> +       chip->options |= (NAND_NO_READRDY |
> +                       NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;

NAND_NO_AUTOINCR is not included in NAND_CHIPOPTIONS_MSK, so including it
here has no effect.

> +       ret = nand_flash_detect_onfi(mtd, chip, &busw);
> +       if (!ret)
> +               nand_flash_detect_non_onfi(mtd, chip, type, &busw);
> 
> -       } else {
> -               /*
> -                * Old devices have chip data hardcoded in the device id table
> -                */
> -               mtd->erasesize = type->erasesize;
> -               mtd->writesize = type->pagesize;
> -               mtd->oobsize = mtd->writesize / 32;
> -               busw = type->options & NAND_BUSWIDTH_16;
> -       }
> +       /* Get chip options, preserve non chip based options */
> +       chip->options &= ~NAND_CHIPOPTIONS_MSK;
> +       chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
> +       /*
> +        * Set chip as a default. Board drivers can override it, if necessary
> +        */
> +       chip->options |= NAND_NO_AUTOINCR;


Won't this duplicate/overwrite what was done in nand_flash_detect_*()?

> +       u8 input_pin_capacitance_max;
> +       u8 driver_strenght_support;
> +       __le16 t_int_r;
> +       __le16 t_ald;
> +       u8 reserved4[7];
> +
> +       /* vendor */
> +       u8 reserved5[90];
> +
> +        __le16 crc;

Extra space before __le16.

-Scott

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [U-Boot] [PATCH v3] NAND: add support for reading ONFI page table
  2011-01-04 20:42     ` Scott Wood
@ 2011-02-14 15:48       ` Florian Fainelli
  2011-02-14 23:46         ` Scott Wood
  0 siblings, 1 reply; 15+ messages in thread
From: Florian Fainelli @ 2011-02-14 15:48 UTC (permalink / raw)
  To: u-boot

From: Florian Fainelli <florian@openwrt.org>

This patch adds support for reading an ONFI page parameter from a NAND
device supporting it. If this is the case, struct nand_chip onfi_version
member contains the supported ONFI version, 0 otherwise.

This allows NAND drivers past nand_scan_ident to set the best timings for the
NAND chip.

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
Patch against your 'next' branch

Changes since v1:
- ifdef out ONFI detection code around CONFIG_SYS_NAND_ONFI_DETECTION
- removed bogus comment
- fixed busw variable usage
- move non-ONFI detection code to its own function
- fixed stylistic issues spotted by Scott

Changes since v2:
- reduce lenght of some lines down to 80 columns
- change chip->options consistently wrt to ONFI detected or not
- removed extra spaces

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 5239c1f..7becb99 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2409,15 +2409,137 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
 		chip->controller = &chip->hwcontrol;
 }

+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+	int i;
+
+	while (len--) {
+		crc ^= *p++ << 8;
+		for (i = 0; i < 8; i++)
+			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+	}
+
+	return crc;
+}
+
+/*
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
+ */
+static int nand_flash_detect_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					int *busw)
+{
+	struct nand_onfi_params *p = &chip->onfi_params;
+	int i;
+	int val;
+
+	chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
+	if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
+		chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
+		return 0;
+
+	printk(KERN_INFO "ONFI flash detected\n");
+	chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+	for (i = 0; i < 3; i++) {
+		chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+		if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+						le16_to_cpu(p->crc)) {
+			printk(KERN_INFO "ONFI param page %d valid\n", i);
+			break;
+		}
+	}
+
+	if (i == 3)
+		return 0;
+
+	/* check version */
+	val = le16_to_cpu(p->revision);
+	if (val == 1 || val > (1 << 4)) {
+		printk(KERN_INFO "%s: unsupported ONFI "
+					"version: %d\n", __func__, val);
+		return 0;
+	}
+
+	if (val & (1 << 4))
+		chip->onfi_version = 22;
+	else if (val & (1 << 3))
+		chip->onfi_version = 21;
+	else if (val & (1 << 2))
+		chip->onfi_version = 20;
+	else
+		chip->onfi_version = 10;
+
+	if (!mtd->name)
+		mtd->name = p->model;
+
+	mtd->writesize = le32_to_cpu(p->byte_per_page);
+	mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+	mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+	chip->chipsize = le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+	*busw = 0;
+	if (le16_to_cpu(p->features) & 1)
+		*busw = NAND_BUSWIDTH_16;
+
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK;
+
+	return 1;
+}
+#else
+static inline int nand_flash_detect_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					int *busw)
+{
+	return 0;
+}
+#endif
+
+static void nand_flash_detect_non_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					const struct nand_flash_dev *type,
+					int *busw)
+{
+	/* Newer devices have all the information in additional id bytes */
+	if (!type->pagesize) {
+		int extid;
+		/* The 3rd id byte holds MLC / multichip data */
+		chip->cellinfo = chip->read_byte(mtd);
+		/* The 4th id byte is the important one */
+		extid = chip->read_byte(mtd);
+		/* Calc pagesize */
+		mtd->writesize = 1024 << (extid & 0x3);
+		extid >>= 2;
+		/* Calc oobsize */
+		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
+		extid >>= 2;
+		/* Calc blocksize. Blocksize is multiples of 64KiB */
+		mtd->erasesize = (64 * 1024) << (extid & 0x03);
+		extid >>= 2;
+		/* Get buswidth information */
+		*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+
+	} else {
+		/*
+		 * Old devices have chip data hardcoded in the device id table
+		 */
+		mtd->erasesize = type->erasesize;
+		mtd->writesize = type->pagesize;
+		mtd->oobsize = mtd->writesize / 32;
+		*busw = type->options & NAND_BUSWIDTH_16;
+	}
+}
+
 /*
  * Get the flash and manufacturer id and lookup if the type is supported
  */
 static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 						  struct nand_chip *chip,
-						  int busw, int *maf_id)
+						  int busw,
+						  int *maf_id, int *dev_id)
 {
 	const struct nand_flash_dev *type = NULL;
-	int i, dev_id, maf_idx;
+	int i, ret, maf_idx;
 	int tmp_id, tmp_manf;

 	/* Select the device */
@@ -2434,7 +2556,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,

 	/* Read manufacturer and device IDs */
 	*maf_id = chip->read_byte(mtd);
-	dev_id = chip->read_byte(mtd);
+	*dev_id = chip->read_byte(mtd);

 	/* Try again to make sure, as some systems the bus-hold or other
 	 * interface concerns can cause random data which looks like a
@@ -2449,16 +2571,16 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	tmp_manf = chip->read_byte(mtd);
 	tmp_id = chip->read_byte(mtd);

-	if (tmp_manf != *maf_id || tmp_id != dev_id) {
+	if (tmp_manf != *maf_id || tmp_id != *dev_id) {
 		printk(KERN_INFO "%s: second ID read did not match "
 		       "%02x,%02x against %02x,%02x\n", __func__,
-		       *maf_id, dev_id, tmp_manf, tmp_id);
+		       *maf_id, *dev_id, tmp_manf, tmp_id);
 		return ERR_PTR(-ENODEV);
 	}

 	/* Lookup the flash id */
 	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
-		if (dev_id == nand_flash_ids[i].id) {
+		if (*dev_id == nand_flash_ids[i].id) {
 			type =  &nand_flash_ids[i];
 			break;
 		}
@@ -2467,10 +2589,10 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	if (!type) {
 		/* supress warning if there is no nand */
 		if (*maf_id != 0x00 && *maf_id != 0xff &&
-		    dev_id  != 0x00 && dev_id  != 0xff)
+		    *dev_id  != 0x00 && *dev_id  != 0xff)
 			printk(KERN_INFO "%s: unknown NAND device: "
 				"Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
-				__func__, *maf_id, dev_id);
+				__func__, *maf_id, *dev_id);
 		return ERR_PTR(-ENODEV);
 	}

@@ -2478,35 +2600,20 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		mtd->name = type->name;

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

-	/* Newer devices have all the information in additional id bytes */
-	if (!type->pagesize) {
-		int extid;
-		/* The 3rd id byte holds MLC / multichip data */
-		chip->cellinfo = chip->read_byte(mtd);
-		/* The 4th id byte is the important one */
-		extid = chip->read_byte(mtd);
-		/* Calc pagesize */
-		mtd->writesize = 1024 << (extid & 0x3);
-		extid >>= 2;
-		/* Calc oobsize */
-		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
-		extid >>= 2;
-		/* Calc blocksize. Blocksize is multiples of 64KiB */
-		mtd->erasesize = (64 * 1024) << (extid & 0x03);
-		extid >>= 2;
-		/* Get buswidth information */
-		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+	ret = nand_flash_detect_onfi(mtd, chip, &busw);
+	if (!ret)
+		nand_flash_detect_non_onfi(mtd, chip, type, &busw);

-	} else {
-		/*
-		 * Old devices have chip data hardcoded in the device id table
-		 */
-		mtd->erasesize = type->erasesize;
-		mtd->writesize = type->pagesize;
-		mtd->oobsize = mtd->writesize / 32;
-		busw = type->options & NAND_BUSWIDTH_16;
-	}
+	/* Get chip options, preserve non chip based options */
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+
+	/*
+	 * Set chip as a default. Board drivers can override it, if necessary
+	 */
+	chip->options |= NAND_NO_AUTOINCR;

 	/* Try to identify manufacturer */
 	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
@@ -2521,7 +2628,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	if (busw != (chip->options & NAND_BUSWIDTH_16)) {
 		printk(KERN_INFO "NAND device: Manufacturer ID:"
 		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
-		       dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
+		       *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
 		printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
 		       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
 		       busw ? 16 : 8);
@@ -2544,15 +2651,6 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	chip->badblockpos = mtd->writesize > 512 ?
 		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;

-	/* Get chip options, preserve non chip based options */
-	chip->options &= ~NAND_CHIPOPTIONS_MSK;
-	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
-
-	/*
-	 * Set chip as a default. Board drivers can override it, if necessary
-	 */
-	chip->options |= NAND_NO_AUTOINCR;
-
 	/* Check if chip is a not a samsung device. Do not clear the
 	 * options for chips which are not having an extended id.
 	 */
@@ -2570,7 +2668,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		chip->cmdfunc = nand_command_lp;

 	MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
-	          " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
+	          " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
 	          nand_manuf_ids[maf_idx].name, type->name);

 	return type;
@@ -2588,7 +2686,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
  */
 int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 {
-	int i, busw, nand_maf_id;
+	int i, busw, nand_maf_id, nand_dev_id;
 	struct nand_chip *chip = mtd->priv;
 	const struct nand_flash_dev *type;

@@ -2598,7 +2696,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 	nand_set_defaults(chip, busw);

 	/* Read the flash type */
-	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
+	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id);

 	if (IS_ERR(type)) {
 #ifndef CONFIG_SYS_NAND_QUIET_TEST
@@ -2617,7 +2715,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 		/* Read manufacturer and device IDs */
 		if (nand_maf_id != chip->read_byte(mtd) ||
-		    type->id != chip->read_byte(mtd))
+		    nand_dev_id != chip->read_byte(mtd))
 			break;
 	}
 #ifdef DEBUG
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 1128f5a..4b77094 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -215,6 +215,71 @@ typedef enum {
 /* Keep gcc happy */
 struct nand_chip;

+struct nand_onfi_params {
+	/* rev info and features block */
+	/* 'O' 'N' 'F' 'I'  */
+	u8 sig[4];
+	__le16 revision;
+	__le16 features;
+	__le16 opt_cmd;
+	u8 reserved[22];
+
+	/* manufacturer information block */
+	char manufacturer[12];
+	char model[20];
+	u8 jedec_id;
+	__le16 date_code;
+	u8 reserved2[13];
+
+	/* memory organization block */
+	__le32 byte_per_page;
+	__le16 spare_bytes_per_page;
+	__le32 data_bytes_per_ppage;
+	__le16 spare_bytes_per_ppage;
+	__le32 pages_per_block;
+	__le32 blocks_per_lun;
+	u8 lun_count;
+	u8 addr_cycles;
+	u8 bits_per_cell;
+	__le16 bb_per_lun;
+	__le16 block_endurance;
+	u8 guaranteed_good_blocks;
+	__le16 guaranteed_block_endurance;
+	u8 programs_per_page;
+	u8 ppage_attr;
+	u8 ecc_bits;
+	u8 interleaved_bits;
+	u8 interleaved_ops;
+	u8 reserved3[13];
+
+	/* electrical parameter block */
+	u8 io_pin_capacitance_max;
+	__le16 async_timing_mode;
+	__le16 program_cache_timing_mode;
+	__le16 t_prog;
+	__le16 t_bers;
+	__le16 t_r;
+	__le16 t_ccs;
+	__le16 src_sync_timing_mode;
+	__le16 src_ssync_features;
+	__le16 clk_pin_capacitance_typ;
+	__le16 io_pin_capacitance_typ;
+	__le16 input_pin_capacitance_typ;
+	u8 input_pin_capacitance_max;
+	u8 driver_strenght_support;
+	__le16 t_int_r;
+	__le16 t_ald;
+	u8 reserved4[7];
+
+	/* vendor */
+	u8 reserved5[90];
+
+	__le16 crc;
+} __attribute__((packed));
+
+#define ONFI_CRC_BASE	0x4F4E
+
+
 /**
  * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
  * @lock:               protection lock
@@ -404,6 +469,10 @@ struct nand_chip {
 	int		subpagesize;
 	uint8_t		cellinfo;
 	int		badblockpos;
+	int		onfi_version;
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+	struct nand_onfi_params onfi_params;
+#endif

 	int 		state;

--
1.7.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [U-Boot] [PATCH v3] NAND: add support for reading ONFI page table
  2011-02-14 15:48       ` [U-Boot] [PATCH v3] " Florian Fainelli
@ 2011-02-14 23:46         ` Scott Wood
  2011-02-15  5:28           ` Wolfgang Denk
                             ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Scott Wood @ 2011-02-14 23:46 UTC (permalink / raw)
  To: u-boot

On Mon, 14 Feb 2011 16:48:01 +0100
Florian Fainelli <florian@openwrt.org> wrote:

> From: Florian Fainelli <florian@openwrt.org>
> 
> This patch adds support for reading an ONFI page parameter from a NAND
> device supporting it. If this is the case, struct nand_chip onfi_version
> member contains the supported ONFI version, 0 otherwise.
> 
> This allows NAND drivers past nand_scan_ident to set the best timings for the
> NAND chip.
> 
> Signed-off-by: Florian Fainelli <florian@openwrt.org>
> ---
> Patch against your 'next' branch

The 'next' branch is old, since I haven't pushed anything to it yet this
cycle.  Base it on Wolfgang's 'next'.

> Changes since v1:
> - ifdef out ONFI detection code around CONFIG_SYS_NAND_ONFI_DETECTION
> - removed bogus comment
> - fixed busw variable usage
> - move non-ONFI detection code to its own function
> - fixed stylistic issues spotted by Scott
> 
> Changes since v2:
> - reduce lenght of some lines down to 80 columns
> - change chip->options consistently wrt to ONFI detected or not
> - removed extra spaces
> 
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index 5239c1f..7becb99 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -2409,15 +2409,137 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
>  		chip->controller = &chip->hwcontrol;
>  }
> 
> +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
> +static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
> +{
> +	int i;
> +
> +	while (len--) {
> +		crc ^= *p++ << 8;
> +		for (i = 0; i < 8; i++)
> +			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
> +	}
> +
> +	return crc;
> +}

Is this different from what's in lib/crc16.c (other than appearing to have
made a different speed/size tradeoff)?

> +#define ONFI_CRC_BASE	0x4F4E

Is this ONFI-specific, or standard for crc16?

> +
> +/*
> + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
> + */
> +static int nand_flash_detect_onfi(struct mtd_info *mtd,
> +					struct nand_chip *chip,
> +					int *busw)
> +{
[snip]
> +	chip->options &= ~NAND_CHIPOPTIONS_MSK;
> +	chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK;

Won't this get overwritten by this later?

> +	/* Get chip options, preserve non chip based options */
> +	chip->options &= ~NAND_CHIPOPTIONS_MSK;
> +	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;

-Scott

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [U-Boot] [PATCH v3] NAND: add support for reading ONFI page table
  2011-02-14 23:46         ` Scott Wood
@ 2011-02-15  5:28           ` Wolfgang Denk
  2011-02-25  9:59           ` Florian Fainelli
  2011-02-25 10:01           ` [U-Boot] [PATCH v4] " Florian Fainelli
  2 siblings, 0 replies; 15+ messages in thread
From: Wolfgang Denk @ 2011-02-15  5:28 UTC (permalink / raw)
  To: u-boot

Dear Scott Wood,

In message <20110214174607.2b56dca8@schlenkerla.am.freescale.net> you wrote:
> 
> The 'next' branch is old, since I haven't pushed anything to it yet this
> cycle.  Base it on Wolfgang's 'next'.

I don't have a "next" yet.  I will create it when -rc2 is out.

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
Plans break down. You cannot plan the future. Only presumptuous fools
plan. The wise man _steers_.        - Terry Pratchett, _Making_Money_

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [U-Boot] [PATCH v3] NAND: add support for reading ONFI page table
  2011-02-14 23:46         ` Scott Wood
  2011-02-15  5:28           ` Wolfgang Denk
@ 2011-02-25  9:59           ` Florian Fainelli
  2011-02-25 10:01           ` [U-Boot] [PATCH v4] " Florian Fainelli
  2 siblings, 0 replies; 15+ messages in thread
From: Florian Fainelli @ 2011-02-25  9:59 UTC (permalink / raw)
  To: u-boot

Hello Scott,

(sorry for the lag in answering)

On Tuesday 15 February 2011 00:46:07 Scott Wood wrote:
> On Mon, 14 Feb 2011 16:48:01 +0100
> 
> Florian Fainelli <florian@openwrt.org> wrote:
> > From: Florian Fainelli <florian@openwrt.org>
> > 
> > This patch adds support for reading an ONFI page parameter from a NAND
> > device supporting it. If this is the case, struct nand_chip onfi_version
> > member contains the supported ONFI version, 0 otherwise.
> > 
> > This allows NAND drivers past nand_scan_ident to set the best timings for
> > the NAND chip.
> > 
> > Signed-off-by: Florian Fainelli <florian@openwrt.org>
> > ---
> > Patch against your 'next' branch
> 
> The 'next' branch is old, since I haven't pushed anything to it yet this
> cycle.  Base it on Wolfgang's 'next'.
> 
> > Changes since v1:
> > - ifdef out ONFI detection code around CONFIG_SYS_NAND_ONFI_DETECTION
> > - removed bogus comment
> > - fixed busw variable usage
> > - move non-ONFI detection code to its own function
> > - fixed stylistic issues spotted by Scott
> > 
> > Changes since v2:
> > - reduce lenght of some lines down to 80 columns
> > - change chip->options consistently wrt to ONFI detected or not
> > - removed extra spaces
> > 
> > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > index 5239c1f..7becb99 100644
> > --- a/drivers/mtd/nand/nand_base.c
> > +++ b/drivers/mtd/nand/nand_base.c
> > @@ -2409,15 +2409,137 @@ static void nand_set_defaults(struct nand_chip
> > *chip, int busw)
> > 
> >  		chip->controller = &chip->hwcontrol;
> >  
> >  }
> > 
> > +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
> > +static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
> > +{
> > +	int i;
> > +
> > +	while (len--) {
> > +		crc ^= *p++ << 8;
> > +		for (i = 0; i < 8; i++)
> > +			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
> > +	}
> > +
> > +	return crc;
> > +}
> 
> Is this different from what's in lib/crc16.c (other than appearing to have
> made a different speed/size tradeoff)?

Yes it is a different version of the CRC16 function.

> 
> > +#define ONFI_CRC_BASE	0x4F4E
> 
> Is this ONFI-specific, or standard for crc16?
> 
> > +
> > +/*
> > + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0
> > otherwise + */
> > +static int nand_flash_detect_onfi(struct mtd_info *mtd,
> > +					struct nand_chip *chip,
> > +					int *busw)
> > +{
> 
> [snip]
> 
> > +	chip->options &= ~NAND_CHIPOPTIONS_MSK;
> > +	chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK;
> 
> Won't this get overwritten by this later?

It will, thanks for spotting.
--
Florian

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [U-Boot] [PATCH v4] NAND: add support for reading ONFI page table
  2011-02-14 23:46         ` Scott Wood
  2011-02-15  5:28           ` Wolfgang Denk
  2011-02-25  9:59           ` Florian Fainelli
@ 2011-02-25 10:01           ` Florian Fainelli
  2011-03-16 23:42             ` [U-Boot] [U-Boot, " Scott Wood
  2 siblings, 1 reply; 15+ messages in thread
From: Florian Fainelli @ 2011-02-25 10:01 UTC (permalink / raw)
  To: u-boot

From: Florian Fainelli <florian@openwrt.org>

This patch adds support for reading an ONFI page parameter from a NAND
device supporting it. If this is the case, struct nand_chip onfi_version
member contains the supported ONFI version, 0 otherwise.

This allows NAND drivers past nand_scan_ident to set the best timings for the
NAND chip.

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
Patch against your 'master' branch

Changes since v1:
- ifdef out ONFI detection code around CONFIG_SYS_NAND_ONFI_DETECTION
- removed bogus comment
- fixed busw variable usage
- move non-ONFI detection code to its own function
- fixed stylistic issues spotted by Scott

Changes since v2:
- reduce lenght of some lines down to 80 columns
- change chip->options consistently wrt to ONFI detected or not
- removed extra spaces

Changes since v3:
- removed chip->options modification in nand_flash_detect_onfi because it is
  overwritten in nand_flash_detect() later

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 70c0593..9034945 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2409,15 +2409,134 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
 		chip->controller = &chip->hwcontrol;
 }

+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+	int i;
+
+	while (len--) {
+		crc ^= *p++ << 8;
+		for (i = 0; i < 8; i++)
+			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+	}
+
+	return crc;
+}
+
+/*
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
+ */
+static int nand_flash_detect_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					int *busw)
+{
+	struct nand_onfi_params *p = &chip->onfi_params;
+	int i;
+	int val;
+
+	chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
+	if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
+		chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
+		return 0;
+
+	printk(KERN_INFO "ONFI flash detected\n");
+	chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+	for (i = 0; i < 3; i++) {
+		chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+		if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+						le16_to_cpu(p->crc)) {
+			printk(KERN_INFO "ONFI param page %d valid\n", i);
+			break;
+		}
+	}
+
+	if (i == 3)
+		return 0;
+
+	/* check version */
+	val = le16_to_cpu(p->revision);
+	if (val == 1 || val > (1 << 4)) {
+		printk(KERN_INFO "%s: unsupported ONFI "
+					"version: %d\n", __func__, val);
+		return 0;
+	}
+
+	if (val & (1 << 4))
+		chip->onfi_version = 22;
+	else if (val & (1 << 3))
+		chip->onfi_version = 21;
+	else if (val & (1 << 2))
+		chip->onfi_version = 20;
+	else
+		chip->onfi_version = 10;
+
+	if (!mtd->name)
+		mtd->name = p->model;
+
+	mtd->writesize = le32_to_cpu(p->byte_per_page);
+	mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+	mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+	chip->chipsize = le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+	*busw = 0;
+	if (le16_to_cpu(p->features) & 1)
+		*busw = NAND_BUSWIDTH_16;
+
+	return 1;
+}
+#else
+static inline int nand_flash_detect_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					int *busw)
+{
+	return 0;
+}
+#endif
+
+static void nand_flash_detect_non_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					const struct nand_flash_dev *type,
+					int *busw)
+{
+	/* Newer devices have all the information in additional id bytes */
+	if (!type->pagesize) {
+		int extid;
+		/* The 3rd id byte holds MLC / multichip data */
+		chip->cellinfo = chip->read_byte(mtd);
+		/* The 4th id byte is the important one */
+		extid = chip->read_byte(mtd);
+		/* Calc pagesize */
+		mtd->writesize = 1024 << (extid & 0x3);
+		extid >>= 2;
+		/* Calc oobsize */
+		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
+		extid >>= 2;
+		/* Calc blocksize. Blocksize is multiples of 64KiB */
+		mtd->erasesize = (64 * 1024) << (extid & 0x03);
+		extid >>= 2;
+		/* Get buswidth information */
+		*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+
+	} else {
+		/*
+		 * Old devices have chip data hardcoded in the device id table
+		 */
+		mtd->erasesize = type->erasesize;
+		mtd->writesize = type->pagesize;
+		mtd->oobsize = mtd->writesize / 32;
+		*busw = type->options & NAND_BUSWIDTH_16;
+	}
+}
+
 /*
  * Get the flash and manufacturer id and lookup if the type is supported
  */
 static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 						  struct nand_chip *chip,
-						  int busw, int *maf_id,
+						  int busw,
+						  int *maf_id, int *dev_id,
 						  const struct nand_flash_dev *type)
 {
-	int dev_id, maf_idx;
+	int ret, maf_idx;
 	int tmp_id, tmp_manf;

 	/* Select the device */
@@ -2434,7 +2553,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,

 	/* Read manufacturer and device IDs */
 	*maf_id = chip->read_byte(mtd);
-	dev_id = chip->read_byte(mtd);
+	*dev_id = chip->read_byte(mtd);

 	/* Try again to make sure, as some systems the bus-hold or other
 	 * interface concerns can cause random data which looks like a
@@ -2449,10 +2568,10 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	tmp_manf = chip->read_byte(mtd);
 	tmp_id = chip->read_byte(mtd);

-	if (tmp_manf != *maf_id || tmp_id != dev_id) {
+	if (tmp_manf != *maf_id || tmp_id != *dev_id) {
 		printk(KERN_INFO "%s: second ID read did not match "
 		       "%02x,%02x against %02x,%02x\n", __func__,
-		       *maf_id, dev_id, tmp_manf, tmp_id);
+		       *maf_id, *dev_id, tmp_manf, tmp_id);
 		return ERR_PTR(-ENODEV);
 	}

@@ -2466,10 +2585,10 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	if (!type->name) {
 		/* supress warning if there is no nand */
 		if (*maf_id != 0x00 && *maf_id != 0xff &&
-		    dev_id  != 0x00 && dev_id  != 0xff)
+		    *dev_id  != 0x00 && *dev_id  != 0xff)
 			printk(KERN_INFO "%s: unknown NAND device: "
 				"Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
-				__func__, *maf_id, dev_id);
+				__func__, *maf_id, *dev_id);
 		return ERR_PTR(-ENODEV);
 	}

@@ -2477,35 +2596,20 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		mtd->name = type->name;

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

-	/* Newer devices have all the information in additional id bytes */
-	if (!type->pagesize) {
-		int extid;
-		/* The 3rd id byte holds MLC / multichip data */
-		chip->cellinfo = chip->read_byte(mtd);
-		/* The 4th id byte is the important one */
-		extid = chip->read_byte(mtd);
-		/* Calc pagesize */
-		mtd->writesize = 1024 << (extid & 0x3);
-		extid >>= 2;
-		/* Calc oobsize */
-		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
-		extid >>= 2;
-		/* Calc blocksize. Blocksize is multiples of 64KiB */
-		mtd->erasesize = (64 * 1024) << (extid & 0x03);
-		extid >>= 2;
-		/* Get buswidth information */
-		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+	ret = nand_flash_detect_onfi(mtd, chip, &busw);
+	if (!ret)
+		nand_flash_detect_non_onfi(mtd, chip, type, &busw);

-	} else {
-		/*
-		 * Old devices have chip data hardcoded in the device id table
-		 */
-		mtd->erasesize = type->erasesize;
-		mtd->writesize = type->pagesize;
-		mtd->oobsize = mtd->writesize / 32;
-		busw = type->options & NAND_BUSWIDTH_16;
-	}
+	/* Get chip options, preserve non chip based options */
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+
+	/*
+	 * Set chip as a default. Board drivers can override it, if necessary
+	 */
+	chip->options |= NAND_NO_AUTOINCR;

 	/* Try to identify manufacturer */
 	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
@@ -2520,7 +2624,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	if (busw != (chip->options & NAND_BUSWIDTH_16)) {
 		printk(KERN_INFO "NAND device: Manufacturer ID:"
 		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
-		       dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
+		       *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
 		printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
 		       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
 		       busw ? 16 : 8);
@@ -2543,15 +2647,6 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	chip->badblockpos = mtd->writesize > 512 ?
 		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;

-	/* Get chip options, preserve non chip based options */
-	chip->options &= ~NAND_CHIPOPTIONS_MSK;
-	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
-
-	/*
-	 * Set chip as a default. Board drivers can override it, if necessary
-	 */
-	chip->options |= NAND_NO_AUTOINCR;
-
 	/* Check if chip is a not a samsung device. Do not clear the
 	 * options for chips which are not having an extended id.
 	 */
@@ -2569,7 +2664,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		chip->cmdfunc = nand_command_lp;

 	MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
-		  " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
+		  " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
 		  nand_manuf_ids[maf_idx].name, type->name);

 	return type;
@@ -2589,7 +2684,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 		    const struct nand_flash_dev *table)
 {
-	int i, busw, nand_maf_id;
+	int i, busw, nand_maf_id, nand_dev_id;
 	struct nand_chip *chip = mtd->priv;
 	const struct nand_flash_dev *type;

@@ -2599,7 +2694,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 	nand_set_defaults(chip, busw);

 	/* Read the flash type */
-	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table);
+	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id, table);

 	if (IS_ERR(type)) {
 #ifndef CONFIG_SYS_NAND_QUIET_TEST
@@ -2618,7 +2713,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 		/* Read manufacturer and device IDs */
 		if (nand_maf_id != chip->read_byte(mtd) ||
-		    type->id != chip->read_byte(mtd))
+		    nand_dev_id != chip->read_byte(mtd))
 			break;
 	}
 #ifdef DEBUG
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index d299929..987a2ec 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -217,6 +217,71 @@ typedef enum {
 /* Keep gcc happy */
 struct nand_chip;

+struct nand_onfi_params {
+	/* rev info and features block */
+	/* 'O' 'N' 'F' 'I'  */
+	u8 sig[4];
+	__le16 revision;
+	__le16 features;
+	__le16 opt_cmd;
+	u8 reserved[22];
+
+	/* manufacturer information block */
+	char manufacturer[12];
+	char model[20];
+	u8 jedec_id;
+	__le16 date_code;
+	u8 reserved2[13];
+
+	/* memory organization block */
+	__le32 byte_per_page;
+	__le16 spare_bytes_per_page;
+	__le32 data_bytes_per_ppage;
+	__le16 spare_bytes_per_ppage;
+	__le32 pages_per_block;
+	__le32 blocks_per_lun;
+	u8 lun_count;
+	u8 addr_cycles;
+	u8 bits_per_cell;
+	__le16 bb_per_lun;
+	__le16 block_endurance;
+	u8 guaranteed_good_blocks;
+	__le16 guaranteed_block_endurance;
+	u8 programs_per_page;
+	u8 ppage_attr;
+	u8 ecc_bits;
+	u8 interleaved_bits;
+	u8 interleaved_ops;
+	u8 reserved3[13];
+
+	/* electrical parameter block */
+	u8 io_pin_capacitance_max;
+	__le16 async_timing_mode;
+	__le16 program_cache_timing_mode;
+	__le16 t_prog;
+	__le16 t_bers;
+	__le16 t_r;
+	__le16 t_ccs;
+	__le16 src_sync_timing_mode;
+	__le16 src_ssync_features;
+	__le16 clk_pin_capacitance_typ;
+	__le16 io_pin_capacitance_typ;
+	__le16 input_pin_capacitance_typ;
+	u8 input_pin_capacitance_max;
+	u8 driver_strenght_support;
+	__le16 t_int_r;
+	__le16 t_ald;
+	u8 reserved4[7];
+
+	/* vendor */
+	u8 reserved5[90];
+
+	__le16 crc;
+} __attribute__((packed));
+
+#define ONFI_CRC_BASE	0x4F4E
+
+
 /**
  * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
  * @lock:               protection lock
@@ -406,6 +471,10 @@ struct nand_chip {
 	int		subpagesize;
 	uint8_t		cellinfo;
 	int		badblockpos;
+	int		onfi_version;
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+	struct nand_onfi_params onfi_params;
+#endif

 	int 		state;

--
1.7.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [U-Boot] [U-Boot, v4] NAND: add support for reading ONFI page table
  2011-02-25 10:01           ` [U-Boot] [PATCH v4] " Florian Fainelli
@ 2011-03-16 23:42             ` Scott Wood
  2011-03-17 16:59               ` Florian Fainelli
  0 siblings, 1 reply; 15+ messages in thread
From: Scott Wood @ 2011-03-16 23:42 UTC (permalink / raw)
  To: u-boot

On Fri, Feb 25, 2011 at 12:01:34AM -0000, Florian Fainelli wrote:
> From: Florian Fainelli <florian@openwrt.org>
> 
> This patch adds support for reading an ONFI page parameter from a NAND
> device supporting it. If this is the case, struct nand_chip onfi_version
> member contains the supported ONFI version, 0 otherwise.
> 
> This allows NAND drivers past nand_scan_ident to set the best timings for the
> NAND chip.
> 
> Signed-off-by: Florian Fainelli <florian@openwrt.org>

Applied to u-boot-nand-flash next with this change:

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 0d7c819..fa286a8 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2585,7 +2585,7 @@ static const struct nand_flash_dev *nand_get_flash_type(st
                type = nand_flash_ids;
 
        for (; type->name != NULL; type++)
-               if (dev_id == type->id)
+               if (*dev_id == type->id)
                        break;
 

-Scott

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [U-Boot] [U-Boot, v4] NAND: add support for reading ONFI page table
  2011-03-16 23:42             ` [U-Boot] [U-Boot, " Scott Wood
@ 2011-03-17 16:59               ` Florian Fainelli
  0 siblings, 0 replies; 15+ messages in thread
From: Florian Fainelli @ 2011-03-17 16:59 UTC (permalink / raw)
  To: u-boot

Le jeudi 17 mars 2011 00:42:55, Scott Wood a ?crit :
> On Fri, Feb 25, 2011 at 12:01:34AM -0000, Florian Fainelli wrote:
> > From: Florian Fainelli <florian@openwrt.org>
> > 
> > This patch adds support for reading an ONFI page parameter from a NAND
> > device supporting it. If this is the case, struct nand_chip onfi_version
> > member contains the supported ONFI version, 0 otherwise.
> > 
> > This allows NAND drivers past nand_scan_ident to set the best timings for
> > the NAND chip.
> > 
> > Signed-off-by: Florian Fainelli <florian@openwrt.org>
> 
> Applied to u-boot-nand-flash next with this change:
> 
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index 0d7c819..fa286a8 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -2585,7 +2585,7 @@ static const struct nand_flash_dev
> *nand_get_flash_type(st type = nand_flash_ids;
> 
>         for (; type->name != NULL; type++)
> -               if (dev_id == type->id)
> +               if (*dev_id == type->id)
>                         break;

My bad, thanks for fixing this and committing the patch!
--
Florian

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2011-03-17 16:59 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-10 22:16 [U-Boot] [PATCH 1/2] NAND: add NAND_CMD_PARAM (0xec) definition Florian Fainelli
2010-12-10 22:16 ` [U-Boot] [PATCH 2/2] NAND: add support for reading ONFI page table Florian Fainelli
2010-12-11  0:15   ` [U-Boot] [U-Boot, " Scott Wood
2010-12-11 17:08     ` Florian Fainelli
2010-12-28  0:21   ` [U-Boot] [PATCH v2] " Florian Fainelli
2010-12-28  0:23     ` Florian Fainelli
2011-01-04 20:42     ` Scott Wood
2011-02-14 15:48       ` [U-Boot] [PATCH v3] " Florian Fainelli
2011-02-14 23:46         ` Scott Wood
2011-02-15  5:28           ` Wolfgang Denk
2011-02-25  9:59           ` Florian Fainelli
2011-02-25 10:01           ` [U-Boot] [PATCH v4] " Florian Fainelli
2011-03-16 23:42             ` [U-Boot] [U-Boot, " Scott Wood
2011-03-17 16:59               ` Florian Fainelli
2010-12-10 23:47 ` [U-Boot] [U-Boot, 1/2] NAND: add NAND_CMD_PARAM (0xec) definition Scott Wood

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.