aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorRobert Jarzmik <robert.jarzmik@free.fr>2012-04-09 07:19:08 -0400
committerArtem Bityutskiy <artem.bityutskiy@linux.intel.com>2012-05-29 04:14:51 -0400
commit52c2d9aad4923536c10d278e02da2e0254e46ee0 (patch)
tree6c921fe911b51d5356aa145dcb2ca00bedd88c60 /drivers/mtd
parent5df41de5870e2184e75a8cb133ca81888006f097 (diff)
mtd: docg3 fix in-middle of blocks reads
Corner case reads do not work, and return false data and ECC. This case is typically seen in a ubifs usage, with a read of type: - docg3 docg3: doc_read_oob(from=14882415, mode=1, data=(c30eca40:12), oob=( (null):0)) This results in the following reads: - docg3 docg3: doc_read_data_area(buf= (null), len=111) - docg3 docg3: doc_read_data_area(buf=c30eca40, len=12) - docg3 docg3: doc_read_data_area(buf= (null), len=389) - docg3 docg3: doc_read_data_area(buf= (null), len=0) - docg3 docg3: doc_read_data_area(buf= (null), len=16) If we suppose that the pages content is : - bytes 0 .. 111 : 0x0a - bytes 112 .. 255 : 0x0f Then the returned bytes will be : - 111 times 0x0a (correct) - 0x0a 2 times and 0x0f 10 times (incorrect, should be 0x0a,0x0f) - 0x0f 389 times (correct) - nothing - correct OOB The reason seams that the first 111 bytes read ends between the 2 docg3 planes, and that the first following read (in the 12 bytes sequence, read of 16 bit word) returns the byte of the rightmost plane duplicated in high and lower byte of the word. Fix this behaviour by ensuring that if the previous read ended up in-between the 2 planes, there will be a first 1 byte read to get back to the beginning of leftmost plane. Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/devices/docg3.c34
1 files changed, 23 insertions, 11 deletions
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 65d22a0439c6..3dbbfa58251d 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -227,7 +227,7 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len,
227 u8 data8, *dst8; 227 u8 data8, *dst8;
228 228
229 doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len); 229 doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len);
230 cdr = len & 0x3; 230 cdr = len & 0x1;
231 len4 = len - cdr; 231 len4 = len - cdr;
232 232
233 if (first) 233 if (first)
@@ -732,12 +732,24 @@ err:
732 * @len: the number of bytes to be read (must be a multiple of 4) 732 * @len: the number of bytes to be read (must be a multiple of 4)
733 * @buf: the buffer to be filled in (or NULL is forget bytes) 733 * @buf: the buffer to be filled in (or NULL is forget bytes)
734 * @first: 1 if first time read, DOC_READADDRESS should be set 734 * @first: 1 if first time read, DOC_READADDRESS should be set
735 * @last_odd: 1 if last read ended up on an odd byte
736 *
737 * Reads bytes from a prepared page. There is a trickery here : if the last read
738 * ended up on an odd offset in the 1024 bytes double page, ie. between the 2
739 * planes, the first byte must be read apart. If a word (16bit) read was used,
740 * the read would return the byte of plane 2 as low *and* high endian, which
741 * will mess the read.
735 * 742 *
736 */ 743 */
737static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf, 744static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf,
738 int first) 745 int first, int last_odd)
739{ 746{
740 doc_read_data_area(docg3, buf, len, first); 747 if (last_odd && len > 0) {
748 doc_read_data_area(docg3, buf, 1, first);
749 doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0);
750 } else {
751 doc_read_data_area(docg3, buf, len, first);
752 }
741 doc_delay(docg3, 2); 753 doc_delay(docg3, 2);
742 return len; 754 return len;
743} 755}
@@ -888,20 +900,20 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
888 ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); 900 ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
889 if (ret < 0) 901 if (ret < 0)
890 goto err_in_read; 902 goto err_in_read;
891 ret = doc_read_page_getbytes(docg3, skip, NULL, 1); 903 ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0);
892 if (ret < skip) 904 if (ret < skip)
893 goto err_in_read; 905 goto err_in_read;
894 ret = doc_read_page_getbytes(docg3, nbdata, buf, 0); 906 ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2);
895 if (ret < nbdata) 907 if (ret < nbdata)
896 goto err_in_read; 908 goto err_in_read;
897 doc_read_page_getbytes(docg3, 909 doc_read_page_getbytes(docg3,
898 DOC_LAYOUT_PAGE_SIZE - nbdata - skip, 910 DOC_LAYOUT_PAGE_SIZE - nbdata - skip,
899 NULL, 0); 911 NULL, 0, (skip + nbdata) % 2);
900 ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0); 912 ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0);
901 if (ret < nboob) 913 if (ret < nboob)
902 goto err_in_read; 914 goto err_in_read;
903 doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, 915 doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob,
904 NULL, 0); 916 NULL, 0, nboob % 2);
905 917
906 doc_get_bch_hw_ecc(docg3, hwecc); 918 doc_get_bch_hw_ecc(docg3, hwecc);
907 eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); 919 eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1);
@@ -1006,7 +1018,7 @@ static int doc_reload_bbt(struct docg3 *docg3)
1006 DOC_LAYOUT_PAGE_SIZE); 1018 DOC_LAYOUT_PAGE_SIZE);
1007 if (!ret) 1019 if (!ret)
1008 doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE, 1020 doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE,
1009 buf, 1); 1021 buf, 1, 0);
1010 buf += DOC_LAYOUT_PAGE_SIZE; 1022 buf += DOC_LAYOUT_PAGE_SIZE;
1011 } 1023 }
1012 doc_read_page_finish(docg3); 1024 doc_read_page_finish(docg3);
@@ -1066,10 +1078,10 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from)
1066 ret = doc_reset_seq(docg3); 1078 ret = doc_reset_seq(docg3);
1067 if (!ret) 1079 if (!ret)
1068 ret = doc_read_page_prepare(docg3, block0, block1, page, 1080 ret = doc_read_page_prepare(docg3, block0, block1, page,
1069 ofs + DOC_LAYOUT_WEAR_OFFSET); 1081 ofs + DOC_LAYOUT_WEAR_OFFSET, 0);
1070 if (!ret) 1082 if (!ret)
1071 ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE, 1083 ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE,
1072 buf, 1); 1084 buf, 1, 0);
1073 doc_read_page_finish(docg3); 1085 doc_read_page_finish(docg3);
1074 1086
1075 if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK)) 1087 if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK))