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 |