From: David Brownell <david-b@pacbell.net>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Bryan Wu <cooloney@kernel.org>,
linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org,
Michael Hennerich <michael.hennerich@analog.com>
Subject: [RESEND PATCH 2.6.26-git 1/2] MTD DataFlash: bugfix, binary page sizes now handled (v3)
Date: Fri, 25 Jul 2008 15:49:12 -0700 [thread overview]
Message-ID: <200807251549.12565.david-b@pacbell.net> (raw)
In-Reply-To: <200806282321.34275.david-b@pacbell.net>
From: Michael Hennerich <michael.hennerich@analog.com>
This fixes a bug in handling certain newer DataFlash chips (at45db321d
and at45db642d have current Linux users) when they were configured to
use binary page sizes (possibly at the factory).
- Use JEDEC probing (borrowed from m25p80 driver) to tell if the chip
supports binary page sizes ... if so, then query the chip to tell
whether it's currently using them.
- Print that pagesize during probe; erasesize is still listed in
the /proc/mtd file, useful for making filesystems.
Also fix a pre-existing bug with 2 MBit parts using the wrong pagesize;
these are a bit smaller than Linux would normally use (even shared with
a small FPGA's configuration bitstream).
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Bryan Wu <cooloney@kernel.org>
Fix regression (so cmdlinepart works again), update error handling
and comments.
# updated to use dev_name(dev) not dev->bus_id
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
drivers/mtd/devices/mtd_dataflash.c | 154 ++++++++++++++++++++++++++++++++++--
1 file changed, 149 insertions(+), 5 deletions(-)
--- a/drivers/mtd/devices/mtd_dataflash.c 2008-06-30 16:44:26.000000000 -0700
+++ b/drivers/mtd/devices/mtd_dataflash.c 2008-06-30 21:38:22.000000000 -0700
@@ -15,6 +15,8 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mutex.h>
+#include <linux/err.h>
+
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
@@ -487,7 +489,8 @@ add_dataflash(struct spi_device *spi, ch
device->write = dataflash_write;
device->priv = priv;
- dev_info(&spi->dev, "%s (%d KBytes)\n", name, device->size/1024);
+ dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes\n",
+ name, DIV_ROUND_UP(device->size, 1024), pagesize);
dev_set_drvdata(&spi->dev, priv);
if (mtd_has_partitions()) {
@@ -516,12 +519,137 @@ add_dataflash(struct spi_device *spi, ch
return add_mtd_device(device) == 1 ? -ENODEV : 0;
}
+struct flash_info {
+ char *name;
+
+ /* JEDEC id zero means "no ID" (most older chips); otherwise it has
+ * a high byte of zero plus three data bytes: the manufacturer id,
+ * then a two byte device id.
+ */
+ u32 jedec_id;
+
+ /* The size listed here is what works with OP_ERASE_PAGE. */
+ unsigned nr_pages;
+ u16 pagesize;
+ u16 pageoffset;
+
+ u16 flags;
+#define SUP_POW2PS 0x0001 /* supports 2^N byte pages */
+#define IS_POW2PS 0x0002 /* uses 2^N byte pages */
+};
+
+static struct flash_info __devinitdata dataflash_data [] = {
+
+ /*
+ * NOTE: chips with SUP_POW2PS (rev D and up) need two entries,
+ * one with IS_POW2PS and the other without. The entry with the
+ * non-2^N byte page size can't name exact chip revisions without
+ * losing backwards compatibility for cmdlinepart.
+ *
+ * These newer chips also support 128-byte security registers (with
+ * 64 bytes one-time-programmable) and software write-protection.
+ */
+ { "AT45DB011B", 0x1f2200, 512, 264, 9, SUP_POW2PS, },
+ { "at45db011d", 0x1f2200, 512, 256, 8, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB021B", 0x1f2300, 1024, 264, 9, SUP_POW2PS, },
+ { "at45db021d", 0x1f2300, 1024, 256, 8, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB041x", 0x1f2400, 2048, 264, 9, SUP_POW2PS, },
+ { "at45db041d", 0x1f2400, 2048, 256, 8, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB081B", 0x1f2500, 4096, 264, 9, SUP_POW2PS, },
+ { "at45db081d", 0x1f2500, 4096, 256, 8, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB161x", 0x1f2600, 4096, 528, 10, SUP_POW2PS, },
+ { "at45db161d", 0x1f2600, 4096, 512, 9, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB321x", 0x1f2700, 8192, 528, 10, 0, }, /* rev C */
+ { "AT45DB321x", 0x1f2701, 8192, 528, 10, SUP_POW2PS, },
+ { "at45db321d", 0x1f2701, 8192, 512, 9, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB642x", 0x1f2800, 8192, 1056, 11, SUP_POW2PS, },
+ { "at45db642d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS, },
+};
+
+static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+{
+ int tmp;
+ u8 code = OP_READ_ID;
+ u8 id[3];
+ u32 jedec;
+ struct flash_info *info;
+ int status;
+
+ /*
+ * JEDEC also defines an optional "extended device information"
+ * string for after vendor-specific data, after the three bytes
+ * we use here. Supporting some chips might require using it.
+ *
+ * If the vendor ID isn't Atmel's (0x1f), assume this call failed.
+ * That's not an error; only rev C and newer chips handle it, and
+ * only Atmel sells these chips.
+ */
+ tmp = spi_write_then_read(spi, &code, 1, id, 3);
+ if (tmp < 0) {
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: read ID error %d\n",
+ dev_name(spi->dev), status);
+ return ERR_PTR(tmp);
+ }
+ if (id[0] != 0x1f)
+ return NULL;
+
+ jedec = id[0];
+ jedec = jedec << 8;
+ jedec |= id[1];
+ jedec = jedec << 8;
+ jedec |= id[2];
+
+ for (tmp = 0, info = dataflash_data;
+ tmp < ARRAY_SIZE(dataflash_data);
+ tmp++, info++) {
+ if (info->jedec_id == jedec) {
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: security register"
+ ", sector protect%s\n",
+ dev_name(spi->dev),
+ (info->flags & SUP_POW2PS)
+ ? ", binary pagesize" : ""
+ );
+ if (info->flags & SUP_POW2PS) {
+ status = dataflash_status(spi);
+ if (status < 0) {
+ DEBUG(MTD_DEBUG_LEVEL1,
+ "%s: status error %d\n",
+ dev_name(spi->dev), status);
+ return ERR_PTR(status);
+ }
+ if (status & 0x1) {
+ if (info->flags & IS_POW2PS)
+ return info;
+ } else {
+ if (!(info->flags & IS_POW2PS))
+ return info;
+ }
+ }
+ }
+ }
+
+ /*
+ * Treat unrecognized chips as errors ... we won't know the
+ * right page size (it might be binary) even when we can tell
+ * which density class is involved
+ */
+ dev_warn(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
+ return ERR_PTR(-ENODEV);
+}
+
/*
- * Detect and initialize DataFlash device:
+ * Detect and initialize DataFlash device, using JEDEC IDs on newer chips
+ * or else the ID code embedded in the status bits:
*
* Device Density ID code #Pages PageSize Offset
* AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9
- * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1025 264 9
+ * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1024 264 9
* AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9
* AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9
* AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10
@@ -531,8 +659,24 @@ add_dataflash(struct spi_device *spi, ch
*/
static int __devinit dataflash_probe(struct spi_device *spi)
{
- int status;
+ int status;
+ struct flash_info *info;
+ /*
+ * Try to detect dataflash by JEDEC ID. If it succeeds, we
+ * have either a C or D part. D supports pagesize options.
+ */
+ info = jedec_probe(spi);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+ if (info != NULL)
+ return add_dataflash(spi, info->name, info->nr_pages,
+ info->pagesize, info->pageoffset);
+
+ /*
+ * Older chips support only legacy commands, identifing
+ * capacity using bits in the status byte.
+ */
status = dataflash_status(spi);
if (status <= 0 || status == 0xff) {
DEBUG(MTD_DEBUG_LEVEL1, "%s: status error %d\n",
@@ -551,7 +695,7 @@ static int __devinit dataflash_probe(str
status = add_dataflash(spi, "AT45DB011B", 512, 264, 9);
break;
case 0x14: /* 0 1 0 1 x x */
- status = add_dataflash(spi, "AT45DB021B", 1025, 264, 9);
+ status = add_dataflash(spi, "AT45DB021B", 1024, 264, 9);
break;
case 0x1c: /* 0 1 1 1 x x */
status = add_dataflash(spi, "AT45DB041x", 2048, 264, 9);
WARNING: multiple messages have this Message-ID (diff)
From: David Brownell <david-b@pacbell.net>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Bryan Wu <cooloney@kernel.org>,
Michael Hennerich <michael.hennerich@analog.com>,
linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: [RESEND PATCH 2.6.26-git 1/2] MTD DataFlash: bugfix, binary page sizes now handled (v3)
Date: Fri, 25 Jul 2008 15:49:12 -0700 [thread overview]
Message-ID: <200807251549.12565.david-b@pacbell.net> (raw)
In-Reply-To: <200806282321.34275.david-b@pacbell.net>
From: Michael Hennerich <michael.hennerich@analog.com>
This fixes a bug in handling certain newer DataFlash chips (at45db321d
and at45db642d have current Linux users) when they were configured to
use binary page sizes (possibly at the factory).
- Use JEDEC probing (borrowed from m25p80 driver) to tell if the chip
supports binary page sizes ... if so, then query the chip to tell
whether it's currently using them.
- Print that pagesize during probe; erasesize is still listed in
the /proc/mtd file, useful for making filesystems.
Also fix a pre-existing bug with 2 MBit parts using the wrong pagesize;
these are a bit smaller than Linux would normally use (even shared with
a small FPGA's configuration bitstream).
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Bryan Wu <cooloney@kernel.org>
Fix regression (so cmdlinepart works again), update error handling
and comments.
# updated to use dev_name(dev) not dev->bus_id
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
drivers/mtd/devices/mtd_dataflash.c | 154 ++++++++++++++++++++++++++++++++++--
1 file changed, 149 insertions(+), 5 deletions(-)
--- a/drivers/mtd/devices/mtd_dataflash.c 2008-06-30 16:44:26.000000000 -0700
+++ b/drivers/mtd/devices/mtd_dataflash.c 2008-06-30 21:38:22.000000000 -0700
@@ -15,6 +15,8 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mutex.h>
+#include <linux/err.h>
+
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
@@ -487,7 +489,8 @@ add_dataflash(struct spi_device *spi, ch
device->write = dataflash_write;
device->priv = priv;
- dev_info(&spi->dev, "%s (%d KBytes)\n", name, device->size/1024);
+ dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes\n",
+ name, DIV_ROUND_UP(device->size, 1024), pagesize);
dev_set_drvdata(&spi->dev, priv);
if (mtd_has_partitions()) {
@@ -516,12 +519,137 @@ add_dataflash(struct spi_device *spi, ch
return add_mtd_device(device) == 1 ? -ENODEV : 0;
}
+struct flash_info {
+ char *name;
+
+ /* JEDEC id zero means "no ID" (most older chips); otherwise it has
+ * a high byte of zero plus three data bytes: the manufacturer id,
+ * then a two byte device id.
+ */
+ u32 jedec_id;
+
+ /* The size listed here is what works with OP_ERASE_PAGE. */
+ unsigned nr_pages;
+ u16 pagesize;
+ u16 pageoffset;
+
+ u16 flags;
+#define SUP_POW2PS 0x0001 /* supports 2^N byte pages */
+#define IS_POW2PS 0x0002 /* uses 2^N byte pages */
+};
+
+static struct flash_info __devinitdata dataflash_data [] = {
+
+ /*
+ * NOTE: chips with SUP_POW2PS (rev D and up) need two entries,
+ * one with IS_POW2PS and the other without. The entry with the
+ * non-2^N byte page size can't name exact chip revisions without
+ * losing backwards compatibility for cmdlinepart.
+ *
+ * These newer chips also support 128-byte security registers (with
+ * 64 bytes one-time-programmable) and software write-protection.
+ */
+ { "AT45DB011B", 0x1f2200, 512, 264, 9, SUP_POW2PS, },
+ { "at45db011d", 0x1f2200, 512, 256, 8, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB021B", 0x1f2300, 1024, 264, 9, SUP_POW2PS, },
+ { "at45db021d", 0x1f2300, 1024, 256, 8, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB041x", 0x1f2400, 2048, 264, 9, SUP_POW2PS, },
+ { "at45db041d", 0x1f2400, 2048, 256, 8, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB081B", 0x1f2500, 4096, 264, 9, SUP_POW2PS, },
+ { "at45db081d", 0x1f2500, 4096, 256, 8, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB161x", 0x1f2600, 4096, 528, 10, SUP_POW2PS, },
+ { "at45db161d", 0x1f2600, 4096, 512, 9, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB321x", 0x1f2700, 8192, 528, 10, 0, }, /* rev C */
+ { "AT45DB321x", 0x1f2701, 8192, 528, 10, SUP_POW2PS, },
+ { "at45db321d", 0x1f2701, 8192, 512, 9, SUP_POW2PS | IS_POW2PS, },
+
+ { "AT45DB642x", 0x1f2800, 8192, 1056, 11, SUP_POW2PS, },
+ { "at45db642d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS, },
+};
+
+static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+{
+ int tmp;
+ u8 code = OP_READ_ID;
+ u8 id[3];
+ u32 jedec;
+ struct flash_info *info;
+ int status;
+
+ /*
+ * JEDEC also defines an optional "extended device information"
+ * string for after vendor-specific data, after the three bytes
+ * we use here. Supporting some chips might require using it.
+ *
+ * If the vendor ID isn't Atmel's (0x1f), assume this call failed.
+ * That's not an error; only rev C and newer chips handle it, and
+ * only Atmel sells these chips.
+ */
+ tmp = spi_write_then_read(spi, &code, 1, id, 3);
+ if (tmp < 0) {
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: read ID error %d\n",
+ dev_name(spi->dev), status);
+ return ERR_PTR(tmp);
+ }
+ if (id[0] != 0x1f)
+ return NULL;
+
+ jedec = id[0];
+ jedec = jedec << 8;
+ jedec |= id[1];
+ jedec = jedec << 8;
+ jedec |= id[2];
+
+ for (tmp = 0, info = dataflash_data;
+ tmp < ARRAY_SIZE(dataflash_data);
+ tmp++, info++) {
+ if (info->jedec_id == jedec) {
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: security register"
+ ", sector protect%s\n",
+ dev_name(spi->dev),
+ (info->flags & SUP_POW2PS)
+ ? ", binary pagesize" : ""
+ );
+ if (info->flags & SUP_POW2PS) {
+ status = dataflash_status(spi);
+ if (status < 0) {
+ DEBUG(MTD_DEBUG_LEVEL1,
+ "%s: status error %d\n",
+ dev_name(spi->dev), status);
+ return ERR_PTR(status);
+ }
+ if (status & 0x1) {
+ if (info->flags & IS_POW2PS)
+ return info;
+ } else {
+ if (!(info->flags & IS_POW2PS))
+ return info;
+ }
+ }
+ }
+ }
+
+ /*
+ * Treat unrecognized chips as errors ... we won't know the
+ * right page size (it might be binary) even when we can tell
+ * which density class is involved
+ */
+ dev_warn(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
+ return ERR_PTR(-ENODEV);
+}
+
/*
- * Detect and initialize DataFlash device:
+ * Detect and initialize DataFlash device, using JEDEC IDs on newer chips
+ * or else the ID code embedded in the status bits:
*
* Device Density ID code #Pages PageSize Offset
* AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9
- * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1025 264 9
+ * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1024 264 9
* AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9
* AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9
* AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10
@@ -531,8 +659,24 @@ add_dataflash(struct spi_device *spi, ch
*/
static int __devinit dataflash_probe(struct spi_device *spi)
{
- int status;
+ int status;
+ struct flash_info *info;
+ /*
+ * Try to detect dataflash by JEDEC ID. If it succeeds, we
+ * have either a C or D part. D supports pagesize options.
+ */
+ info = jedec_probe(spi);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+ if (info != NULL)
+ return add_dataflash(spi, info->name, info->nr_pages,
+ info->pagesize, info->pageoffset);
+
+ /*
+ * Older chips support only legacy commands, identifing
+ * capacity using bits in the status byte.
+ */
status = dataflash_status(spi);
if (status <= 0 || status == 0xff) {
DEBUG(MTD_DEBUG_LEVEL1, "%s: status error %d\n",
@@ -551,7 +695,7 @@ static int __devinit dataflash_probe(str
status = add_dataflash(spi, "AT45DB011B", 512, 264, 9);
break;
case 0x14: /* 0 1 0 1 x x */
- status = add_dataflash(spi, "AT45DB021B", 1025, 264, 9);
+ status = add_dataflash(spi, "AT45DB021B", 1024, 264, 9);
break;
case 0x1c: /* 0 1 1 1 x x */
status = add_dataflash(spi, "AT45DB041x", 2048, 264, 9);
next prev parent reply other threads:[~2008-07-25 22:59 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-06-03 4:26 [PATCH 1/1] MTD DataFlash: fix bug - ATMEL AT45DF321D spi flash card fails to be copied to (v2) Bryan Wu
2008-06-03 4:26 ` Bryan Wu
2008-06-29 5:49 ` David Brownell
2008-06-29 6:21 ` [PATCH 2.6.26-rc8] MTD DataFlash: bugfix, binary page sizes now handled (v3) David Brownell
2008-07-25 22:49 ` David Brownell [this message]
2008-07-25 22:49 ` [RESEND PATCH 2.6.26-git 1/2] " David Brownell
2008-07-29 22:00 ` Andrew Morton
2008-07-29 22:00 ` Andrew Morton
2008-07-29 22:43 ` David Brownell
2008-07-29 22:43 ` David Brownell
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=200807251549.12565.david-b@pacbell.net \
--to=david-b@pacbell.net \
--cc=akpm@linux-foundation.org \
--cc=cooloney@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=michael.hennerich@analog.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 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.