From: Florian Fainelli <florian@openwrt.org>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v3] NAND: add support for reading ONFI page table
Date: Mon, 14 Feb 2011 16:48:01 +0100 [thread overview]
Message-ID: <201102141648.01214.florian@openwrt.org> (raw)
In-Reply-To: <20110104204210.GD22641@udp111988uds.am.freescale.net>
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
next prev parent reply other threads:[~2011-02-14 15:48 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Florian Fainelli [this message]
2011-02-14 23:46 ` [U-Boot] [PATCH v3] " 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
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=201102141648.01214.florian@openwrt.org \
--to=florian@openwrt.org \
--cc=u-boot@lists.denx.de \
/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.