diff options
Diffstat (limited to 'drivers/mtd/onenand/onenand_base.c')
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 139 |
1 files changed, 112 insertions, 27 deletions
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 | ||