2010-01-22 15:56:29

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 0/6] MTD: Many fixes and preparation for SmartMedia support in nand subsystem

Hello,

This patch series add few fixes and changes to nand subsystem.

First patch, fixes the MTD_OOB_PLACE support in nand.
I want to use ->read_oob to read both raw oob and data and use ecc.
MTD_OOB_PLACE should do that, but due to a wrong check it will refuse to
work.

Second patch, probably the most important patch in this series.
I changed behavier of MTD_OOB_RAW to match the one of MTD_OOB_PLACE
minus ecc validation.
This way it is easy to use both modes in same code.
This mode is only used in one place, in scan_read_raw in nand_bbt.c
I edited this function to achieve exactly same effect as was before.

Third patch is another bugfix that I need to be able to write several
pages in one go. ->write_oob should do that, but it was incorrectly
implemented.
I need this and former patch to add support for 256 byte nand chips in
the smartmedia FTL very elegantly.

Fourth patch exports few functions from nand_base.c, so one can write
custom ->block_mark_bad, and ->block_is_bad functions.
I use these functions in next patch.

Fifth patch, adds small helper module for nand drivers for SmartMedia/xD
readers. It contains bad block handlers and ecc layer.

And last patch, adds some workarounds to nand core to make xD work with
it.

It adds new ID table (which is similiar to current ID table, but
dfferent in that it has IDS for readonly SmartMedia devices, and it has
special settings for new xD cards that share ID with regular nand
devices, but unfortunely don't report proper information in READ ID
command, and have other problems.)

It also adds a workaround for broken writeprotect status.
Older smartmedia devices had a special seal for writeprotect, aka a
additional connector. This should be handled by nand driver itself.
Newer xD cards have no write protect support at all.

Best regards,
Maxim Levitsky


2010-01-22 15:57:40

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 1/6] MTD: nand: make MTD_OOB_PLACE work correctly.

>From 53cc4bab741b55c02ee726eed40b876209aa3e91 Mon Sep 17 00:00:00 2001
From: Maxim Levitsky <[email protected]>
Date: Fri, 22 Jan 2010 16:41:33 +0200
Subject: [PATCH 1/6] MTD: nand: make MTD_OOB_PLACE work correctly.

MTD_OOB_PLACE is supposed to read/write raw oob data similiar to MTD_OOB_RAW
however due to a bug, currently its not possible to read more data that
specified in oob 'free' regions

Signed-off-by: Maxim Levitsky <[email protected]>
---
drivers/mtd/nand/nand_base.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 8f2958f..405c538 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1232,6 +1232,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
int ret = 0;
uint32_t readlen = ops->len;
uint32_t oobreadlen = ops->ooblen;
+ uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ?
+ mtd->oobavail : mtd->oobsize;
+
uint8_t *bufpoi, *oob, *buf;

stats = mtd->ecc_stats;
@@ -1282,10 +1285,11 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
buf += bytes;

if (unlikely(oob)) {
+
/* Raw mode does data:oob:data:oob */
if (ops->mode != MTD_OOB_RAW) {
int toread = min(oobreadlen,
- chip->ecc.layout->oobavail);
+ max_oobsize);
if (toread) {
oob = nand_transfer_oob(chip,
oob, ops, toread);
--
1.6.3.3


2010-01-22 15:58:34

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 2/6] MTD: nand: make reads using MTD_OOB_RAW affect only ECC validation

>From d355621ea624e8c9b1e198f0b049253c700b7431 Mon Sep 17 00:00:00 2001
From: Maxim Levitsky <[email protected]>
Date: Fri, 22 Jan 2010 16:41:33 +0200
Subject: [PATCH 2/6] MTD: nand: make reads using MTD_OOB_RAW affect only ECC validation

This changes the behavier of MTD_OOB_RAW. It used to read both OOB and data
to the data buffer, however you would still need to specify the dummy oob buffer.

This is only used in one place, but makes it hard to read data+oob without ECC
test, thus I removed that behavier, and fixed the user.

Now MTD_OOB_RAW behaves like MTD_OOB_PLACE, but doesn't do ECC validation

Signed-off-by: Maxim Levitsky <[email protected]>
---
drivers/mtd/nand/nand_base.c | 5 -----
drivers/mtd/nand/nand_bbt.c | 26 ++++++++++++++++++++++----
include/linux/mtd/mtd.h | 4 +---
3 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 405c538..8ff36be 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1286,8 +1286,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,

if (unlikely(oob)) {

- /* Raw mode does data:oob:data:oob */
- if (ops->mode != MTD_OOB_RAW) {
int toread = min(oobreadlen,
max_oobsize);
if (toread) {
@@ -1295,9 +1293,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
oob, ops, toread);
oobreadlen -= toread;
}
- } else
- buf = nand_transfer_oob(chip,
- buf, ops, mtd->oobsize);
}

if (!(chip->options & NAND_NO_READRDY)) {
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 55c23e5..387c45c 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -237,15 +237,33 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len)
{
struct mtd_oob_ops ops;
+ int res;

ops.mode = MTD_OOB_RAW;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
- ops.oobbuf = buf;
- ops.datbuf = buf;
- ops.len = len;

- return mtd->read_oob(mtd, offs, &ops);
+
+ while (len > 0) {
+ if (len <= mtd->writesize) {
+ ops.oobbuf = buf + len;
+ ops.datbuf = buf;
+ ops.len = len;
+ return mtd->read_oob(mtd, offs, &ops);
+ } else {
+ ops.oobbuf = buf + mtd->writesize;
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ res = mtd->read_oob(mtd, offs, &ops);
+
+ if (res)
+ return res;
+ }
+
+ buf += mtd->oobsize + mtd->writesize;
+ len -= mtd->writesize;
+ }
+ return 0;
}

/*
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 662d747..84bb375 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -61,9 +61,7 @@ struct mtd_erase_region_info {
* MTD_OOB_PLACE: oob data are placed at the given offset
* MTD_OOB_AUTO: oob data are automatically placed at the free areas
* which are defined by the ecclayout
- * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data
- * is inserted into the data. Thats a raw image of the
- * flash contents.
+ * MTD_OOB_RAW: mode to read oob and data without doing ECC checking
*/
typedef enum {
MTD_OOB_PLACE,
--
1.6.3.3


2010-01-22 15:59:24

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 3/6] MTD: nand: fix bug that prevented write of more that one page by ->write_oob

>From ed559063edac36892098915c8e4747ad74203b91 Mon Sep 17 00:00:00 2001
From: Maxim Levitsky <[email protected]>
Date: Fri, 22 Jan 2010 16:41:33 +0200
Subject: [PATCH 3/6] MTD: nand: fix bug that prevented write of more that one page by ->write_oob

Although nand_do_write_ops intends to allow such mode, it fails do do so
Probably this was never tested

Signed-off-by: Maxim Levitsky <[email protected]>
---
drivers/mtd/nand/nand_base.c | 16 +++++++++-------
1 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 8ff36be..29e986e 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1879,11 +1879,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
* @oob: oob data buffer
* @ops: oob ops structure
*/
-static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
- struct mtd_oob_ops *ops)
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
+ struct mtd_oob_ops *ops)
{
- size_t len = ops->ooblen;
-
switch(ops->mode) {

case MTD_OOB_PLACE:
@@ -1938,6 +1936,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
int chipnr, realpage, page, blockmask, column;
struct nand_chip *chip = mtd->priv;
uint32_t writelen = ops->len;
+ uint32_t oobwritelen = ops->ooblen;
uint8_t *oob = ops->oobbuf;
uint8_t *buf = ops->datbuf;
int ret, subpage;
@@ -1994,8 +1993,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
wbuf = chip->buffers->databuf;
}

- if (unlikely(oob))
- oob = nand_fill_oob(chip, oob, ops);
+ if (unlikely(oob)) {
+ size_t len = min(oobwritelen, mtd->oobsize);
+ oob = nand_fill_oob(chip, oob, len, ops);
+ oobwritelen -= len;
+ }

ret = chip->write_page(mtd, chip, wbuf, page, cached,
(ops->mode == MTD_OOB_RAW));
@@ -2169,7 +2171,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
chip->pagebuf = -1;

memset(chip->oob_poi, 0xff, mtd->oobsize);
- nand_fill_oob(chip, ops->oobbuf, ops);
+ nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
memset(chip->oob_poi, 0xff, mtd->oobsize);

--
1.6.3.3


2010-01-22 16:00:42

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 4/6] MTD: export few functions from nand_base.c

