diff options
Diffstat (limited to 'drivers/mtd/onenand')
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 857 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 14 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_sim.c | 81 |
3 files changed, 867 insertions, 85 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 2346857a275d..8d4c9c253732 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -9,6 +9,10 @@ | |||
9 | * auto-placement support, read-while load support, various fixes | 9 | * auto-placement support, read-while load support, various fixes |
10 | * Copyright (C) Nokia Corporation, 2007 | 10 | * Copyright (C) Nokia Corporation, 2007 |
11 | * | 11 | * |
12 | * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com> | ||
13 | * Flex-OneNAND support | ||
14 | * Copyright (C) Samsung Electronics, 2008 | ||
15 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | 16 | * This program is free software; you can redistribute it and/or modify |
13 | * it under the terms of the GNU General Public License version 2 as | 17 | * it under the terms of the GNU General Public License version 2 as |
14 | * published by the Free Software Foundation. | 18 | * published by the Free Software Foundation. |
@@ -27,6 +31,30 @@ | |||
27 | 31 | ||
28 | #include <asm/io.h> | 32 | #include <asm/io.h> |
29 | 33 | ||
34 | /* Default Flex-OneNAND boundary and lock respectively */ | ||
35 | static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; | ||
36 | |||
37 | /** | ||
38 | * onenand_oob_128 - oob info for Flex-Onenand with 4KB page | ||
39 | * For now, we expose only 64 out of 80 ecc bytes | ||
40 | */ | ||
41 | static struct nand_ecclayout onenand_oob_128 = { | ||
42 | .eccbytes = 64, | ||
43 | .eccpos = { | ||
44 | 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | ||
45 | 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, | ||
46 | 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, | ||
47 | 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, | ||
48 | 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, | ||
49 | 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, | ||
50 | 102, 103, 104, 105 | ||
51 | }, | ||
52 | .oobfree = { | ||
53 | {2, 4}, {18, 4}, {34, 4}, {50, 4}, | ||
54 | {66, 4}, {82, 4}, {98, 4}, {114, 4} | ||
55 | } | ||
56 | }; | ||
57 | |||
30 | /** | 58 | /** |
31 | * onenand_oob_64 - oob info for large (2KB) page | 59 | * onenand_oob_64 - oob info for large (2KB) page |
32 | */ | 60 | */ |
@@ -65,6 +93,14 @@ static const unsigned char ffchars[] = { | |||
65 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ | 93 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ |
66 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 94 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
67 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ | 95 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ |
96 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
97 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ | ||
98 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
99 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ | ||
100 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
101 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ | ||
102 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
103 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ | ||
68 | }; | 104 | }; |
69 | 105 | ||
70 | /** | 106 | /** |
@@ -171,6 +207,70 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) | |||
171 | } | 207 | } |
172 | 208 | ||
173 | /** | 209 | /** |
210 | * flexonenand_block- For given address return block number | ||
211 | * @param this - OneNAND device structure | ||
212 | * @param addr - Address for which block number is needed | ||
213 | */ | ||
214 | static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr) | ||
215 | { | ||
216 | unsigned boundary, blk, die = 0; | ||
217 | |||
218 | if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) { | ||
219 | die = 1; | ||
220 | addr -= this->diesize[0]; | ||
221 | } | ||
222 | |||
223 | boundary = this->boundary[die]; | ||
224 | |||
225 | blk = addr >> (this->erase_shift - 1); | ||
226 | if (blk > boundary) | ||
227 | blk = (blk + boundary + 1) >> 1; | ||
228 | |||
229 | blk += die ? this->density_mask : 0; | ||
230 | return blk; | ||
231 | } | ||
232 | |||
233 | inline unsigned onenand_block(struct onenand_chip *this, loff_t addr) | ||
234 | { | ||
235 | if (!FLEXONENAND(this)) | ||
236 | return addr >> this->erase_shift; | ||
237 | return flexonenand_block(this, addr); | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * flexonenand_addr - Return address of the block | ||
242 | * @this: OneNAND device structure | ||
243 | * @block: Block number on Flex-OneNAND | ||
244 | * | ||
245 | * Return address of the block | ||
246 | */ | ||
247 | static loff_t flexonenand_addr(struct onenand_chip *this, int block) | ||
248 | { | ||
249 | loff_t ofs = 0; | ||
250 | int die = 0, boundary; | ||
251 | |||
252 | if (ONENAND_IS_DDP(this) && block >= this->density_mask) { | ||
253 | block -= this->density_mask; | ||
254 | die = 1; | ||
255 | ofs = this->diesize[0]; | ||
256 | } | ||
257 | |||
258 | boundary = this->boundary[die]; | ||
259 | ofs += (loff_t)block << (this->erase_shift - 1); | ||
260 | if (block > (boundary + 1)) | ||
261 | ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1); | ||
262 | return ofs; | ||
263 | } | ||
264 | |||
265 | loff_t onenand_addr(struct onenand_chip *this, int block) | ||
266 | { | ||
267 | if (!FLEXONENAND(this)) | ||
268 | return (loff_t)block << this->erase_shift; | ||
269 | return flexonenand_addr(this, block); | ||
270 | } | ||
271 | EXPORT_SYMBOL(onenand_addr); | ||
272 | |||
273 | /** | ||
174 | * onenand_get_density - [DEFAULT] Get OneNAND density | 274 | * onenand_get_density - [DEFAULT] Get OneNAND density |
175 | * @param dev_id OneNAND device ID | 275 | * @param dev_id OneNAND device ID |
176 | * | 276 | * |
@@ -183,6 +283,22 @@ static inline int onenand_get_density(int dev_id) | |||
183 | } | 283 | } |
184 | 284 | ||
185 | /** | 285 | /** |
286 | * flexonenand_region - [Flex-OneNAND] Return erase region of addr | ||
287 | * @param mtd MTD device structure | ||
288 | * @param addr address whose erase region needs to be identified | ||
289 | */ | ||
290 | int flexonenand_region(struct mtd_info *mtd, loff_t addr) | ||
291 | { | ||
292 | int i; | ||
293 | |||
294 | for (i = 0; i < mtd->numeraseregions; i++) | ||
295 | if (addr < mtd->eraseregions[i].offset) | ||
296 | break; | ||
297 | return i - 1; | ||
298 | } | ||
299 | EXPORT_SYMBOL(flexonenand_region); | ||
300 | |||
301 | /** | ||
186 | * onenand_command - [DEFAULT] Send command to OneNAND device | 302 | * onenand_command - [DEFAULT] Send command to OneNAND device |
187 | * @param mtd MTD device structure | 303 | * @param mtd MTD device structure |
188 | * @param cmd the command to be sent | 304 | * @param cmd the command to be sent |
@@ -207,16 +323,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
207 | page = -1; | 323 | page = -1; |
208 | break; | 324 | break; |
209 | 325 | ||
326 | case FLEXONENAND_CMD_PI_ACCESS: | ||
327 | /* addr contains die index */ | ||
328 | block = addr * this->density_mask; | ||
329 | page = -1; | ||
330 | break; | ||
331 | |||
210 | case ONENAND_CMD_ERASE: | 332 | case ONENAND_CMD_ERASE: |
211 | case ONENAND_CMD_BUFFERRAM: | 333 | case ONENAND_CMD_BUFFERRAM: |
212 | case ONENAND_CMD_OTP_ACCESS: | 334 | case ONENAND_CMD_OTP_ACCESS: |
213 | block = (int) (addr >> this->erase_shift); | 335 | block = onenand_block(this, addr); |
214 | page = -1; | 336 | page = -1; |
215 | break; | 337 | break; |
216 | 338 | ||
339 | case FLEXONENAND_CMD_READ_PI: | ||
340 | cmd = ONENAND_CMD_READ; | ||
341 | block = addr * this->density_mask; | ||
342 | page = 0; | ||
343 | break; | ||
344 | |||
217 | default: | 345 | default: |
218 | block = (int) (addr >> this->erase_shift); | 346 | block = onenand_block(this, addr); |
219 | page = (int) (addr >> this->page_shift); | 347 | page = (int) (addr - onenand_addr(this, block)) >> this->page_shift; |
220 | 348 | ||
221 | if (ONENAND_IS_2PLANE(this)) { | 349 | if (ONENAND_IS_2PLANE(this)) { |
222 | /* Make the even block number */ | 350 | /* Make the even block number */ |
@@ -236,7 +364,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
236 | value = onenand_bufferram_address(this, block); | 364 | value = onenand_bufferram_address(this, block); |
237 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | 365 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); |
238 | 366 | ||
239 | if (ONENAND_IS_2PLANE(this)) | 367 | if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this)) |
240 | /* It is always BufferRAM0 */ | 368 | /* It is always BufferRAM0 */ |
241 | ONENAND_SET_BUFFERRAM0(this); | 369 | ONENAND_SET_BUFFERRAM0(this); |
242 | else | 370 | else |
@@ -258,13 +386,18 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
258 | 386 | ||
259 | if (page != -1) { | 387 | if (page != -1) { |
260 | /* Now we use page size operation */ | 388 | /* Now we use page size operation */ |
261 | int sectors = 4, count = 4; | 389 | int sectors = 0, count = 0; |
262 | int dataram; | 390 | int dataram; |
263 | 391 | ||
264 | switch (cmd) { | 392 | switch (cmd) { |
393 | case FLEXONENAND_CMD_RECOVER_LSB: | ||
265 | case ONENAND_CMD_READ: | 394 | case ONENAND_CMD_READ: |
266 | case ONENAND_CMD_READOOB: | 395 | case ONENAND_CMD_READOOB: |
267 | dataram = ONENAND_SET_NEXT_BUFFERRAM(this); | 396 | if (ONENAND_IS_MLC(this)) |
397 | /* It is always BufferRAM0 */ | ||
398 | dataram = ONENAND_SET_BUFFERRAM0(this); | ||
399 | else | ||
400 | dataram = ONENAND_SET_NEXT_BUFFERRAM(this); | ||
268 | break; | 401 | break; |
269 | 402 | ||
270 | default: | 403 | default: |
@@ -293,6 +426,30 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
293 | } | 426 | } |
294 | 427 | ||
295 | /** | 428 | /** |
429 | * onenand_read_ecc - return ecc status | ||
430 | * @param this onenand chip structure | ||
431 | */ | ||
432 | static inline int onenand_read_ecc(struct onenand_chip *this) | ||
433 | { | ||
434 | int ecc, i, result = 0; | ||
435 | |||
436 | if (!FLEXONENAND(this)) | ||
437 | return this->read_word(this->base + ONENAND_REG_ECC_STATUS); | ||
438 | |||
439 | for (i = 0; i < 4; i++) { | ||
440 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i); | ||
441 | if (likely(!ecc)) | ||
442 | continue; | ||
443 | if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) | ||
444 | return ONENAND_ECC_2BIT_ALL; | ||
445 | else | ||
446 | result = ONENAND_ECC_1BIT_ALL; | ||
447 | } | ||
448 | |||
449 | return result; | ||
450 | } | ||
451 | |||
452 | /** | ||
296 | * onenand_wait - [DEFAULT] wait until the command is done | 453 | * onenand_wait - [DEFAULT] wait until the command is done |
297 | * @param mtd MTD device structure | 454 | * @param mtd MTD device structure |
298 | * @param state state to select the max. timeout value | 455 | * @param state state to select the max. timeout value |
@@ -331,14 +488,14 @@ static int onenand_wait(struct mtd_info *mtd, int state) | |||
331 | * power off recovery (POR) test, it should read ECC status first | 488 | * power off recovery (POR) test, it should read ECC status first |
332 | */ | 489 | */ |
333 | if (interrupt & ONENAND_INT_READ) { | 490 | if (interrupt & ONENAND_INT_READ) { |
334 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 491 | int ecc = onenand_read_ecc(this); |
335 | if (ecc) { | 492 | if (ecc) { |
336 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 493 | if (ecc & ONENAND_ECC_2BIT_ALL) { |
337 | printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); | 494 | printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); |
338 | mtd->ecc_stats.failed++; | 495 | mtd->ecc_stats.failed++; |
339 | return -EBADMSG; | 496 | return -EBADMSG; |
340 | } else if (ecc & ONENAND_ECC_1BIT_ALL) { | 497 | } else if (ecc & ONENAND_ECC_1BIT_ALL) { |
341 | printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc); | 498 | printk(KERN_DEBUG "onenand_wait: correctable ECC error = 0x%04x\n", ecc); |
342 | mtd->ecc_stats.corrected++; | 499 | mtd->ecc_stats.corrected++; |
343 | } | 500 | } |
344 | } | 501 | } |
@@ -656,7 +813,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | |||
656 | 813 | ||
657 | if (found && ONENAND_IS_DDP(this)) { | 814 | if (found && ONENAND_IS_DDP(this)) { |
658 | /* Select DataRAM for DDP */ | 815 | /* Select DataRAM for DDP */ |
659 | int block = (int) (addr >> this->erase_shift); | 816 | int block = onenand_block(this, addr); |
660 | int value = onenand_bufferram_address(this, block); | 817 | int value = onenand_bufferram_address(this, block); |
661 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | 818 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); |
662 | } | 819 | } |
@@ -816,6 +973,149 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col | |||
816 | } | 973 | } |
817 | 974 | ||
818 | /** | 975 | /** |
976 | * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data | ||
977 | * @param mtd MTD device structure | ||
978 | * @param addr address to recover | ||
979 | * @param status return value from onenand_wait / onenand_bbt_wait | ||
980 | * | ||
981 | * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has | ||
982 | * lower page address and MSB page has higher page address in paired pages. | ||
983 | * If power off occurs during MSB page program, the paired LSB page data can | ||
984 | * become corrupt. LSB page recovery read is a way to read LSB page though page | ||
985 | * data are corrupted. When uncorrectable error occurs as a result of LSB page | ||
986 | * read after power up, issue LSB page recovery read. | ||
987 | */ | ||
988 | static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) | ||
989 | { | ||
990 | struct onenand_chip *this = mtd->priv; | ||
991 | int i; | ||
992 | |||
993 | /* Recovery is only for Flex-OneNAND */ | ||
994 | if (!FLEXONENAND(this)) | ||
995 | return status; | ||
996 | |||
997 | /* check if we failed due to uncorrectable error */ | ||
998 | if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR) | ||
999 | return status; | ||
1000 | |||
1001 | /* check if address lies in MLC region */ | ||
1002 | i = flexonenand_region(mtd, addr); | ||
1003 | if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift)) | ||
1004 | return status; | ||
1005 | |||
1006 | /* We are attempting to reread, so decrement stats.failed | ||
1007 | * which was incremented by onenand_wait due to read failure | ||
1008 | */ | ||
1009 | printk(KERN_INFO "onenand_recover_lsb: Attempting to recover from uncorrectable read\n"); | ||
1010 | mtd->ecc_stats.failed--; | ||
1011 | |||
1012 | /* Issue the LSB page recovery command */ | ||
1013 | this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); | ||
1014 | return this->wait(mtd, FL_READING); | ||
1015 | } | ||
1016 | |||
1017 | /** | ||
1018 | * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band | ||
1019 | * @param mtd MTD device structure | ||
1020 | * @param from offset to read from | ||
1021 | * @param ops: oob operation description structure | ||
1022 | * | ||
1023 | * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram. | ||
1024 | * So, read-while-load is not present. | ||
1025 | */ | ||
1026 | static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, | ||
1027 | struct mtd_oob_ops *ops) | ||
1028 | { | ||
1029 | struct onenand_chip *this = mtd->priv; | ||
1030 | struct mtd_ecc_stats stats; | ||
1031 | size_t len = ops->len; | ||
1032 | size_t ooblen = ops->ooblen; | ||
1033 | u_char *buf = ops->datbuf; | ||
1034 | u_char *oobbuf = ops->oobbuf; | ||
1035 | int read = 0, column, thislen; | ||
1036 | int oobread = 0, oobcolumn, thisooblen, oobsize; | ||
1037 | int ret = 0; | ||
1038 | int writesize = this->writesize; | ||
1039 | |||
1040 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_mlc_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | ||
1041 | |||
1042 | if (ops->mode == MTD_OOB_AUTO) | ||
1043 | oobsize = this->ecclayout->oobavail; | ||
1044 | else | ||
1045 | oobsize = mtd->oobsize; | ||
1046 | |||
1047 | oobcolumn = from & (mtd->oobsize - 1); | ||
1048 | |||
1049 | /* Do not allow reads past end of device */ | ||
1050 | if (from + len > mtd->size) { | ||
1051 | printk(KERN_ERR "onenand_mlc_read_ops_nolock: Attempt read beyond end of device\n"); | ||
1052 | ops->retlen = 0; | ||
1053 | ops->oobretlen = 0; | ||
1054 | return -EINVAL; | ||
1055 | } | ||
1056 | |||
1057 | stats = mtd->ecc_stats; | ||
1058 | |||
1059 | while (read < len) { | ||
1060 | cond_resched(); | ||
1061 | |||
1062 | thislen = min_t(int, writesize, len - read); | ||
1063 | |||
1064 | column = from & (writesize - 1); | ||
1065 | if (column + thislen > writesize) | ||
1066 | thislen = writesize - column; | ||
1067 | |||
1068 | if (!onenand_check_bufferram(mtd, from)) { | ||
1069 | this->command(mtd, ONENAND_CMD_READ, from, writesize); | ||
1070 | |||
1071 | ret = this->wait(mtd, FL_READING); | ||
1072 | if (unlikely(ret)) | ||
1073 | ret = onenand_recover_lsb(mtd, from, ret); | ||
1074 | onenand_update_bufferram(mtd, from, !ret); | ||
1075 | if (ret == -EBADMSG) | ||
1076 | ret = 0; | ||
1077 | } | ||
1078 | |||
1079 | this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); | ||
1080 | if (oobbuf) { | ||
1081 | thisooblen = oobsize - oobcolumn; | ||
1082 | thisooblen = min_t(int, thisooblen, ooblen - oobread); | ||
1083 | |||
1084 | if (ops->mode == MTD_OOB_AUTO) | ||
1085 | onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); | ||
1086 | else | ||
1087 | this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); | ||
1088 | oobread += thisooblen; | ||
1089 | oobbuf += thisooblen; | ||
1090 | oobcolumn = 0; | ||
1091 | } | ||
1092 | |||
1093 | read += thislen; | ||
1094 | if (read == len) | ||
1095 | break; | ||
1096 | |||
1097 | from += thislen; | ||
1098 | buf += thislen; | ||
1099 | } | ||
1100 | |||
1101 | /* | ||
1102 | * Return success, if no ECC failures, else -EBADMSG | ||
1103 | * fs driver will take care of that, because | ||
1104 | * retlen == desired len and result == -EBADMSG | ||
1105 | */ | ||
1106 | ops->retlen = read; | ||
1107 | ops->oobretlen = oobread; | ||
1108 | |||
1109 | if (ret) | ||
1110 | return ret; | ||
1111 | |||
1112 | if (mtd->ecc_stats.failed - stats.failed) | ||
1113 | return -EBADMSG; | ||
1114 | |||
1115 | return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; | ||
1116 | } | ||
1117 | |||
1118 | /** | ||
819 | * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band | 1119 | * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band |
820 | * @param mtd MTD device structure | 1120 | * @param mtd MTD device structure |
821 | * @param from offset to read from | 1121 | * @param from offset to read from |
@@ -962,7 +1262,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, | |||
962 | size_t len = ops->ooblen; | 1262 | size_t len = ops->ooblen; |
963 | mtd_oob_mode_t mode = ops->mode; | 1263 | mtd_oob_mode_t mode = ops->mode; |
964 | u_char *buf = ops->oobbuf; | 1264 | u_char *buf = ops->oobbuf; |
965 | int ret = 0; | 1265 | int ret = 0, readcmd; |
966 | 1266 | ||
967 | from += ops->ooboffs; | 1267 | from += ops->ooboffs; |
968 | 1268 | ||
@@ -993,17 +1293,22 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, | |||
993 | 1293 | ||
994 | stats = mtd->ecc_stats; | 1294 | stats = mtd->ecc_stats; |
995 | 1295 | ||
1296 | readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; | ||
1297 | |||
996 | while (read < len) { | 1298 | while (read < len) { |
997 | cond_resched(); | 1299 | cond_resched(); |
998 | 1300 | ||
999 | thislen = oobsize - column; | 1301 | thislen = oobsize - column; |
1000 | thislen = min_t(int, thislen, len); | 1302 | thislen = min_t(int, thislen, len); |
1001 | 1303 | ||
1002 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | 1304 | this->command(mtd, readcmd, from, mtd->oobsize); |
1003 | 1305 | ||
1004 | onenand_update_bufferram(mtd, from, 0); | 1306 | onenand_update_bufferram(mtd, from, 0); |
1005 | 1307 | ||
1006 | ret = this->wait(mtd, FL_READING); | 1308 | ret = this->wait(mtd, FL_READING); |
1309 | if (unlikely(ret)) | ||
1310 | ret = onenand_recover_lsb(mtd, from, ret); | ||
1311 | |||
1007 | if (ret && ret != -EBADMSG) { | 1312 | if (ret && ret != -EBADMSG) { |
1008 | printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); | 1313 | printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); |
1009 | break; | 1314 | break; |
@@ -1053,6 +1358,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, | |||
1053 | static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | 1358 | static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, |
1054 | size_t *retlen, u_char *buf) | 1359 | size_t *retlen, u_char *buf) |
1055 | { | 1360 | { |
1361 | struct onenand_chip *this = mtd->priv; | ||
1056 | struct mtd_oob_ops ops = { | 1362 | struct mtd_oob_ops ops = { |
1057 | .len = len, | 1363 | .len = len, |
1058 | .ooblen = 0, | 1364 | .ooblen = 0, |
@@ -1062,7 +1368,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
1062 | int ret; | 1368 | int ret; |
1063 | 1369 | ||
1064 | onenand_get_device(mtd, FL_READING); | 1370 | onenand_get_device(mtd, FL_READING); |
1065 | ret = onenand_read_ops_nolock(mtd, from, &ops); | 1371 | ret = ONENAND_IS_MLC(this) ? |
1372 | onenand_mlc_read_ops_nolock(mtd, from, &ops) : | ||
1373 | onenand_read_ops_nolock(mtd, from, &ops); | ||
1066 | onenand_release_device(mtd); | 1374 | onenand_release_device(mtd); |
1067 | 1375 | ||
1068 | *retlen = ops.retlen; | 1376 | *retlen = ops.retlen; |
@@ -1080,6 +1388,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
1080 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | 1388 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, |
1081 | struct mtd_oob_ops *ops) | 1389 | struct mtd_oob_ops *ops) |
1082 | { | 1390 | { |
1391 | struct onenand_chip *this = mtd->priv; | ||
1083 | int ret; | 1392 | int ret; |
1084 | 1393 | ||
1085 | switch (ops->mode) { | 1394 | switch (ops->mode) { |
@@ -1094,7 +1403,9 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | |||
1094 | 1403 | ||
1095 | onenand_get_device(mtd, FL_READING); | 1404 | onenand_get_device(mtd, FL_READING); |
1096 | if (ops->datbuf) | 1405 | if (ops->datbuf) |
1097 | ret = onenand_read_ops_nolock(mtd, from, ops); | 1406 | ret = ONENAND_IS_MLC(this) ? |
1407 | onenand_mlc_read_ops_nolock(mtd, from, ops) : | ||
1408 | onenand_read_ops_nolock(mtd, from, ops); | ||
1098 | else | 1409 | else |
1099 | ret = onenand_read_oob_nolock(mtd, from, ops); | 1410 | ret = onenand_read_oob_nolock(mtd, from, ops); |
1100 | onenand_release_device(mtd); | 1411 | onenand_release_device(mtd); |
@@ -1128,11 +1439,11 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) | |||
1128 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | 1439 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); |
1129 | 1440 | ||
1130 | if (interrupt & ONENAND_INT_READ) { | 1441 | if (interrupt & ONENAND_INT_READ) { |
1131 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 1442 | int ecc = onenand_read_ecc(this); |
1132 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 1443 | if (ecc & ONENAND_ECC_2BIT_ALL) { |
1133 | printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" | 1444 | printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" |
1134 | ", controller error 0x%04x\n", ecc, ctrl); | 1445 | ", controller error 0x%04x\n", ecc, ctrl); |
1135 | return ONENAND_BBT_READ_ERROR; | 1446 | return ONENAND_BBT_READ_ECC_ERROR; |
1136 | } | 1447 | } |
1137 | } else { | 1448 | } else { |
1138 | printk(KERN_ERR "onenand_bbt_wait: read timeout!" | 1449 | printk(KERN_ERR "onenand_bbt_wait: read timeout!" |
@@ -1163,7 +1474,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | |||
1163 | { | 1474 | { |
1164 | struct onenand_chip *this = mtd->priv; | 1475 | struct onenand_chip *this = mtd->priv; |
1165 | int read = 0, thislen, column; | 1476 | int read = 0, thislen, column; |
1166 | int ret = 0; | 1477 | int ret = 0, readcmd; |
1167 | size_t len = ops->ooblen; | 1478 | size_t len = ops->ooblen; |
1168 | u_char *buf = ops->oobbuf; | 1479 | u_char *buf = ops->oobbuf; |
1169 | 1480 | ||
@@ -1183,17 +1494,22 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | |||
1183 | 1494 | ||
1184 | column = from & (mtd->oobsize - 1); | 1495 | column = from & (mtd->oobsize - 1); |
1185 | 1496 | ||
1497 | readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; | ||
1498 | |||
1186 | while (read < len) { | 1499 | while (read < len) { |
1187 | cond_resched(); | 1500 | cond_resched(); |
1188 | 1501 | ||
1189 | thislen = mtd->oobsize - column; | 1502 | thislen = mtd->oobsize - column; |
1190 | thislen = min_t(int, thislen, len); | 1503 | thislen = min_t(int, thislen, len); |
1191 | 1504 | ||
1192 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | 1505 | this->command(mtd, readcmd, from, mtd->oobsize); |
1193 | 1506 | ||
1194 | onenand_update_bufferram(mtd, from, 0); | 1507 | onenand_update_bufferram(mtd, from, 0); |
1195 | 1508 | ||
1196 | ret = onenand_bbt_wait(mtd, FL_READING); | 1509 | ret = onenand_bbt_wait(mtd, FL_READING); |
1510 | if (unlikely(ret)) | ||
1511 | ret = onenand_recover_lsb(mtd, from, ret); | ||
1512 | |||
1197 | if (ret) | 1513 | if (ret) |
1198 | break; | 1514 | break; |
1199 | 1515 | ||
@@ -1230,9 +1546,11 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to | |||
1230 | { | 1546 | { |
1231 | struct onenand_chip *this = mtd->priv; | 1547 | struct onenand_chip *this = mtd->priv; |
1232 | u_char *oob_buf = this->oob_buf; | 1548 | u_char *oob_buf = this->oob_buf; |
1233 | int status, i; | 1549 | int status, i, readcmd; |
1550 | |||
1551 | readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; | ||
1234 | 1552 | ||
1235 | this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); | 1553 | this->command(mtd, readcmd, to, mtd->oobsize); |
1236 | onenand_update_bufferram(mtd, to, 0); | 1554 | onenand_update_bufferram(mtd, to, 0); |
1237 | status = this->wait(mtd, FL_READING); | 1555 | status = this->wait(mtd, FL_READING); |
1238 | if (status) | 1556 | if (status) |
@@ -1633,7 +1951,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, | |||
1633 | { | 1951 | { |
1634 | struct onenand_chip *this = mtd->priv; | 1952 | struct onenand_chip *this = mtd->priv; |
1635 | int column, ret = 0, oobsize; | 1953 | int column, ret = 0, oobsize; |
1636 | int written = 0; | 1954 | int written = 0, oobcmd; |
1637 | u_char *oobbuf; | 1955 | u_char *oobbuf; |
1638 | size_t len = ops->ooblen; | 1956 | size_t len = ops->ooblen; |
1639 | const u_char *buf = ops->oobbuf; | 1957 | const u_char *buf = ops->oobbuf; |
@@ -1675,6 +1993,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, | |||
1675 | 1993 | ||
1676 | oobbuf = this->oob_buf; | 1994 | oobbuf = this->oob_buf; |
1677 | 1995 | ||
1996 | oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; | ||
1997 | |||
1678 | /* Loop until all data write */ | 1998 | /* Loop until all data write */ |
1679 | while (written < len) { | 1999 | while (written < len) { |
1680 | int thislen = min_t(int, oobsize, len - written); | 2000 | int thislen = min_t(int, oobsize, len - written); |
@@ -1692,7 +2012,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, | |||
1692 | memcpy(oobbuf + column, buf, thislen); | 2012 | memcpy(oobbuf + column, buf, thislen); |
1693 | this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); | 2013 | this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); |
1694 | 2014 | ||
1695 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | 2015 | if (ONENAND_IS_MLC(this)) { |
2016 | /* Set main area of DataRAM to 0xff*/ | ||
2017 | memset(this->page_buf, 0xff, mtd->writesize); | ||
2018 | this->write_bufferram(mtd, ONENAND_DATARAM, | ||
2019 | this->page_buf, 0, mtd->writesize); | ||
2020 | } | ||
2021 | |||
2022 | this->command(mtd, oobcmd, to, mtd->oobsize); | ||
1696 | 2023 | ||
1697 | onenand_update_bufferram(mtd, to, 0); | 2024 | onenand_update_bufferram(mtd, to, 0); |
1698 | if (ONENAND_IS_2PLANE(this)) { | 2025 | if (ONENAND_IS_2PLANE(this)) { |
@@ -1815,29 +2142,48 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1815 | { | 2142 | { |
1816 | struct onenand_chip *this = mtd->priv; | 2143 | struct onenand_chip *this = mtd->priv; |
1817 | unsigned int block_size; | 2144 | unsigned int block_size; |
1818 | loff_t addr; | 2145 | loff_t addr = instr->addr; |
1819 | int len; | 2146 | loff_t len = instr->len; |
1820 | int ret = 0; | 2147 | int ret = 0, i; |
2148 | struct mtd_erase_region_info *region = NULL; | ||
2149 | loff_t region_end = 0; | ||
1821 | 2150 | ||
1822 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); | 2151 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); |
1823 | 2152 | ||
1824 | block_size = (1 << this->erase_shift); | 2153 | /* Do not allow erase past end of device */ |
1825 | 2154 | if (unlikely((len + addr) > mtd->size)) { | |
1826 | /* Start address must align on block boundary */ | 2155 | printk(KERN_ERR "onenand_erase: Erase past end of device\n"); |
1827 | if (unlikely(instr->addr & (block_size - 1))) { | ||
1828 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); | ||
1829 | return -EINVAL; | 2156 | return -EINVAL; |
1830 | } | 2157 | } |
1831 | 2158 | ||
1832 | /* Length must align on block boundary */ | 2159 | if (FLEXONENAND(this)) { |
1833 | if (unlikely(instr->len & (block_size - 1))) { | 2160 | /* Find the eraseregion of this address */ |
1834 | printk(KERN_ERR "onenand_erase: Length not block aligned\n"); | 2161 | i = flexonenand_region(mtd, addr); |
1835 | return -EINVAL; | 2162 | region = &mtd->eraseregions[i]; |
2163 | |||
2164 | block_size = region->erasesize; | ||
2165 | region_end = region->offset + region->erasesize * region->numblocks; | ||
2166 | |||
2167 | /* Start address within region must align on block boundary. | ||
2168 | * Erase region's start offset is always block start address. | ||
2169 | */ | ||
2170 | if (unlikely((addr - region->offset) & (block_size - 1))) { | ||
2171 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); | ||
2172 | return -EINVAL; | ||
2173 | } | ||
2174 | } else { | ||
2175 | block_size = 1 << this->erase_shift; | ||
2176 | |||
2177 | /* Start address must align on block boundary */ | ||
2178 | if (unlikely(addr & (block_size - 1))) { | ||
2179 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); | ||
2180 | return -EINVAL; | ||
2181 | } | ||
1836 | } | 2182 | } |
1837 | 2183 | ||
1838 | /* Do not allow erase past end of device */ | 2184 | /* Length must align on block boundary */ |
1839 | if (unlikely((instr->len + instr->addr) > mtd->size)) { | 2185 | if (unlikely(len & (block_size - 1))) { |
1840 | printk(KERN_ERR "onenand_erase: Erase past end of device\n"); | 2186 | printk(KERN_ERR "onenand_erase: Length not block aligned\n"); |
1841 | return -EINVAL; | 2187 | return -EINVAL; |
1842 | } | 2188 | } |
1843 | 2189 | ||
@@ -1847,9 +2193,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1847 | onenand_get_device(mtd, FL_ERASING); | 2193 | onenand_get_device(mtd, FL_ERASING); |
1848 | 2194 | ||
1849 | /* Loop throught the pages */ | 2195 | /* Loop throught the pages */ |
1850 | len = instr->len; | ||
1851 | addr = instr->addr; | ||
1852 | |||
1853 | instr->state = MTD_ERASING; | 2196 | instr->state = MTD_ERASING; |
1854 | 2197 | ||
1855 | while (len) { | 2198 | while (len) { |
@@ -1869,7 +2212,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1869 | ret = this->wait(mtd, FL_ERASING); | 2212 | ret = this->wait(mtd, FL_ERASING); |
1870 | /* Check, if it is write protected */ | 2213 | /* Check, if it is write protected */ |
1871 | if (ret) { | 2214 | if (ret) { |
1872 | printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); | 2215 | printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", |
2216 | onenand_block(this, addr)); | ||
1873 | instr->state = MTD_ERASE_FAILED; | 2217 | instr->state = MTD_ERASE_FAILED; |
1874 | instr->fail_addr = addr; | 2218 | instr->fail_addr = addr; |
1875 | goto erase_exit; | 2219 | goto erase_exit; |
@@ -1877,6 +2221,22 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1877 | 2221 | ||
1878 | len -= block_size; | 2222 | len -= block_size; |
1879 | addr += block_size; | 2223 | addr += block_size; |
2224 | |||
2225 | if (addr == region_end) { | ||
2226 | if (!len) | ||
2227 | break; | ||
2228 | region++; | ||
2229 | |||
2230 | block_size = region->erasesize; | ||
2231 | region_end = region->offset + region->erasesize * region->numblocks; | ||
2232 | |||
2233 | if (len & (block_size - 1)) { | ||
2234 | /* FIXME: This should be handled at MTD partitioning level. */ | ||
2235 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); | ||
2236 | goto erase_exit; | ||
2237 | } | ||
2238 | } | ||
2239 | |||
1880 | } | 2240 | } |
1881 | 2241 | ||
1882 | instr->state = MTD_ERASE_DONE; | 2242 | instr->state = MTD_ERASE_DONE; |
@@ -1955,13 +2315,17 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1955 | int block; | 2315 | int block; |
1956 | 2316 | ||
1957 | /* Get block number */ | 2317 | /* Get block number */ |
1958 | block = ((int) ofs) >> bbm->bbt_erase_shift; | 2318 | block = onenand_block(this, ofs); |
1959 | if (bbm->bbt) | 2319 | if (bbm->bbt) |
1960 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); | 2320 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); |
1961 | 2321 | ||
1962 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 2322 | /* We write two bytes, so we dont have to mess with 16 bit access */ |
1963 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | 2323 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); |
1964 | return onenand_write_oob_nolock(mtd, ofs, &ops); | 2324 | /* FIXME : What to do when marking SLC block in partition |
2325 | * with MLC erasesize? For now, it is not advisable to | ||
2326 | * create partitions containing both SLC and MLC regions. | ||
2327 | */ | ||
2328 | return onenand_write_oob_nolock(mtd, ofs, &ops); | ||
1965 | } | 2329 | } |
1966 | 2330 | ||
1967 | /** | 2331 | /** |
@@ -2005,8 +2369,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int | |||
2005 | int start, end, block, value, status; | 2369 | int start, end, block, value, status; |
2006 | int wp_status_mask; | 2370 | int wp_status_mask; |
2007 | 2371 | ||
2008 | start = ofs >> this->erase_shift; | 2372 | start = onenand_block(this, ofs); |
2009 | end = len >> this->erase_shift; | 2373 | end = onenand_block(this, ofs + len) - 1; |
2010 | 2374 | ||
2011 | if (cmd == ONENAND_CMD_LOCK) | 2375 | if (cmd == ONENAND_CMD_LOCK) |
2012 | wp_status_mask = ONENAND_WP_LS; | 2376 | wp_status_mask = ONENAND_WP_LS; |
@@ -2018,7 +2382,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int | |||
2018 | /* Set start block address */ | 2382 | /* Set start block address */ |
2019 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | 2383 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); |
2020 | /* Set end block address */ | 2384 | /* Set end block address */ |
2021 | this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); | 2385 | this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS); |
2022 | /* Write lock command */ | 2386 | /* Write lock command */ |
2023 | this->command(mtd, cmd, 0, 0); | 2387 | this->command(mtd, cmd, 0, 0); |
2024 | 2388 | ||
@@ -2039,7 +2403,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int | |||
2039 | } | 2403 | } |
2040 | 2404 | ||
2041 | /* Block lock scheme */ | 2405 | /* Block lock scheme */ |
2042 | for (block = start; block < start + end; block++) { | 2406 | for (block = start; block < end + 1; block++) { |
2043 | /* Set block address */ | 2407 | /* Set block address */ |
2044 | value = onenand_block_address(this, block); | 2408 | value = onenand_block_address(this, block); |
2045 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); | 2409 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); |
@@ -2147,7 +2511,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) | |||
2147 | { | 2511 | { |
2148 | struct onenand_chip *this = mtd->priv; | 2512 | struct onenand_chip *this = mtd->priv; |
2149 | loff_t ofs = 0; | 2513 | loff_t ofs = 0; |
2150 | size_t len = this->chipsize; | 2514 | loff_t len = mtd->size; |
2151 | 2515 | ||
2152 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { | 2516 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { |
2153 | /* Set start block address */ | 2517 | /* Set start block address */ |
@@ -2168,7 +2532,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) | |||
2168 | return; | 2532 | return; |
2169 | 2533 | ||
2170 | /* Workaround for all block unlock in DDP */ | 2534 | /* Workaround for all block unlock in DDP */ |
2171 | if (ONENAND_IS_DDP(this)) { | 2535 | if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) { |
2172 | /* All blocks on another chip */ | 2536 | /* All blocks on another chip */ |
2173 | ofs = this->chipsize >> 1; | 2537 | ofs = this->chipsize >> 1; |
2174 | len = this->chipsize >> 1; | 2538 | len = this->chipsize >> 1; |
@@ -2210,7 +2574,9 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
2210 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); | 2574 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
2211 | this->wait(mtd, FL_OTPING); | 2575 | this->wait(mtd, FL_OTPING); |
2212 | 2576 | ||
2213 | ret = onenand_read_ops_nolock(mtd, from, &ops); | 2577 | ret = ONENAND_IS_MLC(this) ? |
2578 | onenand_mlc_read_ops_nolock(mtd, from, &ops) : | ||
2579 | onenand_read_ops_nolock(mtd, from, &ops); | ||
2214 | 2580 | ||
2215 | /* Exit OTP access mode */ | 2581 | /* Exit OTP access mode */ |
2216 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 2582 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
@@ -2277,21 +2643,32 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, | |||
2277 | size_t *retlen, u_char *buf) | 2643 | size_t *retlen, u_char *buf) |
2278 | { | 2644 | { |
2279 | struct onenand_chip *this = mtd->priv; | 2645 | struct onenand_chip *this = mtd->priv; |
2280 | struct mtd_oob_ops ops = { | 2646 | struct mtd_oob_ops ops; |
2281 | .mode = MTD_OOB_PLACE, | ||
2282 | .ooblen = len, | ||
2283 | .oobbuf = buf, | ||
2284 | .ooboffs = 0, | ||
2285 | }; | ||
2286 | int ret; | 2647 | int ret; |
2287 | 2648 | ||
2288 | /* Enter OTP access mode */ | 2649 | /* Enter OTP access mode */ |
2289 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); | 2650 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
2290 | this->wait(mtd, FL_OTPING); | 2651 | this->wait(mtd, FL_OTPING); |
2291 | 2652 | ||
2292 | ret = onenand_write_oob_nolock(mtd, from, &ops); | 2653 | if (FLEXONENAND(this)) { |
2293 | 2654 | /* | |
2294 | *retlen = ops.oobretlen; | 2655 | * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of |
2656 | * main area of page 49. | ||
2657 | */ | ||
2658 | ops.len = mtd->writesize; | ||
2659 | ops.ooblen = 0; | ||
2660 | ops.datbuf = buf; | ||
2661 | ops.oobbuf = NULL; | ||
2662 | ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); | ||
2663 | *retlen = ops.retlen; | ||
2664 | } else { | ||
2665 | ops.mode = MTD_OOB_PLACE; | ||
2666 | ops.ooblen = len; | ||
2667 | ops.oobbuf = buf; | ||
2668 | ops.ooboffs = 0; | ||
2669 | ret = onenand_write_oob_nolock(mtd, from, &ops); | ||
2670 | *retlen = ops.oobretlen; | ||
2671 | } | ||
2295 | 2672 | ||
2296 | /* Exit OTP access mode */ | 2673 | /* Exit OTP access mode */ |
2297 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 2674 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
@@ -2475,27 +2852,34 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | |||
2475 | size_t len) | 2852 | size_t len) |
2476 | { | 2853 | { |
2477 | struct onenand_chip *this = mtd->priv; | 2854 | struct onenand_chip *this = mtd->priv; |
2478 | u_char *oob_buf = this->oob_buf; | 2855 | u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf; |
2479 | size_t retlen; | 2856 | size_t retlen; |
2480 | int ret; | 2857 | int ret; |
2481 | 2858 | ||
2482 | memset(oob_buf, 0xff, mtd->oobsize); | 2859 | memset(buf, 0xff, FLEXONENAND(this) ? this->writesize |
2860 | : mtd->oobsize); | ||
2483 | /* | 2861 | /* |
2484 | * Note: OTP lock operation | 2862 | * Note: OTP lock operation |
2485 | * OTP block : 0xXXFC | 2863 | * OTP block : 0xXXFC |
2486 | * 1st block : 0xXXF3 (If chip support) | 2864 | * 1st block : 0xXXF3 (If chip support) |
2487 | * Both : 0xXXF0 (If chip support) | 2865 | * Both : 0xXXF0 (If chip support) |
2488 | */ | 2866 | */ |
2489 | oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; | 2867 | if (FLEXONENAND(this)) |
2868 | buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC; | ||
2869 | else | ||
2870 | buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; | ||
2490 | 2871 | ||
2491 | /* | 2872 | /* |
2492 | * Write lock mark to 8th word of sector0 of page0 of the spare0. | 2873 | * Write lock mark to 8th word of sector0 of page0 of the spare0. |
2493 | * We write 16 bytes spare area instead of 2 bytes. | 2874 | * We write 16 bytes spare area instead of 2 bytes. |
2875 | * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of | ||
2876 | * main area of page 49. | ||
2494 | */ | 2877 | */ |
2878 | |||
2495 | from = 0; | 2879 | from = 0; |
2496 | len = 16; | 2880 | len = FLEXONENAND(this) ? mtd->writesize : 16; |
2497 | 2881 | ||
2498 | ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER); | 2882 | ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER); |
2499 | 2883 | ||
2500 | return ret ? : retlen; | 2884 | return ret ? : retlen; |
2501 | } | 2885 | } |
@@ -2542,6 +2926,14 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
2542 | break; | 2926 | break; |
2543 | } | 2927 | } |
2544 | 2928 | ||
2929 | if (ONENAND_IS_MLC(this)) | ||
2930 | this->options &= ~ONENAND_HAS_2PLANE; | ||
2931 | |||
2932 | if (FLEXONENAND(this)) { | ||
2933 | this->options &= ~ONENAND_HAS_CONT_LOCK; | ||
2934 | this->options |= ONENAND_HAS_UNLOCK_ALL; | ||
2935 | } | ||
2936 | |||
2545 | if (this->options & ONENAND_HAS_CONT_LOCK) | 2937 | if (this->options & ONENAND_HAS_CONT_LOCK) |
2546 | printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); | 2938 | printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); |
2547 | if (this->options & ONENAND_HAS_UNLOCK_ALL) | 2939 | if (this->options & ONENAND_HAS_UNLOCK_ALL) |
@@ -2559,14 +2951,16 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
2559 | */ | 2951 | */ |
2560 | static void onenand_print_device_info(int device, int version) | 2952 | static void onenand_print_device_info(int device, int version) |
2561 | { | 2953 | { |
2562 | int vcc, demuxed, ddp, density; | 2954 | int vcc, demuxed, ddp, density, flexonenand; |
2563 | 2955 | ||
2564 | vcc = device & ONENAND_DEVICE_VCC_MASK; | 2956 | vcc = device & ONENAND_DEVICE_VCC_MASK; |
2565 | demuxed = device & ONENAND_DEVICE_IS_DEMUX; | 2957 | demuxed = device & ONENAND_DEVICE_IS_DEMUX; |
2566 | ddp = device & ONENAND_DEVICE_IS_DDP; | 2958 | ddp = device & ONENAND_DEVICE_IS_DDP; |
2567 | density = onenand_get_density(device); | 2959 | density = onenand_get_density(device); |
2568 | printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", | 2960 | flexonenand = device & DEVICE_IS_FLEXONENAND; |
2569 | demuxed ? "" : "Muxed ", | 2961 | printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", |
2962 | demuxed ? "" : "Muxed ", | ||
2963 | flexonenand ? "Flex-" : "", | ||
2570 | ddp ? "(DDP)" : "", | 2964 | ddp ? "(DDP)" : "", |
2571 | (16 << density), | 2965 | (16 << density), |
2572 | vcc ? "2.65/3.3" : "1.8", | 2966 | vcc ? "2.65/3.3" : "1.8", |
@@ -2606,6 +3000,280 @@ static int onenand_check_maf(int manuf) | |||
2606 | } | 3000 | } |
2607 | 3001 | ||
2608 | /** | 3002 | /** |
3003 | * flexonenand_get_boundary - Reads the SLC boundary | ||
3004 | * @param onenand_info - onenand info structure | ||
3005 | **/ | ||
3006 | static int flexonenand_get_boundary(struct mtd_info *mtd) | ||
3007 | { | ||
3008 | struct onenand_chip *this = mtd->priv; | ||
3009 | unsigned die, bdry; | ||
3010 | int ret, syscfg, locked; | ||
3011 | |||
3012 | /* Disable ECC */ | ||
3013 | syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); | ||
3014 | this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); | ||
3015 | |||
3016 | for (die = 0; die < this->dies; die++) { | ||
3017 | this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); | ||
3018 | this->wait(mtd, FL_SYNCING); | ||
3019 | |||
3020 | this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); | ||
3021 | ret = this->wait(mtd, FL_READING); | ||
3022 | |||
3023 | bdry = this->read_word(this->base + ONENAND_DATARAM); | ||
3024 | if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) | ||
3025 | locked = 0; | ||
3026 | else | ||
3027 | locked = 1; | ||
3028 | this->boundary[die] = bdry & FLEXONENAND_PI_MASK; | ||
3029 | |||
3030 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | ||
3031 | ret = this->wait(mtd, FL_RESETING); | ||
3032 | |||
3033 | printk(KERN_INFO "Die %d boundary: %d%s\n", die, | ||
3034 | this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); | ||
3035 | } | ||
3036 | |||
3037 | /* Enable ECC */ | ||
3038 | this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); | ||
3039 | return 0; | ||
3040 | } | ||
3041 | |||
3042 | /** | ||
3043 | * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info | ||
3044 | * boundary[], diesize[], mtd->size, mtd->erasesize | ||
3045 | * @param mtd - MTD device structure | ||
3046 | */ | ||
3047 | static void flexonenand_get_size(struct mtd_info *mtd) | ||
3048 | { | ||
3049 | struct onenand_chip *this = mtd->priv; | ||
3050 | int die, i, eraseshift, density; | ||
3051 | int blksperdie, maxbdry; | ||
3052 | loff_t ofs; | ||
3053 | |||
3054 | density = onenand_get_density(this->device_id); | ||
3055 | blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift); | ||
3056 | blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; | ||
3057 | maxbdry = blksperdie - 1; | ||
3058 | eraseshift = this->erase_shift - 1; | ||
3059 | |||
3060 | mtd->numeraseregions = this->dies << 1; | ||
3061 | |||
3062 | /* This fills up the device boundary */ | ||
3063 | flexonenand_get_boundary(mtd); | ||
3064 | die = ofs = 0; | ||
3065 | i = -1; | ||
3066 | for (; die < this->dies; die++) { | ||
3067 | if (!die || this->boundary[die-1] != maxbdry) { | ||
3068 | i++; | ||
3069 | mtd->eraseregions[i].offset = ofs; | ||
3070 | mtd->eraseregions[i].erasesize = 1 << eraseshift; | ||
3071 | mtd->eraseregions[i].numblocks = | ||
3072 | this->boundary[die] + 1; | ||
3073 | ofs += mtd->eraseregions[i].numblocks << eraseshift; | ||
3074 | eraseshift++; | ||
3075 | } else { | ||
3076 | mtd->numeraseregions -= 1; | ||
3077 | mtd->eraseregions[i].numblocks += | ||
3078 | this->boundary[die] + 1; | ||
3079 | ofs += (this->boundary[die] + 1) << (eraseshift - 1); | ||
3080 | } | ||
3081 | if (this->boundary[die] != maxbdry) { | ||
3082 | i++; | ||
3083 | mtd->eraseregions[i].offset = ofs; | ||
3084 | mtd->eraseregions[i].erasesize = 1 << eraseshift; | ||
3085 | mtd->eraseregions[i].numblocks = maxbdry ^ | ||
3086 | this->boundary[die]; | ||
3087 | ofs += mtd->eraseregions[i].numblocks << eraseshift; | ||
3088 | eraseshift--; | ||
3089 | } else | ||
3090 | mtd->numeraseregions -= 1; | ||
3091 | } | ||
3092 | |||
3093 | /* Expose MLC erase size except when all blocks are SLC */ | ||
3094 | mtd->erasesize = 1 << this->erase_shift; | ||
3095 | if (mtd->numeraseregions == 1) | ||
3096 | mtd->erasesize >>= 1; | ||
3097 | |||
3098 | printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); | ||
3099 | for (i = 0; i < mtd->numeraseregions; i++) | ||
3100 | printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x," | ||
3101 | " numblocks: %04u]\n", | ||
3102 | (unsigned int) mtd->eraseregions[i].offset, | ||
3103 | mtd->eraseregions[i].erasesize, | ||
3104 | mtd->eraseregions[i].numblocks); | ||
3105 | |||
3106 | for (die = 0, mtd->size = 0; die < this->dies; die++) { | ||
3107 | this->diesize[die] = (loff_t)blksperdie << this->erase_shift; | ||
3108 | this->diesize[die] -= (loff_t)(this->boundary[die] + 1) | ||
3109 | << (this->erase_shift - 1); | ||
3110 | mtd->size += this->diesize[die]; | ||
3111 | } | ||
3112 | } | ||
3113 | |||
3114 | /** | ||
3115 | * flexonenand_check_blocks_erased - Check if blocks are erased | ||
3116 | * @param mtd_info - mtd info structure | ||
3117 | * @param start - first erase block to check | ||
3118 | * @param end - last erase block to check | ||
3119 | * | ||
3120 | * Converting an unerased block from MLC to SLC | ||
3121 | * causes byte values to change. Since both data and its ECC | ||
3122 | * have changed, reads on the block give uncorrectable error. | ||
3123 | * This might lead to the block being detected as bad. | ||
3124 | * | ||
3125 | * Avoid this by ensuring that the block to be converted is | ||
3126 | * erased. | ||
3127 | */ | ||
3128 | static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end) | ||
3129 | { | ||
3130 | struct onenand_chip *this = mtd->priv; | ||
3131 | int i, ret; | ||
3132 | int block; | ||
3133 | struct mtd_oob_ops ops = { | ||
3134 | .mode = MTD_OOB_PLACE, | ||
3135 | .ooboffs = 0, | ||
3136 | .ooblen = mtd->oobsize, | ||
3137 | .datbuf = NULL, | ||
3138 | .oobbuf = this->oob_buf, | ||
3139 | }; | ||
3140 | loff_t addr; | ||
3141 | |||
3142 | printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end); | ||
3143 | |||
3144 | for (block = start; block <= end; block++) { | ||
3145 | addr = flexonenand_addr(this, block); | ||
3146 | if (onenand_block_isbad_nolock(mtd, addr, 0)) | ||
3147 | continue; | ||
3148 | |||
3149 | /* | ||
3150 | * Since main area write results in ECC write to spare, | ||
3151 | * it is sufficient to check only ECC bytes for change. | ||
3152 | */ | ||
3153 | ret = onenand_read_oob_nolock(mtd, addr, &ops); | ||
3154 | if (ret) | ||
3155 | return ret; | ||
3156 | |||
3157 | for (i = 0; i < mtd->oobsize; i++) | ||
3158 | if (this->oob_buf[i] != 0xff) | ||
3159 | break; | ||
3160 | |||
3161 | if (i != mtd->oobsize) { | ||
3162 | printk(KERN_WARNING "Block %d not erased.\n", block); | ||
3163 | return 1; | ||
3164 | } | ||
3165 | } | ||
3166 | |||
3167 | return 0; | ||
3168 | } | ||
3169 | |||
3170 | /** | ||
3171 | * flexonenand_set_boundary - Writes the SLC boundary | ||
3172 | * @param mtd - mtd info structure | ||
3173 | */ | ||
3174 | int flexonenand_set_boundary(struct mtd_info *mtd, int die, | ||
3175 | int boundary, int lock) | ||
3176 | { | ||
3177 | struct onenand_chip *this = mtd->priv; | ||
3178 | int ret, density, blksperdie, old, new, thisboundary; | ||
3179 | loff_t addr; | ||
3180 | |||
3181 | /* Change only once for SDP Flex-OneNAND */ | ||
3182 | if (die && (!ONENAND_IS_DDP(this))) | ||
3183 | return 0; | ||
3184 | |||
3185 | /* boundary value of -1 indicates no required change */ | ||
3186 | if (boundary < 0 || boundary == this->boundary[die]) | ||
3187 | return 0; | ||
3188 | |||
3189 | density = onenand_get_density(this->device_id); | ||
3190 | blksperdie = ((16 << density) << 20) >> this->erase_shift; | ||
3191 | blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; | ||
3192 | |||
3193 | if (boundary >= blksperdie) { | ||
3194 | printk(KERN_ERR "flexonenand_set_boundary: Invalid boundary value. " | ||
3195 | "Boundary not changed.\n"); | ||
3196 | return -EINVAL; | ||
3197 | } | ||
3198 | |||
3199 | /* Check if converting blocks are erased */ | ||
3200 | old = this->boundary[die] + (die * this->density_mask); | ||
3201 | new = boundary + (die * this->density_mask); | ||
3202 | ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new)); | ||
3203 | if (ret) { | ||
3204 | printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n"); | ||
3205 | return ret; | ||
3206 | } | ||
3207 | |||
3208 | this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); | ||
3209 | this->wait(mtd, FL_SYNCING); | ||
3210 | |||
3211 | /* Check is boundary is locked */ | ||
3212 | this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); | ||
3213 | ret = this->wait(mtd, FL_READING); | ||
3214 | |||
3215 | thisboundary = this->read_word(this->base + ONENAND_DATARAM); | ||
3216 | if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { | ||
3217 | printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n"); | ||
3218 | ret = 1; | ||
3219 | goto out; | ||
3220 | } | ||
3221 | |||
3222 | printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n", | ||
3223 | die, boundary, lock ? "(Locked)" : "(Unlocked)"); | ||
3224 | |||
3225 | addr = die ? this->diesize[0] : 0; | ||
3226 | |||
3227 | boundary &= FLEXONENAND_PI_MASK; | ||
3228 | boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); | ||
3229 | |||
3230 | this->command(mtd, ONENAND_CMD_ERASE, addr, 0); | ||
3231 | ret = this->wait(mtd, FL_ERASING); | ||
3232 | if (ret) { | ||
3233 | printk(KERN_ERR "flexonenand_set_boundary: Failed PI erase for Die %d\n", die); | ||
3234 | goto out; | ||
3235 | } | ||
3236 | |||
3237 | this->write_word(boundary, this->base + ONENAND_DATARAM); | ||
3238 | this->command(mtd, ONENAND_CMD_PROG, addr, 0); | ||
3239 | ret = this->wait(mtd, FL_WRITING); | ||
3240 | if (ret) { | ||
3241 | printk(KERN_ERR "flexonenand_set_boundary: Failed PI write for Die %d\n", die); | ||
3242 | goto out; | ||
3243 | } | ||
3244 | |||
3245 | this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0); | ||
3246 | ret = this->wait(mtd, FL_WRITING); | ||
3247 | out: | ||
3248 | this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); | ||
3249 | this->wait(mtd, FL_RESETING); | ||
3250 | if (!ret) | ||
3251 | /* Recalculate device size on boundary change*/ | ||
3252 | flexonenand_get_size(mtd); | ||
3253 | |||
3254 | return ret; | ||
3255 | } | ||
3256 | |||
3257 | /** | ||
3258 | * flexonenand_setup - capture Flex-OneNAND boundary and lock | ||
3259 | * values passed as kernel parameters | ||
3260 | * @param s kernel parameter string | ||
3261 | */ | ||
3262 | static int flexonenand_setup(char *s) | ||
3263 | { | ||
3264 | int ints[5], i; | ||
3265 | |||
3266 | s = get_options(s, 5, ints); | ||
3267 | |||
3268 | for (i = 0; i < ints[0]; i++) | ||
3269 | flex_bdry[i] = ints[i + 1]; | ||
3270 | |||
3271 | return 1; | ||
3272 | } | ||
3273 | |||
3274 | __setup("onenand.bdry=", flexonenand_setup); | ||
3275 | |||
3276 | /** | ||
2609 | * onenand_probe - [OneNAND Interface] Probe the OneNAND device | 3277 | * onenand_probe - [OneNAND Interface] Probe the OneNAND device |
2610 | * @param mtd MTD device structure | 3278 | * @param mtd MTD device structure |
2611 | * | 3279 | * |
@@ -2647,6 +3315,7 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2647 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); | 3315 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); |
2648 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); | 3316 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); |
2649 | ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); | 3317 | ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); |
3318 | this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); | ||
2650 | 3319 | ||
2651 | /* Check OneNAND device */ | 3320 | /* Check OneNAND device */ |
2652 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) | 3321 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) |
@@ -2658,29 +3327,55 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2658 | this->version_id = ver_id; | 3327 | this->version_id = ver_id; |
2659 | 3328 | ||
2660 | density = onenand_get_density(dev_id); | 3329 | density = onenand_get_density(dev_id); |
3330 | if (FLEXONENAND(this)) { | ||
3331 | this->dies = ONENAND_IS_DDP(this) ? 2 : 1; | ||
3332 | /* Maximum possible erase regions */ | ||
3333 | mtd->numeraseregions = this->dies << 1; | ||
3334 | mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info) | ||
3335 | * (this->dies << 1), GFP_KERNEL); | ||
3336 | if (!mtd->eraseregions) | ||
3337 | return -ENOMEM; | ||
3338 | } | ||
3339 | |||
3340 | /* | ||
3341 | * For Flex-OneNAND, chipsize represents maximum possible device size. | ||
3342 | * mtd->size represents the actual device size. | ||
3343 | */ | ||
2661 | this->chipsize = (16 << density) << 20; | 3344 | this->chipsize = (16 << density) << 20; |
2662 | /* Set density mask. it is used for DDP */ | ||
2663 | if (ONENAND_IS_DDP(this)) | ||
2664 | this->density_mask = (1 << (density + 6)); | ||
2665 | else | ||
2666 | this->density_mask = 0; | ||
2667 | 3345 | ||
2668 | /* OneNAND page size & block size */ | 3346 | /* OneNAND page size & block size */ |
2669 | /* The data buffer size is equal to page size */ | 3347 | /* The data buffer size is equal to page size */ |
2670 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); | 3348 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); |
3349 | /* We use the full BufferRAM */ | ||
3350 | if (ONENAND_IS_MLC(this)) | ||
3351 | mtd->writesize <<= 1; | ||
3352 | |||
2671 | mtd->oobsize = mtd->writesize >> 5; | 3353 | mtd->oobsize = mtd->writesize >> 5; |
2672 | /* Pages per a block are always 64 in OneNAND */ | 3354 | /* Pages per a block are always 64 in OneNAND */ |
2673 | mtd->erasesize = mtd->writesize << 6; | 3355 | mtd->erasesize = mtd->writesize << 6; |
3356 | /* | ||
3357 | * Flex-OneNAND SLC area has 64 pages per block. | ||
3358 | * Flex-OneNAND MLC area has 128 pages per block. | ||
3359 | * Expose MLC erase size to find erase_shift and page_mask. | ||
3360 | */ | ||
3361 | if (FLEXONENAND(this)) | ||
3362 | mtd->erasesize <<= 1; | ||
2674 | 3363 | ||
2675 | this->erase_shift = ffs(mtd->erasesize) - 1; | 3364 | this->erase_shift = ffs(mtd->erasesize) - 1; |
2676 | this->page_shift = ffs(mtd->writesize) - 1; | 3365 | this->page_shift = ffs(mtd->writesize) - 1; |
2677 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; | 3366 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; |
3367 | /* Set density mask. it is used for DDP */ | ||
3368 | if (ONENAND_IS_DDP(this)) | ||
3369 | this->density_mask = this->chipsize >> (this->erase_shift + 1); | ||
2678 | /* It's real page size */ | 3370 | /* It's real page size */ |
2679 | this->writesize = mtd->writesize; | 3371 | this->writesize = mtd->writesize; |
2680 | 3372 | ||
2681 | /* REVIST: Multichip handling */ | 3373 | /* REVIST: Multichip handling */ |
2682 | 3374 | ||
2683 | mtd->size = this->chipsize; | 3375 | if (FLEXONENAND(this)) |
3376 | flexonenand_get_size(mtd); | ||
3377 | else | ||
3378 | mtd->size = this->chipsize; | ||
2684 | 3379 | ||
2685 | /* Check OneNAND features */ | 3380 | /* Check OneNAND features */ |
2686 | onenand_check_features(mtd); | 3381 | onenand_check_features(mtd); |
@@ -2735,7 +3430,7 @@ static void onenand_resume(struct mtd_info *mtd) | |||
2735 | */ | 3430 | */ |
2736 | int onenand_scan(struct mtd_info *mtd, int maxchips) | 3431 | int onenand_scan(struct mtd_info *mtd, int maxchips) |
2737 | { | 3432 | { |
2738 | int i; | 3433 | int i, ret; |
2739 | struct onenand_chip *this = mtd->priv; | 3434 | struct onenand_chip *this = mtd->priv; |
2740 | 3435 | ||
2741 | if (!this->read_word) | 3436 | if (!this->read_word) |
@@ -2797,6 +3492,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2797 | * Allow subpage writes up to oobsize. | 3492 | * Allow subpage writes up to oobsize. |
2798 | */ | 3493 | */ |
2799 | switch (mtd->oobsize) { | 3494 | switch (mtd->oobsize) { |
3495 | case 128: | ||
3496 | this->ecclayout = &onenand_oob_128; | ||
3497 | mtd->subpage_sft = 0; | ||
3498 | break; | ||
2800 | case 64: | 3499 | case 64: |
2801 | this->ecclayout = &onenand_oob_64; | 3500 | this->ecclayout = &onenand_oob_64; |
2802 | mtd->subpage_sft = 2; | 3501 | mtd->subpage_sft = 2; |
@@ -2862,7 +3561,16 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2862 | /* Unlock whole block */ | 3561 | /* Unlock whole block */ |
2863 | onenand_unlock_all(mtd); | 3562 | onenand_unlock_all(mtd); |
2864 | 3563 | ||
2865 | return this->scan_bbt(mtd); | 3564 | ret = this->scan_bbt(mtd); |
3565 | if ((!FLEXONENAND(this)) || ret) | ||
3566 | return ret; | ||
3567 | |||
3568 | /* Change Flex-OneNAND boundaries if required */ | ||
3569 | for (i = 0; i < MAX_DIES; i++) | ||
3570 | flexonenand_set_boundary(mtd, i, flex_bdry[2 * i], | ||
3571 | flex_bdry[(2 * i) + 1]); | ||
3572 | |||
3573 | return 0; | ||
2866 | } | 3574 | } |
2867 | 3575 | ||
2868 | /** | 3576 | /** |
@@ -2891,6 +3599,7 @@ void onenand_release(struct mtd_info *mtd) | |||
2891 | kfree(this->page_buf); | 3599 | kfree(this->page_buf); |
2892 | if (this->options & ONENAND_OOBBUF_ALLOC) | 3600 | if (this->options & ONENAND_OOBBUF_ALLOC) |
2893 | kfree(this->oob_buf); | 3601 | kfree(this->oob_buf); |
3602 | kfree(mtd->eraseregions); | ||
2894 | } | 3603 | } |
2895 | 3604 | ||
2896 | EXPORT_SYMBOL_GPL(onenand_scan); | 3605 | EXPORT_SYMBOL_GPL(onenand_scan); |
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 2f53b51c6805..a91fcac1af01 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c | |||
@@ -63,6 +63,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
63 | loff_t from; | 63 | loff_t from; |
64 | size_t readlen, ooblen; | 64 | size_t readlen, ooblen; |
65 | struct mtd_oob_ops ops; | 65 | struct mtd_oob_ops ops; |
66 | int rgn; | ||
66 | 67 | ||
67 | printk(KERN_INFO "Scanning device for bad blocks\n"); | 68 | printk(KERN_INFO "Scanning device for bad blocks\n"); |
68 | 69 | ||
@@ -76,7 +77,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
76 | /* Note that numblocks is 2 * (real numblocks) here; | 77 | /* Note that numblocks is 2 * (real numblocks) here; |
77 | * see i += 2 below as it makses shifting and masking less painful | 78 | * see i += 2 below as it makses shifting and masking less painful |
78 | */ | 79 | */ |
79 | numblocks = mtd->size >> (bbm->bbt_erase_shift - 1); | 80 | numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); |
80 | startblock = 0; | 81 | startblock = 0; |
81 | from = 0; | 82 | from = 0; |
82 | 83 | ||
@@ -106,7 +107,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
106 | } | 107 | } |
107 | } | 108 | } |
108 | i += 2; | 109 | i += 2; |
109 | from += (1 << bbm->bbt_erase_shift); | 110 | |
111 | if (FLEXONENAND(this)) { | ||
112 | rgn = flexonenand_region(mtd, from); | ||
113 | from += mtd->eraseregions[rgn].erasesize; | ||
114 | } else | ||
115 | from += (1 << bbm->bbt_erase_shift); | ||
110 | } | 116 | } |
111 | 117 | ||
112 | return 0; | 118 | return 0; |
@@ -143,7 +149,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) | |||
143 | uint8_t res; | 149 | uint8_t res; |
144 | 150 | ||
145 | /* Get block number * 2 */ | 151 | /* Get block number * 2 */ |
146 | block = (int) (offs >> (bbm->bbt_erase_shift - 1)); | 152 | block = (int) (onenand_block(this, offs) << 1); |
147 | res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; | 153 | res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; |
148 | 154 | ||
149 | DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", | 155 | DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", |
@@ -178,7 +184,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) | |||
178 | struct bbm_info *bbm = this->bbm; | 184 | struct bbm_info *bbm = this->bbm; |
179 | int len, ret = 0; | 185 | int len, ret = 0; |
180 | 186 | ||
181 | len = mtd->size >> (this->erase_shift + 2); | 187 | len = this->chipsize >> (this->erase_shift + 2); |
182 | /* Allocate memory (2bit per block) and clear the memory bad block table */ | 188 | /* Allocate memory (2bit per block) and clear the memory bad block table */ |
183 | bbm->bbt = kzalloc(len, GFP_KERNEL); | 189 | bbm->bbt = kzalloc(len, GFP_KERNEL); |
184 | if (!bbm->bbt) { | 190 | if (!bbm->bbt) { |
diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c index d64200b7c94b..f6e3c8aebd3a 100644 --- a/drivers/mtd/onenand/onenand_sim.c +++ b/drivers/mtd/onenand/onenand_sim.c | |||
@@ -6,6 +6,10 @@ | |||
6 | * Copyright © 2005-2007 Samsung Electronics | 6 | * Copyright © 2005-2007 Samsung Electronics |
7 | * Kyungmin Park <kyungmin.park@samsung.com> | 7 | * Kyungmin Park <kyungmin.park@samsung.com> |
8 | * | 8 | * |
9 | * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com> | ||
10 | * Flex-OneNAND simulator support | ||
11 | * Copyright (C) Samsung Electronics, 2008 | ||
12 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | 13 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 14 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 15 | * published by the Free Software Foundation. |
@@ -24,16 +28,38 @@ | |||
24 | #ifndef CONFIG_ONENAND_SIM_MANUFACTURER | 28 | #ifndef CONFIG_ONENAND_SIM_MANUFACTURER |
25 | #define CONFIG_ONENAND_SIM_MANUFACTURER 0xec | 29 | #define CONFIG_ONENAND_SIM_MANUFACTURER 0xec |
26 | #endif | 30 | #endif |
31 | |||
27 | #ifndef CONFIG_ONENAND_SIM_DEVICE_ID | 32 | #ifndef CONFIG_ONENAND_SIM_DEVICE_ID |
28 | #define CONFIG_ONENAND_SIM_DEVICE_ID 0x04 | 33 | #define CONFIG_ONENAND_SIM_DEVICE_ID 0x04 |
29 | #endif | 34 | #endif |
35 | |||
36 | #define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1) | ||
37 | |||
30 | #ifndef CONFIG_ONENAND_SIM_VERSION_ID | 38 | #ifndef CONFIG_ONENAND_SIM_VERSION_ID |
31 | #define CONFIG_ONENAND_SIM_VERSION_ID 0x1e | 39 | #define CONFIG_ONENAND_SIM_VERSION_ID 0x1e |
32 | #endif | 40 | #endif |
33 | 41 | ||
42 | #ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID | ||
43 | #define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND | ||
44 | #endif | ||
45 | |||
46 | /* Initial boundary values for Flex-OneNAND Simulator */ | ||
47 | #ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY | ||
48 | #define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY 0x01 | ||
49 | #endif | ||
50 | |||
51 | #ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY | ||
52 | #define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY 0x01 | ||
53 | #endif | ||
54 | |||
34 | static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER; | 55 | static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER; |
35 | static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID; | 56 | static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID; |
36 | static int version_id = CONFIG_ONENAND_SIM_VERSION_ID; | 57 | static int version_id = CONFIG_ONENAND_SIM_VERSION_ID; |
58 | static int technology_id = CONFIG_ONENAND_SIM_TECHNOLOGY_ID; | ||
59 | static int boundary[] = { | ||
60 | CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY, | ||
61 | CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY, | ||
62 | }; | ||
37 | 63 | ||
38 | struct onenand_flash { | 64 | struct onenand_flash { |
39 | void __iomem *base; | 65 | void __iomem *base; |
@@ -57,12 +83,18 @@ struct onenand_flash { | |||
57 | (writew(v, this->base + ONENAND_REG_WP_STATUS)) | 83 | (writew(v, this->base + ONENAND_REG_WP_STATUS)) |
58 | 84 | ||
59 | /* It has all 0xff chars */ | 85 | /* It has all 0xff chars */ |
60 | #define MAX_ONENAND_PAGESIZE (2048 + 64) | 86 | #define MAX_ONENAND_PAGESIZE (4096 + 128) |
61 | static unsigned char *ffchars; | 87 | static unsigned char *ffchars; |
62 | 88 | ||
89 | #if CONFIG_FLEXONENAND | ||
90 | #define PARTITION_NAME "Flex-OneNAND simulator partition" | ||
91 | #else | ||
92 | #define PARTITION_NAME "OneNAND simulator partition" | ||
93 | #endif | ||
94 | |||
63 | static struct mtd_partition os_partitions[] = { | 95 | static struct mtd_partition os_partitions[] = { |
64 | { | 96 | { |
65 | .name = "OneNAND simulator partition", | 97 | .name = PARTITION_NAME, |
66 | .offset = 0, | 98 | .offset = 0, |
67 | .size = MTDPART_SIZ_FULL, | 99 | .size = MTDPART_SIZ_FULL, |
68 | }, | 100 | }, |
@@ -104,6 +136,7 @@ static void onenand_lock_handle(struct onenand_chip *this, int cmd) | |||
104 | 136 | ||
105 | switch (cmd) { | 137 | switch (cmd) { |
106 | case ONENAND_CMD_UNLOCK: | 138 | case ONENAND_CMD_UNLOCK: |
139 | case ONENAND_CMD_UNLOCK_ALL: | ||
107 | if (block_lock_scheme) | 140 | if (block_lock_scheme) |
108 | ONENAND_SET_WP_STATUS(ONENAND_WP_US, this); | 141 | ONENAND_SET_WP_STATUS(ONENAND_WP_US, this); |
109 | else | 142 | else |
@@ -228,10 +261,12 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, | |||
228 | { | 261 | { |
229 | struct mtd_info *mtd = &info->mtd; | 262 | struct mtd_info *mtd = &info->mtd; |
230 | struct onenand_flash *flash = this->priv; | 263 | struct onenand_flash *flash = this->priv; |
231 | int main_offset, spare_offset; | 264 | int main_offset, spare_offset, die = 0; |
232 | void __iomem *src; | 265 | void __iomem *src; |
233 | void __iomem *dest; | 266 | void __iomem *dest; |
234 | unsigned int i; | 267 | unsigned int i; |
268 | static int pi_operation; | ||
269 | int erasesize, rgn; | ||
235 | 270 | ||
236 | if (dataram) { | 271 | if (dataram) { |
237 | main_offset = mtd->writesize; | 272 | main_offset = mtd->writesize; |
@@ -241,10 +276,27 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, | |||
241 | spare_offset = 0; | 276 | spare_offset = 0; |
242 | } | 277 | } |
243 | 278 | ||
279 | if (pi_operation) { | ||
280 | die = readw(this->base + ONENAND_REG_START_ADDRESS2); | ||
281 | die >>= ONENAND_DDP_SHIFT; | ||
282 | } | ||
283 | |||
244 | switch (cmd) { | 284 | switch (cmd) { |
285 | case FLEXONENAND_CMD_PI_ACCESS: | ||
286 | pi_operation = 1; | ||
287 | break; | ||
288 | |||
289 | case ONENAND_CMD_RESET: | ||
290 | pi_operation = 0; | ||
291 | break; | ||
292 | |||
245 | case ONENAND_CMD_READ: | 293 | case ONENAND_CMD_READ: |
246 | src = ONENAND_CORE(flash) + offset; | 294 | src = ONENAND_CORE(flash) + offset; |
247 | dest = ONENAND_MAIN_AREA(this, main_offset); | 295 | dest = ONENAND_MAIN_AREA(this, main_offset); |
296 | if (pi_operation) { | ||
297 | writew(boundary[die], this->base + ONENAND_DATARAM); | ||
298 | break; | ||
299 | } | ||
248 | memcpy(dest, src, mtd->writesize); | 300 | memcpy(dest, src, mtd->writesize); |
249 | /* Fall through */ | 301 | /* Fall through */ |
250 | 302 | ||
@@ -257,6 +309,10 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, | |||
257 | case ONENAND_CMD_PROG: | 309 | case ONENAND_CMD_PROG: |
258 | src = ONENAND_MAIN_AREA(this, main_offset); | 310 | src = ONENAND_MAIN_AREA(this, main_offset); |
259 | dest = ONENAND_CORE(flash) + offset; | 311 | dest = ONENAND_CORE(flash) + offset; |
312 | if (pi_operation) { | ||
313 | boundary[die] = readw(this->base + ONENAND_DATARAM); | ||
314 | break; | ||
315 | } | ||
260 | /* To handle partial write */ | 316 | /* To handle partial write */ |
261 | for (i = 0; i < (1 << mtd->subpage_sft); i++) { | 317 | for (i = 0; i < (1 << mtd->subpage_sft); i++) { |
262 | int off = i * this->subpagesize; | 318 | int off = i * this->subpagesize; |
@@ -284,9 +340,18 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, | |||
284 | break; | 340 | break; |
285 | 341 | ||
286 | case ONENAND_CMD_ERASE: | 342 | case ONENAND_CMD_ERASE: |
287 | memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize); | 343 | if (pi_operation) |
344 | break; | ||
345 | |||
346 | if (FLEXONENAND(this)) { | ||
347 | rgn = flexonenand_region(mtd, offset); | ||
348 | erasesize = mtd->eraseregions[rgn].erasesize; | ||
349 | } else | ||
350 | erasesize = mtd->erasesize; | ||
351 | |||
352 | memset(ONENAND_CORE(flash) + offset, 0xff, erasesize); | ||
288 | memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff, | 353 | memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff, |
289 | (mtd->erasesize >> 5)); | 354 | (erasesize >> 5)); |
290 | break; | 355 | break; |
291 | 356 | ||
292 | default: | 357 | default: |
@@ -339,7 +404,7 @@ static void onenand_command_handle(struct onenand_chip *this, int cmd) | |||
339 | } | 404 | } |
340 | 405 | ||
341 | if (block != -1) | 406 | if (block != -1) |
342 | offset += block << this->erase_shift; | 407 | offset = onenand_addr(this, block); |
343 | 408 | ||
344 | if (page != -1) | 409 | if (page != -1) |
345 | offset += page << this->page_shift; | 410 | offset += page << this->page_shift; |
@@ -390,6 +455,7 @@ static int __init flash_init(struct onenand_flash *flash) | |||
390 | } | 455 | } |
391 | 456 | ||
392 | density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT; | 457 | density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT; |
458 | density &= ONENAND_DEVICE_DENSITY_MASK; | ||
393 | size = ((16 << 20) << density); | 459 | size = ((16 << 20) << density); |
394 | 460 | ||
395 | ONENAND_CORE(flash) = vmalloc(size + (size >> 5)); | 461 | ONENAND_CORE(flash) = vmalloc(size + (size >> 5)); |
@@ -405,8 +471,9 @@ static int __init flash_init(struct onenand_flash *flash) | |||
405 | writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID); | 471 | writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID); |
406 | writew(device_id, flash->base + ONENAND_REG_DEVICE_ID); | 472 | writew(device_id, flash->base + ONENAND_REG_DEVICE_ID); |
407 | writew(version_id, flash->base + ONENAND_REG_VERSION_ID); | 473 | writew(version_id, flash->base + ONENAND_REG_VERSION_ID); |
474 | writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY); | ||
408 | 475 | ||
409 | if (density < 2) | 476 | if (density < 2 && (!CONFIG_FLEXONENAND)) |
410 | buffer_size = 0x0400; /* 1KiB page */ | 477 | buffer_size = 0x0400; /* 1KiB page */ |
411 | else | 478 | else |
412 | buffer_size = 0x0800; /* 2KiB page */ | 479 | buffer_size = 0x0800; /* 2KiB page */ |