diff options
author | Kyungmin Park <kyungmin.park@samsung.com> | 2007-06-30 00:57:49 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-06-30 03:24:57 -0400 |
commit | ee9745fcf214272b7cdd9d320d044cf433ee958e (patch) | |
tree | 340cbf42788ea9ea9f504eeb80efc12c5d1c11ed /drivers | |
parent | 1bddb9a8ba30dffa963c76283bc482b67fb90d8a (diff) |
[MTD] [OneNAND] 2X program support
The 2X Program is an extension of Program Operation.
Since the device is equipped with two DataRAMs, and two-plane NAND Flash
memory array, these two component enables simultaneous program of 4KiB.
Plane1 has only even blocks such as block0, block2, block4 while Plane2
has only odd blocks such as block1, block3, block5.
So MTD regards it as 4KiB page size and 256KiB block size
Now the following chips support it. (KFXXX16Q2M)
Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M,
Mux: KFM2G16Q2M, KFN4G16Q2M,
And more recent chips
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/onenand/Kconfig | 16 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 139 |
2 files changed, 128 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; | 817 | from += thislen; |
771 | if (read + thislen < len) { | 818 | if (read + thislen < len) { |
772 | this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); | 819 | this->command(mtd, ONENAND_CMD_READ, from, writesize); |
773 | /* | 820 | /* |
774 | * Chip boundary handling in DDP | 821 | * Chip boundary handling in DDP |
775 | * Now we issued chip 1 read and pointed chip 1 | 822 | * Now we issued chip 1 read and pointed chip 1 |
@@ -794,7 +841,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
794 | this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); | 841 | this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); |
795 | ONENAND_SET_NEXT_BUFFERRAM(this); | 842 | ONENAND_SET_NEXT_BUFFERRAM(this); |
796 | buf += thislen; | 843 | buf += thislen; |
797 | thislen = min_t(int, mtd->writesize, len - read); | 844 | thislen = min_t(int, writesize, len - read); |
798 | column = 0; | 845 | column = 0; |
799 | cond_resched(); | 846 | cond_resched(); |
800 | /* Now wait for load */ | 847 | /* Now wait for load */ |
@@ -1079,7 +1126,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | |||
1079 | /* Read more? */ | 1126 | /* Read more? */ |
1080 | if (read < len) { | 1127 | if (read < len) { |
1081 | /* Update Page size */ | 1128 | /* Update Page size */ |
1082 | from += mtd->writesize; | 1129 | from += this->writesize; |
1083 | column = 0; | 1130 | column = 0; |
1084 | } | 1131 | } |
1085 | } | 1132 | } |
@@ -1135,12 +1182,12 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, | |||
1135 | int thislen, column; | 1182 | int thislen, column; |
1136 | 1183 | ||
1137 | while (len != 0) { | 1184 | while (len != 0) { |
1138 | thislen = min_t(int, mtd->writesize, len); | 1185 | thislen = min_t(int, this->writesize, len); |
1139 | column = addr & (mtd->writesize - 1); | 1186 | column = addr & (this->writesize - 1); |
1140 | if (column + thislen > mtd->writesize) | 1187 | if (column + thislen > this->writesize) |
1141 | thislen = mtd->writesize - column; | 1188 | thislen = this->writesize - column; |
1142 | 1189 | ||
1143 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); | 1190 | this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); |
1144 | 1191 | ||
1145 | onenand_update_bufferram(mtd, addr, 0); | 1192 | onenand_update_bufferram(mtd, addr, 0); |
1146 | 1193 | ||
@@ -1236,6 +1283,10 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1236 | 1283 | ||
1237 | /* In partial page write we don't update bufferram */ | 1284 | /* In partial page write we don't update bufferram */ |
1238 | onenand_update_bufferram(mtd, to, !ret && !subpage); | 1285 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
1286 | if (ONENAND_IS_2PLANE(this)) { | ||
1287 | ONENAND_SET_BUFFERRAM1(this); | ||
1288 | onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); | ||
1289 | } | ||
1239 | 1290 | ||
1240 | if (ret) { | 1291 | if (ret) { |
1241 | printk(KERN_ERR "onenand_write: write filaed %d\n", ret); | 1292 | printk(KERN_ERR "onenand_write: write filaed %d\n", ret); |
@@ -1384,6 +1435,10 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1384 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | 1435 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); |
1385 | 1436 | ||
1386 | onenand_update_bufferram(mtd, to, 0); | 1437 | onenand_update_bufferram(mtd, to, 0); |
1438 | if (ONENAND_IS_2PLANE(this)) { | ||
1439 | ONENAND_SET_BUFFERRAM1(this); | ||
1440 | onenand_update_bufferram(mtd, to + this->writesize, 0); | ||
1441 | } | ||
1387 | 1442 | ||
1388 | ret = this->wait(mtd, FL_WRITING); | 1443 | ret = this->wait(mtd, FL_WRITING); |
1389 | if (ret) { | 1444 | if (ret) { |
@@ -2107,6 +2162,7 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | |||
2107 | * | 2162 | * |
2108 | * Check and set OneNAND features | 2163 | * Check and set OneNAND features |
2109 | * - lock scheme | 2164 | * - lock scheme |
2165 | * - two plane | ||
2110 | */ | 2166 | */ |
2111 | static void onenand_check_features(struct mtd_info *mtd) | 2167 | static void onenand_check_features(struct mtd_info *mtd) |
2112 | { | 2168 | { |
@@ -2118,19 +2174,35 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
2118 | process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; | 2174 | process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; |
2119 | 2175 | ||
2120 | /* Lock scheme */ | 2176 | /* Lock scheme */ |
2121 | if (density >= ONENAND_DEVICE_DENSITY_1Gb) { | 2177 | switch (density) { |
2178 | case ONENAND_DEVICE_DENSITY_4Gb: | ||
2179 | this->options |= ONENAND_HAS_2PLANE; | ||
2180 | |||
2181 | case ONENAND_DEVICE_DENSITY_2Gb: | ||
2182 | /* 2Gb DDP don't have 2 plane */ | ||
2183 | if (!ONENAND_IS_DDP(this)) | ||
2184 | this->options |= ONENAND_HAS_2PLANE; | ||
2185 | this->options |= ONENAND_HAS_UNLOCK_ALL; | ||
2186 | |||
2187 | case ONENAND_DEVICE_DENSITY_1Gb: | ||
2122 | /* A-Die has all block unlock */ | 2188 | /* A-Die has all block unlock */ |
2123 | if (process) { | 2189 | if (process) |
2124 | printk(KERN_DEBUG "Chip support all block unlock\n"); | ||
2125 | this->options |= ONENAND_HAS_UNLOCK_ALL; | 2190 | this->options |= ONENAND_HAS_UNLOCK_ALL; |
2126 | } | 2191 | break; |
2127 | } else { | 2192 | |
2128 | /* Some OneNAND has continues lock scheme */ | 2193 | default: |
2129 | if (!process) { | 2194 | /* Some OneNAND has continuous lock scheme */ |
2130 | printk(KERN_DEBUG "Lock scheme is Continues Lock\n"); | 2195 | if (!process) |
2131 | this->options |= ONENAND_HAS_CONT_LOCK; | 2196 | this->options |= ONENAND_HAS_CONT_LOCK; |
2132 | } | 2197 | break; |
2133 | } | 2198 | } |
2199 | |||
2200 | if (this->options & ONENAND_HAS_CONT_LOCK) | ||
2201 | printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); | ||
2202 | if (this->options & ONENAND_HAS_UNLOCK_ALL) | ||
2203 | printk(KERN_DEBUG "Chip support all block unlock\n"); | ||
2204 | if (this->options & ONENAND_HAS_2PLANE) | ||
2205 | printk(KERN_DEBUG "Chip has 2 plane\n"); | ||
2134 | } | 2206 | } |
2135 | 2207 | ||
2136 | /** | 2208 | /** |
@@ -2257,6 +2329,8 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2257 | this->erase_shift = ffs(mtd->erasesize) - 1; | 2329 | this->erase_shift = ffs(mtd->erasesize) - 1; |
2258 | this->page_shift = ffs(mtd->writesize) - 1; | 2330 | this->page_shift = ffs(mtd->writesize) - 1; |
2259 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; | 2331 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; |
2332 | /* It's real page size */ | ||
2333 | this->writesize = mtd->writesize; | ||
2260 | 2334 | ||
2261 | /* REVIST: Multichip handling */ | 2335 | /* REVIST: Multichip handling */ |
2262 | 2336 | ||
@@ -2265,6 +2339,17 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2265 | /* Check OneNAND features */ | 2339 | /* Check OneNAND features */ |
2266 | onenand_check_features(mtd); | 2340 | onenand_check_features(mtd); |
2267 | 2341 | ||
2342 | /* | ||
2343 | * We emulate the 4KiB page and 256KiB erase block size | ||
2344 | * But oobsize is still 64 bytes. | ||
2345 | * It is only valid if you turn on 2X program support, | ||
2346 | * Otherwise it will be ignored by compiler. | ||
2347 | */ | ||
2348 | if (ONENAND_IS_2PLANE(this)) { | ||
2349 | mtd->writesize <<= 1; | ||
2350 | mtd->erasesize <<= 1; | ||
2351 | } | ||
2352 | |||
2268 | return 0; | 2353 | return 0; |
2269 | } | 2354 | } |
2270 | 2355 | ||