>From 682591265a03966b495f21ff6b77dfb5fadcbefa Mon Sep 17 00:00:00 2001
From: Maxim Levitsky <[email protected]>
Date: Fri, 22 Jan 2010 16:41:34 +0200
Subject: [PATCH 4/6] MTD: export few functions from nand_base.c

This exports:

nand_do_read_oob
nand_do_write_oob
nand_get_device
nand_put_device

This functions will be used to implement custom oob based
bad block handling in upcoming smartmedia common module

Signed-off-by: Maxim Levitsky <[email protected]>
---
drivers/mtd/nand/nand_base.c | 18 +++++++++---------
include/linux/mtd/nand.h | 12 ++++++++++++
2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 29e986e..e433103 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -96,11 +96,6 @@ static struct nand_ecclayout nand_oob_128 = {
.length = 78}}
};

-static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
- int new_state);
-
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops);

/*
* For devices which display every fart in the system on a separate LED. Is
@@ -114,7 +109,7 @@ DEFINE_LED_TRIGGER(nand_led_trigger);
*
* Deselect, release chip lock and wake up anyone waiting on the device
*/
-static void nand_release_device(struct mtd_info *mtd)
+void nand_release_device(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;

@@ -128,6 +123,7 @@ static void nand_release_device(struct mtd_info *mtd)
wake_up(&chip->controller->wq);
spin_unlock(&chip->controller->lock);
}
+EXPORT_SYMBOL_GPL(nand_release_device);

/**
* nand_read_byte - [DEFAULT] read one byte from the chip
@@ -721,7 +717,7 @@ static void panic_nand_get_device(struct nand_chip *chip,
*
* Get the device and lock it for exclusive access
*/
-static int
+int
nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
{
spinlock_t *lock = &chip->controller->lock;
@@ -756,6 +752,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
remove_wait_queue(wq, &wait);
goto retry;
}
+EXPORT_SYMBOL_GPL(nand_get_device);

/**
* panic_nand_wait - [GENERIC] wait until the command is done
@@ -1535,7 +1532,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
*
* NAND read out-of-band data from the spare area
*/
-static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int page, realpage, chipnr, sndcmd = 1;
@@ -1619,6 +1616,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
ops->oobretlen = ops->ooblen;
return 0;
}
+EXPORT_SYMBOL_GPL(nand_do_read_oob);

/**
* nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
@@ -2111,7 +2109,7 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
*
* NAND write out-of-band
*/
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
int chipnr, page, status, len;
@@ -2182,6 +2180,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,

return 0;
}
+EXPORT_SYMBOL_GPL(nand_do_write_oob);

