diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-22 19:56:22 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-22 19:56:22 -0400 |
commit | ac1b7c378ef26fba6694d5f118fe7fc16fee2fe2 (patch) | |
tree | 3f72979545bb070eb2c3e903cbf31dc4aef3ffc9 /drivers/mtd/onenand | |
parent | 9e268beb92ee3a853b3946e84b10358207e2085f (diff) | |
parent | c90173f0907486fe4010c2a8cef534e2473db43f (diff) |
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (63 commits)
mtd: OneNAND: Allow setting of boundary information when built as module
jffs2: leaking jffs2_summary in function jffs2_scan_medium
mtd: nand: Fix memory leak on txx9ndfmc probe failure.
mtd: orion_nand: use burst reads with double word accesses
mtd/nand: s3c6400 support for s3c2410 driver
[MTD] [NAND] S3C2410: Use DIV_ROUND_UP
[MTD] [NAND] S3C2410: Deal with unaligned lengths in S3C2440 buffer read/write
[MTD] [NAND] S3C2410: Allow the machine code to get the BBT table from NAND
[MTD] [NAND] S3C2410: Added a kerneldoc for s3c2410_nand_set
mtd: physmap_of: Add multiple regions and concatenation support
mtd: nand: max_retries off by one in mxc_nand
mtd: nand: s3c2410_nand_setrate(): use correct macros for 2412/2440
mtd: onenand: add bbt_wait & unlock_all as replaceable for some platform
mtd: Flex-OneNAND support
mtd: nand: add OMAP2/OMAP3 NAND driver
mtd: maps: Blackfin async: fix memory leaks in probe/remove funcs
mtd: uclinux: mark local stuff static
mtd: uclinux: do not allow to be built as a module
mtd: uclinux: allow systems to override map addr/size
mtd: blackfin NFC: fix hang when using NAND on BF527-EZKITs
...
Diffstat (limited to 'drivers/mtd/onenand')
-rw-r--r-- | drivers/mtd/onenand/omap2.c | 4 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 862 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 14 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_sim.c | 81 |
4 files changed, 871 insertions, 90 deletions
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 6391e3dc8002..38d656b9b2ee 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c | |||
@@ -565,7 +565,7 @@ int omap2_onenand_rephase(void) | |||
565 | NULL, __adjust_timing); | 565 | NULL, __adjust_timing); |
566 | } | 566 | } |
567 | 567 | ||
568 | static void __devexit omap2_onenand_shutdown(struct platform_device *pdev) | 568 | static void omap2_onenand_shutdown(struct platform_device *pdev) |
569 | { | 569 | { |
570 | struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); | 570 | struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); |
571 | 571 | ||
@@ -777,7 +777,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev) | |||
777 | 777 | ||
778 | static struct platform_driver omap2_onenand_driver = { | 778 | static struct platform_driver omap2_onenand_driver = { |
779 | .probe = omap2_onenand_probe, | 779 | .probe = omap2_onenand_probe, |
780 | .remove = omap2_onenand_remove, | 780 | .remove = __devexit_p(omap2_onenand_remove), |
781 | .shutdown = omap2_onenand_shutdown, | 781 | .shutdown = omap2_onenand_shutdown, |
782 | .driver = { | 782 | .driver = { |
783 | .name = DRIVER_NAME, | 783 | .name = DRIVER_NAME, |
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 30d6999e5f9f..6e829095ea9d 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. |
@@ -16,6 +20,7 @@ | |||
16 | 20 | ||
17 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
18 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | #include <linux/moduleparam.h> | ||
19 | #include <linux/init.h> | 24 | #include <linux/init.h> |
20 | #include <linux/sched.h> | 25 | #include <linux/sched.h> |
21 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
@@ -27,6 +32,38 @@ | |||
27 | 32 | ||
28 | #include <asm/io.h> | 33 | #include <asm/io.h> |
29 | 34 | ||
35 | /* Default Flex-OneNAND boundary and lock respectively */ | ||
36 | static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; | ||
37 | |||
38 | module_param_array(flex_bdry, int, NULL, 0400); | ||
39 | MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND" | ||
40 | "Syntax:flex_bdry=DIE_BDRY,LOCK,..." | ||
41 | "DIE_BDRY: SLC boundary of the die" | ||
42 | "LOCK: Locking information for SLC boundary" | ||
43 | " : 0->Set boundary in unlocked status" | ||
44 | " : 1->Set boundary in locked status"); | ||
45 | |||
46 | /** | ||
47 | * onenand_oob_128 - oob info for Flex-Onenand with 4KB page | ||
48 | * For now, we expose only 64 out of 80 ecc bytes | ||
49 | */ | ||
50 | static struct nand_ecclayout onenand_oob_128 = { | ||
51 | .eccbytes = 64, | ||
52 | .eccpos = { | ||
53 | 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | ||
54 | 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, | ||
55 | 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, | ||
56 | 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, | ||
57 | 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, | ||
58 | 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, | ||
59 | 102, 103, 104, 105 | ||
60 | }, | ||
61 | .oobfree = { | ||
62 | {2, 4}, {18, 4}, {34, 4}, {50, 4}, | ||
63 | {66, 4}, {82, 4}, {98, 4}, {114, 4} | ||
64 | } | ||
65 | }; | ||
66 | |||
30 | /** | 67 | /** |
31 | * onenand_oob_64 - oob info for large (2KB) page | 68 | * onenand_oob_64 - oob info for large (2KB) page |
32 | */ | 69 | */ |
@@ -65,6 +102,14 @@ static const unsigned char ffchars[] = { | |||
65 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ | 102 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ |
66 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 103 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
67 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ | 104 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ |
105 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
106 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ | ||
107 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
108 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ | ||
109 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
110 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ | ||
111 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
112 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ | ||
68 | }; | 113 | }; |
69 | 114 | ||
70 | /** | 115 | /** |
@@ -171,6 +216,70 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) | |||
171 | } | 216 | } |
172 | 217 | ||
173 | /** | 218 | /** |
219 | * flexonenand_block- For given address return block number | ||
220 | * @param this - OneNAND device structure | ||
221 | * @param addr - Address for which block number is needed | ||
222 | */ | ||
223 | static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr) | ||
224 | { | ||
225 | unsigned boundary, blk, die = 0; | ||
226 | |||
227 | if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) { | ||
228 | die = 1; | ||
229 | addr -= this->diesize[0]; | ||
230 | } | ||
231 | |||
232 | boundary = this->boundary[die]; | ||
233 | |||
234 | blk = addr >> (this->erase_shift - 1); | ||
235 | if (blk > boundary) | ||
236 | blk = (blk + boundary + 1) >> 1; | ||
237 | |||
238 | blk += die ? this->density_mask : 0; | ||
239 | return blk; | ||
240 | } | ||
241 | |||
242 | inline unsigned onenand_block(struct onenand_chip *this, loff_t addr) | ||
243 | { | ||
244 | if (!FLEXONENAND(this)) | ||
245 | return addr >> this->erase_shift; | ||
246 | return flexonenand_block(this, addr); | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * flexonenand_addr - Return address of the block | ||
251 | * @this: OneNAND device structure | ||
252 | * @block: Block number on Flex-OneNAND | ||
253 | * | ||
254 | * Return address of the block | ||
255 | */ | ||
256 | static loff_t flexonenand_addr(struct onenand_chip *this, int block) | ||
257 | { | ||
258 | loff_t ofs = 0; | ||
259 | int die = 0, boundary; | ||
260 | |||
261 | if (ONENAND_IS_DDP(this) && block >= this->density_mask) { | ||
262 | block -= this->density_mask; | ||
263 | die = 1; | ||
264 | ofs = this->diesize[0]; | ||
265 | } | ||
266 | |||
267 | boundary = this->boundary[die]; | ||
268 | ofs += (loff_t)block << (this->erase_shift - 1); | ||
269 | if (block > (boundary + 1)) | ||
270 | ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1); | ||
271 | return ofs; | ||
272 | } | ||
273 | |||
274 | loff_t onenand_addr(struct onenand_chip *this, int block) | ||
275 | { | ||
276 | if (!FLEXONENAND(this)) | ||
277 | return (loff_t)block << this->erase_shift; | ||
278 | return flexonenand_addr(this, block); | ||
279 | } | ||
280 | EXPORT_SYMBOL(onenand_addr); | ||
281 | |||
282 | /** | ||
174 | * onenand_get_density - [DEFAULT] Get OneNAND density | 283 | * onenand_get_density - [DEFAULT] Get OneNAND density |
175 | * @param dev_id OneNAND device ID | 284 | * @param dev_id OneNAND device ID |
176 | * | 285 | * |
@@ -183,6 +292,22 @@ static inline int onenand_get_density(int dev_id) | |||
183 | } | 292 | } |
184 | 293 | ||
185 | /** | 294 | /** |
295 | * flexonenand_region - [Flex-OneNAND] Return erase region of addr | ||
296 | * @param mtd MTD device structure | ||
297 | * @param addr address whose erase region needs to be identified | ||
298 | */ | ||
299 | int flexonenand_region(struct mtd_info *mtd, loff_t addr) | ||
300 | { | ||
301 | int i; | ||
302 | |||
303 | for (i = 0; i < mtd->numeraseregions; i++) | ||
304 | if (addr < mtd->eraseregions[i].offset) | ||
305 | break; | ||
306 | return i - 1; | ||
307 | } | ||
308 | EXPORT_SYMBOL(flexonenand_region); | ||
309 | |||
310 | /** | ||
186 | * onenand_command - [DEFAULT] Send command to OneNAND device | 311 | * onenand_command - [DEFAULT] Send command to OneNAND device |
187 | * @param mtd MTD device structure | 312 | * @param mtd MTD device structure |
188 | * @param cmd the command to be sent | 313 | * @param cmd the command to be sent |
@@ -207,16 +332,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
207 | page = -1; | 332 | page = -1; |
208 | break; | 333 | break; |
209 | 334 | ||
335 | case FLEXONENAND_CMD_PI_ACCESS: | ||
336 | /* addr contains die index */ | ||
337 | block = addr * this->density_mask; | ||
338 | page = -1; | ||
339 | break; | ||
340 | |||
210 | case ONENAND_CMD_ERASE: | 341 | case ONENAND_CMD_ERASE: |
211 | case ONENAND_CMD_BUFFERRAM: | 342 | case ONENAND_CMD_BUFFERRAM: |
212 | case ONENAND_CMD_OTP_ACCESS: | 343 | case ONENAND_CMD_OTP_ACCESS: |
213 | block = (int) (addr >> this->erase_shift); | 344 | block = onenand_block(this, addr); |
214 | page = -1; | 345 | page = -1; |
215 | break; | 346 | break; |
216 | 347 | ||
348 | case FLEXONENAND_CMD_READ_PI: | ||
349 | cmd = ONENAND_CMD_READ; | ||
350 | block = addr * this->density_mask; | ||
351 | page = 0; | ||
352 | break; | ||
353 | |||
217 | default: | 354 | default: |
218 | block = (int) (addr >> this->erase_shift); | 355 | block = onenand_block(this, addr); |
219 | page = (int) (addr >> this->page_shift); | 356 | page = (int) (addr - onenand_addr(this, block)) >> this->page_shift; |
220 | 357 | ||
221 | if (ONENAND_IS_2PLANE(this)) { | 358 | if (ONENAND_IS_2PLANE(this)) { |
222 | /* Make the even block number */ | 359 | /* Make the even block number */ |
@@ -236,7 +373,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
236 | value = onenand_bufferram_address(this, block); | 373 | value = onenand_bufferram_address(this, block); |
237 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | 374 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); |
238 | 375 | ||
239 | if (ONENAND_IS_2PLANE(this)) | 376 | if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this)) |
240 | /* It is always BufferRAM0 */ | 377 | /* It is always BufferRAM0 */ |
241 | ONENAND_SET_BUFFERRAM0(this); | 378 | ONENAND_SET_BUFFERRAM0(this); |
242 | else | 379 | else |
@@ -258,13 +395,18 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
258 | 395 | ||
259 | if (page != -1) { | 396 | if (page != -1) { |
260 | /* Now we use page size operation */ | 397 | /* Now we use page size operation */ |
261 | int sectors = 4, count = 4; | 398 | int sectors = 0, count = 0; |
262 | int dataram; | 399 | int dataram; |
263 | 400 | ||
264 | switch (cmd) { | 401 | switch (cmd) { |
402 | case FLEXONENAND_CMD_RECOVER_LSB: | ||
265 | case ONENAND_CMD_READ: | 403 | case ONENAND_CMD_READ: |
266 | case ONENAND_CMD_READOOB: | 404 | case ONENAND_CMD_READOOB: |
267 | dataram = ONENAND_SET_NEXT_BUFFERRAM(this); | 405 | if (ONENAND_IS_MLC(this)) |
406 | /* It is always BufferRAM0 */ | ||
407 | dataram = ONENAND_SET_BUFFERRAM0(this); | ||
408 | else | ||
409 | dataram = ONENAND_SET_NEXT_BUFFERRAM(this); | ||
268 | break; | 410 | break; |
269 | 411 | ||
270 | default: | 412 | default: |
@@ -293,6 +435,30 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
293 | } | 435 | } |
294 | 436 | ||
295 | /** | 437 | /** |
438 | * onenand_read_ecc - return ecc status | ||
439 | * @param this onenand chip structure | ||
440 | */ | ||
441 | static inline int onenand_read_ecc(struct onenand_chip *this) | ||
442 | { | ||
443 | int ecc, i, result = 0; | ||
444 | |||
445 | if (!FLEXONENAND(this)) | ||
446 | return this->read_word(this->base + ONENAND_REG_ECC_STATUS); | ||
447 | |||
448 | for (i = 0; i < 4; i++) { | ||
449 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i); | ||
450 | if (likely(!ecc)) | ||
451 | continue; | ||
452 | if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) | ||
453 | return ONENAND_ECC_2BIT_ALL; | ||
454 | else | ||
455 | result = ONENAND_ECC_1BIT_ALL; | ||
456 | } | ||
457 | |||
458 | return result; | ||
459 | } | ||
460 | |||
461 | /** | ||
296 | * onenand_wait - [DEFAULT] wait until the command is done | 462 | * onenand_wait - [DEFAULT] wait until the command is done |
297 | * @param mtd MTD device structure | 463 | * @param mtd MTD device structure |
298 | * @param state state to select the max. timeout value | 464 | * @param state state to select the max. timeout value |
@@ -331,14 +497,14 @@ static int onenand_wait(struct mtd_info *mtd, int state) | |||
331 | * power off recovery (POR) test, it should read ECC status first | 497 | * power off recovery (POR) test, it should read ECC status first |
332 | */ | 498 | */ |
333 | if (interrupt & ONENAND_INT_READ) { | 499 | if (interrupt & ONENAND_INT_READ) { |
334 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 500 | int ecc = onenand_read_ecc(this); |
335 | if (ecc) { | 501 | if (ecc) { |
336 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 502 | if (ecc & ONENAND_ECC_2BIT_ALL) { |
337 | printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); | 503 | printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); |
338 | mtd->ecc_stats.failed++; | 504 | mtd->ecc_stats.failed++; |
339 | return -EBADMSG; | 505 | return -EBADMSG; |
340 | } else if (ecc & ONENAND_ECC_1BIT_ALL) { | 506 | } else if (ecc & ONENAND_ECC_1BIT_ALL) { |
341 | printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc); | 507 | printk(KERN_DEBUG "onenand_wait: correctable ECC error = 0x%04x\n", ecc); |
342 | mtd->ecc_stats.corrected++; | 508 | mtd->ecc_stats.corrected++; |
343 | } | 509 | } |
344 | } | 510 | } |
@@ -656,7 +822,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | |||
656 | 822 | ||
657 | if (found && ONENAND_IS_DDP(this)) { | 823 | if (found && ONENAND_IS_DDP(this)) { |
658 | /* Select DataRAM for DDP */ | 824 | /* Select DataRAM for DDP */ |
659 | int block = (int) (addr >> this->erase_shift); | 825 | int block = onenand_block(this, addr); |
660 | int value = onenand_bufferram_address(this, block); | 826 | int value = onenand_bufferram_address(this, block); |
661 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | 827 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); |
662 | } | 828 | } |
@@ -816,6 +982,149 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col | |||
816 | } | 982 | } |
817 | 983 | ||
818 | /** | 984 | /** |
985 | * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data | ||
986 | * @param mtd MTD device structure | ||
987 | * @param addr address to recover | ||
988 | * @param status return value from onenand_wait / onenand_bbt_wait | ||
989 | * | ||
990 | * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has | ||
991 | * lower page address and MSB page has higher page address in paired pages. | ||
992 | * If power off occurs during MSB page program, the paired LSB page data can | ||
993 | * become corrupt. LSB page recovery read is a way to read LSB page though page | ||
994 | * data are corrupted. When uncorrectable error occurs as a result of LSB page | ||
995 | * read after power up, issue LSB page recovery read. | ||
996 | */ | ||
997 | static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) | ||
998 | { | ||
999 | struct onenand_chip *this = mtd->priv; | ||
1000 | int i; | ||
1001 | |||
1002 | /* Recovery is only for Flex-OneNAND */ | ||
1003 | if (!FLEXONENAND(this)) | ||
1004 | return status; | ||
1005 | |||
1006 | /* check if we failed due to uncorrectable error */ | ||
1007 | if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR) | ||
1008 | return status; | ||
1009 | |||
1010 | /* check if address lies in MLC region */ | ||
1011 | i = flexonenand_region(mtd, addr); | ||
1012 | if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift)) | ||
1013 | return status; | ||
1014 | |||
1015 | /* We are attempting to reread, so decrement stats.failed | ||
1016 | * which was incremented by onenand_wait due to read failure | ||
1017 | */ | ||
1018 | printk(KERN_INFO "onenand_recover_lsb: Attempting to recover from uncorrectable read\n"); | ||
1019 | mtd->ecc_stats.failed--; | ||
1020 | |||
1021 | /* Issue the LSB page recovery command */ | ||
1022 | this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); | ||
1023 | return this->wait(mtd, FL_READING); | ||
1024 | } | ||
1025 | |||
1026 | /** | ||
1027 | * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band | ||
1028 | * @param mtd MTD device structure | ||
1029 | * @param from offset to read from | ||
1030 | * @param ops: oob operation description structure | ||
1031 | * | ||
1032 | * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram. | ||
1033 | * So, read-while-load is not present. | ||
1034 | */ | ||
1035 | static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, | ||
1036 | struct mtd_oob_ops *ops) | ||
1037 | { | ||
1038 | struct onenand_chip *this = mtd->priv; | ||
1039 | struct mtd_ecc_stats stats; | ||
1040 | size_t len = ops->len; | ||
1041 | size_t ooblen = ops->ooblen; | ||
1042 | u_char *buf = ops->datbuf; | ||
1043 | u_char *oobbuf = ops->oobbuf; | ||
1044 | int read = 0, column, thislen; | ||
1045 | int oobread = 0, oobcolumn, thisooblen, oobsize; | ||
1046 | int ret = 0; | ||
1047 | int writesize = this->writesize; | ||
1048 | |||
1049 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_mlc_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | ||
1050 | |||
1051 | if (ops->mode == MTD_OOB_AUTO) | ||
1052 | oobsize = this->ecclayout->oobavail; | ||
1053 | else | ||
1054 | oobsize = mtd->oobsize; | ||
1055 | |||
1056 | oobcolumn = from & (mtd->oobsize - 1); | ||
1057 | |||
1058 | /* Do not allow reads past end of device */ | ||
1059 | if (from + len > mtd->size) { | ||
1060 | printk(KERN_ERR "onenand_mlc_read_ops_nolock: Attempt read beyond end of device\n"); | ||
1061 | ops->retlen = 0; | ||
1062 | ops->oobretlen = 0; | ||
1063 | return -EINVAL; | ||
1064 | } | ||
1065 | |||
1066 | stats = mtd->ecc_stats; | ||
1067 | |||
1068 | while (read < len) { | ||
1069 | cond_resched(); | ||
1070 | |||
1071 | thislen = min_t(int, writesize, len - read); | ||
1072 | |||
1073 | column = from & (writesize - 1); | ||
1074 | if (column + thislen > writesize) | ||
1075 | thislen = writesize - column; | ||
1076 | |||
1077 | if (!onenand_check_bufferram(mtd, from)) { | ||
1078 | this->command(mtd, ONENAND_CMD_READ, from, writesize); | ||
1079 | |||
1080 | ret = this->wait(mtd, FL_READING); | ||
1081 | if (unlikely(ret)) | ||
1082 | ret = onenand_recover_lsb(mtd, from, ret); | ||
1083 | onenand_update_bufferram(mtd, from, !ret); | ||
1084 | if (ret == -EBADMSG) | ||
1085 | ret = 0; | ||
1086 | } | ||
1087 | |||
1088 | this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); | ||
1089 | if (oobbuf) { | ||
1090 | thisooblen = oobsize - oobcolumn; | ||
1091 | thisooblen = min_t(int, thisooblen, ooblen - oobread); | ||
1092 | |||
1093 | if (ops->mode == MTD_OOB_AUTO) | ||
1094 | onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); | ||
1095 | else | ||
1096 | this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); | ||
1097 | oobread += thisooblen; | ||
1098 | oobbuf += thisooblen; | ||
1099 | oobcolumn = 0; | ||
1100 | } | ||
1101 | |||
1102 | read += thislen; | ||
1103 | if (read == len) | ||
1104 | break; | ||
1105 | |||
1106 | from += thislen; | ||
1107 | buf += thislen; | ||
1108 | } | ||
1109 | |||
1110 | /* | ||
1111 | * Return success, if no ECC failures, else -EBADMSG | ||
1112 | * fs driver will take care of that, because | ||
1113 | * retlen == desired len and result == -EBADMSG | ||
1114 | */ | ||
1115 | ops->retlen = read; | ||
1116 | ops->oobretlen = oobread; | ||
1117 | |||
1118 | if (ret) | ||
1119 | return ret; | ||
1120 | |||
1121 | if (mtd->ecc_stats.failed - stats.failed) | ||
1122 | return -EBADMSG; | ||
1123 | |||
1124 | return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; | ||
1125 | } | ||
1126 | |||
1127 | /** | ||
819 | * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band | 1128 | * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band |
820 | * @param mtd MTD device structure | 1129 | * @param mtd MTD device structure |
821 | * @param from offset to read from | 1130 | * @param from offset to read from |
@@ -962,7 +1271,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, | |||
962 | size_t len = ops->ooblen; | 1271 | size_t len = ops->ooblen; |
963 | mtd_oob_mode_t mode = ops->mode; | 1272 | mtd_oob_mode_t mode = ops->mode; |
964 | u_char *buf = ops->oobbuf; | 1273 | u_char *buf = ops->oobbuf; |
965 | int ret = 0; | 1274 | int ret = 0, readcmd; |
966 | 1275 | ||
967 | from += ops->ooboffs; | 1276 | from += ops->ooboffs; |
968 | 1277 | ||
@@ -993,17 +1302,22 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, | |||
993 | 1302 | ||
994 | stats = mtd->ecc_stats; | 1303 | stats = mtd->ecc_stats; |
995 | 1304 | ||
1305 | readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; | ||
1306 | |||
996 | while (read < len) { | 1307 | while (read < len) { |
997 | cond_resched(); | 1308 | cond_resched(); |
998 | 1309 | ||
999 | thislen = oobsize - column; | 1310 | thislen = oobsize - column; |
1000 | thislen = min_t(int, thislen, len); | 1311 | thislen = min_t(int, thislen, len); |
1001 | 1312 | ||
1002 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | 1313 | this->command(mtd, readcmd, from, mtd->oobsize); |
1003 | 1314 | ||
1004 | onenand_update_bufferram(mtd, from, 0); | 1315 | onenand_update_bufferram(mtd, from, 0); |
1005 | 1316 | ||
1006 | ret = this->wait(mtd, FL_READING); | 1317 | ret = this->wait(mtd, FL_READING); |
1318 | if (unlikely(ret)) | ||
1319 | ret = onenand_recover_lsb(mtd, from, ret); | ||
1320 | |||
1007 | if (ret && ret != -EBADMSG) { | 1321 | if (ret && ret != -EBADMSG) { |
1008 | printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); | 1322 | printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); |
1009 | break; | 1323 | break; |
@@ -1053,6 +1367,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, | 1367 | static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, |
1054 | size_t *retlen, u_char *buf) | 1368 | size_t *retlen, u_char *buf) |
1055 | { | 1369 | { |
1370 | struct onenand_chip *this = mtd->priv; | ||
1056 | struct mtd_oob_ops ops = { | 1371 | struct mtd_oob_ops ops = { |
1057 | .len = len, | 1372 | .len = len, |
1058 | .ooblen = 0, | 1373 | .ooblen = 0, |
@@ -1062,7 +1377,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
1062 | int ret; | 1377 | int ret; |
1063 | 1378 | ||
1064 | onenand_get_device(mtd, FL_READING); | 1379 | onenand_get_device(mtd, FL_READING); |
1065 | ret = onenand_read_ops_nolock(mtd, from, &ops); | 1380 | ret = ONENAND_IS_MLC(this) ? |
1381 | onenand_mlc_read_ops_nolock(mtd, from, &ops) : | ||
1382 | onenand_read_ops_nolock(mtd, from, &ops); | ||
1066 | onenand_release_device(mtd); | 1383 | onenand_release_device(mtd); |
1067 | 1384 | ||
1068 | *retlen = ops.retlen; | 1385 | *retlen = ops.retlen; |
@@ -1080,6 +1397,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, | 1397 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, |
1081 | struct mtd_oob_ops *ops) | 1398 | struct mtd_oob_ops *ops) |
1082 | { | 1399 | { |
1400 | struct onenand_chip *this = mtd->priv; | ||
1083 | int ret; | 1401 | int ret; |
1084 | 1402 | ||
1085 | switch (ops->mode) { | 1403 | switch (ops->mode) { |
@@ -1094,7 +1412,9 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | |||
1094 | 1412 | ||
1095 | onenand_get_device(mtd, FL_READING); | 1413 | onenand_get_device(mtd, FL_READING); |
1096 | if (ops->datbuf) | 1414 | if (ops->datbuf) |
1097 | ret = onenand_read_ops_nolock(mtd, from, ops); | 1415 | ret = ONENAND_IS_MLC(this) ? |
1416 | onenand_mlc_read_ops_nolock(mtd, from, ops) : | ||
1417 | onenand_read_ops_nolock(mtd, from, ops); | ||
1098 | else | 1418 | else |
1099 | ret = onenand_read_oob_nolock(mtd, from, ops); | 1419 | ret = onenand_read_oob_nolock(mtd, from, ops); |
1100 | onenand_release_device(mtd); | 1420 | onenand_release_device(mtd); |
@@ -1128,11 +1448,11 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) | |||
1128 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | 1448 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); |
1129 | 1449 | ||
1130 | if (interrupt & ONENAND_INT_READ) { | 1450 | if (interrupt & ONENAND_INT_READ) { |
1131 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 1451 | int ecc = onenand_read_ecc(this); |
1132 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 1452 | if (ecc & ONENAND_ECC_2BIT_ALL) { |
1133 | printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" | 1453 | printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" |
1134 | ", controller error 0x%04x\n", ecc, ctrl); | 1454 | ", controller error 0x%04x\n", ecc, ctrl); |
1135 | return ONENAND_BBT_READ_ERROR; | 1455 | return ONENAND_BBT_READ_ECC_ERROR; |
1136 | } | 1456 | } |
1137 | } else { | 1457 | } else { |
1138 | printk(KERN_ERR "onenand_bbt_wait: read timeout!" | 1458 | printk(KERN_ERR "onenand_bbt_wait: read timeout!" |
@@ -1163,7 +1483,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | |||
1163 | { | 1483 | { |
1164 | struct onenand_chip *this = mtd->priv; | 1484 | struct onenand_chip *this = mtd->priv; |
1165 | int read = 0, thislen, column; | 1485 | int read = 0, thislen, column; |
1166 | int ret = 0; | 1486 | int ret = 0, readcmd; |
1167 | size_t len = ops->ooblen; | 1487 | size_t len = ops->ooblen; |
1168 | u_char *buf = ops->oobbuf; | 1488 | u_char *buf = ops->oobbuf; |
1169 | 1489 | ||
@@ -1183,17 +1503,22 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | |||
1183 | 1503 | ||
1184 | column = from & (mtd->oobsize - 1); | 1504 | column = from & (mtd->oobsize - 1); |
1185 | 1505 | ||
1506 | readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; | ||
1507 | |||
1186 | while (read < len) { | 1508 | while (read < len) { |
1187 | cond_resched(); | 1509 | cond_resched(); |
1188 | 1510 | ||
1189 | thislen = mtd->oobsize - column; | 1511 | thislen = mtd->oobsize - column; |
1190 | thislen = min_t(int, thislen, len); | 1512 | thislen = min_t(int, thislen, len); |
1191 | 1513 | ||
1192 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | 1514 | this->command(mtd, readcmd, from, mtd->oobsize); |
1193 | 1515 | ||
1194 | onenand_update_bufferram(mtd, from, 0); | 1516 | onenand_update_bufferram(mtd, from, 0); |
1195 | 1517 | ||
1196 | ret = onenand_bbt_wait(mtd, FL_READING); | 1518 | ret = this->bbt_wait(mtd, FL_READING); |
1519 | if (unlikely(ret)) | ||
1520 | ret = onenand_recover_lsb(mtd, from, ret); | ||
1521 | |||
1197 | if (ret) | 1522 | if (ret) |
1198 | break; | 1523 | break; |
1199 | 1524 | ||
@@ -1230,9 +1555,11 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to | |||
1230 | { | 1555 | { |
1231 | struct onenand_chip *this = mtd->priv; | 1556 | struct onenand_chip *this = mtd->priv; |
1232 | u_char *oob_buf = this->oob_buf; | 1557 | u_char *oob_buf = this->oob_buf; |
1233 | int status, i; | 1558 | int status, i, readcmd; |
1234 | 1559 | ||
1235 | this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); | 1560 | readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; |
1561 | |||
1562 | this->command(mtd, readcmd, to, mtd->oobsize); | ||
1236 | onenand_update_bufferram(mtd, to, 0); | 1563 | onenand_update_bufferram(mtd, to, 0); |
1237 | status = this->wait(mtd, FL_READING); | 1564 | status = this->wait(mtd, FL_READING); |
1238 | if (status) | 1565 | if (status) |
@@ -1633,7 +1960,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, | |||
1633 | { | 1960 | { |
1634 | struct onenand_chip *this = mtd->priv; | 1961 | struct onenand_chip *this = mtd->priv; |
1635 | int column, ret = 0, oobsize; | 1962 | int column, ret = 0, oobsize; |
1636 | int written = 0; | 1963 | int written = 0, oobcmd; |
1637 | u_char *oobbuf; | 1964 | u_char *oobbuf; |
1638 | size_t len = ops->ooblen; | 1965 | size_t len = ops->ooblen; |
1639 | const u_char *buf = ops->oobbuf; | 1966 | const u_char *buf = ops->oobbuf; |
@@ -1675,6 +2002,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, | |||
1675 | 2002 | ||
1676 | oobbuf = this->oob_buf; | 2003 | oobbuf = this->oob_buf; |
1677 | 2004 | ||
2005 | oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; | ||
2006 | |||
1678 | /* Loop until all data write */ | 2007 | /* Loop until all data write */ |
1679 | while (written < len) { | 2008 | while (written < len) { |
1680 | int thislen = min_t(int, oobsize, len - written); | 2009 | int thislen = min_t(int, oobsize, len - written); |
@@ -1692,7 +2021,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, | |||
1692 | memcpy(oobbuf + column, buf, thislen); | 2021 | memcpy(oobbuf + column, buf, thislen); |
1693 | this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); | 2022 | this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); |
1694 | 2023 | ||
1695 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | 2024 | if (ONENAND_IS_MLC(this)) { |
2025 | /* Set main area of DataRAM to 0xff*/ | ||
2026 | memset(this->page_buf, 0xff, mtd->writesize); | ||
2027 | this->write_bufferram(mtd, ONENAND_DATARAM, | ||
2028 | this->page_buf, 0, mtd->writesize); | ||
2029 | } | ||
2030 | |||
2031 | this->command(mtd, oobcmd, to, mtd->oobsize); | ||
1696 | 2032 | ||
1697 | onenand_update_bufferram(mtd, to, 0); | 2033 | onenand_update_bufferram(mtd, to, 0); |
1698 | if (ONENAND_IS_2PLANE(this)) { | 2034 | if (ONENAND_IS_2PLANE(this)) { |
@@ -1815,29 +2151,48 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1815 | { | 2151 | { |
1816 | struct onenand_chip *this = mtd->priv; | 2152 | struct onenand_chip *this = mtd->priv; |
1817 | unsigned int block_size; | 2153 | unsigned int block_size; |
1818 | loff_t addr; | 2154 | loff_t addr = instr->addr; |
1819 | int len; | 2155 | loff_t len = instr->len; |
1820 | int ret = 0; | 2156 | int ret = 0, i; |
2157 | struct mtd_erase_region_info *region = NULL; | ||
2158 | loff_t region_end = 0; | ||
1821 | 2159 | ||
1822 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); | 2160 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); |
1823 | 2161 | ||
1824 | block_size = (1 << this->erase_shift); | 2162 | /* Do not allow erase past end of device */ |
1825 | 2163 | if (unlikely((len + addr) > mtd->size)) { | |
1826 | /* Start address must align on block boundary */ | 2164 | 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; | 2165 | return -EINVAL; |
1830 | } | 2166 | } |
1831 | 2167 | ||
1832 | /* Length must align on block boundary */ | 2168 | if (FLEXONENAND(this)) { |
1833 | if (unlikely(instr->len & (block_size - 1))) { | 2169 | /* Find the eraseregion of this address */ |
1834 | printk(KERN_ERR "onenand_erase: Length not block aligned\n"); | 2170 | i = flexonenand_region(mtd, addr); |
1835 | return -EINVAL; | 2171 | region = &mtd->eraseregions[i]; |
2172 | |||
2173 | block_size = region->erasesize; | ||
2174 | region_end = region->offset + region->erasesize * region->numblocks; | ||
2175 | |||
2176 | /* Start address within region must align on block boundary. | ||
2177 | * Erase region's start offset is always block start address. | ||
2178 | */ | ||
2179 | if (unlikely((addr - region->offset) & (block_size - 1))) { | ||
2180 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); | ||
2181 | return -EINVAL; | ||
2182 | } | ||
2183 | } else { | ||
2184 | block_size = 1 << this->erase_shift; | ||
2185 | |||
2186 | /* Start address must align on block boundary */ | ||
2187 | if (unlikely(addr & (block_size - 1))) { | ||
2188 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); | ||
2189 | return -EINVAL; | ||
2190 | } | ||
1836 | } | 2191 | } |
1837 | 2192 | ||
1838 | /* Do not allow erase past end of device */ | 2193 | /* Length must align on block boundary */ |
1839 | if (unlikely((instr->len + instr->addr) > mtd->size)) { | 2194 | if (unlikely(len & (block_size - 1))) { |
1840 | printk(KERN_ERR "onenand_erase: Erase past end of device\n"); | 2195 | printk(KERN_ERR "onenand_erase: Length not block aligned\n"); |
1841 | return -EINVAL; | 2196 | return -EINVAL; |
1842 | } | 2197 | } |
1843 | 2198 | ||
@@ -1847,9 +2202,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1847 | onenand_get_device(mtd, FL_ERASING); | 2202 | onenand_get_device(mtd, FL_ERASING); |
1848 | 2203 | ||
1849 | /* Loop throught the pages */ | 2204 | /* Loop throught the pages */ |
1850 | len = instr->len; | ||
1851 | addr = instr->addr; | ||
1852 | |||
1853 | instr->state = MTD_ERASING; | 2205 | instr->state = MTD_ERASING; |
1854 | 2206 | ||
1855 | while (len) { | 2207 | while (len) { |
@@ -1869,7 +2221,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1869 | ret = this->wait(mtd, FL_ERASING); | 2221 | ret = this->wait(mtd, FL_ERASING); |
1870 | /* Check, if it is write protected */ | 2222 | /* Check, if it is write protected */ |
1871 | if (ret) { | 2223 | if (ret) { |
1872 | printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); | 2224 | printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", |
2225 | onenand_block(this, addr)); | ||
1873 | instr->state = MTD_ERASE_FAILED; | 2226 | instr->state = MTD_ERASE_FAILED; |
1874 | instr->fail_addr = addr; | 2227 | instr->fail_addr = addr; |
1875 | goto erase_exit; | 2228 | goto erase_exit; |
@@ -1877,6 +2230,22 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1877 | 2230 | ||
1878 | len -= block_size; | 2231 | len -= block_size; |
1879 | addr += block_size; | 2232 | addr += block_size; |
2233 | |||
2234 | if (addr == region_end) { | ||
2235 | if (!len) | ||
2236 | break; | ||
2237 | region++; | ||
2238 | |||
2239 | block_size = region->erasesize; | ||
2240 | region_end = region->offset + region->erasesize * region->numblocks; | ||
2241 | |||
2242 | if (len & (block_size - 1)) { | ||
2243 | /* FIXME: This should be handled at MTD partitioning level. */ | ||
2244 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); | ||
2245 | goto erase_exit; | ||
2246 | } | ||
2247 | } | ||
2248 | |||
1880 | } | 2249 | } |
1881 | 2250 | ||
1882 | instr->state = MTD_ERASE_DONE; | 2251 | instr->state = MTD_ERASE_DONE; |
@@ -1955,13 +2324,17 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1955 | int block; | 2324 | int block; |
1956 | 2325 | ||
1957 | /* Get block number */ | 2326 | /* Get block number */ |
1958 | block = ((int) ofs) >> bbm->bbt_erase_shift; | 2327 | block = onenand_block(this, ofs); |
1959 | if (bbm->bbt) | 2328 | if (bbm->bbt) |
1960 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); | 2329 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); |
1961 | 2330 | ||
1962 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 2331 | /* We write two bytes, so we dont have to mess with 16 bit access */ |
1963 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | 2332 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); |
1964 | return onenand_write_oob_nolock(mtd, ofs, &ops); | 2333 | /* FIXME : What to do when marking SLC block in partition |
2334 | * with MLC erasesize? For now, it is not advisable to | ||
2335 | * create partitions containing both SLC and MLC regions. | ||
2336 | */ | ||
2337 | return onenand_write_oob_nolock(mtd, ofs, &ops); | ||
1965 | } | 2338 | } |
1966 | 2339 | ||
1967 | /** | 2340 | /** |
@@ -2005,8 +2378,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; | 2378 | int start, end, block, value, status; |
2006 | int wp_status_mask; | 2379 | int wp_status_mask; |
2007 | 2380 | ||
2008 | start = ofs >> this->erase_shift; | 2381 | start = onenand_block(this, ofs); |
2009 | end = len >> this->erase_shift; | 2382 | end = onenand_block(this, ofs + len) - 1; |
2010 | 2383 | ||
2011 | if (cmd == ONENAND_CMD_LOCK) | 2384 | if (cmd == ONENAND_CMD_LOCK) |
2012 | wp_status_mask = ONENAND_WP_LS; | 2385 | wp_status_mask = ONENAND_WP_LS; |
@@ -2018,7 +2391,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int | |||
2018 | /* Set start block address */ | 2391 | /* Set start block address */ |
2019 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | 2392 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); |
2020 | /* Set end block address */ | 2393 | /* Set end block address */ |
2021 | this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); | 2394 | this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS); |
2022 | /* Write lock command */ | 2395 | /* Write lock command */ |
2023 | this->command(mtd, cmd, 0, 0); | 2396 | this->command(mtd, cmd, 0, 0); |
2024 | 2397 | ||
@@ -2039,7 +2412,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int | |||
2039 | } | 2412 | } |
2040 | 2413 | ||
2041 | /* Block lock scheme */ | 2414 | /* Block lock scheme */ |
2042 | for (block = start; block < start + end; block++) { | 2415 | for (block = start; block < end + 1; block++) { |
2043 | /* Set block address */ | 2416 | /* Set block address */ |
2044 | value = onenand_block_address(this, block); | 2417 | value = onenand_block_address(this, block); |
2045 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); | 2418 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); |
@@ -2147,7 +2520,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) | |||
2147 | { | 2520 | { |
2148 | struct onenand_chip *this = mtd->priv; | 2521 | struct onenand_chip *this = mtd->priv; |
2149 | loff_t ofs = 0; | 2522 | loff_t ofs = 0; |
2150 | size_t len = this->chipsize; | 2523 | loff_t len = mtd->size; |
2151 | 2524 | ||
2152 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { | 2525 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { |
2153 | /* Set start block address */ | 2526 | /* Set start block address */ |
@@ -2163,12 +2536,16 @@ static void onenand_unlock_all(struct mtd_info *mtd) | |||
2163 | & ONENAND_CTRL_ONGO) | 2536 | & ONENAND_CTRL_ONGO) |
2164 | continue; | 2537 | continue; |
2165 | 2538 | ||
2539 | /* Don't check lock status */ | ||
2540 | if (this->options & ONENAND_SKIP_UNLOCK_CHECK) | ||
2541 | return; | ||
2542 | |||
2166 | /* Check lock status */ | 2543 | /* Check lock status */ |
2167 | if (onenand_check_lock_status(this)) | 2544 | if (onenand_check_lock_status(this)) |
2168 | return; | 2545 | return; |
2169 | 2546 | ||
2170 | /* Workaround for all block unlock in DDP */ | 2547 | /* Workaround for all block unlock in DDP */ |
2171 | if (ONENAND_IS_DDP(this)) { | 2548 | if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) { |
2172 | /* All blocks on another chip */ | 2549 | /* All blocks on another chip */ |
2173 | ofs = this->chipsize >> 1; | 2550 | ofs = this->chipsize >> 1; |
2174 | len = this->chipsize >> 1; | 2551 | len = this->chipsize >> 1; |
@@ -2210,7 +2587,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); | 2587 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
2211 | this->wait(mtd, FL_OTPING); | 2588 | this->wait(mtd, FL_OTPING); |
2212 | 2589 | ||
2213 | ret = onenand_read_ops_nolock(mtd, from, &ops); | 2590 | ret = ONENAND_IS_MLC(this) ? |
2591 | onenand_mlc_read_ops_nolock(mtd, from, &ops) : | ||
2592 | onenand_read_ops_nolock(mtd, from, &ops); | ||
2214 | 2593 | ||
2215 | /* Exit OTP access mode */ | 2594 | /* Exit OTP access mode */ |
2216 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 2595 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
@@ -2277,21 +2656,32 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, | |||
2277 | size_t *retlen, u_char *buf) | 2656 | size_t *retlen, u_char *buf) |
2278 | { | 2657 | { |
2279 | struct onenand_chip *this = mtd->priv; | 2658 | struct onenand_chip *this = mtd->priv; |
2280 | struct mtd_oob_ops ops = { | 2659 | struct mtd_oob_ops ops; |
2281 | .mode = MTD_OOB_PLACE, | ||
2282 | .ooblen = len, | ||
2283 | .oobbuf = buf, | ||
2284 | .ooboffs = 0, | ||
2285 | }; | ||
2286 | int ret; | 2660 | int ret; |
2287 | 2661 | ||
2288 | /* Enter OTP access mode */ | 2662 | /* Enter OTP access mode */ |
2289 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); | 2663 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
2290 | this->wait(mtd, FL_OTPING); | 2664 | this->wait(mtd, FL_OTPING); |
2291 | 2665 | ||
2292 | ret = onenand_write_oob_nolock(mtd, from, &ops); | 2666 | if (FLEXONENAND(this)) { |
2293 | 2667 | /* | |
2294 | *retlen = ops.oobretlen; | 2668 | * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of |
2669 | * main area of page 49. | ||
2670 | */ | ||
2671 | ops.len = mtd->writesize; | ||
2672 | ops.ooblen = 0; | ||
2673 | ops.datbuf = buf; | ||
2674 | ops.oobbuf = NULL; | ||
2675 | ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); | ||
2676 | *retlen = ops.retlen; | ||
2677 | } else { | ||
2678 | ops.mode = MTD_OOB_PLACE; | ||
2679 | ops.ooblen = len; | ||
2680 | ops.oobbuf = buf; | ||
2681 | ops.ooboffs = 0; | ||
2682 | ret = onenand_write_oob_nolock(mtd, from, &ops); | ||
2683 | *retlen = ops.oobretlen; | ||
2684 | } | ||
2295 | 2685 | ||
2296 | /* Exit OTP access mode */ | 2686 | /* Exit OTP access mode */ |
2297 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 2687 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
@@ -2475,27 +2865,34 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | |||
2475 | size_t len) | 2865 | size_t len) |
2476 | { | 2866 | { |
2477 | struct onenand_chip *this = mtd->priv; | 2867 | struct onenand_chip *this = mtd->priv; |
2478 | u_char *oob_buf = this->oob_buf; | 2868 | u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf; |
2479 | size_t retlen; | 2869 | size_t retlen; |
2480 | int ret; | 2870 | int ret; |
2481 | 2871 | ||
2482 | memset(oob_buf, 0xff, mtd->oobsize); | 2872 | memset(buf, 0xff, FLEXONENAND(this) ? this->writesize |
2873 | : mtd->oobsize); | ||
2483 | /* | 2874 | /* |
2484 | * Note: OTP lock operation | 2875 | * Note: OTP lock operation |
2485 | * OTP block : 0xXXFC | 2876 | * OTP block : 0xXXFC |
2486 | * 1st block : 0xXXF3 (If chip support) | 2877 | * 1st block : 0xXXF3 (If chip support) |
2487 | * Both : 0xXXF0 (If chip support) | 2878 | * Both : 0xXXF0 (If chip support) |
2488 | */ | 2879 | */ |
2489 | oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; | 2880 | if (FLEXONENAND(this)) |
2881 | buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC; | ||
2882 | else | ||
2883 | buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; | ||
2490 | 2884 | ||
2491 | /* | 2885 | /* |
2492 | * Write lock mark to 8th word of sector0 of page0 of the spare0. | 2886 | * Write lock mark to 8th word of sector0 of page0 of the spare0. |
2493 | * We write 16 bytes spare area instead of 2 bytes. | 2887 | * We write 16 bytes spare area instead of 2 bytes. |
2888 | * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of | ||
2889 | * main area of page 49. | ||
2494 | */ | 2890 | */ |
2891 | |||
2495 | from = 0; | 2892 | from = 0; |
2496 | len = 16; | 2893 | len = FLEXONENAND(this) ? mtd->writesize : 16; |
2497 | 2894 | ||
2498 | ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER); | 2895 | ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER); |
2499 | 2896 | ||
2500 | return ret ? : retlen; | 2897 | return ret ? : retlen; |
2501 | } | 2898 | } |
@@ -2542,6 +2939,14 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
2542 | break; | 2939 | break; |
2543 | } | 2940 | } |
2544 | 2941 | ||
2942 | if (ONENAND_IS_MLC(this)) | ||
2943 | this->options &= ~ONENAND_HAS_2PLANE; | ||
2944 | |||
2945 | if (FLEXONENAND(this)) { | ||
2946 | this->options &= ~ONENAND_HAS_CONT_LOCK; | ||
2947 | this->options |= ONENAND_HAS_UNLOCK_ALL; | ||
2948 | } | ||
2949 | |||
2545 | if (this->options & ONENAND_HAS_CONT_LOCK) | 2950 | if (this->options & ONENAND_HAS_CONT_LOCK) |
2546 | printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); | 2951 | printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); |
2547 | if (this->options & ONENAND_HAS_UNLOCK_ALL) | 2952 | if (this->options & ONENAND_HAS_UNLOCK_ALL) |
@@ -2559,14 +2964,16 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
2559 | */ | 2964 | */ |
2560 | static void onenand_print_device_info(int device, int version) | 2965 | static void onenand_print_device_info(int device, int version) |
2561 | { | 2966 | { |
2562 | int vcc, demuxed, ddp, density; | 2967 | int vcc, demuxed, ddp, density, flexonenand; |
2563 | 2968 | ||
2564 | vcc = device & ONENAND_DEVICE_VCC_MASK; | 2969 | vcc = device & ONENAND_DEVICE_VCC_MASK; |
2565 | demuxed = device & ONENAND_DEVICE_IS_DEMUX; | 2970 | demuxed = device & ONENAND_DEVICE_IS_DEMUX; |
2566 | ddp = device & ONENAND_DEVICE_IS_DDP; | 2971 | ddp = device & ONENAND_DEVICE_IS_DDP; |
2567 | density = onenand_get_density(device); | 2972 | density = onenand_get_density(device); |
2568 | printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", | 2973 | flexonenand = device & DEVICE_IS_FLEXONENAND; |
2569 | demuxed ? "" : "Muxed ", | 2974 | printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", |
2975 | demuxed ? "" : "Muxed ", | ||
2976 | flexonenand ? "Flex-" : "", | ||
2570 | ddp ? "(DDP)" : "", | 2977 | ddp ? "(DDP)" : "", |
2571 | (16 << density), | 2978 | (16 << density), |
2572 | vcc ? "2.65/3.3" : "1.8", | 2979 | vcc ? "2.65/3.3" : "1.8", |
@@ -2576,6 +2983,7 @@ static void onenand_print_device_info(int device, int version) | |||
2576 | 2983 | ||
2577 | static const struct onenand_manufacturers onenand_manuf_ids[] = { | 2984 | static const struct onenand_manufacturers onenand_manuf_ids[] = { |
2578 | {ONENAND_MFR_SAMSUNG, "Samsung"}, | 2985 | {ONENAND_MFR_SAMSUNG, "Samsung"}, |
2986 | {ONENAND_MFR_NUMONYX, "Numonyx"}, | ||
2579 | }; | 2987 | }; |
2580 | 2988 | ||
2581 | /** | 2989 | /** |
@@ -2605,6 +3013,261 @@ static int onenand_check_maf(int manuf) | |||
2605 | } | 3013 | } |
2606 | 3014 | ||
2607 | /** | 3015 | /** |
3016 | * flexonenand_get_boundary - Reads the SLC boundary | ||
3017 | * @param onenand_info - onenand info structure | ||
3018 | **/ | ||
3019 | static int flexonenand_get_boundary(struct mtd_info *mtd) | ||
3020 | { | ||
3021 | struct onenand_chip *this = mtd->priv; | ||
3022 | unsigned die, bdry; | ||
3023 | int ret, syscfg, locked; | ||
3024 | |||
3025 | /* Disable ECC */ | ||
3026 | syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); | ||
3027 | this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); | ||
3028 | |||
3029 | for (die = 0; die < this->dies; die++) { | ||
3030 | this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); | ||
3031 | this->wait(mtd, FL_SYNCING); | ||
3032 | |||
3033 | this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); | ||
3034 | ret = this->wait(mtd, FL_READING); | ||
3035 | |||
3036 | bdry = this->read_word(this->base + ONENAND_DATARAM); | ||
3037 | if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) | ||
3038 | locked = 0; | ||
3039 | else | ||
3040 | locked = 1; | ||
3041 | this->boundary[die] = bdry & FLEXONENAND_PI_MASK; | ||
3042 | |||
3043 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | ||
3044 | ret = this->wait(mtd, FL_RESETING); | ||
3045 | |||
3046 | printk(KERN_INFO "Die %d boundary: %d%s\n", die, | ||
3047 | this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); | ||
3048 | } | ||
3049 | |||
3050 | /* Enable ECC */ | ||
3051 | this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); | ||
3052 | return 0; | ||
3053 | } | ||
3054 | |||
3055 | /** | ||
3056 | * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info | ||
3057 | * boundary[], diesize[], mtd->size, mtd->erasesize | ||
3058 | * @param mtd - MTD device structure | ||
3059 | */ | ||
3060 | static void flexonenand_get_size(struct mtd_info *mtd) | ||
3061 | { | ||
3062 | struct onenand_chip *this = mtd->priv; | ||
3063 | int die, i, eraseshift, density; | ||
3064 | int blksperdie, maxbdry; | ||
3065 | loff_t ofs; | ||
3066 | |||
3067 | density = onenand_get_density(this->device_id); | ||
3068 | blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift); | ||
3069 | blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; | ||
3070 | maxbdry = blksperdie - 1; | ||
3071 | eraseshift = this->erase_shift - 1; | ||
3072 | |||
3073 | mtd->numeraseregions = this->dies << 1; | ||
3074 | |||
3075 | /* This fills up the device boundary */ | ||
3076 | flexonenand_get_boundary(mtd); | ||
3077 | die = ofs = 0; | ||
3078 | i = -1; | ||
3079 | for (; die < this->dies; die++) { | ||
3080 | if (!die || this->boundary[die-1] != maxbdry) { | ||
3081 | i++; | ||
3082 | mtd->eraseregions[i].offset = ofs; | ||
3083 | mtd->eraseregions[i].erasesize = 1 << eraseshift; | ||
3084 | mtd->eraseregions[i].numblocks = | ||
3085 | this->boundary[die] + 1; | ||
3086 | ofs += mtd->eraseregions[i].numblocks << eraseshift; | ||
3087 | eraseshift++; | ||
3088 | } else { | ||
3089 | mtd->numeraseregions -= 1; | ||
3090 | mtd->eraseregions[i].numblocks += | ||
3091 | this->boundary[die] + 1; | ||
3092 | ofs += (this->boundary[die] + 1) << (eraseshift - 1); | ||
3093 | } | ||
3094 | if (this->boundary[die] != maxbdry) { | ||
3095 | i++; | ||
3096 | mtd->eraseregions[i].offset = ofs; | ||
3097 | mtd->eraseregions[i].erasesize = 1 << eraseshift; | ||
3098 | mtd->eraseregions[i].numblocks = maxbdry ^ | ||
3099 | this->boundary[die]; | ||
3100 | ofs += mtd->eraseregions[i].numblocks << eraseshift; | ||
3101 | eraseshift--; | ||
3102 | } else | ||
3103 | mtd->numeraseregions -= 1; | ||
3104 | } | ||
3105 | |||
3106 | /* Expose MLC erase size except when all blocks are SLC */ | ||
3107 | mtd->erasesize = 1 << this->erase_shift; | ||
3108 | if (mtd->numeraseregions == 1) | ||
3109 | mtd->erasesize >>= 1; | ||
3110 | |||
3111 | printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); | ||
3112 | for (i = 0; i < mtd->numeraseregions; i++) | ||
3113 | printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x," | ||
3114 | " numblocks: %04u]\n", | ||
3115 | (unsigned int) mtd->eraseregions[i].offset, | ||
3116 | mtd->eraseregions[i].erasesize, | ||
3117 | mtd->eraseregions[i].numblocks); | ||
3118 | |||
3119 | for (die = 0, mtd->size = 0; die < this->dies; die++) { | ||
3120 | this->diesize[die] = (loff_t)blksperdie << this->erase_shift; | ||
3121 | this->diesize[die] -= (loff_t)(this->boundary[die] + 1) | ||
3122 | << (this->erase_shift - 1); | ||
3123 | mtd->size += this->diesize[die]; | ||
3124 | } | ||
3125 | } | ||
3126 | |||
3127 | /** | ||
3128 | * flexonenand_check_blocks_erased - Check if blocks are erased | ||
3129 | * @param mtd_info - mtd info structure | ||
3130 | * @param start - first erase block to check | ||
3131 | * @param end - last erase block to check | ||
3132 | * | ||
3133 | * Converting an unerased block from MLC to SLC | ||
3134 | * causes byte values to change. Since both data and its ECC | ||
3135 | * have changed, reads on the block give uncorrectable error. | ||
3136 | * This might lead to the block being detected as bad. | ||
3137 | * | ||
3138 | * Avoid this by ensuring that the block to be converted is | ||
3139 | * erased. | ||
3140 | */ | ||
3141 | static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end) | ||
3142 | { | ||
3143 | struct onenand_chip *this = mtd->priv; | ||
3144 | int i, ret; | ||
3145 | int block; | ||
3146 | struct mtd_oob_ops ops = { | ||
3147 | .mode = MTD_OOB_PLACE, | ||
3148 | .ooboffs = 0, | ||
3149 | .ooblen = mtd->oobsize, | ||
3150 | .datbuf = NULL, | ||
3151 | .oobbuf = this->oob_buf, | ||
3152 | }; | ||
3153 | loff_t addr; | ||
3154 | |||
3155 | printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end); | ||
3156 | |||
3157 | for (block = start; block <= end; block++) { | ||
3158 | addr = flexonenand_addr(this, block); | ||
3159 | if (onenand_block_isbad_nolock(mtd, addr, 0)) | ||
3160 | continue; | ||
3161 | |||
3162 | /* | ||
3163 | * Since main area write results in ECC write to spare, | ||
3164 | * it is sufficient to check only ECC bytes for change. | ||
3165 | */ | ||
3166 | ret = onenand_read_oob_nolock(mtd, addr, &ops); | ||
3167 | if (ret) | ||
3168 | return ret; | ||
3169 | |||
3170 | for (i = 0; i < mtd->oobsize; i++) | ||
3171 | if (this->oob_buf[i] != 0xff) | ||
3172 | break; | ||
3173 | |||
3174 | if (i != mtd->oobsize) { | ||
3175 | printk(KERN_WARNING "Block %d not erased.\n", block); | ||
3176 | return 1; | ||
3177 | } | ||
3178 | } | ||
3179 | |||
3180 | return 0; | ||
3181 | } | ||
3182 | |||
3183 | /** | ||
3184 | * flexonenand_set_boundary - Writes the SLC boundary | ||
3185 | * @param mtd - mtd info structure | ||
3186 | */ | ||
3187 | int flexonenand_set_boundary(struct mtd_info *mtd, int die, | ||
3188 | int boundary, int lock) | ||
3189 | { | ||
3190 | struct onenand_chip *this = mtd->priv; | ||
3191 | int ret, density, blksperdie, old, new, thisboundary; | ||
3192 | loff_t addr; | ||
3193 | |||
3194 | /* Change only once for SDP Flex-OneNAND */ | ||
3195 | if (die && (!ONENAND_IS_DDP(this))) | ||
3196 | return 0; | ||
3197 | |||
3198 | /* boundary value of -1 indicates no required change */ | ||
3199 | if (boundary < 0 || boundary == this->boundary[die]) | ||
3200 | return 0; | ||
3201 | |||
3202 | density = onenand_get_density(this->device_id); | ||
3203 | blksperdie = ((16 << density) << 20) >> this->erase_shift; | ||
3204 | blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; | ||
3205 | |||
3206 | if (boundary >= blksperdie) { | ||
3207 | printk(KERN_ERR "flexonenand_set_boundary: Invalid boundary value. " | ||
3208 | "Boundary not changed.\n"); | ||
3209 | return -EINVAL; | ||
3210 | } | ||
3211 | |||
3212 | /* Check if converting blocks are erased */ | ||
3213 | old = this->boundary[die] + (die * this->density_mask); | ||
3214 | new = boundary + (die * this->density_mask); | ||
3215 | ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new)); | ||
3216 | if (ret) { | ||
3217 | printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n"); | ||
3218 | return ret; | ||
3219 | } | ||
3220 | |||
3221 | this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); | ||
3222 | this->wait(mtd, FL_SYNCING); | ||
3223 | |||
3224 | /* Check is boundary is locked */ | ||
3225 | this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); | ||
3226 | ret = this->wait(mtd, FL_READING); | ||
3227 | |||
3228 | thisboundary = this->read_word(this->base + ONENAND_DATARAM); | ||
3229 | if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { | ||
3230 | printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n"); | ||
3231 | ret = 1; | ||
3232 | goto out; | ||
3233 | } | ||
3234 | |||
3235 | printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n", | ||
3236 | die, boundary, lock ? "(Locked)" : "(Unlocked)"); | ||
3237 | |||
3238 | addr = die ? this->diesize[0] : 0; | ||
3239 | |||
3240 | boundary &= FLEXONENAND_PI_MASK; | ||
3241 | boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); | ||
3242 | |||
3243 | this->command(mtd, ONENAND_CMD_ERASE, addr, 0); | ||
3244 | ret = this->wait(mtd, FL_ERASING); | ||
3245 | if (ret) { | ||
3246 | printk(KERN_ERR "flexonenand_set_boundary: Failed PI erase for Die %d\n", die); | ||
3247 | goto out; | ||
3248 | } | ||
3249 | |||
3250 | this->write_word(boundary, this->base + ONENAND_DATARAM); | ||
3251 | this->command(mtd, ONENAND_CMD_PROG, addr, 0); | ||
3252 | ret = this->wait(mtd, FL_WRITING); | ||
3253 | if (ret) { | ||
3254 | printk(KERN_ERR "flexonenand_set_boundary: Failed PI write for Die %d\n", die); | ||
3255 | goto out; | ||
3256 | } | ||
3257 | |||
3258 | this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0); | ||
3259 | ret = this->wait(mtd, FL_WRITING); | ||
3260 | out: | ||
3261 | this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); | ||
3262 | this->wait(mtd, FL_RESETING); | ||
3263 | if (!ret) | ||
3264 | /* Recalculate device size on boundary change*/ | ||
3265 | flexonenand_get_size(mtd); | ||
3266 | |||
3267 | return ret; | ||
3268 | } | ||
3269 | |||
3270 | /** | ||
2608 | * onenand_probe - [OneNAND Interface] Probe the OneNAND device | 3271 | * onenand_probe - [OneNAND Interface] Probe the OneNAND device |
2609 | * @param mtd MTD device structure | 3272 | * @param mtd MTD device structure |
2610 | * | 3273 | * |
@@ -2621,7 +3284,7 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2621 | /* Save system configuration 1 */ | 3284 | /* Save system configuration 1 */ |
2622 | syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); | 3285 | syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); |
2623 | /* Clear Sync. Burst Read mode to read BootRAM */ | 3286 | /* Clear Sync. Burst Read mode to read BootRAM */ |
2624 | this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1); | 3287 | this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE), this->base + ONENAND_REG_SYS_CFG1); |
2625 | 3288 | ||
2626 | /* Send the command for reading device ID from BootRAM */ | 3289 | /* Send the command for reading device ID from BootRAM */ |
2627 | this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); | 3290 | this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); |
@@ -2646,6 +3309,7 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2646 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); | 3309 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); |
2647 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); | 3310 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); |
2648 | ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); | 3311 | ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); |
3312 | this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); | ||
2649 | 3313 | ||
2650 | /* Check OneNAND device */ | 3314 | /* Check OneNAND device */ |
2651 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) | 3315 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) |
@@ -2657,29 +3321,55 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2657 | this->version_id = ver_id; | 3321 | this->version_id = ver_id; |
2658 | 3322 | ||
2659 | density = onenand_get_density(dev_id); | 3323 | density = onenand_get_density(dev_id); |
3324 | if (FLEXONENAND(this)) { | ||
3325 | this->dies = ONENAND_IS_DDP(this) ? 2 : 1; | ||
3326 | /* Maximum possible erase regions */ | ||
3327 | mtd->numeraseregions = this->dies << 1; | ||
3328 | mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info) | ||
3329 | * (this->dies << 1), GFP_KERNEL); | ||
3330 | if (!mtd->eraseregions) | ||
3331 | return -ENOMEM; | ||
3332 | } | ||
3333 | |||
3334 | /* | ||
3335 | * For Flex-OneNAND, chipsize represents maximum possible device size. | ||
3336 | * mtd->size represents the actual device size. | ||
3337 | */ | ||
2660 | this->chipsize = (16 << density) << 20; | 3338 | this->chipsize = (16 << density) << 20; |
2661 | /* Set density mask. it is used for DDP */ | ||
2662 | if (ONENAND_IS_DDP(this)) | ||
2663 | this->density_mask = (1 << (density + 6)); | ||
2664 | else | ||
2665 | this->density_mask = 0; | ||
2666 | 3339 | ||
2667 | /* OneNAND page size & block size */ | 3340 | /* OneNAND page size & block size */ |
2668 | /* The data buffer size is equal to page size */ | 3341 | /* The data buffer size is equal to page size */ |
2669 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); | 3342 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); |
3343 | /* We use the full BufferRAM */ | ||
3344 | if (ONENAND_IS_MLC(this)) | ||
3345 | mtd->writesize <<= 1; | ||
3346 | |||
2670 | mtd->oobsize = mtd->writesize >> 5; | 3347 | mtd->oobsize = mtd->writesize >> 5; |
2671 | /* Pages per a block are always 64 in OneNAND */ | 3348 | /* Pages per a block are always 64 in OneNAND */ |
2672 | mtd->erasesize = mtd->writesize << 6; | 3349 | mtd->erasesize = mtd->writesize << 6; |
3350 | /* | ||
3351 | * Flex-OneNAND SLC area has 64 pages per block. | ||
3352 | * Flex-OneNAND MLC area has 128 pages per block. | ||
3353 | * Expose MLC erase size to find erase_shift and page_mask. | ||
3354 | */ | ||
3355 | if (FLEXONENAND(this)) | ||
3356 | mtd->erasesize <<= 1; | ||
2673 | 3357 | ||
2674 | this->erase_shift = ffs(mtd->erasesize) - 1; | 3358 | this->erase_shift = ffs(mtd->erasesize) - 1; |
2675 | this->page_shift = ffs(mtd->writesize) - 1; | 3359 | this->page_shift = ffs(mtd->writesize) - 1; |
2676 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; | 3360 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; |
3361 | /* Set density mask. it is used for DDP */ | ||
3362 | if (ONENAND_IS_DDP(this)) | ||
3363 | this->density_mask = this->chipsize >> (this->erase_shift + 1); | ||
2677 | /* It's real page size */ | 3364 | /* It's real page size */ |
2678 | this->writesize = mtd->writesize; | 3365 | this->writesize = mtd->writesize; |
2679 | 3366 | ||
2680 | /* REVIST: Multichip handling */ | 3367 | /* REVIST: Multichip handling */ |
2681 | 3368 | ||
2682 | mtd->size = this->chipsize; | 3369 | if (FLEXONENAND(this)) |
3370 | flexonenand_get_size(mtd); | ||
3371 | else | ||
3372 | mtd->size = this->chipsize; | ||
2683 | 3373 | ||
2684 | /* Check OneNAND features */ | 3374 | /* Check OneNAND features */ |
2685 | onenand_check_features(mtd); | 3375 | onenand_check_features(mtd); |
@@ -2734,7 +3424,7 @@ static void onenand_resume(struct mtd_info *mtd) | |||
2734 | */ | 3424 | */ |
2735 | int onenand_scan(struct mtd_info *mtd, int maxchips) | 3425 | int onenand_scan(struct mtd_info *mtd, int maxchips) |
2736 | { | 3426 | { |
2737 | int i; | 3427 | int i, ret; |
2738 | struct onenand_chip *this = mtd->priv; | 3428 | struct onenand_chip *this = mtd->priv; |
2739 | 3429 | ||
2740 | if (!this->read_word) | 3430 | if (!this->read_word) |
@@ -2746,6 +3436,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2746 | this->command = onenand_command; | 3436 | this->command = onenand_command; |
2747 | if (!this->wait) | 3437 | if (!this->wait) |
2748 | onenand_setup_wait(mtd); | 3438 | onenand_setup_wait(mtd); |
3439 | if (!this->bbt_wait) | ||
3440 | this->bbt_wait = onenand_bbt_wait; | ||
3441 | if (!this->unlock_all) | ||
3442 | this->unlock_all = onenand_unlock_all; | ||
2749 | 3443 | ||
2750 | if (!this->read_bufferram) | 3444 | if (!this->read_bufferram) |
2751 | this->read_bufferram = onenand_read_bufferram; | 3445 | this->read_bufferram = onenand_read_bufferram; |
@@ -2796,6 +3490,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2796 | * Allow subpage writes up to oobsize. | 3490 | * Allow subpage writes up to oobsize. |
2797 | */ | 3491 | */ |
2798 | switch (mtd->oobsize) { | 3492 | switch (mtd->oobsize) { |
3493 | case 128: | ||
3494 | this->ecclayout = &onenand_oob_128; | ||
3495 | mtd->subpage_sft = 0; | ||
3496 | break; | ||
2799 | case 64: | 3497 | case 64: |
2800 | this->ecclayout = &onenand_oob_64; | 3498 | this->ecclayout = &onenand_oob_64; |
2801 | mtd->subpage_sft = 2; | 3499 | mtd->subpage_sft = 2; |
@@ -2859,9 +3557,18 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2859 | mtd->owner = THIS_MODULE; | 3557 | mtd->owner = THIS_MODULE; |
2860 | 3558 | ||
2861 | /* Unlock whole block */ | 3559 | /* Unlock whole block */ |
2862 | onenand_unlock_all(mtd); | 3560 | this->unlock_all(mtd); |
3561 | |||
3562 | ret = this->scan_bbt(mtd); | ||
3563 | if ((!FLEXONENAND(this)) || ret) | ||
3564 | return ret; | ||
2863 | 3565 | ||
2864 | return this->scan_bbt(mtd); | 3566 | /* Change Flex-OneNAND boundaries if required */ |
3567 | for (i = 0; i < MAX_DIES; i++) | ||
3568 | flexonenand_set_boundary(mtd, i, flex_bdry[2 * i], | ||
3569 | flex_bdry[(2 * i) + 1]); | ||
3570 | |||
3571 | return 0; | ||
2865 | } | 3572 | } |
2866 | 3573 | ||
2867 | /** | 3574 | /** |
@@ -2890,6 +3597,7 @@ void onenand_release(struct mtd_info *mtd) | |||
2890 | kfree(this->page_buf); | 3597 | kfree(this->page_buf); |
2891 | if (this->options & ONENAND_OOBBUF_ALLOC) | 3598 | if (this->options & ONENAND_OOBBUF_ALLOC) |
2892 | kfree(this->oob_buf); | 3599 | kfree(this->oob_buf); |
3600 | kfree(mtd->eraseregions); | ||
2893 | } | 3601 | } |
2894 | 3602 | ||
2895 | EXPORT_SYMBOL_GPL(onenand_scan); | 3603 | 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 */ |