diff options
| -rw-r--r-- | Documentation/kernel-parameters.txt | 10 | ||||
| -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 | ||||
| -rw-r--r-- | include/linux/mtd/onenand.h | 18 | ||||
| -rw-r--r-- | include/linux/mtd/onenand_regs.h | 20 |
6 files changed, 913 insertions, 87 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e87bdbfbcc75..12df135f8af9 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -1380,6 +1380,16 @@ and is between 256 and 4096 characters. It is defined in the file | |||
| 1380 | mtdparts= [MTD] | 1380 | mtdparts= [MTD] |
| 1381 | See drivers/mtd/cmdlinepart.c. | 1381 | See drivers/mtd/cmdlinepart.c. |
| 1382 | 1382 | ||
| 1383 | onenand.bdry= [HW,MTD] Flex-OneNAND Boundary Configuration | ||
| 1384 | |||
| 1385 | Format: [die0_boundary][,die0_lock][,die1_boundary][,die1_lock] | ||
| 1386 | |||
| 1387 | boundary - index of last SLC block on Flex-OneNAND. | ||
| 1388 | The remaining blocks are configured as MLC blocks. | ||
| 1389 | lock - Configure if Flex-OneNAND boundary should be locked. | ||
| 1390 | Once locked, the boundary cannot be changed. | ||
| 1391 | 1 indicates lock status, 0 indicates unlock status. | ||
| 1392 | |||
| 1383 | mtdset= [ARM] | 1393 | mtdset= [ARM] |
| 1384 | ARM/S3C2412 JIVE boot control | 1394 | ARM/S3C2412 JIVE boot control |
| 1385 | 1395 | ||
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 */ |
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 0fa3ac4ad576..9aab82c1c743 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/mtd/onenand_regs.h> | 17 | #include <linux/mtd/onenand_regs.h> |
| 18 | #include <linux/mtd/bbm.h> | 18 | #include <linux/mtd/bbm.h> |
| 19 | 19 | ||
| 20 | #define MAX_DIES 2 | ||
| 20 | #define MAX_BUFFERRAM 2 | 21 | #define MAX_BUFFERRAM 2 |
| 21 | 22 | ||
| 22 | /* Scan and identify a OneNAND device */ | 23 | /* Scan and identify a OneNAND device */ |
| @@ -51,7 +52,12 @@ struct onenand_bufferram { | |||
| 51 | /** | 52 | /** |
| 52 | * struct onenand_chip - OneNAND Private Flash Chip Data | 53 | * struct onenand_chip - OneNAND Private Flash Chip Data |
| 53 | * @base: [BOARDSPECIFIC] address to access OneNAND | 54 | * @base: [BOARDSPECIFIC] address to access OneNAND |
| 55 | * @dies: [INTERN][FLEX-ONENAND] number of dies on chip | ||
| 56 | * @boundary: [INTERN][FLEX-ONENAND] Boundary of the dies | ||
| 57 | * @diesize: [INTERN][FLEX-ONENAND] Size of the dies | ||
| 54 | * @chipsize: [INTERN] the size of one chip for multichip arrays | 58 | * @chipsize: [INTERN] the size of one chip for multichip arrays |
| 59 | * FIXME For Flex-OneNAND, chipsize holds maximum possible | ||
| 60 | * device size ie when all blocks are considered MLC | ||
| 55 | * @device_id: [INTERN] device ID | 61 | * @device_id: [INTERN] device ID |
| 56 | * @density_mask: chip density, used for DDP devices | 62 | * @density_mask: chip density, used for DDP devices |
| 57 | * @verstion_id: [INTERN] version ID | 63 | * @verstion_id: [INTERN] version ID |
| @@ -92,9 +98,13 @@ struct onenand_bufferram { | |||
| 92 | */ | 98 | */ |
| 93 | struct onenand_chip { | 99 | struct onenand_chip { |
| 94 | void __iomem *base; | 100 | void __iomem *base; |
| 101 | unsigned dies; | ||
| 102 | unsigned boundary[MAX_DIES]; | ||
| 103 | loff_t diesize[MAX_DIES]; | ||
| 95 | unsigned int chipsize; | 104 | unsigned int chipsize; |
| 96 | unsigned int device_id; | 105 | unsigned int device_id; |
| 97 | unsigned int version_id; | 106 | unsigned int version_id; |
| 107 | unsigned int technology; | ||
| 98 | unsigned int density_mask; | 108 | unsigned int density_mask; |
| 99 | unsigned int options; | 109 | unsigned int options; |
| 100 | 110 | ||
| @@ -145,6 +155,8 @@ struct onenand_chip { | |||
| 145 | #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) | 155 | #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) |
| 146 | #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1) | 156 | #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1) |
| 147 | 157 | ||
| 158 | #define FLEXONENAND(this) \ | ||
| 159 | (this->device_id & DEVICE_IS_FLEXONENAND) | ||
| 148 | #define ONENAND_GET_SYS_CFG1(this) \ | 160 | #define ONENAND_GET_SYS_CFG1(this) \ |
| 149 | (this->read_word(this->base + ONENAND_REG_SYS_CFG1)) | 161 | (this->read_word(this->base + ONENAND_REG_SYS_CFG1)) |
| 150 | #define ONENAND_SET_SYS_CFG1(v, this) \ | 162 | #define ONENAND_SET_SYS_CFG1(v, this) \ |
| @@ -153,6 +165,9 @@ struct onenand_chip { | |||
| 153 | #define ONENAND_IS_DDP(this) \ | 165 | #define ONENAND_IS_DDP(this) \ |
| 154 | (this->device_id & ONENAND_DEVICE_IS_DDP) | 166 | (this->device_id & ONENAND_DEVICE_IS_DDP) |
| 155 | 167 | ||
| 168 | #define ONENAND_IS_MLC(this) \ | ||
| 169 | (this->technology & ONENAND_TECHNOLOGY_IS_MLC) | ||
| 170 | |||
| 156 | #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM | 171 | #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM |
| 157 | #define ONENAND_IS_2PLANE(this) \ | 172 | #define ONENAND_IS_2PLANE(this) \ |
| 158 | (this->options & ONENAND_HAS_2PLANE) | 173 | (this->options & ONENAND_HAS_2PLANE) |
| @@ -190,5 +205,8 @@ struct onenand_manufacturers { | |||
| 190 | 205 | ||
| 191 | int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | 206 | int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, |
| 192 | struct mtd_oob_ops *ops); | 207 | struct mtd_oob_ops *ops); |
| 208 | unsigned onenand_block(struct onenand_chip *this, loff_t addr); | ||
| 209 | loff_t onenand_addr(struct onenand_chip *this, int block); | ||
| 210 | int flexonenand_region(struct mtd_info *mtd, loff_t addr); | ||
| 193 | 211 | ||
| 194 | #endif /* __LINUX_MTD_ONENAND_H */ | 212 | #endif /* __LINUX_MTD_ONENAND_H */ |
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 0c6bbe28f38c..86a6bbef6465 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h | |||
| @@ -67,6 +67,9 @@ | |||
| 67 | /* | 67 | /* |
| 68 | * Device ID Register F001h (R) | 68 | * Device ID Register F001h (R) |
| 69 | */ | 69 | */ |
| 70 | #define DEVICE_IS_FLEXONENAND (1 << 9) | ||
| 71 | #define FLEXONENAND_PI_MASK (0x3ff) | ||
| 72 | #define FLEXONENAND_PI_UNLOCK_SHIFT (14) | ||
| 70 | #define ONENAND_DEVICE_DENSITY_MASK (0xf) | 73 | #define ONENAND_DEVICE_DENSITY_MASK (0xf) |
| 71 | #define ONENAND_DEVICE_DENSITY_SHIFT (4) | 74 | #define ONENAND_DEVICE_DENSITY_SHIFT (4) |
| 72 | #define ONENAND_DEVICE_IS_DDP (1 << 3) | 75 | #define ONENAND_DEVICE_IS_DDP (1 << 3) |
| @@ -84,6 +87,11 @@ | |||
| 84 | #define ONENAND_VERSION_PROCESS_SHIFT (8) | 87 | #define ONENAND_VERSION_PROCESS_SHIFT (8) |
| 85 | 88 | ||
| 86 | /* | 89 | /* |
| 90 | * Technology Register F006h (R) | ||
| 91 | */ | ||
| 92 | #define ONENAND_TECHNOLOGY_IS_MLC (1 << 0) | ||
| 93 | |||
| 94 | /* | ||
| 87 | * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W) | 95 | * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W) |
| 88 | */ | 96 | */ |
| 89 | #define ONENAND_DDP_SHIFT (15) | 97 | #define ONENAND_DDP_SHIFT (15) |
| @@ -93,7 +101,8 @@ | |||
| 93 | /* | 101 | /* |
| 94 | * Start Address 8 F107h (R/W) | 102 | * Start Address 8 F107h (R/W) |
| 95 | */ | 103 | */ |
| 96 | #define ONENAND_FPA_MASK (0x3f) | 104 | /* Note: It's actually 0x3f in case of SLC */ |
| 105 | #define ONENAND_FPA_MASK (0x7f) | ||
| 97 | #define ONENAND_FPA_SHIFT (2) | 106 | #define ONENAND_FPA_SHIFT (2) |
| 98 | #define ONENAND_FSA_MASK (0x03) | 107 | #define ONENAND_FSA_MASK (0x03) |
| 99 | 108 | ||
| @@ -105,7 +114,8 @@ | |||
| 105 | #define ONENAND_BSA_BOOTRAM (0 << 2) | 114 | #define ONENAND_BSA_BOOTRAM (0 << 2) |
| 106 | #define ONENAND_BSA_DATARAM0 (2 << 2) | 115 | #define ONENAND_BSA_DATARAM0 (2 << 2) |
| 107 | #define ONENAND_BSA_DATARAM1 (3 << 2) | 116 | #define ONENAND_BSA_DATARAM1 (3 << 2) |
| 108 | #define ONENAND_BSC_MASK (0x03) | 117 | /* Note: It's actually 0x03 in case of SLC */ |
| 118 | #define ONENAND_BSC_MASK (0x07) | ||
| 109 | 119 | ||
| 110 | /* | 120 | /* |
| 111 | * Command Register F220h (R/W) | 121 | * Command Register F220h (R/W) |
| @@ -124,9 +134,13 @@ | |||
| 124 | #define ONENAND_CMD_RESET (0xF0) | 134 | #define ONENAND_CMD_RESET (0xF0) |
| 125 | #define ONENAND_CMD_OTP_ACCESS (0x65) | 135 | #define ONENAND_CMD_OTP_ACCESS (0x65) |
| 126 | #define ONENAND_CMD_READID (0x90) | 136 | #define ONENAND_CMD_READID (0x90) |
| 137 | #define FLEXONENAND_CMD_PI_UPDATE (0x05) | ||
| 138 | #define FLEXONENAND_CMD_PI_ACCESS (0x66) | ||
| 139 | #define FLEXONENAND_CMD_RECOVER_LSB (0x05) | ||
| 127 | 140 | ||
| 128 | /* NOTE: Those are not *REAL* commands */ | 141 | /* NOTE: Those are not *REAL* commands */ |
| 129 | #define ONENAND_CMD_BUFFERRAM (0x1978) | 142 | #define ONENAND_CMD_BUFFERRAM (0x1978) |
| 143 | #define FLEXONENAND_CMD_READ_PI (0x1985) | ||
| 130 | 144 | ||
| 131 | /* | 145 | /* |
| 132 | * System Configuration 1 Register F221h (R, R/W) | 146 | * System Configuration 1 Register F221h (R, R/W) |
| @@ -192,10 +206,12 @@ | |||
| 192 | #define ONENAND_ECC_1BIT_ALL (0x5555) | 206 | #define ONENAND_ECC_1BIT_ALL (0x5555) |
| 193 | #define ONENAND_ECC_2BIT (1 << 1) | 207 | #define ONENAND_ECC_2BIT (1 << 1) |
| 194 | #define ONENAND_ECC_2BIT_ALL (0xAAAA) | 208 | #define ONENAND_ECC_2BIT_ALL (0xAAAA) |
| 209 | #define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010) | ||
| 195 | 210 | ||
| 196 | /* | 211 | /* |
| 197 | * One-Time Programmable (OTP) | 212 | * One-Time Programmable (OTP) |
| 198 | */ | 213 | */ |
| 214 | #define FLEXONENAND_OTP_LOCK_OFFSET (2048) | ||
| 199 | #define ONENAND_OTP_LOCK_OFFSET (14) | 215 | #define ONENAND_OTP_LOCK_OFFSET (14) |
| 200 | 216 | ||
| 201 | #endif /* __ONENAND_REG_H */ | 217 | #endif /* __ONENAND_REG_H */ |