/**
* nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
@@ -3083,6 +3082,7 @@ EXPORT_SYMBOL_GPL(nand_scan_ident);
EXPORT_SYMBOL_GPL(nand_scan_tail);
EXPORT_SYMBOL_GPL(nand_release);

+
static int __init nand_base_init(void)
{
led_trigger_register_simple("nand-disk", &nand_led_trigger);
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index ccab9df..8c2333a 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -469,6 +469,18 @@ extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, uint8_t * buf);

+extern int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops);
+
+extern int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
+
+extern int nand_get_device(struct nand_chip *chip,
+ struct mtd_info *mtd, int new_state);
+
+extern void nand_release_device(struct mtd_info *mtd);
+
+
/**
* struct platform_nand_chip - chip level device structure
* @nr_chips: max. number of chips to scan for
--
1.6.3.3


2010-01-22 16:01:50

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 5/6] MTD: common module for smartmedia/xD support

>From c5841b6d1a5c58552b3cf16cf916f2a8abc0a2ad Mon Sep 17 00:00:00 2001
From: Maxim Levitsky <[email protected]>
Date: Fri, 22 Jan 2010 16:41:34 +0200
Subject: [PATCH 5/6] MTD: common module for smartmedia/xD support

This small module implements few helpers that are usefull
for nand drivers for SmartMedia/xD card readers.

Signed-off-by: Maxim Levitsky <[email protected]>
---
drivers/mtd/nand/Kconfig | 9 +++
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/sm_common.c | 126 ++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/nand/sm_common.h | 61 ++++++++++++++++++++
4 files changed, 197 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/nand/sm_common.c
create mode 100644 drivers/mtd/nand/sm_common.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 677cd53..13c1fb2 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -18,6 +18,10 @@ config MTD_NAND_VERIFY_WRITE
device thinks the write was successful, a bit could have been
flipped accidentally due to device wear or something else.

+config MTD_NAND_SMARTMEDIA
+ boolean
+ default n
+
config MTD_NAND_ECC_SMC
bool "NAND ECC Smart Media byte order"
default n
@@ -25,6 +29,11 @@ config MTD_NAND_ECC_SMC
Software ECC according to the Smart Media Specification.
The original Linux implementation had byte 0 and 1 swapped.

+config MTD_SM_COMMON
+ select MTD_NAND_SMARTMEDIA
+ tristate
+ default n
+
config MTD_NAND_MUSEUM_IDS
bool "Enable chip ids for obsolete ancient NAND devices"
depends on MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1407bd1..09891f6 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -4,6 +4,7 @@

obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
+obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o

obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c
new file mode 100644
index 0000000..aa7e740
--- /dev/null
+++ b/drivers/mtd/nand/sm_common.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2009 - Maxim Levitsky
+ * Common routines & support for xD format
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/log2.h>
+#include "sm_common.h"
+
+static struct nand_ecclayout nand_oob_sm = {
+ .eccbytes = 6,
+ .eccpos = {8, 9, 10, 13, 14, 15},
+ .oobfree = {
+ {.offset = 0 , .length = 4}, /* reserved */
+ {.offset = 6 , .length = 2}, /* LBA1 */
+ {.offset = 11, .length = 2} /* LBA2 */
+ }
+};
+
+/* Tests if block (more correctly page) is bad */
+static int sm_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+ struct mtd_oob_ops ops;
+ struct sm_oob oob;
+ int bad = 0, ret;
+
+ ops.mode = MTD_OOB_PLACE;
+ ops.ooboffs = 0;
+ ops.ooblen = SM_OOB_SIZE;
+ ops.oobbuf = (void *)&oob;
+ ops.datbuf = NULL;
+
+ if (getchip) {
+ nand_get_device(chip, mtd, FL_READING);
+ chip->select_chip(mtd, 0);
+ }
+
+ ret = nand_do_read_oob(mtd, ofs, &ops);
+ if (ret < 0 || ops.oobretlen != SM_OOB_SIZE)
+ goto out;
+
+ if (!sm_sector_valid(&oob) || !sm_block_valid(&oob)) {
+ bad = 1;
+ goto out;
+ }
+out:
+ if (getchip)
+ nand_release_device(mtd);
+ return bad;
+}
+
+/* Marks block as bad */
+static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+ struct mtd_oob_ops ops;
+ struct sm_oob oob;
+ int ret, error = 0;
+
+ memset(&oob, -1, SM_OOB_SIZE);
+ oob.data_status = 0;
+
+ ops.mode = MTD_OOB_PLACE;
+ ops.ooboffs = 0;
+ ops.ooblen = SM_OOB_SIZE;
+ ops.oobbuf = (void *)&oob;
+ ops.datbuf = NULL;
+
+ nand_get_device(chip, mtd, FL_WRITING);
+
+ ret = nand_do_write_oob(mtd, ofs, &ops);
+ if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
+ printk(KERN_NOTICE
+ "sm_common: can't mark sector at %i as bad\n",
+ (int)ofs);
+ error = -EIO;
+ } else
+ mtd->ecc_stats.badblocks++;
+
+ nand_release_device(mtd);
+ return error;
+}
+
+int sm_register_device(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+ int ret;
+
+ chip->options |= NAND_SKIP_BBTSCAN | NAND_SMARTMEDIA;
+
+ /* Scan for card properties */
+ ret = nand_scan_ident(mtd, 1);
+
+ if (ret)
+ return ret;
+
+ /* Set oob handling functions. */
+ if (mtd->writesize == SM_SECTOR_SIZE) {
+ chip->block_bad = sm_block_bad;
+ chip->block_markbad = sm_block_markbad;
+ chip->ecc.layout = &nand_oob_sm;
+
+ /* SmartMedia on small page nand, has page depedent oob layout,
+ thus let FTL do that hard job */
+ } else if (mtd->writesize != SM_SMALL_PAGE)
+ return -ENODEV;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return ret;
+
+ ret = add_mtd_device(mtd);
+ if (ret)
+ return ret;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sm_register_device);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maxim Levitsky <[email protected]>");
+MODULE_DESCRIPTION("Common SmartMedia/xD functions");
diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h
new file mode 100644
index 0000000..a579fb6
--- /dev/null
+++ b/drivers/mtd/nand/sm_common.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 - Maxim Levitsky
+ * Common routines & support for SmartMedia/xD format
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mtd/nand.h>
+
+/* Full oob structure as written on the flash */
+struct sm_oob {
+ u32 reserved;
+ u8 data_status;
+ u8 block_status;
+ u8 lba_copy1[2];
+ u8 ecc2[3];
+ u8 lba_copy2[2];
+ u8 ecc1[3];
+} __attribute__((packed));
+
+
+/* one sector is always 512 bytes, but it can consist of two nand pages */
+#define SM_SECTOR_SIZE 512
+
+/* oob area is also 16 bytes, but might be from two pages */
+#define SM_OOB_SIZE 16
+
+/* This is maximum zone size, and all devices that have more that one zone
+ have this size */
+#define SM_MAX_ZONE_SIZE 1024
+
+/* support for small page nand */
+#define SM_SMALL_PAGE 256
+#define SM_SMALL_OOB_SIZE 8
+
+
+extern int sm_register_device(struct mtd_info *mtd);
+
+
+inline int sm_sector_valid(struct sm_oob *oob)
+{
+ return hweight16(oob->data_status) >= 5;
+}
+
+inline int sm_block_valid(struct sm_oob *oob)
+{
+ return hweight16(oob->block_status) >= 7;
+}
+
+inline int sm_block_erased(struct sm_oob *oob)
+{
+ static const u32 erased_pattern[4] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+
+ /* First test for erased block */
+ if (!memcmp(oob, erased_pattern, sizeof(*oob)))
+ return 1;
+ return 0;
+}
--
1.6.3.3


