From: Matthieu CASTET <matthieu.castet@parrot.com>
To: Florian Fainelli <ffainelli@freebox.fr>
Cc: David Woodhouse <dwmw2@infradead.org>,
"linux-mtd@lists.infradead.org" <linux-mtd@lists.infradead.org>,
Brian Norris <norris@broadcom.com>,
Maxime Bizon <mbizon@freebox.fr>
Subject: Re: [PATCH] NAND: add support for reading ONFI parameters from NAND device
Date: Mon, 2 Aug 2010 11:25:49 +0200 [thread overview]
Message-ID: <4C568F1D.4020007@parrot.com> (raw)
In-Reply-To: <201007291051.38278.ffainelli@freebox.fr>
[-- Attachment #1: Type: text/plain, Size: 1060 bytes --]
Florian Fainelli a écrit :
> Hi Matthieu,
>
> On Thursday 29 July 2010 09:54:20 Matthieu CASTET wrote:
>> Hi,
>>
>>
>> Also you don't handle endianness (integer are little endian) for value
>> in nand_onfi_params.
>
> Yes, so far the drivers using those values were doing the correct endian
> conversion when they need to use them.
In that case use le16, le32, ... type. Also prefer kernel type over
uintx_t type.
>> This won't work this unknown nand, and not work with some LP nand that
>> doesn't provide additional id bytes.
>
> So how do you see things regarding the provisioning of the relevant ONFI
> parameters?
I will see something like in the patch attached in
http://article.gmane.org/gmane.linux.drivers.mtd/30935.
ONFI parsing is done early in nand_get_flash_type (unknow chip or LP nand).
If the ONFI parsing is ok we bypass the old identification method
(additional id bytes).
As an example I attach a patch that mix your patch and mine.
Matthieu
PS : the NAND_ONFI flags seems useless, we can use onfi_version (0 means
no onfi).
[-- Attachment #2: onfi.diff --]
[-- Type: text/x-diff, Size: 10918 bytes --]
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 4a7b864..25a44fa 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2772,15 +2772,50 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
}
+static u16 onfi_crc(u16 crc, unsigned char 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;
+}
+
+/*
+ * sanitize ONFI strings so we can safely print them
+ */
+static void sanitize_string(uint8_t *s, size_t len)
+{
+ ssize_t i;
+
+ /* null terminate */
+ s[len - 1] = 0;
+
+ /* remove non printable chars */
+ for (i = 0; i < len - 1; i++) {
+ if (s[i] < ' ' || s[i] > 127)
+ s[i] = '?';
+ }
+
+ /* remove trailing spaces */
+ for (i = len - 1; i >= 0; i--) {
+ if (s[i] && s[i] != ' ')
+ break;
+ s[i] = 0;
+ }
+}
+
/*
* 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)
{
- int i, dev_id, maf_idx;
+ int i, maf_idx;
u8 id_data[8];
/* Select the device */
@@ -2797,7 +2832,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
@@ -2807,15 +2842,13 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
- /* Read entire ID string */
-
- for (i = 0; i < 8; i++)
+ for (i = 0; i < 2; i++)
id_data[i] = chip->read_byte(mtd);
- if (id_data[0] != *maf_id || id_data[1] != dev_id) {
+ if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
printk(KERN_INFO "%s: second ID read did not match "
"%02x,%02x against %02x,%02x\n", __func__,
- *maf_id, dev_id, id_data[0], id_data[1]);
+ *maf_id, *dev_id, id_data[0], id_data[1]);
return ERR_PTR(-ENODEV);
}
@@ -2823,8 +2856,79 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
type = nand_flash_ids;
for (; type->name != NULL; type++)
- if (dev_id == type->id)
+ if (*dev_id == type->id)
break;
+#if 1
+ chip->onfi_version = 0;
+ if (!type->name || !type->pagesize) {
+ /* 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') {
+
+ struct nand_onfi_params *p = &chip->onfi_params;
+ int i;
+
+ printk(KERN_INFO "ONFI flash detected\n");
+ chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+ for (i = 0; i < 3; i++) {
+ /* XXX this that ok to use read_buf at this stage ? */
+ chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+ if (onfi_crc(0x4F4E, (uint8_t *)p, 254) == le16_to_cpu(p->crc))
+ {
+ printk(KERN_INFO "ONFI param page %d valid\n", i);
+ break;
+ }
+ }
+ if (i < 3) {
+ /* check version */
+ int val = le16_to_cpu(p->revision);
+ if (!is_power_of_2(val) || val == 1 || val > (1 << 4)) {
+ printk(KERN_INFO "%s: unsupported ONFI version\n", __func__);
+ }
+ else {
+ if (val & (1 << 1))
+ chip->onfi_version = 10;
+ else if (val & (1 << 2))
+ chip->onfi_version = 20;
+ else if (val & (1 << 3))
+ chip->onfi_version = 21;
+ else
+ chip->onfi_version = 22;
+ }
+ }
+ if (chip->onfi_version) {
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+ 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;
+
+ /* emulate cellinfo ??? */
+ //chip->cellinfo = 0x8;
+ goto ident_done;
+
+ }
+ }
+ }
+#endif
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read entire ID string */
+
+ for (i = 0; i < 8; i++)
+ id_data[i] = chip->read_byte(mtd);
if (!type->name)
return ERR_PTR(-ENODEV);
@@ -2886,6 +2990,21 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
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;
+
+ /* Check if chip is a not a samsung device. Do not clear the
+ * options for chips which are not having an extended id.
+ */
+ if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
+ chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+ident_done:
+
+ /*
+ * 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++) {
@@ -2900,7 +3019,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);
@@ -2924,21 +3043,6 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
chip->badblockbits = 8;
- /* 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.
- */
- if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
- chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
-
/*
* Bad block marker is stored in the last page of each block
* on Samsung and Hynix MLC devices
@@ -2958,9 +3062,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;
+ /* TODO onfi flash name */
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, type->name);
+ " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
+ nand_manuf_ids[maf_idx].name, chip->onfi_version?type->name:chip->onfi_params.model);
return type;
}
@@ -2979,7 +3084,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
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;
struct nand_flash_dev *type;
@@ -2989,7 +3094,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)) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
@@ -3007,7 +3112,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;
}
if (i > 1)
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index a81b185..c358100 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -88,6 +88,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
#define NAND_CMD_RNDIN 0x85
#define NAND_CMD_READID 0x90
#define NAND_CMD_ERASE2 0xd0
+#define NAND_CMD_PARAM 0xec
#define NAND_CMD_RESET 0xff
#define NAND_CMD_LOCK 0x2a
@@ -229,6 +230,68 @@ typedef enum {
/* Keep gcc happy */
struct nand_chip;
+/* XXX use le16, le32 type */
+struct nand_onfi_params {
+ /* rev info and features block */
+ uint8_t sig[4]; /* 'O' 'N' 'F' 'I' */
+ uint16_t revision;
+ uint16_t features;
+ uint16_t opt_cmd;
+ uint8_t reserved[22];
+
+ /* manufacturer information block */
+ char manufacturer[12];
+ char model[20];
+ uint8_t jedec_id;
+ uint16_t date_code;
+ uint8_t reserved2[13];
+
+ /* memory organization block */
+ uint32_t byte_per_page;
+ uint16_t spare_bytes_per_page;
+ uint32_t data_bytes_per_ppage;
+ uint16_t sparre_bytes_per_ppage;
+ uint32_t pages_per_block;
+ uint32_t blocks_per_lun;
+ uint8_t lun_count;
+ uint8_t addr_cycles;
+ uint8_t bits_per_cell;
+ uint16_t bb_per_lun;
+ uint16_t block_endurance;
+ uint8_t guaranteed_good_blocks;
+ uint16_t guaranteed_block_endurance;
+ uint8_t programs_per_page;
+ uint8_t ppage_attr;
+ uint8_t ecc_bits;
+ uint8_t interleaved_bits;
+ uint8_t interleaved_ops;
+ uint8_t reserved3[13];
+
+ /* electrical parameter block */
+ uint8_t io_pin_capacitance_max;
+ uint16_t async_timing_mode;
+ uint16_t program_cache_timing_mode;
+ uint16_t t_prog;
+ uint16_t t_bers;
+ uint16_t t_r;
+ uint16_t t_ccs;
+ uint16_t src_sync_timing_mode;
+ uint16_t src_ssync_features;
+ uint16_t clk_pin_capacitance_typ;
+ uint16_t io_pin_capacitance_typ;
+ uint16_t input_pin_capacitance_typ;
+ uint8_t input_pin_capacitance_max;
+ uint8_t driver_strenght_support;
+ uint16_t t_int_r;
+ uint16_t t_ald;
+ uint8_t reserved4[7];
+
+ /* vendor */
+ uint8_t reserved5[90];
+
+ __le16 crc;
+} __attribute__((packed));
+
/**
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
* @lock: protection lock
@@ -413,6 +476,9 @@ struct nand_chip {
int badblockpos;
int badblockbits;
+ int onfi_version;
+ struct nand_onfi_params onfi_params;
+
flstate_t state;
uint8_t *oob_poi;
next prev parent reply other threads:[~2010-08-02 9:25 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-07-28 22:47 [PATCH] NAND: add support for reading ONFI parameters from NAND device Florian Fainelli
2010-07-28 23:38 ` Brian Norris
2010-07-29 8:10 ` Florian Fainelli
2010-07-29 7:54 ` Matthieu CASTET
2010-07-29 8:51 ` Florian Fainelli
2010-08-02 9:25 ` Matthieu CASTET [this message]
2010-08-02 11:55 ` Florian Fainelli
2010-08-09 9:25 ` Matthieu CASTET
2010-08-09 9:43 ` Florian Fainelli
2010-08-05 4:54 ` Artem Bityutskiy
2010-08-05 12:56 ` Maxime Bizon
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=4C568F1D.4020007@parrot.com \
--to=matthieu.castet@parrot.com \
--cc=dwmw2@infradead.org \
--cc=ffainelli@freebox.fr \
--cc=linux-mtd@lists.infradead.org \
--cc=mbizon@freebox.fr \
--cc=norris@broadcom.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox