diff options
author | Gabor Juhos <juhosg@openwrt.org> | 2014-01-28 17:30:25 +0000 |
---|---|---|
committer | Gabor Juhos <juhosg@openwrt.org> | 2014-01-28 17:30:25 +0000 |
commit | da5a47f0be3f79ef52801f3b109220c701dd4c53 (patch) | |
tree | fbf749ed7b6da2bccf1cd13ba25bca3b69fd8fe8 /target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch | |
parent | c977768c4f6bf453e8064a5ba64b6e6567dc4b5b (diff) | |
download | upstream-da5a47f0be3f79ef52801f3b109220c701dd4c53.tar.gz upstream-da5a47f0be3f79ef52801f3b109220c701dd4c53.tar.bz2 upstream-da5a47f0be3f79ef52801f3b109220c701dd4c53.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>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@39409 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch')
-rw-r--r-- | target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch | 239 |
1 files changed, 239 insertions, 0 deletions
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 |