diff options
author | Robert Jarzmik <robert.jarzmik@free.fr> | 2011-11-19 10:02:47 -0500 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2012-01-09 13:07:17 -0500 |
commit | 32a50b3a457fda9606fd0946eb77ba28a520cd7f (patch) | |
tree | 43df59205d0b08e3257249df581f3202d44c8d02 /drivers | |
parent | 34db8a5a72c5c5eb5d2811f237dcc9bf3c6425a9 (diff) |
mtd: docg3: fix reading oob+data without correction
Fix the docg3 reads to be able to cope with all possible
data buffer / oob buffer / file mode combinations from
docg3_read_oob().
This especially ensures that raw reads do not use ECC
corrections, and AUTOOOB and PLACEOOB do use ECC
correction.
The approach is to empty docg3_read() and make it a wrapper
to docg3_read_oob(). As docg3_read_oob() handles all the
funny cases (no data buffer but oob buffer, data buffer but
no oob buffer, ...), docg3_read() is just a special use of
docg3_read_oob().
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Reviewed-by: Ivan Djelic <ivan.djelic@parrot.com>
Reviewed-by: Mike Dunn <mikedunn@newsguy.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/devices/docg3.c | 194 |
1 files changed, 95 insertions, 99 deletions
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 5834e6e65d1c..1c2f54d23c7e 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c | |||
@@ -196,8 +196,8 @@ static int doc_reset_seq(struct docg3 *docg3) | |||
196 | /** | 196 | /** |
197 | * doc_read_data_area - Read data from data area | 197 | * doc_read_data_area - Read data from data area |
198 | * @docg3: the device | 198 | * @docg3: the device |
199 | * @buf: the buffer to fill in | 199 | * @buf: the buffer to fill in (might be NULL is dummy reads) |
200 | * @len: the lenght to read | 200 | * @len: the length to read |
201 | * @first: first time read, DOC_READADDRESS should be set | 201 | * @first: first time read, DOC_READADDRESS should be set |
202 | * | 202 | * |
203 | * Reads bytes from flash data. Handles the single byte / even bytes reads. | 203 | * Reads bytes from flash data. Handles the single byte / even bytes reads. |
@@ -218,8 +218,10 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, | |||
218 | dst16 = buf; | 218 | dst16 = buf; |
219 | for (i = 0; i < len4; i += 2) { | 219 | for (i = 0; i < len4; i += 2) { |
220 | data16 = doc_readw(docg3, DOC_IOSPACE_DATA); | 220 | data16 = doc_readw(docg3, DOC_IOSPACE_DATA); |
221 | *dst16 = data16; | 221 | if (dst16) { |
222 | dst16++; | 222 | *dst16 = data16; |
223 | dst16++; | ||
224 | } | ||
223 | } | 225 | } |
224 | 226 | ||
225 | if (cdr) { | 227 | if (cdr) { |
@@ -229,8 +231,10 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, | |||
229 | dst8 = (u8 *)dst16; | 231 | dst8 = (u8 *)dst16; |
230 | for (i = 0; i < cdr; i++) { | 232 | for (i = 0; i < cdr; i++) { |
231 | data8 = doc_readb(docg3, DOC_IOSPACE_DATA); | 233 | data8 = doc_readb(docg3, DOC_IOSPACE_DATA); |
232 | *dst8 = data8; | 234 | if (dst8) { |
233 | dst8++; | 235 | *dst8 = data8; |
236 | dst8++; | ||
237 | } | ||
234 | } | 238 | } |
235 | } | 239 | } |
236 | } | 240 | } |
@@ -542,96 +546,109 @@ static void calc_block_sector(loff_t from, int *block0, int *block1, int *page, | |||
542 | } | 546 | } |
543 | 547 | ||
544 | /** | 548 | /** |
545 | * doc_read - Read bytes from flash | 549 | * doc_read_oob - Read out of band bytes from flash |
546 | * @mtd: the device | 550 | * @mtd: the device |
547 | * @from: the offset from first block and first page, in bytes, aligned on page | 551 | * @from: the offset from first block and first page, in bytes, aligned on page |
548 | * size | 552 | * size |
549 | * @len: the number of bytes to read (must be a multiple of 4) | 553 | * @ops: the mtd oob structure |
550 | * @retlen: the number of bytes actually read | ||
551 | * @buf: the filled in buffer | ||
552 | * | 554 | * |
553 | * Reads flash memory pages. This function does not read the OOB chunk, but only | 555 | * Reads flash memory OOB area of pages. |
554 | * the page data. | ||
555 | * | 556 | * |
556 | * Returns 0 if read successfull, of -EIO, -EINVAL if an error occured | 557 | * Returns 0 if read successfull, of -EIO, -EINVAL if an error occured |
557 | */ | 558 | */ |
558 | static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, | 559 | static int doc_read_oob(struct mtd_info *mtd, loff_t from, |
559 | size_t *retlen, u_char *buf) | 560 | struct mtd_oob_ops *ops) |
560 | { | 561 | { |
561 | struct docg3 *docg3 = mtd->priv; | 562 | struct docg3 *docg3 = mtd->priv; |
562 | int block0, block1, page, readlen, ret, ofs = 0; | 563 | int block0, block1, page, ret, ofs = 0; |
563 | int syn[DOC_ECC_BCH_SIZE], eccconf1; | 564 | u8 *oobbuf = ops->oobbuf; |
564 | u8 oob[DOC_LAYOUT_OOB_SIZE]; | 565 | u8 *buf = ops->datbuf; |
566 | size_t len, ooblen, nbdata, nboob; | ||
567 | u8 calc_ecc[DOC_ECC_BCH_SIZE], eccconf1; | ||
568 | |||
569 | if (buf) | ||
570 | len = ops->len; | ||
571 | else | ||
572 | len = 0; | ||
573 | if (oobbuf) | ||
574 | ooblen = ops->ooblen; | ||
575 | else | ||
576 | ooblen = 0; | ||
577 | |||
578 | if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) | ||
579 | oobbuf += ops->ooboffs; | ||
580 | |||
581 | doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", | ||
582 | from, ops->mode, buf, len, oobbuf, ooblen); | ||
583 | if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % DOC_LAYOUT_OOB_SIZE) || | ||
584 | (from % DOC_LAYOUT_PAGE_SIZE)) | ||
585 | return -EINVAL; | ||
565 | 586 | ||
566 | ret = -EINVAL; | 587 | ret = -EINVAL; |
567 | doc_dbg("doc_read(from=%lld, len=%zu, buf=%p)\n", from, len, buf); | 588 | calc_block_sector(from + len, &block0, &block1, &page, &ofs); |
568 | if (from % DOC_LAYOUT_PAGE_SIZE) | ||
569 | goto err; | ||
570 | if (len % 4) | ||
571 | goto err; | ||
572 | calc_block_sector(from, &block0, &block1, &page, &ofs); | ||
573 | if (block1 > docg3->max_block) | 589 | if (block1 > docg3->max_block) |
574 | goto err; | 590 | goto err; |
575 | 591 | ||
576 | *retlen = 0; | 592 | ops->oobretlen = 0; |
593 | ops->retlen = 0; | ||
577 | ret = 0; | 594 | ret = 0; |
578 | readlen = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE); | 595 | while (!ret && (len > 0 || ooblen > 0)) { |
579 | while (!ret && len > 0) { | 596 | calc_block_sector(from, &block0, &block1, &page, &ofs); |
580 | readlen = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE); | 597 | nbdata = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE); |
598 | nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE); | ||
581 | ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); | 599 | ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); |
582 | if (ret < 0) | 600 | if (ret < 0) |
583 | goto err; | 601 | goto err; |
584 | ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES); | 602 | ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES); |
585 | if (ret < 0) | 603 | if (ret < 0) |
586 | goto err_in_read; | 604 | goto err_in_read; |
587 | ret = doc_read_page_getbytes(docg3, readlen, buf, 1); | 605 | ret = doc_read_page_getbytes(docg3, nbdata, buf, 1); |
588 | if (ret < readlen) | 606 | if (ret < nbdata) |
589 | goto err_in_read; | 607 | goto err_in_read; |
590 | ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE, | 608 | doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE - nbdata, |
591 | oob, 0); | 609 | NULL, 0); |
592 | if (ret < DOC_LAYOUT_OOB_SIZE) | 610 | ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0); |
611 | if (ret < nboob) | ||
593 | goto err_in_read; | 612 | goto err_in_read; |
613 | doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, | ||
614 | NULL, 0); | ||
594 | 615 | ||
595 | *retlen += readlen; | 616 | doc_get_hw_bch_syndroms(docg3, calc_ecc); |
596 | buf += readlen; | ||
597 | len -= readlen; | ||
598 | |||
599 | ofs ^= DOC_LAYOUT_PAGE_OOB_SIZE; | ||
600 | if (ofs == 0) | ||
601 | page += 2; | ||
602 | if (page > DOC_ADDR_PAGE_MASK) { | ||
603 | page = 0; | ||
604 | block0 += 2; | ||
605 | block1 += 2; | ||
606 | } | ||
607 | |||
608 | /* | ||
609 | * There should be a BCH bitstream fixing algorithm here ... | ||
610 | * By now, a page read failure is triggered by BCH error | ||
611 | */ | ||
612 | doc_get_hw_bch_syndroms(docg3, syn); | ||
613 | eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); | 617 | eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); |
614 | 618 | ||
615 | doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", | 619 | if (nboob >= DOC_LAYOUT_OOB_SIZE) { |
616 | oob[0], oob[1], oob[2], oob[3], oob[4], | 620 | doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", |
617 | oob[5], oob[6]); | 621 | oobbuf[0], oobbuf[1], oobbuf[2], oobbuf[3], |
618 | doc_dbg("OOB - HAMMING: %02x\n", oob[7]); | 622 | oobbuf[4], oobbuf[5], oobbuf[6]); |
619 | doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", | 623 | doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]); |
620 | oob[8], oob[9], oob[10], oob[11], oob[12], | 624 | doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", |
621 | oob[13], oob[14]); | 625 | oobbuf[8], oobbuf[9], oobbuf[10], oobbuf[11], |
622 | doc_dbg("OOB - UNUSED: %02x\n", oob[15]); | 626 | oobbuf[12], oobbuf[13], oobbuf[14]); |
627 | doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]); | ||
628 | } | ||
623 | doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1); | 629 | doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1); |
624 | doc_dbg("ECC BCH syndrom: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", | 630 | doc_dbg("ECC CALC_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", |
625 | syn[0], syn[1], syn[2], syn[3], syn[4], syn[5], syn[6]); | 631 | calc_ecc[0], calc_ecc[1], calc_ecc[2], |
632 | calc_ecc[3], calc_ecc[4], calc_ecc[5], | ||
633 | calc_ecc[6]); | ||
626 | 634 | ||
627 | ret = -EBADMSG; | 635 | ret = -EBADMSG; |
628 | if (block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) { | 636 | if (block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) { |
629 | if (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) | 637 | if ((eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) && |
638 | (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN)) | ||
630 | goto err_in_read; | 639 | goto err_in_read; |
631 | if (is_prot_seq_error(docg3)) | 640 | if (is_prot_seq_error(docg3)) |
632 | goto err_in_read; | 641 | goto err_in_read; |
633 | } | 642 | } |
643 | |||
634 | doc_read_page_finish(docg3); | 644 | doc_read_page_finish(docg3); |
645 | ops->retlen += nbdata; | ||
646 | ops->oobretlen += nboob; | ||
647 | buf += nbdata; | ||
648 | oobbuf += nboob; | ||
649 | len -= nbdata; | ||
650 | ooblen -= nboob; | ||
651 | from += DOC_LAYOUT_PAGE_SIZE; | ||
635 | } | 652 | } |
636 | 653 | ||
637 | return 0; | 654 | return 0; |
@@ -642,54 +659,33 @@ err: | |||
642 | } | 659 | } |
643 | 660 | ||
644 | /** | 661 | /** |
645 | * doc_read_oob - Read out of band bytes from flash | 662 | * doc_read - Read bytes from flash |
646 | * @mtd: the device | 663 | * @mtd: the device |
647 | * @from: the offset from first block and first page, in bytes, aligned on page | 664 | * @from: the offset from first block and first page, in bytes, aligned on page |
648 | * size | 665 | * size |
649 | * @ops: the mtd oob structure | 666 | * @len: the number of bytes to read (must be a multiple of 4) |
667 | * @retlen: the number of bytes actually read | ||
668 | * @buf: the filled in buffer | ||
650 | * | 669 | * |
651 | * Reads flash memory OOB area of pages. | 670 | * Reads flash memory pages. This function does not read the OOB chunk, but only |
671 | * the page data. | ||
652 | * | 672 | * |
653 | * Returns 0 if read successfull, of -EIO, -EINVAL if an error occured | 673 | * Returns 0 if read successfull, of -EIO, -EINVAL if an error occured |
654 | */ | 674 | */ |
655 | static int doc_read_oob(struct mtd_info *mtd, loff_t from, | 675 | static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, |
656 | struct mtd_oob_ops *ops) | 676 | size_t *retlen, u_char *buf) |
657 | { | 677 | { |
658 | struct docg3 *docg3 = mtd->priv; | 678 | struct mtd_oob_ops ops; |
659 | int block0, block1, page, ofs, ret; | 679 | size_t ret; |
660 | u8 *buf = ops->oobbuf; | ||
661 | size_t len = ops->ooblen; | ||
662 | 680 | ||
663 | doc_dbg("doc_read_oob(from=%lld, buf=%p, len=%zu)\n", from, buf, len); | 681 | memset(&ops, 0, sizeof(ops)); |
664 | if (len != DOC_LAYOUT_OOB_SIZE) | 682 | ops.datbuf = buf; |
665 | return -EINVAL; | 683 | ops.len = len; |
684 | ops.mode = MTD_OPS_AUTO_OOB; | ||
666 | 685 | ||
667 | switch (ops->mode) { | 686 | ret = doc_read_oob(mtd, from, &ops); |
668 | case MTD_OPS_PLACE_OOB: | 687 | *retlen = ops.retlen; |
669 | buf += ops->ooboffs; | 688 | return ret; |
670 | break; | ||
671 | default: | ||
672 | break; | ||
673 | } | ||
674 | |||
675 | calc_block_sector(from, &block0, &block1, &page, &ofs); | ||
676 | if (block1 > docg3->max_block) | ||
677 | return -EINVAL; | ||
678 | |||
679 | ret = doc_read_page_prepare(docg3, block0, block1, page, | ||
680 | ofs + DOC_LAYOUT_PAGE_SIZE); | ||
681 | if (!ret) | ||
682 | ret = doc_read_page_ecc_init(docg3, DOC_LAYOUT_OOB_SIZE); | ||
683 | if (!ret) | ||
684 | ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE, | ||
685 | buf, 1); | ||
686 | doc_read_page_finish(docg3); | ||
687 | |||
688 | if (ret > 0) | ||
689 | ops->oobretlen = ret; | ||
690 | else | ||
691 | ops->oobretlen = 0; | ||
692 | return (ret > 0) ? 0 : ret; | ||
693 | } | 689 | } |
694 | 690 | ||
695 | static int doc_reload_bbt(struct docg3 *docg3) | 691 | static int doc_reload_bbt(struct docg3 *docg3) |