diff options
49 files changed, 3133 insertions, 827 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd new file mode 100644 index 000000000000..4d55a1888981 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-mtd | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | What: /sys/class/mtd/ | ||
| 2 | Date: April 2009 | ||
| 3 | KernelVersion: 2.6.29 | ||
| 4 | Contact: linux-mtd@lists.infradead.org | ||
| 5 | Description: | ||
| 6 | The mtd/ class subdirectory belongs to the MTD subsystem | ||
| 7 | (MTD core). | ||
| 8 | |||
| 9 | What: /sys/class/mtd/mtdX/ | ||
| 10 | Date: April 2009 | ||
| 11 | KernelVersion: 2.6.29 | ||
| 12 | Contact: linux-mtd@lists.infradead.org | ||
| 13 | Description: | ||
| 14 | The /sys/class/mtd/mtd{0,1,2,3,...} directories correspond | ||
| 15 | to each /dev/mtdX character device. These may represent | ||
| 16 | physical/simulated flash devices, partitions on a flash | ||
| 17 | device, or concatenated flash devices. They exist regardless | ||
| 18 | of whether CONFIG_MTD_CHAR is actually enabled. | ||
| 19 | |||
| 20 | What: /sys/class/mtd/mtdXro/ | ||
| 21 | Date: April 2009 | ||
| 22 | KernelVersion: 2.6.29 | ||
| 23 | Contact: linux-mtd@lists.infradead.org | ||
| 24 | Description: | ||
| 25 | These directories provide the corresponding read-only device | ||
| 26 | nodes for /sys/class/mtd/mtdX/ . They are only created | ||
| 27 | (for the benefit of udev) if CONFIG_MTD_CHAR is enabled. | ||
| 28 | |||
| 29 | What: /sys/class/mtd/mtdX/dev | ||
| 30 | Date: April 2009 | ||
| 31 | KernelVersion: 2.6.29 | ||
| 32 | Contact: linux-mtd@lists.infradead.org | ||
| 33 | Description: | ||
| 34 | Major and minor numbers of the character device corresponding | ||
| 35 | to this MTD device (in <major>:<minor> format). This is the | ||
| 36 | read-write device so <minor> will be even. | ||
| 37 | |||
| 38 | What: /sys/class/mtd/mtdXro/dev | ||
| 39 | Date: April 2009 | ||
| 40 | KernelVersion: 2.6.29 | ||
| 41 | Contact: linux-mtd@lists.infradead.org | ||
| 42 | Description: | ||
| 43 | Major and minor numbers of the character device corresponding | ||
| 44 | to the read-only variant of thie MTD device (in | ||
| 45 | <major>:<minor> format). In this case <minor> will be odd. | ||
| 46 | |||
| 47 | What: /sys/class/mtd/mtdX/erasesize | ||
| 48 | Date: April 2009 | ||
| 49 | KernelVersion: 2.6.29 | ||
| 50 | Contact: linux-mtd@lists.infradead.org | ||
| 51 | Description: | ||
| 52 | "Major" erase size for the device. If numeraseregions is | ||
| 53 | zero, this is the eraseblock size for the entire device. | ||
| 54 | Otherwise, the MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls | ||
| 55 | can be used to determine the actual eraseblock layout. | ||
| 56 | |||
| 57 | What: /sys/class/mtd/mtdX/flags | ||
| 58 | Date: April 2009 | ||
| 59 | KernelVersion: 2.6.29 | ||
| 60 | Contact: linux-mtd@lists.infradead.org | ||
| 61 | Description: | ||
| 62 | A hexadecimal value representing the device flags, ORed | ||
| 63 | together: | ||
| 64 | |||
| 65 | 0x0400: MTD_WRITEABLE - device is writable | ||
| 66 | 0x0800: MTD_BIT_WRITEABLE - single bits can be flipped | ||
| 67 | 0x1000: MTD_NO_ERASE - no erase necessary | ||
| 68 | 0x2000: MTD_POWERUP_LOCK - always locked after reset | ||
| 69 | |||
| 70 | What: /sys/class/mtd/mtdX/name | ||
| 71 | Date: April 2009 | ||
| 72 | KernelVersion: 2.6.29 | ||
| 73 | Contact: linux-mtd@lists.infradead.org | ||
| 74 | Description: | ||
| 75 | A human-readable ASCII name for the device or partition. | ||
| 76 | This will match the name in /proc/mtd . | ||
| 77 | |||
| 78 | What: /sys/class/mtd/mtdX/numeraseregions | ||
| 79 | Date: April 2009 | ||
| 80 | KernelVersion: 2.6.29 | ||
| 81 | Contact: linux-mtd@lists.infradead.org | ||
| 82 | Description: | ||
| 83 | For devices that have variable eraseblock sizes, this | ||
| 84 | provides the total number of erase regions. Otherwise, | ||
| 85 | it will read back as zero. | ||
| 86 | |||
| 87 | What: /sys/class/mtd/mtdX/oobsize | ||
| 88 | Date: April 2009 | ||
| 89 | KernelVersion: 2.6.29 | ||
| 90 | Contact: linux-mtd@lists.infradead.org | ||
| 91 | Description: | ||
| 92 | Number of OOB bytes per page. | ||
| 93 | |||
| 94 | What: /sys/class/mtd/mtdX/size | ||
| 95 | Date: April 2009 | ||
| 96 | KernelVersion: 2.6.29 | ||
| 97 | Contact: linux-mtd@lists.infradead.org | ||
| 98 | Description: | ||
| 99 | Total size of the device/partition, in bytes. | ||
| 100 | |||
| 101 | What: /sys/class/mtd/mtdX/type | ||
| 102 | Date: April 2009 | ||
| 103 | KernelVersion: 2.6.29 | ||
| 104 | Contact: linux-mtd@lists.infradead.org | ||
| 105 | Description: | ||
| 106 | One of the following ASCII strings, representing the device | ||
| 107 | type: | ||
| 108 | |||
| 109 | absent, ram, rom, nor, nand, dataflash, ubi, unknown | ||
| 110 | |||
| 111 | What: /sys/class/mtd/mtdX/writesize | ||
| 112 | Date: April 2009 | ||
| 113 | KernelVersion: 2.6.29 | ||
| 114 | Contact: linux-mtd@lists.infradead.org | ||
| 115 | Description: | ||
| 116 | Minimal writable flash unit size. This will always be | ||
| 117 | a positive integer. | ||
| 118 | |||
| 119 | In the case of NOR flash it is 1 (even though individual | ||
| 120 | bits can be cleared). | ||
| 121 | |||
| 122 | In the case of NAND flash it is one NAND page (or a | ||
| 123 | half page, or a quarter page). | ||
| 124 | |||
| 125 | In the case of ECC NOR, it is the ECC block size. | ||
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 5092a2be83c5..54ebf100e4e0 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -1431,6 +1431,16 @@ and is between 256 and 4096 characters. It is defined in the file | |||
| 1431 | mtdparts= [MTD] | 1431 | mtdparts= [MTD] |
| 1432 | See drivers/mtd/cmdlinepart.c. | 1432 | See drivers/mtd/cmdlinepart.c. |
| 1433 | 1433 | ||
| 1434 | onenand.bdry= [HW,MTD] Flex-OneNAND Boundary Configuration | ||
| 1435 | |||
| 1436 | Format: [die0_boundary][,die0_lock][,die1_boundary][,die1_lock] | ||
| 1437 | |||
| 1438 | boundary - index of last SLC block on Flex-OneNAND. | ||
| 1439 | The remaining blocks are configured as MLC blocks. | ||
| 1440 | lock - Configure if Flex-OneNAND boundary should be locked. | ||
| 1441 | Once locked, the boundary cannot be changed. | ||
| 1442 | 1 indicates lock status, 0 indicates unlock status. | ||
| 1443 | |||
| 1434 | mtdset= [ARM] | 1444 | mtdset= [ARM] |
| 1435 | ARM/S3C2412 JIVE boot control | 1445 | ARM/S3C2412 JIVE boot control |
| 1436 | 1446 | ||
diff --git a/MAINTAINERS b/MAINTAINERS index cf5a46ef6b3f..02f6f78b561f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -3252,7 +3252,6 @@ W: http://www.linux-mtd.infradead.org/doc/jffs2.html | |||
| 3252 | S: Maintained | 3252 | S: Maintained |
| 3253 | F: fs/jffs2/ | 3253 | F: fs/jffs2/ |
| 3254 | F: include/linux/jffs2.h | 3254 | F: include/linux/jffs2.h |
| 3255 | F: include/mtd/jffs2-user.h | ||
| 3256 | 3255 | ||
| 3257 | JOURNALLING LAYER FOR BLOCK DEVICES (JBD) | 3256 | JOURNALLING LAYER FOR BLOCK DEVICES (JBD) |
| 3258 | P: Stephen Tweedie | 3257 | P: Stephen Tweedie |
diff --git a/arch/arm/mach-davinci/include/mach/nand.h b/arch/arm/mach-davinci/include/mach/nand.h index aa482841270b..b520c4b5678a 100644 --- a/arch/arm/mach-davinci/include/mach/nand.h +++ b/arch/arm/mach-davinci/include/mach/nand.h | |||
| @@ -68,10 +68,14 @@ struct davinci_nand_pdata { /* platform_data */ | |||
| 68 | 68 | ||
| 69 | /* none == NAND_ECC_NONE (strongly *not* advised!!) | 69 | /* none == NAND_ECC_NONE (strongly *not* advised!!) |
| 70 | * soft == NAND_ECC_SOFT | 70 | * soft == NAND_ECC_SOFT |
| 71 | * 1-bit == NAND_ECC_HW | 71 | * else == NAND_ECC_HW, according to ecc_bits |
| 72 | * 4-bit == NAND_ECC_HW_SYNDROME (not on all chips) | 72 | * |
| 73 | * All DaVinci-family chips support 1-bit hardware ECC. | ||
| 74 | * Newer ones also support 4-bit ECC, but are awkward | ||
| 75 | * using it with large page chips. | ||
| 73 | */ | 76 | */ |
| 74 | nand_ecc_modes_t ecc_mode; | 77 | nand_ecc_modes_t ecc_mode; |
| 78 | u8 ecc_bits; | ||
| 75 | 79 | ||
| 76 | /* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */ | 80 | /* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */ |
| 77 | unsigned options; | 81 | unsigned options; |
diff --git a/arch/arm/plat-s3c/include/plat/nand.h b/arch/arm/plat-s3c/include/plat/nand.h index f4dcd14af059..18f958801e64 100644 --- a/arch/arm/plat-s3c/include/plat/nand.h +++ b/arch/arm/plat-s3c/include/plat/nand.h | |||
| @@ -10,19 +10,26 @@ | |||
| 10 | * published by the Free Software Foundation. | 10 | * published by the Free Software Foundation. |
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | /* struct s3c2410_nand_set | 13 | /** |
| 14 | * struct s3c2410_nand_set - define a set of one or more nand chips | ||
| 15 | * @disable_ecc: Entirely disable ECC - Dangerous | ||
| 16 | * @flash_bbt: Openmoko u-boot can create a Bad Block Table | ||
| 17 | * Setting this flag will allow the kernel to | ||
| 18 | * look for it at boot time and also skip the NAND | ||
| 19 | * scan. | ||
| 20 | * @nr_chips: Number of chips in this set | ||
| 21 | * @nr_partitions: Number of partitions pointed to by @partitions | ||
| 22 | * @name: Name of set (optional) | ||
| 23 | * @nr_map: Map for low-layer logical to physical chip numbers (option) | ||
| 24 | * @partitions: The mtd partition list | ||
| 14 | * | 25 | * |
| 15 | * define an set of one or more nand chips registered with an unique mtd | 26 | * define a set of one or more nand chips registered with an unique mtd. Also |
| 16 | * | 27 | * allows to pass flag to the underlying NAND layer. 'disable_ecc' will trigger |
| 17 | * nr_chips = number of chips in this set | 28 | * a warning at boot time. |
| 18 | * nr_partitions = number of partitions pointed to be partitoons (or zero) | 29 | */ |
| 19 | * name = name of set (optional) | ||
| 20 | * nr_map = map for low-layer logical to physical chip numbers (option) | ||
| 21 | * partitions = mtd partition list | ||
| 22 | */ | ||
| 23 | |||
| 24 | struct s3c2410_nand_set { | 30 | struct s3c2410_nand_set { |
| 25 | unsigned int disable_ecc : 1; | 31 | unsigned int disable_ecc:1; |
| 32 | unsigned int flash_bbt:1; | ||
| 26 | 33 | ||
| 27 | int nr_chips; | 34 | int nr_chips; |
| 28 | int nr_partitions; | 35 | int nr_partitions; |
| @@ -39,7 +46,7 @@ struct s3c2410_platform_nand { | |||
| 39 | int twrph0; /* active time for nWE/nOE */ | 46 | int twrph0; /* active time for nWE/nOE */ |
| 40 | int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ | 47 | int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ |
| 41 | 48 | ||
| 42 | unsigned int ignore_unset_ecc : 1; | 49 | unsigned int ignore_unset_ecc:1; |
| 43 | 50 | ||
| 44 | int nr_sets; | 51 | int nr_sets; |
| 45 | struct s3c2410_nand_set *sets; | 52 | struct s3c2410_nand_set *sets; |
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index c240454fd113..8664feebc93b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c | |||
| @@ -46,6 +46,7 @@ | |||
| 46 | #define MANUFACTURER_INTEL 0x0089 | 46 | #define MANUFACTURER_INTEL 0x0089 |
| 47 | #define I82802AB 0x00ad | 47 | #define I82802AB 0x00ad |
| 48 | #define I82802AC 0x00ac | 48 | #define I82802AC 0x00ac |
| 49 | #define PF38F4476 0x881c | ||
| 49 | #define MANUFACTURER_ST 0x0020 | 50 | #define MANUFACTURER_ST 0x0020 |
| 50 | #define M50LPW080 0x002F | 51 | #define M50LPW080 0x002F |
| 51 | #define M50FLW080A 0x0080 | 52 | #define M50FLW080A 0x0080 |
| @@ -315,10 +316,20 @@ static struct cfi_fixup fixup_table[] = { | |||
| 315 | { 0, 0, NULL, NULL } | 316 | { 0, 0, NULL, NULL } |
| 316 | }; | 317 | }; |
| 317 | 318 | ||
| 319 | static void cfi_fixup_major_minor(struct cfi_private *cfi, | ||
| 320 | struct cfi_pri_intelext *extp) | ||
| 321 | { | ||
| 322 | if (cfi->mfr == MANUFACTURER_INTEL && | ||
| 323 | cfi->id == PF38F4476 && extp->MinorVersion == '3') | ||
| 324 | extp->MinorVersion = '1'; | ||
| 325 | } | ||
| 326 | |||
| 318 | static inline struct cfi_pri_intelext * | 327 | static inline struct cfi_pri_intelext * |
| 319 | read_pri_intelext(struct map_info *map, __u16 adr) | 328 | read_pri_intelext(struct map_info *map, __u16 adr) |
| 320 | { | 329 | { |
| 330 | struct cfi_private *cfi = map->fldrv_priv; | ||
| 321 | struct cfi_pri_intelext *extp; | 331 | struct cfi_pri_intelext *extp; |
| 332 | unsigned int extra_size = 0; | ||
| 322 | unsigned int extp_size = sizeof(*extp); | 333 | unsigned int extp_size = sizeof(*extp); |
| 323 | 334 | ||
| 324 | again: | 335 | again: |
| @@ -326,6 +337,8 @@ read_pri_intelext(struct map_info *map, __u16 adr) | |||
| 326 | if (!extp) | 337 | if (!extp) |
| 327 | return NULL; | 338 | return NULL; |
| 328 | 339 | ||
| 340 | cfi_fixup_major_minor(cfi, extp); | ||
| 341 | |||
| 329 | if (extp->MajorVersion != '1' || | 342 | if (extp->MajorVersion != '1' || |
| 330 | (extp->MinorVersion < '0' || extp->MinorVersion > '5')) { | 343 | (extp->MinorVersion < '0' || extp->MinorVersion > '5')) { |
| 331 | printk(KERN_ERR " Unknown Intel/Sharp Extended Query " | 344 | printk(KERN_ERR " Unknown Intel/Sharp Extended Query " |
| @@ -340,19 +353,24 @@ read_pri_intelext(struct map_info *map, __u16 adr) | |||
| 340 | extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); | 353 | extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); |
| 341 | extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); | 354 | extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); |
| 342 | 355 | ||
| 343 | if (extp->MajorVersion == '1' && extp->MinorVersion >= '3') { | 356 | if (extp->MinorVersion >= '0') { |
| 344 | unsigned int extra_size = 0; | 357 | extra_size = 0; |
| 345 | int nb_parts, i; | ||
| 346 | 358 | ||
| 347 | /* Protection Register info */ | 359 | /* Protection Register info */ |
| 348 | extra_size += (extp->NumProtectionFields - 1) * | 360 | extra_size += (extp->NumProtectionFields - 1) * |
| 349 | sizeof(struct cfi_intelext_otpinfo); | 361 | sizeof(struct cfi_intelext_otpinfo); |
| 362 | } | ||
| 350 | 363 | ||
| 364 | if (extp->MinorVersion >= '1') { | ||
| 351 | /* Burst Read info */ | 365 | /* Burst Read info */ |
| 352 | extra_size += 2; | 366 | extra_size += 2; |
| 353 | if (extp_size < sizeof(*extp) + extra_size) | 367 | if (extp_size < sizeof(*extp) + extra_size) |
| 354 | goto need_more; | 368 | goto need_more; |
| 355 | extra_size += extp->extra[extra_size-1]; | 369 | extra_size += extp->extra[extra_size - 1]; |
| 370 | } | ||
| 371 | |||
| 372 | if (extp->MinorVersion >= '3') { | ||
| 373 | int nb_parts, i; | ||
| 356 | 374 | ||
| 357 | /* Number of hardware-partitions */ | 375 | /* Number of hardware-partitions */ |
| 358 | extra_size += 1; | 376 | extra_size += 1; |
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index e824b9b9b056..ccc4cfc7e4b5 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c | |||
| @@ -166,6 +166,7 @@ | |||
| 166 | #define SST39LF040 0x00D7 | 166 | #define SST39LF040 0x00D7 |
| 167 | #define SST39SF010A 0x00B5 | 167 | #define SST39SF010A 0x00B5 |
| 168 | #define SST39SF020A 0x00B6 | 168 | #define SST39SF020A 0x00B6 |
| 169 | #define SST39SF040 0x00B7 | ||
| 169 | #define SST49LF004B 0x0060 | 170 | #define SST49LF004B 0x0060 |
| 170 | #define SST49LF040B 0x0050 | 171 | #define SST49LF040B 0x0050 |
| 171 | #define SST49LF008A 0x005a | 172 | #define SST49LF008A 0x005a |
| @@ -1393,6 +1394,18 @@ static const struct amd_flash_info jedec_table[] = { | |||
| 1393 | } | 1394 | } |
| 1394 | }, { | 1395 | }, { |
| 1395 | .mfr_id = MANUFACTURER_SST, | 1396 | .mfr_id = MANUFACTURER_SST, |
| 1397 | .dev_id = SST39SF040, | ||
| 1398 | .name = "SST 39SF040", | ||
| 1399 | .devtypes = CFI_DEVICETYPE_X8, | ||
| 1400 | .uaddr = MTD_UADDR_0x5555_0x2AAA, | ||
| 1401 | .dev_size = SIZE_512KiB, | ||
| 1402 | .cmd_set = P_ID_AMD_STD, | ||
| 1403 | .nr_regions = 1, | ||
| 1404 | .regions = { | ||
| 1405 | ERASEINFO(0x01000,128), | ||
| 1406 | } | ||
| 1407 | }, { | ||
| 1408 | .mfr_id = MANUFACTURER_SST, | ||
| 1396 | .dev_id = SST49LF040B, | 1409 | .dev_id = SST49LF040B, |
| 1397 | .name = "SST 49LF040B", | 1410 | .name = "SST 49LF040B", |
| 1398 | .devtypes = CFI_DEVICETYPE_X8, | 1411 | .devtypes = CFI_DEVICETYPE_X8, |
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index cc6369ea67dd..59c46126a5ce 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
| @@ -500,6 +500,9 @@ static struct flash_info __devinitdata m25p_data [] = { | |||
| 500 | { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, }, | 500 | { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, }, |
| 501 | { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, | 501 | { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, |
| 502 | 502 | ||
| 503 | /* Macronix */ | ||
| 504 | { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, | ||
| 505 | |||
| 503 | /* Spansion -- single (large) sector size only, at least | 506 | /* Spansion -- single (large) sector size only, at least |
| 504 | * for the chips listed here (without boot sectors). | 507 | * for the chips listed here (without boot sectors). |
| 505 | */ | 508 | */ |
| @@ -528,6 +531,7 @@ static struct flash_info __devinitdata m25p_data [] = { | |||
| 528 | { "m25p64", 0x202017, 0, 64 * 1024, 128, }, | 531 | { "m25p64", 0x202017, 0, 64 * 1024, 128, }, |
| 529 | { "m25p128", 0x202018, 0, 256 * 1024, 64, }, | 532 | { "m25p128", 0x202018, 0, 256 * 1024, 64, }, |
| 530 | 533 | ||
| 534 | { "m45pe10", 0x204011, 0, 64 * 1024, 2, }, | ||
| 531 | { "m45pe80", 0x204014, 0, 64 * 1024, 16, }, | 535 | { "m45pe80", 0x204014, 0, 64 * 1024, 16, }, |
| 532 | { "m45pe16", 0x204015, 0, 64 * 1024, 32, }, | 536 | { "m45pe16", 0x204015, 0, 64 * 1024, 32, }, |
| 533 | 537 | ||
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 82923bd2d9c5..0b98654d8eed 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig | |||
| @@ -105,15 +105,6 @@ config MSP_FLASH_MAP_LIMIT | |||
| 105 | default "0x02000000" | 105 | default "0x02000000" |
| 106 | depends on MSP_FLASH_MAP_LIMIT_32M | 106 | depends on MSP_FLASH_MAP_LIMIT_32M |
| 107 | 107 | ||
| 108 | config MTD_PMC_MSP_RAMROOT | ||
| 109 | tristate "Embedded RAM block device for root on PMC-Sierra MSP" | ||
| 110 | depends on PMC_MSP_EMBEDDED_ROOTFS && \ | ||
| 111 | (MTD_BLOCK || MTD_BLOCK_RO) && \ | ||
| 112 | MTD_RAM | ||
| 113 | help | ||
| 114 | This provides support for the embedded root file system | ||
| 115 | on PMC MSP devices. This memory is mapped as a MTD block device. | ||
| 116 | |||
| 117 | config MTD_SUN_UFLASH | 108 | config MTD_SUN_UFLASH |
| 118 | tristate "Sun Microsystems userflash support" | 109 | tristate "Sun Microsystems userflash support" |
| 119 | depends on SPARC && MTD_CFI && PCI | 110 | depends on SPARC && MTD_CFI && PCI |
| @@ -270,7 +261,7 @@ config MTD_ALCHEMY | |||
| 270 | 261 | ||
| 271 | config MTD_DILNETPC | 262 | config MTD_DILNETPC |
| 272 | tristate "CFI Flash device mapped on DIL/Net PC" | 263 | tristate "CFI Flash device mapped on DIL/Net PC" |
| 273 | depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT | 264 | depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN |
| 274 | help | 265 | help |
| 275 | MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP". | 266 | MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP". |
| 276 | For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm> | 267 | For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm> |
| @@ -501,7 +492,7 @@ config MTD_BFIN_ASYNC | |||
| 501 | If compiled as a module, it will be called bfin-async-flash. | 492 | If compiled as a module, it will be called bfin-async-flash. |
| 502 | 493 | ||
| 503 | config MTD_UCLINUX | 494 | config MTD_UCLINUX |
| 504 | tristate "Generic uClinux RAM/ROM filesystem support" | 495 | bool "Generic uClinux RAM/ROM filesystem support" |
| 505 | depends on MTD_PARTITIONS && MTD_RAM && !MMU | 496 | depends on MTD_PARTITIONS && MTD_RAM && !MMU |
| 506 | help | 497 | help |
| 507 | Map driver to support image based filesystems for uClinux. | 498 | Map driver to support image based filesystems for uClinux. |
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 2dbc1bec8488..8bae7f9850c0 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile | |||
| @@ -25,7 +25,6 @@ obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o | |||
| 25 | obj-$(CONFIG_MTD_PHYSMAP) += physmap.o | 25 | obj-$(CONFIG_MTD_PHYSMAP) += physmap.o |
| 26 | obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o | 26 | obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o |
| 27 | obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o | 27 | obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o |
| 28 | obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o | ||
| 29 | obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o | 28 | obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o |
| 30 | obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o | 29 | obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o |
| 31 | obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o | 30 | obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o |
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index 576611f605db..365c77b1b871 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c | |||
| @@ -40,6 +40,9 @@ struct async_state { | |||
| 40 | uint32_t flash_ambctl0, flash_ambctl1; | 40 | uint32_t flash_ambctl0, flash_ambctl1; |
| 41 | uint32_t save_ambctl0, save_ambctl1; | 41 | uint32_t save_ambctl0, save_ambctl1; |
| 42 | unsigned long irq_flags; | 42 | unsigned long irq_flags; |
| 43 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 44 | struct mtd_partition *parts; | ||
| 45 | #endif | ||
| 43 | }; | 46 | }; |
| 44 | 47 | ||
| 45 | static void switch_to_flash(struct async_state *state) | 48 | static void switch_to_flash(struct async_state *state) |
| @@ -170,6 +173,7 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) | |||
| 170 | if (ret > 0) { | 173 | if (ret > 0) { |
| 171 | pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); | 174 | pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); |
| 172 | add_mtd_partitions(state->mtd, pdata->parts, ret); | 175 | add_mtd_partitions(state->mtd, pdata->parts, ret); |
| 176 | state->parts = pdata->parts; | ||
| 173 | 177 | ||
| 174 | } else if (pdata->nr_parts) { | 178 | } else if (pdata->nr_parts) { |
| 175 | pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); | 179 | pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); |
| @@ -193,6 +197,7 @@ static int __devexit bfin_flash_remove(struct platform_device *pdev) | |||
| 193 | gpio_free(state->enet_flash_pin); | 197 | gpio_free(state->enet_flash_pin); |
| 194 | #ifdef CONFIG_MTD_PARTITIONS | 198 | #ifdef CONFIG_MTD_PARTITIONS |
| 195 | del_mtd_partitions(state->mtd); | 199 | del_mtd_partitions(state->mtd); |
| 200 | kfree(state->parts); | ||
| 196 | #endif | 201 | #endif |
| 197 | map_destroy(state->mtd); | 202 | map_destroy(state->mtd); |
| 198 | kfree(state); | 203 | kfree(state); |
diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index c9681a339a59..b08a798ee254 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c | |||
| @@ -36,27 +36,33 @@ | |||
| 36 | #include <linux/mtd/mtd.h> | 36 | #include <linux/mtd/mtd.h> |
| 37 | #include <linux/mtd/map.h> | 37 | #include <linux/mtd/map.h> |
| 38 | #include <linux/mtd/partitions.h> | 38 | #include <linux/mtd/partitions.h> |
| 39 | #include <linux/mtd/concat.h> | ||
| 39 | 40 | ||
| 40 | #include <asm/mach/flash.h> | 41 | #include <asm/mach/flash.h> |
| 41 | #include <mach/hardware.h> | 42 | #include <mach/hardware.h> |
| 42 | #include <asm/system.h> | 43 | #include <asm/system.h> |
| 43 | 44 | ||
| 44 | #ifdef CONFIG_ARCH_P720T | 45 | #define SUBDEV_NAME_SIZE (BUS_ID_SIZE + 2) |
| 45 | #define FLASH_BASE (0x04000000) | ||
| 46 | #define FLASH_SIZE (64*1024*1024) | ||
| 47 | #endif | ||
| 48 | 46 | ||
| 49 | struct armflash_info { | 47 | struct armflash_subdev_info { |
| 48 | char name[SUBDEV_NAME_SIZE]; | ||
| 49 | struct mtd_info *mtd; | ||
| 50 | struct map_info map; | ||
| 50 | struct flash_platform_data *plat; | 51 | struct flash_platform_data *plat; |
| 52 | }; | ||
| 53 | |||
| 54 | struct armflash_info { | ||
| 51 | struct resource *res; | 55 | struct resource *res; |
| 52 | struct mtd_partition *parts; | 56 | struct mtd_partition *parts; |
| 53 | struct mtd_info *mtd; | 57 | struct mtd_info *mtd; |
| 54 | struct map_info map; | 58 | int nr_subdev; |
| 59 | struct armflash_subdev_info subdev[0]; | ||
| 55 | }; | 60 | }; |
| 56 | 61 | ||
| 57 | static void armflash_set_vpp(struct map_info *map, int on) | 62 | static void armflash_set_vpp(struct map_info *map, int on) |
| 58 | { | 63 | { |
| 59 | struct armflash_info *info = container_of(map, struct armflash_info, map); | 64 | struct armflash_subdev_info *info = |
| 65 | container_of(map, struct armflash_subdev_info, map); | ||
| 60 | 66 | ||
| 61 | if (info->plat && info->plat->set_vpp) | 67 | if (info->plat && info->plat->set_vpp) |
| 62 | info->plat->set_vpp(on); | 68 | info->plat->set_vpp(on); |
| @@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on) | |||
| 64 | 70 | ||
| 65 | static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; | 71 | static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; |
| 66 | 72 | ||
| 67 | static int armflash_probe(struct platform_device *dev) | 73 | static int armflash_subdev_probe(struct armflash_subdev_info *subdev, |
| 74 | struct resource *res) | ||
| 68 | { | 75 | { |
| 69 | struct flash_platform_data *plat = dev->dev.platform_data; | 76 | struct flash_platform_data *plat = subdev->plat; |
| 70 | struct resource *res = dev->resource; | 77 | resource_size_t size = res->end - res->start + 1; |
| 71 | unsigned int size = res->end - res->start + 1; | ||
| 72 | struct armflash_info *info; | ||
| 73 | int err; | ||
| 74 | void __iomem *base; | 78 | void __iomem *base; |
| 79 | int err = 0; | ||
| 75 | 80 | ||
| 76 | info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); | 81 | if (!request_mem_region(res->start, size, subdev->name)) { |
| 77 | if (!info) { | ||
| 78 | err = -ENOMEM; | ||
| 79 | goto out; | ||
| 80 | } | ||
| 81 | |||
| 82 | info->plat = plat; | ||
| 83 | if (plat && plat->init) { | ||
| 84 | err = plat->init(); | ||
| 85 | if (err) | ||
| 86 | goto no_resource; | ||
| 87 | } | ||
| 88 | |||
| 89 | info->res = request_mem_region(res->start, size, "armflash"); | ||
| 90 | if (!info->res) { | ||
| 91 | err = -EBUSY; | 82 | err = -EBUSY; |
| 92 | goto no_resource; | 83 | goto out; |
| 93 | } | 84 | } |
| 94 | 85 | ||
| 95 | base = ioremap(res->start, size); | 86 | base = ioremap(res->start, size); |
| @@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev) | |||
| 101 | /* | 92 | /* |
| 102 | * look for CFI based flash parts fitted to this board | 93 | * look for CFI based flash parts fitted to this board |
| 103 | */ | 94 | */ |
| 104 | info->map.size = size; | 95 | subdev->map.size = size; |
| 105 | info->map.bankwidth = plat->width; | 96 | subdev->map.bankwidth = plat->width; |
| 106 | info->map.phys = res->start; | 97 | subdev->map.phys = res->start; |
| 107 | info->map.virt = base; | 98 | subdev->map.virt = base; |
| 108 | info->map.name = dev_name(&dev->dev); | 99 | subdev->map.name = subdev->name; |
| 109 | info->map.set_vpp = armflash_set_vpp; | 100 | subdev->map.set_vpp = armflash_set_vpp; |
| 110 | 101 | ||
| 111 | simple_map_init(&info->map); | 102 | simple_map_init(&subdev->map); |
| 112 | 103 | ||
| 113 | /* | 104 | /* |
| 114 | * Also, the CFI layer automatically works out what size | 105 | * Also, the CFI layer automatically works out what size |
| 115 | * of chips we have, and does the necessary identification | 106 | * of chips we have, and does the necessary identification |
| 116 | * for us automatically. | 107 | * for us automatically. |
| 117 | */ | 108 | */ |
| 118 | info->mtd = do_map_probe(plat->map_name, &info->map); | 109 | subdev->mtd = do_map_probe(plat->map_name, &subdev->map); |
| 119 | if (!info->mtd) { | 110 | if (!subdev->mtd) { |
| 120 | err = -ENXIO; | 111 | err = -ENXIO; |
| 121 | goto no_device; | 112 | goto no_device; |
| 122 | } | 113 | } |
| 123 | 114 | ||
| 124 | info->mtd->owner = THIS_MODULE; | 115 | subdev->mtd->owner = THIS_MODULE; |
| 116 | |||
| 117 | /* Successful? */ | ||
| 118 | if (err == 0) | ||
| 119 | return err; | ||
| 120 | |||
| 121 | if (subdev->mtd) | ||
| 122 | map_destroy(subdev->mtd); | ||
| 123 | no_device: | ||
| 124 | iounmap(base); | ||
| 125 | no_mem: | ||
| 126 | release_mem_region(res->start, size); | ||
| 127 | out: | ||
| 128 | return err; | ||
| 129 | } | ||
| 130 | |||
| 131 | static void armflash_subdev_remove(struct armflash_subdev_info *subdev) | ||
| 132 | { | ||
| 133 | if (subdev->mtd) | ||
| 134 | map_destroy(subdev->mtd); | ||
| 135 | if (subdev->map.virt) | ||
| 136 | iounmap(subdev->map.virt); | ||
| 137 | release_mem_region(subdev->map.phys, subdev->map.size); | ||
| 138 | } | ||
| 139 | |||
| 140 | static int armflash_probe(struct platform_device *dev) | ||
| 141 | { | ||
| 142 | struct flash_platform_data *plat = dev->dev.platform_data; | ||
| 143 | unsigned int size; | ||
| 144 | struct armflash_info *info; | ||
| 145 | int i, nr, err; | ||
| 146 | |||
| 147 | /* Count the number of devices */ | ||
| 148 | for (nr = 0; ; nr++) | ||
| 149 | if (!platform_get_resource(dev, IORESOURCE_MEM, nr)) | ||
| 150 | break; | ||
| 151 | if (nr == 0) { | ||
| 152 | err = -ENODEV; | ||
| 153 | goto out; | ||
| 154 | } | ||
| 155 | |||
| 156 | size = sizeof(struct armflash_info) + | ||
| 157 | sizeof(struct armflash_subdev_info) * nr; | ||
| 158 | info = kzalloc(size, GFP_KERNEL); | ||
| 159 | if (!info) { | ||
| 160 | err = -ENOMEM; | ||
| 161 | goto out; | ||
| 162 | } | ||
| 163 | |||
| 164 | if (plat && plat->init) { | ||
| 165 | err = plat->init(); | ||
| 166 | if (err) | ||
| 167 | goto no_resource; | ||
| 168 | } | ||
| 169 | |||
| 170 | for (i = 0; i < nr; i++) { | ||
| 171 | struct armflash_subdev_info *subdev = &info->subdev[i]; | ||
| 172 | struct resource *res; | ||
| 173 | |||
| 174 | res = platform_get_resource(dev, IORESOURCE_MEM, i); | ||
| 175 | if (!res) | ||
| 176 | break; | ||
| 177 | |||
| 178 | if (nr == 1) | ||
| 179 | /* No MTD concatenation, just use the default name */ | ||
| 180 | snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s", | ||
| 181 | dev_name(&dev->dev)); | ||
| 182 | else | ||
| 183 | snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d", | ||
| 184 | dev_name(&dev->dev), i); | ||
| 185 | subdev->plat = plat; | ||
| 186 | |||
| 187 | err = armflash_subdev_probe(subdev, res); | ||
| 188 | if (err) | ||
| 189 | break; | ||
| 190 | } | ||
| 191 | info->nr_subdev = i; | ||
| 192 | |||
| 193 | if (err) | ||
| 194 | goto subdev_err; | ||
| 195 | |||
| 196 | if (info->nr_subdev == 1) | ||
| 197 | info->mtd = info->subdev[0].mtd; | ||
| 198 | else if (info->nr_subdev > 1) { | ||
| 199 | #ifdef CONFIG_MTD_CONCAT | ||
| 200 | struct mtd_info *cdev[info->nr_subdev]; | ||
| 201 | |||
| 202 | /* | ||
| 203 | * We detected multiple devices. Concatenate them together. | ||
| 204 | */ | ||
| 205 | for (i = 0; i < info->nr_subdev; i++) | ||
| 206 | cdev[i] = info->subdev[i].mtd; | ||
| 207 | |||
| 208 | info->mtd = mtd_concat_create(cdev, info->nr_subdev, | ||
| 209 | dev_name(&dev->dev)); | ||
| 210 | if (info->mtd == NULL) | ||
| 211 | err = -ENXIO; | ||
| 212 | #else | ||
| 213 | printk(KERN_ERR "armflash: multiple devices found but " | ||
| 214 | "MTD concat support disabled.\n"); | ||
| 215 | err = -ENXIO; | ||
| 216 | #endif | ||
| 217 | } | ||
| 218 | |||
| 219 | if (err < 0) | ||
| 220 | goto cleanup; | ||
| 125 | 221 | ||
| 126 | err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); | 222 | err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); |
| 127 | if (err > 0) { | 223 | if (err > 0) { |
| @@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev) | |||
| 131 | "mtd partition registration failed: %d\n", err); | 227 | "mtd partition registration failed: %d\n", err); |
| 132 | } | 228 | } |
| 133 | 229 | ||
| 134 | if (err == 0) | 230 | if (err == 0) { |
| 135 | platform_set_drvdata(dev, info); | 231 | platform_set_drvdata(dev, info); |
| 232 | return err; | ||
| 233 | } | ||
| 136 | 234 | ||
| 137 | /* | 235 | /* |
| 138 | * If we got an error, free all resources. | 236 | * We got an error, free all resources. |
| 139 | */ | 237 | */ |
| 140 | if (err < 0) { | 238 | cleanup: |
| 141 | if (info->mtd) { | 239 | if (info->mtd) { |
| 142 | del_mtd_partitions(info->mtd); | 240 | del_mtd_partitions(info->mtd); |
| 143 | map_destroy(info->mtd); | 241 | #ifdef CONFIG_MTD_CONCAT |
| 144 | } | 242 | if (info->mtd != info->subdev[0].mtd) |
| 145 | kfree(info->parts); | 243 | mtd_concat_destroy(info->mtd); |
| 146 | 244 | #endif | |
| 147 | no_device: | ||
| 148 | iounmap(base); | ||
| 149 | no_mem: | ||
| 150 | release_mem_region(res->start, size); | ||
| 151 | no_resource: | ||
| 152 | if (plat && plat->exit) | ||
| 153 | plat->exit(); | ||
| 154 | kfree(info); | ||
| 155 | } | 245 | } |
| 246 | kfree(info->parts); | ||
| 247 | subdev_err: | ||
| 248 | for (i = info->nr_subdev - 1; i >= 0; i--) | ||
| 249 | armflash_subdev_remove(&info->subdev[i]); | ||
| 250 | no_resource: | ||
| 251 | if (plat && plat->exit) | ||
| 252 | plat->exit(); | ||
| 253 | kfree(info); | ||
| 156 | out: | 254 | out: |
| 157 | return err; | 255 | return err; |
| 158 | } | 256 | } |
| @@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev) | |||
| 160 | static int armflash_remove(struct platform_device *dev) | 258 | static int armflash_remove(struct platform_device *dev) |
| 161 | { | 259 | { |
| 162 | struct armflash_info *info = platform_get_drvdata(dev); | 260 | struct armflash_info *info = platform_get_drvdata(dev); |
| 261 | struct flash_platform_data *plat = dev->dev.platform_data; | ||
| 262 | int i; | ||
| 163 | 263 | ||
| 164 | platform_set_drvdata(dev, NULL); | 264 | platform_set_drvdata(dev, NULL); |
| 165 | 265 | ||
| 166 | if (info) { | 266 | if (info) { |
| 167 | if (info->mtd) { | 267 | if (info->mtd) { |
| 168 | del_mtd_partitions(info->mtd); | 268 | del_mtd_partitions(info->mtd); |
| 169 | map_destroy(info->mtd); | 269 | #ifdef CONFIG_MTD_CONCAT |
| 270 | if (info->mtd != info->subdev[0].mtd) | ||
| 271 | mtd_concat_destroy(info->mtd); | ||
| 272 | #endif | ||
| 170 | } | 273 | } |
| 171 | kfree(info->parts); | 274 | kfree(info->parts); |
| 172 | 275 | ||
| 173 | iounmap(info->map.virt); | 276 | for (i = info->nr_subdev - 1; i >= 0; i--) |
| 174 | release_resource(info->res); | 277 | armflash_subdev_remove(&info->subdev[i]); |
| 175 | kfree(info->res); | ||
| 176 | 278 | ||
| 177 | if (info->plat && info->plat->exit) | 279 | if (plat && plat->exit) |
| 178 | info->plat->exit(); | 280 | plat->exit(); |
| 179 | 281 | ||
| 180 | kfree(info); | 282 | kfree(info); |
| 181 | } | 283 | } |
diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 29a901157352..380648e9051a 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c | |||
| @@ -195,42 +195,6 @@ err_out: | |||
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | #ifdef CONFIG_PM | 197 | #ifdef CONFIG_PM |
| 198 | static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state) | ||
| 199 | { | ||
| 200 | struct physmap_flash_info *info = platform_get_drvdata(dev); | ||
| 201 | int ret = 0; | ||
| 202 | int i; | ||
| 203 | |||
| 204 | for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) | ||
| 205 | if (info->mtd[i]->suspend) { | ||
| 206 | ret = info->mtd[i]->suspend(info->mtd[i]); | ||
| 207 | if (ret) | ||
| 208 | goto fail; | ||
| 209 | } | ||
| 210 | |||
| 211 | return 0; | ||
| 212 | fail: | ||
| 213 | for (--i; i >= 0; --i) | ||
| 214 | if (info->mtd[i]->suspend) { | ||
| 215 | BUG_ON(!info->mtd[i]->resume); | ||
| 216 | info->mtd[i]->resume(info->mtd[i]); | ||
| 217 | } | ||
| 218 | |||
| 219 | return ret; | ||
| 220 | } | ||
| 221 | |||
| 222 | static int physmap_flash_resume(struct platform_device *dev) | ||
| 223 | { | ||
| 224 | struct physmap_flash_info *info = platform_get_drvdata(dev); | ||
| 225 | int i; | ||
| 226 | |||
| 227 | for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) | ||
| 228 | if (info->mtd[i]->resume) | ||
| 229 | info->mtd[i]->resume(info->mtd[i]); | ||
| 230 | |||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | static void physmap_flash_shutdown(struct platform_device *dev) | 198 | static void physmap_flash_shutdown(struct platform_device *dev) |
| 235 | { | 199 | { |
| 236 | struct physmap_flash_info *info = platform_get_drvdata(dev); | 200 | struct physmap_flash_info *info = platform_get_drvdata(dev); |
| @@ -242,16 +206,12 @@ static void physmap_flash_shutdown(struct platform_device *dev) | |||
| 242 | info->mtd[i]->resume(info->mtd[i]); | 206 | info->mtd[i]->resume(info->mtd[i]); |
| 243 | } | 207 | } |
| 244 | #else | 208 | #else |
| 245 | #define physmap_flash_suspend NULL | ||
| 246 | #define physmap_flash_resume NULL | ||
| 247 | #define physmap_flash_shutdown NULL | 209 | #define physmap_flash_shutdown NULL |
| 248 | #endif | 210 | #endif |
| 249 | 211 | ||
| 250 | static struct platform_driver physmap_flash_driver = { | 212 | static struct platform_driver physmap_flash_driver = { |
| 251 | .probe = physmap_flash_probe, | 213 | .probe = physmap_flash_probe, |
| 252 | .remove = physmap_flash_remove, | 214 | .remove = physmap_flash_remove, |
| 253 | .suspend = physmap_flash_suspend, | ||
| 254 | .resume = physmap_flash_resume, | ||
| 255 | .shutdown = physmap_flash_shutdown, | 215 | .shutdown = physmap_flash_shutdown, |
| 256 | .driver = { | 216 | .driver = { |
| 257 | .name = "physmap-flash", | 217 | .name = "physmap-flash", |
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index c83a60fada53..39d357b2eb47 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
| @@ -20,16 +20,23 @@ | |||
| 20 | #include <linux/mtd/mtd.h> | 20 | #include <linux/mtd/mtd.h> |
| 21 | #include <linux/mtd/map.h> | 21 | #include <linux/mtd/map.h> |
| 22 | #include <linux/mtd/partitions.h> | 22 | #include <linux/mtd/partitions.h> |
| 23 | #include <linux/mtd/concat.h> | ||
| 23 | #include <linux/of.h> | 24 | #include <linux/of.h> |
| 24 | #include <linux/of_platform.h> | 25 | #include <linux/of_platform.h> |
| 25 | 26 | ||
| 27 | struct of_flash_list { | ||
| 28 | struct mtd_info *mtd; | ||
| 29 | struct map_info map; | ||
| 30 | struct resource *res; | ||
| 31 | }; | ||
| 32 | |||
| 26 | struct of_flash { | 33 | struct of_flash { |
| 27 | struct mtd_info *mtd; | 34 | struct mtd_info *cmtd; |
| 28 | struct map_info map; | ||
| 29 | struct resource *res; | ||
| 30 | #ifdef CONFIG_MTD_PARTITIONS | 35 | #ifdef CONFIG_MTD_PARTITIONS |
| 31 | struct mtd_partition *parts; | 36 | struct mtd_partition *parts; |
| 32 | #endif | 37 | #endif |
| 38 | int list_size; /* number of elements in of_flash_list */ | ||
| 39 | struct of_flash_list list[0]; | ||
| 33 | }; | 40 | }; |
| 34 | 41 | ||
| 35 | #ifdef CONFIG_MTD_PARTITIONS | 42 | #ifdef CONFIG_MTD_PARTITIONS |
| @@ -88,30 +95,44 @@ static int parse_obsolete_partitions(struct of_device *dev, | |||
| 88 | static int of_flash_remove(struct of_device *dev) | 95 | static int of_flash_remove(struct of_device *dev) |
| 89 | { | 96 | { |
| 90 | struct of_flash *info; | 97 | struct of_flash *info; |
| 98 | int i; | ||
| 91 | 99 | ||
| 92 | info = dev_get_drvdata(&dev->dev); | 100 | info = dev_get_drvdata(&dev->dev); |
| 93 | if (!info) | 101 | if (!info) |
| 94 | return 0; | 102 | return 0; |
| 95 | dev_set_drvdata(&dev->dev, NULL); | 103 | dev_set_drvdata(&dev->dev, NULL); |
| 96 | 104 | ||
| 97 | if (info->mtd) { | 105 | #ifdef CONFIG_MTD_CONCAT |
| 106 | if (info->cmtd != info->list[0].mtd) { | ||
| 107 | del_mtd_device(info->cmtd); | ||
| 108 | mtd_concat_destroy(info->cmtd); | ||
| 109 | } | ||
| 110 | #endif | ||
| 111 | |||
| 112 | if (info->cmtd) { | ||
| 98 | if (OF_FLASH_PARTS(info)) { | 113 | if (OF_FLASH_PARTS(info)) { |
| 99 | del_mtd_partitions(info->mtd); | 114 | del_mtd_partitions(info->cmtd); |
| 100 | kfree(OF_FLASH_PARTS(info)); | 115 | kfree(OF_FLASH_PARTS(info)); |
| 101 | } else { | 116 | } else { |
| 102 | del_mtd_device(info->mtd); | 117 | del_mtd_device(info->cmtd); |
| 103 | } | 118 | } |
| 104 | map_destroy(info->mtd); | ||
| 105 | } | 119 | } |
| 106 | 120 | ||
| 107 | if (info->map.virt) | 121 | for (i = 0; i < info->list_size; i++) { |
| 108 | iounmap(info->map.virt); | 122 | if (info->list[i].mtd) |
| 123 | map_destroy(info->list[i].mtd); | ||
| 109 | 124 | ||
| 110 | if (info->res) { | 125 | if (info->list[i].map.virt) |
| 111 | release_resource(info->res); | 126 | iounmap(info->list[i].map.virt); |
| 112 | kfree(info->res); | 127 | |
| 128 | if (info->list[i].res) { | ||
| 129 | release_resource(info->list[i].res); | ||
| 130 | kfree(info->list[i].res); | ||
| 131 | } | ||
| 113 | } | 132 | } |
| 114 | 133 | ||
| 134 | kfree(info); | ||
| 135 | |||
| 115 | return 0; | 136 | return 0; |
| 116 | } | 137 | } |
| 117 | 138 | ||
| @@ -164,68 +185,130 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
| 164 | const char *probe_type = match->data; | 185 | const char *probe_type = match->data; |
| 165 | const u32 *width; | 186 | const u32 *width; |
| 166 | int err; | 187 | int err; |
| 167 | 188 | int i; | |
| 168 | err = -ENXIO; | 189 | int count; |
| 169 | if (of_address_to_resource(dp, 0, &res)) { | 190 | const u32 *p; |
| 170 | dev_err(&dev->dev, "Can't get IO address from device tree\n"); | 191 | int reg_tuple_size; |
| 192 | struct mtd_info **mtd_list = NULL; | ||
| 193 | |||
| 194 | reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); | ||
| 195 | |||
| 196 | /* | ||
| 197 | * Get number of "reg" tuples. Scan for MTD devices on area's | ||
| 198 | * described by each "reg" region. This makes it possible (including | ||
| 199 | * the concat support) to support the Intel P30 48F4400 chips which | ||
| 200 | * consists internally of 2 non-identical NOR chips on one die. | ||
| 201 | */ | ||
| 202 | p = of_get_property(dp, "reg", &count); | ||
| 203 | if (count % reg_tuple_size != 0) { | ||
| 204 | dev_err(&dev->dev, "Malformed reg property on %s\n", | ||
| 205 | dev->node->full_name); | ||
| 206 | err = -EINVAL; | ||
| 171 | goto err_out; | 207 | goto err_out; |
| 172 | } | 208 | } |
| 173 | 209 | count /= reg_tuple_size; | |
| 174 | dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n", | ||
| 175 | (unsigned long long)res.start, (unsigned long long)res.end); | ||
| 176 | 210 | ||
| 177 | err = -ENOMEM; | 211 | err = -ENOMEM; |
| 178 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 212 | info = kzalloc(sizeof(struct of_flash) + |
| 213 | sizeof(struct of_flash_list) * count, GFP_KERNEL); | ||
| 214 | if (!info) | ||
| 215 | goto err_out; | ||
| 216 | |||
| 217 | mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); | ||
| 179 | if (!info) | 218 | if (!info) |
| 180 | goto err_out; | 219 | goto err_out; |
| 181 | 220 | ||
| 182 | dev_set_drvdata(&dev->dev, info); | 221 | dev_set_drvdata(&dev->dev, info); |
| 183 | 222 | ||
| 184 | err = -EBUSY; | 223 | for (i = 0; i < count; i++) { |
| 185 | info->res = request_mem_region(res.start, res.end - res.start + 1, | 224 | err = -ENXIO; |
| 186 | dev_name(&dev->dev)); | 225 | if (of_address_to_resource(dp, i, &res)) { |
| 187 | if (!info->res) | 226 | dev_err(&dev->dev, "Can't get IO address from device" |
| 188 | goto err_out; | 227 | " tree\n"); |
| 228 | goto err_out; | ||
| 229 | } | ||
| 189 | 230 | ||
| 190 | err = -ENXIO; | 231 | dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n", |
| 191 | width = of_get_property(dp, "bank-width", NULL); | 232 | (unsigned long long)res.start, |
| 192 | if (!width) { | 233 | (unsigned long long)res.end); |
| 193 | dev_err(&dev->dev, "Can't get bank width from device tree\n"); | 234 | |
| 194 | goto err_out; | 235 | err = -EBUSY; |
| 195 | } | 236 | info->list[i].res = request_mem_region(res.start, res.end - |
| 237 | res.start + 1, | ||
| 238 | dev_name(&dev->dev)); | ||
| 239 | if (!info->list[i].res) | ||
| 240 | goto err_out; | ||
| 241 | |||
| 242 | err = -ENXIO; | ||
| 243 | width = of_get_property(dp, "bank-width", NULL); | ||
| 244 | if (!width) { | ||
| 245 | dev_err(&dev->dev, "Can't get bank width from device" | ||
| 246 | " tree\n"); | ||
| 247 | goto err_out; | ||
| 248 | } | ||
| 196 | 249 | ||
| 197 | info->map.name = dev_name(&dev->dev); | 250 | info->list[i].map.name = dev_name(&dev->dev); |
| 198 | info->map.phys = res.start; | 251 | info->list[i].map.phys = res.start; |
| 199 | info->map.size = res.end - res.start + 1; | 252 | info->list[i].map.size = res.end - res.start + 1; |
| 200 | info->map.bankwidth = *width; | 253 | info->list[i].map.bankwidth = *width; |
| 254 | |||
| 255 | err = -ENOMEM; | ||
| 256 | info->list[i].map.virt = ioremap(info->list[i].map.phys, | ||
| 257 | info->list[i].map.size); | ||
| 258 | if (!info->list[i].map.virt) { | ||
| 259 | dev_err(&dev->dev, "Failed to ioremap() flash" | ||
| 260 | " region\n"); | ||
| 261 | goto err_out; | ||
| 262 | } | ||
| 201 | 263 | ||
| 202 | err = -ENOMEM; | 264 | simple_map_init(&info->list[i].map); |
| 203 | info->map.virt = ioremap(info->map.phys, info->map.size); | ||
| 204 | if (!info->map.virt) { | ||
| 205 | dev_err(&dev->dev, "Failed to ioremap() flash region\n"); | ||
| 206 | goto err_out; | ||
| 207 | } | ||
| 208 | 265 | ||
| 209 | simple_map_init(&info->map); | 266 | if (probe_type) { |
| 267 | info->list[i].mtd = do_map_probe(probe_type, | ||
| 268 | &info->list[i].map); | ||
| 269 | } else { | ||
| 270 | info->list[i].mtd = obsolete_probe(dev, | ||
| 271 | &info->list[i].map); | ||
| 272 | } | ||
| 273 | mtd_list[i] = info->list[i].mtd; | ||
| 210 | 274 | ||
| 211 | if (probe_type) | 275 | err = -ENXIO; |
| 212 | info->mtd = do_map_probe(probe_type, &info->map); | 276 | if (!info->list[i].mtd) { |
| 213 | else | 277 | dev_err(&dev->dev, "do_map_probe() failed\n"); |
| 214 | info->mtd = obsolete_probe(dev, &info->map); | 278 | goto err_out; |
| 279 | } else { | ||
| 280 | info->list_size++; | ||
| 281 | } | ||
| 282 | info->list[i].mtd->owner = THIS_MODULE; | ||
| 283 | info->list[i].mtd->dev.parent = &dev->dev; | ||
| 284 | } | ||
| 215 | 285 | ||
| 216 | err = -ENXIO; | 286 | err = 0; |
| 217 | if (!info->mtd) { | 287 | if (info->list_size == 1) { |
| 218 | dev_err(&dev->dev, "do_map_probe() failed\n"); | 288 | info->cmtd = info->list[0].mtd; |
| 219 | goto err_out; | 289 | } else if (info->list_size > 1) { |
| 290 | /* | ||
| 291 | * We detected multiple devices. Concatenate them together. | ||
| 292 | */ | ||
| 293 | #ifdef CONFIG_MTD_CONCAT | ||
| 294 | info->cmtd = mtd_concat_create(mtd_list, info->list_size, | ||
| 295 | dev_name(&dev->dev)); | ||
| 296 | if (info->cmtd == NULL) | ||
| 297 | err = -ENXIO; | ||
| 298 | #else | ||
| 299 | printk(KERN_ERR "physmap_of: multiple devices " | ||
| 300 | "found but MTD concat support disabled.\n"); | ||
| 301 | err = -ENXIO; | ||
| 302 | #endif | ||
| 220 | } | 303 | } |
| 221 | info->mtd->owner = THIS_MODULE; | 304 | if (err) |
| 222 | info->mtd->dev.parent = &dev->dev; | 305 | goto err_out; |
| 223 | 306 | ||
| 224 | #ifdef CONFIG_MTD_PARTITIONS | 307 | #ifdef CONFIG_MTD_PARTITIONS |
| 225 | /* First look for RedBoot table or partitions on the command | 308 | /* First look for RedBoot table or partitions on the command |
| 226 | * line, these take precedence over device tree information */ | 309 | * line, these take precedence over device tree information */ |
| 227 | err = parse_mtd_partitions(info->mtd, part_probe_types, | 310 | err = parse_mtd_partitions(info->cmtd, part_probe_types, |
| 228 | &info->parts, 0); | 311 | &info->parts, 0); |
| 229 | if (err < 0) | 312 | if (err < 0) |
| 230 | return err; | 313 | return err; |
| 231 | 314 | ||
| @@ -244,15 +327,19 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
| 244 | } | 327 | } |
| 245 | 328 | ||
| 246 | if (err > 0) | 329 | if (err > 0) |
| 247 | add_mtd_partitions(info->mtd, info->parts, err); | 330 | add_mtd_partitions(info->cmtd, info->parts, err); |
| 248 | else | 331 | else |
| 249 | #endif | 332 | #endif |
| 250 | add_mtd_device(info->mtd); | 333 | add_mtd_device(info->cmtd); |
| 334 | |||
| 335 | kfree(mtd_list); | ||
| 251 | 336 | ||
| 252 | return 0; | 337 | return 0; |
| 253 | 338 | ||
| 254 | err_out: | 339 | err_out: |
| 340 | kfree(mtd_list); | ||
| 255 | of_flash_remove(dev); | 341 | of_flash_remove(dev); |
| 342 | |||
| 256 | return err; | 343 | return err; |
| 257 | } | 344 | } |
| 258 | 345 | ||
diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c deleted file mode 100644 index 30de5c0c09a9..000000000000 --- a/drivers/mtd/maps/pmcmsp-ramroot.c +++ /dev/null | |||
| @@ -1,104 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Mapping of the rootfs in a physical region of memory | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2007 PMC-Sierra Inc. | ||
| 5 | * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms of the GNU General Public License as published by the | ||
| 9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 10 | * option) any later version. | ||
| 11 | * | ||
| 12 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 13 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 14 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
| 15 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 16 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 17 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
| 18 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 19 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 21 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 22 | * | ||
| 23 | * You should have received a copy of the GNU General Public License along | ||
| 24 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 25 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <linux/module.h> | ||
| 29 | #include <linux/types.h> | ||
| 30 | #include <linux/kernel.h> | ||
| 31 | #include <linux/init.h> | ||
| 32 | #include <linux/slab.h> | ||
| 33 | #include <linux/fs.h> | ||
| 34 | #include <linux/root_dev.h> | ||
| 35 | #include <linux/mtd/mtd.h> | ||
| 36 | #include <linux/mtd/map.h> | ||
| 37 | |||
| 38 | #include <asm/io.h> | ||
| 39 | |||
| 40 | #include <msp_prom.h> | ||
| 41 | |||
| 42 | static struct mtd_info *rr_mtd; | ||
| 43 | |||
| 44 | struct map_info rr_map = { | ||
| 45 | .name = "ramroot", | ||
| 46 | .bankwidth = 4, | ||
| 47 | }; | ||
| 48 | |||
| 49 | static int __init init_rrmap(void) | ||
| 50 | { | ||
| 51 | void *ramroot_start; | ||
| 52 | unsigned long ramroot_size; | ||
| 53 | |||
| 54 | /* Check for supported rootfs types */ | ||
| 55 | if (get_ramroot(&ramroot_start, &ramroot_size)) { | ||
| 56 | rr_map.phys = CPHYSADDR(ramroot_start); | ||
| 57 | rr_map.size = ramroot_size; | ||
| 58 | |||
| 59 | printk(KERN_NOTICE | ||
| 60 | "PMC embedded root device: 0x%08lx @ 0x%08lx\n", | ||
| 61 | rr_map.size, (unsigned long)rr_map.phys); | ||
| 62 | } else { | ||
| 63 | printk(KERN_ERR | ||
| 64 | "init_rrmap: no supported embedded rootfs detected!\n"); | ||
| 65 | return -ENXIO; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* Map rootfs to I/O space for block device driver */ | ||
| 69 | rr_map.virt = ioremap(rr_map.phys, rr_map.size); | ||
| 70 | if (!rr_map.virt) { | ||
| 71 | printk(KERN_ERR "Failed to ioremap\n"); | ||
| 72 | return -EIO; | ||
| 73 | } | ||
| 74 | |||
| 75 | simple_map_init(&rr_map); | ||
| 76 | |||
| 77 | rr_mtd = do_map_probe("map_ram", &rr_map); | ||
| 78 | if (rr_mtd) { | ||
| 79 | rr_mtd->owner = THIS_MODULE; | ||
| 80 | |||
| 81 | add_mtd_device(rr_mtd); | ||
| 82 | |||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | iounmap(rr_map.virt); | ||
| 87 | return -ENXIO; | ||
| 88 | } | ||
| 89 | |||
| 90 | static void __exit cleanup_rrmap(void) | ||
| 91 | { | ||
| 92 | del_mtd_device(rr_mtd); | ||
| 93 | map_destroy(rr_mtd); | ||
| 94 | |||
| 95 | iounmap(rr_map.virt); | ||
| 96 | rr_map.virt = NULL; | ||
| 97 | } | ||
| 98 | |||
| 99 | MODULE_AUTHOR("PMC-Sierra, Inc"); | ||
| 100 | MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem"); | ||
| 101 | MODULE_LICENSE("GPL"); | ||
| 102 | |||
| 103 | module_init(init_rrmap); | ||
| 104 | module_exit(cleanup_rrmap); | ||
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 572d32fdf38a..643aa06b599e 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c | |||
| @@ -140,24 +140,6 @@ static int __devexit pxa2xx_flash_remove(struct platform_device *dev) | |||
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | #ifdef CONFIG_PM | 142 | #ifdef CONFIG_PM |
| 143 | static int pxa2xx_flash_suspend(struct platform_device *dev, pm_message_t state) | ||
| 144 | { | ||
| 145 | struct pxa2xx_flash_info *info = platform_get_drvdata(dev); | ||
| 146 | int ret = 0; | ||
| 147 | |||
| 148 | if (info->mtd && info->mtd->suspend) | ||
| 149 | ret = info->mtd->suspend(info->mtd); | ||
| 150 | return ret; | ||
| 151 | } | ||
| 152 | |||
| 153 | static int pxa2xx_flash_resume(struct platform_device *dev) | ||
| 154 | { | ||
| 155 | struct pxa2xx_flash_info *info = platform_get_drvdata(dev); | ||
| 156 | |||
| 157 | if (info->mtd && info->mtd->resume) | ||
| 158 | info->mtd->resume(info->mtd); | ||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | static void pxa2xx_flash_shutdown(struct platform_device *dev) | 143 | static void pxa2xx_flash_shutdown(struct platform_device *dev) |
| 162 | { | 144 | { |
| 163 | struct pxa2xx_flash_info *info = platform_get_drvdata(dev); | 145 | struct pxa2xx_flash_info *info = platform_get_drvdata(dev); |
| @@ -166,8 +148,6 @@ static void pxa2xx_flash_shutdown(struct platform_device *dev) | |||
| 166 | info->mtd->resume(info->mtd); | 148 | info->mtd->resume(info->mtd); |
| 167 | } | 149 | } |
| 168 | #else | 150 | #else |
| 169 | #define pxa2xx_flash_suspend NULL | ||
| 170 | #define pxa2xx_flash_resume NULL | ||
| 171 | #define pxa2xx_flash_shutdown NULL | 151 | #define pxa2xx_flash_shutdown NULL |
| 172 | #endif | 152 | #endif |
| 173 | 153 | ||
| @@ -178,8 +158,6 @@ static struct platform_driver pxa2xx_flash_driver = { | |||
| 178 | }, | 158 | }, |
| 179 | .probe = pxa2xx_flash_probe, | 159 | .probe = pxa2xx_flash_probe, |
| 180 | .remove = __devexit_p(pxa2xx_flash_remove), | 160 | .remove = __devexit_p(pxa2xx_flash_remove), |
| 181 | .suspend = pxa2xx_flash_suspend, | ||
| 182 | .resume = pxa2xx_flash_resume, | ||
| 183 | .shutdown = pxa2xx_flash_shutdown, | 161 | .shutdown = pxa2xx_flash_shutdown, |
| 184 | }; | 162 | }; |
| 185 | 163 | ||
diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index d39f0adac846..83ed64512c5e 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c | |||
| @@ -145,25 +145,6 @@ err_out: | |||
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | #ifdef CONFIG_PM | 147 | #ifdef CONFIG_PM |
| 148 | static int rbtx4939_flash_suspend(struct platform_device *dev, | ||
| 149 | pm_message_t state) | ||
| 150 | { | ||
| 151 | struct rbtx4939_flash_info *info = platform_get_drvdata(dev); | ||
| 152 | |||
| 153 | if (info->mtd->suspend) | ||
| 154 | return info->mtd->suspend(info->mtd); | ||
| 155 | return 0; | ||
| 156 | } | ||
| 157 | |||
| 158 | static int rbtx4939_flash_resume(struct platform_device *dev) | ||
| 159 | { | ||
| 160 | struct rbtx4939_flash_info *info = platform_get_drvdata(dev); | ||
| 161 | |||
| 162 | if (info->mtd->resume) | ||
| 163 | info->mtd->resume(info->mtd); | ||
| 164 | return 0; | ||
| 165 | } | ||
| 166 | |||
| 167 | static void rbtx4939_flash_shutdown(struct platform_device *dev) | 148 | static void rbtx4939_flash_shutdown(struct platform_device *dev) |
| 168 | { | 149 | { |
| 169 | struct rbtx4939_flash_info *info = platform_get_drvdata(dev); | 150 | struct rbtx4939_flash_info *info = platform_get_drvdata(dev); |
| @@ -173,16 +154,12 @@ static void rbtx4939_flash_shutdown(struct platform_device *dev) | |||
| 173 | info->mtd->resume(info->mtd); | 154 | info->mtd->resume(info->mtd); |
| 174 | } | 155 | } |
| 175 | #else | 156 | #else |
| 176 | #define rbtx4939_flash_suspend NULL | ||
| 177 | #define rbtx4939_flash_resume NULL | ||
| 178 | #define rbtx4939_flash_shutdown NULL | 157 | #define rbtx4939_flash_shutdown NULL |
| 179 | #endif | 158 | #endif |
| 180 | 159 | ||
| 181 | static struct platform_driver rbtx4939_flash_driver = { | 160 | static struct platform_driver rbtx4939_flash_driver = { |
| 182 | .probe = rbtx4939_flash_probe, | 161 | .probe = rbtx4939_flash_probe, |
| 183 | .remove = rbtx4939_flash_remove, | 162 | .remove = rbtx4939_flash_remove, |
| 184 | .suspend = rbtx4939_flash_suspend, | ||
| 185 | .resume = rbtx4939_flash_resume, | ||
| 186 | .shutdown = rbtx4939_flash_shutdown, | 163 | .shutdown = rbtx4939_flash_shutdown, |
| 187 | .driver = { | 164 | .driver = { |
| 188 | .name = "rbtx4939-flash", | 165 | .name = "rbtx4939-flash", |
diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 05e9362dc7f0..c6210f5118d1 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c | |||
| @@ -415,25 +415,6 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev) | |||
| 415 | } | 415 | } |
| 416 | 416 | ||
| 417 | #ifdef CONFIG_PM | 417 | #ifdef CONFIG_PM |
| 418 | static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state) | ||
| 419 | { | ||
| 420 | struct sa_info *info = platform_get_drvdata(dev); | ||
| 421 | int ret = 0; | ||
| 422 | |||
| 423 | if (info) | ||
| 424 | ret = info->mtd->suspend(info->mtd); | ||
| 425 | |||
| 426 | return ret; | ||
| 427 | } | ||
| 428 | |||
| 429 | static int sa1100_mtd_resume(struct platform_device *dev) | ||
| 430 | { | ||
| 431 | struct sa_info *info = platform_get_drvdata(dev); | ||
| 432 | if (info) | ||
| 433 | info->mtd->resume(info->mtd); | ||
| 434 | return 0; | ||
| 435 | } | ||
| 436 | |||
| 437 | static void sa1100_mtd_shutdown(struct platform_device *dev) | 418 | static void sa1100_mtd_shutdown(struct platform_device *dev) |
| 438 | { | 419 | { |
| 439 | struct sa_info *info = platform_get_drvdata(dev); | 420 | struct sa_info *info = platform_get_drvdata(dev); |
| @@ -441,16 +422,12 @@ static void sa1100_mtd_shutdown(struct platform_device *dev) | |||
| 441 | info->mtd->resume(info->mtd); | 422 | info->mtd->resume(info->mtd); |
| 442 | } | 423 | } |
| 443 | #else | 424 | #else |
| 444 | #define sa1100_mtd_suspend NULL | ||
| 445 | #define sa1100_mtd_resume NULL | ||
| 446 | #define sa1100_mtd_shutdown NULL | 425 | #define sa1100_mtd_shutdown NULL |
| 447 | #endif | 426 | #endif |
| 448 | 427 | ||
| 449 | static struct platform_driver sa1100_mtd_driver = { | 428 | static struct platform_driver sa1100_mtd_driver = { |
| 450 | .probe = sa1100_mtd_probe, | 429 | .probe = sa1100_mtd_probe, |
| 451 | .remove = __exit_p(sa1100_mtd_remove), | 430 | .remove = __exit_p(sa1100_mtd_remove), |
| 452 | .suspend = sa1100_mtd_suspend, | ||
| 453 | .resume = sa1100_mtd_resume, | ||
| 454 | .shutdown = sa1100_mtd_shutdown, | 431 | .shutdown = sa1100_mtd_shutdown, |
| 455 | .driver = { | 432 | .driver = { |
| 456 | .name = "sa1100-mtd", | 433 | .name = "sa1100-mtd", |
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 81756e397711..d4314fb88212 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c | |||
| @@ -22,15 +22,19 @@ | |||
| 22 | 22 | ||
| 23 | /****************************************************************************/ | 23 | /****************************************************************************/ |
| 24 | 24 | ||
| 25 | extern char _ebss; | ||
| 26 | |||
| 25 | struct map_info uclinux_ram_map = { | 27 | struct map_info uclinux_ram_map = { |
| 26 | .name = "RAM", | 28 | .name = "RAM", |
| 29 | .phys = (unsigned long)&_ebss, | ||
| 30 | .size = 0, | ||
| 27 | }; | 31 | }; |
| 28 | 32 | ||
| 29 | struct mtd_info *uclinux_ram_mtdinfo; | 33 | static struct mtd_info *uclinux_ram_mtdinfo; |
| 30 | 34 | ||
| 31 | /****************************************************************************/ | 35 | /****************************************************************************/ |
| 32 | 36 | ||
| 33 | struct mtd_partition uclinux_romfs[] = { | 37 | static struct mtd_partition uclinux_romfs[] = { |
| 34 | { .name = "ROMfs" } | 38 | { .name = "ROMfs" } |
| 35 | }; | 39 | }; |
| 36 | 40 | ||
| @@ -38,7 +42,7 @@ struct mtd_partition uclinux_romfs[] = { | |||
| 38 | 42 | ||
| 39 | /****************************************************************************/ | 43 | /****************************************************************************/ |
| 40 | 44 | ||
| 41 | int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, | 45 | static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, |
| 42 | size_t *retlen, void **virt, resource_size_t *phys) | 46 | size_t *retlen, void **virt, resource_size_t *phys) |
| 43 | { | 47 | { |
| 44 | struct map_info *map = mtd->priv; | 48 | struct map_info *map = mtd->priv; |
| @@ -55,12 +59,10 @@ static int __init uclinux_mtd_init(void) | |||
| 55 | { | 59 | { |
| 56 | struct mtd_info *mtd; | 60 | struct mtd_info *mtd; |
| 57 | struct map_info *mapp; | 61 | struct map_info *mapp; |
| 58 | extern char _ebss; | ||
| 59 | unsigned long addr = (unsigned long) &_ebss; | ||
| 60 | 62 | ||
| 61 | mapp = &uclinux_ram_map; | 63 | mapp = &uclinux_ram_map; |
| 62 | mapp->phys = addr; | 64 | if (!mapp->size) |
| 63 | mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(addr + 8)))); | 65 | mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8)))); |
| 64 | mapp->bankwidth = 4; | 66 | mapp->bankwidth = 4; |
| 65 | 67 | ||
| 66 | printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", | 68 | printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", |
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index aaac3b6800b7..c3f62654b6df 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c | |||
| @@ -291,7 +291,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) | |||
| 291 | gd->private_data = new; | 291 | gd->private_data = new; |
| 292 | new->blkcore_priv = gd; | 292 | new->blkcore_priv = gd; |
| 293 | gd->queue = tr->blkcore_priv->rq; | 293 | gd->queue = tr->blkcore_priv->rq; |
| 294 | gd->driverfs_dev = new->mtd->dev.parent; | 294 | gd->driverfs_dev = &new->mtd->dev; |
| 295 | 295 | ||
| 296 | if (new->readonly) | 296 | if (new->readonly) |
| 297 | set_disk_ro(gd, 1); | 297 | set_disk_ro(gd, 1); |
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 763d3f0a1f42..5b081cb84351 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
| 15 | #include <linux/smp_lock.h> | 15 | #include <linux/smp_lock.h> |
| 16 | #include <linux/backing-dev.h> | 16 | #include <linux/backing-dev.h> |
| 17 | #include <linux/compat.h> | ||
| 17 | 18 | ||
| 18 | #include <linux/mtd/mtd.h> | 19 | #include <linux/mtd/mtd.h> |
| 19 | #include <linux/mtd/compatmac.h> | 20 | #include <linux/mtd/compatmac.h> |
| @@ -355,6 +356,100 @@ static int otp_select_filemode(struct mtd_file_info *mfi, int mode) | |||
| 355 | # define otp_select_filemode(f,m) -EOPNOTSUPP | 356 | # define otp_select_filemode(f,m) -EOPNOTSUPP |
| 356 | #endif | 357 | #endif |
| 357 | 358 | ||
| 359 | static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd, | ||
| 360 | uint64_t start, uint32_t length, void __user *ptr, | ||
| 361 | uint32_t __user *retp) | ||
| 362 | { | ||
| 363 | struct mtd_oob_ops ops; | ||
| 364 | uint32_t retlen; | ||
| 365 | int ret = 0; | ||
| 366 | |||
| 367 | if (!(file->f_mode & FMODE_WRITE)) | ||
| 368 | return -EPERM; | ||
| 369 | |||
| 370 | if (length > 4096) | ||
| 371 | return -EINVAL; | ||
| 372 | |||
| 373 | if (!mtd->write_oob) | ||
| 374 | ret = -EOPNOTSUPP; | ||
| 375 | else | ||
| 376 | ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT; | ||
| 377 | |||
| 378 | if (ret) | ||
| 379 | return ret; | ||
| 380 | |||
| 381 | ops.ooblen = length; | ||
| 382 | ops.ooboffs = start & (mtd->oobsize - 1); | ||
| 383 | ops.datbuf = NULL; | ||
| 384 | ops.mode = MTD_OOB_PLACE; | ||
| 385 | |||
| 386 | if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) | ||
| 387 | return -EINVAL; | ||
| 388 | |||
| 389 | ops.oobbuf = kmalloc(length, GFP_KERNEL); | ||
| 390 | if (!ops.oobbuf) | ||
| 391 | return -ENOMEM; | ||
| 392 | |||
| 393 | if (copy_from_user(ops.oobbuf, ptr, length)) { | ||
| 394 | kfree(ops.oobbuf); | ||
| 395 | return -EFAULT; | ||
| 396 | } | ||
| 397 | |||
| 398 | start &= ~((uint64_t)mtd->oobsize - 1); | ||
| 399 | ret = mtd->write_oob(mtd, start, &ops); | ||
| 400 | |||
| 401 | if (ops.oobretlen > 0xFFFFFFFFU) | ||
| 402 | ret = -EOVERFLOW; | ||
| 403 | retlen = ops.oobretlen; | ||
| 404 | if (copy_to_user(retp, &retlen, sizeof(length))) | ||
| 405 | ret = -EFAULT; | ||
| 406 | |||
| 407 | kfree(ops.oobbuf); | ||
| 408 | return ret; | ||
| 409 | } | ||
| 410 | |||
| 411 | static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start, | ||
| 412 | uint32_t length, void __user *ptr, uint32_t __user *retp) | ||
| 413 | { | ||
| 414 | struct mtd_oob_ops ops; | ||
| 415 | int ret = 0; | ||
| 416 | |||
| 417 | if (length > 4096) | ||
| 418 | return -EINVAL; | ||
| 419 | |||
| 420 | if (!mtd->read_oob) | ||
| 421 | ret = -EOPNOTSUPP; | ||
| 422 | else | ||
| 423 | ret = access_ok(VERIFY_WRITE, ptr, | ||
| 424 | length) ? 0 : -EFAULT; | ||
| 425 | if (ret) | ||
| 426 | return ret; | ||
| 427 | |||
| 428 | ops.ooblen = length; | ||
| 429 | ops.ooboffs = start & (mtd->oobsize - 1); | ||
| 430 | ops.datbuf = NULL; | ||
| 431 | ops.mode = MTD_OOB_PLACE; | ||
| 432 | |||
| 433 | if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) | ||
| 434 | return -EINVAL; | ||
| 435 | |||
| 436 | ops.oobbuf = kmalloc(length, GFP_KERNEL); | ||
| 437 | if (!ops.oobbuf) | ||
| 438 | return -ENOMEM; | ||
| 439 | |||
| 440 | start &= ~((uint64_t)mtd->oobsize - 1); | ||
| 441 | ret = mtd->read_oob(mtd, start, &ops); | ||
| 442 | |||
| 443 | if (put_user(ops.oobretlen, retp)) | ||
| 444 | ret = -EFAULT; | ||
| 445 | else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf, | ||
| 446 | ops.oobretlen)) | ||
| 447 | ret = -EFAULT; | ||
| 448 | |||
| 449 | kfree(ops.oobbuf); | ||
| 450 | return ret; | ||
| 451 | } | ||
| 452 | |||
| 358 | static int mtd_ioctl(struct inode *inode, struct file *file, | 453 | static int mtd_ioctl(struct inode *inode, struct file *file, |
| 359 | u_int cmd, u_long arg) | 454 | u_int cmd, u_long arg) |
| 360 | { | 455 | { |
| @@ -417,6 +512,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
| 417 | break; | 512 | break; |
| 418 | 513 | ||
| 419 | case MEMERASE: | 514 | case MEMERASE: |
| 515 | case MEMERASE64: | ||
| 420 | { | 516 | { |
| 421 | struct erase_info *erase; | 517 | struct erase_info *erase; |
| 422 | 518 | ||
| @@ -427,20 +523,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
| 427 | if (!erase) | 523 | if (!erase) |
| 428 | ret = -ENOMEM; | 524 | ret = -ENOMEM; |
| 429 | else { | 525 | else { |
| 430 | struct erase_info_user einfo; | ||
| 431 | |||
| 432 | wait_queue_head_t waitq; | 526 | wait_queue_head_t waitq; |
| 433 | DECLARE_WAITQUEUE(wait, current); | 527 | DECLARE_WAITQUEUE(wait, current); |
| 434 | 528 | ||
| 435 | init_waitqueue_head(&waitq); | 529 | init_waitqueue_head(&waitq); |
| 436 | 530 | ||
| 437 | if (copy_from_user(&einfo, argp, | 531 | if (cmd == MEMERASE64) { |
| 438 | sizeof(struct erase_info_user))) { | 532 | struct erase_info_user64 einfo64; |
| 439 | kfree(erase); | 533 | |
| 440 | return -EFAULT; | 534 | if (copy_from_user(&einfo64, argp, |
| 535 | sizeof(struct erase_info_user64))) { | ||
| 536 | kfree(erase); | ||
| 537 | return -EFAULT; | ||
| 538 | } | ||
| 539 | erase->addr = einfo64.start; | ||
| 540 | erase->len = einfo64.length; | ||
| 541 | } else { | ||
| 542 | struct erase_info_user einfo32; | ||
| 543 | |||
| 544 | if (copy_from_user(&einfo32, argp, | ||
| 545 | sizeof(struct erase_info_user))) { | ||
| 546 | kfree(erase); | ||
| 547 | return -EFAULT; | ||
| 548 | } | ||
| 549 | erase->addr = einfo32.start; | ||
| 550 | erase->len = einfo32.length; | ||
| 441 | } | 551 | } |
| 442 | erase->addr = einfo.start; | ||
| 443 | erase->len = einfo.length; | ||
| 444 | erase->mtd = mtd; | 552 | erase->mtd = mtd; |
| 445 | erase->callback = mtdchar_erase_callback; | 553 | erase->callback = mtdchar_erase_callback; |
| 446 | erase->priv = (unsigned long)&waitq; | 554 | erase->priv = (unsigned long)&waitq; |
| @@ -474,100 +582,56 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
| 474 | case MEMWRITEOOB: | 582 | case MEMWRITEOOB: |
| 475 | { | 583 | { |
| 476 | struct mtd_oob_buf buf; | 584 | struct mtd_oob_buf buf; |
| 477 | struct mtd_oob_ops ops; | 585 | struct mtd_oob_buf __user *buf_user = argp; |
| 478 | struct mtd_oob_buf __user *user_buf = argp; | ||
| 479 | uint32_t retlen; | ||
| 480 | |||
| 481 | if(!(file->f_mode & FMODE_WRITE)) | ||
| 482 | return -EPERM; | ||
| 483 | |||
| 484 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) | ||
| 485 | return -EFAULT; | ||
| 486 | |||
| 487 | if (buf.length > 4096) | ||
| 488 | return -EINVAL; | ||
| 489 | |||
| 490 | if (!mtd->write_oob) | ||
| 491 | ret = -EOPNOTSUPP; | ||
| 492 | else | ||
| 493 | ret = access_ok(VERIFY_READ, buf.ptr, | ||
| 494 | buf.length) ? 0 : EFAULT; | ||
| 495 | |||
| 496 | if (ret) | ||
| 497 | return ret; | ||
| 498 | |||
| 499 | ops.ooblen = buf.length; | ||
| 500 | ops.ooboffs = buf.start & (mtd->oobsize - 1); | ||
| 501 | ops.datbuf = NULL; | ||
| 502 | ops.mode = MTD_OOB_PLACE; | ||
| 503 | |||
| 504 | if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) | ||
| 505 | return -EINVAL; | ||
| 506 | |||
| 507 | ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); | ||
| 508 | if (!ops.oobbuf) | ||
| 509 | return -ENOMEM; | ||
| 510 | |||
| 511 | if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { | ||
| 512 | kfree(ops.oobbuf); | ||
| 513 | return -EFAULT; | ||
| 514 | } | ||
| 515 | 586 | ||
| 516 | buf.start &= ~(mtd->oobsize - 1); | 587 | /* NOTE: writes return length to buf_user->length */ |
| 517 | ret = mtd->write_oob(mtd, buf.start, &ops); | 588 | if (copy_from_user(&buf, argp, sizeof(buf))) |
| 518 | |||
| 519 | if (ops.oobretlen > 0xFFFFFFFFU) | ||
| 520 | ret = -EOVERFLOW; | ||
| 521 | retlen = ops.oobretlen; | ||
| 522 | if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length))) | ||
| 523 | ret = -EFAULT; | 589 | ret = -EFAULT; |
| 524 | 590 | else | |
| 525 | kfree(ops.oobbuf); | 591 | ret = mtd_do_writeoob(file, mtd, buf.start, buf.length, |
| 592 | buf.ptr, &buf_user->length); | ||
| 526 | break; | 593 | break; |
| 527 | |||
| 528 | } | 594 | } |
| 529 | 595 | ||
| 530 | case MEMREADOOB: | 596 | case MEMREADOOB: |
| 531 | { | 597 | { |
| 532 | struct mtd_oob_buf buf; | 598 | struct mtd_oob_buf buf; |
| 533 | struct mtd_oob_ops ops; | 599 | struct mtd_oob_buf __user *buf_user = argp; |
| 534 | |||
| 535 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) | ||
| 536 | return -EFAULT; | ||
| 537 | |||
| 538 | if (buf.length > 4096) | ||
| 539 | return -EINVAL; | ||
| 540 | 600 | ||
| 541 | if (!mtd->read_oob) | 601 | /* NOTE: writes return length to buf_user->start */ |
| 542 | ret = -EOPNOTSUPP; | 602 | if (copy_from_user(&buf, argp, sizeof(buf))) |
| 603 | ret = -EFAULT; | ||
| 543 | else | 604 | else |
| 544 | ret = access_ok(VERIFY_WRITE, buf.ptr, | 605 | ret = mtd_do_readoob(mtd, buf.start, buf.length, |
| 545 | buf.length) ? 0 : -EFAULT; | 606 | buf.ptr, &buf_user->start); |
| 546 | if (ret) | 607 | break; |
| 547 | return ret; | 608 | } |
| 548 | |||
| 549 | ops.ooblen = buf.length; | ||
| 550 | ops.ooboffs = buf.start & (mtd->oobsize - 1); | ||
| 551 | ops.datbuf = NULL; | ||
| 552 | ops.mode = MTD_OOB_PLACE; | ||
| 553 | 609 | ||
| 554 | if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) | 610 | case MEMWRITEOOB64: |
| 555 | return -EINVAL; | 611 | { |
| 612 | struct mtd_oob_buf64 buf; | ||
| 613 | struct mtd_oob_buf64 __user *buf_user = argp; | ||
| 556 | 614 | ||
| 557 | ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); | 615 | if (copy_from_user(&buf, argp, sizeof(buf))) |
| 558 | if (!ops.oobbuf) | 616 | ret = -EFAULT; |
| 559 | return -ENOMEM; | 617 | else |
| 618 | ret = mtd_do_writeoob(file, mtd, buf.start, buf.length, | ||
| 619 | (void __user *)(uintptr_t)buf.usr_ptr, | ||
| 620 | &buf_user->length); | ||
| 621 | break; | ||
| 622 | } | ||
| 560 | 623 | ||
| 561 | buf.start &= ~(mtd->oobsize - 1); | 624 | case MEMREADOOB64: |
| 562 | ret = mtd->read_oob(mtd, buf.start, &ops); | 625 | { |
| 626 | struct mtd_oob_buf64 buf; | ||
| 627 | struct mtd_oob_buf64 __user *buf_user = argp; | ||
| 563 | 628 | ||
| 564 | if (put_user(ops.oobretlen, (uint32_t __user *)argp)) | 629 | if (copy_from_user(&buf, argp, sizeof(buf))) |
| 565 | ret = -EFAULT; | ||
| 566 | else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf, | ||
| 567 | ops.oobretlen)) | ||
| 568 | ret = -EFAULT; | 630 | ret = -EFAULT; |
| 569 | 631 | else | |
| 570 | kfree(ops.oobbuf); | 632 | ret = mtd_do_readoob(mtd, buf.start, buf.length, |
| 633 | (void __user *)(uintptr_t)buf.usr_ptr, | ||
| 634 | &buf_user->length); | ||
| 571 | break; | 635 | break; |
| 572 | } | 636 | } |
| 573 | 637 | ||
| @@ -758,6 +822,68 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
| 758 | return ret; | 822 | return ret; |
| 759 | } /* memory_ioctl */ | 823 | } /* memory_ioctl */ |
| 760 | 824 | ||
| 825 | #ifdef CONFIG_COMPAT | ||
| 826 | |||
| 827 | struct mtd_oob_buf32 { | ||
| 828 | u_int32_t start; | ||
| 829 | u_int32_t length; | ||
| 830 | compat_caddr_t ptr; /* unsigned char* */ | ||
| 831 | }; | ||
| 832 | |||
| 833 | #define MEMWRITEOOB32 _IOWR('M', 3, struct mtd_oob_buf32) | ||
| 834 | #define MEMREADOOB32 _IOWR('M', 4, struct mtd_oob_buf32) | ||
| 835 | |||
| 836 | static long mtd_compat_ioctl(struct file *file, unsigned int cmd, | ||
| 837 | unsigned long arg) | ||
| 838 | { | ||
| 839 | struct inode *inode = file->f_path.dentry->d_inode; | ||
| 840 | struct mtd_file_info *mfi = file->private_data; | ||
| 841 | struct mtd_info *mtd = mfi->mtd; | ||
| 842 | void __user *argp = compat_ptr(arg); | ||
| 843 | int ret = 0; | ||
| 844 | |||
| 845 | lock_kernel(); | ||
| 846 | |||
| 847 | switch (cmd) { | ||
| 848 | case MEMWRITEOOB32: | ||
| 849 | { | ||
| 850 | struct mtd_oob_buf32 buf; | ||
| 851 | struct mtd_oob_buf32 __user *buf_user = argp; | ||
| 852 | |||
| 853 | if (copy_from_user(&buf, argp, sizeof(buf))) | ||
| 854 | ret = -EFAULT; | ||
| 855 | else | ||
| 856 | ret = mtd_do_writeoob(file, mtd, buf.start, | ||
| 857 | buf.length, compat_ptr(buf.ptr), | ||
| 858 | &buf_user->length); | ||
| 859 | break; | ||
| 860 | } | ||
| 861 | |||
| 862 | case MEMREADOOB32: | ||
| 863 | { | ||
| 864 | struct mtd_oob_buf32 buf; | ||
| 865 | struct mtd_oob_buf32 __user *buf_user = argp; | ||
| 866 | |||
| 867 | /* NOTE: writes return length to buf->start */ | ||
| 868 | if (copy_from_user(&buf, argp, sizeof(buf))) | ||
| 869 | ret = -EFAULT; | ||
| 870 | else | ||
| 871 | ret = mtd_do_readoob(mtd, buf.start, | ||
| 872 | buf.length, compat_ptr(buf.ptr), | ||
| 873 | &buf_user->start); | ||
| 874 | break; | ||
| 875 | } | ||
| 876 | default: | ||
| 877 | ret = mtd_ioctl(inode, file, cmd, (unsigned long)argp); | ||
| 878 | } | ||
| 879 | |||
| 880 | unlock_kernel(); | ||
| 881 | |||
| 882 | return ret; | ||
| 883 | } | ||
| 884 | |||
| 885 | #endif /* CONFIG_COMPAT */ | ||
| 886 | |||
| 761 | /* | 887 | /* |
| 762 | * try to determine where a shared mapping can be made | 888 | * try to determine where a shared mapping can be made |
| 763 | * - only supported for NOMMU at the moment (MMU can't doesn't copy private | 889 | * - only supported for NOMMU at the moment (MMU can't doesn't copy private |
| @@ -817,6 +943,9 @@ static const struct file_operations mtd_fops = { | |||
| 817 | .read = mtd_read, | 943 | .read = mtd_read, |
| 818 | .write = mtd_write, | 944 | .write = mtd_write, |
| 819 | .ioctl = mtd_ioctl, | 945 | .ioctl = mtd_ioctl, |
| 946 | #ifdef CONFIG_COMPAT | ||
| 947 | .compat_ioctl = mtd_compat_ioctl, | ||
| 948 | #endif | ||
| 820 | .open = mtd_open, | 949 | .open = mtd_open, |
| 821 | .release = mtd_close, | 950 | .release = mtd_close, |
| 822 | .mmap = mtd_mmap, | 951 | .mmap = mtd_mmap, |
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index bccb4b1ffc46..fac54a3fa3f1 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c | |||
| @@ -23,8 +23,15 @@ | |||
| 23 | 23 | ||
| 24 | #include "mtdcore.h" | 24 | #include "mtdcore.h" |
| 25 | 25 | ||
| 26 | 26 | static int mtd_cls_suspend(struct device *dev, pm_message_t state); | |
| 27 | static struct class *mtd_class; | 27 | static int mtd_cls_resume(struct device *dev); |
| 28 | |||
| 29 | static struct class mtd_class = { | ||
| 30 | .name = "mtd", | ||
| 31 | .owner = THIS_MODULE, | ||
| 32 | .suspend = mtd_cls_suspend, | ||
| 33 | .resume = mtd_cls_resume, | ||
| 34 | }; | ||
| 28 | 35 | ||
| 29 | /* These are exported solely for the purpose of mtd_blkdevs.c. You | 36 | /* These are exported solely for the purpose of mtd_blkdevs.c. You |
| 30 | should not use them for _anything_ else */ | 37 | should not use them for _anything_ else */ |
| @@ -52,7 +59,26 @@ static void mtd_release(struct device *dev) | |||
| 52 | 59 | ||
| 53 | /* remove /dev/mtdXro node if needed */ | 60 | /* remove /dev/mtdXro node if needed */ |
| 54 | if (index) | 61 | if (index) |
| 55 | device_destroy(mtd_class, index + 1); | 62 | device_destroy(&mtd_class, index + 1); |
| 63 | } | ||
| 64 | |||
| 65 | static int mtd_cls_suspend(struct device *dev, pm_message_t state) | ||
| 66 | { | ||
| 67 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
| 68 | |||
| 69 | if (mtd->suspend) | ||
| 70 | return mtd->suspend(mtd); | ||
| 71 | else | ||
| 72 | return 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | static int mtd_cls_resume(struct device *dev) | ||
| 76 | { | ||
| 77 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
| 78 | |||
| 79 | if (mtd->resume) | ||
| 80 | mtd->resume(mtd); | ||
| 81 | return 0; | ||
| 56 | } | 82 | } |
| 57 | 83 | ||
| 58 | static ssize_t mtd_type_show(struct device *dev, | 84 | static ssize_t mtd_type_show(struct device *dev, |
| @@ -269,7 +295,7 @@ int add_mtd_device(struct mtd_info *mtd) | |||
| 269 | * physical device. | 295 | * physical device. |
| 270 | */ | 296 | */ |
| 271 | mtd->dev.type = &mtd_devtype; | 297 | mtd->dev.type = &mtd_devtype; |
| 272 | mtd->dev.class = mtd_class; | 298 | mtd->dev.class = &mtd_class; |
| 273 | mtd->dev.devt = MTD_DEVT(i); | 299 | mtd->dev.devt = MTD_DEVT(i); |
| 274 | dev_set_name(&mtd->dev, "mtd%d", i); | 300 | dev_set_name(&mtd->dev, "mtd%d", i); |
| 275 | if (device_register(&mtd->dev) != 0) { | 301 | if (device_register(&mtd->dev) != 0) { |
| @@ -278,7 +304,7 @@ int add_mtd_device(struct mtd_info *mtd) | |||
| 278 | } | 304 | } |
| 279 | 305 | ||
| 280 | if (MTD_DEVT(i)) | 306 | if (MTD_DEVT(i)) |
| 281 | device_create(mtd_class, mtd->dev.parent, | 307 | device_create(&mtd_class, mtd->dev.parent, |
| 282 | MTD_DEVT(i) + 1, | 308 | MTD_DEVT(i) + 1, |
| 283 | NULL, "mtd%dro", i); | 309 | NULL, "mtd%dro", i); |
| 284 | 310 | ||
| @@ -604,11 +630,12 @@ done: | |||
| 604 | 630 | ||
| 605 | static int __init init_mtd(void) | 631 | static int __init init_mtd(void) |
| 606 | { | 632 | { |
| 607 | mtd_class = class_create(THIS_MODULE, "mtd"); | 633 | int ret; |
| 634 | ret = class_register(&mtd_class); | ||
| 608 | 635 | ||
| 609 | if (IS_ERR(mtd_class)) { | 636 | if (ret) { |
| 610 | pr_err("Error creating mtd class.\n"); | 637 | pr_err("Error registering mtd class: %d\n", ret); |
| 611 | return PTR_ERR(mtd_class); | 638 | return ret; |
| 612 | } | 639 | } |
| 613 | #ifdef CONFIG_PROC_FS | 640 | #ifdef CONFIG_PROC_FS |
| 614 | if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) | 641 | if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) |
| @@ -623,7 +650,7 @@ static void __exit cleanup_mtd(void) | |||
| 623 | if (proc_mtd) | 650 | if (proc_mtd) |
| 624 | remove_proc_entry( "mtd", NULL); | 651 | remove_proc_entry( "mtd", NULL); |
| 625 | #endif /* CONFIG_PROC_FS */ | 652 | #endif /* CONFIG_PROC_FS */ |
| 626 | class_destroy(mtd_class); | 653 | class_unregister(&mtd_class); |
| 627 | } | 654 | } |
| 628 | 655 | ||
| 629 | module_init(init_mtd); | 656 | module_init(init_mtd); |
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 29675edb44b4..349fcbe5cc0f 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
| @@ -27,9 +27,7 @@ struct mtd_part { | |||
| 27 | struct mtd_info mtd; | 27 | struct mtd_info mtd; |
| 28 | struct mtd_info *master; | 28 | struct mtd_info *master; |
| 29 | uint64_t offset; | 29 | uint64_t offset; |
| 30 | int index; | ||
| 31 | struct list_head list; | 30 | struct list_head list; |
| 32 | int registered; | ||
| 33 | }; | 31 | }; |
| 34 | 32 | ||
| 35 | /* | 33 | /* |
| @@ -321,8 +319,7 @@ int del_mtd_partitions(struct mtd_info *master) | |||
| 321 | list_for_each_entry_safe(slave, next, &mtd_partitions, list) | 319 | list_for_each_entry_safe(slave, next, &mtd_partitions, list) |
| 322 | if (slave->master == master) { | 320 | if (slave->master == master) { |
| 323 | list_del(&slave->list); | 321 | list_del(&slave->list); |
| 324 | if (slave->registered) | 322 | del_mtd_device(&slave->mtd); |
| 325 | del_mtd_device(&slave->mtd); | ||
| 326 | kfree(slave); | 323 | kfree(slave); |
| 327 | } | 324 | } |
| 328 | 325 | ||
| @@ -395,7 +392,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 395 | slave->mtd.get_fact_prot_info = part_get_fact_prot_info; | 392 | slave->mtd.get_fact_prot_info = part_get_fact_prot_info; |
| 396 | if (master->sync) | 393 | if (master->sync) |
| 397 | slave->mtd.sync = part_sync; | 394 | slave->mtd.sync = part_sync; |
| 398 | if (!partno && master->suspend && master->resume) { | 395 | if (!partno && !master->dev.class && master->suspend && master->resume) { |
| 399 | slave->mtd.suspend = part_suspend; | 396 | slave->mtd.suspend = part_suspend; |
| 400 | slave->mtd.resume = part_resume; | 397 | slave->mtd.resume = part_resume; |
| 401 | } | 398 | } |
| @@ -412,7 +409,6 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 412 | slave->mtd.erase = part_erase; | 409 | slave->mtd.erase = part_erase; |
| 413 | slave->master = master; | 410 | slave->master = master; |
| 414 | slave->offset = part->offset; | 411 | slave->offset = part->offset; |
| 415 | slave->index = partno; | ||
| 416 | 412 | ||
| 417 | if (slave->offset == MTDPART_OFS_APPEND) | 413 | if (slave->offset == MTDPART_OFS_APPEND) |
| 418 | slave->offset = cur_offset; | 414 | slave->offset = cur_offset; |
| @@ -500,15 +496,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 500 | } | 496 | } |
| 501 | 497 | ||
| 502 | out_register: | 498 | out_register: |
| 503 | if (part->mtdp) { | 499 | /* register our partition */ |
| 504 | /* store the object pointer (caller may or may not register it*/ | 500 | add_mtd_device(&slave->mtd); |
| 505 | *part->mtdp = &slave->mtd; | 501 | |
| 506 | slave->registered = 0; | ||
| 507 | } else { | ||
| 508 | /* register our partition */ | ||
| 509 | add_mtd_device(&slave->mtd); | ||
| 510 | slave->registered = 1; | ||
| 511 | } | ||
| 512 | return slave; | 502 | return slave; |
| 513 | } | 503 | } |
| 514 | 504 | ||
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f3276897859e..ce96c091f01b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
| @@ -74,6 +74,12 @@ config MTD_NAND_AMS_DELTA | |||
| 74 | help | 74 | help |
| 75 | Support for NAND flash on Amstrad E3 (Delta). | 75 | Support for NAND flash on Amstrad E3 (Delta). |
| 76 | 76 | ||
| 77 | config MTD_NAND_OMAP2 | ||
| 78 | tristate "NAND Flash device on OMAP2 and OMAP3" | ||
| 79 | depends on ARM && MTD_NAND && (ARCH_OMAP2 || ARCH_OMAP3) | ||
| 80 | help | ||
| 81 | Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. | ||
| 82 | |||
| 77 | config MTD_NAND_TS7250 | 83 | config MTD_NAND_TS7250 |
| 78 | tristate "NAND Flash device on TS-7250 board" | 84 | tristate "NAND Flash device on TS-7250 board" |
| 79 | depends on MACH_TS72XX | 85 | depends on MACH_TS72XX |
| @@ -139,27 +145,27 @@ config MTD_NAND_PPCHAMELEONEVB | |||
| 139 | This enables the NAND flash driver on the PPChameleon EVB Board. | 145 | This enables the NAND flash driver on the PPChameleon EVB Board. |
| 140 | 146 | ||
| 141 | config MTD_NAND_S3C2410 | 147 | config MTD_NAND_S3C2410 |
| 142 | tristate "NAND Flash support for S3C2410/S3C2440 SoC" | 148 | tristate "NAND Flash support for Samsung S3C SoCs" |
| 143 | depends on ARCH_S3C2410 | 149 | depends on ARCH_S3C2410 || ARCH_S3C64XX |
| 144 | help | 150 | help |
| 145 | This enables the NAND flash controller on the S3C2410 and S3C2440 | 151 | This enables the NAND flash controller on the S3C24xx and S3C64xx |
| 146 | SoCs | 152 | SoCs |
| 147 | 153 | ||
| 148 | No board specific support is done by this driver, each board | 154 | No board specific support is done by this driver, each board |
| 149 | must advertise a platform_device for the driver to attach. | 155 | must advertise a platform_device for the driver to attach. |
| 150 | 156 | ||
| 151 | config MTD_NAND_S3C2410_DEBUG | 157 | config MTD_NAND_S3C2410_DEBUG |
| 152 | bool "S3C2410 NAND driver debug" | 158 | bool "Samsung S3C NAND driver debug" |
| 153 | depends on MTD_NAND_S3C2410 | 159 | depends on MTD_NAND_S3C2410 |
| 154 | help | 160 | help |
| 155 | Enable debugging of the S3C2410 NAND driver | 161 | Enable debugging of the S3C NAND driver |
| 156 | 162 | ||
| 157 | config MTD_NAND_S3C2410_HWECC | 163 | config MTD_NAND_S3C2410_HWECC |
| 158 | bool "S3C2410 NAND Hardware ECC" | 164 | bool "Samsung S3C NAND Hardware ECC" |
| 159 | depends on MTD_NAND_S3C2410 | 165 | depends on MTD_NAND_S3C2410 |
| 160 | help | 166 | help |
| 161 | Enable the use of the S3C2410's internal ECC generator when | 167 | Enable the use of the controller's internal ECC generator when |
| 162 | using NAND. Early versions of the chip have had problems with | 168 | using NAND. Early versions of the chips have had problems with |
| 163 | incorrect ECC generation, and if using these, the default of | 169 | incorrect ECC generation, and if using these, the default of |
| 164 | software ECC is preferable. | 170 | software ECC is preferable. |
| 165 | 171 | ||
| @@ -171,7 +177,7 @@ config MTD_NAND_NDFC | |||
| 171 | NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs | 177 | NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs |
| 172 | 178 | ||
| 173 | config MTD_NAND_S3C2410_CLKSTOP | 179 | config MTD_NAND_S3C2410_CLKSTOP |
| 174 | bool "S3C2410 NAND IDLE clock stop" | 180 | bool "Samsung S3C NAND IDLE clock stop" |
| 175 | depends on MTD_NAND_S3C2410 | 181 | depends on MTD_NAND_S3C2410 |
| 176 | default n | 182 | default n |
| 177 | help | 183 | help |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index d33860ac42c3..f3a786b3cff3 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
| @@ -25,6 +25,7 @@ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o | |||
| 25 | obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o | 25 | obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o |
| 26 | obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o | 26 | obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o |
| 27 | obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o | 27 | obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o |
| 28 | obj-$(CONFIG_MTD_NAND_OMAP2) += omap2.o | ||
| 28 | obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o | 29 | obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o |
| 29 | obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o | 30 | obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o |
| 30 | obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o | 31 | obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o |
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 47a33cec3793..2802992b39da 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | 24 | ||
| 25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
| 26 | #include <linux/module.h> | 26 | #include <linux/module.h> |
| 27 | #include <linux/moduleparam.h> | ||
| 27 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
| 28 | #include <linux/mtd/mtd.h> | 29 | #include <linux/mtd/mtd.h> |
| 29 | #include <linux/mtd/nand.h> | 30 | #include <linux/mtd/nand.h> |
| @@ -47,6 +48,9 @@ | |||
| 47 | #define no_ecc 0 | 48 | #define no_ecc 0 |
| 48 | #endif | 49 | #endif |
| 49 | 50 | ||
| 51 | static int on_flash_bbt = 0; | ||
| 52 | module_param(on_flash_bbt, int, 0); | ||
| 53 | |||
| 50 | /* Register access macros */ | 54 | /* Register access macros */ |
| 51 | #define ecc_readl(add, reg) \ | 55 | #define ecc_readl(add, reg) \ |
| 52 | __raw_readl(add + ATMEL_ECC_##reg) | 56 | __raw_readl(add + ATMEL_ECC_##reg) |
| @@ -459,12 +463,17 @@ static int __init atmel_nand_probe(struct platform_device *pdev) | |||
| 459 | 463 | ||
| 460 | if (host->board->det_pin) { | 464 | if (host->board->det_pin) { |
| 461 | if (gpio_get_value(host->board->det_pin)) { | 465 | if (gpio_get_value(host->board->det_pin)) { |
| 462 | printk("No SmartMedia card inserted.\n"); | 466 | printk(KERN_INFO "No SmartMedia card inserted.\n"); |
| 463 | res = ENXIO; | 467 | res = ENXIO; |
| 464 | goto err_no_card; | 468 | goto err_no_card; |
| 465 | } | 469 | } |
| 466 | } | 470 | } |
| 467 | 471 | ||
| 472 | if (on_flash_bbt) { | ||
| 473 | printk(KERN_INFO "atmel_nand: Use On Flash BBT\n"); | ||
| 474 | nand_chip->options |= NAND_USE_FLASH_BBT; | ||
| 475 | } | ||
| 476 | |||
| 468 | /* first scan to find the device and get the page size */ | 477 | /* first scan to find the device and get the page size */ |
| 469 | if (nand_scan_ident(mtd, 1)) { | 478 | if (nand_scan_ident(mtd, 1)) { |
| 470 | res = -ENXIO; | 479 | res = -ENXIO; |
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 4c2a67ca801e..8506e7e606fd 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c | |||
| @@ -458,7 +458,7 @@ static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id) | |||
| 458 | return IRQ_HANDLED; | 458 | return IRQ_HANDLED; |
| 459 | } | 459 | } |
| 460 | 460 | ||
| 461 | static int bf5xx_nand_dma_rw(struct mtd_info *mtd, | 461 | static void bf5xx_nand_dma_rw(struct mtd_info *mtd, |
| 462 | uint8_t *buf, int is_read) | 462 | uint8_t *buf, int is_read) |
| 463 | { | 463 | { |
| 464 | struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); | 464 | struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); |
| @@ -496,11 +496,20 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd, | |||
| 496 | /* setup DMA register with Blackfin DMA API */ | 496 | /* setup DMA register with Blackfin DMA API */ |
| 497 | set_dma_config(CH_NFC, 0x0); | 497 | set_dma_config(CH_NFC, 0x0); |
| 498 | set_dma_start_addr(CH_NFC, (unsigned long) buf); | 498 | set_dma_start_addr(CH_NFC, (unsigned long) buf); |
| 499 | |||
| 500 | /* The DMAs have different size on BF52x and BF54x */ | ||
| 501 | #ifdef CONFIG_BF52x | ||
| 502 | set_dma_x_count(CH_NFC, (page_size >> 1)); | ||
| 503 | set_dma_x_modify(CH_NFC, 2); | ||
| 504 | val = DI_EN | WDSIZE_16; | ||
| 505 | #endif | ||
| 506 | |||
| 507 | #ifdef CONFIG_BF54x | ||
| 499 | set_dma_x_count(CH_NFC, (page_size >> 2)); | 508 | set_dma_x_count(CH_NFC, (page_size >> 2)); |
| 500 | set_dma_x_modify(CH_NFC, 4); | 509 | set_dma_x_modify(CH_NFC, 4); |
| 501 | |||
| 502 | /* setup write or read operation */ | ||
| 503 | val = DI_EN | WDSIZE_32; | 510 | val = DI_EN | WDSIZE_32; |
| 511 | #endif | ||
| 512 | /* setup write or read operation */ | ||
| 504 | if (is_read) | 513 | if (is_read) |
| 505 | val |= WNR; | 514 | val |= WNR; |
| 506 | set_dma_config(CH_NFC, val); | 515 | set_dma_config(CH_NFC, val); |
| @@ -512,8 +521,6 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd, | |||
| 512 | else | 521 | else |
| 513 | bfin_write_NFC_PGCTL(0x2); | 522 | bfin_write_NFC_PGCTL(0x2); |
| 514 | wait_for_completion(&info->dma_completion); | 523 | wait_for_completion(&info->dma_completion); |
| 515 | |||
| 516 | return 0; | ||
| 517 | } | 524 | } |
| 518 | 525 | ||
| 519 | static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, | 526 | static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, |
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 02700f769b8a..0fad6487e6f4 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c | |||
| @@ -44,7 +44,7 @@ | |||
| 44 | * and some flavors of secondary chipselect (e.g. based on A12) as used | 44 | * and some flavors of secondary chipselect (e.g. based on A12) as used |
| 45 | * with multichip packages. | 45 | * with multichip packages. |
| 46 | * | 46 | * |
| 47 | * The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC | 47 | * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC |
| 48 | * available on chips like the DM355 and OMAP-L137 and needed with the | 48 | * available on chips like the DM355 and OMAP-L137 and needed with the |
| 49 | * more error-prone MLC NAND chips. | 49 | * more error-prone MLC NAND chips. |
| 50 | * | 50 | * |
| @@ -54,11 +54,14 @@ | |||
| 54 | struct davinci_nand_info { | 54 | struct davinci_nand_info { |
| 55 | struct mtd_info mtd; | 55 | struct mtd_info mtd; |
| 56 | struct nand_chip chip; | 56 | struct nand_chip chip; |
| 57 | struct nand_ecclayout ecclayout; | ||
| 57 | 58 | ||
| 58 | struct device *dev; | 59 | struct device *dev; |
| 59 | struct clk *clk; | 60 | struct clk *clk; |
| 60 | bool partitioned; | 61 | bool partitioned; |
| 61 | 62 | ||
| 63 | bool is_readmode; | ||
| 64 | |||
| 62 | void __iomem *base; | 65 | void __iomem *base; |
| 63 | void __iomem *vaddr; | 66 | void __iomem *vaddr; |
| 64 | 67 | ||
| @@ -73,6 +76,7 @@ struct davinci_nand_info { | |||
| 73 | }; | 76 | }; |
| 74 | 77 | ||
| 75 | static DEFINE_SPINLOCK(davinci_nand_lock); | 78 | static DEFINE_SPINLOCK(davinci_nand_lock); |
| 79 | static bool ecc4_busy; | ||
| 76 | 80 | ||
| 77 | #define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd) | 81 | #define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd) |
| 78 | 82 | ||
| @@ -218,6 +222,192 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, | |||
| 218 | /*----------------------------------------------------------------------*/ | 222 | /*----------------------------------------------------------------------*/ |
| 219 | 223 | ||
| 220 | /* | 224 | /* |
| 225 | * 4-bit hardware ECC ... context maintained over entire AEMIF | ||
| 226 | * | ||
| 227 | * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME | ||
| 228 | * since that forces use of a problematic "infix OOB" layout. | ||
| 229 | * Among other things, it trashes manufacturer bad block markers. | ||
| 230 | * Also, and specific to this hardware, it ECC-protects the "prepad" | ||
| 231 | * in the OOB ... while having ECC protection for parts of OOB would | ||
| 232 | * seem useful, the current MTD stack sometimes wants to update the | ||
| 233 | * OOB without recomputing ECC. | ||
| 234 | */ | ||
| 235 | |||
| 236 | static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode) | ||
| 237 | { | ||
| 238 | struct davinci_nand_info *info = to_davinci_nand(mtd); | ||
| 239 | unsigned long flags; | ||
| 240 | u32 val; | ||
| 241 | |||
| 242 | spin_lock_irqsave(&davinci_nand_lock, flags); | ||
| 243 | |||
| 244 | /* Start 4-bit ECC calculation for read/write */ | ||
| 245 | val = davinci_nand_readl(info, NANDFCR_OFFSET); | ||
| 246 | val &= ~(0x03 << 4); | ||
| 247 | val |= (info->core_chipsel << 4) | BIT(12); | ||
| 248 | davinci_nand_writel(info, NANDFCR_OFFSET, val); | ||
| 249 | |||
| 250 | info->is_readmode = (mode == NAND_ECC_READ); | ||
| 251 | |||
| 252 | spin_unlock_irqrestore(&davinci_nand_lock, flags); | ||
| 253 | } | ||
| 254 | |||
| 255 | /* Read raw ECC code after writing to NAND. */ | ||
| 256 | static void | ||
| 257 | nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4]) | ||
| 258 | { | ||
| 259 | const u32 mask = 0x03ff03ff; | ||
| 260 | |||
| 261 | code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask; | ||
| 262 | code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask; | ||
| 263 | code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask; | ||
| 264 | code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask; | ||
| 265 | } | ||
| 266 | |||
| 267 | /* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */ | ||
| 268 | static int nand_davinci_calculate_4bit(struct mtd_info *mtd, | ||
| 269 | const u_char *dat, u_char *ecc_code) | ||
| 270 | { | ||
| 271 | struct davinci_nand_info *info = to_davinci_nand(mtd); | ||
| 272 | u32 raw_ecc[4], *p; | ||
| 273 | unsigned i; | ||
| 274 | |||
| 275 | /* After a read, terminate ECC calculation by a dummy read | ||
| 276 | * of some 4-bit ECC register. ECC covers everything that | ||
| 277 | * was read; correct() just uses the hardware state, so | ||
| 278 | * ecc_code is not needed. | ||
| 279 | */ | ||
| 280 | if (info->is_readmode) { | ||
| 281 | davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); | ||
| 282 | return 0; | ||
| 283 | } | ||
| 284 | |||
| 285 | /* Pack eight raw 10-bit ecc values into ten bytes, making | ||
| 286 | * two passes which each convert four values (in upper and | ||
| 287 | * lower halves of two 32-bit words) into five bytes. The | ||
| 288 | * ROM boot loader uses this same packing scheme. | ||
| 289 | */ | ||
| 290 | nand_davinci_readecc_4bit(info, raw_ecc); | ||
| 291 | for (i = 0, p = raw_ecc; i < 2; i++, p += 2) { | ||
| 292 | *ecc_code++ = p[0] & 0xff; | ||
| 293 | *ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc); | ||
| 294 | *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0); | ||
| 295 | *ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0); | ||
| 296 | *ecc_code++ = (p[1] >> 18) & 0xff; | ||
| 297 | } | ||
| 298 | |||
| 299 | return 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | /* Correct up to 4 bits in data we just read, using state left in the | ||
| 303 | * hardware plus the ecc_code computed when it was first written. | ||
| 304 | */ | ||
| 305 | static int nand_davinci_correct_4bit(struct mtd_info *mtd, | ||
| 306 | u_char *data, u_char *ecc_code, u_char *null) | ||
| 307 | { | ||
| 308 | int i; | ||
| 309 | struct davinci_nand_info *info = to_davinci_nand(mtd); | ||
| 310 | unsigned short ecc10[8]; | ||
| 311 | unsigned short *ecc16; | ||
| 312 | u32 syndrome[4]; | ||
| 313 | unsigned num_errors, corrected; | ||
| 314 | |||
| 315 | /* All bytes 0xff? It's an erased page; ignore its ECC. */ | ||
| 316 | for (i = 0; i < 10; i++) { | ||
| 317 | if (ecc_code[i] != 0xff) | ||
| 318 | goto compare; | ||
| 319 | } | ||
| 320 | return 0; | ||
| 321 | |||
| 322 | compare: | ||
| 323 | /* Unpack ten bytes into eight 10 bit values. We know we're | ||
| 324 | * little-endian, and use type punning for less shifting/masking. | ||
| 325 | */ | ||
| 326 | if (WARN_ON(0x01 & (unsigned) ecc_code)) | ||
| 327 | return -EINVAL; | ||
| 328 | ecc16 = (unsigned short *)ecc_code; | ||
| 329 | |||
| 330 | ecc10[0] = (ecc16[0] >> 0) & 0x3ff; | ||
| 331 | ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0); | ||
| 332 | ecc10[2] = (ecc16[1] >> 4) & 0x3ff; | ||
| 333 | ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc); | ||
| 334 | ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300); | ||
| 335 | ecc10[5] = (ecc16[3] >> 2) & 0x3ff; | ||
| 336 | ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0); | ||
| 337 | ecc10[7] = (ecc16[4] >> 6) & 0x3ff; | ||
| 338 | |||
| 339 | /* Tell ECC controller about the expected ECC codes. */ | ||
| 340 | for (i = 7; i >= 0; i--) | ||
| 341 | davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]); | ||
| 342 | |||
| 343 | /* Allow time for syndrome calculation ... then read it. | ||
| 344 | * A syndrome of all zeroes 0 means no detected errors. | ||
| 345 | */ | ||
| 346 | davinci_nand_readl(info, NANDFSR_OFFSET); | ||
| 347 | nand_davinci_readecc_4bit(info, syndrome); | ||
| 348 | if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) | ||
| 349 | return 0; | ||
| 350 | |||
| 351 | /* Start address calculation, and wait for it to complete. | ||
| 352 | * We _could_ start reading more data while this is working, | ||
| 353 | * to speed up the overall page read. | ||
| 354 | */ | ||
| 355 | davinci_nand_writel(info, NANDFCR_OFFSET, | ||
| 356 | davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13)); | ||
| 357 | for (;;) { | ||
| 358 | u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET); | ||
| 359 | |||
| 360 | switch ((fsr >> 8) & 0x0f) { | ||
| 361 | case 0: /* no error, should not happen */ | ||
| 362 | return 0; | ||
| 363 | case 1: /* five or more errors detected */ | ||
| 364 | return -EIO; | ||
| 365 | case 2: /* error addresses computed */ | ||
| 366 | case 3: | ||
| 367 | num_errors = 1 + ((fsr >> 16) & 0x03); | ||
| 368 | goto correct; | ||
| 369 | default: /* still working on it */ | ||
| 370 | cpu_relax(); | ||
| 371 | continue; | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | correct: | ||
| 376 | /* correct each error */ | ||
| 377 | for (i = 0, corrected = 0; i < num_errors; i++) { | ||
| 378 | int error_address, error_value; | ||
| 379 | |||
| 380 | if (i > 1) { | ||
| 381 | error_address = davinci_nand_readl(info, | ||
| 382 | NAND_ERR_ADD2_OFFSET); | ||
| 383 | error_value = davinci_nand_readl(info, | ||
| 384 | NAND_ERR_ERRVAL2_OFFSET); | ||
| 385 | } else { | ||
| 386 | error_address = davinci_nand_readl(info, | ||
| 387 | NAND_ERR_ADD1_OFFSET); | ||
| 388 | error_value = davinci_nand_readl(info, | ||
| 389 | NAND_ERR_ERRVAL1_OFFSET); | ||
| 390 | } | ||
| 391 | |||
| 392 | if (i & 1) { | ||
| 393 | error_address >>= 16; | ||
| 394 | error_value >>= 16; | ||
| 395 | } | ||
| 396 | error_address &= 0x3ff; | ||
| 397 | error_address = (512 + 7) - error_address; | ||
| 398 | |||
| 399 | if (error_address < 512) { | ||
| 400 | data[error_address] ^= error_value; | ||
| 401 | corrected++; | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | return corrected; | ||
| 406 | } | ||
| 407 | |||
| 408 | /*----------------------------------------------------------------------*/ | ||
| 409 | |||
| 410 | /* | ||
| 221 | * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's | 411 | * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's |
| 222 | * how these chips are normally wired. This translates to both 8 and 16 | 412 | * how these chips are normally wired. This translates to both 8 and 16 |
| 223 | * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4). | 413 | * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4). |
| @@ -294,6 +484,23 @@ static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info) | |||
| 294 | 484 | ||
| 295 | /*----------------------------------------------------------------------*/ | 485 | /*----------------------------------------------------------------------*/ |
| 296 | 486 | ||
| 487 | /* An ECC layout for using 4-bit ECC with small-page flash, storing | ||
| 488 | * ten ECC bytes plus the manufacturer's bad block marker byte, and | ||
| 489 | * and not overlapping the default BBT markers. | ||
| 490 | */ | ||
| 491 | static struct nand_ecclayout hwecc4_small __initconst = { | ||
| 492 | .eccbytes = 10, | ||
| 493 | .eccpos = { 0, 1, 2, 3, 4, | ||
| 494 | /* offset 5 holds the badblock marker */ | ||
| 495 | 6, 7, | ||
| 496 | 13, 14, 15, }, | ||
| 497 | .oobfree = { | ||
| 498 | {.offset = 8, .length = 5, }, | ||
| 499 | {.offset = 16, }, | ||
| 500 | }, | ||
| 501 | }; | ||
| 502 | |||
| 503 | |||
| 297 | static int __init nand_davinci_probe(struct platform_device *pdev) | 504 | static int __init nand_davinci_probe(struct platform_device *pdev) |
| 298 | { | 505 | { |
| 299 | struct davinci_nand_pdata *pdata = pdev->dev.platform_data; | 506 | struct davinci_nand_pdata *pdata = pdev->dev.platform_data; |
| @@ -306,6 +513,10 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 306 | uint32_t val; | 513 | uint32_t val; |
| 307 | nand_ecc_modes_t ecc_mode; | 514 | nand_ecc_modes_t ecc_mode; |
| 308 | 515 | ||
| 516 | /* insist on board-specific configuration */ | ||
| 517 | if (!pdata) | ||
| 518 | return -ENODEV; | ||
| 519 | |||
| 309 | /* which external chipselect will we be managing? */ | 520 | /* which external chipselect will we be managing? */ |
| 310 | if (pdev->id < 0 || pdev->id > 3) | 521 | if (pdev->id < 0 || pdev->id > 3) |
| 311 | return -ENODEV; | 522 | return -ENODEV; |
| @@ -351,7 +562,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 351 | info->chip.select_chip = nand_davinci_select_chip; | 562 | info->chip.select_chip = nand_davinci_select_chip; |
| 352 | 563 | ||
| 353 | /* options such as NAND_USE_FLASH_BBT or 16-bit widths */ | 564 | /* options such as NAND_USE_FLASH_BBT or 16-bit widths */ |
| 354 | info->chip.options = pdata ? pdata->options : 0; | 565 | info->chip.options = pdata->options; |
| 355 | 566 | ||
| 356 | info->ioaddr = (uint32_t __force) vaddr; | 567 | info->ioaddr = (uint32_t __force) vaddr; |
| 357 | 568 | ||
| @@ -360,14 +571,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 360 | info->mask_chipsel = pdata->mask_chipsel; | 571 | info->mask_chipsel = pdata->mask_chipsel; |
| 361 | 572 | ||
| 362 | /* use nandboot-capable ALE/CLE masks by default */ | 573 | /* use nandboot-capable ALE/CLE masks by default */ |
| 363 | if (pdata && pdata->mask_ale) | 574 | info->mask_ale = pdata->mask_cle ? : MASK_ALE; |
| 364 | info->mask_ale = pdata->mask_cle; | 575 | info->mask_cle = pdata->mask_cle ? : MASK_CLE; |
| 365 | else | ||
| 366 | info->mask_ale = MASK_ALE; | ||
| 367 | if (pdata && pdata->mask_cle) | ||
| 368 | info->mask_cle = pdata->mask_cle; | ||
| 369 | else | ||
| 370 | info->mask_cle = MASK_CLE; | ||
| 371 | 576 | ||
| 372 | /* Set address of hardware control function */ | 577 | /* Set address of hardware control function */ |
| 373 | info->chip.cmd_ctrl = nand_davinci_hwcontrol; | 578 | info->chip.cmd_ctrl = nand_davinci_hwcontrol; |
| @@ -377,30 +582,44 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 377 | info->chip.read_buf = nand_davinci_read_buf; | 582 | info->chip.read_buf = nand_davinci_read_buf; |
| 378 | info->chip.write_buf = nand_davinci_write_buf; | 583 | info->chip.write_buf = nand_davinci_write_buf; |
| 379 | 584 | ||
| 380 | /* use board-specific ECC config; else, the best available */ | 585 | /* Use board-specific ECC config */ |
| 381 | if (pdata) | 586 | ecc_mode = pdata->ecc_mode; |
| 382 | ecc_mode = pdata->ecc_mode; | ||
| 383 | else | ||
| 384 | ecc_mode = NAND_ECC_HW; | ||
| 385 | 587 | ||
| 588 | ret = -EINVAL; | ||
| 386 | switch (ecc_mode) { | 589 | switch (ecc_mode) { |
| 387 | case NAND_ECC_NONE: | 590 | case NAND_ECC_NONE: |
| 388 | case NAND_ECC_SOFT: | 591 | case NAND_ECC_SOFT: |
| 592 | pdata->ecc_bits = 0; | ||
| 389 | break; | 593 | break; |
| 390 | case NAND_ECC_HW: | 594 | case NAND_ECC_HW: |
| 391 | info->chip.ecc.calculate = nand_davinci_calculate_1bit; | 595 | if (pdata->ecc_bits == 4) { |
| 392 | info->chip.ecc.correct = nand_davinci_correct_1bit; | 596 | /* No sanity checks: CPUs must support this, |
| 393 | info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; | 597 | * and the chips may not use NAND_BUSWIDTH_16. |
| 598 | */ | ||
| 599 | |||
| 600 | /* No sharing 4-bit hardware between chipselects yet */ | ||
| 601 | spin_lock_irq(&davinci_nand_lock); | ||
| 602 | if (ecc4_busy) | ||
| 603 | ret = -EBUSY; | ||
| 604 | else | ||
| 605 | ecc4_busy = true; | ||
| 606 | spin_unlock_irq(&davinci_nand_lock); | ||
| 607 | |||
| 608 | if (ret == -EBUSY) | ||
| 609 | goto err_ecc; | ||
| 610 | |||
| 611 | info->chip.ecc.calculate = nand_davinci_calculate_4bit; | ||
| 612 | info->chip.ecc.correct = nand_davinci_correct_4bit; | ||
| 613 | info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; | ||
| 614 | info->chip.ecc.bytes = 10; | ||
| 615 | } else { | ||
| 616 | info->chip.ecc.calculate = nand_davinci_calculate_1bit; | ||
| 617 | info->chip.ecc.correct = nand_davinci_correct_1bit; | ||
| 618 | info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; | ||
| 619 | info->chip.ecc.bytes = 3; | ||
| 620 | } | ||
| 394 | info->chip.ecc.size = 512; | 621 | info->chip.ecc.size = 512; |
| 395 | info->chip.ecc.bytes = 3; | ||
| 396 | break; | 622 | break; |
| 397 | case NAND_ECC_HW_SYNDROME: | ||
| 398 | /* FIXME implement */ | ||
| 399 | info->chip.ecc.size = 512; | ||
| 400 | info->chip.ecc.bytes = 10; | ||
| 401 | |||
| 402 | dev_warn(&pdev->dev, "4-bit ECC nyet supported\n"); | ||
| 403 | /* FALL THROUGH */ | ||
| 404 | default: | 623 | default: |
| 405 | ret = -EINVAL; | 624 | ret = -EINVAL; |
| 406 | goto err_ecc; | 625 | goto err_ecc; |
| @@ -441,12 +660,56 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 441 | spin_unlock_irq(&davinci_nand_lock); | 660 | spin_unlock_irq(&davinci_nand_lock); |
| 442 | 661 | ||
| 443 | /* Scan to find existence of the device(s) */ | 662 | /* Scan to find existence of the device(s) */ |
| 444 | ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1); | 663 | ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1); |
| 445 | if (ret < 0) { | 664 | if (ret < 0) { |
| 446 | dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); | 665 | dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); |
| 447 | goto err_scan; | 666 | goto err_scan; |
| 448 | } | 667 | } |
| 449 | 668 | ||
| 669 | /* Update ECC layout if needed ... for 1-bit HW ECC, the default | ||
| 670 | * is OK, but it allocates 6 bytes when only 3 are needed (for | ||
| 671 | * each 512 bytes). For the 4-bit HW ECC, that default is not | ||
| 672 | * usable: 10 bytes are needed, not 6. | ||
| 673 | */ | ||
| 674 | if (pdata->ecc_bits == 4) { | ||
| 675 | int chunks = info->mtd.writesize / 512; | ||
| 676 | |||
| 677 | if (!chunks || info->mtd.oobsize < 16) { | ||
| 678 | dev_dbg(&pdev->dev, "too small\n"); | ||
| 679 | ret = -EINVAL; | ||
| 680 | goto err_scan; | ||
| 681 | } | ||
| 682 | |||
| 683 | /* For small page chips, preserve the manufacturer's | ||
| 684 | * badblock marking data ... and make sure a flash BBT | ||
| 685 | * table marker fits in the free bytes. | ||
| 686 | */ | ||
| 687 | if (chunks == 1) { | ||
| 688 | info->ecclayout = hwecc4_small; | ||
| 689 | info->ecclayout.oobfree[1].length = | ||
| 690 | info->mtd.oobsize - 16; | ||
| 691 | goto syndrome_done; | ||
| 692 | } | ||
| 693 | |||
| 694 | /* For large page chips we'll be wanting to use a | ||
| 695 | * not-yet-implemented mode that reads OOB data | ||
| 696 | * before reading the body of the page, to avoid | ||
| 697 | * the "infix OOB" model of NAND_ECC_HW_SYNDROME | ||
| 698 | * (and preserve manufacturer badblock markings). | ||
| 699 | */ | ||
| 700 | dev_warn(&pdev->dev, "no 4-bit ECC support yet " | ||
| 701 | "for large page NAND\n"); | ||
| 702 | ret = -EIO; | ||
| 703 | goto err_scan; | ||
| 704 | |||
| 705 | syndrome_done: | ||
| 706 | info->chip.ecc.layout = &info->ecclayout; | ||
| 707 | } | ||
| 708 | |||
| 709 | ret = nand_scan_tail(&info->mtd); | ||
| 710 | if (ret < 0) | ||
| 711 | goto err_scan; | ||
| 712 | |||
| 450 | if (mtd_has_partitions()) { | 713 | if (mtd_has_partitions()) { |
| 451 | struct mtd_partition *mtd_parts = NULL; | 714 | struct mtd_partition *mtd_parts = NULL; |
| 452 | int mtd_parts_nb = 0; | 715 | int mtd_parts_nb = 0; |
| @@ -455,22 +718,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 455 | static const char *probes[] __initconst = | 718 | static const char *probes[] __initconst = |
| 456 | { "cmdlinepart", NULL }; | 719 | { "cmdlinepart", NULL }; |
| 457 | 720 | ||
| 458 | const char *master_name; | ||
| 459 | |||
| 460 | /* Set info->mtd.name = 0 temporarily */ | ||
| 461 | master_name = info->mtd.name; | ||
| 462 | info->mtd.name = (char *)0; | ||
| 463 | |||
| 464 | /* info->mtd.name == 0, means: don't bother checking | ||
| 465 | <mtd-id> */ | ||
| 466 | mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, | 721 | mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, |
| 467 | &mtd_parts, 0); | 722 | &mtd_parts, 0); |
| 468 | |||
| 469 | /* Restore info->mtd.name */ | ||
| 470 | info->mtd.name = master_name; | ||
| 471 | } | 723 | } |
| 472 | 724 | ||
| 473 | if (mtd_parts_nb <= 0 && pdata) { | 725 | if (mtd_parts_nb <= 0) { |
| 474 | mtd_parts = pdata->parts; | 726 | mtd_parts = pdata->parts; |
| 475 | mtd_parts_nb = pdata->nr_parts; | 727 | mtd_parts_nb = pdata->nr_parts; |
| 476 | } | 728 | } |
| @@ -483,7 +735,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 483 | info->partitioned = true; | 735 | info->partitioned = true; |
| 484 | } | 736 | } |
| 485 | 737 | ||
| 486 | } else if (pdata && pdata->nr_parts) { | 738 | } else if (pdata->nr_parts) { |
| 487 | dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", | 739 | dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", |
| 488 | pdata->nr_parts, info->mtd.name); | 740 | pdata->nr_parts, info->mtd.name); |
| 489 | } | 741 | } |
| @@ -509,6 +761,11 @@ err_scan: | |||
| 509 | err_clk_enable: | 761 | err_clk_enable: |
| 510 | clk_put(info->clk); | 762 | clk_put(info->clk); |
| 511 | 763 | ||
| 764 | spin_lock_irq(&davinci_nand_lock); | ||
| 765 | if (ecc_mode == NAND_ECC_HW_SYNDROME) | ||
| 766 | ecc4_busy = false; | ||
| 767 | spin_unlock_irq(&davinci_nand_lock); | ||
| 768 | |||
| 512 | err_ecc: | 769 | err_ecc: |
| 513 | err_clk: | 770 | err_clk: |
| 514 | err_ioremap: | 771 | err_ioremap: |
| @@ -532,6 +789,11 @@ static int __exit nand_davinci_remove(struct platform_device *pdev) | |||
| 532 | else | 789 | else |
| 533 | status = del_mtd_device(&info->mtd); | 790 | status = del_mtd_device(&info->mtd); |
| 534 | 791 | ||
| 792 | spin_lock_irq(&davinci_nand_lock); | ||
| 793 | if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) | ||
| 794 | ecc4_busy = false; | ||
| 795 | spin_unlock_irq(&davinci_nand_lock); | ||
| 796 | |||
| 535 | iounmap(info->base); | 797 | iounmap(info->base); |
| 536 | iounmap(info->vaddr); | 798 | iounmap(info->vaddr); |
| 537 | 799 | ||
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 40c26080ecda..76beea40d2cf 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c | |||
| @@ -138,7 +138,14 @@ static struct nand_ecclayout nand_hw_eccoob_8 = { | |||
| 138 | static struct nand_ecclayout nand_hw_eccoob_16 = { | 138 | static struct nand_ecclayout nand_hw_eccoob_16 = { |
| 139 | .eccbytes = 5, | 139 | .eccbytes = 5, |
| 140 | .eccpos = {6, 7, 8, 9, 10}, | 140 | .eccpos = {6, 7, 8, 9, 10}, |
| 141 | .oobfree = {{0, 6}, {12, 4}, } | 141 | .oobfree = {{0, 5}, {11, 5}, } |
| 142 | }; | ||
| 143 | |||
| 144 | static struct nand_ecclayout nand_hw_eccoob_64 = { | ||
| 145 | .eccbytes = 20, | ||
| 146 | .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, | ||
| 147 | 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, | ||
| 148 | .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, } | ||
| 142 | }; | 149 | }; |
| 143 | 150 | ||
| 144 | #ifdef CONFIG_MTD_PARTITIONS | 151 | #ifdef CONFIG_MTD_PARTITIONS |
| @@ -192,7 +199,7 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries, | |||
| 192 | } | 199 | } |
| 193 | udelay(1); | 200 | udelay(1); |
| 194 | } | 201 | } |
| 195 | if (max_retries <= 0) | 202 | if (max_retries < 0) |
| 196 | DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", | 203 | DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", |
| 197 | __func__, param); | 204 | __func__, param); |
| 198 | } | 205 | } |
| @@ -795,9 +802,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, | |||
| 795 | send_addr(host, (page_addr & 0xff), false); | 802 | send_addr(host, (page_addr & 0xff), false); |
| 796 | 803 | ||
| 797 | if (host->pagesize_2k) { | 804 | if (host->pagesize_2k) { |
| 798 | send_addr(host, (page_addr >> 8) & 0xFF, false); | 805 | if (mtd->size >= 0x10000000) { |
| 799 | if (mtd->size >= 0x40000000) | 806 | /* paddr_8 - paddr_15 */ |
| 807 | send_addr(host, (page_addr >> 8) & 0xff, false); | ||
| 800 | send_addr(host, (page_addr >> 16) & 0xff, true); | 808 | send_addr(host, (page_addr >> 16) & 0xff, true); |
| 809 | } else | ||
| 810 | /* paddr_8 - paddr_15 */ | ||
| 811 | send_addr(host, (page_addr >> 8) & 0xff, true); | ||
| 801 | } else { | 812 | } else { |
| 802 | /* One more address cycle for higher density devices */ | 813 | /* One more address cycle for higher density devices */ |
| 803 | if (mtd->size >= 0x4000000) { | 814 | if (mtd->size >= 0x4000000) { |
| @@ -923,7 +934,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
| 923 | this->ecc.mode = NAND_ECC_HW; | 934 | this->ecc.mode = NAND_ECC_HW; |
| 924 | this->ecc.size = 512; | 935 | this->ecc.size = 512; |
| 925 | this->ecc.bytes = 3; | 936 | this->ecc.bytes = 3; |
| 926 | this->ecc.layout = &nand_hw_eccoob_8; | ||
| 927 | tmp = readw(host->regs + NFC_CONFIG1); | 937 | tmp = readw(host->regs + NFC_CONFIG1); |
| 928 | tmp |= NFC_ECC_EN; | 938 | tmp |= NFC_ECC_EN; |
| 929 | writew(tmp, host->regs + NFC_CONFIG1); | 939 | writew(tmp, host->regs + NFC_CONFIG1); |
| @@ -957,12 +967,44 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
| 957 | this->ecc.layout = &nand_hw_eccoob_16; | 967 | this->ecc.layout = &nand_hw_eccoob_16; |
| 958 | } | 968 | } |
| 959 | 969 | ||
| 960 | host->pagesize_2k = 0; | 970 | /* first scan to find the device and get the page size */ |
| 971 | if (nand_scan_ident(mtd, 1)) { | ||
| 972 | err = -ENXIO; | ||
| 973 | goto escan; | ||
| 974 | } | ||
| 961 | 975 | ||
| 962 | /* Scan to find existence of the device */ | 976 | host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; |
| 963 | if (nand_scan(mtd, 1)) { | 977 | |
| 964 | DEBUG(MTD_DEBUG_LEVEL0, | 978 | if (this->ecc.mode == NAND_ECC_HW) { |
| 965 | "MXC_ND: Unable to find any NAND device.\n"); | 979 | switch (mtd->oobsize) { |
| 980 | case 8: | ||
| 981 | this->ecc.layout = &nand_hw_eccoob_8; | ||
| 982 | break; | ||
| 983 | case 16: | ||
| 984 | this->ecc.layout = &nand_hw_eccoob_16; | ||
| 985 | break; | ||
| 986 | case 64: | ||
| 987 | this->ecc.layout = &nand_hw_eccoob_64; | ||
| 988 | break; | ||
| 989 | default: | ||
| 990 | /* page size not handled by HW ECC */ | ||
| 991 | /* switching back to soft ECC */ | ||
| 992 | this->ecc.size = 512; | ||
| 993 | this->ecc.bytes = 3; | ||
| 994 | this->ecc.layout = &nand_hw_eccoob_8; | ||
| 995 | this->ecc.mode = NAND_ECC_SOFT; | ||
| 996 | this->ecc.calculate = NULL; | ||
| 997 | this->ecc.correct = NULL; | ||
| 998 | this->ecc.hwctl = NULL; | ||
| 999 | tmp = readw(host->regs + NFC_CONFIG1); | ||
| 1000 | tmp &= ~NFC_ECC_EN; | ||
| 1001 | writew(tmp, host->regs + NFC_CONFIG1); | ||
| 1002 | break; | ||
| 1003 | } | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | /* second phase scan */ | ||
| 1007 | if (nand_scan_tail(mtd)) { | ||
| 966 | err = -ENXIO; | 1008 | err = -ENXIO; |
| 967 | goto escan; | 1009 | goto escan; |
| 968 | } | 1010 | } |
| @@ -985,7 +1027,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
| 985 | return 0; | 1027 | return 0; |
| 986 | 1028 | ||
| 987 | escan: | 1029 | escan: |
| 988 | free_irq(host->irq, NULL); | 1030 | free_irq(host->irq, host); |
| 989 | eirq: | 1031 | eirq: |
| 990 | iounmap(host->regs); | 1032 | iounmap(host->regs); |
| 991 | eres: | 1033 | eres: |
| @@ -1005,7 +1047,7 @@ static int __devexit mxcnd_remove(struct platform_device *pdev) | |||
| 1005 | platform_set_drvdata(pdev, NULL); | 1047 | platform_set_drvdata(pdev, NULL); |
| 1006 | 1048 | ||
| 1007 | nand_release(&host->mtd); | 1049 | nand_release(&host->mtd); |
| 1008 | free_irq(host->irq, NULL); | 1050 | free_irq(host->irq, host); |
| 1009 | iounmap(host->regs); | 1051 | iounmap(host->regs); |
| 1010 | kfree(host); | 1052 | kfree(host); |
| 1011 | 1053 | ||
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 3d7ed432fa41..8c21b89d2d0c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
| @@ -2756,7 +2756,8 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
| 2756 | * the out of band area | 2756 | * the out of band area |
| 2757 | */ | 2757 | */ |
| 2758 | chip->ecc.layout->oobavail = 0; | 2758 | chip->ecc.layout->oobavail = 0; |
| 2759 | for (i = 0; chip->ecc.layout->oobfree[i].length; i++) | 2759 | for (i = 0; chip->ecc.layout->oobfree[i].length |
| 2760 | && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) | ||
| 2760 | chip->ecc.layout->oobavail += | 2761 | chip->ecc.layout->oobavail += |
| 2761 | chip->ecc.layout->oobfree[i].length; | 2762 | chip->ecc.layout->oobfree[i].length; |
| 2762 | mtd->oobavail = chip->ecc.layout->oobavail; | 2763 | mtd->oobavail = chip->ecc.layout->oobavail; |
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 868147acce2c..c0cb87d6d16e 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c | |||
| @@ -428,8 +428,8 @@ EXPORT_SYMBOL(nand_calculate_ecc); | |||
| 428 | int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, | 428 | int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, |
| 429 | unsigned char *read_ecc, unsigned char *calc_ecc) | 429 | unsigned char *read_ecc, unsigned char *calc_ecc) |
| 430 | { | 430 | { |
| 431 | unsigned char b0, b1, b2; | 431 | unsigned char b0, b1, b2, bit_addr; |
| 432 | unsigned char byte_addr, bit_addr; | 432 | unsigned int byte_addr; |
| 433 | /* 256 or 512 bytes/ecc */ | 433 | /* 256 or 512 bytes/ecc */ |
| 434 | const uint32_t eccsize_mult = | 434 | const uint32_t eccsize_mult = |
| 435 | (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; | 435 | (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; |
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c new file mode 100644 index 000000000000..0cd76f89f4b0 --- /dev/null +++ b/drivers/mtd/nand/omap2.c | |||
| @@ -0,0 +1,776 @@ | |||
| 1 | /* | ||
| 2 | * Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com> | ||
| 3 | * Copyright © 2004 Micron Technology Inc. | ||
| 4 | * Copyright © 2004 David Brownell | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/platform_device.h> | ||
| 12 | #include <linux/dma-mapping.h> | ||
| 13 | #include <linux/delay.h> | ||
| 14 | #include <linux/mtd/mtd.h> | ||
| 15 | #include <linux/mtd/nand.h> | ||
| 16 | #include <linux/mtd/partitions.h> | ||
| 17 | #include <linux/io.h> | ||
| 18 | |||
| 19 | #include <asm/dma.h> | ||
| 20 | |||
| 21 | #include <mach/gpmc.h> | ||
| 22 | #include <mach/nand.h> | ||
| 23 | |||
| 24 | #define GPMC_IRQ_STATUS 0x18 | ||
| 25 | #define GPMC_ECC_CONFIG 0x1F4 | ||
| 26 | #define GPMC_ECC_CONTROL 0x1F8 | ||
| 27 | #define GPMC_ECC_SIZE_CONFIG 0x1FC | ||
| 28 | #define GPMC_ECC1_RESULT 0x200 | ||
| 29 | |||
| 30 | #define DRIVER_NAME "omap2-nand" | ||
| 31 | |||
| 32 | /* size (4 KiB) for IO mapping */ | ||
| 33 | #define NAND_IO_SIZE SZ_4K | ||
| 34 | |||
| 35 | #define NAND_WP_OFF 0 | ||
| 36 | #define NAND_WP_BIT 0x00000010 | ||
| 37 | #define WR_RD_PIN_MONITORING 0x00600000 | ||
| 38 | |||
| 39 | #define GPMC_BUF_FULL 0x00000001 | ||
| 40 | #define GPMC_BUF_EMPTY 0x00000000 | ||
| 41 | |||
| 42 | #define NAND_Ecc_P1e (1 << 0) | ||
| 43 | #define NAND_Ecc_P2e (1 << 1) | ||
| 44 | #define NAND_Ecc_P4e (1 << 2) | ||
| 45 | #define NAND_Ecc_P8e (1 << 3) | ||
| 46 | #define NAND_Ecc_P16e (1 << 4) | ||
| 47 | #define NAND_Ecc_P32e (1 << 5) | ||
| 48 | #define NAND_Ecc_P64e (1 << 6) | ||
| 49 | #define NAND_Ecc_P128e (1 << 7) | ||
| 50 | #define NAND_Ecc_P256e (1 << 8) | ||
| 51 | #define NAND_Ecc_P512e (1 << 9) | ||
| 52 | #define NAND_Ecc_P1024e (1 << 10) | ||
| 53 | #define NAND_Ecc_P2048e (1 << 11) | ||
| 54 | |||
| 55 | #define NAND_Ecc_P1o (1 << 16) | ||
| 56 | #define NAND_Ecc_P2o (1 << 17) | ||
| 57 | #define NAND_Ecc_P4o (1 << 18) | ||
| 58 | #define NAND_Ecc_P8o (1 << 19) | ||
| 59 | #define NAND_Ecc_P16o (1 << 20) | ||
| 60 | #define NAND_Ecc_P32o (1 << 21) | ||
| 61 | #define NAND_Ecc_P64o (1 << 22) | ||
| 62 | #define NAND_Ecc_P128o (1 << 23) | ||
| 63 | #define NAND_Ecc_P256o (1 << 24) | ||
| 64 | #define NAND_Ecc_P512o (1 << 25) | ||
| 65 | #define NAND_Ecc_P1024o (1 << 26) | ||
| 66 | #define NAND_Ecc_P2048o (1 << 27) | ||
| 67 | |||
| 68 | #define TF(value) (value ? 1 : 0) | ||
| 69 | |||
| 70 | #define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0) | ||
| 71 | #define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1) | ||
| 72 | #define P1e(a) (TF(a & NAND_Ecc_P1e) << 2) | ||
| 73 | #define P1o(a) (TF(a & NAND_Ecc_P1o) << 3) | ||
| 74 | #define P2e(a) (TF(a & NAND_Ecc_P2e) << 4) | ||
| 75 | #define P2o(a) (TF(a & NAND_Ecc_P2o) << 5) | ||
| 76 | #define P4e(a) (TF(a & NAND_Ecc_P4e) << 6) | ||
| 77 | #define P4o(a) (TF(a & NAND_Ecc_P4o) << 7) | ||
| 78 | |||
| 79 | #define P8e(a) (TF(a & NAND_Ecc_P8e) << 0) | ||
| 80 | #define P8o(a) (TF(a & NAND_Ecc_P8o) << 1) | ||
| 81 | #define P16e(a) (TF(a & NAND_Ecc_P16e) << 2) | ||
| 82 | #define P16o(a) (TF(a & NAND_Ecc_P16o) << 3) | ||
| 83 | #define P32e(a) (TF(a & NAND_Ecc_P32e) << 4) | ||
| 84 | #define P32o(a) (TF(a & NAND_Ecc_P32o) << 5) | ||
| 85 | #define P64e(a) (TF(a & NAND_Ecc_P64e) << 6) | ||
| 86 | #define P64o(a) (TF(a & NAND_Ecc_P64o) << 7) | ||
| 87 | |||
| 88 | #define P128e(a) (TF(a & NAND_Ecc_P128e) << 0) | ||
| 89 | #define P128o(a) (TF(a & NAND_Ecc_P128o) << 1) | ||
| 90 | #define P256e(a) (TF(a & NAND_Ecc_P256e) << 2) | ||
| 91 | #define P256o(a) (TF(a & NAND_Ecc_P256o) << 3) | ||
| 92 | #define P512e(a) (TF(a & NAND_Ecc_P512e) << 4) | ||
| 93 | #define P512o(a) (TF(a & NAND_Ecc_P512o) << 5) | ||
| 94 | #define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6) | ||
| 95 | #define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7) | ||
| 96 | |||
| 97 | #define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0) | ||
| 98 | #define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1) | ||
| 99 | #define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2) | ||
| 100 | #define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3) | ||
| 101 | #define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4) | ||
| 102 | #define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5) | ||
| 103 | #define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6) | ||
| 104 | #define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7) | ||
| 105 | |||
| 106 | #define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) | ||
| 107 | #define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) | ||
| 108 | |||
| 109 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 110 | static const char *part_probes[] = { "cmdlinepart", NULL }; | ||
| 111 | #endif | ||
| 112 | |||
| 113 | struct omap_nand_info { | ||
| 114 | struct nand_hw_control controller; | ||
| 115 | struct omap_nand_platform_data *pdata; | ||
| 116 | struct mtd_info mtd; | ||
| 117 | struct mtd_partition *parts; | ||
| 118 | struct nand_chip nand; | ||
| 119 | struct platform_device *pdev; | ||
| 120 | |||
| 121 | int gpmc_cs; | ||
| 122 | unsigned long phys_base; | ||
| 123 | void __iomem *gpmc_cs_baseaddr; | ||
| 124 | void __iomem *gpmc_baseaddr; | ||
| 125 | }; | ||
| 126 | |||
| 127 | /** | ||
| 128 | * omap_nand_wp - This function enable or disable the Write Protect feature | ||
| 129 | * @mtd: MTD device structure | ||
| 130 | * @mode: WP ON/OFF | ||
| 131 | */ | ||
| 132 | static void omap_nand_wp(struct mtd_info *mtd, int mode) | ||
| 133 | { | ||
| 134 | struct omap_nand_info *info = container_of(mtd, | ||
| 135 | struct omap_nand_info, mtd); | ||
| 136 | |||
| 137 | unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG); | ||
| 138 | |||
| 139 | if (mode) | ||
| 140 | config &= ~(NAND_WP_BIT); /* WP is ON */ | ||
| 141 | else | ||
| 142 | config |= (NAND_WP_BIT); /* WP is OFF */ | ||
| 143 | |||
| 144 | __raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG)); | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * omap_hwcontrol - hardware specific access to control-lines | ||
| 149 | * @mtd: MTD device structure | ||
| 150 | * @cmd: command to device | ||
| 151 | * @ctrl: | ||
| 152 | * NAND_NCE: bit 0 -> don't care | ||
| 153 | * NAND_CLE: bit 1 -> Command Latch | ||
| 154 | * NAND_ALE: bit 2 -> Address Latch | ||
| 155 | * | ||
| 156 | * NOTE: boards may use different bits for these!! | ||
| 157 | */ | ||
| 158 | static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) | ||
| 159 | { | ||
| 160 | struct omap_nand_info *info = container_of(mtd, | ||
| 161 | struct omap_nand_info, mtd); | ||
| 162 | switch (ctrl) { | ||
| 163 | case NAND_CTRL_CHANGE | NAND_CTRL_CLE: | ||
| 164 | info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + | ||
| 165 | GPMC_CS_NAND_COMMAND; | ||
| 166 | info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + | ||
| 167 | GPMC_CS_NAND_DATA; | ||
| 168 | break; | ||
| 169 | |||
| 170 | case NAND_CTRL_CHANGE | NAND_CTRL_ALE: | ||
| 171 | info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + | ||
| 172 | GPMC_CS_NAND_ADDRESS; | ||
| 173 | info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + | ||
| 174 | GPMC_CS_NAND_DATA; | ||
| 175 | break; | ||
| 176 | |||
| 177 | case NAND_CTRL_CHANGE | NAND_NCE: | ||
| 178 | info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + | ||
| 179 | GPMC_CS_NAND_DATA; | ||
| 180 | info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + | ||
| 181 | GPMC_CS_NAND_DATA; | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | |||
| 185 | if (cmd != NAND_CMD_NONE) | ||
| 186 | __raw_writeb(cmd, info->nand.IO_ADDR_W); | ||
| 187 | } | ||
| 188 | |||
| 189 | /** | ||
| 190 | * omap_read_buf16 - read data from NAND controller into buffer | ||
| 191 | * @mtd: MTD device structure | ||
| 192 | * @buf: buffer to store date | ||
| 193 | * @len: number of bytes to read | ||
| 194 | */ | ||
| 195 | static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) | ||
| 196 | { | ||
| 197 | struct nand_chip *nand = mtd->priv; | ||
| 198 | |||
| 199 | __raw_readsw(nand->IO_ADDR_R, buf, len / 2); | ||
| 200 | } | ||
| 201 | |||
| 202 | /** | ||
| 203 | * omap_write_buf16 - write buffer to NAND controller | ||
| 204 | * @mtd: MTD device structure | ||
| 205 | * @buf: data buffer | ||
| 206 | * @len: number of bytes to write | ||
| 207 | */ | ||
| 208 | static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) | ||
| 209 | { | ||
| 210 | struct omap_nand_info *info = container_of(mtd, | ||
| 211 | struct omap_nand_info, mtd); | ||
| 212 | u16 *p = (u16 *) buf; | ||
| 213 | |||
| 214 | /* FIXME try bursts of writesw() or DMA ... */ | ||
| 215 | len >>= 1; | ||
| 216 | |||
| 217 | while (len--) { | ||
| 218 | writew(*p++, info->nand.IO_ADDR_W); | ||
| 219 | |||
| 220 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + | ||
| 221 | GPMC_STATUS) & GPMC_BUF_FULL)) | ||
| 222 | ; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | /** | ||
| 226 | * omap_verify_buf - Verify chip data against buffer | ||
| 227 | * @mtd: MTD device structure | ||
| 228 | * @buf: buffer containing the data to compare | ||
| 229 | * @len: number of bytes to compare | ||
| 230 | */ | ||
| 231 | static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len) | ||
| 232 | { | ||
| 233 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
| 234 | mtd); | ||
| 235 | u16 *p = (u16 *) buf; | ||
| 236 | |||
| 237 | len >>= 1; | ||
| 238 | while (len--) { | ||
| 239 | if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R))) | ||
| 240 | return -EFAULT; | ||
| 241 | } | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC | ||
| 247 | /** | ||
| 248 | * omap_hwecc_init - Initialize the HW ECC for NAND flash in GPMC controller | ||
| 249 | * @mtd: MTD device structure | ||
| 250 | */ | ||
| 251 | static void omap_hwecc_init(struct mtd_info *mtd) | ||
| 252 | { | ||
| 253 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
| 254 | mtd); | ||
| 255 | struct nand_chip *chip = mtd->priv; | ||
| 256 | unsigned long val = 0x0; | ||
| 257 | |||
| 258 | /* Read from ECC Control Register */ | ||
| 259 | val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL); | ||
| 260 | /* Clear all ECC | Enable Reg1 */ | ||
| 261 | val = ((0x00000001<<8) | 0x00000001); | ||
| 262 | __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL); | ||
| 263 | |||
| 264 | /* Read from ECC Size Config Register */ | ||
| 265 | val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG); | ||
| 266 | /* ECCSIZE1=512 | Select eccResultsize[0-3] */ | ||
| 267 | val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F)); | ||
| 268 | __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG); | ||
| 269 | } | ||
| 270 | |||
| 271 | /** | ||
| 272 | * gen_true_ecc - This function will generate true ECC value | ||
| 273 | * @ecc_buf: buffer to store ecc code | ||
| 274 | * | ||
| 275 | * This generated true ECC value can be used when correcting | ||
| 276 | * data read from NAND flash memory core | ||
| 277 | */ | ||
| 278 | static void gen_true_ecc(u8 *ecc_buf) | ||
| 279 | { | ||
| 280 | u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | | ||
| 281 | ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); | ||
| 282 | |||
| 283 | ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | | ||
| 284 | P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp)); | ||
| 285 | ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | | ||
| 286 | P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp)); | ||
| 287 | ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | | ||
| 288 | P1e(tmp) | P2048o(tmp) | P2048e(tmp)); | ||
| 289 | } | ||
| 290 | |||
| 291 | /** | ||
| 292 | * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data | ||
| 293 | * @ecc_data1: ecc code from nand spare area | ||
| 294 | * @ecc_data2: ecc code from hardware register obtained from hardware ecc | ||
| 295 | * @page_data: page data | ||
| 296 | * | ||
| 297 | * This function compares two ECC's and indicates if there is an error. | ||
| 298 | * If the error can be corrected it will be corrected to the buffer. | ||
| 299 | */ | ||
| 300 | static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ | ||
| 301 | u8 *ecc_data2, /* read from register */ | ||
| 302 | u8 *page_data) | ||
| 303 | { | ||
| 304 | uint i; | ||
| 305 | u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; | ||
| 306 | u8 comp0_bit[8], comp1_bit[8], comp2_bit[8]; | ||
| 307 | u8 ecc_bit[24]; | ||
| 308 | u8 ecc_sum = 0; | ||
| 309 | u8 find_bit = 0; | ||
| 310 | uint find_byte = 0; | ||
| 311 | int isEccFF; | ||
| 312 | |||
| 313 | isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF); | ||
| 314 | |||
| 315 | gen_true_ecc(ecc_data1); | ||
| 316 | gen_true_ecc(ecc_data2); | ||
| 317 | |||
| 318 | for (i = 0; i <= 2; i++) { | ||
| 319 | *(ecc_data1 + i) = ~(*(ecc_data1 + i)); | ||
| 320 | *(ecc_data2 + i) = ~(*(ecc_data2 + i)); | ||
| 321 | } | ||
| 322 | |||
| 323 | for (i = 0; i < 8; i++) { | ||
| 324 | tmp0_bit[i] = *ecc_data1 % 2; | ||
| 325 | *ecc_data1 = *ecc_data1 / 2; | ||
| 326 | } | ||
| 327 | |||
| 328 | for (i = 0; i < 8; i++) { | ||
| 329 | tmp1_bit[i] = *(ecc_data1 + 1) % 2; | ||
| 330 | *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2; | ||
| 331 | } | ||
| 332 | |||
| 333 | for (i = 0; i < 8; i++) { | ||
| 334 | tmp2_bit[i] = *(ecc_data1 + 2) % 2; | ||
| 335 | *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2; | ||
| 336 | } | ||
| 337 | |||
| 338 | for (i = 0; i < 8; i++) { | ||
| 339 | comp0_bit[i] = *ecc_data2 % 2; | ||
| 340 | *ecc_data2 = *ecc_data2 / 2; | ||
| 341 | } | ||
| 342 | |||
| 343 | for (i = 0; i < 8; i++) { | ||
| 344 | comp1_bit[i] = *(ecc_data2 + 1) % 2; | ||
| 345 | *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2; | ||
| 346 | } | ||
| 347 | |||
| 348 | for (i = 0; i < 8; i++) { | ||
| 349 | comp2_bit[i] = *(ecc_data2 + 2) % 2; | ||
| 350 | *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2; | ||
| 351 | } | ||
| 352 | |||
| 353 | for (i = 0; i < 6; i++) | ||
| 354 | ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2]; | ||
| 355 | |||
| 356 | for (i = 0; i < 8; i++) | ||
| 357 | ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i]; | ||
| 358 | |||
| 359 | for (i = 0; i < 8; i++) | ||
| 360 | ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i]; | ||
| 361 | |||
| 362 | ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0]; | ||
| 363 | ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1]; | ||
| 364 | |||
| 365 | for (i = 0; i < 24; i++) | ||
| 366 | ecc_sum += ecc_bit[i]; | ||
| 367 | |||
| 368 | switch (ecc_sum) { | ||
| 369 | case 0: | ||
| 370 | /* Not reached because this function is not called if | ||
| 371 | * ECC values are equal | ||
| 372 | */ | ||
| 373 | return 0; | ||
| 374 | |||
| 375 | case 1: | ||
| 376 | /* Uncorrectable error */ | ||
| 377 | DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n"); | ||
| 378 | return -1; | ||
| 379 | |||
| 380 | case 11: | ||
| 381 | /* UN-Correctable error */ | ||
| 382 | DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n"); | ||
| 383 | return -1; | ||
| 384 | |||
| 385 | case 12: | ||
| 386 | /* Correctable error */ | ||
| 387 | find_byte = (ecc_bit[23] << 8) + | ||
| 388 | (ecc_bit[21] << 7) + | ||
| 389 | (ecc_bit[19] << 6) + | ||
| 390 | (ecc_bit[17] << 5) + | ||
| 391 | (ecc_bit[15] << 4) + | ||
| 392 | (ecc_bit[13] << 3) + | ||
| 393 | (ecc_bit[11] << 2) + | ||
| 394 | (ecc_bit[9] << 1) + | ||
| 395 | ecc_bit[7]; | ||
| 396 | |||
| 397 | find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; | ||
| 398 | |||
| 399 | DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at " | ||
| 400 | "offset: %d, bit: %d\n", find_byte, find_bit); | ||
| 401 | |||
| 402 | page_data[find_byte] ^= (1 << find_bit); | ||
| 403 | |||
| 404 | return 0; | ||
| 405 | default: | ||
| 406 | if (isEccFF) { | ||
| 407 | if (ecc_data2[0] == 0 && | ||
| 408 | ecc_data2[1] == 0 && | ||
| 409 | ecc_data2[2] == 0) | ||
| 410 | return 0; | ||
| 411 | } | ||
| 412 | DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n"); | ||
| 413 | return -1; | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | /** | ||
| 418 | * omap_correct_data - Compares the ECC read with HW generated ECC | ||
| 419 | * @mtd: MTD device structure | ||
| 420 | * @dat: page data | ||
| 421 | * @read_ecc: ecc read from nand flash | ||
| 422 | * @calc_ecc: ecc read from HW ECC registers | ||
| 423 | * | ||
| 424 | * Compares the ecc read from nand spare area with ECC registers values | ||
| 425 | * and if ECC's mismached, it will call 'omap_compare_ecc' for error detection | ||
| 426 | * and correction. | ||
| 427 | */ | ||
| 428 | static int omap_correct_data(struct mtd_info *mtd, u_char *dat, | ||
| 429 | u_char *read_ecc, u_char *calc_ecc) | ||
| 430 | { | ||
| 431 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
| 432 | mtd); | ||
| 433 | int blockCnt = 0, i = 0, ret = 0; | ||
| 434 | |||
| 435 | /* Ex NAND_ECC_HW12_2048 */ | ||
| 436 | if ((info->nand.ecc.mode == NAND_ECC_HW) && | ||
| 437 | (info->nand.ecc.size == 2048)) | ||
| 438 | blockCnt = 4; | ||
| 439 | else | ||
| 440 | blockCnt = 1; | ||
| 441 | |||
| 442 | for (i = 0; i < blockCnt; i++) { | ||
| 443 | if (memcmp(read_ecc, calc_ecc, 3) != 0) { | ||
| 444 | ret = omap_compare_ecc(read_ecc, calc_ecc, dat); | ||
| 445 | if (ret < 0) | ||
| 446 | return ret; | ||
| 447 | } | ||
| 448 | read_ecc += 3; | ||
| 449 | calc_ecc += 3; | ||
| 450 | dat += 512; | ||
| 451 | } | ||
| 452 | return 0; | ||
| 453 | } | ||
| 454 | |||
| 455 | /** | ||
| 456 | * omap_calcuate_ecc - Generate non-inverted ECC bytes. | ||
| 457 | * @mtd: MTD device structure | ||
| 458 | * @dat: The pointer to data on which ecc is computed | ||
| 459 | * @ecc_code: The ecc_code buffer | ||
| 460 | * | ||
| 461 | * Using noninverted ECC can be considered ugly since writing a blank | ||
| 462 | * page ie. padding will clear the ECC bytes. This is no problem as long | ||
| 463 | * nobody is trying to write data on the seemingly unused page. Reading | ||
| 464 | * an erased page will produce an ECC mismatch between generated and read | ||
| 465 | * ECC bytes that has to be dealt with separately. | ||
| 466 | */ | ||
| 467 | static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, | ||
| 468 | u_char *ecc_code) | ||
| 469 | { | ||
| 470 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
| 471 | mtd); | ||
| 472 | unsigned long val = 0x0; | ||
| 473 | unsigned long reg; | ||
| 474 | |||
| 475 | /* Start Reading from HW ECC1_Result = 0x200 */ | ||
| 476 | reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT); | ||
| 477 | val = __raw_readl(reg); | ||
| 478 | *ecc_code++ = val; /* P128e, ..., P1e */ | ||
| 479 | *ecc_code++ = val >> 16; /* P128o, ..., P1o */ | ||
| 480 | /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ | ||
| 481 | *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); | ||
| 482 | reg += 4; | ||
| 483 | |||
| 484 | return 0; | ||
| 485 | } | ||
| 486 | |||
| 487 | /** | ||
| 488 | * omap_enable_hwecc - This function enables the hardware ecc functionality | ||
| 489 | * @mtd: MTD device structure | ||
| 490 | * @mode: Read/Write mode | ||
| 491 | */ | ||
| 492 | static void omap_enable_hwecc(struct mtd_info *mtd, int mode) | ||
| 493 | { | ||
| 494 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
| 495 | mtd); | ||
| 496 | struct nand_chip *chip = mtd->priv; | ||
| 497 | unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; | ||
| 498 | unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG); | ||
| 499 | |||
| 500 | switch (mode) { | ||
| 501 | case NAND_ECC_READ: | ||
| 502 | __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL); | ||
| 503 | /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ | ||
| 504 | val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); | ||
| 505 | break; | ||
| 506 | case NAND_ECC_READSYN: | ||
| 507 | __raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL); | ||
| 508 | /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ | ||
| 509 | val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); | ||
| 510 | break; | ||
| 511 | case NAND_ECC_WRITE: | ||
| 512 | __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL); | ||
| 513 | /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ | ||
| 514 | val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); | ||
| 515 | break; | ||
| 516 | default: | ||
| 517 | DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n", | ||
| 518 | mode); | ||
| 519 | break; | ||
| 520 | } | ||
| 521 | |||
| 522 | __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG); | ||
| 523 | } | ||
| 524 | #endif | ||
| 525 | |||
| 526 | /** | ||
| 527 | * omap_wait - wait until the command is done | ||
| 528 | * @mtd: MTD device structure | ||
| 529 | * @chip: NAND Chip structure | ||
| 530 | * | ||
| 531 | * Wait function is called during Program and erase operations and | ||
| 532 | * the way it is called from MTD layer, we should wait till the NAND | ||
| 533 | * chip is ready after the programming/erase operation has completed. | ||
| 534 | * | ||
| 535 | * Erase can take up to 400ms and program up to 20ms according to | ||
| 536 | * general NAND and SmartMedia specs | ||
| 537 | */ | ||
| 538 | static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) | ||
| 539 | { | ||
| 540 | struct nand_chip *this = mtd->priv; | ||
| 541 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
| 542 | mtd); | ||
| 543 | unsigned long timeo = jiffies; | ||
| 544 | int status, state = this->state; | ||
| 545 | |||
| 546 | if (state == FL_ERASING) | ||
| 547 | timeo += (HZ * 400) / 1000; | ||
| 548 | else | ||
| 549 | timeo += (HZ * 20) / 1000; | ||
| 550 | |||
| 551 | this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr + | ||
| 552 | GPMC_CS_NAND_COMMAND; | ||
| 553 | this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA; | ||
| 554 | |||
| 555 | __raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W); | ||
| 556 | |||
| 557 | while (time_before(jiffies, timeo)) { | ||
| 558 | status = __raw_readb(this->IO_ADDR_R); | ||
| 559 | if (!(status & 0x40)) | ||
| 560 | break; | ||
| 561 | } | ||
| 562 | return status; | ||
| 563 | } | ||
| 564 | |||
| 565 | /** | ||
| 566 | * omap_dev_ready - calls the platform specific dev_ready function | ||
| 567 | * @mtd: MTD device structure | ||
| 568 | */ | ||
| 569 | static int omap_dev_ready(struct mtd_info *mtd) | ||
| 570 | { | ||
| 571 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
| 572 | mtd); | ||
| 573 | unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS); | ||
| 574 | |||
| 575 | if ((val & 0x100) == 0x100) { | ||
| 576 | /* Clear IRQ Interrupt */ | ||
| 577 | val |= 0x100; | ||
| 578 | val &= ~(0x0); | ||
| 579 | __raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS); | ||
| 580 | } else { | ||
| 581 | unsigned int cnt = 0; | ||
| 582 | while (cnt++ < 0x1FF) { | ||
| 583 | if ((val & 0x100) == 0x100) | ||
| 584 | return 0; | ||
| 585 | val = __raw_readl(info->gpmc_baseaddr + | ||
| 586 | GPMC_IRQ_STATUS); | ||
| 587 | } | ||
| 588 | } | ||
| 589 | |||
| 590 | return 1; | ||
| 591 | } | ||
| 592 | |||
| 593 | static int __devinit omap_nand_probe(struct platform_device *pdev) | ||
| 594 | { | ||
| 595 | struct omap_nand_info *info; | ||
| 596 | struct omap_nand_platform_data *pdata; | ||
| 597 | int err; | ||
| 598 | unsigned long val; | ||
| 599 | |||
| 600 | |||
| 601 | pdata = pdev->dev.platform_data; | ||
| 602 | if (pdata == NULL) { | ||
| 603 | dev_err(&pdev->dev, "platform data missing\n"); | ||
| 604 | return -ENODEV; | ||
| 605 | } | ||
| 606 | |||
| 607 | info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL); | ||
| 608 | if (!info) | ||
| 609 | return -ENOMEM; | ||
| 610 | |||
| 611 | platform_set_drvdata(pdev, info); | ||
| 612 | |||
| 613 | spin_lock_init(&info->controller.lock); | ||
| 614 | init_waitqueue_head(&info->controller.wq); | ||
| 615 | |||
| 616 | info->pdev = pdev; | ||
| 617 | |||
| 618 | info->gpmc_cs = pdata->cs; | ||
| 619 | info->gpmc_baseaddr = pdata->gpmc_baseaddr; | ||
| 620 | info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr; | ||
| 621 | |||
| 622 | info->mtd.priv = &info->nand; | ||
| 623 | info->mtd.name = dev_name(&pdev->dev); | ||
| 624 | info->mtd.owner = THIS_MODULE; | ||
| 625 | |||
| 626 | err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base); | ||
| 627 | if (err < 0) { | ||
| 628 | dev_err(&pdev->dev, "Cannot request GPMC CS\n"); | ||
| 629 | goto out_free_info; | ||
| 630 | } | ||
| 631 | |||
| 632 | /* Enable RD PIN Monitoring Reg */ | ||
| 633 | if (pdata->dev_ready) { | ||
| 634 | val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1); | ||
| 635 | val |= WR_RD_PIN_MONITORING; | ||
| 636 | gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val); | ||
| 637 | } | ||
| 638 | |||
| 639 | val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7); | ||
| 640 | val &= ~(0xf << 8); | ||
| 641 | val |= (0xc & 0xf) << 8; | ||
| 642 | gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val); | ||
| 643 | |||
| 644 | /* NAND write protect off */ | ||
| 645 | omap_nand_wp(&info->mtd, NAND_WP_OFF); | ||
| 646 | |||
| 647 | if (!request_mem_region(info->phys_base, NAND_IO_SIZE, | ||
| 648 | pdev->dev.driver->name)) { | ||
| 649 | err = -EBUSY; | ||
| 650 | goto out_free_cs; | ||
| 651 | } | ||
| 652 | |||
| 653 | info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE); | ||
| 654 | if (!info->nand.IO_ADDR_R) { | ||
| 655 | err = -ENOMEM; | ||
| 656 | goto out_release_mem_region; | ||
| 657 | } | ||
| 658 | info->nand.controller = &info->controller; | ||
| 659 | |||
| 660 | info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; | ||
| 661 | info->nand.cmd_ctrl = omap_hwcontrol; | ||
| 662 | |||
| 663 | /* REVISIT: only supports 16-bit NAND flash */ | ||
| 664 | |||
| 665 | info->nand.read_buf = omap_read_buf16; | ||
| 666 | info->nand.write_buf = omap_write_buf16; | ||
| 667 | info->nand.verify_buf = omap_verify_buf; | ||
| 668 | |||
| 669 | /* | ||
| 670 | * If RDY/BSY line is connected to OMAP then use the omap ready | ||
| 671 | * funcrtion and the generic nand_wait function which reads the status | ||
| 672 | * register after monitoring the RDY/BSY line.Otherwise use a standard | ||
| 673 | * chip delay which is slightly more than tR (AC Timing) of the NAND | ||
| 674 | * device and read status register until you get a failure or success | ||
| 675 | */ | ||
| 676 | if (pdata->dev_ready) { | ||
| 677 | info->nand.dev_ready = omap_dev_ready; | ||
| 678 | info->nand.chip_delay = 0; | ||
| 679 | } else { | ||
| 680 | info->nand.waitfunc = omap_wait; | ||
| 681 | info->nand.chip_delay = 50; | ||
| 682 | } | ||
| 683 | |||
| 684 | info->nand.options |= NAND_SKIP_BBTSCAN; | ||
| 685 | if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000) | ||
| 686 | == 0x1000) | ||
| 687 | info->nand.options |= NAND_BUSWIDTH_16; | ||
| 688 | |||
| 689 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC | ||
| 690 | info->nand.ecc.bytes = 3; | ||
| 691 | info->nand.ecc.size = 512; | ||
| 692 | info->nand.ecc.calculate = omap_calculate_ecc; | ||
| 693 | info->nand.ecc.hwctl = omap_enable_hwecc; | ||
| 694 | info->nand.ecc.correct = omap_correct_data; | ||
| 695 | info->nand.ecc.mode = NAND_ECC_HW; | ||
| 696 | |||
| 697 | /* init HW ECC */ | ||
| 698 | omap_hwecc_init(&info->mtd); | ||
| 699 | #else | ||
| 700 | info->nand.ecc.mode = NAND_ECC_SOFT; | ||
| 701 | #endif | ||
| 702 | |||
| 703 | /* DIP switches on some boards change between 8 and 16 bit | ||
| 704 | * bus widths for flash. Try the other width if the first try fails. | ||
| 705 | */ | ||
| 706 | if (nand_scan(&info->mtd, 1)) { | ||
| 707 | info->nand.options ^= NAND_BUSWIDTH_16; | ||
| 708 | if (nand_scan(&info->mtd, 1)) { | ||
| 709 | err = -ENXIO; | ||
| 710 | goto out_release_mem_region; | ||
| 711 | } | ||
| 712 | } | ||
| 713 | |||
| 714 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 715 | err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); | ||
| 716 | if (err > 0) | ||
| 717 | add_mtd_partitions(&info->mtd, info->parts, err); | ||
| 718 | else if (pdata->parts) | ||
| 719 | add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); | ||
| 720 | else | ||
| 721 | #endif | ||
| 722 | add_mtd_device(&info->mtd); | ||
| 723 | |||
| 724 | platform_set_drvdata(pdev, &info->mtd); | ||
| 725 | |||
| 726 | return 0; | ||
| 727 | |||
| 728 | out_release_mem_region: | ||
| 729 | release_mem_region(info->phys_base, NAND_IO_SIZE); | ||
| 730 | out_free_cs: | ||
| 731 | gpmc_cs_free(info->gpmc_cs); | ||
| 732 | out_free_info: | ||
| 733 | kfree(info); | ||
| 734 | |||
| 735 | return err; | ||
| 736 | } | ||
| 737 | |||
| 738 | static int omap_nand_remove(struct platform_device *pdev) | ||
| 739 | { | ||
| 740 | struct mtd_info *mtd = platform_get_drvdata(pdev); | ||
| 741 | struct omap_nand_info *info = mtd->priv; | ||
| 742 | |||
| 743 | platform_set_drvdata(pdev, NULL); | ||
| 744 | /* Release NAND device, its internal structures and partitions */ | ||
| 745 | nand_release(&info->mtd); | ||
| 746 | iounmap(info->nand.IO_ADDR_R); | ||
| 747 | kfree(&info->mtd); | ||
| 748 | return 0; | ||
| 749 | } | ||
| 750 | |||
| 751 | static struct platform_driver omap_nand_driver = { | ||
| 752 | .probe = omap_nand_probe, | ||
| 753 | .remove = omap_nand_remove, | ||
| 754 | .driver = { | ||
| 755 | .name = DRIVER_NAME, | ||
| 756 | .owner = THIS_MODULE, | ||
| 757 | }, | ||
| 758 | }; | ||
| 759 | |||
| 760 | static int __init omap_nand_init(void) | ||
| 761 | { | ||
| 762 | printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); | ||
| 763 | return platform_driver_register(&omap_nand_driver); | ||
| 764 | } | ||
| 765 | |||
| 766 | static void __exit omap_nand_exit(void) | ||
| 767 | { | ||
| 768 | platform_driver_unregister(&omap_nand_driver); | ||
| 769 | } | ||
| 770 | |||
| 771 | module_init(omap_nand_init); | ||
| 772 | module_exit(omap_nand_exit); | ||
| 773 | |||
| 774 | MODULE_ALIAS(DRIVER_NAME); | ||
| 775 | MODULE_LICENSE("GPL"); | ||
| 776 | MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); | ||
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index c2dfd3ea353d..7ad972229db4 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c | |||
| @@ -47,6 +47,28 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl | |||
| 47 | writeb(cmd, nc->IO_ADDR_W + offs); | 47 | writeb(cmd, nc->IO_ADDR_W + offs); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) | ||
| 51 | { | ||
| 52 | struct nand_chip *chip = mtd->priv; | ||
| 53 | void __iomem *io_base = chip->IO_ADDR_R; | ||
| 54 | uint64_t *buf64; | ||
| 55 | int i = 0; | ||
| 56 | |||
| 57 | while (len && (unsigned long)buf & 7) { | ||
| 58 | *buf++ = readb(io_base); | ||
| 59 | len--; | ||
| 60 | } | ||
| 61 | buf64 = (uint64_t *)buf; | ||
| 62 | while (i < len/8) { | ||
| 63 | uint64_t x; | ||
| 64 | asm ("ldrd\t%0, [%1]" : "=r" (x) : "r" (io_base)); | ||
| 65 | buf64[i++] = x; | ||
| 66 | } | ||
| 67 | i *= 8; | ||
| 68 | while (i < len) | ||
| 69 | buf[i++] = readb(io_base); | ||
| 70 | } | ||
| 71 | |||
| 50 | static int __init orion_nand_probe(struct platform_device *pdev) | 72 | static int __init orion_nand_probe(struct platform_device *pdev) |
| 51 | { | 73 | { |
| 52 | struct mtd_info *mtd; | 74 | struct mtd_info *mtd; |
| @@ -83,6 +105,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) | |||
| 83 | nc->priv = board; | 105 | nc->priv = board; |
| 84 | nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; | 106 | nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; |
| 85 | nc->cmd_ctrl = orion_nand_cmd_ctrl; | 107 | nc->cmd_ctrl = orion_nand_cmd_ctrl; |
| 108 | nc->read_buf = orion_nand_read_buf; | ||
| 86 | nc->ecc.mode = NAND_ECC_SOFT; | 109 | nc->ecc.mode = NAND_ECC_SOFT; |
| 87 | 110 | ||
| 88 | if (board->chip_delay) | 111 | if (board->chip_delay) |
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 86e1d08eee00..4e16c6f5bdd5 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c | |||
| @@ -61,6 +61,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) | |||
| 61 | data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; | 61 | data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; |
| 62 | data->chip.dev_ready = pdata->ctrl.dev_ready; | 62 | data->chip.dev_ready = pdata->ctrl.dev_ready; |
| 63 | data->chip.select_chip = pdata->ctrl.select_chip; | 63 | data->chip.select_chip = pdata->ctrl.select_chip; |
| 64 | data->chip.write_buf = pdata->ctrl.write_buf; | ||
| 65 | data->chip.read_buf = pdata->ctrl.read_buf; | ||
| 64 | data->chip.chip_delay = pdata->chip.chip_delay; | 66 | data->chip.chip_delay = pdata->chip.chip_delay; |
| 65 | data->chip.options |= pdata->chip.options; | 67 | data->chip.options |= pdata->chip.options; |
| 66 | 68 | ||
| @@ -70,6 +72,13 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) | |||
| 70 | 72 | ||
| 71 | platform_set_drvdata(pdev, data); | 73 | platform_set_drvdata(pdev, data); |
| 72 | 74 | ||
| 75 | /* Handle any platform specific setup */ | ||
| 76 | if (pdata->ctrl.probe) { | ||
| 77 | res = pdata->ctrl.probe(pdev); | ||
| 78 | if (res) | ||
| 79 | goto out; | ||
| 80 | } | ||
| 81 | |||
| 73 | /* Scan to find existance of the device */ | 82 | /* Scan to find existance of the device */ |
| 74 | if (nand_scan(&data->mtd, 1)) { | 83 | if (nand_scan(&data->mtd, 1)) { |
| 75 | res = -ENXIO; | 84 | res = -ENXIO; |
| @@ -86,6 +95,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) | |||
| 86 | return 0; | 95 | return 0; |
| 87 | } | 96 | } |
| 88 | } | 97 | } |
| 98 | if (pdata->chip.set_parts) | ||
| 99 | pdata->chip.set_parts(data->mtd.size, &pdata->chip); | ||
| 89 | if (pdata->chip.partitions) { | 100 | if (pdata->chip.partitions) { |
| 90 | data->parts = pdata->chip.partitions; | 101 | data->parts = pdata->chip.partitions; |
| 91 | res = add_mtd_partitions(&data->mtd, data->parts, | 102 | res = add_mtd_partitions(&data->mtd, data->parts, |
| @@ -99,6 +110,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) | |||
| 99 | 110 | ||
| 100 | nand_release(&data->mtd); | 111 | nand_release(&data->mtd); |
| 101 | out: | 112 | out: |
| 113 | if (pdata->ctrl.remove) | ||
| 114 | pdata->ctrl.remove(pdev); | ||
| 102 | platform_set_drvdata(pdev, NULL); | 115 | platform_set_drvdata(pdev, NULL); |
| 103 | iounmap(data->io_base); | 116 | iounmap(data->io_base); |
| 104 | kfree(data); | 117 | kfree(data); |
| @@ -111,15 +124,15 @@ out: | |||
| 111 | static int __devexit plat_nand_remove(struct platform_device *pdev) | 124 | static int __devexit plat_nand_remove(struct platform_device *pdev) |
| 112 | { | 125 | { |
| 113 | struct plat_nand_data *data = platform_get_drvdata(pdev); | 126 | struct plat_nand_data *data = platform_get_drvdata(pdev); |
| 114 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 115 | struct platform_nand_data *pdata = pdev->dev.platform_data; | 127 | struct platform_nand_data *pdata = pdev->dev.platform_data; |
| 116 | #endif | ||
| 117 | 128 | ||
| 118 | nand_release(&data->mtd); | 129 | nand_release(&data->mtd); |
| 119 | #ifdef CONFIG_MTD_PARTITIONS | 130 | #ifdef CONFIG_MTD_PARTITIONS |
| 120 | if (data->parts && data->parts != pdata->chip.partitions) | 131 | if (data->parts && data->parts != pdata->chip.partitions) |
| 121 | kfree(data->parts); | 132 | kfree(data->parts); |
| 122 | #endif | 133 | #endif |
| 134 | if (pdata->ctrl.remove) | ||
| 135 | pdata->ctrl.remove(pdev); | ||
| 123 | iounmap(data->io_base); | 136 | iounmap(data->io_base); |
| 124 | kfree(data); | 137 | kfree(data); |
| 125 | 138 | ||
| @@ -128,7 +141,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) | |||
| 128 | 141 | ||
| 129 | static struct platform_driver plat_nand_driver = { | 142 | static struct platform_driver plat_nand_driver = { |
| 130 | .probe = plat_nand_probe, | 143 | .probe = plat_nand_probe, |
| 131 | .remove = plat_nand_remove, | 144 | .remove = __devexit_p(plat_nand_remove), |
| 132 | .driver = { | 145 | .driver = { |
| 133 | .name = "gen_nand", | 146 | .name = "gen_nand", |
| 134 | .owner = THIS_MODULE, | 147 | .owner = THIS_MODULE, |
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8e375d5fe231..11dc7e69c4fb 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c | |||
| @@ -74,6 +74,14 @@ static struct nand_ecclayout nand_hw_eccoob = { | |||
| 74 | 74 | ||
| 75 | struct s3c2410_nand_info; | 75 | struct s3c2410_nand_info; |
| 76 | 76 | ||
| 77 | /** | ||
| 78 | * struct s3c2410_nand_mtd - driver MTD structure | ||
| 79 | * @mtd: The MTD instance to pass to the MTD layer. | ||
| 80 | * @chip: The NAND chip information. | ||
| 81 | * @set: The platform information supplied for this set of NAND chips. | ||
| 82 | * @info: Link back to the hardware information. | ||
| 83 | * @scan_res: The result from calling nand_scan_ident(). | ||
| 84 | */ | ||
| 77 | struct s3c2410_nand_mtd { | 85 | struct s3c2410_nand_mtd { |
| 78 | struct mtd_info mtd; | 86 | struct mtd_info mtd; |
| 79 | struct nand_chip chip; | 87 | struct nand_chip chip; |
| @@ -90,6 +98,21 @@ enum s3c_cpu_type { | |||
| 90 | 98 | ||
| 91 | /* overview of the s3c2410 nand state */ | 99 | /* overview of the s3c2410 nand state */ |
| 92 | 100 | ||
| 101 | /** | ||
| 102 | * struct s3c2410_nand_info - NAND controller state. | ||
| 103 | * @mtds: An array of MTD instances on this controoler. | ||
| 104 | * @platform: The platform data for this board. | ||
| 105 | * @device: The platform device we bound to. | ||
| 106 | * @area: The IO area resource that came from request_mem_region(). | ||
| 107 | * @clk: The clock resource for this controller. | ||
| 108 | * @regs: The area mapped for the hardware registers described by @area. | ||
| 109 | * @sel_reg: Pointer to the register controlling the NAND selection. | ||
| 110 | * @sel_bit: The bit in @sel_reg to select the NAND chip. | ||
| 111 | * @mtd_count: The number of MTDs created from this controller. | ||
| 112 | * @save_sel: The contents of @sel_reg to be saved over suspend. | ||
| 113 | * @clk_rate: The clock rate from @clk. | ||
| 114 | * @cpu_type: The exact type of this controller. | ||
| 115 | */ | ||
| 93 | struct s3c2410_nand_info { | 116 | struct s3c2410_nand_info { |
| 94 | /* mtd info */ | 117 | /* mtd info */ |
| 95 | struct nand_hw_control controller; | 118 | struct nand_hw_control controller; |
| @@ -145,12 +168,19 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info) | |||
| 145 | 168 | ||
| 146 | #define NS_IN_KHZ 1000000 | 169 | #define NS_IN_KHZ 1000000 |
| 147 | 170 | ||
| 171 | /** | ||
| 172 | * s3c_nand_calc_rate - calculate timing data. | ||
| 173 | * @wanted: The cycle time in nanoseconds. | ||
| 174 | * @clk: The clock rate in kHz. | ||
| 175 | * @max: The maximum divider value. | ||
| 176 | * | ||
| 177 | * Calculate the timing value from the given parameters. | ||
| 178 | */ | ||
| 148 | static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) | 179 | static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) |
| 149 | { | 180 | { |
| 150 | int result; | 181 | int result; |
| 151 | 182 | ||
| 152 | result = (wanted * clk) / NS_IN_KHZ; | 183 | result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ); |
| 153 | result++; | ||
| 154 | 184 | ||
| 155 | pr_debug("result %d from %ld, %d\n", result, clk, wanted); | 185 | pr_debug("result %d from %ld, %d\n", result, clk, wanted); |
| 156 | 186 | ||
| @@ -169,13 +199,21 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) | |||
| 169 | 199 | ||
| 170 | /* controller setup */ | 200 | /* controller setup */ |
| 171 | 201 | ||
| 202 | /** | ||
| 203 | * s3c2410_nand_setrate - setup controller timing information. | ||
| 204 | * @info: The controller instance. | ||
| 205 | * | ||
| 206 | * Given the information supplied by the platform, calculate and set | ||
| 207 | * the necessary timing registers in the hardware to generate the | ||
| 208 | * necessary timing cycles to the hardware. | ||
| 209 | */ | ||
| 172 | static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) | 210 | static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) |
| 173 | { | 211 | { |
| 174 | struct s3c2410_platform_nand *plat = info->platform; | 212 | struct s3c2410_platform_nand *plat = info->platform; |
| 175 | int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; | 213 | int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; |
| 176 | int tacls, twrph0, twrph1; | 214 | int tacls, twrph0, twrph1; |
| 177 | unsigned long clkrate = clk_get_rate(info->clk); | 215 | unsigned long clkrate = clk_get_rate(info->clk); |
| 178 | unsigned long set, cfg, mask; | 216 | unsigned long uninitialized_var(set), cfg, uninitialized_var(mask); |
| 179 | unsigned long flags; | 217 | unsigned long flags; |
| 180 | 218 | ||
| 181 | /* calculate the timing information for the controller */ | 219 | /* calculate the timing information for the controller */ |
| @@ -215,9 +253,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) | |||
| 215 | 253 | ||
| 216 | case TYPE_S3C2440: | 254 | case TYPE_S3C2440: |
| 217 | case TYPE_S3C2412: | 255 | case TYPE_S3C2412: |
| 218 | mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) | | 256 | mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | |
| 219 | S3C2410_NFCONF_TWRPH0(7) | | 257 | S3C2440_NFCONF_TWRPH0(7) | |
| 220 | S3C2410_NFCONF_TWRPH1(7)); | 258 | S3C2440_NFCONF_TWRPH1(7)); |
| 221 | 259 | ||
| 222 | set = S3C2440_NFCONF_TACLS(tacls - 1); | 260 | set = S3C2440_NFCONF_TACLS(tacls - 1); |
| 223 | set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); | 261 | set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); |
| @@ -225,14 +263,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) | |||
| 225 | break; | 263 | break; |
| 226 | 264 | ||
| 227 | default: | 265 | default: |
| 228 | /* keep compiler happy */ | ||
| 229 | mask = 0; | ||
| 230 | set = 0; | ||
| 231 | BUG(); | 266 | BUG(); |
| 232 | } | 267 | } |
| 233 | 268 | ||
| 234 | dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); | ||
| 235 | |||
| 236 | local_irq_save(flags); | 269 | local_irq_save(flags); |
| 237 | 270 | ||
| 238 | cfg = readl(info->regs + S3C2410_NFCONF); | 271 | cfg = readl(info->regs + S3C2410_NFCONF); |
| @@ -242,9 +275,18 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) | |||
| 242 | 275 | ||
| 243 | local_irq_restore(flags); | 276 | local_irq_restore(flags); |
| 244 | 277 | ||
| 278 | dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); | ||
| 279 | |||
| 245 | return 0; | 280 | return 0; |
| 246 | } | 281 | } |
| 247 | 282 | ||
| 283 | /** | ||
| 284 | * s3c2410_nand_inithw - basic hardware initialisation | ||
| 285 | * @info: The hardware state. | ||
| 286 | * | ||
| 287 | * Do the basic initialisation of the hardware, using s3c2410_nand_setrate() | ||
| 288 | * to setup the hardware access speeds and set the controller to be enabled. | ||
| 289 | */ | ||
| 248 | static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) | 290 | static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) |
| 249 | { | 291 | { |
| 250 | int ret; | 292 | int ret; |
| @@ -268,8 +310,19 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) | |||
| 268 | return 0; | 310 | return 0; |
| 269 | } | 311 | } |
| 270 | 312 | ||
| 271 | /* select chip */ | 313 | /** |
| 272 | 314 | * s3c2410_nand_select_chip - select the given nand chip | |
| 315 | * @mtd: The MTD instance for this chip. | ||
| 316 | * @chip: The chip number. | ||
| 317 | * | ||
| 318 | * This is called by the MTD layer to either select a given chip for the | ||
| 319 | * @mtd instance, or to indicate that the access has finished and the | ||
| 320 | * chip can be de-selected. | ||
| 321 | * | ||
| 322 | * The routine ensures that the nFCE line is correctly setup, and any | ||
| 323 | * platform specific selection code is called to route nFCE to the specific | ||
| 324 | * chip. | ||
| 325 | */ | ||
| 273 | static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) | 326 | static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) |
| 274 | { | 327 | { |
| 275 | struct s3c2410_nand_info *info; | 328 | struct s3c2410_nand_info *info; |
| @@ -530,7 +583,16 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) | |||
| 530 | static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) | 583 | static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) |
| 531 | { | 584 | { |
| 532 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 585 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
| 533 | readsl(info->regs + S3C2440_NFDATA, buf, len / 4); | 586 | |
| 587 | readsl(info->regs + S3C2440_NFDATA, buf, len >> 2); | ||
| 588 | |||
| 589 | /* cleanup if we've got less than a word to do */ | ||
| 590 | if (len & 3) { | ||
| 591 | buf += len & ~3; | ||
| 592 | |||
| 593 | for (; len & 3; len--) | ||
| 594 | *buf++ = readb(info->regs + S3C2440_NFDATA); | ||
| 595 | } | ||
| 534 | } | 596 | } |
| 535 | 597 | ||
| 536 | static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) | 598 | static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) |
| @@ -542,7 +604,16 @@ static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int | |||
| 542 | static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) | 604 | static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) |
| 543 | { | 605 | { |
| 544 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 606 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
| 545 | writesl(info->regs + S3C2440_NFDATA, buf, len / 4); | 607 | |
| 608 | writesl(info->regs + S3C2440_NFDATA, buf, len >> 2); | ||
| 609 | |||
| 610 | /* cleanup any fractional write */ | ||
| 611 | if (len & 3) { | ||
| 612 | buf += len & ~3; | ||
| 613 | |||
| 614 | for (; len & 3; len--, buf++) | ||
| 615 | writeb(*buf, info->regs + S3C2440_NFDATA); | ||
| 616 | } | ||
| 546 | } | 617 | } |
| 547 | 618 | ||
| 548 | /* cpufreq driver support */ | 619 | /* cpufreq driver support */ |
| @@ -593,7 +664,7 @@ static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *inf | |||
| 593 | 664 | ||
| 594 | /* device management functions */ | 665 | /* device management functions */ |
| 595 | 666 | ||
| 596 | static int s3c2410_nand_remove(struct platform_device *pdev) | 667 | static int s3c24xx_nand_remove(struct platform_device *pdev) |
| 597 | { | 668 | { |
| 598 | struct s3c2410_nand_info *info = to_nand_info(pdev); | 669 | struct s3c2410_nand_info *info = to_nand_info(pdev); |
| 599 | 670 | ||
| @@ -645,17 +716,31 @@ static int s3c2410_nand_remove(struct platform_device *pdev) | |||
| 645 | } | 716 | } |
| 646 | 717 | ||
| 647 | #ifdef CONFIG_MTD_PARTITIONS | 718 | #ifdef CONFIG_MTD_PARTITIONS |
| 719 | const char *part_probes[] = { "cmdlinepart", NULL }; | ||
| 648 | static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, | 720 | static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, |
| 649 | struct s3c2410_nand_mtd *mtd, | 721 | struct s3c2410_nand_mtd *mtd, |
| 650 | struct s3c2410_nand_set *set) | 722 | struct s3c2410_nand_set *set) |
| 651 | { | 723 | { |
| 724 | struct mtd_partition *part_info; | ||
| 725 | int nr_part = 0; | ||
| 726 | |||
| 652 | if (set == NULL) | 727 | if (set == NULL) |
| 653 | return add_mtd_device(&mtd->mtd); | 728 | return add_mtd_device(&mtd->mtd); |
| 654 | 729 | ||
| 655 | if (set->nr_partitions > 0 && set->partitions != NULL) { | 730 | if (set->nr_partitions == 0) { |
| 656 | return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions); | 731 | mtd->mtd.name = set->name; |
| 732 | nr_part = parse_mtd_partitions(&mtd->mtd, part_probes, | ||
| 733 | &part_info, 0); | ||
| 734 | } else { | ||
| 735 | if (set->nr_partitions > 0 && set->partitions != NULL) { | ||
| 736 | nr_part = set->nr_partitions; | ||
| 737 | part_info = set->partitions; | ||
| 738 | } | ||
| 657 | } | 739 | } |
| 658 | 740 | ||
| 741 | if (nr_part > 0 && part_info) | ||
| 742 | return add_mtd_partitions(&mtd->mtd, part_info, nr_part); | ||
| 743 | |||
| 659 | return add_mtd_device(&mtd->mtd); | 744 | return add_mtd_device(&mtd->mtd); |
| 660 | } | 745 | } |
| 661 | #else | 746 | #else |
| @@ -667,11 +752,16 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, | |||
| 667 | } | 752 | } |
| 668 | #endif | 753 | #endif |
| 669 | 754 | ||
| 670 | /* s3c2410_nand_init_chip | 755 | /** |
| 756 | * s3c2410_nand_init_chip - initialise a single instance of an chip | ||
| 757 | * @info: The base NAND controller the chip is on. | ||
| 758 | * @nmtd: The new controller MTD instance to fill in. | ||
| 759 | * @set: The information passed from the board specific platform data. | ||
| 671 | * | 760 | * |
| 672 | * init a single instance of an chip | 761 | * Initialise the given @nmtd from the information in @info and @set. This |
| 673 | */ | 762 | * readies the structure for use with the MTD layer functions by ensuring |
| 674 | 763 | * all pointers are setup and the necessary control routines selected. | |
| 764 | */ | ||
| 675 | static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | 765 | static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, |
| 676 | struct s3c2410_nand_mtd *nmtd, | 766 | struct s3c2410_nand_mtd *nmtd, |
| 677 | struct s3c2410_nand_set *set) | 767 | struct s3c2410_nand_set *set) |
| @@ -757,14 +847,40 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | |||
| 757 | 847 | ||
| 758 | if (set->disable_ecc) | 848 | if (set->disable_ecc) |
| 759 | chip->ecc.mode = NAND_ECC_NONE; | 849 | chip->ecc.mode = NAND_ECC_NONE; |
| 850 | |||
| 851 | switch (chip->ecc.mode) { | ||
| 852 | case NAND_ECC_NONE: | ||
| 853 | dev_info(info->device, "NAND ECC disabled\n"); | ||
| 854 | break; | ||
| 855 | case NAND_ECC_SOFT: | ||
| 856 | dev_info(info->device, "NAND soft ECC\n"); | ||
| 857 | break; | ||
| 858 | case NAND_ECC_HW: | ||
| 859 | dev_info(info->device, "NAND hardware ECC\n"); | ||
| 860 | break; | ||
| 861 | default: | ||
| 862 | dev_info(info->device, "NAND ECC UNKNOWN\n"); | ||
| 863 | break; | ||
| 864 | } | ||
| 865 | |||
| 866 | /* If you use u-boot BBT creation code, specifying this flag will | ||
| 867 | * let the kernel fish out the BBT from the NAND, and also skip the | ||
| 868 | * full NAND scan that can take 1/2s or so. Little things... */ | ||
| 869 | if (set->flash_bbt) | ||
| 870 | chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; | ||
| 760 | } | 871 | } |
| 761 | 872 | ||
| 762 | /* s3c2410_nand_update_chip | 873 | /** |
| 874 | * s3c2410_nand_update_chip - post probe update | ||
| 875 | * @info: The controller instance. | ||
| 876 | * @nmtd: The driver version of the MTD instance. | ||
| 763 | * | 877 | * |
| 764 | * post-probe chip update, to change any items, such as the | 878 | * This routine is called after the chip probe has succesfully completed |
| 765 | * layout for large page nand | 879 | * and the relevant per-chip information updated. This call ensure that |
| 766 | */ | 880 | * we update the internal state accordingly. |
| 767 | 881 | * | |
| 882 | * The internal state is currently limited to the ECC state information. | ||
| 883 | */ | ||
| 768 | static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, | 884 | static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, |
| 769 | struct s3c2410_nand_mtd *nmtd) | 885 | struct s3c2410_nand_mtd *nmtd) |
| 770 | { | 886 | { |
| @@ -773,33 +889,33 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, | |||
| 773 | dev_dbg(info->device, "chip %p => page shift %d\n", | 889 | dev_dbg(info->device, "chip %p => page shift %d\n", |
| 774 | chip, chip->page_shift); | 890 | chip, chip->page_shift); |
| 775 | 891 | ||
| 776 | if (hardware_ecc) { | 892 | if (chip->ecc.mode != NAND_ECC_HW) |
| 893 | return; | ||
| 894 | |||
| 777 | /* change the behaviour depending on wether we are using | 895 | /* change the behaviour depending on wether we are using |
| 778 | * the large or small page nand device */ | 896 | * the large or small page nand device */ |
| 779 | 897 | ||
| 780 | if (chip->page_shift > 10) { | 898 | if (chip->page_shift > 10) { |
| 781 | chip->ecc.size = 256; | 899 | chip->ecc.size = 256; |
| 782 | chip->ecc.bytes = 3; | 900 | chip->ecc.bytes = 3; |
| 783 | } else { | 901 | } else { |
| 784 | chip->ecc.size = 512; | 902 | chip->ecc.size = 512; |
| 785 | chip->ecc.bytes = 3; | 903 | chip->ecc.bytes = 3; |
| 786 | chip->ecc.layout = &nand_hw_eccoob; | 904 | chip->ecc.layout = &nand_hw_eccoob; |
| 787 | } | ||
| 788 | } | 905 | } |
| 789 | } | 906 | } |
| 790 | 907 | ||
| 791 | /* s3c2410_nand_probe | 908 | /* s3c24xx_nand_probe |
| 792 | * | 909 | * |
| 793 | * called by device layer when it finds a device matching | 910 | * called by device layer when it finds a device matching |
| 794 | * one our driver can handled. This code checks to see if | 911 | * one our driver can handled. This code checks to see if |
| 795 | * it can allocate all necessary resources then calls the | 912 | * it can allocate all necessary resources then calls the |
| 796 | * nand layer to look for devices | 913 | * nand layer to look for devices |
| 797 | */ | 914 | */ |
| 798 | 915 | static int s3c24xx_nand_probe(struct platform_device *pdev) | |
| 799 | static int s3c24xx_nand_probe(struct platform_device *pdev, | ||
| 800 | enum s3c_cpu_type cpu_type) | ||
| 801 | { | 916 | { |
| 802 | struct s3c2410_platform_nand *plat = to_nand_plat(pdev); | 917 | struct s3c2410_platform_nand *plat = to_nand_plat(pdev); |
| 918 | enum s3c_cpu_type cpu_type; | ||
| 803 | struct s3c2410_nand_info *info; | 919 | struct s3c2410_nand_info *info; |
| 804 | struct s3c2410_nand_mtd *nmtd; | 920 | struct s3c2410_nand_mtd *nmtd; |
| 805 | struct s3c2410_nand_set *sets; | 921 | struct s3c2410_nand_set *sets; |
| @@ -809,6 +925,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, | |||
| 809 | int nr_sets; | 925 | int nr_sets; |
| 810 | int setno; | 926 | int setno; |
| 811 | 927 | ||
| 928 | cpu_type = platform_get_device_id(pdev)->driver_data; | ||
| 929 | |||
| 812 | pr_debug("s3c2410_nand_probe(%p)\n", pdev); | 930 | pr_debug("s3c2410_nand_probe(%p)\n", pdev); |
| 813 | 931 | ||
| 814 | info = kmalloc(sizeof(*info), GFP_KERNEL); | 932 | info = kmalloc(sizeof(*info), GFP_KERNEL); |
| @@ -922,7 +1040,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, | |||
| 922 | return 0; | 1040 | return 0; |
| 923 | 1041 | ||
| 924 | exit_error: | 1042 | exit_error: |
| 925 | s3c2410_nand_remove(pdev); | 1043 | s3c24xx_nand_remove(pdev); |
| 926 | 1044 | ||
| 927 | if (err == 0) | 1045 | if (err == 0) |
| 928 | err = -EINVAL; | 1046 | err = -EINVAL; |
| @@ -983,50 +1101,33 @@ static int s3c24xx_nand_resume(struct platform_device *dev) | |||
| 983 | 1101 | ||
| 984 | /* driver device registration */ | 1102 | /* driver device registration */ |
| 985 | 1103 | ||
| 986 | static int s3c2410_nand_probe(struct platform_device *dev) | 1104 | static struct platform_device_id s3c24xx_driver_ids[] = { |
| 987 | { | 1105 | { |
| 988 | return s3c24xx_nand_probe(dev, TYPE_S3C2410); | 1106 | .name = "s3c2410-nand", |
| 989 | } | 1107 | .driver_data = TYPE_S3C2410, |
| 990 | 1108 | }, { | |
| 991 | static int s3c2440_nand_probe(struct platform_device *dev) | 1109 | .name = "s3c2440-nand", |
| 992 | { | 1110 | .driver_data = TYPE_S3C2440, |
| 993 | return s3c24xx_nand_probe(dev, TYPE_S3C2440); | 1111 | }, { |
| 994 | } | 1112 | .name = "s3c2412-nand", |
| 995 | 1113 | .driver_data = TYPE_S3C2412, | |
| 996 | static int s3c2412_nand_probe(struct platform_device *dev) | 1114 | }, { |
| 997 | { | 1115 | .name = "s3c6400-nand", |
| 998 | return s3c24xx_nand_probe(dev, TYPE_S3C2412); | 1116 | .driver_data = TYPE_S3C2412, /* compatible with 2412 */ |
| 999 | } | ||
| 1000 | |||
| 1001 | static struct platform_driver s3c2410_nand_driver = { | ||
| 1002 | .probe = s3c2410_nand_probe, | ||
| 1003 | .remove = s3c2410_nand_remove, | ||
| 1004 | .suspend = s3c24xx_nand_suspend, | ||
| 1005 | .resume = s3c24xx_nand_resume, | ||
| 1006 | .driver = { | ||
| 1007 | .name = "s3c2410-nand", | ||
| 1008 | .owner = THIS_MODULE, | ||
| 1009 | }, | 1117 | }, |
| 1118 | { } | ||
| 1010 | }; | 1119 | }; |
| 1011 | 1120 | ||
| 1012 | static struct platform_driver s3c2440_nand_driver = { | 1121 | MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); |
| 1013 | .probe = s3c2440_nand_probe, | ||
| 1014 | .remove = s3c2410_nand_remove, | ||
| 1015 | .suspend = s3c24xx_nand_suspend, | ||
| 1016 | .resume = s3c24xx_nand_resume, | ||
| 1017 | .driver = { | ||
| 1018 | .name = "s3c2440-nand", | ||
| 1019 | .owner = THIS_MODULE, | ||
| 1020 | }, | ||
| 1021 | }; | ||
| 1022 | 1122 | ||
| 1023 | static struct platform_driver s3c2412_nand_driver = { | 1123 | static struct platform_driver s3c24xx_nand_driver = { |
| 1024 | .probe = s3c2412_nand_probe, | 1124 | .probe = s3c24xx_nand_probe, |
| 1025 | .remove = s3c2410_nand_remove, | 1125 | .remove = s3c24xx_nand_remove, |
| 1026 | .suspend = s3c24xx_nand_suspend, | 1126 | .suspend = s3c24xx_nand_suspend, |
| 1027 | .resume = s3c24xx_nand_resume, | 1127 | .resume = s3c24xx_nand_resume, |
| 1128 | .id_table = s3c24xx_driver_ids, | ||
| 1028 | .driver = { | 1129 | .driver = { |
| 1029 | .name = "s3c2412-nand", | 1130 | .name = "s3c24xx-nand", |
| 1030 | .owner = THIS_MODULE, | 1131 | .owner = THIS_MODULE, |
| 1031 | }, | 1132 | }, |
| 1032 | }; | 1133 | }; |
| @@ -1035,16 +1136,12 @@ static int __init s3c2410_nand_init(void) | |||
| 1035 | { | 1136 | { |
| 1036 | printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); | 1137 | printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); |
| 1037 | 1138 | ||
| 1038 | platform_driver_register(&s3c2412_nand_driver); | 1139 | return platform_driver_register(&s3c24xx_nand_driver); |
| 1039 | platform_driver_register(&s3c2440_nand_driver); | ||
| 1040 | return platform_driver_register(&s3c2410_nand_driver); | ||
| 1041 | } | 1140 | } |
| 1042 | 1141 | ||
| 1043 | static void __exit s3c2410_nand_exit(void) | 1142 | static void __exit s3c2410_nand_exit(void) |
| 1044 | { | 1143 | { |
| 1045 | platform_driver_unregister(&s3c2412_nand_driver); | 1144 | platform_driver_unregister(&s3c24xx_nand_driver); |
| 1046 | platform_driver_unregister(&s3c2440_nand_driver); | ||
| 1047 | platform_driver_unregister(&s3c2410_nand_driver); | ||
| 1048 | } | 1145 | } |
| 1049 | 1146 | ||
| 1050 | module_init(s3c2410_nand_init); | 1147 | module_init(s3c2410_nand_init); |
| @@ -1053,6 +1150,3 @@ module_exit(s3c2410_nand_exit); | |||
| 1053 | MODULE_LICENSE("GPL"); | 1150 | MODULE_LICENSE("GPL"); |
| 1054 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); | 1151 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); |
| 1055 | MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); | 1152 | MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); |
| 1056 | MODULE_ALIAS("platform:s3c2410-nand"); | ||
| 1057 | MODULE_ALIAS("platform:s3c2412-nand"); | ||
| 1058 | MODULE_ALIAS("platform:s3c2440-nand"); | ||
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 812479264896..488088eff2ca 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c | |||
| @@ -64,7 +64,7 @@ struct txx9ndfmc_priv { | |||
| 64 | struct nand_chip chip; | 64 | struct nand_chip chip; |
| 65 | struct mtd_info mtd; | 65 | struct mtd_info mtd; |
| 66 | int cs; | 66 | int cs; |
| 67 | char mtdname[BUS_ID_SIZE + 2]; | 67 | const char *mtdname; |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | #define MAX_TXX9NDFMC_DEV 4 | 70 | #define MAX_TXX9NDFMC_DEV 4 |
| @@ -334,16 +334,23 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) | |||
| 334 | 334 | ||
| 335 | if (plat->ch_mask != 1) { | 335 | if (plat->ch_mask != 1) { |
| 336 | txx9_priv->cs = i; | 336 | txx9_priv->cs = i; |
| 337 | sprintf(txx9_priv->mtdname, "%s.%u", | 337 | txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u", |
| 338 | dev_name(&dev->dev), i); | 338 | dev_name(&dev->dev), i); |
| 339 | } else { | 339 | } else { |
| 340 | txx9_priv->cs = -1; | 340 | txx9_priv->cs = -1; |
| 341 | strcpy(txx9_priv->mtdname, dev_name(&dev->dev)); | 341 | txx9_priv->mtdname = kstrdup(dev_name(&dev->dev), |
| 342 | GFP_KERNEL); | ||
| 343 | } | ||
| 344 | if (!txx9_priv->mtdname) { | ||
| 345 | kfree(txx9_priv); | ||
| 346 | dev_err(&dev->dev, "Unable to allocate MTD name.\n"); | ||
| 347 | continue; | ||
| 342 | } | 348 | } |
| 343 | if (plat->wide_mask & (1 << i)) | 349 | if (plat->wide_mask & (1 << i)) |
| 344 | chip->options |= NAND_BUSWIDTH_16; | 350 | chip->options |= NAND_BUSWIDTH_16; |
| 345 | 351 | ||
| 346 | if (nand_scan(mtd, 1)) { | 352 | if (nand_scan(mtd, 1)) { |
| 353 | kfree(txx9_priv->mtdname); | ||
| 347 | kfree(txx9_priv); | 354 | kfree(txx9_priv); |
| 348 | continue; | 355 | continue; |
| 349 | } | 356 | } |
| @@ -385,6 +392,7 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) | |||
| 385 | kfree(drvdata->parts[i]); | 392 | kfree(drvdata->parts[i]); |
| 386 | #endif | 393 | #endif |
| 387 | del_mtd_device(mtd); | 394 | del_mtd_device(mtd); |
| 395 | kfree(txx9_priv->mtdname); | ||
| 388 | kfree(txx9_priv); | 396 | kfree(txx9_priv); |
| 389 | } | 397 | } |
| 390 | return 0; | 398 | return 0; |
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 */ |
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index c5ded5ff72b5..c135202c38b3 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c | |||
| @@ -94,7 +94,6 @@ | |||
| 94 | #include <linux/atm_tcp.h> | 94 | #include <linux/atm_tcp.h> |
| 95 | #include <linux/sonet.h> | 95 | #include <linux/sonet.h> |
| 96 | #include <linux/atm_suni.h> | 96 | #include <linux/atm_suni.h> |
| 97 | #include <linux/mtd/mtd.h> | ||
| 98 | 97 | ||
| 99 | #include <linux/usb.h> | 98 | #include <linux/usb.h> |
| 100 | #include <linux/usbdevice_fs.h> | 99 | #include <linux/usbdevice_fs.h> |
| @@ -1405,46 +1404,6 @@ static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg) | |||
| 1405 | #define HIDPGETCONNLIST _IOR('H', 210, int) | 1404 | #define HIDPGETCONNLIST _IOR('H', 210, int) |
| 1406 | #define HIDPGETCONNINFO _IOR('H', 211, int) | 1405 | #define HIDPGETCONNINFO _IOR('H', 211, int) |
| 1407 | 1406 | ||
| 1408 | struct mtd_oob_buf32 { | ||
| 1409 | u_int32_t start; | ||
| 1410 | u_int32_t length; | ||
| 1411 | compat_caddr_t ptr; /* unsigned char* */ | ||
| 1412 | }; | ||
| 1413 | |||
| 1414 | #define MEMWRITEOOB32 _IOWR('M',3,struct mtd_oob_buf32) | ||
| 1415 | #define MEMREADOOB32 _IOWR('M',4,struct mtd_oob_buf32) | ||
| 1416 | |||
| 1417 | static int mtd_rw_oob(unsigned int fd, unsigned int cmd, unsigned long arg) | ||
| 1418 | { | ||
| 1419 | struct mtd_oob_buf __user *buf = compat_alloc_user_space(sizeof(*buf)); | ||
| 1420 | struct mtd_oob_buf32 __user *buf32 = compat_ptr(arg); | ||
| 1421 | u32 data; | ||
| 1422 | char __user *datap; | ||
| 1423 | unsigned int real_cmd; | ||
| 1424 | int err; | ||
| 1425 | |||
| 1426 | real_cmd = (cmd == MEMREADOOB32) ? | ||
| 1427 | MEMREADOOB : MEMWRITEOOB; | ||
| 1428 | |||
| 1429 | if (copy_in_user(&buf->start, &buf32->start, | ||
| 1430 | 2 * sizeof(u32)) || | ||
| 1431 | get_user(data, &buf32->ptr)) | ||
| 1432 | return -EFAULT; | ||
| 1433 | datap = compat_ptr(data); | ||
| 1434 | if (put_user(datap, &buf->ptr)) | ||
| 1435 | return -EFAULT; | ||
| 1436 | |||
| 1437 | err = sys_ioctl(fd, real_cmd, (unsigned long) buf); | ||
| 1438 | |||
| 1439 | if (!err) { | ||
| 1440 | if (copy_in_user(&buf32->start, &buf->start, | ||
| 1441 | 2 * sizeof(u32))) | ||
| 1442 | err = -EFAULT; | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | return err; | ||
| 1446 | } | ||
| 1447 | |||
| 1448 | #ifdef CONFIG_BLOCK | 1407 | #ifdef CONFIG_BLOCK |
| 1449 | struct raw32_config_request | 1408 | struct raw32_config_request |
| 1450 | { | 1409 | { |
| @@ -2426,15 +2385,6 @@ COMPATIBLE_IOCTL(USBDEVFS_SUBMITURB32) | |||
| 2426 | COMPATIBLE_IOCTL(USBDEVFS_REAPURB32) | 2385 | COMPATIBLE_IOCTL(USBDEVFS_REAPURB32) |
| 2427 | COMPATIBLE_IOCTL(USBDEVFS_REAPURBNDELAY32) | 2386 | COMPATIBLE_IOCTL(USBDEVFS_REAPURBNDELAY32) |
| 2428 | COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT) | 2387 | COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT) |
| 2429 | /* MTD */ | ||
| 2430 | COMPATIBLE_IOCTL(MEMGETINFO) | ||
| 2431 | COMPATIBLE_IOCTL(MEMERASE) | ||
| 2432 | COMPATIBLE_IOCTL(MEMLOCK) | ||
| 2433 | COMPATIBLE_IOCTL(MEMUNLOCK) | ||
| 2434 | COMPATIBLE_IOCTL(MEMGETREGIONCOUNT) | ||
| 2435 | COMPATIBLE_IOCTL(MEMGETREGIONINFO) | ||
| 2436 | COMPATIBLE_IOCTL(MEMGETBADBLOCK) | ||
| 2437 | COMPATIBLE_IOCTL(MEMSETBADBLOCK) | ||
| 2438 | /* NBD */ | 2388 | /* NBD */ |
| 2439 | ULONG_IOCTL(NBD_SET_SOCK) | 2389 | ULONG_IOCTL(NBD_SET_SOCK) |
| 2440 | ULONG_IOCTL(NBD_SET_BLKSIZE) | 2390 | ULONG_IOCTL(NBD_SET_BLKSIZE) |
| @@ -2544,8 +2494,6 @@ COMPATIBLE_IOCTL(JSIOCGBUTTONS) | |||
| 2544 | COMPATIBLE_IOCTL(JSIOCGNAME(0)) | 2494 | COMPATIBLE_IOCTL(JSIOCGNAME(0)) |
| 2545 | 2495 | ||
| 2546 | /* now things that need handlers */ | 2496 | /* now things that need handlers */ |
| 2547 | HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob) | ||
| 2548 | HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob) | ||
| 2549 | #ifdef CONFIG_NET | 2497 | #ifdef CONFIG_NET |
| 2550 | HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32) | 2498 | HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32) |
| 2551 | HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf) | 2499 | HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf) |
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 1d437de1e9a8..7515e73e2bfb 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c | |||
| @@ -196,7 +196,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) | |||
| 196 | if (c->nextblock) { | 196 | if (c->nextblock) { |
| 197 | ret = file_dirty(c, c->nextblock); | 197 | ret = file_dirty(c, c->nextblock); |
| 198 | if (ret) | 198 | if (ret) |
| 199 | return ret; | 199 | goto out; |
| 200 | /* deleting summary information of the old nextblock */ | 200 | /* deleting summary information of the old nextblock */ |
| 201 | jffs2_sum_reset_collected(c->summary); | 201 | jffs2_sum_reset_collected(c->summary); |
| 202 | } | 202 | } |
| @@ -207,7 +207,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) | |||
| 207 | } else { | 207 | } else { |
| 208 | ret = file_dirty(c, jeb); | 208 | ret = file_dirty(c, jeb); |
| 209 | if (ret) | 209 | if (ret) |
| 210 | return ret; | 210 | goto out; |
| 211 | } | 211 | } |
| 212 | break; | 212 | break; |
| 213 | 213 | ||
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 7efb9be34662..4030ebada49e 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
| @@ -563,6 +563,7 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 563 | * @options: Option flags, e.g. 16bit buswidth | 563 | * @options: Option flags, e.g. 16bit buswidth |
| 564 | * @ecclayout: ecc layout info structure | 564 | * @ecclayout: ecc layout info structure |
| 565 | * @part_probe_types: NULL-terminated array of probe types | 565 | * @part_probe_types: NULL-terminated array of probe types |
| 566 | * @set_parts: platform specific function to set partitions | ||
| 566 | * @priv: hardware controller specific settings | 567 | * @priv: hardware controller specific settings |
| 567 | */ | 568 | */ |
| 568 | struct platform_nand_chip { | 569 | struct platform_nand_chip { |
| @@ -574,26 +575,41 @@ struct platform_nand_chip { | |||
| 574 | int chip_delay; | 575 | int chip_delay; |
| 575 | unsigned int options; | 576 | unsigned int options; |
| 576 | const char **part_probe_types; | 577 | const char **part_probe_types; |
| 578 | void (*set_parts)(uint64_t size, | ||
| 579 | struct platform_nand_chip *chip); | ||
| 577 | void *priv; | 580 | void *priv; |
| 578 | }; | 581 | }; |
| 579 | 582 | ||
| 583 | /* Keep gcc happy */ | ||
| 584 | struct platform_device; | ||
| 585 | |||
| 580 | /** | 586 | /** |
| 581 | * struct platform_nand_ctrl - controller level device structure | 587 | * struct platform_nand_ctrl - controller level device structure |
| 588 | * @probe: platform specific function to probe/setup hardware | ||
| 589 | * @remove: platform specific function to remove/teardown hardware | ||
| 582 | * @hwcontrol: platform specific hardware control structure | 590 | * @hwcontrol: platform specific hardware control structure |
| 583 | * @dev_ready: platform specific function to read ready/busy pin | 591 | * @dev_ready: platform specific function to read ready/busy pin |
| 584 | * @select_chip: platform specific chip select function | 592 | * @select_chip: platform specific chip select function |
| 585 | * @cmd_ctrl: platform specific function for controlling | 593 | * @cmd_ctrl: platform specific function for controlling |
| 586 | * ALE/CLE/nCE. Also used to write command and address | 594 | * ALE/CLE/nCE. Also used to write command and address |
| 595 | * @write_buf: platform specific function for write buffer | ||
| 596 | * @read_buf: platform specific function for read buffer | ||
| 587 | * @priv: private data to transport driver specific settings | 597 | * @priv: private data to transport driver specific settings |
| 588 | * | 598 | * |
| 589 | * All fields are optional and depend on the hardware driver requirements | 599 | * All fields are optional and depend on the hardware driver requirements |
| 590 | */ | 600 | */ |
| 591 | struct platform_nand_ctrl { | 601 | struct platform_nand_ctrl { |
| 602 | int (*probe)(struct platform_device *pdev); | ||
| 603 | void (*remove)(struct platform_device *pdev); | ||
| 592 | void (*hwcontrol)(struct mtd_info *mtd, int cmd); | 604 | void (*hwcontrol)(struct mtd_info *mtd, int cmd); |
| 593 | int (*dev_ready)(struct mtd_info *mtd); | 605 | int (*dev_ready)(struct mtd_info *mtd); |
| 594 | void (*select_chip)(struct mtd_info *mtd, int chip); | 606 | void (*select_chip)(struct mtd_info *mtd, int chip); |
| 595 | void (*cmd_ctrl)(struct mtd_info *mtd, int dat, | 607 | void (*cmd_ctrl)(struct mtd_info *mtd, int dat, |
| 596 | unsigned int ctrl); | 608 | unsigned int ctrl); |
| 609 | void (*write_buf)(struct mtd_info *mtd, | ||
| 610 | const uint8_t *buf, int len); | ||
| 611 | void (*read_buf)(struct mtd_info *mtd, | ||
| 612 | uint8_t *buf, int len); | ||
| 597 | void *priv; | 613 | void *priv; |
| 598 | }; | 614 | }; |
| 599 | 615 | ||
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 9aa2a9149b58..8ed873374381 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 |
| @@ -68,6 +74,8 @@ struct onenand_bufferram { | |||
| 68 | * @command: [REPLACEABLE] hardware specific function for writing | 74 | * @command: [REPLACEABLE] hardware specific function for writing |
| 69 | * commands to the chip | 75 | * commands to the chip |
| 70 | * @wait: [REPLACEABLE] hardware specific function for wait on ready | 76 | * @wait: [REPLACEABLE] hardware specific function for wait on ready |
| 77 | * @bbt_wait: [REPLACEABLE] hardware specific function for bbt wait on ready | ||
| 78 | * @unlock_all: [REPLACEABLE] hardware specific function for unlock all | ||
| 71 | * @read_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area | 79 | * @read_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area |
| 72 | * @write_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area | 80 | * @write_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area |
| 73 | * @read_word: [REPLACEABLE] hardware specific function for read | 81 | * @read_word: [REPLACEABLE] hardware specific function for read |
| @@ -92,9 +100,13 @@ struct onenand_bufferram { | |||
| 92 | */ | 100 | */ |
| 93 | struct onenand_chip { | 101 | struct onenand_chip { |
| 94 | void __iomem *base; | 102 | void __iomem *base; |
| 103 | unsigned dies; | ||
| 104 | unsigned boundary[MAX_DIES]; | ||
| 105 | loff_t diesize[MAX_DIES]; | ||
| 95 | unsigned int chipsize; | 106 | unsigned int chipsize; |
| 96 | unsigned int device_id; | 107 | unsigned int device_id; |
| 97 | unsigned int version_id; | 108 | unsigned int version_id; |
| 109 | unsigned int technology; | ||
| 98 | unsigned int density_mask; | 110 | unsigned int density_mask; |
| 99 | unsigned int options; | 111 | unsigned int options; |
| 100 | 112 | ||
| @@ -108,6 +120,8 @@ struct onenand_chip { | |||
| 108 | 120 | ||
| 109 | int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len); | 121 | int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len); |
| 110 | int (*wait)(struct mtd_info *mtd, int state); | 122 | int (*wait)(struct mtd_info *mtd, int state); |
| 123 | int (*bbt_wait)(struct mtd_info *mtd, int state); | ||
| 124 | void (*unlock_all)(struct mtd_info *mtd); | ||
| 111 | int (*read_bufferram)(struct mtd_info *mtd, int area, | 125 | int (*read_bufferram)(struct mtd_info *mtd, int area, |
| 112 | unsigned char *buffer, int offset, size_t count); | 126 | unsigned char *buffer, int offset, size_t count); |
| 113 | int (*write_bufferram)(struct mtd_info *mtd, int area, | 127 | int (*write_bufferram)(struct mtd_info *mtd, int area, |
| @@ -145,6 +159,8 @@ struct onenand_chip { | |||
| 145 | #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) | 159 | #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) |
| 146 | #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1) | 160 | #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1) |
| 147 | 161 | ||
| 162 | #define FLEXONENAND(this) \ | ||
| 163 | (this->device_id & DEVICE_IS_FLEXONENAND) | ||
| 148 | #define ONENAND_GET_SYS_CFG1(this) \ | 164 | #define ONENAND_GET_SYS_CFG1(this) \ |
| 149 | (this->read_word(this->base + ONENAND_REG_SYS_CFG1)) | 165 | (this->read_word(this->base + ONENAND_REG_SYS_CFG1)) |
| 150 | #define ONENAND_SET_SYS_CFG1(v, this) \ | 166 | #define ONENAND_SET_SYS_CFG1(v, this) \ |
| @@ -153,6 +169,9 @@ struct onenand_chip { | |||
| 153 | #define ONENAND_IS_DDP(this) \ | 169 | #define ONENAND_IS_DDP(this) \ |
| 154 | (this->device_id & ONENAND_DEVICE_IS_DDP) | 170 | (this->device_id & ONENAND_DEVICE_IS_DDP) |
| 155 | 171 | ||
| 172 | #define ONENAND_IS_MLC(this) \ | ||
| 173 | (this->technology & ONENAND_TECHNOLOGY_IS_MLC) | ||
| 174 | |||
| 156 | #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM | 175 | #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM |
| 157 | #define ONENAND_IS_2PLANE(this) \ | 176 | #define ONENAND_IS_2PLANE(this) \ |
| 158 | (this->options & ONENAND_HAS_2PLANE) | 177 | (this->options & ONENAND_HAS_2PLANE) |
| @@ -169,6 +188,7 @@ struct onenand_chip { | |||
| 169 | #define ONENAND_HAS_CONT_LOCK (0x0001) | 188 | #define ONENAND_HAS_CONT_LOCK (0x0001) |
| 170 | #define ONENAND_HAS_UNLOCK_ALL (0x0002) | 189 | #define ONENAND_HAS_UNLOCK_ALL (0x0002) |
| 171 | #define ONENAND_HAS_2PLANE (0x0004) | 190 | #define ONENAND_HAS_2PLANE (0x0004) |
| 191 | #define ONENAND_SKIP_UNLOCK_CHECK (0x0100) | ||
| 172 | #define ONENAND_PAGEBUF_ALLOC (0x1000) | 192 | #define ONENAND_PAGEBUF_ALLOC (0x1000) |
| 173 | #define ONENAND_OOBBUF_ALLOC (0x2000) | 193 | #define ONENAND_OOBBUF_ALLOC (0x2000) |
| 174 | 194 | ||
| @@ -176,6 +196,7 @@ struct onenand_chip { | |||
| 176 | * OneNAND Flash Manufacturer ID Codes | 196 | * OneNAND Flash Manufacturer ID Codes |
| 177 | */ | 197 | */ |
| 178 | #define ONENAND_MFR_SAMSUNG 0xec | 198 | #define ONENAND_MFR_SAMSUNG 0xec |
| 199 | #define ONENAND_MFR_NUMONYX 0x20 | ||
| 179 | 200 | ||
| 180 | /** | 201 | /** |
| 181 | * struct onenand_manufacturers - NAND Flash Manufacturer ID Structure | 202 | * struct onenand_manufacturers - NAND Flash Manufacturer ID Structure |
| @@ -189,5 +210,8 @@ struct onenand_manufacturers { | |||
| 189 | 210 | ||
| 190 | int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | 211 | int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, |
| 191 | struct mtd_oob_ops *ops); | 212 | struct mtd_oob_ops *ops); |
| 213 | unsigned onenand_block(struct onenand_chip *this, loff_t addr); | ||
| 214 | loff_t onenand_addr(struct onenand_chip *this, int block); | ||
| 215 | int flexonenand_region(struct mtd_info *mtd, loff_t addr); | ||
| 192 | 216 | ||
| 193 | #endif /* __LINUX_MTD_ONENAND_H */ | 217 | #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 */ |
diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 7535a74083b9..af6dcb992bc3 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h | |||
| @@ -40,7 +40,6 @@ struct mtd_partition { | |||
| 40 | uint64_t offset; /* offset within the master MTD space */ | 40 | uint64_t offset; /* offset within the master MTD space */ |
| 41 | uint32_t mask_flags; /* master MTD flags to mask out for this partition */ | 41 | uint32_t mask_flags; /* master MTD flags to mask out for this partition */ |
| 42 | struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ | 42 | struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ |
| 43 | struct mtd_info **mtdp; /* pointer to store the MTD object */ | ||
| 44 | }; | 43 | }; |
| 45 | 44 | ||
| 46 | #define MTDPART_OFS_NXTBLK (-2) | 45 | #define MTDPART_OFS_NXTBLK (-2) |
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild index 8eb018f96002..192f8fb7d546 100644 --- a/include/mtd/Kbuild +++ b/include/mtd/Kbuild | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | header-y += inftl-user.h | 1 | header-y += inftl-user.h |
| 2 | header-y += jffs2-user.h | ||
| 3 | header-y += mtd-abi.h | 2 | header-y += mtd-abi.h |
| 4 | header-y += mtd-user.h | 3 | header-y += mtd-user.h |
| 5 | header-y += nftl-user.h | 4 | header-y += nftl-user.h |
diff --git a/include/mtd/jffs2-user.h b/include/mtd/jffs2-user.h deleted file mode 100644 index fa94b0eb67c1..000000000000 --- a/include/mtd/jffs2-user.h +++ /dev/null | |||
| @@ -1,34 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * JFFS2 definitions for use in user space only | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef __JFFS2_USER_H__ | ||
| 6 | #define __JFFS2_USER_H__ | ||
| 7 | |||
| 8 | /* This file is blessed for inclusion by userspace */ | ||
| 9 | #include <linux/jffs2.h> | ||
| 10 | #include <linux/types.h> | ||
| 11 | #include <endian.h> | ||
| 12 | #include <byteswap.h> | ||
| 13 | |||
| 14 | #undef cpu_to_je16 | ||
| 15 | #undef cpu_to_je32 | ||
| 16 | #undef cpu_to_jemode | ||
| 17 | #undef je16_to_cpu | ||
| 18 | #undef je32_to_cpu | ||
| 19 | #undef jemode_to_cpu | ||
| 20 | |||
| 21 | extern int target_endian; | ||
| 22 | |||
| 23 | #define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); }) | ||
| 24 | #define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); }) | ||
| 25 | |||
| 26 | #define cpu_to_je16(x) ((jint16_t){t16(x)}) | ||
| 27 | #define cpu_to_je32(x) ((jint32_t){t32(x)}) | ||
| 28 | #define cpu_to_jemode(x) ((jmode_t){t32(x)}) | ||
| 29 | |||
| 30 | #define je16_to_cpu(x) (t16((x).v16)) | ||
| 31 | #define je32_to_cpu(x) (t32((x).v32)) | ||
| 32 | #define jemode_to_cpu(x) (t32((x).m)) | ||
| 33 | |||
| 34 | #endif /* __JFFS2_USER_H__ */ | ||
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index b6595b3c68b6..be51ae2bd0ff 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h | |||
| @@ -12,12 +12,24 @@ struct erase_info_user { | |||
| 12 | __u32 length; | 12 | __u32 length; |
| 13 | }; | 13 | }; |
| 14 | 14 | ||
| 15 | struct erase_info_user64 { | ||
| 16 | __u64 start; | ||
| 17 | __u64 length; | ||
| 18 | }; | ||
| 19 | |||
| 15 | struct mtd_oob_buf { | 20 | struct mtd_oob_buf { |
| 16 | __u32 start; | 21 | __u32 start; |
| 17 | __u32 length; | 22 | __u32 length; |
| 18 | unsigned char __user *ptr; | 23 | unsigned char __user *ptr; |
| 19 | }; | 24 | }; |
| 20 | 25 | ||
| 26 | struct mtd_oob_buf64 { | ||
| 27 | __u64 start; | ||
| 28 | __u32 pad; | ||
| 29 | __u32 length; | ||
| 30 | __u64 usr_ptr; | ||
| 31 | }; | ||
| 32 | |||
| 21 | #define MTD_ABSENT 0 | 33 | #define MTD_ABSENT 0 |
| 22 | #define MTD_RAM 1 | 34 | #define MTD_RAM 1 |
| 23 | #define MTD_ROM 2 | 35 | #define MTD_ROM 2 |
| @@ -95,6 +107,9 @@ struct otp_info { | |||
| 95 | #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) | 107 | #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) |
| 96 | #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) | 108 | #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) |
| 97 | #define MTDFILEMODE _IO('M', 19) | 109 | #define MTDFILEMODE _IO('M', 19) |
| 110 | #define MEMERASE64 _IOW('M', 20, struct erase_info_user64) | ||
| 111 | #define MEMWRITEOOB64 _IOWR('M', 21, struct mtd_oob_buf64) | ||
| 112 | #define MEMREADOOB64 _IOWR('M', 22, struct mtd_oob_buf64) | ||
| 98 | 113 | ||
| 99 | /* | 114 | /* |
| 100 | * Obsolete legacy interface. Keep it in order not to break userspace | 115 | * Obsolete legacy interface. Keep it in order not to break userspace |
