aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGabor Juhos <juhosg@openwrt.org>2014-01-28 17:30:25 +0000
committerGabor Juhos <juhosg@openwrt.org>2014-01-28 17:30:25 +0000
commita558bb44713ce101973043c9e2805743771e8340 (patch)
tree30e53bc3093a3d564393b3e7552b089c7d920d97
parent6ebfa8c33ca80c79dec1ba512fa5bd7bd119de62 (diff)
downloadupstream-a558bb44713ce101973043c9e2805743771e8340.tar.gz
upstream-a558bb44713ce101973043c9e2805743771e8340.tar.bz2
upstream-a558bb44713ce101973043c9e2805743771e8340.zip
kernel/3.1x: yaffs: fix handling of small-page NAND devices
Since the yaffs code update (r39084), it is not possible to install OpenWrt on RouterBoards with small-page NAND chips. Fix the yaffs code to make it work again. Signed-off-by: Gabor Juhos <juhosg@openwrt.org> SVN-Revision: 39409
-rw-r--r--target/linux/generic/patches-3.10/503-yaffs-add-tags-9bytes-mount-option.patch115
-rw-r--r--target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch239
-rw-r--r--target/linux/generic/patches-3.12/503-yaffs-add-tags-9bytes-mount-option.patch115
-rw-r--r--target/linux/generic/patches-3.12/504-yaffs-fix-compat-tags-handling.patch239
-rw-r--r--target/linux/generic/patches-3.13/503-yaffs-add-tags-9bytes-mount-option.patch115
-rw-r--r--target/linux/generic/patches-3.13/504-yaffs-fix-compat-tags-handling.patch239
6 files changed, 1062 insertions, 0 deletions
diff --git a/target/linux/generic/patches-3.10/503-yaffs-add-tags-9bytes-mount-option.patch b/target/linux/generic/patches-3.10/503-yaffs-add-tags-9bytes-mount-option.patch
new file mode 100644
index 0000000000..904ad7ec46
--- /dev/null
+++ b/target/linux/generic/patches-3.10/503-yaffs-add-tags-9bytes-mount-option.patch
@@ -0,0 +1,115 @@
+Subject: yaffs: add support for tags-9bytes mount option
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- a/fs/yaffs2/yaffs_vfs.c
++++ b/fs/yaffs2/yaffs_vfs.c
+@@ -2525,6 +2525,7 @@ static const struct super_operations yaf
+
+ struct yaffs_options {
+ int inband_tags;
++ int tags_9bytes;
+ int skip_checkpoint_read;
+ int skip_checkpoint_write;
+ int no_cache;
+@@ -2564,6 +2565,8 @@ static int yaffs_parse_options(struct ya
+
+ if (!strcmp(cur_opt, "inband-tags")) {
+ options->inband_tags = 1;
++ } else if (!strcmp(cur_opt, "tags-9bytes")) {
++ options->tags_9bytes = 1;
+ } else if (!strcmp(cur_opt, "tags-ecc-off")) {
+ options->tags_ecc_on = 0;
+ options->tags_ecc_overridden = 1;
+@@ -2637,7 +2640,6 @@ static struct super_block *yaffs_interna
+ struct yaffs_param *param;
+
+ int read_only = 0;
+- int inband_tags = 0;
+
+ struct yaffs_options options;
+
+@@ -2677,6 +2679,9 @@ static struct super_block *yaffs_interna
+
+ memset(&options, 0, sizeof(options));
+
++ if (IS_ENABLED(CONFIG_YAFFS_9BYTE_TAGS))
++ options.tags_9bytes = 1;
++
+ if (yaffs_parse_options(&options, data_str)) {
+ /* Option parsing failed */
+ return NULL;
+@@ -2710,17 +2715,22 @@ static struct super_block *yaffs_interna
+ }
+
+ /* Added NCB 26/5/2006 for completeness */
+- if (yaffs_version == 2 && !options.inband_tags
+- && WRITE_SIZE(mtd) == 512) {
++ if (yaffs_version == 2 &&
++ (!options.inband_tags || options.tags_9bytes) &&
++ WRITE_SIZE(mtd) == 512) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1");
+ yaffs_version = 1;
+ }
+
+- if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) ||
+- options.inband_tags)
+- inband_tags = 1;
++ if (yaffs_version == 2 &&
++ mtd->oobavail < sizeof(struct yaffs_packed_tags2)) {
++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting inband tags");
++ options.inband_tags = 1;
++ }
+
+- if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0)
++ err = yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags,
++ options.tags_9bytes);
++ if (err < 0)
+ return NULL;
+
+ /* OK, so if we got here, we have an MTD that's NAND and looks
+@@ -2781,7 +2791,8 @@ static struct super_block *yaffs_interna
+
+ param->n_reserved_blocks = 5;
+ param->n_caches = (options.no_cache) ? 0 : 10;
+- param->inband_tags = inband_tags;
++ param->inband_tags = options.inband_tags;
++ param->tags_9bytes = options.tags_9bytes;
+
+ param->enable_xattr = 1;
+ if (options.lazy_loading_overridden)
+--- a/fs/yaffs2/yaffs_mtdif.c
++++ b/fs/yaffs2/yaffs_mtdif.c
+@@ -276,7 +276,8 @@ struct mtd_info * yaffs_get_mtd_device(d
+ return mtd;
+ }
+
+-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
++ int tags_9bytes)
+ {
+ if (yaffs_version == 2) {
+ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
+@@ -295,6 +296,12 @@ int yaffs_verify_mtd(struct mtd_info *mt
+ );
+ return -1;
+ }
++
++ if (tags_9bytes && mtd->oobavail < 9) {
++ yaffs_trace(YAFFS_TRACE_ALWAYS,
++ "MTD device does not support 9-byte tags");
++ return -1;
++ }
+ }
+
+ return 0;
+--- a/fs/yaffs2/yaffs_mtdif.h
++++ b/fs/yaffs2/yaffs_mtdif.h
+@@ -21,5 +21,6 @@
+ void yaffs_mtd_drv_install(struct yaffs_dev *dev);
+ struct mtd_info * yaffs_get_mtd_device(dev_t sdev);
+ void yaffs_put_mtd_device(struct mtd_info *mtd);
+-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags);
++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
++ int tags_9bytes);
+ #endif
diff --git a/target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch b/target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch
new file mode 100644
index 0000000000..a18cf6fd7b
--- /dev/null
+++ b/target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch
@@ -0,0 +1,239 @@
+Subject: yaffs: fix compat tags handling
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- a/fs/yaffs2/yaffs_tagscompat.c
++++ b/fs/yaffs2/yaffs_tagscompat.c
+@@ -17,7 +17,9 @@
+ #include "yaffs_getblockinfo.h"
+ #include "yaffs_trace.h"
+
++#if 0
+ static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
++#endif
+
+
+ /********** Tags ECC calculations *********/
+@@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta
+ return 0;
+ }
+
++#if 0
+ /********** Tags **********/
+
+ static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
+@@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya
+ if(!dev->tagger.mark_bad_fn)
+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
+ }
++#else
++
++#include "yaffs_packedtags1.h"
++
++static int yaffs_tags_compat_write(struct yaffs_dev *dev,
++ int nand_chunk,
++ const u8 *data,
++ const struct yaffs_ext_tags *tags)
++{
++ struct yaffs_packed_tags1 pt1;
++ u8 tag_buf[9];
++ int retval;
++
++ /* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */
++ compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12);
++ compile_time_assertion(sizeof(struct yaffs_tags) == 8);
++
++ yaffs_pack_tags1(&pt1, tags);
++ yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);
++
++ /* When deleting a chunk, the upper layer provides only skeletal
++ * tags, one with is_deleted set. However, we need to update the
++ * tags, not erase them completely. So we use the NAND write property
++ * that only zeroed-bits stick and set tag bytes to all-ones and
++ * zero just the (not) deleted bit.
++ */
++ if (!dev->param.tags_9bytes) {
++ if (tags->is_deleted) {
++ memset(&pt1, 0xff, 8);
++ /* clear delete status bit to indicate deleted */
++ pt1.deleted = 0;
++ }
++ memcpy(tag_buf, &pt1, 8);
++ } else {
++ if (tags->is_deleted) {
++ memset(tag_buf, 0xff, 8);
++ tag_buf[8] = 0;
++ } else {
++ memcpy(tag_buf, &pt1, 8);
++ tag_buf[8] = 0xff;
++ }
++ }
++
++ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
++ data,
++ (data) ? dev->data_bytes_per_chunk : 0,
++ tag_buf,
++ (dev->param.tags_9bytes) ? 9 : 8);
++
++ return retval;
++}
++
++/* Return with empty extended tags but add ecc_result.
++ */
++static int return_empty_tags(struct yaffs_ext_tags *tags,
++ enum yaffs_ecc_result ecc_result,
++ int retval)
++{
++ if (tags) {
++ memset(tags, 0, sizeof(*tags));
++ tags->ecc_result = ecc_result;
++ }
++
++ return retval;
++}
++
++static int yaffs_tags_compat_read(struct yaffs_dev *dev,
++ int nand_chunk,
++ u8 *data,
++ struct yaffs_ext_tags *tags)
++{
++ struct yaffs_packed_tags1 pt1;
++ enum yaffs_ecc_result ecc_result;
++ int retval;
++ int deleted;
++ u8 tag_buf[9];
++
++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
++ data, dev->param.total_bytes_per_chunk,
++ tag_buf,
++ (dev->param.tags_9bytes) ? 9 : 8,
++ &ecc_result);
++
++ switch (ecc_result) {
++ case YAFFS_ECC_RESULT_NO_ERROR:
++ case YAFFS_ECC_RESULT_FIXED:
++ break;
++
++ case YAFFS_ECC_RESULT_UNFIXED:
++ default:
++ return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0);
++ tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk);
++ return YAFFS_FAIL;
++ }
++
++ /* Check for a blank/erased chunk. */
++ if (yaffs_check_ff(tag_buf, 8)) {
++ /* when blank, upper layers want ecc_result to be <= NO_ERROR */
++ return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR,
++ YAFFS_OK);
++ }
++
++ memcpy(&pt1, tag_buf, 8);
++
++ if (!dev->param.tags_9bytes) {
++ /* Read deleted status (bit) then return it to it's non-deleted
++ * state before performing tags mini-ECC check. pt1.deleted is
++ * inverted.
++ */
++ deleted = !pt1.deleted;
++ pt1.deleted = 1;
++ } else {
++ deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0;
++ }
++
++ /* Check the packed tags mini-ECC and correct if necessary/possible. */
++ retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1);
++ switch (retval) {
++ case 0:
++ /* no tags error, use MTD result */
++ break;
++ case 1:
++ /* recovered tags-ECC error */
++ dev->n_tags_ecc_fixed++;
++ if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
++ ecc_result = YAFFS_ECC_RESULT_FIXED;
++ break;
++ default:
++ /* unrecovered tags-ECC error */
++ dev->n_tags_ecc_unfixed++;
++ return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED,
++ YAFFS_FAIL);
++ }
++
++ /* Unpack the tags to extended form and set ECC result.
++ * [set should_be_ff just to keep yaffs_unpack_tags1 happy]
++ */
++ pt1.should_be_ff = 0xffffffff;
++ yaffs_unpack_tags1(tags, &pt1);
++ tags->ecc_result = ecc_result;
++
++ /* Set deleted state */
++ tags->is_deleted = deleted;
++ return YAFFS_OK;
++}
++
++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no)
++{
++ return dev->drv.drv_mark_bad_fn(dev, block_no);
++}
++
++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
++ int block_no,
++ enum yaffs_block_state *state,
++ u32 *seq_number)
++{
++ struct yaffs_ext_tags tags;
++ int retval;
++
++ yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no);
++
++ *seq_number = 0;
++
++ retval = dev->drv.drv_check_bad_fn(dev, block_no);
++ if (retval == YAFFS_FAIL) {
++ *state = YAFFS_BLOCK_STATE_DEAD;
++ goto out;
++ }
++
++ yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block,
++ NULL, &tags);
++
++ if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) {
++ yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad",
++ block_no);
++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
++ } else if (tags.chunk_used) {
++ *seq_number = tags.seq_number;
++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
++ } else {
++ *state = YAFFS_BLOCK_STATE_EMPTY;
++ }
++
++ retval = YAFFS_OK;
++
++out:
++ yaffs_trace(YAFFS_TRACE_MTD,
++ "block query returns seq %u state %d",
++ *seq_number, *state);
++
++ return retval;
++}
++
++void yaffs_tags_compat_install(struct yaffs_dev *dev)
++{
++ if (dev->param.is_yaffs2)
++ return;
++
++ if (!dev->tagger.write_chunk_tags_fn)
++ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write;
++
++ if (!dev->tagger.read_chunk_tags_fn)
++ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read;
++
++ if (!dev->tagger.query_block_fn)
++ dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
++
++ if (!dev->tagger.mark_bad_fn)
++ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
++}
++#endif
diff --git a/target/linux/generic/patches-3.12/503-yaffs-add-tags-9bytes-mount-option.patch b/target/linux/generic/patches-3.12/503-yaffs-add-tags-9bytes-mount-option.patch
new file mode 100644
index 0000000000..5a15b516b5
--- /dev/null
+++ b/target/linux/generic/patches-3.12/503-yaffs-add-tags-9bytes-mount-option.patch
@@ -0,0 +1,115 @@
+Subject: yaffs: add support for tags-9bytes mount option
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- a/fs/yaffs2/yaffs_vfs.c
++++ b/fs/yaffs2/yaffs_vfs.c
+@@ -2634,6 +2634,7 @@ static const struct super_operations yaf
+
+ struct yaffs_options {
+ int inband_tags;
++ int tags_9bytes;
+ int skip_checkpoint_read;
+ int skip_checkpoint_write;
+ int no_cache;
+@@ -2673,6 +2674,8 @@ static int yaffs_parse_options(struct ya
+
+ if (!strcmp(cur_opt, "inband-tags")) {
+ options->inband_tags = 1;
++ } else if (!strcmp(cur_opt, "tags-9bytes")) {
++ options->tags_9bytes = 1;
+ } else if (!strcmp(cur_opt, "tags-ecc-off")) {
+ options->tags_ecc_on = 0;
+ options->tags_ecc_overridden = 1;
+@@ -2746,7 +2749,6 @@ static struct super_block *yaffs_interna
+ struct yaffs_param *param;
+
+ int read_only = 0;
+- int inband_tags = 0;
+
+ struct yaffs_options options;
+
+@@ -2786,6 +2788,9 @@ static struct super_block *yaffs_interna
+
+ memset(&options, 0, sizeof(options));
+
++ if (IS_ENABLED(CONFIG_YAFFS_9BYTE_TAGS))
++ options.tags_9bytes = 1;
++
+ if (yaffs_parse_options(&options, data_str)) {
+ /* Option parsing failed */
+ return NULL;
+@@ -2819,17 +2824,22 @@ static struct super_block *yaffs_interna
+ }
+
+ /* Added NCB 26/5/2006 for completeness */
+- if (yaffs_version == 2 && !options.inband_tags
+- && WRITE_SIZE(mtd) == 512) {
++ if (yaffs_version == 2 &&
++ (!options.inband_tags || options.tags_9bytes) &&
++ WRITE_SIZE(mtd) == 512) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1");
+ yaffs_version = 1;
+ }
+
+- if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) ||
+- options.inband_tags)
+- inband_tags = 1;
++ if (yaffs_version == 2 &&
++ mtd->oobavail < sizeof(struct yaffs_packed_tags2)) {
++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting inband tags");
++ options.inband_tags = 1;
++ }
+
+- if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0)
++ err = yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags,
++ options.tags_9bytes);
++ if (err < 0)
+ return NULL;
+
+ /* OK, so if we got here, we have an MTD that's NAND and looks
+@@ -2890,7 +2900,8 @@ static struct super_block *yaffs_interna
+
+ param->n_reserved_blocks = 5;
+ param->n_caches = (options.no_cache) ? 0 : 10;
+- param->inband_tags = inband_tags;
++ param->inband_tags = options.inband_tags;
++ param->tags_9bytes = options.tags_9bytes;
+
+ param->enable_xattr = 1;
+ if (options.lazy_loading_overridden)
+--- a/fs/yaffs2/yaffs_mtdif.c
++++ b/fs/yaffs2/yaffs_mtdif.c
+@@ -276,7 +276,8 @@ struct mtd_info * yaffs_get_mtd_device(d
+ return mtd;
+ }
+
+-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
++ int tags_9bytes)
+ {
+ if (yaffs_version == 2) {
+ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
+@@ -295,6 +296,12 @@ int yaffs_verify_mtd(struct mtd_info *mt
+ );
+ return -1;
+ }
++
++ if (tags_9bytes && mtd->oobavail < 9) {
++ yaffs_trace(YAFFS_TRACE_ALWAYS,
++ "MTD device does not support 9-byte tags");
++ return -1;
++ }
+ }
+
+ return 0;
+--- a/fs/yaffs2/yaffs_mtdif.h
++++ b/fs/yaffs2/yaffs_mtdif.h
+@@ -21,5 +21,6 @@
+ void yaffs_mtd_drv_install(struct yaffs_dev *dev);
+ struct mtd_info * yaffs_get_mtd_device(dev_t sdev);
+ void yaffs_put_mtd_device(struct mtd_info *mtd);
+-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags);
++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
++ int tags_9bytes);
+ #endif
diff --git a/target/linux/generic/patches-3.12/504-yaffs-fix-compat-tags-handling.patch b/target/linux/generic/patches-3.12/504-yaffs-fix-compat-tags-handling.patch
new file mode 100644
index 0000000000..a18cf6fd7b
--- /dev/null
+++ b/target/linux/generic/patches-3.12/504-yaffs-fix-compat-tags-handling.patch
@@ -0,0 +1,239 @@
+Subject: yaffs: fix compat tags handling
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- a/fs/yaffs2/yaffs_tagscompat.c
++++ b/fs/yaffs2/yaffs_tagscompat.c
+@@ -17,7 +17,9 @@
+ #include "yaffs_getblockinfo.h"
+ #include "yaffs_trace.h"
+
++#if 0
+ static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
++#endif
+
+
+ /********** Tags ECC calculations *********/
+@@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta
+ return 0;
+ }
+
++#if 0
+ /********** Tags **********/
+
+ static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
+@@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya
+ if(!dev->tagger.mark_bad_fn)
+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
+ }
++#else
++
++#include "yaffs_packedtags1.h"
++
++static int yaffs_tags_compat_write(struct yaffs_dev *dev,
++ int nand_chunk,
++ const u8 *data,
++ const struct yaffs_ext_tags *tags)
++{
++ struct yaffs_packed_tags1 pt1;
++ u8 tag_buf[9];
++ int retval;
++
++ /* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */
++ compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12);
++ compile_time_assertion(sizeof(struct yaffs_tags) == 8);
++
++ yaffs_pack_tags1(&pt1, tags);
++ yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);
++
++ /* When deleting a chunk, the upper layer provides only skeletal
++ * tags, one with is_deleted set. However, we need to update the
++ * tags, not erase them completely. So we use the NAND write property
++ * that only zeroed-bits stick and set tag bytes to all-ones and
++ * zero just the (not) deleted bit.
++ */
++ if (!dev->param.tags_9bytes) {
++ if (tags->is_deleted) {
++ memset(&pt1, 0xff, 8);
++ /* clear delete status bit to indicate deleted */
++ pt1.deleted = 0;
++ }
++ memcpy(tag_buf, &pt1, 8);
++ } else {
++ if (tags->is_deleted) {
++ memset(tag_buf, 0xff, 8);
++ tag_buf[8] = 0;
++ } else {
++ memcpy(tag_buf, &pt1, 8);
++ tag_buf[8] = 0xff;
++ }
++ }
++
++ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
++ data,
++ (data) ? dev->data_bytes_per_chunk : 0,
++ tag_buf,
++ (dev->param.tags_9bytes) ? 9 : 8);
++
++ return retval;
++}
++
++/* Return with empty extended tags but add ecc_result.
++ */
++static int return_empty_tags(struct yaffs_ext_tags *tags,
++ enum yaffs_ecc_result ecc_result,
++ int retval)
++{
++ if (tags) {
++ memset(tags, 0, sizeof(*tags));
++ tags->ecc_result = ecc_result;
++ }
++
++ return retval;
++}
++
++static int yaffs_tags_compat_read(struct yaffs_dev *dev,
++ int nand_chunk,
++ u8 *data,
++ struct yaffs_ext_tags *tags)
++{
++ struct yaffs_packed_tags1 pt1;
++ enum yaffs_ecc_result ecc_result;
++ int retval;
++ int deleted;
++ u8 tag_buf[9];
++
++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
++ data, dev->param.total_bytes_per_chunk,
++ tag_buf,
++ (dev->param.tags_9bytes) ? 9 : 8,
++ &ecc_result);
++
++ switch (ecc_result) {
++ case YAFFS_ECC_RESULT_NO_ERROR:
++ case YAFFS_ECC_RESULT_FIXED:
++ break;
++
++ case YAFFS_ECC_RESULT_UNFIXED:
++ default:
++ return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0);
++ tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk);
++ return YAFFS_FAIL;
++ }
++
++ /* Check for a blank/erased chunk. */
++ if (yaffs_check_ff(tag_buf, 8)) {
++ /* when blank, upper layers want ecc_result to be <= NO_ERROR */
++ return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR,
++ YAFFS_OK);
++ }
++
++ memcpy(&pt1, tag_buf, 8);
++
++ if (!dev->param.tags_9bytes) {
++ /* Read deleted status (bit) then return it to it's non-deleted
++ * state before performing tags mini-ECC check. pt1.deleted is
++ * inverted.
++ */
++ deleted = !pt1.deleted;
++ pt1.deleted = 1;
++ } else {
++ deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0;
++ }
++
++ /* Check the packed tags mini-ECC and correct if necessary/possible. */
++ retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1);
++ switch (retval) {
++ case 0:
++ /* no tags error, use MTD result */
++ break;
++ case 1:
++ /* recovered tags-ECC error */
++ dev->n_tags_ecc_fixed++;
++ if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
++ ecc_result = YAFFS_ECC_RESULT_FIXED;
++ break;
++ default:
++ /* unrecovered tags-ECC error */
++ dev->n_tags_ecc_unfixed++;
++ return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED,
++ YAFFS_FAIL);
++ }
++
++ /* Unpack the tags to extended form and set ECC result.
++ * [set should_be_ff just to keep yaffs_unpack_tags1 happy]
++ */
++ pt1.should_be_ff = 0xffffffff;
++ yaffs_unpack_tags1(tags, &pt1);
++ tags->ecc_result = ecc_result;
++
++ /* Set deleted state */
++ tags->is_deleted = deleted;
++ return YAFFS_OK;
++}
++
++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no)
++{
++ return dev->drv.drv_mark_bad_fn(dev, block_no);
++}
++
++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
++ int block_no,
++ enum yaffs_block_state *state,
++ u32 *seq_number)
++{
++ struct yaffs_ext_tags tags;
++ int retval;
++
++ yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no);
++
++ *seq_number = 0;
++
++ retval = dev->drv.drv_check_bad_fn(dev, block_no);
++ if (retval == YAFFS_FAIL) {
++ *state = YAFFS_BLOCK_STATE_DEAD;
++ goto out;
++ }
++
++ yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block,
++ NULL, &tags);
++
++ if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) {
++ yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad",
++ block_no);
++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
++ } else if (tags.chunk_used) {
++ *seq_number = tags.seq_number;
++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
++ } else {
++ *state = YAFFS_BLOCK_STATE_EMPTY;
++ }
++
++ retval = YAFFS_OK;
++
++out:
++ yaffs_trace(YAFFS_TRACE_MTD,
++ "block query returns seq %u state %d",
++ *seq_number, *state);
++
++ return retval;
++}
++
++void yaffs_tags_compat_install(struct yaffs_dev *dev)
++{
++ if (dev->param.is_yaffs2)
++ return;
++
++ if (!dev->tagger.write_chunk_tags_fn)
++ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write;
++
++ if (!dev->tagger.read_chunk_tags_fn)
++ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read;
++
++ if (!dev->tagger.query_block_fn)
++ dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
++
++ if (!dev->tagger.mark_bad_fn)
++ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
++}
++#endif
diff --git a/target/linux/generic/patches-3.13/503-yaffs-add-tags-9bytes-mount-option.patch b/target/linux/generic/patches-3.13/503-yaffs-add-tags-9bytes-mount-option.patch
new file mode 100644
index 0000000000..5a15b516b5
--- /dev/null
+++ b/target/linux/generic/patches-3.13/503-yaffs-add-tags-9bytes-mount-option.patch
@@ -0,0 +1,115 @@
+Subject: yaffs: add support for tags-9bytes mount option
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- a/fs/yaffs2/yaffs_vfs.c
++++ b/fs/yaffs2/yaffs_vfs.c
+@@ -2634,6 +2634,7 @@ static const struct super_operations yaf
+
+ struct yaffs_options {
+ int inband_tags;
++ int tags_9bytes;
+ int skip_checkpoint_read;
+ int skip_checkpoint_write;
+ int no_cache;
+@@ -2673,6 +2674,8 @@ static int yaffs_parse_options(struct ya
+
+ if (!strcmp(cur_opt, "inband-tags")) {
+ options->inband_tags = 1;
++ } else if (!strcmp(cur_opt, "tags-9bytes")) {
++ options->tags_9bytes = 1;
+ } else if (!strcmp(cur_opt, "tags-ecc-off")) {
+ options->tags_ecc_on = 0;
+ options->tags_ecc_overridden = 1;
+@@ -2746,7 +2749,6 @@ static struct super_block *yaffs_interna
+ struct yaffs_param *param;
+
+ int read_only = 0;
+- int inband_tags = 0;
+
+ struct yaffs_options options;
+
+@@ -2786,6 +2788,9 @@ static struct super_block *yaffs_interna
+
+ memset(&options, 0, sizeof(options));
+
++ if (IS_ENABLED(CONFIG_YAFFS_9BYTE_TAGS))
++ options.tags_9bytes = 1;
++
+ if (yaffs_parse_options(&options, data_str)) {
+ /* Option parsing failed */
+ return NULL;
+@@ -2819,17 +2824,22 @@ static struct super_block *yaffs_interna
+ }
+
+ /* Added NCB 26/5/2006 for completeness */
+- if (yaffs_version == 2 && !options.inband_tags
+- && WRITE_SIZE(mtd) == 512) {
++ if (yaffs_version == 2 &&
++ (!options.inband_tags || options.tags_9bytes) &&
++ WRITE_SIZE(mtd) == 512) {
+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1");
+ yaffs_version = 1;
+ }
+
+- if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) ||
+- options.inband_tags)
+- inband_tags = 1;
++ if (yaffs_version == 2 &&
++ mtd->oobavail < sizeof(struct yaffs_packed_tags2)) {
++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting inband tags");
++ options.inband_tags = 1;
++ }
+
+- if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0)
++ err = yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags,
++ options.tags_9bytes);
++ if (err < 0)
+ return NULL;
+
+ /* OK, so if we got here, we have an MTD that's NAND and looks
+@@ -2890,7 +2900,8 @@ static struct super_block *yaffs_interna
+
+ param->n_reserved_blocks = 5;
+ param->n_caches = (options.no_cache) ? 0 : 10;
+- param->inband_tags = inband_tags;
++ param->inband_tags = options.inband_tags;
++ param->tags_9bytes = options.tags_9bytes;
+
+ param->enable_xattr = 1;
+ if (options.lazy_loading_overridden)
+--- a/fs/yaffs2/yaffs_mtdif.c
++++ b/fs/yaffs2/yaffs_mtdif.c
+@@ -276,7 +276,8 @@ struct mtd_info * yaffs_get_mtd_device(d
+ return mtd;
+ }
+
+-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
++ int tags_9bytes)
+ {
+ if (yaffs_version == 2) {
+ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
+@@ -295,6 +296,12 @@ int yaffs_verify_mtd(struct mtd_info *mt
+ );
+ return -1;
+ }
++
++ if (tags_9bytes && mtd->oobavail < 9) {
++ yaffs_trace(YAFFS_TRACE_ALWAYS,
++ "MTD device does not support 9-byte tags");
++ return -1;
++ }
+ }
+
+ return 0;
+--- a/fs/yaffs2/yaffs_mtdif.h
++++ b/fs/yaffs2/yaffs_mtdif.h
+@@ -21,5 +21,6 @@
+ void yaffs_mtd_drv_install(struct yaffs_dev *dev);
+ struct mtd_info * yaffs_get_mtd_device(dev_t sdev);
+ void yaffs_put_mtd_device(struct mtd_info *mtd);
+-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags);
++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags,
++ int tags_9bytes);
+ #endif
diff --git a/target/linux/generic/patches-3.13/504-yaffs-fix-compat-tags-handling.patch b/target/linux/generic/patches-3.13/504-yaffs-fix-compat-tags-handling.patch
new file mode 100644
index 0000000000..a18cf6fd7b
--- /dev/null
+++ b/target/linux/generic/patches-3.13/504-yaffs-fix-compat-tags-handling.patch
@@ -0,0 +1,239 @@
+Subject: yaffs: fix compat tags handling
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- a/fs/yaffs2/yaffs_tagscompat.c
++++ b/fs/yaffs2/yaffs_tagscompat.c
+@@ -17,7 +17,9 @@
+ #include "yaffs_getblockinfo.h"
+ #include "yaffs_trace.h"
+
++#if 0
+ static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
++#endif
+
+
+ /********** Tags ECC calculations *********/
+@@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta
+ return 0;
+ }
+
++#if 0
+ /********** Tags **********/
+
+ static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
+@@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya
+ if(!dev->tagger.mark_bad_fn)
+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
+ }
++#else
++
++#include "yaffs_packedtags1.h"
++
++static int yaffs_tags_compat_write(struct yaffs_dev *dev,
++ int nand_chunk,
++ const u8 *data,
++ const struct yaffs_ext_tags *tags)
++{
++ struct yaffs_packed_tags1 pt1;
++ u8 tag_buf[9];
++ int retval;
++
++ /* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */
++ compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12);
++ compile_time_assertion(sizeof(struct yaffs_tags) == 8);
++
++ yaffs_pack_tags1(&pt1, tags);
++ yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);
++
++ /* When deleting a chunk, the upper layer provides only skeletal
++ * tags, one with is_deleted set. However, we need to update the
++ * tags, not erase them completely. So we use the NAND write property
++ * that only zeroed-bits stick and set tag bytes to all-ones and
++ * zero just the (not) deleted bit.
++ */
++ if (!dev->param.tags_9bytes) {
++ if (tags->is_deleted) {
++ memset(&pt1, 0xff, 8);
++ /* clear delete status bit to indicate deleted */
++ pt1.deleted = 0;
++ }
++ memcpy(tag_buf, &pt1, 8);
++ } else {
++ if (tags->is_deleted) {
++ memset(tag_buf, 0xff, 8);
++ tag_buf[8] = 0;
++ } else {
++ memcpy(tag_buf, &pt1, 8);
++ tag_buf[8] = 0xff;
++ }
++ }
++
++ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
++ data,
++ (data) ? dev->data_bytes_per_chunk : 0,
++ tag_buf,
++ (dev->param.tags_9bytes) ? 9 : 8);
++
++ return retval;
++}
++
++/* Return with empty extended tags but add ecc_result.
++ */
++static int return_empty_tags(struct yaffs_ext_tags *tags,
++ enum yaffs_ecc_result ecc_result,
++ int retval)
++{
++ if (tags) {
++ memset(tags, 0, sizeof(*tags));
++ tags->ecc_result = ecc_result;
++ }
++
++ return retval;
++}
++
++static int yaffs_tags_compat_read(struct yaffs_dev *dev,
++ int nand_chunk,
++ u8 *data,
++ struct yaffs_ext_tags *tags)
++{
++ struct yaffs_packed_tags1 pt1;
++ enum yaffs_ecc_result ecc_result;
++ int retval;
++ int deleted;
++ u8 tag_buf[9];
++
++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
++ data, dev->param.total_bytes_per_chunk,
++ tag_buf,
++ (dev->param.tags_9bytes) ? 9 : 8,
++ &ecc_result);
++
++ switch (ecc_result) {
++ case YAFFS_ECC_RESULT_NO_ERROR:
++ case YAFFS_ECC_RESULT_FIXED:
++ break;
++
++ case YAFFS_ECC_RESULT_UNFIXED:
++ default:
++ return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0);
++ tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk);
++ return YAFFS_FAIL;
++ }
++
++ /* Check for a blank/erased chunk. */
++ if (yaffs_check_ff(tag_buf, 8)) {
++ /* when blank, upper layers want ecc_result to be <= NO_ERROR */
++ return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR,
++ YAFFS_OK);
++ }
++
++ memcpy(&pt1, tag_buf, 8);
++
++ if (!dev->param.tags_9bytes) {
++ /* Read deleted status (bit) then return it to it's non-deleted
++ * state before performing tags mini-ECC check. pt1.deleted is
++ * inverted.
++ */
++ deleted = !pt1.deleted;
++ pt1.deleted = 1;
++ } else {
++ deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0;
++ }
++
++ /* Check the packed tags mini-ECC and correct if necessary/possible. */
++ retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1);
++ switch (retval) {
++ case 0:
++ /* no tags error, use MTD result */
++ break;
++ case 1:
++ /* recovered tags-ECC error */
++ dev->n_tags_ecc_fixed++;
++ if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
++ ecc_result = YAFFS_ECC_RESULT_FIXED;
++ break;
++ default:
++ /* unrecovered tags-ECC error */
++ dev->n_tags_ecc_unfixed++;
++ return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED,
++ YAFFS_FAIL);
++ }
++
++ /* Unpack the tags to extended form and set ECC result.
++ * [set should_be_ff just to keep yaffs_unpack_tags1 happy]
++ */
++ pt1.should_be_ff = 0xffffffff;
++ yaffs_unpack_tags1(tags, &pt1);
++ tags->ecc_result = ecc_result;
++
++ /* Set deleted state */
++ tags->is_deleted = deleted;
++ return YAFFS_OK;
++}
++
++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no)
++{
++ return dev->drv.drv_mark_bad_fn(dev, block_no);
++}
++
++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
++ int block_no,
++ enum yaffs_block_state *state,
++ u32 *seq_number)
++{
++ struct yaffs_ext_tags tags;
++ int retval;
++
++ yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no);
++
++ *seq_number = 0;
++
++ retval = dev->drv.drv_check_bad_fn(dev, block_no);
++ if (retval == YAFFS_FAIL) {
++ *state = YAFFS_BLOCK_STATE_DEAD;
++ goto out;
++ }
++
++ yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block,
++ NULL, &tags);
++
++ if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) {
++ yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad",
++ block_no);
++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
++ } else if (tags.chunk_used) {
++ *seq_number = tags.seq_number;
++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
++ } else {
++ *state = YAFFS_BLOCK_STATE_EMPTY;
++ }
++
++ retval = YAFFS_OK;
++
++out:
++ yaffs_trace(YAFFS_TRACE_MTD,
++ "block query returns seq %u state %d",
++ *seq_number, *state);
++
++ return retval;
++}
++
++void yaffs_tags_compat_install(struct yaffs_dev *dev)
++{
++ if (dev->param.is_yaffs2)
++ return;
++
++ if (!dev->tagger.write_chunk_tags_fn)
++ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write;
++
++ if (!dev->tagger.read_chunk_tags_fn)
++ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read;
++
++ if (!dev->tagger.query_block_fn)
++ dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
++
++ if (!dev->tagger.mark_bad_fn)
++ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
++}
++#endif