2010-01-22 16:02:44

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 6/6] MTD: add few workarounds to nand system for SmartMedia/xD chips.

>From 6693342c25548d1f345d2dc359a38df0ee22e863 Mon Sep 17 00:00:00 2001
From: Maxim Levitsky <[email protected]>
Date: Fri, 22 Jan 2010 16:41:34 +0200
Subject: [PATCH 6/6] MTD: add few workarounds to nand system for SmartMedia/xD chips.

* Add an option NAND_SMARTMEDIA that can be set by nand driver
If set, it will cause separate ID table to be used, which includes
mask rom devices and new xD cards

* Workaround for wrong write protect status on some xD cards

Signed-off-by: Maxim Levitsky <[email protected]>
---
drivers/mtd/nand/nand_base.c | 30 ++++++++++++++++++++++--------
drivers/mtd/nand/nand_ids.c | 39 +++++++++++++++++++++++++++++++++++++++
include/linux/mtd/nand.h | 11 +++++++++++
3 files changed, 72 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index e433103..4ede90b 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -397,9 +397,18 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
static int nand_check_wp(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
+ int wp;
+
+ /* broken xD cards report WP despite beeing writable */
+ if (chip->options & NAND_BROKEN_XD)
+ return 0;
+
/* Check the WP bit */
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
- return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
+ wp = (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
+
+
+ return wp;
}

/**
@@ -2625,14 +2634,18 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
}

/* Lookup the flash id */
- for (i = 0; nand_flash_ids[i].name != NULL; i++) {
- if (dev_id == nand_flash_ids[i].id) {
- type = &nand_flash_ids[i];
+#ifdef CONFIG_MTD_NAND_SMARTMEDIA
+ if (chip->options & NAND_SMARTMEDIA)
+ type = nand_smartmedia_flash_ids;
+ else
+#endif
+ type = nand_flash_ids;
+
+ for (i = 0; type->name != NULL; type++)
+ if (dev_id == type->id)
break;
- }
- }

- if (!type)
+ if (!type->name)
return ERR_PTR(-ENODEV);

if (!mtd->name)
@@ -2989,7 +3002,8 @@ int nand_scan_tail(struct mtd_info *mtd)

/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
- mtd->flags = MTD_CAP_NANDFLASH;
+ mtd->flags = chip->options & NAND_ROM ? MTD_CAP_ROM :
+ MTD_CAP_NANDFLASH;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 69ee2c9..f9e72d5 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -127,6 +127,45 @@ struct nand_flash_dev nand_flash_ids[] = {
{NULL,}
};

+#ifdef CONFIG_MTD_NAND_SMARTMEDIA
+struct nand_flash_dev nand_smartmedia_flash_ids[] = {
+
+ /* SmartMedia */
+ {"SmartMedia 1MiB 5V", 0x6e, 256, 1, 0x1000, 0},
+ {"SmartMedia 1MiB 3,3V", 0xe8, 256, 1, 0x1000, 0},
+ {"SmartMedia 1MiB 3,3V", 0xec, 256, 1, 0x1000, 0},
+ {"SmartMedia 2MiB 3,3V", 0xea, 256, 2, 0x1000, 0},
+ {"SmartMedia 2MiB 5V", 0x64, 256, 2, 0x1000, 0},
+ {"SmartMedia 2MiB 3,3V ROM", 0x5d, 512, 2, 0x2000, NAND_ROM},
+ {"SmartMedia 4MiB 3,3V", 0xe3, 512, 4, 0x2000, 0},
+ {"SmartMedia 4MiB 3,3/5V", 0xe5, 512, 4, 0x2000, 0},
+ {"SmartMedia 4MiB 5V", 0x6b, 512, 4, 0x2000, 0},
+ {"SmartMedia 4MiB 3,3V ROM", 0xd5, 512, 4, 0x2000, NAND_ROM},
+ {"SmartMedia 8MiB 3,3V", 0xe6, 512, 8, 0x2000, 0},
+ {"SmartMedia 8MiB 3,3V ROM", 0xd6, 512, 8, 0x2000, NAND_ROM},
+
+#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD)
+ /* xD / SmartMedia */
+ {"SmartMedia/xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0},
+ {"SmartMedia 16MiB 3,3V ROM", 0x57, 512, 16, 0x4000, NAND_ROM},
+ {"SmartMedia/xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0},
+ {"SmartMedia 32MiB 3,3V ROM", 0x58, 512, 32, 0x4000, NAND_ROM},
+ {"SmartMedia/xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0},
+ {"SmartMedia 64MiB 3,3V ROM", 0xd9, 512, 64, 0x4000, NAND_ROM},
+ {"SmartMedia/xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0},
+ {"SmartMedia 128MiB 3,3V ROM", 0xda, 512, 128, 0x4000, NAND_ROM},
+ {"SmartMedia/xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM},
+ {"SmartMedia 256MiB 3,3V ROM", 0x5b, 512, 256, 0x4000, NAND_ROM},
+
+ /* xD only */
+ {"xD 512MiB 3,3V", 0xDC, 512, 512, 0x4000, XD_TYPEM},
+ {"xD 1GiB 3,3V", 0xD3, 512, 1024, 0x4000, XD_TYPEM},
+ {"xD 2GiB 3,3V", 0xD5, 512, 2048, 0x4000, XD_TYPEM},
+ {NULL,}
+};
+EXPORT_SYMBOL(nand_smartmedia_flash_ids);
+#endif
+
/*
* Manufacturer ID list
*/
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 8c2333a..bb25cd8 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -170,6 +170,12 @@ typedef enum {
/* Chip does not allow subpage writes */
#define NAND_NO_SUBPAGE_WRITE 0x00000200

+/* Device is one of 'new' xD cards that expose fake nand command set */
+#define NAND_BROKEN_XD 0x00000400
+
+/* Device behaves just like nand, but is readonly */
+#define NAND_ROM 0x00000800
+
/* Options valid for Samsung large page devices */
#define NAND_SAMSUNG_LP_OPTIONS \
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
@@ -195,9 +201,13 @@ typedef enum {
/* This option is defined if the board driver allocates its own buffers
(e.g. because it needs them DMA-coherent */
#define NAND_OWN_BUFFERS 0x00040000
+
/* Chip may not exist, so silence any errors in scan */
#define NAND_SCAN_SILENT_NODEV 0x00080000

+/* controller supports only xD/SmartMedia cards*/
+#define NAND_SMARTMEDIA 0x00100000
+
/* Options set by nand scan */
/* Nand scan has allocated controller struct */
#define NAND_CONTROLLER_ALLOC 0x80000000
@@ -458,6 +468,7 @@ struct nand_manufacturers {
};

extern struct nand_flash_dev nand_flash_ids[];
+extern struct nand_flash_dev nand_smartmedia_flash_ids[];
extern struct nand_manufacturers nand_manuf_ids[];

extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
--
1.6.3.3