diff options
| -rw-r--r-- | drivers/mtd/onenand/Kconfig | 16 | ||||
| -rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 139 | ||||
| -rw-r--r-- | include/linux/mtd/onenand.h | 12 | ||||
| -rw-r--r-- | include/linux/mtd/onenand_regs.h | 4 |
4 files changed, 144 insertions, 27 deletions
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index c257d397d08a..64ec034c43c5 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig | |||
| @@ -40,4 +40,20 @@ config MTD_ONENAND_OTP | |||
| 40 | 40 | ||
| 41 | OTP block is fully-guaranteed to be a valid block. | 41 | OTP block is fully-guaranteed to be a valid block. |
| 42 | 42 | ||
| 43 | config MTD_ONENAND_2X_PROGRAM | ||
| 44 | bool "OneNAND 2X program support" | ||
| 45 | help | ||
| 46 | The 2X Program is an extension of Program Operation. | ||
| 47 | Since the device is equipped with two DataRAMs, and two-plane NAND | ||
| 48 | Flash memory array, these two component enables simultaneous program | ||
| 49 | of 4KiB. Plane1 has only even blocks such as block0, block2, block4 | ||
| 50 | while Plane2 has only odd blocks such as block1, block3, block5. | ||
| 51 | So MTD regards it as 4KiB page size and 256KiB block size | ||
| 52 | |||
| 53 | Now the following chips support it. (KFXXX16Q2M) | ||
| 54 | Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M, | ||
| 55 | Mux: KFM2G16Q2M, KFN4G16Q2M, | ||
| 56 | |||
| 57 | And more recent chips | ||
| 58 | |||
| 43 | endif # MTD_ONENAND | 59 | endif # MTD_ONENAND |
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 0537fac8de74..7d194cfdb873 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
| @@ -206,6 +206,15 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
| 206 | default: | 206 | default: |
| 207 | block = (int) (addr >> this->erase_shift); | 207 | block = (int) (addr >> this->erase_shift); |
| 208 | page = (int) (addr >> this->page_shift); | 208 | page = (int) (addr >> this->page_shift); |
| 209 | |||
| 210 | if (ONENAND_IS_2PLANE(this)) { | ||
| 211 | /* Make the even block number */ | ||
| 212 | block &= ~1; | ||
| 213 | /* Is it the odd plane? */ | ||
| 214 | if (addr & this->writesize) | ||
| 215 | block++; | ||
| 216 | page >>= 1; | ||
| 217 | } | ||
| 209 | page &= this->page_mask; | 218 | page &= this->page_mask; |
| 210 | break; | 219 | break; |
| 211 | } | 220 | } |
| @@ -216,8 +225,12 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
| 216 | value = onenand_bufferram_address(this, block); | 225 | value = onenand_bufferram_address(this, block); |
| 217 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | 226 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); |
| 218 | 227 | ||
| 219 | /* Switch to the next data buffer */ | 228 | if (ONENAND_IS_2PLANE(this)) |
| 220 | ONENAND_SET_NEXT_BUFFERRAM(this); | 229 | /* It is always BufferRAM0 */ |
| 230 | ONENAND_SET_BUFFERRAM0(this); | ||
| 231 | else | ||
| 232 | /* Switch to the next data buffer */ | ||
| 233 | ONENAND_SET_NEXT_BUFFERRAM(this); | ||
| 221 | 234 | ||
| 222 | return 0; | 235 | return 0; |
| 223 | } | 236 | } |
| @@ -247,6 +260,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
| 247 | break; | 260 | break; |
| 248 | 261 | ||
| 249 | default: | 262 | default: |
| 263 | if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) | ||
| 264 | cmd = ONENAND_CMD_2X_PROG; | ||
| 250 | dataram = ONENAND_CURRENT_BUFFERRAM(this); | 265 | dataram = ONENAND_CURRENT_BUFFERRAM(this); |
| 251 | break; | 266 | break; |
| 252 | } | 267 | } |
| @@ -445,8 +460,9 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) | |||
| 445 | struct onenand_chip *this = mtd->priv; | 460 | struct onenand_chip *this = mtd->priv; |
| 446 | 461 | ||
| 447 | if (ONENAND_CURRENT_BUFFERRAM(this)) { | 462 | if (ONENAND_CURRENT_BUFFERRAM(this)) { |
| 463 | /* Note: the 'this->writesize' is a real page size */ | ||
| 448 | if (area == ONENAND_DATARAM) | 464 | if (area == ONENAND_DATARAM) |
| 449 | return mtd->writesize; | 465 | return this->writesize; |
| 450 | if (area == ONENAND_SPARERAM) | 466 | if (area == ONENAND_SPARERAM) |
| 451 | return mtd->oobsize; | 467 | return mtd->oobsize; |
| 452 | } | 468 | } |
| @@ -572,6 +588,30 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, | |||
| 572 | } | 588 | } |
| 573 | 589 | ||
| 574 | /** | 590 | /** |
| 591 | * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode | ||
| 592 | * @param mtd MTD data structure | ||
| 593 | * @param addr address to check | ||
| 594 | * @return blockpage address | ||
| 595 | * | ||
| 596 | * Get blockpage address at 2x program mode | ||
| 597 | */ | ||
| 598 | static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) | ||
| 599 | { | ||
| 600 | struct onenand_chip *this = mtd->priv; | ||
| 601 | int blockpage, block, page; | ||
| 602 | |||
| 603 | /* Calculate the even block number */ | ||
| 604 | block = (int) (addr >> this->erase_shift) & ~1; | ||
| 605 | /* Is it the odd plane? */ | ||
| 606 | if (addr & this->writesize) | ||
| 607 | block++; | ||
| 608 | page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; | ||
| 609 | blockpage = (block << 7) | page; | ||
| 610 | |||
| 611 | return blockpage; | ||
| 612 | } | ||
| 613 | |||
| 614 | /** | ||
| 575 | * onenand_check_bufferram - [GENERIC] Check BufferRAM information | 615 | * onenand_check_bufferram - [GENERIC] Check BufferRAM information |
| 576 | * @param mtd MTD data structure | 616 | * @param mtd MTD data structure |
| 577 | * @param addr address to check | 617 | * @param addr address to check |
| @@ -585,7 +625,10 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | |||
| 585 | int blockpage, found = 0; | 625 | int blockpage, found = 0; |
| 586 | unsigned int i; | 626 | unsigned int i; |
| 587 | 627 | ||
| 588 | blockpage = (int) (addr >> this->page_shift); | 628 | if (ONENAND_IS_2PLANE(this)) |
| 629 | blockpage = onenand_get_2x_blockpage(mtd, addr); | ||
| 630 | else | ||
| 631 | blockpage = (int) (addr >> this->page_shift); | ||
| 589 | 632 | ||
| 590 | /* Is there valid data? */ | 633 | /* Is there valid data? */ |
| 591 | i = ONENAND_CURRENT_BUFFERRAM(this); | 634 | i = ONENAND_CURRENT_BUFFERRAM(this); |
| @@ -625,7 +668,10 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, | |||
| 625 | int blockpage; | 668 | int blockpage; |
| 626 | unsigned int i; | 669 | unsigned int i; |
| 627 | 670 | ||
| 628 | blockpage = (int) (addr >> this->page_shift); | 671 | if (ONENAND_IS_2PLANE(this)) |
| 672 | blockpage = onenand_get_2x_blockpage(mtd, addr); | ||
| 673 | else | ||
| 674 | blockpage = (int) (addr >> this->page_shift); | ||
| 629 | 675 | ||
| 630 | /* Invalidate another BufferRAM */ | 676 | /* Invalidate another BufferRAM */ |
| 631 | i = ONENAND_NEXT_BUFFERRAM(this); | 677 | i = ONENAND_NEXT_BUFFERRAM(this); |
| @@ -734,6 +780,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 734 | int read = 0, column; | 780 | int read = 0, column; |
| 735 | int thislen; | 781 | int thislen; |
| 736 | int ret = 0, boundary = 0; | 782 | int ret = 0, boundary = 0; |
| 783 | int writesize = this->writesize; | ||
| 737 | 784 | ||
| 738 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | 785 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); |
| 739 | 786 | ||
| @@ -754,22 +801,22 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 754 | /* Do first load to bufferRAM */ | 801 | /* Do first load to bufferRAM */ |
| 755 | if (read < len) { | 802 | if (read < len) { |
| 756 | if (!onenand_check_bufferram(mtd, from)) { | 803 | if (!onenand_check_bufferram(mtd, from)) { |
| 757 | this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); | 804 | this->command(mtd, ONENAND_CMD_READ, from, writesize); |
| 758 | ret = this->wait(mtd, FL_READING); | 805 | ret = this->wait(mtd, FL_READING); |
| 759 | onenand_update_bufferram(mtd, from, !ret); | 806 | onenand_update_bufferram(mtd, from, !ret); |
| 760 | } | 807 | } |
| 761 | } | 808 | } |
| 762 | 809 | ||
| 763 | thislen = min_t(int, mtd->writesize, len - read); | 810 | thislen = min_t(int, writesize, len - read); |
| 764 | column = from & (mtd->writesize - 1); | 811 | column = from & (writesize - 1); |
| 765 | if (column + thislen > mtd->writesize) | 812 | if (column + thislen > writesize) |
| 766 | thislen = mtd->writesize - column; | 813 | thislen = writesize - column; |
| 767 | 814 | ||
| 768 | while (!ret) { | 815 | while (!ret) { |
| 769 | /* If there is more to load then start next load */ | 816 | /* If there is more to load then start next load */ |
| 770 | from += thislen; | ||
