diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 13:07:49 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 13:07:49 -0400 |
commit | a7c367b95a9d8e65e0f0e7da31f700a556794efb (patch) | |
tree | 5b1bb202801e29e3237381aa7aad5aa288378d5b | |
parent | 15f964bed054821d6d940d3752508c5f96a9ffd3 (diff) | |
parent | e1070211f7327a1f197d535aa886f721a241c32f (diff) |
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (58 commits)
mtd: jedec_probe: add PSD4256G6V id
mtd: OneNand support for Nomadik 8815 SoC (on NHK8815 board)
mtd: nand: driver for Nomadik 8815 SoC (on NHK8815 board)
m25p80: Add Spansion S25FL129P serial flashes
jffs2: Use SLAB_HWCACHE_ALIGN for jffs2_raw_{dirent,inode} slabs
mtd: sh_flctl: register sh_flctl using platform_driver_probe()
mtd: nand: txx9ndfmc: transfer 512 byte at a time if possible
mtd: nand: fix tmio_nand ecc correction
mtd: nand: add __nand_correct_data helper function
mtd: cfi_cmdset_0002: add 0xFF intolerance for M29W128G
mtd: inftl: fix fold chain block number
mtd: jedec: fix compilation problem with I28F640C3B definition
mtd: nand: fix ECC Correction bug for SMC ordering for NDFC driver
mtd: ofpart: Check availability of reg property instead of name property
driver/Makefile: Initialize "mtd" and "spi" before "net"
mtd: omap: adding DMA mode support in nand prefetch/post-write
mtd: omap: add support for nand prefetch-read and post-write
mtd: add nand support for w90p910 (v2)
mtd: maps: add mtd-ram support to physmap_of
mtd: pxa3xx_nand: add single-bit error corrections reporting
...
64 files changed, 2795 insertions, 251 deletions
diff --git a/Documentation/powerpc/dts-bindings/mtd-physmap.txt b/Documentation/powerpc/dts-bindings/mtd-physmap.txt index 667c9bde8699..80152cb567d9 100644 --- a/Documentation/powerpc/dts-bindings/mtd-physmap.txt +++ b/Documentation/powerpc/dts-bindings/mtd-physmap.txt | |||
@@ -1,18 +1,19 @@ | |||
1 | CFI or JEDEC memory-mapped NOR flash | 1 | CFI or JEDEC memory-mapped NOR flash, MTD-RAM (NVRAM...) |
2 | 2 | ||
3 | Flash chips (Memory Technology Devices) are often used for solid state | 3 | Flash chips (Memory Technology Devices) are often used for solid state |
4 | file systems on embedded devices. | 4 | file systems on embedded devices. |
5 | 5 | ||
6 | - compatible : should contain the specific model of flash chip(s) | 6 | - compatible : should contain the specific model of mtd chip(s) |
7 | used, if known, followed by either "cfi-flash" or "jedec-flash" | 7 | used, if known, followed by either "cfi-flash", "jedec-flash" |
8 | - reg : Address range(s) of the flash chip(s) | 8 | or "mtd-ram". |
9 | - reg : Address range(s) of the mtd chip(s) | ||
9 | It's possible to (optionally) define multiple "reg" tuples so that | 10 | It's possible to (optionally) define multiple "reg" tuples so that |
10 | non-identical NOR chips can be described in one flash node. | 11 | non-identical chips can be described in one node. |
11 | - bank-width : Width (in bytes) of the flash bank. Equal to the | 12 | - bank-width : Width (in bytes) of the bank. Equal to the |
12 | device width times the number of interleaved chips. | 13 | device width times the number of interleaved chips. |
13 | - device-width : (optional) Width of a single flash chip. If | 14 | - device-width : (optional) Width of a single mtd chip. If |
14 | omitted, assumed to be equal to 'bank-width'. | 15 | omitted, assumed to be equal to 'bank-width'. |
15 | - #address-cells, #size-cells : Must be present if the flash has | 16 | - #address-cells, #size-cells : Must be present if the device has |
16 | sub-nodes representing partitions (see below). In this case | 17 | sub-nodes representing partitions (see below). In this case |
17 | both #address-cells and #size-cells must be equal to 1. | 18 | both #address-cells and #size-cells must be equal to 1. |
18 | 19 | ||
@@ -22,24 +23,24 @@ are defined: | |||
22 | - vendor-id : Contains the flash chip's vendor id (1 byte). | 23 | - vendor-id : Contains the flash chip's vendor id (1 byte). |
23 | - device-id : Contains the flash chip's device id (1 byte). | 24 | - device-id : Contains the flash chip's device id (1 byte). |
24 | 25 | ||
25 | In addition to the information on the flash bank itself, the | 26 | In addition to the information on the mtd bank itself, the |
26 | device tree may optionally contain additional information | 27 | device tree may optionally contain additional information |
27 | describing partitions of the flash address space. This can be | 28 | describing partitions of the address space. This can be |
28 | used on platforms which have strong conventions about which | 29 | used on platforms which have strong conventions about which |
29 | portions of the flash are used for what purposes, but which don't | 30 | portions of a flash are used for what purposes, but which don't |
30 | use an on-flash partition table such as RedBoot. | 31 | use an on-flash partition table such as RedBoot. |
31 | 32 | ||
32 | Each partition is represented as a sub-node of the flash device. | 33 | Each partition is represented as a sub-node of the mtd device. |
33 | Each node's name represents the name of the corresponding | 34 | Each node's name represents the name of the corresponding |
34 | partition of the flash device. | 35 | partition of the mtd device. |
35 | 36 | ||
36 | Flash partitions | 37 | Flash partitions |
37 | - reg : The partition's offset and size within the flash bank. | 38 | - reg : The partition's offset and size within the mtd bank. |
38 | - label : (optional) The label / name for this flash partition. | 39 | - label : (optional) The label / name for this partition. |
39 | If omitted, the label is taken from the node name (excluding | 40 | If omitted, the label is taken from the node name (excluding |
40 | the unit address). | 41 | the unit address). |
41 | - read-only : (optional) This parameter, if present, is a hint to | 42 | - read-only : (optional) This parameter, if present, is a hint to |
42 | Linux that this flash partition should only be mounted | 43 | Linux that this partition should only be mounted |
43 | read-only. This is usually used for flash partitions | 44 | read-only. This is usually used for flash partitions |
44 | containing early-boot firmware images or data which should not | 45 | containing early-boot firmware images or data which should not |
45 | be clobbered. | 46 | be clobbered. |
@@ -78,3 +79,12 @@ Here an example with multiple "reg" tuples: | |||
78 | reg = <0 0x04000000>; | 79 | reg = <0 0x04000000>; |
79 | }; | 80 | }; |
80 | }; | 81 | }; |
82 | |||
83 | An example using SRAM: | ||
84 | |||
85 | sram@2,0 { | ||
86 | compatible = "samsung,k6f1616u6a", "mtd-ram"; | ||
87 | reg = <2 0 0x00200000>; | ||
88 | bank-width = <2>; | ||
89 | }; | ||
90 | |||
diff --git a/arch/arm/configs/nhk8815_defconfig b/arch/arm/configs/nhk8815_defconfig index 9bb45b932f04..600cb270f2bf 100644 --- a/arch/arm/configs/nhk8815_defconfig +++ b/arch/arm/configs/nhk8815_defconfig | |||
@@ -498,7 +498,7 @@ CONFIG_MTD_CFI_I2=y | |||
498 | # CONFIG_MTD_DOC2001PLUS is not set | 498 | # CONFIG_MTD_DOC2001PLUS is not set |
499 | CONFIG_MTD_NAND=y | 499 | CONFIG_MTD_NAND=y |
500 | CONFIG_MTD_NAND_VERIFY_WRITE=y | 500 | CONFIG_MTD_NAND_VERIFY_WRITE=y |
501 | # CONFIG_MTD_NAND_ECC_SMC is not set | 501 | CONFIG_MTD_NAND_ECC_SMC=y |
502 | # CONFIG_MTD_NAND_MUSEUM_IDS is not set | 502 | # CONFIG_MTD_NAND_MUSEUM_IDS is not set |
503 | # CONFIG_MTD_NAND_GPIO is not set | 503 | # CONFIG_MTD_NAND_GPIO is not set |
504 | CONFIG_MTD_NAND_IDS=y | 504 | CONFIG_MTD_NAND_IDS=y |
diff --git a/arch/arm/mach-nomadik/board-nhk8815.c b/arch/arm/mach-nomadik/board-nhk8815.c index 79bdea943eb4..6bfd537d5afb 100644 --- a/arch/arm/mach-nomadik/board-nhk8815.c +++ b/arch/arm/mach-nomadik/board-nhk8815.c | |||
@@ -16,12 +16,164 @@ | |||
16 | #include <linux/amba/bus.h> | 16 | #include <linux/amba/bus.h> |
17 | #include <linux/interrupt.h> | 17 | #include <linux/interrupt.h> |
18 | #include <linux/gpio.h> | 18 | #include <linux/gpio.h> |
19 | #include <linux/mtd/mtd.h> | ||
20 | #include <linux/mtd/nand.h> | ||
21 | #include <linux/mtd/partitions.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <asm/sizes.h> | ||
19 | #include <asm/mach-types.h> | 24 | #include <asm/mach-types.h> |
20 | #include <asm/mach/arch.h> | 25 | #include <asm/mach/arch.h> |
21 | #include <asm/mach/irq.h> | 26 | #include <asm/mach/irq.h> |
27 | #include <asm/mach/flash.h> | ||
22 | #include <mach/setup.h> | 28 | #include <mach/setup.h> |
29 | #include <mach/nand.h> | ||
30 | #include <mach/fsmc.h> | ||
23 | #include "clock.h" | 31 | #include "clock.h" |
24 | 32 | ||
33 | /* These adresses span 16MB, so use three individual pages */ | ||
34 | static struct resource nhk8815_nand_resources[] = { | ||
35 | { | ||
36 | .name = "nand_addr", | ||
37 | .start = NAND_IO_ADDR, | ||
38 | .end = NAND_IO_ADDR + 0xfff, | ||
39 | .flags = IORESOURCE_MEM, | ||
40 | }, { | ||
41 | .name = "nand_cmd", | ||
42 | .start = NAND_IO_CMD, | ||
43 | .end = NAND_IO_CMD + 0xfff, | ||
44 | .flags = IORESOURCE_MEM, | ||
45 | }, { | ||
46 | .name = "nand_data", | ||
47 | .start = NAND_IO_DATA, | ||
48 | .end = NAND_IO_DATA + 0xfff, | ||
49 | .flags = IORESOURCE_MEM, | ||
50 | } | ||
51 | }; | ||
52 | |||
53 | static int nhk8815_nand_init(void) | ||
54 | { | ||
55 | /* FSMC setup for nand chip select (8-bit nand in 8815NHK) */ | ||
56 | writel(0x0000000E, FSMC_PCR(0)); | ||
57 | writel(0x000D0A00, FSMC_PMEM(0)); | ||
58 | writel(0x00100A00, FSMC_PATT(0)); | ||
59 | |||
60 | /* enable access to the chip select area */ | ||
61 | writel(readl(FSMC_PCR(0)) | 0x04, FSMC_PCR(0)); | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * These partitions are the same as those used in the 2.6.20 release | ||
68 | * shipped by the vendor; the first two partitions are mandated | ||
69 | * by the boot ROM, and the bootloader area is somehow oversized... | ||
70 | */ | ||
71 | static struct mtd_partition nhk8815_partitions[] = { | ||
72 | { | ||
73 | .name = "X-Loader(NAND)", | ||
74 | .offset = 0, | ||
75 | .size = SZ_256K, | ||
76 | }, { | ||
77 | .name = "MemInit(NAND)", | ||
78 | .offset = MTDPART_OFS_APPEND, | ||
79 | .size = SZ_256K, | ||
80 | }, { | ||
81 | .name = "BootLoader(NAND)", | ||
82 | .offset = MTDPART_OFS_APPEND, | ||
83 | .size = SZ_2M, | ||
84 | }, { | ||
85 | .name = "Kernel zImage(NAND)", | ||
86 | .offset = MTDPART_OFS_APPEND, | ||
87 | .size = 3 * SZ_1M, | ||
88 | }, { | ||
89 | .name = "Root Filesystem(NAND)", | ||
90 | .offset = MTDPART_OFS_APPEND, | ||
91 | .size = 22 * SZ_1M, | ||
92 | }, { | ||
93 | .name = "User Filesystem(NAND)", | ||
94 | .offset = MTDPART_OFS_APPEND, | ||
95 | .size = MTDPART_SIZ_FULL, | ||
96 | } | ||
97 | }; | ||
98 | |||
99 | static struct nomadik_nand_platform_data nhk8815_nand_data = { | ||
100 | .parts = nhk8815_partitions, | ||
101 | .nparts = ARRAY_SIZE(nhk8815_partitions), | ||
102 | .options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING \ | ||
103 | | NAND_NO_READRDY | NAND_NO_AUTOINCR, | ||
104 | .init = nhk8815_nand_init, | ||
105 | }; | ||
106 | |||
107 | static struct platform_device nhk8815_nand_device = { | ||
108 | .name = "nomadik_nand", | ||
109 | .dev = { | ||
110 | .platform_data = &nhk8815_nand_data, | ||
111 | }, | ||
112 | .resource = nhk8815_nand_resources, | ||
113 | .num_resources = ARRAY_SIZE(nhk8815_nand_resources), | ||
114 | }; | ||
115 | |||
116 | /* These are the partitions for the OneNand device, different from above */ | ||
117 | static struct mtd_partition nhk8815_onenand_partitions[] = { | ||
118 | { | ||
119 | .name = "X-Loader(OneNAND)", | ||
120 | .offset = 0, | ||
121 | .size = SZ_256K, | ||
122 | }, { | ||
123 | .name = "MemInit(OneNAND)", | ||
124 | .offset = MTDPART_OFS_APPEND, | ||
125 | .size = SZ_256K, | ||
126 | }, { | ||
127 | .name = "BootLoader(OneNAND)", | ||
128 | .offset = MTDPART_OFS_APPEND, | ||
129 | .size = SZ_2M-SZ_256K, | ||
130 | }, { | ||
131 | .name = "SysImage(OneNAND)", | ||
132 | .offset = MTDPART_OFS_APPEND, | ||
133 | .size = 4 * SZ_1M, | ||
134 | }, { | ||
135 | .name = "Root Filesystem(OneNAND)", | ||
136 | .offset = MTDPART_OFS_APPEND, | ||
137 | .size = 22 * SZ_1M, | ||
138 | }, { | ||
139 | .name = "User Filesystem(OneNAND)", | ||
140 | .offset = MTDPART_OFS_APPEND, | ||
141 | .size = MTDPART_SIZ_FULL, | ||
142 | } | ||
143 | }; | ||
144 | |||
145 | static struct flash_platform_data nhk8815_onenand_data = { | ||
146 | .parts = nhk8815_onenand_partitions, | ||
147 | .nr_parts = ARRAY_SIZE(nhk8815_onenand_partitions), | ||
148 | }; | ||
149 | |||
150 | static struct resource nhk8815_onenand_resource[] = { | ||
151 | { | ||
152 | .start = 0x30000000, | ||
153 | .end = 0x30000000 + SZ_128K - 1, | ||
154 | .flags = IORESOURCE_MEM, | ||
155 | }, | ||
156 | }; | ||
157 | |||
158 | static struct platform_device nhk8815_onenand_device = { | ||
159 | .name = "onenand", | ||
160 | .id = -1, | ||
161 | .dev = { | ||
162 | .platform_data = &nhk8815_onenand_data, | ||
163 | }, | ||
164 | .resource = nhk8815_onenand_resource, | ||
165 | .num_resources = ARRAY_SIZE(nhk8815_onenand_resource), | ||
166 | }; | ||
167 | |||
168 | static void __init nhk8815_onenand_init(void) | ||
169 | { | ||
170 | #ifdef CONFIG_ONENAND | ||
171 | /* Set up SMCS0 for OneNand */ | ||
172 | writel(0x000030db, FSMC_BCR0); | ||
173 | writel(0x02100551, FSMC_BTR0); | ||
174 | #endif | ||
175 | } | ||
176 | |||
25 | #define __MEM_4K_RESOURCE(x) \ | 177 | #define __MEM_4K_RESOURCE(x) \ |
26 | .res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM} | 178 | .res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM} |
27 | 179 | ||
@@ -81,6 +233,8 @@ static int __init nhk8815_eth_init(void) | |||
81 | device_initcall(nhk8815_eth_init); | 233 | device_initcall(nhk8815_eth_init); |
82 | 234 | ||
83 | static struct platform_device *nhk8815_platform_devices[] __initdata = { | 235 | static struct platform_device *nhk8815_platform_devices[] __initdata = { |
236 | &nhk8815_nand_device, | ||
237 | &nhk8815_onenand_device, | ||
84 | &nhk8815_eth_device, | 238 | &nhk8815_eth_device, |
85 | /* will add more devices */ | 239 | /* will add more devices */ |
86 | }; | 240 | }; |
@@ -90,6 +244,7 @@ static void __init nhk8815_platform_init(void) | |||
90 | int i; | 244 | int i; |
91 | 245 | ||
92 | cpu8815_platform_init(); | 246 | cpu8815_platform_init(); |
247 | nhk8815_onenand_init(); | ||
93 | platform_add_devices(nhk8815_platform_devices, | 248 | platform_add_devices(nhk8815_platform_devices, |
94 | ARRAY_SIZE(nhk8815_platform_devices)); | 249 | ARRAY_SIZE(nhk8815_platform_devices)); |
95 | 250 | ||
diff --git a/arch/arm/mach-nomadik/include/mach/fsmc.h b/arch/arm/mach-nomadik/include/mach/fsmc.h new file mode 100644 index 000000000000..8c2c05183685 --- /dev/null +++ b/arch/arm/mach-nomadik/include/mach/fsmc.h | |||
@@ -0,0 +1,29 @@ | |||
1 | |||
2 | /* Definitions for the Nomadik FSMC "Flexible Static Memory controller" */ | ||
3 | |||
4 | #ifndef __ASM_ARCH_FSMC_H | ||
5 | #define __ASM_ARCH_FSMC_H | ||
6 | |||
7 | #include <mach/hardware.h> | ||
8 | /* | ||
9 | * Register list | ||
10 | */ | ||
11 | |||
12 | /* bus control reg. and bus timing reg. for CS0..CS3 */ | ||
13 | #define FSMC_BCR(x) (NOMADIK_FSMC_VA + (x << 3)) | ||
14 | #define FSMC_BTR(x) (NOMADIK_FSMC_VA + (x << 3) + 0x04) | ||
15 | |||
16 | /* PC-card and NAND: | ||
17 | * PCR = control register | ||
18 | * PMEM = memory timing | ||
19 | * PATT = attribute timing | ||
20 | * PIO = I/O timing | ||
21 | * PECCR = ECC result | ||
22 | */ | ||
23 | #define FSMC_PCR(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x00) | ||
24 | #define FSMC_PMEM(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x08) | ||
25 | #define FSMC_PATT(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x0c) | ||
26 | #define FSMC_PIO(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x10) | ||
27 | #define FSMC_PECCR(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x14) | ||
28 | |||
29 | #endif /* __ASM_ARCH_FSMC_H */ | ||
diff --git a/arch/arm/mach-nomadik/include/mach/nand.h b/arch/arm/mach-nomadik/include/mach/nand.h new file mode 100644 index 000000000000..c3c8254c22a5 --- /dev/null +++ b/arch/arm/mach-nomadik/include/mach/nand.h | |||
@@ -0,0 +1,16 @@ | |||
1 | #ifndef __ASM_ARCH_NAND_H | ||
2 | #define __ASM_ARCH_NAND_H | ||
3 | |||
4 | struct nomadik_nand_platform_data { | ||
5 | struct mtd_partition *parts; | ||
6 | int nparts; | ||
7 | int options; | ||
8 | int (*init) (void); | ||
9 | int (*exit) (void); | ||
10 | }; | ||
11 | |||
12 | #define NAND_IO_DATA 0x40000000 | ||
13 | #define NAND_IO_CMD 0x40800000 | ||
14 | #define NAND_IO_ADDR 0x41000000 | ||
15 | |||
16 | #endif /* __ASM_ARCH_NAND_H */ | ||
diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c index 7a2b54c7291a..a1132288c701 100644 --- a/arch/arm/mach-omap2/board-apollon.c +++ b/arch/arm/mach-omap2/board-apollon.c | |||
@@ -87,7 +87,7 @@ static struct mtd_partition apollon_partitions[] = { | |||
87 | }, | 87 | }, |
88 | }; | 88 | }; |
89 | 89 | ||
90 | static struct flash_platform_data apollon_flash_data = { | 90 | static struct onenand_platform_data apollon_flash_data = { |
91 | .parts = apollon_partitions, | 91 | .parts = apollon_partitions, |
92 | .nr_parts = ARRAY_SIZE(apollon_partitions), | 92 | .nr_parts = ARRAY_SIZE(apollon_partitions), |
93 | }; | 93 | }; |
@@ -99,7 +99,7 @@ static struct resource apollon_flash_resource[] = { | |||
99 | }; | 99 | }; |
100 | 100 | ||
101 | static struct platform_device apollon_onenand_device = { | 101 | static struct platform_device apollon_onenand_device = { |
102 | .name = "onenand", | 102 | .name = "onenand-flash", |
103 | .id = -1, | 103 | .id = -1, |
104 | .dev = { | 104 | .dev = { |
105 | .platform_data = &apollon_flash_data, | 105 | .platform_data = &apollon_flash_data, |
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index f91934b2b092..15876828db23 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c | |||
@@ -57,6 +57,11 @@ | |||
57 | #define GPMC_CHUNK_SHIFT 24 /* 16 MB */ | 57 | #define GPMC_CHUNK_SHIFT 24 /* 16 MB */ |
58 | #define GPMC_SECTION_SHIFT 28 /* 128 MB */ | 58 | #define GPMC_SECTION_SHIFT 28 /* 128 MB */ |
59 | 59 | ||
60 | #define PREFETCH_FIFOTHRESHOLD (0x40 << 8) | ||
61 | #define CS_NUM_SHIFT 24 | ||
62 | #define ENABLE_PREFETCH (0x1 << 7) | ||
63 | #define DMA_MPU_MODE 2 | ||
64 | |||
60 | static struct resource gpmc_mem_root; | 65 | static struct resource gpmc_mem_root; |
61 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; | 66 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; |
62 | static DEFINE_SPINLOCK(gpmc_mem_lock); | 67 | static DEFINE_SPINLOCK(gpmc_mem_lock); |
@@ -386,6 +391,63 @@ void gpmc_cs_free(int cs) | |||
386 | } | 391 | } |
387 | EXPORT_SYMBOL(gpmc_cs_free); | 392 | EXPORT_SYMBOL(gpmc_cs_free); |
388 | 393 | ||
394 | /** | ||
395 | * gpmc_prefetch_enable - configures and starts prefetch transfer | ||
396 | * @cs: nand cs (chip select) number | ||
397 | * @dma_mode: dma mode enable (1) or disable (0) | ||
398 | * @u32_count: number of bytes to be transferred | ||
399 | * @is_write: prefetch read(0) or write post(1) mode | ||
400 | */ | ||
401 | int gpmc_prefetch_enable(int cs, int dma_mode, | ||
402 | unsigned int u32_count, int is_write) | ||
403 | { | ||
404 | uint32_t prefetch_config1; | ||
405 | |||
406 | if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) { | ||
407 | /* Set the amount of bytes to be prefetched */ | ||
408 | gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count); | ||
409 | |||
410 | /* Set dma/mpu mode, the prefetch read / post write and | ||
411 | * enable the engine. Set which cs is has requested for. | ||
412 | */ | ||
413 | prefetch_config1 = ((cs << CS_NUM_SHIFT) | | ||
414 | PREFETCH_FIFOTHRESHOLD | | ||
415 | ENABLE_PREFETCH | | ||
416 | (dma_mode << DMA_MPU_MODE) | | ||
417 | (0x1 & is_write)); | ||
418 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1); | ||
419 | } else { | ||
420 | return -EBUSY; | ||
421 | } | ||
422 | /* Start the prefetch engine */ | ||
423 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1); | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | EXPORT_SYMBOL(gpmc_prefetch_enable); | ||
428 | |||
429 | /** | ||
430 | * gpmc_prefetch_reset - disables and stops the prefetch engine | ||
431 | */ | ||
432 | void gpmc_prefetch_reset(void) | ||
433 | { | ||
434 | /* Stop the PFPW engine */ | ||
435 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0); | ||
436 | |||
437 | /* Reset/disable the PFPW engine */ | ||
438 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0); | ||
439 | } | ||
440 | EXPORT_SYMBOL(gpmc_prefetch_reset); | ||
441 | |||
442 | /** | ||
443 | * gpmc_prefetch_status - reads prefetch status of engine | ||
444 | */ | ||
445 | int gpmc_prefetch_status(void) | ||
446 | { | ||
447 | return gpmc_read_reg(GPMC_PREFETCH_STATUS); | ||
448 | } | ||
449 | EXPORT_SYMBOL(gpmc_prefetch_status); | ||
450 | |||
389 | static void __init gpmc_mem_init(void) | 451 | static void __init gpmc_mem_init(void) |
390 | { | 452 | { |
391 | int cs; | 453 | int cs; |
@@ -452,6 +514,5 @@ void __init gpmc_init(void) | |||
452 | l &= 0x03 << 3; | 514 | l &= 0x03 << 3; |
453 | l |= (0x02 << 3) | (1 << 0); | 515 | l |= (0x02 << 3) | (1 << 0); |
454 | gpmc_write_reg(GPMC_SYSCONFIG, l); | 516 | gpmc_write_reg(GPMC_SYSCONFIG, l); |
455 | |||
456 | gpmc_mem_init(); | 517 | gpmc_mem_init(); |
457 | } | 518 | } |
diff --git a/arch/arm/plat-omap/include/mach/gpmc.h b/arch/arm/plat-omap/include/mach/gpmc.h index 921b16532ff5..9c99cda77ba6 100644 --- a/arch/arm/plat-omap/include/mach/gpmc.h +++ b/arch/arm/plat-omap/include/mach/gpmc.h | |||
@@ -103,6 +103,10 @@ extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); | |||
103 | extern void gpmc_cs_free(int cs); | 103 | extern void gpmc_cs_free(int cs); |
104 | extern int gpmc_cs_set_reserved(int cs, int reserved); | 104 | extern int gpmc_cs_set_reserved(int cs, int reserved); |
105 | extern int gpmc_cs_reserved(int cs); | 105 | extern int gpmc_cs_reserved(int cs); |
106 | extern int gpmc_prefetch_enable(int cs, int dma_mode, | ||
107 | unsigned int u32_count, int is_write); | ||
108 | extern void gpmc_prefetch_reset(void); | ||
109 | extern int gpmc_prefetch_status(void); | ||
106 | extern void __init gpmc_init(void); | 110 | extern void __init gpmc_init(void); |
107 | 111 | ||
108 | #endif | 112 | #endif |
diff --git a/drivers/Makefile b/drivers/Makefile index ccfa259fa848..6ee53c7a57a1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile | |||
@@ -43,6 +43,8 @@ obj-y += macintosh/ | |||
43 | obj-$(CONFIG_IDE) += ide/ | 43 | obj-$(CONFIG_IDE) += ide/ |
44 | obj-$(CONFIG_SCSI) += scsi/ | 44 | obj-$(CONFIG_SCSI) += scsi/ |
45 | obj-$(CONFIG_ATA) += ata/ | 45 | obj-$(CONFIG_ATA) += ata/ |
46 | obj-$(CONFIG_MTD) += mtd/ | ||
47 | obj-$(CONFIG_SPI) += spi/ | ||
46 | obj-y += net/ | 48 | obj-y += net/ |
47 | obj-$(CONFIG_ATM) += atm/ | 49 | obj-$(CONFIG_ATM) += atm/ |
48 | obj-$(CONFIG_FUSION) += message/ | 50 | obj-$(CONFIG_FUSION) += message/ |
@@ -51,8 +53,6 @@ obj-y += ieee1394/ | |||
51 | obj-$(CONFIG_UIO) += uio/ | 53 | obj-$(CONFIG_UIO) += uio/ |
52 | obj-y += cdrom/ | 54 | obj-y += cdrom/ |
53 | obj-y += auxdisplay/ | 55 | obj-y += auxdisplay/ |
54 | obj-$(CONFIG_MTD) += mtd/ | ||
55 | obj-$(CONFIG_SPI) += spi/ | ||
56 | obj-$(CONFIG_PCCARD) += pcmcia/ | 56 | obj-$(CONFIG_PCCARD) += pcmcia/ |
57 | obj-$(CONFIG_DIO) += dio/ | 57 | obj-$(CONFIG_DIO) += dio/ |
58 | obj-$(CONFIG_SBUS) += sbus/ | 58 | obj-$(CONFIG_SBUS) += sbus/ |
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index b8e35a0b4d72..e4ec3659759a 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig | |||
@@ -25,6 +25,14 @@ config MTD_DEBUG_VERBOSE | |||
25 | help | 25 | help |
26 | Determines the verbosity level of the MTD debugging messages. | 26 | Determines the verbosity level of the MTD debugging messages. |
27 | 27 | ||
28 | config MTD_TESTS | ||
29 | tristate "MTD tests support" | ||
30 | depends on m | ||
31 | help | ||
32 | This option includes various MTD tests into compilation. The tests | ||
33 | should normally be compiled as kernel modules. The modules perform | ||
34 | various checks and verifications when loaded. | ||
35 | |||
28 | config MTD_CONCAT | 36 | config MTD_CONCAT |
29 | tristate "MTD concatenating support" | 37 | tristate "MTD concatenating support" |
30 | help | 38 | help |
@@ -45,14 +53,6 @@ config MTD_PARTITIONS | |||
45 | devices. Partitioning on NFTL 'devices' is a different - that's the | 53 | devices. Partitioning on NFTL 'devices' is a different - that's the |
46 | 'normal' form of partitioning used on a block device. | 54 | 'normal' form of partitioning used on a block device. |
47 | 55 | ||
48 | config MTD_TESTS | ||
49 | tristate "MTD tests support" | ||
50 | depends on m | ||
51 | help | ||
52 | This option includes various MTD tests into compilation. The tests | ||
53 | should normally be compiled as kernel modules. The modules perform | ||
54 | various checks and verifications when loaded. | ||
55 | |||
56 | config MTD_REDBOOT_PARTS | 56 | config MTD_REDBOOT_PARTS |
57 | tristate "RedBoot partition table parsing" | 57 | tristate "RedBoot partition table parsing" |
58 | depends on MTD_PARTITIONS | 58 | depends on MTD_PARTITIONS |
diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index d072ca5be689..cec7ab98b2a9 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c | |||
@@ -239,7 +239,7 @@ static int parse_afs_partitions(struct mtd_info *mtd, | |||
239 | parts[idx].offset = img_ptr; | 239 | parts[idx].offset = img_ptr; |
240 | parts[idx].mask_flags = 0; | 240 | parts[idx].mask_flags = 0; |
241 | 241 | ||
242 | printk(" mtd%d: at 0x%08x, %5dKB, %8u, %s\n", | 242 | printk(" mtd%d: at 0x%08x, %5lluKiB, %8u, %s\n", |
243 | idx, img_ptr, parts[idx].size / 1024, | 243 | idx, img_ptr, parts[idx].size / 1024, |
244 | iis.imageNumber, str); | 244 | iis.imageNumber, str); |
245 | 245 | ||
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 61ea833e0908..94bb61e19047 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | |||
@@ -282,16 +282,6 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param) | |||
282 | } | 282 | } |
283 | } | 283 | } |
284 | 284 | ||
285 | static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param) | ||
286 | { | ||
287 | struct map_info *map = mtd->priv; | ||
288 | struct cfi_private *cfi = map->fldrv_priv; | ||
289 | if (cfi->cfiq->BufWriteTimeoutTyp) { | ||
290 | pr_warning("Don't use write buffer on ST flash M29W128G\n"); | ||
291 | cfi->cfiq->BufWriteTimeoutTyp = 0; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | static struct cfi_fixup cfi_fixup_table[] = { | 285 | static struct cfi_fixup cfi_fixup_table[] = { |
296 | { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, | 286 | { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, |
297 | #ifdef AMD_BOOTLOC_BUG | 287 | #ifdef AMD_BOOTLOC_BUG |
@@ -308,7 +298,6 @@ static struct cfi_fixup cfi_fixup_table[] = { | |||
308 | { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, | 298 | { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, |
309 | { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, | 299 | { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, |
310 | { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, | 300 | { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, |
311 | { CFI_MFR_ST, 0x227E, fixup_M29W128G_write_buffer, NULL, }, | ||
312 | #if !FORCE_WORD_WRITE | 301 | #if !FORCE_WORD_WRITE |
313 | { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, | 302 | { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, |
314 | #endif | 303 | #endif |
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 34d40e25d312..c5a84fda5410 100644..100755 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c | |||
@@ -81,6 +81,10 @@ void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map, | |||
81 | { | 81 | { |
82 | cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); | 82 | cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); |
83 | cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); | 83 | cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); |
84 | /* M29W128G flashes require an additional reset command | ||
85 | when exit qry mode */ | ||
86 | if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E)) | ||
87 | cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); | ||
84 | } | 88 | } |
85 | EXPORT_SYMBOL_GPL(cfi_qry_mode_off); | 89 | EXPORT_SYMBOL_GPL(cfi_qry_mode_off); |
86 | 90 | ||
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index ccc4cfc7e4b5..736a3be265f2 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c | |||
@@ -111,6 +111,11 @@ | |||
111 | #define I28F320B3B 0x8897 | 111 | #define I28F320B3B 0x8897 |
112 | #define I28F640B3T 0x8898 | 112 | #define I28F640B3T 0x8898 |
113 | #define I28F640B3B 0x8899 | 113 | #define I28F640B3B 0x8899 |
114 | #define I28F640C3B 0x88CD | ||
115 | #define I28F160F3T 0x88F3 | ||
116 | #define I28F160F3B 0x88F4 | ||
117 | #define I28F160C3T 0x88C2 | ||
118 | #define I28F160C3B 0x88C3 | ||
114 | #define I82802AB 0x00ad | 119 | #define I82802AB 0x00ad |
115 | #define I82802AC 0x00ac | 120 | #define I82802AC 0x00ac |
116 | 121 | ||
@@ -150,6 +155,7 @@ | |||
150 | #define M50LPW080 0x002F | 155 | #define M50LPW080 0x002F |
151 | #define M50FLW080A 0x0080 | 156 | #define M50FLW080A 0x0080 |
152 | #define M50FLW080B 0x0081 | 157 | #define M50FLW080B 0x0081 |
158 | #define PSD4256G6V 0x00e9 | ||
153 | 159 | ||
154 | /* SST */ | 160 | /* SST */ |
155 | #define SST29EE020 0x0010 | 161 | #define SST29EE020 0x0010 |
@@ -201,6 +207,7 @@ enum uaddr { | |||
201 | MTD_UADDR_0x0555_0x02AA, | 207 | MTD_UADDR_0x0555_0x02AA, |
202 | MTD_UADDR_0x0555_0x0AAA, | 208 | MTD_UADDR_0x0555_0x0AAA, |
203 | MTD_UADDR_0x5555_0x2AAA, | 209 | MTD_UADDR_0x5555_0x2AAA, |
210 | MTD_UADDR_0x0AAA_0x0554, | ||
204 | MTD_UADDR_0x0AAA_0x0555, | 211 | MTD_UADDR_0x0AAA_0x0555, |
205 | MTD_UADDR_0xAAAA_0x5555, | 212 | MTD_UADDR_0xAAAA_0x5555, |
206 | MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ | 213 | MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ |
@@ -245,6 +252,11 @@ static const struct unlock_addr unlock_addrs[] = { | |||
245 | .addr2 = 0x2aaa | 252 | .addr2 = 0x2aaa |
246 | }, | 253 | }, |
247 | 254 | ||
255 | [MTD_UADDR_0x0AAA_0x0554] = { | ||
256 | .addr1 = 0x0AAA, | ||
257 | .addr2 = 0x0554 | ||
258 | }, | ||
259 | |||
248 | [MTD_UADDR_0x0AAA_0x0555] = { | 260 | [MTD_UADDR_0x0AAA_0x0555] = { |
249 | .addr1 = 0x0AAA, | 261 | .addr1 = 0x0AAA, |
250 | .addr2 = 0x0555 | 262 | .addr2 = 0x0555 |
@@ -1103,6 +1115,19 @@ static const struct amd_flash_info jedec_table[] = { | |||
1103 | } | 1115 | } |
1104 | }, { | 1116 | }, { |
1105 | .mfr_id = MANUFACTURER_INTEL, | 1117 | .mfr_id = MANUFACTURER_INTEL, |
1118 | .dev_id = I28F640C3B, | ||
1119 | .name = "Intel 28F640C3B", | ||
1120 | .devtypes = CFI_DEVICETYPE_X16, | ||
1121 | .uaddr = MTD_UADDR_UNNECESSARY, | ||
1122 | .dev_size = SIZE_8MiB, | ||
1123 | .cmd_set = P_ID_INTEL_STD, | ||
1124 | .nr_regions = 2, | ||
1125 | .regions = { | ||
1126 | ERASEINFO(0x02000, 8), | ||
1127 | ERASEINFO(0x10000, 127), | ||
1128 | } | ||
1129 | }, { | ||
1130 | .mfr_id = MANUFACTURER_INTEL, | ||
1106 | .dev_id = I82802AB, | 1131 | .dev_id = I82802AB, |
1107 | .name = "Intel 82802AB", | 1132 | .name = "Intel 82802AB", |
1108 | .devtypes = CFI_DEVICETYPE_X8, | 1133 | .devtypes = CFI_DEVICETYPE_X8, |
@@ -1156,8 +1181,8 @@ static const struct amd_flash_info jedec_table[] = { | |||
1156 | .mfr_id = MANUFACTURER_NEC, | 1181 | .mfr_id = MANUFACTURER_NEC, |
1157 | .dev_id = UPD29F064115, | 1182 | .dev_id = UPD29F064115, |
1158 | .name = "NEC uPD29F064115", | 1183 | .name = "NEC uPD29F064115", |
1159 | .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, | 1184 | .devtypes = CFI_DEVICETYPE_X16, |
1160 | .uaddr = MTD_UADDR_0x0555_0x02AA, /* ???? */ | 1185 | .uaddr = MTD_UADDR_0xAAAA_0x5555, |
1161 | .dev_size = SIZE_8MiB, | 1186 | .dev_size = SIZE_8MiB, |
1162 | .cmd_set = P_ID_AMD_STD, | 1187 | .cmd_set = P_ID_AMD_STD, |
1163 | .nr_regions = 3, | 1188 | .nr_regions = 3, |
@@ -1726,6 +1751,18 @@ static const struct amd_flash_info jedec_table[] = { | |||
1726 | ERASEINFO(0x1000,16), | 1751 | ERASEINFO(0x1000,16), |
1727 | } | 1752 | } |
1728 | }, { | 1753 | }, { |
1754 | .mfr_id = 0xff00 | MANUFACTURER_ST, | ||
1755 | .dev_id = 0xff00 | PSD4256G6V, | ||
1756 | .name = "ST PSD4256G6V", | ||
1757 | .devtypes = CFI_DEVICETYPE_X16, | ||
1758 | .uaddr = MTD_UADDR_0x0AAA_0x0554, | ||
1759 | .dev_size = SIZE_1MiB, | ||
1760 | .cmd_set = P_ID_AMD_STD, | ||
1761 | .nr_regions = 1, | ||
1762 | .regions = { | ||
1763 | ERASEINFO(0x10000,16), | ||
1764 | } | ||
1765 | }, { | ||
1729 | .mfr_id = MANUFACTURER_TOSHIBA, | 1766 | .mfr_id = MANUFACTURER_TOSHIBA, |
1730 | .dev_id = TC58FVT160, | 1767 | .dev_id = TC58FVT160, |
1731 | .name = "Toshiba TC58FVT160", | 1768 | .name = "Toshiba TC58FVT160", |
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 325fab92a62c..c222514bb70d 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig | |||
@@ -104,6 +104,16 @@ config M25PXX_USE_FAST_READ | |||
104 | help | 104 | help |
105 | This option enables FAST_READ access supported by ST M25Pxx. | 105 | This option enables FAST_READ access supported by ST M25Pxx. |
106 | 106 | ||
107 | config MTD_SST25L | ||
108 | tristate "Support SST25L (non JEDEC) SPI Flash chips" | ||
109 | depends on SPI_MASTER | ||
110 | help | ||
111 | This enables access to the non JEDEC SST25L SPI flash chips, used | ||
112 | for program and data storage. | ||
113 | |||
114 | Set up your spi devices with the right board-specific platform data, | ||
115 | if you want to specify device partitioning. | ||
116 | |||
107 | config MTD_SLRAM | 117 | config MTD_SLRAM |
108 | tristate "Uncached system RAM" | 118 | tristate "Uncached system RAM" |
109 | help | 119 | help |
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0993d5cf3923..ab5c9b92ac82 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile | |||
@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o | |||
16 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o | 16 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
17 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o | 17 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
18 | obj-$(CONFIG_MTD_M25P80) += m25p80.o | 18 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
19 | obj-$(CONFIG_MTD_SST25L) += sst25l.o | ||
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 578de1c67bfe..f4359fe7150f 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c | |||
@@ -393,7 +393,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) | |||
393 | * erase range is aligned with the erase size which is in | 393 | * erase range is aligned with the erase size which is in |
394 | * effect here. | 394 | * effect here. |
395 | */ | 395 | */ |
396 | if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); | 396 | if (i < 0 || (instr->addr & (mtd->eraseregions[i].erasesize - 1))) |
397 | return -EINVAL; | ||
397 | 398 | ||
398 | /* Remember the erase region we start on */ | 399 | /* Remember the erase region we start on */ |
399 | first = i; | 400 | first = i; |
@@ -409,7 +410,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) | |||
409 | i--; | 410 | i--; |
410 | 411 | ||
411 | /* is the end aligned on a block boundary? */ | 412 | /* is the end aligned on a block boundary? */ |
412 | if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); | 413 | if (i < 0 || ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1))) |
414 | return -EINVAL; | ||
413 | 415 | ||
414 | addr = instr->addr; | 416 | addr = instr->addr; |
415 | len = instr->len; | 417 | len = instr->len; |
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index eb495d83064f..379c316f329e 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
@@ -44,6 +44,11 @@ | |||
44 | #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ | 44 | #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ |
45 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ | 45 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ |
46 | 46 | ||
47 | /* Used for SST flashes only. */ | ||
48 | #define OPCODE_BP 0x02 /* Byte program */ | ||
49 | #define OPCODE_WRDI 0x04 /* Write disable */ | ||
50 | #define OPCODE_AAI_WP 0xad /* Auto address increment word program */ | ||
51 | |||
47 | /* Status Register bits. */ | 52 | /* Status Register bits. */ |
48 | #define SR_WIP 1 /* Write in progress */ | 53 | #define SR_WIP 1 /* Write in progress */ |
49 | #define SR_WEL 2 /* Write enable latch */ | 54 | #define SR_WEL 2 /* Write enable latch */ |
@@ -132,6 +137,15 @@ static inline int write_enable(struct m25p *flash) | |||
132 | return spi_write_then_read(flash->spi, &code, 1, NULL, 0); | 137 | return spi_write_then_read(flash->spi, &code, 1, NULL, 0); |
133 | } | 138 | } |
134 | 139 | ||
140 | /* | ||
141 | * Send write disble instruction to the chip. | ||
142 | */ | ||
143 | static inline int write_disable(struct m25p *flash) | ||
144 | { | ||
145 | u8 code = OPCODE_WRDI; | ||
146 | |||
147 | return spi_write_then_read(flash->spi, &code, 1, NULL, 0); | ||
148 | } | ||
135 | 149 | ||
136 | /* | 150 | /* |
137 | * Service routine to read status register until ready, or timeout occurs. | 151 | * Service routine to read status register until ready, or timeout occurs. |
@@ -454,6 +468,111 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
454 | return 0; | 468 | return 0; |
455 | } | 469 | } |
456 | 470 | ||
471 | static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
472 | size_t *retlen, const u_char *buf) | ||
473 | { | ||
474 | struct m25p *flash = mtd_to_m25p(mtd); | ||
475 | struct spi_transfer t[2]; | ||
476 | struct spi_message m; | ||
477 | size_t actual; | ||
478 | int cmd_sz, ret; | ||
479 | |||
480 | if (retlen) | ||
481 | *retlen = 0; | ||
482 | |||
483 | /* sanity checks */ | ||
484 | if (!len) | ||
485 | return 0; | ||
486 | |||
487 | if (to + len > flash->mtd.size) | ||
488 | return -EINVAL; | ||
489 | |||
490 | spi_message_init(&m); | ||
491 | memset(t, 0, (sizeof t)); | ||
492 | |||
493 | t[0].tx_buf = flash->command; | ||
494 | t[0].len = CMD_SIZE; | ||
495 | spi_message_add_tail(&t[0], &m); | ||
496 | |||
497 | t[1].tx_buf = buf; | ||
498 | spi_message_add_tail(&t[1], &m); | ||
499 | |||
500 | mutex_lock(&flash->lock); | ||
501 | |||
502 | /* Wait until finished previous write command. */ | ||
503 | ret = wait_till_ready(flash); | ||
504 | if (ret) | ||
505 | goto time_out; | ||
506 | |||
507 | write_enable(flash); | ||
508 | |||
509 | actual = to % 2; | ||
510 | /* Start write from odd address. */ | ||
511 | if (actual) { | ||
512 | flash->command[0] = OPCODE_BP; | ||
513 | flash->command[1] = to >> 16; | ||
514 | flash->command[2] = to >> 8; | ||
515 | flash->command[3] = to; | ||
516 | |||
517 | /* write one byte. */ | ||
518 | t[1].len = 1; | ||
519 | spi_sync(flash->spi, &m); | ||
520 | ret = wait_till_ready(flash); | ||
521 | if (ret) | ||
522 | goto time_out; | ||
523 | *retlen += m.actual_length - CMD_SIZE; | ||
524 | } | ||
525 | to += actual; | ||
526 | |||
527 | flash->command[0] = OPCODE_AAI_WP; | ||
528 | flash->command[1] = to >> 16; | ||
529 | flash->command[2] = to >> 8; | ||
530 | flash->command[3] = to; | ||
531 | |||
532 | /* Write out most of the data here. */ | ||
533 | cmd_sz = CMD_SIZE; | ||
534 | for (; actual < len - 1; actual += 2) { | ||
535 | t[0].len = cmd_sz; | ||
536 | /* write two bytes. */ | ||
537 | t[1].len = 2; | ||
538 | t[1].tx_buf = buf + actual; | ||
539 | |||
540 | spi_sync(flash->spi, &m); | ||
541 | ret = wait_till_ready(flash); | ||
542 | if (ret) | ||
543 | goto time_out; | ||
544 | *retlen += m.actual_length - cmd_sz; | ||
545 | cmd_sz = 1; | ||
546 | to += 2; | ||
547 | } | ||
548 | write_disable(flash); | ||
549 | ret = wait_till_ready(flash); | ||
550 | if (ret) | ||
551 | goto time_out; | ||
552 | |||
553 | /* Write out trailing byte if it exists. */ | ||
554 | if (actual != len) { | ||
555 | write_enable(flash); | ||
556 | flash->command[0] = OPCODE_BP; | ||
557 | flash->command[1] = to >> 16; | ||
558 | flash->command[2] = to >> 8; | ||
559 | flash->command[3] = to; | ||
560 | t[0].len = CMD_SIZE; | ||
561 | t[1].len = 1; | ||
562 | t[1].tx_buf = buf + actual; | ||
563 | |||
564 | spi_sync(flash->spi, &m); | ||
565 | ret = wait_till_ready(flash); | ||
566 | if (ret) | ||
567 | goto time_out; | ||
568 | *retlen += m.actual_length - CMD_SIZE; | ||
569 | write_disable(flash); | ||
570 | } | ||
571 | |||
572 | time_out: | ||
573 | mutex_unlock(&flash->lock); | ||
574 | return ret; | ||
575 | } | ||
457 | 576 | ||
458 | /****************************************************************************/ | 577 | /****************************************************************************/ |
459 | 578 | ||
@@ -501,7 +620,10 @@ static struct flash_info __devinitdata m25p_data [] = { | |||
501 | { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, | 620 | { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, |
502 | 621 | ||
503 | /* Macronix */ | 622 | /* Macronix */ |
623 | { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, }, | ||
624 | { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, }, | ||
504 | { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, | 625 | { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, |
626 | { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, }, | ||
505 | 627 | ||
506 | /* Spansion -- single (large) sector size only, at least | 628 | /* Spansion -- single (large) sector size only, at least |
507 | * for the chips listed here (without boot sectors). | 629 | * for the chips listed here (without boot sectors). |
@@ -511,14 +633,20 @@ static struct flash_info __devinitdata m25p_data [] = { | |||
511 | { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, | 633 | { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, |
512 | { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, | 634 | { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, |
513 | { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, | 635 | { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, |
514 | { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, | 636 | { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, |
515 | { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, | 637 | { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, |
638 | { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, }, | ||
639 | { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, }, | ||
516 | 640 | ||
517 | /* SST -- large erase sizes are "overlays", "sectors" are 4K */ | 641 | /* SST -- large erase sizes are "overlays", "sectors" are 4K */ |
518 | { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, }, | 642 | { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, }, |
519 | { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, }, | 643 | { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, }, |
520 | { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, }, | 644 | { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, }, |
521 | { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, }, | 645 | { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, }, |
646 | { "sst25wf512", 0xbf2501, 0, 64 * 1024, 1, SECT_4K, }, | ||
647 | { "sst25wf010", 0xbf2502, 0, 64 * 1024, 2, SECT_4K, }, | ||
648 | { "sst25wf020", 0xbf2503, 0, 64 * 1024, 4, SECT_4K, }, | ||
649 | { "sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K, }, | ||
522 | 650 | ||
523 | /* ST Microelectronics -- newer production may have feature updates */ | 651 | /* ST Microelectronics -- newer production may have feature updates */ |
524 | { "m25p05", 0x202010, 0, 32 * 1024, 2, }, | 652 | { "m25p05", 0x202010, 0, 32 * 1024, 2, }, |
@@ -667,7 +795,12 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
667 | flash->mtd.size = info->sector_size * info->n_sectors; | 795 | flash->mtd.size = info->sector_size * info->n_sectors; |
668 | flash->mtd.erase = m25p80_erase; | 796 | flash->mtd.erase = m25p80_erase; |
669 | flash->mtd.read = m25p80_read; | 797 | flash->mtd.read = m25p80_read; |
670 | flash->mtd.write = m25p80_write; | 798 | |
799 | /* sst flash chips use AAI word program */ | ||
800 | if (info->jedec_id >> 16 == 0xbf) | ||
801 | flash->mtd.write = sst_write; | ||
802 | else | ||
803 | flash->mtd.write = m25p80_write; | ||
671 | 804 | ||
672 | /* prefer "small sector" erase if possible */ | 805 | /* prefer "small sector" erase if possible */ |
673 | if (info->flags & SECT_4K) { | 806 | if (info->flags & SECT_4K) { |
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 211c27acd01e..93e3627be74c 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c | |||
@@ -401,7 +401,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
401 | (void) dataflash_waitready(priv->spi); | 401 | (void) dataflash_waitready(priv->spi); |
402 | 402 | ||
403 | 403 | ||
404 | #ifdef CONFIG_MTD_DATAFLASH_VERIFY_WRITE | 404 | #ifdef CONFIG_MTD_DATAFLASH_WRITE_VERIFY |
405 | 405 | ||
406 | /* (3) Compare to Buffer1 */ | 406 | /* (3) Compare to Buffer1 */ |
407 | addr = pageaddr << priv->page_offset; | 407 | addr = pageaddr << priv->page_offset; |
@@ -430,7 +430,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
430 | } else | 430 | } else |
431 | status = 0; | 431 | status = 0; |
432 | 432 | ||
433 | #endif /* CONFIG_MTD_DATAFLASH_VERIFY_WRITE */ | 433 | #endif /* CONFIG_MTD_DATAFLASH_WRITE_VERIFY */ |
434 | 434 | ||
435 | remaining = remaining - writelen; | 435 | remaining = remaining - writelen; |
436 | pageaddr++; | 436 | pageaddr++; |
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 088fbb7595b5..1696bbecaa7e 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c | |||
@@ -14,6 +14,9 @@ | |||
14 | * Example: | 14 | * Example: |
15 | * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi | 15 | * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi |
16 | */ | 16 | */ |
17 | |||
18 | #define pr_fmt(fmt) "phram: " fmt | ||
19 | |||
17 | #include <asm/io.h> | 20 | #include <asm/io.h> |
18 | #include <linux/init.h> | 21 | #include <linux/init.h> |
19 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
@@ -23,8 +26,6 @@ | |||
23 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
24 | #include <linux/mtd/mtd.h> | 27 | #include <linux/mtd/mtd.h> |
25 | 28 | ||
26 | #define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) | ||
27 | |||
28 | struct phram_mtd_list { | 29 | struct phram_mtd_list { |
29 | struct mtd_info mtd; | 30 | struct mtd_info mtd; |
30 | struct list_head list; | 31 | struct list_head list; |
@@ -132,7 +133,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) | |||
132 | ret = -EIO; | 133 | ret = -EIO; |
133 | new->mtd.priv = ioremap(start, len); | 134 | new->mtd.priv = ioremap(start, len); |
134 | if (!new->mtd.priv) { | 135 | if (!new->mtd.priv) { |
135 | ERROR("ioremap failed\n"); | 136 | pr_err("ioremap failed\n"); |
136 | goto out1; | 137 | goto out1; |
137 | } | 138 | } |
138 | 139 | ||
@@ -152,7 +153,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) | |||
152 | 153 | ||
153 | ret = -EAGAIN; | 154 | ret = -EAGAIN; |
154 | if (add_mtd_device(&new->mtd)) { | 155 | if (add_mtd_device(&new->mtd)) { |
155 | ERROR("Failed to register new device\n"); | 156 | pr_err("Failed to register new device\n"); |
156 | goto out2; | 157 | goto out2; |
157 | } | 158 | } |
158 | 159 | ||
@@ -227,8 +228,8 @@ static inline void kill_final_newline(char *str) | |||
227 | 228 | ||
228 | 229 | ||
229 | #define parse_err(fmt, args...) do { \ | 230 | #define parse_err(fmt, args...) do { \ |
230 | ERROR(fmt , ## args); \ | 231 | pr_err(fmt , ## args); \ |
231 | return 0; \ | 232 | return 1; \ |
232 | } while (0) | 233 | } while (0) |
233 | 234 | ||
234 | static int phram_setup(const char *val, struct kernel_param *kp) | 235 | static int phram_setup(const char *val, struct kernel_param *kp) |
@@ -256,12 +257,8 @@ static int phram_setup(const char *val, struct kernel_param *kp) | |||
256 | parse_err("not enough arguments\n"); | 257 | parse_err("not enough arguments\n"); |
257 | 258 | ||
258 | ret = parse_name(&name, token[0]); | 259 | ret = parse_name(&name, token[0]); |
259 | if (ret == -ENOMEM) | ||
260 | parse_err("out of memory\n"); | ||
261 | if (ret == -ENOSPC) | ||
262 | parse_err("name too long\n"); | ||
263 | if (ret) | 260 | if (ret) |
264 | return 0; | 261 | return ret; |
265 | 262 | ||
266 | ret = parse_num32(&start, token[1]); | 263 | ret = parse_num32(&start, token[1]); |
267 | if (ret) { | 264 | if (ret) { |
@@ -275,9 +272,11 @@ static int phram_setup(const char *val, struct kernel_param *kp) | |||
275 | parse_err("illegal device length\n"); | 272 | parse_err("illegal device length\n"); |
276 | } | 273 | } |
277 | 274 | ||
278 | register_device(name, start, len); | 275 | ret = register_device(name, start, len); |
276 | if (!ret) | ||
277 | pr_info("%s device: %#x at %#x\n", name, len, start); | ||
279 | 278 | ||
280 | return 0; | 279 | return ret; |
281 | } | 280 | } |
282 | 281 | ||
283 | module_param_call(phram, phram_setup, NULL, NULL, 000); | 282 | module_param_call(phram, phram_setup, NULL, NULL, 000); |
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 7d846e9173da..3aa05cd18ea1 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c | |||
@@ -341,7 +341,7 @@ static int __init init_slram(void) | |||
341 | #else | 341 | #else |
342 | int count; | 342 | int count; |
343 | 343 | ||
344 | for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS); | 344 | for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count]; |
345 | count++) { | 345 | count++) { |
346 | } | 346 | } |
347 | 347 | ||
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c new file mode 100644 index 000000000000..c2baf3353f84 --- /dev/null +++ b/drivers/mtd/devices/sst25l.c | |||
@@ -0,0 +1,512 @@ | |||
1 | /* | ||
2 | * sst25l.c | ||
3 | * | ||
4 | * Driver for SST25L SPI Flash chips | ||
5 | * | ||
6 | * Copyright © 2009 Bluewater Systems Ltd | ||
7 | * Author: Andre Renaud <andre@bluewatersys.com> | ||
8 | * Author: Ryan Mallon <ryan@bluewatersys.com> | ||
9 | * | ||
10 | * Based on m25p80.c | ||
11 | * | ||
12 | * This code is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/init.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/mutex.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | |||
24 | #include <linux/mtd/mtd.h> | ||
25 | #include <linux/mtd/partitions.h> | ||
26 | |||
27 | #include <linux/spi/spi.h> | ||
28 | #include <linux/spi/flash.h> | ||
29 | |||
30 | /* Erases can take up to 3 seconds! */ | ||
31 | #define MAX_READY_WAIT_JIFFIES msecs_to_jiffies(3000) | ||
32 | |||
33 | #define SST25L_CMD_WRSR 0x01 /* Write status register */ | ||
34 | #define SST25L_CMD_WRDI 0x04 /* Write disable */ | ||
35 | #define SST25L_CMD_RDSR 0x05 /* Read status register */ | ||
36 | #define SST25L_CMD_WREN 0x06 /* Write enable */ | ||
37 | #define SST25L_CMD_READ 0x03 /* High speed read */ | ||
38 | |||
39 | #define SST25L_CMD_EWSR 0x50 /* Enable write status register */ | ||
40 | #define SST25L_CMD_SECTOR_ERASE 0x20 /* Erase sector */ | ||
41 | #define SST25L_CMD_READ_ID 0x90 /* Read device ID */ | ||
42 | #define SST25L_CMD_AAI_PROGRAM 0xaf /* Auto address increment */ | ||
43 | |||
44 | #define SST25L_STATUS_BUSY (1 << 0) /* Chip is busy */ | ||
45 | #define SST25L_STATUS_WREN (1 << 1) /* Write enabled */ | ||
46 | #define SST25L_STATUS_BP0 (1 << 2) /* Block protection 0 */ | ||
47 | #define SST25L_STATUS_BP1 (1 << 3) /* Block protection 1 */ | ||
48 | |||
49 | struct sst25l_flash { | ||
50 | struct spi_device *spi; | ||
51 | struct mutex lock; | ||
52 | struct mtd_info mtd; | ||
53 | |||
54 | int partitioned; | ||
55 | }; | ||
56 | |||
57 | struct flash_info { | ||
58 | const char *name; | ||
59 | uint16_t device_id; | ||
60 | unsigned page_size; | ||
61 | unsigned nr_pages; | ||
62 | unsigned erase_size; | ||
63 | }; | ||
64 | |||
65 | #define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd) | ||
66 | |||
67 | static struct flash_info __initdata sst25l_flash_info[] = { | ||
68 | {"sst25lf020a", 0xbf43, 256, 1024, 4096}, | ||
69 | {"sst25lf040a", 0xbf44, 256, 2048, 4096}, | ||
70 | }; | ||
71 | |||
72 | static int sst25l_status(struct sst25l_flash *flash, int *status) | ||
73 | { | ||
74 | unsigned char command, response; | ||
75 | int err; | ||
76 | |||
77 | command = SST25L_CMD_RDSR; | ||
78 | err = spi_write_then_read(flash->spi, &command, 1, &response, 1); | ||
79 | if (err < 0) | ||
80 | return err; | ||
81 | |||
82 | *status = response; | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static int sst25l_write_enable(struct sst25l_flash *flash, int enable) | ||
87 | { | ||
88 | unsigned char command[2]; | ||
89 | int status, err; | ||
90 | |||
91 | command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI; | ||
92 | err = spi_write(flash->spi, command, 1); | ||
93 | if (err) | ||
94 | return err; | ||
95 | |||
96 | command[0] = SST25L_CMD_EWSR; | ||
97 | err = spi_write(flash->spi, command, 1); | ||
98 | if (err) | ||
99 | return err; | ||
100 | |||
101 | command[0] = SST25L_CMD_WRSR; | ||
102 | command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1; | ||
103 | err = spi_write(flash->spi, command, 2); | ||
104 | if (err) | ||
105 | return err; | ||
106 | |||
107 | if (enable) { | ||
108 | err = sst25l_status(flash, &status); | ||
109 | if (err) | ||
110 | return err; | ||
111 | if (!(status & SST25L_STATUS_WREN)) | ||
112 | return -EROFS; | ||
113 | } | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int sst25l_wait_till_ready(struct sst25l_flash *flash) | ||
119 | { | ||
120 | unsigned long deadline; | ||
121 | int status, err; | ||
122 | |||
123 | deadline = jiffies + MAX_READY_WAIT_JIFFIES; | ||
124 | do { | ||
125 | err = sst25l_status(flash, &status); | ||
126 | if (err) | ||
127 | return err; | ||
128 | if (!(status & SST25L_STATUS_BUSY)) | ||
129 | return 0; | ||
130 | |||
131 | cond_resched(); | ||
132 | } while (!time_after_eq(jiffies, deadline)); | ||
133 | |||
134 | return -ETIMEDOUT; | ||
135 | } | ||
136 | |||
137 | static int sst25l_erase_sector(struct sst25l_flash *flash, uint32_t offset) | ||
138 | { | ||
139 | unsigned char command[4]; | ||
140 | int err; | ||
141 | |||
142 | err = sst25l_write_enable(flash, 1); | ||
143 | if (err) | ||
144 | return err; | ||
145 | |||
146 | command[0] = SST25L_CMD_SECTOR_ERASE; | ||
147 | command[1] = offset >> 16; | ||
148 | command[2] = offset >> 8; | ||
149 | command[3] = offset; | ||
150 | err = spi_write(flash->spi, command, 4); | ||
151 | if (err) | ||
152 | return err; | ||
153 | |||
154 | err = sst25l_wait_till_ready(flash); | ||
155 | if (err) | ||
156 | return err; | ||
157 | |||
158 | return sst25l_write_enable(flash, 0); | ||
159 | } | ||
160 | |||
161 | static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
162 | { | ||
163 | struct sst25l_flash *flash = to_sst25l_flash(mtd); | ||
164 | uint32_t addr, end; | ||
165 | int err; | ||
166 | |||
167 | /* Sanity checks */ | ||
168 | if (instr->addr + instr->len > flash->mtd.size) | ||
169 | return -EINVAL; | ||
170 | |||
171 | if ((uint32_t)instr->len % mtd->erasesize) | ||
172 | return -EINVAL; | ||
173 | |||
174 | if ((uint32_t)instr->addr % mtd->erasesize) | ||
175 | return -EINVAL; | ||
176 | |||
177 | addr = instr->addr; | ||
178 | end = addr + instr->len; | ||
179 | |||
180 | mutex_lock(&flash->lock); | ||
181 | |||
182 | err = sst25l_wait_till_ready(flash); | ||
183 | if (err) { | ||
184 | mutex_unlock(&flash->lock); | ||
185 | return err; | ||
186 | } | ||
187 | |||
188 | while (addr < end) { | ||
189 | err = sst25l_erase_sector(flash, addr); | ||
190 | if (err) { | ||
191 | mutex_unlock(&flash->lock); | ||
192 | instr->state = MTD_ERASE_FAILED; | ||
193 | dev_err(&flash->spi->dev, "Erase failed\n"); | ||
194 | return err; | ||
195 | } | ||
196 | |||
197 | addr += mtd->erasesize; | ||
198 | } | ||
199 | |||
200 | mutex_unlock(&flash->lock); | ||
201 | |||
202 | instr->state = MTD_ERASE_DONE; | ||
203 | mtd_erase_callback(instr); | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
208 | size_t *retlen, unsigned char *buf) | ||
209 | { | ||
210 | struct sst25l_flash *flash = to_sst25l_flash(mtd); | ||
211 | struct spi_transfer transfer[2]; | ||
212 | struct spi_message message; | ||
213 | unsigned char command[4]; | ||
214 | int ret; | ||
215 | |||
216 | /* Sanity checking */ | ||
217 | if (len == 0) | ||
218 | return 0; | ||
219 | |||
220 | if (from + len > flash->mtd.size) | ||
221 | return -EINVAL; | ||
222 | |||
223 | if (retlen) | ||
224 | *retlen = 0; | ||
225 | |||
226 | spi_message_init(&message); | ||
227 | memset(&transfer, 0, sizeof(transfer)); | ||
228 | |||
229 | command[0] = SST25L_CMD_READ; | ||
230 | command[1] = from >> 16; | ||
231 | command[2] = from >> 8; | ||
232 | command[3] = from; | ||
233 | |||
234 | transfer[0].tx_buf = command; | ||
235 | transfer[0].len = sizeof(command); | ||
236 | spi_message_add_tail(&transfer[0], &message); | ||
237 | |||
238 | transfer[1].rx_buf = buf; | ||
239 | transfer[1].len = len; | ||
240 | spi_message_add_tail(&transfer[1], &message); | ||
241 | |||
242 | mutex_lock(&flash->lock); | ||
243 | |||
244 | /* Wait for previous write/erase to complete */ | ||
245 | ret = sst25l_wait_till_ready(flash); | ||
246 | if (ret) { | ||
247 | mutex_unlock(&flash->lock); | ||
248 | return ret; | ||
249 | } | ||
250 | |||
251 | spi_sync(flash->spi, &message); | ||
252 | |||
253 | if (retlen && message.actual_length > sizeof(command)) | ||
254 | *retlen += message.actual_length - sizeof(command); | ||
255 | |||
256 | mutex_unlock(&flash->lock); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
261 | size_t *retlen, const unsigned char *buf) | ||
262 | { | ||
263 | struct sst25l_flash *flash = to_sst25l_flash(mtd); | ||
264 | int i, j, ret, bytes, copied = 0; | ||
265 | unsigned char command[5]; | ||
266 | |||
267 | /* Sanity checks */ | ||
268 | if (!len) | ||
269 | return 0; | ||
270 | |||
271 | if (to + len > flash->mtd.size) | ||
272 | return -EINVAL; | ||
273 | |||
274 | if ((uint32_t)to % mtd->writesize) | ||
275 | return -EINVAL; | ||
276 | |||
277 | mutex_lock(&flash->lock); | ||
278 | |||
279 | ret = sst25l_write_enable(flash, 1); | ||
280 | if (ret) | ||
281 | goto out; | ||
282 | |||
283 | for (i = 0; i < len; i += mtd->writesize) { | ||
284 | ret = sst25l_wait_till_ready(flash); | ||
285 | if (ret) | ||
286 | goto out; | ||
287 | |||
288 | /* Write the first byte of the page */ | ||
289 | command[0] = SST25L_CMD_AAI_PROGRAM; | ||
290 | command[1] = (to + i) >> 16; | ||
291 | command[2] = (to + i) >> 8; | ||
292 | command[3] = (to + i); | ||
293 | command[4] = buf[i]; | ||
294 | ret = spi_write(flash->spi, command, 5); | ||
295 | if (ret < 0) | ||
296 | goto out; | ||
297 | copied++; | ||
298 | |||
299 | /* | ||
300 | * Write the remaining bytes using auto address | ||
301 | * increment mode | ||
302 | */ | ||
303 | bytes = min_t(uint32_t, mtd->writesize, len - i); | ||
304 | for (j = 1; j < bytes; j++, copied++) { | ||
305 | ret = sst25l_wait_till_ready(flash); | ||
306 | if (ret) | ||
307 | goto out; | ||
308 | |||
309 | command[1] = buf[i + j]; | ||
310 | ret = spi_write(flash->spi, command, 2); | ||
311 | if (ret) | ||
312 | goto out; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | out: | ||
317 | ret = sst25l_write_enable(flash, 0); | ||
318 | |||
319 | if (retlen) | ||
320 | *retlen = copied; | ||
321 | |||
322 | mutex_unlock(&flash->lock); | ||
323 | return ret; | ||
324 | } | ||
325 | |||
326 | static struct flash_info *__init sst25l_match_device(struct spi_device *spi) | ||
327 | { | ||
328 | struct flash_info *flash_info = NULL; | ||
329 | unsigned char command[4], response; | ||
330 | int i, err; | ||
331 | uint16_t id; | ||
332 | |||
333 | command[0] = SST25L_CMD_READ_ID; | ||
334 | command[1] = 0; | ||
335 | command[2] = 0; | ||
336 | command[3] = 0; | ||
337 | err = spi_write_then_read(spi, command, sizeof(command), &response, 1); | ||
338 | if (err < 0) { | ||
339 | dev_err(&spi->dev, "error reading device id msb\n"); | ||
340 | return NULL; | ||
341 | } | ||
342 | |||
343 | id = response << 8; | ||
344 | |||
345 | command[0] = SST25L_CMD_READ_ID; | ||
346 | command[1] = 0; | ||
347 | command[2] = 0; | ||
348 | command[3] = 1; | ||
349 | err = spi_write_then_read(spi, command, sizeof(command), &response, 1); | ||
350 | if (err < 0) { | ||
351 | dev_err(&spi->dev, "error reading device id lsb\n"); | ||
352 | return NULL; | ||
353 | } | ||
354 | |||
355 | id |= response; | ||
356 | |||
357 | for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++) | ||
358 | if (sst25l_flash_info[i].device_id == id) | ||
359 | flash_info = &sst25l_flash_info[i]; | ||
360 | |||
361 | if (!flash_info) | ||
362 | dev_err(&spi->dev, "unknown id %.4x\n", id); | ||
363 | |||
364 | return flash_info; | ||
365 | } | ||
366 | |||
367 | static int __init sst25l_probe(struct spi_device *spi) | ||
368 | { | ||
369 | struct flash_info *flash_info; | ||
370 | struct sst25l_flash *flash; | ||
371 | struct flash_platform_data *data; | ||
372 | int ret, i; | ||
373 | |||
374 | flash_info = sst25l_match_device(spi); | ||
375 | if (!flash_info) | ||
376 | return -ENODEV; | ||
377 | |||
378 | flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL); | ||
379 | if (!flash) | ||
380 | return -ENOMEM; | ||
381 | |||
382 | flash->spi = spi; | ||
383 | mutex_init(&flash->lock); | ||
384 | dev_set_drvdata(&spi->dev, flash); | ||
385 | |||
386 | data = spi->dev.platform_data; | ||
387 | if (data && data->name) | ||
388 | flash->mtd.name = data->name; | ||
389 | else | ||
390 | flash->mtd.name = dev_name(&spi->dev); | ||
391 | |||
392 | flash->mtd.type = MTD_NORFLASH; | ||
393 | flash->mtd.flags = MTD_CAP_NORFLASH; | ||
394 | flash->mtd.erasesize = flash_info->erase_size; | ||
395 | flash->mtd.writesize = flash_info->page_size; | ||
396 | flash->mtd.size = flash_info->page_size * flash_info->nr_pages; | ||
397 | flash->mtd.erase = sst25l_erase; | ||
398 | flash->mtd.read = sst25l_read; | ||
399 | flash->mtd.write = sst25l_write; | ||
400 | |||
401 | dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name, | ||
402 | (long long)flash->mtd.size >> 10); | ||
403 | |||
404 | DEBUG(MTD_DEBUG_LEVEL2, | ||
405 | "mtd .name = %s, .size = 0x%llx (%lldMiB) " | ||
406 | ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", | ||
407 | flash->mtd.name, | ||
408 | (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), | ||
409 | flash->mtd.erasesize, flash->mtd.erasesize / 1024, | ||
410 | flash->mtd.numeraseregions); | ||
411 | |||
412 | if (flash->mtd.numeraseregions) | ||
413 | for (i = 0; i < flash->mtd.numeraseregions; i++) | ||
414 | DEBUG(MTD_DEBUG_LEVEL2, | ||
415 | "mtd.eraseregions[%d] = { .offset = 0x%llx, " | ||
416 | ".erasesize = 0x%.8x (%uKiB), " | ||
417 | ".numblocks = %d }\n", | ||
418 | i, (long long)flash->mtd.eraseregions[i].offset, | ||
419 | flash->mtd.eraseregions[i].erasesize, | ||
420 | flash->mtd.eraseregions[i].erasesize / 1024, | ||
421 | flash->mtd.eraseregions[i].numblocks); | ||
422 | |||
423 | if (mtd_has_partitions()) { | ||
424 | struct mtd_partition *parts = NULL; | ||
425 | int nr_parts = 0; | ||
426 | |||
427 | if (mtd_has_cmdlinepart()) { | ||
428 | static const char *part_probes[] = | ||
429 | {"cmdlinepart", NULL}; | ||
430 | |||
431 | nr_parts = parse_mtd_partitions(&flash->mtd, | ||
432 | part_probes, | ||
433 | &parts, 0); | ||
434 | } | ||
435 | |||
436 | if (nr_parts <= 0 && data && data->parts) { | ||
437 | parts = data->parts; | ||
438 | nr_parts = data->nr_parts; | ||
439 | } | ||
440 | |||
441 | if (nr_parts > 0) { | ||
442 | for (i = 0; i < nr_parts; i++) { | ||
443 | DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " | ||
444 | "{.name = %s, .offset = 0x%llx, " | ||
445 | ".size = 0x%llx (%lldKiB) }\n", | ||
446 | i, parts[i].name, | ||
447 | (long long)parts[i].offset, | ||
448 | (long long)parts[i].size, | ||
449 | (long long)(parts[i].size >> 10)); | ||
450 | } | ||
451 | |||
452 | flash->partitioned = 1; | ||
453 | return add_mtd_partitions(&flash->mtd, | ||
454 | parts, nr_parts); | ||
455 | } | ||
456 | |||
457 | } else if (data->nr_parts) { | ||
458 | dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", | ||
459 | data->nr_parts, data->name); | ||
460 | } | ||
461 | |||
462 | ret = add_mtd_device(&flash->mtd); | ||
463 | if (ret == 1) { | ||
464 | kfree(flash); | ||
465 | dev_set_drvdata(&spi->dev, NULL); | ||
466 | return -ENODEV; | ||
467 | } | ||
468 | |||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | static int __exit sst25l_remove(struct spi_device *spi) | ||
473 | { | ||
474 | struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); | ||
475 | int ret; | ||
476 | |||
477 | if (mtd_has_partitions() && flash->partitioned) | ||
478 | ret = del_mtd_partitions(&flash->mtd); | ||
479 | else | ||
480 | ret = del_mtd_device(&flash->mtd); | ||
481 | if (ret == 0) | ||
482 | kfree(flash); | ||
483 | return ret; | ||
484 | } | ||
485 | |||
486 | static struct spi_driver sst25l_driver = { | ||
487 | .driver = { | ||
488 | .name = "sst25l", | ||
489 | .bus = &spi_bus_type, | ||
490 | .owner = THIS_MODULE, | ||
491 | }, | ||
492 | .probe = sst25l_probe, | ||
493 | .remove = __exit_p(sst25l_remove), | ||
494 | }; | ||
495 | |||
496 | static int __init sst25l_init(void) | ||
497 | { | ||
498 | return spi_register_driver(&sst25l_driver); | ||
499 | } | ||
500 | |||
501 | static void __exit sst25l_exit(void) | ||
502 | { | ||
503 | spi_unregister_driver(&sst25l_driver); | ||
504 | } | ||
505 | |||
506 | module_init(sst25l_init); | ||
507 | module_exit(sst25l_exit); | ||
508 | |||
509 | MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips"); | ||
510 | MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, " | ||
511 | "Ryan Mallon <ryan@bluewatersys.com>"); | ||
512 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index d8cf29c01cc4..8aca5523a337 100644..100755 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c | |||
@@ -550,7 +550,7 @@ hitused: | |||
550 | * waiting to be picked up. We're going to have to fold | 550 | * waiting to be picked up. We're going to have to fold |
551 | * a chain to make room. | 551 | * a chain to make room. |
552 | */ | 552 | */ |
553 | thisEUN = INFTL_makefreeblock(inftl, BLOCK_NIL); | 553 | thisEUN = INFTL_makefreeblock(inftl, block); |
554 | 554 | ||
555 | /* | 555 | /* |
556 | * Hopefully we free something, lets try again. | 556 | * Hopefully we free something, lets try again. |
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 7a58bd5522fd..3a9a960644b6 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig | |||
@@ -484,9 +484,19 @@ config MTD_BFIN_ASYNC | |||
484 | 484 | ||
485 | If compiled as a module, it will be called bfin-async-flash. | 485 | If compiled as a module, it will be called bfin-async-flash. |
486 | 486 | ||
487 | config MTD_GPIO_ADDR | ||
488 | tristate "GPIO-assisted Flash Chip Support" | ||
489 | depends on MTD_COMPLEX_MAPPINGS | ||
490 | select MTD_PARTITIONS | ||
491 | help | ||
492 | Map driver which allows flashes to be partially physically addressed | ||
493 | and assisted by GPIOs. | ||
494 | |||
495 | If compiled as a module, it will be called gpio-addr-flash. | ||
496 | |||
487 | config MTD_UCLINUX | 497 | config MTD_UCLINUX |
488 | bool "Generic uClinux RAM/ROM filesystem support" | 498 | bool "Generic uClinux RAM/ROM filesystem support" |
489 | depends on MTD_PARTITIONS && MTD_RAM && !MMU | 499 | depends on MTD_PARTITIONS && MTD_RAM=y && !MMU |
490 | help | 500 | help |
491 | Map driver to support image based filesystems for uClinux. | 501 | Map driver to support image based filesystems for uClinux. |
492 | 502 | ||
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 5beb0662d724..1d5cf8636723 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile | |||
@@ -58,5 +58,4 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o | |||
58 | obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o | 58 | obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o |
59 | obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o | 59 | obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o |
60 | obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o | 60 | obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o |
61 | obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o | 61 | obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o |
62 | obj-$(CONFIG_MTD_VMU) += vmu-flash.o | ||
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c new file mode 100644 index 000000000000..44ef9a49a860 --- /dev/null +++ b/drivers/mtd/maps/gpio-addr-flash.c | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * drivers/mtd/maps/gpio-addr-flash.c | ||
3 | * | ||
4 | * Handle the case where a flash device is mostly addressed using physical | ||
5 | * line and supplemented by GPIOs. This way you can hook up say a 8MiB flash | ||
6 | * to a 2MiB memory range and use the GPIOs to select a particular range. | ||
7 | * | ||
8 | * Copyright © 2000 Nicolas Pitre <nico@cam.org> | ||
9 | * Copyright © 2005-2009 Analog Devices Inc. | ||
10 | * | ||
11 | * Enter bugs at http://blackfin.uclinux.org/ | ||
12 | * | ||
13 | * Licensed under the GPL-2 or later. | ||
14 | */ | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/mtd/mtd.h> | ||
20 | #include <linux/mtd/map.h> | ||
21 | #include <linux/mtd/partitions.h> | ||
22 | #include <linux/mtd/physmap.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/types.h> | ||
25 | |||
26 | #include <asm/gpio.h> | ||
27 | #include <asm/io.h> | ||
28 | |||
29 | #define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) | ||
30 | |||
31 | #define DRIVER_NAME "gpio-addr-flash" | ||
32 | #define PFX DRIVER_NAME ": " | ||
33 | |||
34 | /** | ||
35 | * struct async_state - keep GPIO flash state | ||
36 | * @mtd: MTD state for this mapping | ||
37 | * @map: MTD map state for this flash | ||
38 | * @gpio_count: number of GPIOs used to address | ||
39 | * @gpio_addrs: array of GPIOs to twiddle | ||
40 | * @gpio_values: cached GPIO values | ||
41 | * @win_size: dedicated memory size (if no GPIOs) | ||
42 | */ | ||
43 | struct async_state { | ||
44 | struct mtd_info *mtd; | ||
45 | struct map_info map; | ||
46 | size_t gpio_count; | ||
47 | unsigned *gpio_addrs; | ||
48 | int *gpio_values; | ||
49 | unsigned long win_size; | ||
50 | }; | ||
51 | #define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1) | ||
52 | |||
53 | /** | ||
54 | * gf_set_gpios() - set GPIO address lines to access specified flash offset | ||
55 | * @state: GPIO flash state | ||
56 | * @ofs: desired offset to access | ||
57 | * | ||
58 | * Rather than call the GPIO framework every time, cache the last-programmed | ||
59 | * value. This speeds up sequential accesses (which are by far the most common | ||
60 | * type). We rely on the GPIO framework to treat non-zero value as high so | ||
61 | * that we don't have to normalize the bits. | ||
62 | */ | ||
63 | static void gf_set_gpios(struct async_state *state, unsigned long ofs) | ||
64 | { | ||
65 | size_t i = 0; | ||
66 | int value; | ||
67 | ofs /= state->win_size; | ||
68 | do { | ||
69 | value = ofs & (1 << i); | ||
70 | if (state->gpio_values[i] != value) { | ||
71 | gpio_set_value(state->gpio_addrs[i], value); | ||
72 | state->gpio_values[i] = value; | ||
73 | } | ||
74 | } while (++i < state->gpio_count); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * gf_read() - read a word at the specified offset | ||
79 | * @map: MTD map state | ||
80 | * @ofs: desired offset to read | ||
81 | */ | ||
82 | static map_word gf_read(struct map_info *map, unsigned long ofs) | ||
83 | { | ||
84 | struct async_state *state = gf_map_info_to_state(map); | ||
85 | uint16_t word; | ||
86 | map_word test; | ||
87 | |||
88 | gf_set_gpios(state, ofs); | ||
89 | |||
90 | word = readw(map->virt + (ofs % state->win_size)); | ||
91 | test.x[0] = word; | ||
92 | return test; | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * gf_copy_from() - copy a chunk of data from the flash | ||
97 | * @map: MTD map state | ||
98 | * @to: memory to copy to | ||
99 | * @from: flash offset to copy from | ||
100 | * @len: how much to copy | ||
101 | * | ||
102 | * We rely on the MTD layer to chunk up copies such that a single request here | ||
103 | * will not cross a window size. This allows us to only wiggle the GPIOs once | ||
104 | * before falling back to a normal memcpy. Reading the higher layer code shows | ||
105 | * that this is indeed the case, but add a BUG_ON() to future proof. | ||
106 | */ | ||
107 | static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) | ||
108 | { | ||
109 | struct async_state *state = gf_map_info_to_state(map); | ||
110 | |||
111 | gf_set_gpios(state, from); | ||
112 | |||
113 | /* BUG if operation crosses the win_size */ | ||
114 | BUG_ON(!((from + len) % state->win_size <= (from + len))); | ||
115 | |||
116 | /* operation does not cross the win_size, so one shot it */ | ||
117 | memcpy_fromio(to, map->virt + (from % state->win_size), len); | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * gf_write() - write a word at the specified offset | ||
122 | * @map: MTD map state | ||
123 | * @ofs: desired offset to write | ||
124 | */ | ||
125 | static void gf_write(struct map_info *map, map_word d1, unsigned long ofs) | ||
126 | { | ||
127 | struct async_state *state = gf_map_info_to_state(map); | ||
128 | uint16_t d; | ||
129 | |||
130 | gf_set_gpios(state, ofs); | ||
131 | |||
132 | d = d1.x[0]; | ||
133 | writew(d, map->virt + (ofs % state->win_size)); | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * gf_copy_to() - copy a chunk of data to the flash | ||
138 | * @map: MTD map state | ||
139 | * @to: flash offset to copy to | ||
140 | * @from: memory to copy from | ||
141 | * @len: how much to copy | ||
142 | * | ||
143 | * See gf_copy_from() caveat. | ||
144 | */ | ||
145 | static void gf_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) | ||
146 | { | ||
147 | struct async_state *state = gf_map_info_to_state(map); | ||
148 | |||
149 | gf_set_gpios(state, to); | ||
150 | |||
151 | /* BUG if operation crosses the win_size */ | ||
152 | BUG_ON(!((to + len) % state->win_size <= (to + len))); | ||
153 | |||
154 | /* operation does not cross the win_size, so one shot it */ | ||
155 | memcpy_toio(map->virt + (to % state->win_size), from, len); | ||
156 | } | ||
157 | |||
158 | #ifdef CONFIG_MTD_PARTITIONS | ||
159 | static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; | ||
160 | #endif | ||
161 | |||
162 | /** | ||
163 | * gpio_flash_probe() - setup a mapping for a GPIO assisted flash | ||
164 | * @pdev: platform device | ||
165 | * | ||
166 | * The platform resource layout expected looks something like: | ||
167 | * struct mtd_partition partitions[] = { ... }; | ||
168 | * struct physmap_flash_data flash_data = { ... }; | ||
169 | * unsigned flash_gpios[] = { GPIO_XX, GPIO_XX, ... }; | ||
170 | * struct resource flash_resource[] = { | ||
171 | * { | ||
172 | * .name = "cfi_probe", | ||
173 | * .start = 0x20000000, | ||
174 | * .end = 0x201fffff, | ||
175 | * .flags = IORESOURCE_MEM, | ||
176 | * }, { | ||
177 | * .start = (unsigned long)flash_gpios, | ||
178 | * .end = ARRAY_SIZE(flash_gpios), | ||
179 | * .flags = IORESOURCE_IRQ, | ||
180 | * } | ||
181 | * }; | ||
182 | * struct platform_device flash_device = { | ||
183 | * .name = "gpio-addr-flash", | ||
184 | * .dev = { .platform_data = &flash_data, }, | ||
185 | * .num_resources = ARRAY_SIZE(flash_resource), | ||
186 | * .resource = flash_resource, | ||
187 | * ... | ||
188 | * }; | ||
189 | */ | ||
190 | static int __devinit gpio_flash_probe(struct platform_device *pdev) | ||
191 | { | ||
192 | int ret; | ||
193 | size_t i, arr_size; | ||
194 | struct physmap_flash_data *pdata; | ||
195 | struct resource *memory; | ||
196 | struct resource *gpios; | ||
197 | struct async_state *state; | ||
198 | |||
199 | pdata = pdev->dev.platform_data; | ||
200 | memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
201 | gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
202 | |||
203 | if (!memory || !gpios || !gpios->end) | ||
204 | return -EINVAL; | ||
205 | |||
206 | arr_size = sizeof(int) * gpios->end; | ||
207 | state = kzalloc(sizeof(*state) + arr_size, GFP_KERNEL); | ||
208 | if (!state) | ||
209 | return -ENOMEM; | ||
210 | |||
211 | state->gpio_count = gpios->end; | ||
212 | state->gpio_addrs = (void *)gpios->start; | ||
213 | state->gpio_values = (void *)(state + 1); | ||
214 | state->win_size = memory->end - memory->start + 1; | ||
215 | memset(state->gpio_values, 0xff, arr_size); | ||
216 | |||
217 | state->map.name = DRIVER_NAME; | ||
218 | state->map.read = gf_read; | ||
219 | state->map.copy_from = gf_copy_from; | ||
220 | state->map.write = gf_write; | ||
221 | state->map.copy_to = gf_copy_to; | ||
222 | state->map.bankwidth = pdata->width; | ||
223 | state->map.size = state->win_size * (1 << state->gpio_count); | ||
224 | state->map.virt = (void __iomem *)memory->start; | ||
225 | state->map.phys = NO_XIP; | ||
226 | state->map.map_priv_1 = (unsigned long)state; | ||
227 | |||
228 | platform_set_drvdata(pdev, state); | ||
229 | |||
230 | i = 0; | ||
231 | do { | ||
232 | if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) { | ||
233 | pr_devinit(KERN_ERR PFX "failed to request gpio %d\n", | ||
234 | state->gpio_addrs[i]); | ||
235 | while (i--) | ||
236 | gpio_free(state->gpio_addrs[i]); | ||
237 | kfree(state); | ||
238 | return -EBUSY; | ||
239 | } | ||
240 | gpio_direction_output(state->gpio_addrs[i], 0); | ||
241 | } while (++i < state->gpio_count); | ||
242 | |||
243 | pr_devinit(KERN_NOTICE PFX "probing %d-bit flash bus\n", | ||
244 | state->map.bankwidth * 8); | ||
245 | state->mtd = do_map_probe(memory->name, &state->map); | ||
246 | if (!state->mtd) { | ||
247 | for (i = 0; i < state->gpio_count; ++i) | ||
248 | gpio_free(state->gpio_addrs[i]); | ||
249 | kfree(state); | ||
250 | return -ENXIO; | ||
251 | } | ||
252 | |||
253 | #ifdef CONFIG_MTD_PARTITIONS | ||
254 | ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); | ||
255 | if (ret > 0) { | ||
256 | pr_devinit(KERN_NOTICE PFX "Using commandline partition definition\n"); | ||
257 | add_mtd_partitions(state->mtd, pdata->parts, ret); | ||
258 | kfree(pdata->parts); | ||
259 | |||
260 | } else if (pdata->nr_parts) { | ||
261 | pr_devinit(KERN_NOTICE PFX "Using board partition definition\n"); | ||
262 | add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); | ||
263 | |||
264 | } else | ||
265 | #endif | ||
266 | { | ||
267 | pr_devinit(KERN_NOTICE PFX "no partition info available, registering whole flash at once\n"); | ||
268 | add_mtd_device(state->mtd); | ||
269 | } | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static int __devexit gpio_flash_remove(struct platform_device *pdev) | ||
275 | { | ||
276 | struct async_state *state = platform_get_drvdata(pdev); | ||
277 | size_t i = 0; | ||
278 | do { | ||
279 | gpio_free(state->gpio_addrs[i]); | ||
280 | } while (++i < state->gpio_count); | ||
281 | #ifdef CONFIG_MTD_PARTITIONS | ||
282 | del_mtd_partitions(state->mtd); | ||
283 | #endif | ||
284 | map_destroy(state->mtd); | ||
285 | kfree(state); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static struct platform_driver gpio_flash_driver = { | ||
290 | .probe = gpio_flash_probe, | ||
291 | .remove = __devexit_p(gpio_flash_remove), | ||
292 | .driver = { | ||
293 | .name = DRIVER_NAME, | ||
294 | }, | ||
295 | }; | ||
296 | |||
297 | static int __init gpio_flash_init(void) | ||
298 | { | ||
299 | return platform_driver_register(&gpio_flash_driver); | ||
300 | } | ||
301 | module_init(gpio_flash_init); | ||
302 | |||
303 | static void __exit gpio_flash_exit(void) | ||
304 | { | ||
305 | platform_driver_unregister(&gpio_flash_driver); | ||
306 | } | ||
307 | module_exit(gpio_flash_exit); | ||
308 | |||
309 | MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); | ||
310 | MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios"); | ||
311 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 39d357b2eb47..61e4eb48bb2d 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
@@ -190,6 +190,7 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
190 | const u32 *p; | 190 | const u32 *p; |
191 | int reg_tuple_size; | 191 | int reg_tuple_size; |
192 | struct mtd_info **mtd_list = NULL; | 192 | struct mtd_info **mtd_list = NULL; |
193 | resource_size_t res_size; | ||
193 | 194 | ||
194 | reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); | 195 | reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); |
195 | 196 | ||
@@ -204,7 +205,7 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
204 | dev_err(&dev->dev, "Malformed reg property on %s\n", | 205 | dev_err(&dev->dev, "Malformed reg property on %s\n", |
205 | dev->node->full_name); | 206 | dev->node->full_name); |
206 | err = -EINVAL; | 207 | err = -EINVAL; |
207 | goto err_out; | 208 | goto err_flash_remove; |
208 | } | 209 | } |
209 | count /= reg_tuple_size; | 210 | count /= reg_tuple_size; |
210 | 211 | ||
@@ -212,14 +213,14 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
212 | info = kzalloc(sizeof(struct of_flash) + | 213 | info = kzalloc(sizeof(struct of_flash) + |
213 | sizeof(struct of_flash_list) * count, GFP_KERNEL); | 214 | sizeof(struct of_flash_list) * count, GFP_KERNEL); |
214 | if (!info) | 215 | if (!info) |
215 | goto err_out; | 216 | goto err_flash_remove; |
216 | |||
217 | mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); | ||
218 | if (!info) | ||
219 | goto err_out; | ||
220 | 217 | ||
221 | dev_set_drvdata(&dev->dev, info); | 218 | dev_set_drvdata(&dev->dev, info); |
222 | 219 | ||
220 | mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); | ||
221 | if (!mtd_list) | ||
222 | goto err_flash_remove; | ||
223 | |||
223 | for (i = 0; i < count; i++) { | 224 | for (i = 0; i < count; i++) { |
224 | err = -ENXIO; | 225 | err = -ENXIO; |
225 | if (of_address_to_resource(dp, i, &res)) { | 226 | if (of_address_to_resource(dp, i, &res)) { |
@@ -233,8 +234,8 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
233 | (unsigned long long)res.end); | 234 | (unsigned long long)res.end); |
234 | 235 | ||
235 | err = -EBUSY; | 236 | err = -EBUSY; |
236 | info->list[i].res = request_mem_region(res.start, res.end - | 237 | res_size = resource_size(&res); |
237 | res.start + 1, | 238 | info->list[i].res = request_mem_region(res.start, res_size, |
238 | dev_name(&dev->dev)); | 239 | dev_name(&dev->dev)); |
239 | if (!info->list[i].res) | 240 | if (!info->list[i].res) |
240 | goto err_out; | 241 | goto err_out; |
@@ -249,7 +250,7 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
249 | 250 | ||
250 | info->list[i].map.name = dev_name(&dev->dev); | 251 | info->list[i].map.name = dev_name(&dev->dev); |
251 | info->list[i].map.phys = res.start; | 252 | info->list[i].map.phys = res.start; |
252 | info->list[i].map.size = res.end - res.start + 1; | 253 | info->list[i].map.size = res_size; |
253 | info->list[i].map.bankwidth = *width; | 254 | info->list[i].map.bankwidth = *width; |
254 | 255 | ||
255 | err = -ENOMEM; | 256 | err = -ENOMEM; |
@@ -338,6 +339,7 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
338 | 339 | ||
339 | err_out: | 340 | err_out: |
340 | kfree(mtd_list); | 341 | kfree(mtd_list); |
342 | err_flash_remove: | ||
341 | of_flash_remove(dev); | 343 | of_flash_remove(dev); |
342 | 344 | ||
343 | return err; | 345 | return err; |
@@ -360,6 +362,10 @@ static struct of_device_id of_flash_match[] = { | |||
360 | .data = (void *)"jedec_probe", | 362 | .data = (void *)"jedec_probe", |
361 | }, | 363 | }, |
362 | { | 364 | { |
365 | .compatible = "mtd-ram", | ||
366 | .data = (void *)"map_ram", | ||
367 | }, | ||
368 | { | ||
363 | .type = "rom", | 369 | .type = "rom", |
364 | .compatible = "direct-mapped" | 370 | .compatible = "direct-mapped" |
365 | }, | 371 | }, |
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 49c9ece76477..dafb91944e70 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c | |||
@@ -175,7 +175,7 @@ static int platram_probe(struct platform_device *pdev) | |||
175 | /* setup map parameters */ | 175 | /* setup map parameters */ |
176 | 176 | ||
177 | info->map.phys = res->start; | 177 | info->map.phys = res->start; |
178 | info->map.size = (res->end - res->start) + 1; | 178 | info->map.size = resource_size(res); |
179 | info->map.name = pdata->mapname != NULL ? | 179 | info->map.name = pdata->mapname != NULL ? |
180 | (char *)pdata->mapname : (char *)pdev->name; | 180 | (char *)pdata->mapname : (char *)pdev->name; |
181 | info->map.bankwidth = pdata->bankwidth; | 181 | info->map.bankwidth = pdata->bankwidth; |
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 4768bd5459d6..c8fd8da4bc87 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c | |||
@@ -50,7 +50,7 @@ static int fcnt; | |||
50 | 50 | ||
51 | static int __init init_msp_flash(void) | 51 | static int __init init_msp_flash(void) |
52 | { | 52 | { |
53 | int i, j; | 53 | int i, j, ret = -ENOMEM; |
54 | int offset, coff; | 54 | int offset, coff; |
55 | char *env; | 55 | char *env; |
56 | int pcnt; | 56 | int pcnt; |
@@ -75,14 +75,16 @@ static int __init init_msp_flash(void) | |||
75 | printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); | 75 | printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); |
76 | 76 | ||
77 | msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); | 77 | msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); |
78 | if (!msp_flash) | ||
79 | return -ENOMEM; | ||
80 | |||
78 | msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); | 81 | msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); |
82 | if (!msp_parts) | ||
83 | goto free_msp_flash; | ||
84 | |||
79 | msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL); | 85 | msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL); |
80 | if (!msp_flash || !msp_parts || !msp_maps) { | 86 | if (!msp_maps) |
81 | kfree(msp_maps); | 87 | goto free_msp_parts; |
82 | kfree(msp_parts); | ||
83 | kfree(msp_flash); | ||
84 | return -ENOMEM; | ||
85 | } | ||
86 | 88 | ||
87 | /* loop over the flash devices, initializing each */ | 89 | /* loop over the flash devices, initializing each */ |
88 | for (i = 0; i < fcnt; i++) { | 90 | for (i = 0; i < fcnt; i++) { |
@@ -100,13 +102,18 @@ static int __init init_msp_flash(void) | |||
100 | 102 | ||
101 | msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition), | 103 | msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition), |
102 | GFP_KERNEL); | 104 | GFP_KERNEL); |
105 | if (!msp_parts[i]) | ||
106 | goto cleanup_loop; | ||
103 | 107 | ||
104 | /* now initialize the devices proper */ | 108 | /* now initialize the devices proper */ |
105 | flash_name[5] = '0' + i; | 109 | flash_name[5] = '0' + i; |
106 | env = prom_getenv(flash_name); | 110 | env = prom_getenv(flash_name); |
107 | 111 | ||
108 | if (sscanf(env, "%x:%x", &addr, &size) < 2) | 112 | if (sscanf(env, "%x:%x", &addr, &size) < 2) { |
109 | return -ENXIO; | 113 | ret = -ENXIO; |
114 | kfree(msp_parts[i]); | ||
115 | goto cleanup_loop; | ||
116 | } | ||
110 | addr = CPHYSADDR(addr); | 117 | addr = CPHYSADDR(addr); |
111 | 118 | ||
112 | printk(KERN_NOTICE | 119 | printk(KERN_NOTICE |
@@ -122,13 +129,23 @@ static int __init init_msp_flash(void) | |||
122 | */ | 129 | */ |
123 | if (size > CONFIG_MSP_FLASH_MAP_LIMIT) | 130 | if (size > CONFIG_MSP_FLASH_MAP_LIMIT) |
124 | size = CONFIG_MSP_FLASH_MAP_LIMIT; | 131 | size = CONFIG_MSP_FLASH_MAP_LIMIT; |
132 | |||
125 | msp_maps[i].virt = ioremap(addr, size); | 133 | msp_maps[i].virt = ioremap(addr, size); |
134 | if (msp_maps[i].virt == NULL) { | ||
135 | ret = -ENXIO; | ||
136 | kfree(msp_parts[i]); | ||
137 | goto cleanup_loop; | ||
138 | } | ||
139 | |||
126 | msp_maps[i].bankwidth = 1; | 140 | msp_maps[i].bankwidth = 1; |
127 | msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL), | 141 | msp_maps[i].name = kmalloc(7, GFP_KERNEL); |
128 | flash_name, 7); | 142 | if (!msp_maps[i].name) { |
143 | iounmap(msp_maps[i].virt); | ||
144 | kfree(msp_parts[i]); | ||
145 | goto cleanup_loop; | ||
146 | } | ||
129 | 147 | ||
130 | if (msp_maps[i].virt == NULL) | 148 | msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7); |
131 | return -ENXIO; | ||
132 | 149 | ||
133 | for (j = 0; j < pcnt; j++) { | 150 | for (j = 0; j < pcnt; j++) { |
134 | part_name[5] = '0' + i; | 151 | part_name[5] = '0' + i; |
@@ -136,8 +153,14 @@ static int __init init_msp_flash(void) | |||
136 | 153 | ||
137 | env = prom_getenv(part_name); | 154 | env = prom_getenv(part_name); |
138 | 155 | ||
139 | if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2) | 156 | if (sscanf(env, "%x:%x:%n", &offset, &size, |
140 | return -ENXIO; | 157 | &coff) < 2) { |
158 | ret = -ENXIO; | ||
159 | kfree(msp_maps[i].name); | ||
160 | iounmap(msp_maps[i].virt); | ||
161 | kfree(msp_parts[i]); | ||
162 | goto cleanup_loop; | ||
163 | } | ||
141 | 164 | ||
142 | msp_parts[i][j].size = size; | 165 | msp_parts[i][j].size = size; |
143 | msp_parts[i][j].offset = offset; | 166 | msp_parts[i][j].offset = offset; |
@@ -152,18 +175,37 @@ static int __init init_msp_flash(void) | |||
152 | add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt); | 175 | add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt); |
153 | } else { | 176 | } else { |
154 | printk(KERN_ERR "map probe failed for flash\n"); | 177 | printk(KERN_ERR "map probe failed for flash\n"); |
155 | return -ENXIO; | 178 | ret = -ENXIO; |
179 | kfree(msp_maps[i].name); | ||
180 | iounmap(msp_maps[i].virt); | ||
181 | kfree(msp_parts[i]); | ||
182 | goto cleanup_loop; | ||
156 | } | 183 | } |
157 | } | 184 | } |
158 | 185 | ||
159 | return 0; | 186 | return 0; |
187 | |||
188 | cleanup_loop: | ||
189 | while (i--) { | ||
190 | del_mtd_partitions(msp_flash[i]); | ||
191 | map_destroy(msp_flash[i]); | ||
192 | kfree(msp_maps[i].name); | ||
193 | iounmap(msp_maps[i].virt); | ||
194 | kfree(msp_parts[i]); | ||
195 | } | ||
196 | kfree(msp_maps); | ||
197 | free_msp_parts: | ||
198 | kfree(msp_parts); | ||
199 | free_msp_flash: | ||
200 | kfree(msp_flash); | ||
201 | return ret; | ||
160 | } | 202 | } |
161 | 203 | ||
162 | static void __exit cleanup_msp_flash(void) | 204 | static void __exit cleanup_msp_flash(void) |
163 | { | 205 | { |
164 | int i; | 206 | int i; |
165 | 207 | ||
166 | for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) { | 208 | for (i = 0; i < fcnt; i++) { |
167 | del_mtd_partitions(msp_flash[i]); | 209 | del_mtd_partitions(msp_flash[i]); |
168 | map_destroy(msp_flash[i]); | 210 | map_destroy(msp_flash[i]); |
169 | iounmap((void *)msp_maps[i].virt); | 211 | iounmap((void *)msp_maps[i].virt); |
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index d4314fb88212..35009294b435 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c | |||
@@ -89,7 +89,11 @@ static int __init uclinux_mtd_init(void) | |||
89 | mtd->priv = mapp; | 89 | mtd->priv = mapp; |
90 | 90 | ||
91 | uclinux_ram_mtdinfo = mtd; | 91 | uclinux_ram_mtdinfo = mtd; |
92 | #ifdef CONFIG_MTD_PARTITIONS | ||
92 | add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS); | 93 | add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS); |
94 | #else | ||
95 | add_mtd_device(mtd); | ||
96 | #endif | ||
93 | 97 | ||
94 | return(0); | 98 | return(0); |
95 | } | 99 | } |
@@ -99,7 +103,11 @@ static int __init uclinux_mtd_init(void) | |||
99 | static void __exit uclinux_mtd_cleanup(void) | 103 | static void __exit uclinux_mtd_cleanup(void) |
100 | { | 104 | { |
101 | if (uclinux_ram_mtdinfo) { | 105 | if (uclinux_ram_mtdinfo) { |
106 | #ifdef CONFIG_MTD_PARTITIONS | ||
102 | del_mtd_partitions(uclinux_ram_mtdinfo); | 107 | del_mtd_partitions(uclinux_ram_mtdinfo); |
108 | #else | ||
109 | del_mtd_device(uclinux_ram_mtdinfo); | ||
110 | #endif | ||
103 | map_destroy(uclinux_ram_mtdinfo); | 111 | map_destroy(uclinux_ram_mtdinfo); |
104 | uclinux_ram_mtdinfo = NULL; | 112 | uclinux_ram_mtdinfo = NULL; |
105 | } | 113 | } |
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 2d70295a5fa3..9f41b1a853c1 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c | |||
@@ -84,7 +84,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, | |||
84 | remove_wait_queue(&wait_q, &wait); | 84 | remove_wait_queue(&wait_q, &wait); |
85 | 85 | ||
86 | /* | 86 | /* |
87 | * Next, writhe data to flash. | 87 | * Next, write the data to flash. |
88 | */ | 88 | */ |
89 | 89 | ||
90 | ret = mtd->write(mtd, pos, len, &retlen, buf); | 90 | ret = mtd->write(mtd, pos, len, &retlen, buf); |
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 792b547786b8..db6de74082ad 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c | |||
@@ -427,7 +427,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
427 | * to-be-erased area begins. Verify that the starting | 427 | * to-be-erased area begins. Verify that the starting |
428 | * offset is aligned to this region's erase size: | 428 | * offset is aligned to this region's erase size: |
429 | */ | 429 | */ |
430 | if (instr->addr & (erase_regions[i].erasesize - 1)) | 430 | if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1)) |
431 | return -EINVAL; | 431 | return -EINVAL; |
432 | 432 | ||
433 | /* | 433 | /* |
@@ -440,8 +440,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
440 | /* | 440 | /* |
441 | * check if the ending offset is aligned to this region's erase size | 441 | * check if the ending offset is aligned to this region's erase size |
442 | */ | 442 | */ |
443 | if ((instr->addr + instr->len) & (erase_regions[i].erasesize - | 443 | if (i < 0 || ((instr->addr + instr->len) & |
444 | 1)) | 444 | (erase_regions[i].erasesize - 1))) |
445 | return -EINVAL; | 445 | return -EINVAL; |
446 | } | 446 | } |
447 | 447 | ||
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 69007a6eff50..467a4f177bfb 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c | |||
@@ -213,11 +213,11 @@ static struct attribute *mtd_attrs[] = { | |||
213 | NULL, | 213 | NULL, |
214 | }; | 214 | }; |
215 | 215 | ||
216 | struct attribute_group mtd_group = { | 216 | static struct attribute_group mtd_group = { |
217 | .attrs = mtd_attrs, | 217 | .attrs = mtd_attrs, |
218 | }; | 218 | }; |
219 | 219 | ||
220 | const struct attribute_group *mtd_groups[] = { | 220 | static const struct attribute_group *mtd_groups[] = { |
221 | &mtd_group, | 221 | &mtd_group, |
222 | NULL, | 222 | NULL, |
223 | }; | 223 | }; |
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 742504ea96f5..b8043a9ba32d 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
@@ -453,7 +453,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
453 | for (i = 0; i < max && regions[i].offset <= slave->offset; i++) | 453 | for (i = 0; i < max && regions[i].offset <= slave->offset; i++) |
454 | ; | 454 | ; |
455 | /* The loop searched for the region _behind_ the first one */ | 455 | /* The loop searched for the region _behind_ the first one */ |
456 | i--; | 456 | if (i > 0) |
457 | i--; | ||
457 | 458 | ||
458 | /* Pick biggest erasesize */ | 459 | /* Pick biggest erasesize */ |
459 | for (; i < max && regions[i].offset < end; i++) { | 460 | for (; i < max && regions[i].offset < end; i++) { |
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ce96c091f01b..2fda0b615246 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -80,6 +80,23 @@ config MTD_NAND_OMAP2 | |||
80 | help | 80 | help |
81 | Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. | 81 | Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. |
82 | 82 | ||
83 | config MTD_NAND_OMAP_PREFETCH | ||
84 | bool "GPMC prefetch support for NAND Flash device" | ||
85 | depends on MTD_NAND && MTD_NAND_OMAP2 | ||
86 | default y | ||
87 | help | ||
88 | The NAND device can be accessed for Read/Write using GPMC PREFETCH engine | ||
89 | to improve the performance. | ||
90 | |||
91 | config MTD_NAND_OMAP_PREFETCH_DMA | ||
92 | depends on MTD_NAND_OMAP_PREFETCH | ||
93 | bool "DMA mode" | ||
94 | default n | ||
95 | help | ||
96 | The GPMC PREFETCH engine can be configured eigther in MPU interrupt mode | ||
97 | or in DMA interrupt mode. | ||
98 | Say y for DMA mode or MPU mode will be used | ||
99 | |||
83 | config MTD_NAND_TS7250 | 100 | config MTD_NAND_TS7250 |
84 | tristate "NAND Flash device on TS-7250 board" | 101 | tristate "NAND Flash device on TS-7250 board" |
85 | depends on MACH_TS72XX | 102 | depends on MACH_TS72XX |
@@ -426,6 +443,12 @@ config MTD_NAND_MXC | |||
426 | This enables the driver for the NAND flash controller on the | 443 | This enables the driver for the NAND flash controller on the |
427 | MXC processors. | 444 | MXC processors. |
428 | 445 | ||
446 | config MTD_NAND_NOMADIK | ||
447 | tristate "ST Nomadik 8815 NAND support" | ||
448 | depends on ARCH_NOMADIK | ||
449 | help | ||
450 | Driver for the NAND flash controller on the Nomadik, with ECC. | ||
451 | |||
429 | config MTD_NAND_SH_FLCTL | 452 | config MTD_NAND_SH_FLCTL |
430 | tristate "Support for NAND on Renesas SuperH FLCTL" | 453 | tristate "Support for NAND on Renesas SuperH FLCTL" |
431 | depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723 | 454 | depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723 |
@@ -452,4 +475,11 @@ config MTD_NAND_SOCRATES | |||
452 | help | 475 | help |
453 | Enables support for NAND Flash chips wired onto Socrates board. | 476 | Enables support for NAND Flash chips wired onto Socrates board. |
454 | 477 | ||
478 | config MTD_NAND_W90P910 | ||
479 | tristate "Support for NAND on w90p910 evaluation board." | ||
480 | depends on ARCH_W90X900 && MTD_PARTITIONS | ||
481 | help | ||
482 | This enables the driver for the NAND Flash on evaluation board based | ||
483 | on w90p910. | ||
484 | |||
455 | endif # MTD_NAND | 485 | endif # MTD_NAND |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f3a786b3cff3..6950d3dabf10 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
@@ -40,5 +40,7 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o | |||
40 | obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o | 40 | obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o |
41 | obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o | 41 | obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o |
42 | obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o | 42 | obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o |
43 | obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o | ||
44 | obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o | ||
43 | 45 | ||
44 | nand-objs := nand_base.o nand_bbt.o | 46 | nand-objs := nand_base.o nand_bbt.o |
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 20c828ba9405..f8e9975c86e5 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c | |||
@@ -218,7 +218,7 @@ static int atmel_nand_calculate(struct mtd_info *mtd, | |||
218 | * buf: buffer to store read data | 218 | * buf: buffer to store read data |
219 | */ | 219 | */ |
220 | static int atmel_nand_read_page(struct mtd_info *mtd, | 220 | static int atmel_nand_read_page(struct mtd_info *mtd, |
221 | struct nand_chip *chip, uint8_t *buf) | 221 | struct nand_chip *chip, uint8_t *buf, int page) |
222 | { | 222 | { |
223 | int eccsize = chip->ecc.size; | 223 | int eccsize = chip->ecc.size; |
224 | int eccbytes = chip->ecc.bytes; | 224 | int eccbytes = chip->ecc.bytes; |
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 1b4690bdfdb3..c828d9ac7bd7 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c | |||
@@ -381,7 +381,7 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, | |||
381 | * we need a special oob layout and handling. | 381 | * we need a special oob layout and handling. |
382 | */ | 382 | */ |
383 | static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, | 383 | static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, |
384 | uint8_t *buf) | 384 | uint8_t *buf, int page) |
385 | { | 385 | { |
386 | struct cafe_priv *cafe = mtd->priv; | 386 | struct cafe_priv *cafe = mtd->priv; |
387 | 387 | ||
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 0fad6487e6f4..f13f5b9afaf7 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c | |||
@@ -348,6 +348,12 @@ compare: | |||
348 | if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) | 348 | if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) |
349 | return 0; | 349 | return 0; |
350 | 350 | ||
351 | /* | ||
352 | * Clear any previous address calculation by doing a dummy read of an | ||
353 | * error address register. | ||
354 | */ | ||
355 | davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET); | ||
356 | |||
351 | /* Start address calculation, and wait for it to complete. | 357 | /* Start address calculation, and wait for it to complete. |
352 | * We _could_ start reading more data while this is working, | 358 | * We _could_ start reading more data while this is working, |
353 | * to speed up the overall page read. | 359 | * to speed up the overall page read. |
@@ -359,8 +365,10 @@ compare: | |||
359 | 365 | ||
360 | switch ((fsr >> 8) & 0x0f) { | 366 | switch ((fsr >> 8) & 0x0f) { |
361 | case 0: /* no error, should not happen */ | 367 | case 0: /* no error, should not happen */ |
368 | davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); | ||
362 | return 0; | 369 | return 0; |
363 | case 1: /* five or more errors detected */ | 370 | case 1: /* five or more errors detected */ |
371 | davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); | ||
364 | return -EIO; | 372 | return -EIO; |
365 | case 2: /* error addresses computed */ | 373 | case 2: /* error addresses computed */ |
366 | case 3: | 374 | case 3: |
@@ -500,6 +508,26 @@ static struct nand_ecclayout hwecc4_small __initconst = { | |||
500 | }, | 508 | }, |
501 | }; | 509 | }; |
502 | 510 | ||
511 | /* An ECC layout for using 4-bit ECC with large-page (2048bytes) flash, | ||
512 | * storing ten ECC bytes plus the manufacturer's bad block marker byte, | ||
513 | * and not overlapping the default BBT markers. | ||
514 | */ | ||
515 | static struct nand_ecclayout hwecc4_2048 __initconst = { | ||
516 | .eccbytes = 40, | ||
517 | .eccpos = { | ||
518 | /* at the end of spare sector */ | ||
519 | 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, | ||
520 | 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, | ||
521 | 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, | ||
522 | 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, | ||
523 | }, | ||
524 | .oobfree = { | ||
525 | /* 2 bytes at offset 0 hold manufacturer badblock markers */ | ||
526 | {.offset = 2, .length = 22, }, | ||
527 | /* 5 bytes at offset 8 hold BBT markers */ | ||
528 | /* 8 bytes at offset 16 hold JFFS2 clean markers */ | ||
529 | }, | ||
530 | }; | ||
503 | 531 | ||
504 | static int __init nand_davinci_probe(struct platform_device *pdev) | 532 | static int __init nand_davinci_probe(struct platform_device *pdev) |
505 | { | 533 | { |
@@ -690,15 +718,20 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
690 | info->mtd.oobsize - 16; | 718 | info->mtd.oobsize - 16; |
691 | goto syndrome_done; | 719 | goto syndrome_done; |
692 | } | 720 | } |
721 | if (chunks == 4) { | ||
722 | info->ecclayout = hwecc4_2048; | ||
723 | info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; | ||
724 | goto syndrome_done; | ||
725 | } | ||
693 | 726 | ||
694 | /* For large page chips we'll be wanting to use a | 727 | /* 4KiB page chips are not yet supported. The eccpos from |
695 | * not-yet-implemented mode that reads OOB data | 728 | * nand_ecclayout cannot hold 80 bytes and change to eccpos[] |
696 | * before reading the body of the page, to avoid | 729 | * breaks userspace ioctl interface with mtd-utils. Once we |
697 | * the "infix OOB" model of NAND_ECC_HW_SYNDROME | 730 | * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used |
698 | * (and preserve manufacturer badblock markings). | 731 | * for the 4KiB page chips. |
699 | */ | 732 | */ |
700 | dev_warn(&pdev->dev, "no 4-bit ECC support yet " | 733 | dev_warn(&pdev->dev, "no 4-bit ECC support yet " |
701 | "for large page NAND\n"); | 734 | "for 4KiB-page NAND\n"); |
702 | ret = -EIO; | 735 | ret = -EIO; |
703 | goto err_scan; | 736 | goto err_scan; |
704 | 737 | ||
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 1f6eb2578717..ddd37d2554ed 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c | |||
@@ -739,7 +739,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) | |||
739 | 739 | ||
740 | static int fsl_elbc_read_page(struct mtd_info *mtd, | 740 | static int fsl_elbc_read_page(struct mtd_info *mtd, |
741 | struct nand_chip *chip, | 741 | struct nand_chip *chip, |
742 | uint8_t *buf) | 742 | uint8_t *buf, |
743 | int page) | ||
743 | { | 744 | { |
744 | fsl_elbc_read_buf(mtd, buf, mtd->writesize); | 745 | fsl_elbc_read_buf(mtd, buf, mtd->writesize); |
745 | fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); | 746 | fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); |
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 76beea40d2cf..65b26d5a5c0d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c | |||
@@ -857,6 +857,17 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, | |||
857 | } | 857 | } |
858 | } | 858 | } |
859 | 859 | ||
860 | /* Define some generic bad / good block scan pattern which are used | ||
861 | * while scanning a device for factory marked good / bad blocks. */ | ||
862 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | ||
863 | |||
864 | static struct nand_bbt_descr smallpage_memorybased = { | ||
865 | .options = NAND_BBT_SCAN2NDPAGE, | ||
866 | .offs = 5, | ||
867 | .len = 1, | ||
868 | .pattern = scan_ff_pattern | ||
869 | }; | ||
870 | |||
860 | static int __init mxcnd_probe(struct platform_device *pdev) | 871 | static int __init mxcnd_probe(struct platform_device *pdev) |
861 | { | 872 | { |
862 | struct nand_chip *this; | 873 | struct nand_chip *this; |
@@ -973,7 +984,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
973 | goto escan; | 984 | goto escan; |
974 | } | 985 | } |
975 | 986 | ||
976 | host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; | 987 | if (mtd->writesize == 2048) { |
988 | host->pagesize_2k = 1; | ||
989 | this->badblock_pattern = &smallpage_memorybased; | ||
990 | } | ||
977 | 991 | ||
978 | if (this->ecc.mode == NAND_ECC_HW) { | 992 | if (this->ecc.mode == NAND_ECC_HW) { |
979 | switch (mtd->oobsize) { | 993 | switch (mtd->oobsize) { |
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8c21b89d2d0c..22113865438b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -688,8 +688,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) | |||
688 | retry: | 688 | retry: |
689 | spin_lock(lock); | 689 | spin_lock(lock); |
690 | 690 | ||
691 | /* Hardware controller shared among independend devices */ | 691 | /* Hardware controller shared among independent devices */ |
692 | /* Hardware controller shared among independend devices */ | ||
693 | if (!chip->controller->active) | 692 | if (!chip->controller->active) |
694 | chip->controller->active = chip; | 693 | chip->controller->active = chip; |
695 | 694 | ||
@@ -766,7 +765,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) | |||
766 | * Not for syndrome calculating ecc controllers, which use a special oob layout | 765 | * Not for syndrome calculating ecc controllers, which use a special oob layout |
767 | */ | 766 | */ |
768 | static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | 767 | static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, |
769 | uint8_t *buf) | 768 | uint8_t *buf, int page) |
770 | { | 769 | { |
771 | chip->read_buf(mtd, buf, mtd->writesize); | 770 | chip->read_buf(mtd, buf, mtd->writesize); |
772 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | 771 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); |
@@ -782,7 +781,7 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | |||
782 | * We need a special oob layout and handling even when OOB isn't used. | 781 | * We need a special oob layout and handling even when OOB isn't used. |
783 | */ | 782 | */ |
784 | static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, | 783 | static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, |
785 | uint8_t *buf) | 784 | uint8_t *buf, int page) |
786 | { | 785 | { |
787 | int eccsize = chip->ecc.size; | 786 | int eccsize = chip->ecc.size; |
788 | int eccbytes = chip->ecc.bytes; | 787 | int eccbytes = chip->ecc.bytes; |
@@ -821,7 +820,7 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *c | |||
821 | * @buf: buffer to store read data | 820 | * @buf: buffer to store read data |
822 | */ | 821 | */ |
823 | static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, | 822 | static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, |
824 | uint8_t *buf) | 823 | uint8_t *buf, int page) |
825 | { | 824 | { |
826 | int i, eccsize = chip->ecc.size; | 825 | int i, eccsize = chip->ecc.size; |
827 | int eccbytes = chip->ecc.bytes; | 826 | int eccbytes = chip->ecc.bytes; |
@@ -831,7 +830,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
831 | uint8_t *ecc_code = chip->buffers->ecccode; | 830 | uint8_t *ecc_code = chip->buffers->ecccode; |
832 | uint32_t *eccpos = chip->ecc.layout->eccpos; | 831 | uint32_t *eccpos = chip->ecc.layout->eccpos; |
833 | 832 | ||
834 | chip->ecc.read_page_raw(mtd, chip, buf); | 833 | chip->ecc.read_page_raw(mtd, chip, buf, page); |
835 | 834 | ||
836 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) | 835 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) |
837 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); | 836 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); |
@@ -944,7 +943,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3 | |||
944 | * Not for syndrome calculating ecc controllers which need a special oob layout | 943 | * Not for syndrome calculating ecc controllers which need a special oob layout |
945 | */ | 944 | */ |
946 | static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | 945 | static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, |
947 | uint8_t *buf) | 946 | uint8_t *buf, int page) |
948 | { | 947 | { |
949 | int i, eccsize = chip->ecc.size; | 948 | int i, eccsize = chip->ecc.size; |
950 | int eccbytes = chip->ecc.bytes; | 949 | int eccbytes = chip->ecc.bytes; |
@@ -980,6 +979,54 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
980 | } | 979 | } |
981 | 980 | ||
982 | /** | 981 | /** |
982 | * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first | ||
983 | * @mtd: mtd info structure | ||
984 | * @chip: nand chip info structure | ||
985 | * @buf: buffer to store read data | ||
986 | * | ||
987 | * Hardware ECC for large page chips, require OOB to be read first. | ||
988 | * For this ECC mode, the write_page method is re-used from ECC_HW. | ||
989 | * These methods read/write ECC from the OOB area, unlike the | ||
990 | * ECC_HW_SYNDROME support with multiple ECC steps, follows the | ||
991 | * "infix ECC" scheme and reads/writes ECC from the data area, by | ||
992 | * overwriting the NAND manufacturer bad block markings. | ||
993 | */ | ||
994 | static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, | ||
995 | struct nand_chip *chip, uint8_t *buf, int page) | ||
996 | { | ||
997 | int i, eccsize = chip->ecc.size; | ||
998 | int eccbytes = chip->ecc.bytes; | ||
999 | int eccsteps = chip->ecc.steps; | ||
1000 | uint8_t *p = buf; | ||
1001 | uint8_t *ecc_code = chip->buffers->ecccode; | ||
1002 | uint32_t *eccpos = chip->ecc.layout->eccpos; | ||
1003 | uint8_t *ecc_calc = chip->buffers->ecccalc; | ||
1004 | |||
1005 | /* Read the OOB area first */ | ||
1006 | chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); | ||
1007 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
1008 | chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); | ||
1009 | |||
1010 | for (i = 0; i < chip->ecc.total; i++) | ||
1011 | ecc_code[i] = chip->oob_poi[eccpos[i]]; | ||
1012 | |||
1013 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | ||
1014 | int stat; | ||
1015 | |||
1016 | chip->ecc.hwctl(mtd, NAND_ECC_READ); | ||
1017 | chip->read_buf(mtd, p, eccsize); | ||
1018 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); | ||
1019 | |||
1020 | stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); | ||
1021 | if (stat < 0) | ||
1022 | mtd->ecc_stats.failed++; | ||
1023 | else | ||
1024 | mtd->ecc_stats.corrected += stat; | ||
1025 | } | ||
1026 | return 0; | ||
1027 | } | ||
1028 | |||
1029 | /** | ||
983 | * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read | 1030 | * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read |
984 | * @mtd: mtd info structure | 1031 | * @mtd: mtd info structure |
985 | * @chip: nand chip info structure | 1032 | * @chip: nand chip info structure |
@@ -989,7 +1036,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
989 | * we need a special oob layout and handling. | 1036 | * we need a special oob layout and handling. |
990 | */ | 1037 | */ |
991 | static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, | 1038 | static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, |
992 | uint8_t *buf) | 1039 | uint8_t *buf, int page) |
993 | { | 1040 | { |
994 | int i, eccsize = chip->ecc.size; | 1041 | int i, eccsize = chip->ecc.size; |
995 | int eccbytes = chip->ecc.bytes; | 1042 | int eccbytes = chip->ecc.bytes; |
@@ -1131,11 +1178,13 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, | |||
1131 | 1178 | ||
1132 | /* Now read the page into the buffer */ | 1179 | /* Now read the page into the buffer */ |
1133 | if (unlikely(ops->mode == MTD_OOB_RAW)) | 1180 | if (unlikely(ops->mode == MTD_OOB_RAW)) |
1134 | ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); | 1181 | ret = chip->ecc.read_page_raw(mtd, chip, |
1182 | bufpoi, page); | ||
1135 | else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) | 1183 | else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) |
1136 | ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); | 1184 | ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); |
1137 | else | 1185 | else |
1138 | ret = chip->ecc.read_page(mtd, chip, bufpoi); | 1186 | ret = chip->ecc.read_page(mtd, chip, bufpoi, |
1187 | page); | ||
1139 | if (ret < 0) | 1188 | if (ret < 0) |
1140 | break; | 1189 | break; |
1141 | 1190 | ||
@@ -1413,8 +1462,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, | |||
1413 | int len; | 1462 | int len; |
1414 | uint8_t *buf = ops->oobbuf; | 1463 | uint8_t *buf = ops->oobbuf; |
1415 | 1464 | ||
1416 | DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", | 1465 | DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", |
1417 | (unsigned long long)from, readlen); | 1466 | __func__, (unsigned long long)from, readlen); |
1418 | 1467 | ||
1419 | if (ops->mode == MTD_OOB_AUTO) | 1468 | if (ops->mode == MTD_OOB_AUTO) |
1420 | len = chip->ecc.layout->oobavail; | 1469 | len = chip->ecc.layout->oobavail; |
@@ -1422,8 +1471,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, | |||
1422 | len = mtd->oobsize; | 1471 | len = mtd->oobsize; |
1423 | 1472 | ||
1424 | if (unlikely(ops->ooboffs >= len)) { | 1473 | if (unlikely(ops->ooboffs >= len)) { |
1425 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | 1474 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read " |
1426 | "Attempt to start read outside oob\n"); | 1475 | "outside oob\n", __func__); |
1427 | return -EINVAL; | 1476 | return -EINVAL; |
1428 | } | 1477 | } |
1429 | 1478 | ||
@@ -1431,8 +1480,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, | |||
1431 | if (unlikely(from >= mtd->size || | 1480 | if (unlikely(from >= mtd->size || |
1432 | ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - | 1481 | ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - |
1433 | (from >> chip->page_shift)) * len)) { | 1482 | (from >> chip->page_shift)) * len)) { |
1434 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | 1483 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end " |
1435 | "Attempt read beyond end of device\n"); | 1484 | "of device\n", __func__); |
1436 | return -EINVAL; | 1485 | return -EINVAL; |
1437 | } | 1486 | } |
1438 | 1487 | ||
@@ -1506,8 +1555,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, | |||
1506 | 1555 | ||
1507 | /* Do not allow reads past end of device */ | 1556 | /* Do not allow reads past end of device */ |
1508 | if (ops->datbuf && (from + ops->len) > mtd->size) { | 1557 | if (ops->datbuf && (from + ops->len) > mtd->size) { |
1509 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | 1558 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read " |
1510 | "Attempt read beyond end of device\n"); | 1559 | "beyond end of device\n", __func__); |
1511 | return -EINVAL; | 1560 | return -EINVAL; |
1512 | } | 1561 | } |
1513 | 1562 | ||
@@ -1816,8 +1865,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, | |||
1816 | 1865 | ||
1817 | /* reject writes, which are not page aligned */ | 1866 | /* reject writes, which are not page aligned */ |
1818 | if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { | 1867 | if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { |
1819 | printk(KERN_NOTICE "nand_write: " | 1868 | printk(KERN_NOTICE "%s: Attempt to write not " |
1820 | "Attempt to write not page aligned data\n"); | 1869 | "page aligned data\n", __func__); |
1821 | return -EINVAL; | 1870 | return -EINVAL; |
1822 | } | 1871 | } |
1823 | 1872 | ||
@@ -1944,8 +1993,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | |||
1944 | int chipnr, page, status, len; | 1993 | int chipnr, page, status, len; |
1945 | struct nand_chip *chip = mtd->priv; | 1994 | struct nand_chip *chip = mtd->priv; |
1946 | 1995 | ||
1947 | DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", | 1996 | DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", |
1948 | (unsigned int)to, (int)ops->ooblen); | 1997 | __func__, (unsigned int)to, (int)ops->ooblen); |
1949 | 1998 | ||
1950 | if (ops->mode == MTD_OOB_AUTO) | 1999 | if (ops->mode == MTD_OOB_AUTO) |
1951 | len = chip->ecc.layout->oobavail; | 2000 | len = chip->ecc.layout->oobavail; |
@@ -1954,14 +2003,14 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | |||
1954 | 2003 | ||
1955 | /* Do not allow write past end of page */ | 2004 | /* Do not allow write past end of page */ |
1956 | if ((ops->ooboffs + ops->ooblen) > len) { | 2005 | if ((ops->ooboffs + ops->ooblen) > len) { |
1957 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " | 2006 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write " |
1958 | "Attempt to write past end of page\n"); | 2007 | "past end of page\n", __func__); |
1959 | return -EINVAL; | 2008 | return -EINVAL; |
1960 | } | 2009 | } |
1961 | 2010 | ||
1962 | if (unlikely(ops->ooboffs >= len)) { | 2011 | if (unlikely(ops->ooboffs >= len)) { |
1963 | DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " | 2012 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start " |
1964 | "Attempt to start write outside oob\n"); | 2013 | "write outside oob\n", __func__); |
1965 | return -EINVAL; | 2014 | return -EINVAL; |
1966 | } | 2015 | } |
1967 | 2016 | ||
@@ -1970,8 +2019,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | |||
1970 | ops->ooboffs + ops->ooblen > | 2019 | ops->ooboffs + ops->ooblen > |
1971 | ((mtd->size >> chip->page_shift) - | 2020 | ((mtd->size >> chip->page_shift) - |
1972 | (to >> chip->page_shift)) * len)) { | 2021 | (to >> chip->page_shift)) * len)) { |
1973 | DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " | 2022 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " |
1974 | "Attempt write beyond end of device\n"); | 2023 | "end of device\n", __func__); |
1975 | return -EINVAL; | 2024 | return -EINVAL; |
1976 | } | 2025 | } |
1977 | 2026 | ||
@@ -2026,8 +2075,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, | |||
2026 | 2075 | ||
2027 | /* Do not allow writes past end of device */ | 2076 | /* Do not allow writes past end of device */ |
2028 | if (ops->datbuf && (to + ops->len) > mtd->size) { | 2077 | if (ops->datbuf && (to + ops->len) > mtd->size) { |
2029 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " | 2078 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " |
2030 | "Attempt write beyond end of device\n"); | 2079 | "end of device\n", __func__); |
2031 | return -EINVAL; | 2080 | return -EINVAL; |
2032 | } | 2081 | } |
2033 | 2082 | ||
@@ -2117,26 +2166,27 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
2117 | unsigned int bbt_masked_page = 0xffffffff; | 2166 | unsigned int bbt_masked_page = 0xffffffff; |
2118 | loff_t len; | 2167 | loff_t len; |
2119 | 2168 | ||
2120 | DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n", | 2169 | DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", |
2121 | (unsigned long long)instr->addr, (unsigned long long)instr->len); | 2170 | __func__, (unsigned long long)instr->addr, |
2171 | (unsigned long long)instr->len); | ||
2122 | 2172 | ||
2123 | /* Start address must align on block boundary */ | 2173 | /* Start address must align on block boundary */ |
2124 | if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { | 2174 | if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { |
2125 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); | 2175 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); |
2126 | return -EINVAL; | 2176 | return -EINVAL; |
2127 | } | 2177 | } |
2128 | 2178 | ||
2129 | /* Length must align on block boundary */ | 2179 | /* Length must align on block boundary */ |
2130 | if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { | 2180 | if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { |
2131 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " | 2181 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", |
2132 | "Length not block aligned\n"); | 2182 | __func__); |
2133 | return -EINVAL; | 2183 | return -EINVAL; |
2134 | } | 2184 | } |
2135 | 2185 | ||
2136 | /* Do not allow erase past end of device */ | 2186 | /* Do not allow erase past end of device */ |
2137 | if ((instr->len + instr->addr) > mtd->size) { | 2187 | if ((instr->len + instr->addr) > mtd->size) { |
2138 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " | 2188 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Erase past end of device\n", |
2139 | "Erase past end of device\n"); | 2189 | __func__); |
2140 | return -EINVAL; | 2190 | return -EINVAL; |
2141 | } | 2191 | } |
2142 | 2192 | ||
@@ -2157,8 +2207,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
2157 | 2207 | ||
2158 | /* Check, if it is write protected */ | 2208 | /* Check, if it is write protected */ |
2159 | if (nand_check_wp(mtd)) { | 2209 | if (nand_check_wp(mtd)) { |
2160 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " | 2210 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", |
2161 | "Device is write protected!!!\n"); | 2211 | __func__); |
2162 | instr->state = MTD_ERASE_FAILED; | 2212 | instr->state = MTD_ERASE_FAILED; |
2163 | goto erase_exit; | 2213 | goto erase_exit; |
2164 | } | 2214 | } |
@@ -2183,8 +2233,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
2183 | */ | 2233 | */ |
2184 | if (nand_block_checkbad(mtd, ((loff_t) page) << | 2234 | if (nand_block_checkbad(mtd, ((loff_t) page) << |
2185 | chip->page_shift, 0, allowbbt)) { | 2235 | chip->page_shift, 0, allowbbt)) { |
2186 | printk(KERN_WARNING "nand_erase: attempt to erase a " | 2236 | printk(KERN_WARNING "%s: attempt to erase a bad block " |
2187 | "bad block at page 0x%08x\n", page); | 2237 | "at page 0x%08x\n", __func__, page); |
2188 | instr->state = MTD_ERASE_FAILED; | 2238 | instr->state = MTD_ERASE_FAILED; |
2189 | goto erase_exit; | 2239 | goto erase_exit; |
2190 | } | 2240 | } |
@@ -2211,8 +2261,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
2211 | 2261 | ||
2212 | /* See if block erase succeeded */ | 2262 | /* See if block erase succeeded */ |
2213 | if (status & NAND_STATUS_FAIL) { | 2263 | if (status & NAND_STATUS_FAIL) { |
2214 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " | 2264 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, " |
2215 | "Failed erase, page 0x%08x\n", page); | 2265 | "page 0x%08x\n", __func__, page); |
2216 | instr->state = MTD_ERASE_FAILED; | 2266 | instr->state = MTD_ERASE_FAILED; |
2217 | instr->fail_addr = | 2267 | instr->fail_addr = |
2218 | ((loff_t)page << chip->page_shift); | 2268 | ((loff_t)page << chip->page_shift); |
@@ -2272,9 +2322,9 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
2272 | if (!rewrite_bbt[chipnr]) | 2322 | if (!rewrite_bbt[chipnr]) |
2273 | continue; | 2323 | continue; |
2274 | /* update the BBT for chip */ | 2324 | /* update the BBT for chip */ |
2275 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " | 2325 | DEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt " |
2276 | "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], | 2326 | "(%d:0x%0llx 0x%0x)\n", __func__, chipnr, |
2277 | chip->bbt_td->pages[chipnr]); | 2327 | rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); |
2278 | nand_update_bbt(mtd, rewrite_bbt[chipnr]); | 2328 | nand_update_bbt(mtd, rewrite_bbt[chipnr]); |
2279 | } | 2329 | } |
2280 | 2330 | ||
@@ -2292,7 +2342,7 @@ static void nand_sync(struct mtd_info *mtd) | |||
2292 | { | 2342 | { |
2293 | struct nand_chip *chip = mtd->priv; | 2343 | struct nand_chip *chip = mtd->priv; |
2294 | 2344 | ||
2295 | DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n"); | 2345 | DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__); |
2296 | 2346 | ||
2297 | /* Grab the lock and see if the device is available */ | 2347 | /* Grab the lock and see if the device is available */ |
2298 | nand_get_device(chip, mtd, FL_SYNCING); | 2348 | nand_get_device(chip, mtd, FL_SYNCING); |
@@ -2356,8 +2406,8 @@ static void nand_resume(struct mtd_info *mtd) | |||
2356 | if (chip->state == FL_PM_SUSPENDED) | 2406 | if (chip->state == FL_PM_SUSPENDED) |
2357 | nand_release_device(mtd); | 2407 | nand_release_device(mtd); |
2358 | else | 2408 | else |
2359 | printk(KERN_ERR "nand_resume() called for a chip which is not " | 2409 | printk(KERN_ERR "%s called for a chip which is not " |
2360 | "in suspended state\n"); | 2410 | "in suspended state\n", __func__); |
2361 | } | 2411 | } |
2362 | 2412 | ||
2363 | /* | 2413 | /* |
@@ -2671,6 +2721,17 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2671 | */ | 2721 | */ |
2672 | 2722 | ||
2673 | switch (chip->ecc.mode) { | 2723 | switch (chip->ecc.mode) { |
2724 | case NAND_ECC_HW_OOB_FIRST: | ||
2725 | /* Similar to NAND_ECC_HW, but a separate read_page handle */ | ||
2726 | if (!chip->ecc.calculate || !chip->ecc.correct || | ||
2727 | !chip->ecc.hwctl) { | ||
2728 | printk(KERN_WARNING "No ECC functions supplied; " | ||
2729 | "Hardware ECC not possible\n"); | ||
2730 | BUG(); | ||
2731 | } | ||
2732 | if (!chip->ecc.read_page) | ||
2733 | chip->ecc.read_page = nand_read_page_hwecc_oob_first; | ||
2734 | |||
2674 | case NAND_ECC_HW: | 2735 | case NAND_ECC_HW: |
2675 | /* Use standard hwecc read page function ? */ | 2736 | /* Use standard hwecc read page function ? */ |
2676 | if (!chip->ecc.read_page) | 2737 | if (!chip->ecc.read_page) |
@@ -2693,7 +2754,7 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2693 | chip->ecc.read_page == nand_read_page_hwecc || | 2754 | chip->ecc.read_page == nand_read_page_hwecc || |
2694 | !chip->ecc.write_page || | 2755 | !chip->ecc.write_page || |
2695 | chip->ecc.write_page == nand_write_page_hwecc)) { | 2756 | chip->ecc.write_page == nand_write_page_hwecc)) { |
2696 | printk(KERN_WARNING "No ECC functions supplied, " | 2757 | printk(KERN_WARNING "No ECC functions supplied; " |
2697 | "Hardware ECC not possible\n"); | 2758 | "Hardware ECC not possible\n"); |
2698 | BUG(); | 2759 | BUG(); |
2699 | } | 2760 | } |
@@ -2728,7 +2789,8 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2728 | chip->ecc.write_page_raw = nand_write_page_raw; | 2789 | chip->ecc.write_page_raw = nand_write_page_raw; |
2729 | chip->ecc.read_oob = nand_read_oob_std; | 2790 | chip->ecc.read_oob = nand_read_oob_std; |
2730 | chip->ecc.write_oob = nand_write_oob_std; | 2791 | chip->ecc.write_oob = nand_write_oob_std; |
2731 | chip->ecc.size = 256; | 2792 | if (!chip->ecc.size) |
2793 | chip->ecc.size = 256; | ||
2732 | chip->ecc.bytes = 3; | 2794 | chip->ecc.bytes = 3; |
2733 | break; | 2795 | break; |
2734 | 2796 | ||
@@ -2858,7 +2920,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) | |||
2858 | 2920 | ||
2859 | /* Many callers got this wrong, so check for it for a while... */ | 2921 | /* Many callers got this wrong, so check for it for a while... */ |
2860 | if (!mtd->owner && caller_is_module()) { | 2922 | if (!mtd->owner && caller_is_module()) { |
2861 | printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); | 2923 | printk(KERN_CRIT "%s called with NULL mtd->owner!\n", |
2924 | __func__); | ||
2862 | BUG(); | 2925 | BUG(); |
2863 | } | 2926 | } |
2864 | 2927 | ||
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index c0cb87d6d16e..db7ae9d6a296 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c | |||
@@ -417,22 +417,22 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, | |||
417 | EXPORT_SYMBOL(nand_calculate_ecc); | 417 | EXPORT_SYMBOL(nand_calculate_ecc); |
418 | 418 | ||
419 | /** | 419 | /** |
420 | * nand_correct_data - [NAND Interface] Detect and correct bit error(s) | 420 | * __nand_correct_data - [NAND Interface] Detect and correct bit error(s) |
421 | * @mtd: MTD block structure | ||
422 | * @buf: raw data read from the chip | 421 | * @buf: raw data read from the chip |
423 | * @read_ecc: ECC from the chip | 422 | * @read_ecc: ECC from the chip |
424 | * @calc_ecc: the ECC calculated from raw data | 423 | * @calc_ecc: the ECC calculated from raw data |
424 | * @eccsize: data bytes per ecc step (256 or 512) | ||
425 | * | 425 | * |
426 | * Detect and correct a 1 bit error for 256/512 byte block | 426 | * Detect and correct a 1 bit error for eccsize byte block |
427 | */ | 427 | */ |
428 | int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, | 428 | int __nand_correct_data(unsigned char *buf, |
429 | unsigned char *read_ecc, unsigned char *calc_ecc) | 429 | unsigned char *read_ecc, unsigned char *calc_ecc, |
430 | unsigned int eccsize) | ||
430 | { | 431 | { |
431 | unsigned char b0, b1, b2, bit_addr; | 432 | unsigned char b0, b1, b2, bit_addr; |
432 | unsigned int byte_addr; | 433 | unsigned int byte_addr; |
433 | /* 256 or 512 bytes/ecc */ | 434 | /* 256 or 512 bytes/ecc */ |
434 | const uint32_t eccsize_mult = | 435 | const uint32_t eccsize_mult = eccsize >> 8; |
435 | (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; | ||
436 | 436 | ||
437 | /* | 437 | /* |
438 | * b0 to b2 indicate which bit is faulty (if any) | 438 | * b0 to b2 indicate which bit is faulty (if any) |
@@ -495,6 +495,23 @@ int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, | |||
495 | printk(KERN_ERR "uncorrectable error : "); | 495 | printk(KERN_ERR "uncorrectable error : "); |
496 | return -1; | 496 | return -1; |
497 | } | 497 | } |
498 | EXPORT_SYMBOL(__nand_correct_data); | ||
499 | |||
500 | /** | ||
501 | * nand_correct_data - [NAND Interface] Detect and correct bit error(s) | ||
502 | * @mtd: MTD block structure | ||
503 | * @buf: raw data read from the chip | ||
504 | * @read_ecc: ECC from the chip | ||
505 | * @calc_ecc: the ECC calculated from raw data | ||
506 | * | ||
507 | * Detect and correct a 1 bit error for 256/512 byte block | ||
508 | */ | ||
509 | int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, | ||
510 | unsigned char *read_ecc, unsigned char *calc_ecc) | ||
511 | { | ||
512 | return __nand_correct_data(buf, read_ecc, calc_ecc, | ||
513 | ((struct nand_chip *)mtd->priv)->ecc.size); | ||
514 | } | ||
498 | EXPORT_SYMBOL(nand_correct_data); | 515 | EXPORT_SYMBOL(nand_correct_data); |
499 | 516 | ||
500 | MODULE_LICENSE("GPL"); | 517 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 89bf85af642c..40b5658bdbe6 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c | |||
@@ -102,8 +102,8 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd, | |||
102 | wmb(); | 102 | wmb(); |
103 | ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); | 103 | ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); |
104 | /* The NDFC uses Smart Media (SMC) bytes order */ | 104 | /* The NDFC uses Smart Media (SMC) bytes order */ |
105 | ecc_code[0] = p[2]; | 105 | ecc_code[0] = p[1]; |
106 | ecc_code[1] = p[1]; | 106 | ecc_code[1] = p[2]; |
107 | ecc_code[2] = p[3]; | 107 | ecc_code[2] = p[3]; |
108 | 108 | ||
109 | return 0; | 109 | return 0; |
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c new file mode 100644 index 000000000000..7c302d55910e --- /dev/null +++ b/drivers/mtd/nand/nomadik_nand.c | |||
@@ -0,0 +1,250 @@ | |||
1 | /* | ||
2 | * drivers/mtd/nand/nomadik_nand.c | ||
3 | * | ||
4 | * Overview: | ||
5 | * Driver for on-board NAND flash on Nomadik Platforms | ||
6 | * | ||
7 | * Copyright © 2007 STMicroelectronics Pvt. Ltd. | ||
8 | * Author: Sachin Verma <sachin.verma@st.com> | ||
9 | * | ||
10 | * Copyright © 2009 Alessandro Rubini | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/init.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <linux/mtd/mtd.h> | ||
28 | #include <linux/mtd/nand.h> | ||
29 | #include <linux/mtd/nand_ecc.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/mtd/partitions.h> | ||
32 | #include <linux/io.h> | ||
33 | #include <mach/nand.h> | ||
34 | #include <mach/fsmc.h> | ||
35 | |||
36 | #include <mtd/mtd-abi.h> | ||
37 | |||
38 | struct nomadik_nand_host { | ||
39 | struct mtd_info mtd; | ||
40 | struct nand_chip nand; | ||
41 | void __iomem *data_va; | ||
42 | void __iomem *cmd_va; | ||
43 | void __iomem *addr_va; | ||
44 | struct nand_bbt_descr *bbt_desc; | ||
45 | }; | ||
46 | |||
47 | static struct nand_ecclayout nomadik_ecc_layout = { | ||
48 | .eccbytes = 3 * 4, | ||
49 | .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */ | ||
50 | 0x02, 0x03, 0x04, | ||
51 | 0x12, 0x13, 0x14, | ||
52 | 0x22, 0x23, 0x24, | ||
53 | 0x32, 0x33, 0x34}, | ||
54 | /* let's keep bytes 5,6,7 for us, just in case we change ECC algo */ | ||
55 | .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} }, | ||
56 | }; | ||
57 | |||
58 | static void nomadik_ecc_control(struct mtd_info *mtd, int mode) | ||
59 | { | ||
60 | /* No need to enable hw ecc, it's on by default */ | ||
61 | } | ||
62 | |||
63 | static void nomadik_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) | ||
64 | { | ||
65 | struct nand_chip *nand = mtd->priv; | ||
66 | struct nomadik_nand_host *host = nand->priv; | ||
67 | |||
68 | if (cmd == NAND_CMD_NONE) | ||
69 | return; | ||
70 | |||
71 | if (ctrl & NAND_CLE) | ||
72 | writeb(cmd, host->cmd_va); | ||
73 | else | ||
74 | writeb(cmd, host->addr_va); | ||
75 | } | ||
76 | |||
77 | static int nomadik_nand_probe(struct platform_device *pdev) | ||
78 | { | ||
79 | struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; | ||
80 | struct nomadik_nand_host *host; | ||
81 | struct mtd_info *mtd; | ||
82 | struct nand_chip *nand; | ||
83 | struct resource *res; | ||
84 | int ret = 0; | ||
85 | |||
86 | /* Allocate memory for the device structure (and zero it) */ | ||
87 | host = kzalloc(sizeof(struct nomadik_nand_host), GFP_KERNEL); | ||
88 | if (!host) { | ||
89 | dev_err(&pdev->dev, "Failed to allocate device structure.\n"); | ||
90 | return -ENOMEM; | ||
91 | } | ||
92 | |||
93 | /* Call the client's init function, if any */ | ||
94 | if (pdata->init) | ||
95 | ret = pdata->init(); | ||
96 | if (ret < 0) { | ||
97 | dev_err(&pdev->dev, "Init function failed\n"); | ||
98 | goto err; | ||
99 | } | ||
100 | |||
101 | /* ioremap three regions */ | ||
102 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); | ||
103 | if (!res) { | ||
104 | ret = -EIO; | ||
105 | goto err_unmap; | ||
106 | } | ||
107 | host->addr_va = ioremap(res->start, res->end - res->start + 1); | ||
108 | |||
109 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); | ||
110 | if (!res) { | ||
111 | ret = -EIO; | ||
112 | goto err_unmap; | ||
113 | } | ||
114 | host->data_va = ioremap(res->start, res->end - res->start + 1); | ||
115 | |||
116 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); | ||
117 | if (!res) { | ||
118 | ret = -EIO; | ||
119 | goto err_unmap; | ||
120 | } | ||
121 | host->cmd_va = ioremap(res->start, res->end - res->start + 1); | ||
122 | |||
123 | if (!host->addr_va || !host->data_va || !host->cmd_va) { | ||
124 | ret = -ENOMEM; | ||
125 | goto err_unmap; | ||
126 | } | ||
127 | |||
128 | /* Link all private pointers */ | ||
129 | mtd = &host->mtd; | ||
130 | nand = &host->nand; | ||
131 | mtd->priv = nand; | ||
132 | nand->priv = host; | ||
133 | |||
134 | host->mtd.owner = THIS_MODULE; | ||
135 | nand->IO_ADDR_R = host->data_va; | ||
136 | nand->IO_ADDR_W = host->data_va; | ||
137 | nand->cmd_ctrl = nomadik_cmd_ctrl; | ||
138 | |||
139 | /* | ||
140 | * This stanza declares ECC_HW but uses soft routines. It's because | ||
141 | * HW claims to make the calculation but not the correction. However, | ||
142 | * I haven't managed to get the desired data out of it until now. | ||
143 | */ | ||
144 | nand->ecc.mode = NAND_ECC_SOFT; | ||
145 | nand->ecc.layout = &nomadik_ecc_layout; | ||
146 | nand->ecc.hwctl = nomadik_ecc_control; | ||
147 | nand->ecc.size = 512; | ||
148 | nand->ecc.bytes = 3; | ||
149 | |||
150 | nand->options = pdata->options; | ||
151 | |||
152 | /* | ||
153 | * Scan to find existance of the device | ||
154 | */ | ||
155 | if (nand_scan(&host->mtd, 1)) { | ||
156 | ret = -ENXIO; | ||
157 | goto err_unmap; | ||
158 | } | ||
159 | |||
160 | #ifdef CONFIG_MTD_PARTITIONS | ||
161 | add_mtd_partitions(&host->mtd, pdata->parts, pdata->nparts); | ||
162 | #else | ||
163 | pr_info("Registering %s as whole device\n", mtd->name); | ||
164 | add_mtd_device(mtd); | ||
165 | #endif | ||
166 | |||
167 | platform_set_drvdata(pdev, host); | ||
168 | return 0; | ||
169 | |||
170 | err_unmap: | ||
171 | if (host->cmd_va) | ||
172 | iounmap(host->cmd_va); | ||
173 | if (host->data_va) | ||
174 | iounmap(host->data_va); | ||
175 | if (host->addr_va) | ||
176 | iounmap(host->addr_va); | ||
177 | err: | ||
178 | kfree(host); | ||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * Clean up routine | ||
184 | */ | ||
185 | static int nomadik_nand_remove(struct platform_device *pdev) | ||
186 | { | ||
187 | struct nomadik_nand_host *host = platform_get_drvdata(pdev); | ||
188 | struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; | ||
189 | |||
190 | if (pdata->exit) | ||
191 | pdata->exit(); | ||
192 | |||
193 | if (host) { | ||
194 | iounmap(host->cmd_va); | ||
195 | iounmap(host->data_va); | ||
196 | iounmap(host->addr_va); | ||
197 | kfree(host); | ||
198 | } | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int nomadik_nand_suspend(struct device *dev) | ||
203 | { | ||
204 | struct nomadik_nand_host *host = dev_get_drvdata(dev); | ||
205 | int ret = 0; | ||
206 | if (host) | ||
207 | ret = host->mtd.suspend(&host->mtd); | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | static int nomadik_nand_resume(struct device *dev) | ||
212 | { | ||
213 | struct nomadik_nand_host *host = dev_get_drvdata(dev); | ||
214 | if (host) | ||
215 | host->mtd.resume(&host->mtd); | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static struct dev_pm_ops nomadik_nand_pm_ops = { | ||
220 | .suspend = nomadik_nand_suspend, | ||
221 | .resume = nomadik_nand_resume, | ||
222 | }; | ||
223 | |||
224 | static struct platform_driver nomadik_nand_driver = { | ||
225 | .probe = nomadik_nand_probe, | ||
226 | .remove = nomadik_nand_remove, | ||
227 | .driver = { | ||
228 | .owner = THIS_MODULE, | ||
229 | .name = "nomadik_nand", | ||
230 | .pm = &nomadik_nand_pm_ops, | ||
231 | }, | ||
232 | }; | ||
233 | |||
234 | static int __init nand_nomadik_init(void) | ||
235 | { | ||
236 | pr_info("Nomadik NAND driver\n"); | ||
237 | return platform_driver_register(&nomadik_nand_driver); | ||
238 | } | ||
239 | |||
240 | static void __exit nand_nomadik_exit(void) | ||
241 | { | ||
242 | platform_driver_unregister(&nomadik_nand_driver); | ||
243 | } | ||
244 | |||
245 | module_init(nand_nomadik_init); | ||
246 | module_exit(nand_nomadik_exit); | ||
247 | |||
248 | MODULE_LICENSE("GPL"); | ||
249 | MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)"); | ||
250 | MODULE_DESCRIPTION("NAND driver for Nomadik Platform"); | ||
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index ebd07e95b814..090ab87086b5 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c | |||
@@ -18,8 +18,7 @@ | |||
18 | #include <linux/mtd/partitions.h> | 18 | #include <linux/mtd/partitions.h> |
19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
20 | 20 | ||
21 | #include <asm/dma.h> | 21 | #include <mach/dma.h> |
22 | |||
23 | #include <mach/gpmc.h> | 22 | #include <mach/gpmc.h> |
24 | #include <mach/nand.h> | 23 | #include <mach/nand.h> |
25 | 24 | ||
@@ -112,6 +111,27 @@ | |||
112 | static const char *part_probes[] = { "cmdlinepart", NULL }; | 111 | static const char *part_probes[] = { "cmdlinepart", NULL }; |
113 | #endif | 112 | #endif |
114 | 113 | ||
114 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH | ||
115 | static int use_prefetch = 1; | ||
116 | |||
117 | /* "modprobe ... use_prefetch=0" etc */ | ||
118 | module_param(use_prefetch, bool, 0); | ||
119 | MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH"); | ||
120 | |||
121 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA | ||
122 | static int use_dma = 1; | ||
123 | |||
124 | /* "modprobe ... use_dma=0" etc */ | ||
125 | module_param(use_dma, bool, 0); | ||
126 | MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); | ||
127 | #else | ||
128 | const int use_dma; | ||
129 | #endif | ||
130 | #else | ||
131 | const int use_prefetch; | ||
132 | const int use_dma; | ||
133 | #endif | ||
134 | |||
115 | struct omap_nand_info { | 135 | struct omap_nand_info { |
116 | struct nand_hw_control controller; | 136 | struct nand_hw_control controller; |
117 | struct omap_nand_platform_data *pdata; | 137 | struct omap_nand_platform_data *pdata; |
@@ -124,6 +144,9 @@ struct omap_nand_info { | |||
124 | unsigned long phys_base; | 144 | unsigned long phys_base; |
125 | void __iomem *gpmc_cs_baseaddr; | 145 | void __iomem *gpmc_cs_baseaddr; |
126 | void __iomem *gpmc_baseaddr; | 146 | void __iomem *gpmc_baseaddr; |
147 | void __iomem *nand_pref_fifo_add; | ||
148 | struct completion comp; | ||
149 | int dma_ch; | ||
127 | }; | 150 | }; |
128 | 151 | ||
129 | /** | 152 | /** |
@@ -189,6 +212,38 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) | |||
189 | } | 212 | } |
190 | 213 | ||
191 | /** | 214 | /** |
215 | * omap_read_buf8 - read data from NAND controller into buffer | ||
216 | * @mtd: MTD device structure | ||
217 | * @buf: buffer to store date | ||
218 | * @len: number of bytes to read | ||
219 | */ | ||
220 | static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) | ||
221 | { | ||
222 | struct nand_chip *nand = mtd->priv; | ||
223 | |||
224 | ioread8_rep(nand->IO_ADDR_R, buf, len); | ||
225 | } | ||
226 | |||
227 | /** | ||
228 | * omap_write_buf8 - write buffer to NAND controller | ||
229 | * @mtd: MTD device structure | ||
230 | * @buf: data buffer | ||
231 | * @len: number of bytes to write | ||
232 | */ | ||
233 | static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) | ||
234 | { | ||
235 | struct omap_nand_info *info = container_of(mtd, | ||
236 | struct omap_nand_info, mtd); | ||
237 | u_char *p = (u_char *)buf; | ||
238 | |||
239 | while (len--) { | ||
240 | iowrite8(*p++, info->nand.IO_ADDR_W); | ||
241 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + | ||
242 | GPMC_STATUS) & GPMC_BUF_FULL)); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | /** | ||
192 | * omap_read_buf16 - read data from NAND controller into buffer | 247 | * omap_read_buf16 - read data from NAND controller into buffer |
193 | * @mtd: MTD device structure | 248 | * @mtd: MTD device structure |
194 | * @buf: buffer to store date | 249 | * @buf: buffer to store date |
@@ -198,7 +253,7 @@ static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) | |||
198 | { | 253 | { |
199 | struct nand_chip *nand = mtd->priv; | 254 | struct nand_chip *nand = mtd->priv; |
200 | 255 | ||
201 | __raw_readsw(nand->IO_ADDR_R, buf, len / 2); | 256 | ioread16_rep(nand->IO_ADDR_R, buf, len / 2); |
202 | } | 257 | } |
203 | 258 | ||
204 | /** | 259 | /** |
@@ -217,13 +272,242 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) | |||
217 | len >>= 1; | 272 | len >>= 1; |
218 | 273 | ||
219 | while (len--) { | 274 | while (len--) { |
220 | writew(*p++, info->nand.IO_ADDR_W); | 275 | iowrite16(*p++, info->nand.IO_ADDR_W); |
221 | 276 | ||
222 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + | 277 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + |
223 | GPMC_STATUS) & GPMC_BUF_FULL)) | 278 | GPMC_STATUS) & GPMC_BUF_FULL)) |
224 | ; | 279 | ; |
225 | } | 280 | } |
226 | } | 281 | } |
282 | |||
283 | /** | ||
284 | * omap_read_buf_pref - read data from NAND controller into buffer | ||
285 | * @mtd: MTD device structure | ||
286 | * @buf: buffer to store date | ||
287 | * @len: number of bytes to read | ||
288 | */ | ||
289 | static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) | ||
290 | { | ||
291 | struct omap_nand_info *info = container_of(mtd, | ||
292 | struct omap_nand_info, mtd); | ||
293 | uint32_t pfpw_status = 0, r_count = 0; | ||
294 | int ret = 0; | ||
295 | u32 *p = (u32 *)buf; | ||
296 | |||
297 | /* take care of subpage reads */ | ||
298 | for (; len % 4 != 0; ) { | ||
299 | *buf++ = __raw_readb(info->nand.IO_ADDR_R); | ||
300 | len--; | ||
301 | } | ||
302 | p = (u32 *) buf; | ||
303 | |||
304 | /* configure and start prefetch transfer */ | ||
305 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); | ||
306 | if (ret) { | ||
307 | /* PFPW engine is busy, use cpu copy method */ | ||
308 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
309 | omap_read_buf16(mtd, buf, len); | ||
310 | else | ||
311 | omap_read_buf8(mtd, buf, len); | ||
312 | } else { | ||
313 | do { | ||
314 | pfpw_status = gpmc_prefetch_status(); | ||
315 | r_count = ((pfpw_status >> 24) & 0x7F) >> 2; | ||
316 | ioread32_rep(info->nand_pref_fifo_add, p, r_count); | ||
317 | p += r_count; | ||
318 | len -= r_count << 2; | ||
319 | } while (len); | ||
320 | |||
321 | /* disable and stop the PFPW engine */ | ||
322 | gpmc_prefetch_reset(); | ||
323 | } | ||
324 | } | ||
325 | |||
326 | /** | ||
327 | * omap_write_buf_pref - write buffer to NAND controller | ||
328 | * @mtd: MTD device structure | ||
329 | * @buf: data buffer | ||
330 | * @len: number of bytes to write | ||
331 | */ | ||
332 | static void omap_write_buf_pref(struct mtd_info *mtd, | ||
333 | const u_char *buf, int len) | ||
334 | { | ||
335 | struct omap_nand_info *info = container_of(mtd, | ||
336 | struct omap_nand_info, mtd); | ||
337 | uint32_t pfpw_status = 0, w_count = 0; | ||
338 | int i = 0, ret = 0; | ||
339 | u16 *p = (u16 *) buf; | ||
340 | |||
341 | /* take care of subpage writes */ | ||
342 | if (len % 2 != 0) { | ||
343 | writeb(*buf, info->nand.IO_ADDR_R); | ||
344 | p = (u16 *)(buf + 1); | ||
345 | len--; | ||
346 | } | ||
347 | |||
348 | /* configure and start prefetch transfer */ | ||
349 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1); | ||
350 | if (ret) { | ||
351 | /* PFPW engine is busy, use cpu copy method */ | ||
352 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
353 | omap_write_buf16(mtd, buf, len); | ||
354 | else | ||
355 | omap_write_buf8(mtd, buf, len); | ||
356 | } else { | ||
357 | pfpw_status = gpmc_prefetch_status(); | ||
358 | while (pfpw_status & 0x3FFF) { | ||
359 | w_count = ((pfpw_status >> 24) & 0x7F) >> 1; | ||
360 | for (i = 0; (i < w_count) && len; i++, len -= 2) | ||
361 | iowrite16(*p++, info->nand_pref_fifo_add); | ||
362 | pfpw_status = gpmc_prefetch_status(); | ||
363 | } | ||
364 | |||
365 | /* disable and stop the PFPW engine */ | ||
366 | gpmc_prefetch_reset(); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA | ||
371 | /* | ||
372 | * omap_nand_dma_cb: callback on the completion of dma transfer | ||
373 | * @lch: logical channel | ||
374 | * @ch_satuts: channel status | ||
375 | * @data: pointer to completion data structure | ||
376 | */ | ||
377 | static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) | ||
378 | { | ||
379 | complete((struct completion *) data); | ||
380 | } | ||
381 | |||
382 | /* | ||
383 | * omap_nand_dma_transfer: configer and start dma transfer | ||
384 | * @mtd: MTD device structure | ||
385 | * @addr: virtual address in RAM of source/destination | ||
386 | * @len: number of data bytes to be transferred | ||
387 | * @is_write: flag for read/write operation | ||
388 | */ | ||
389 | static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, | ||
390 | unsigned int len, int is_write) | ||
391 | { | ||
392 | struct omap_nand_info *info = container_of(mtd, | ||
393 | struct omap_nand_info, mtd); | ||
394 | uint32_t prefetch_status = 0; | ||
395 | enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : | ||
396 | DMA_FROM_DEVICE; | ||
397 | dma_addr_t dma_addr; | ||
398 | int ret; | ||
399 | |||
400 | /* The fifo depth is 64 bytes. We have a sync at each frame and frame | ||
401 | * length is 64 bytes. | ||
402 | */ | ||
403 | int buf_len = len >> 6; | ||
404 | |||
405 | if (addr >= high_memory) { | ||
406 | struct page *p1; | ||
407 | |||
408 | if (((size_t)addr & PAGE_MASK) != | ||
409 | ((size_t)(addr + len - 1) & PAGE_MASK)) | ||
410 | goto out_copy; | ||
411 | p1 = vmalloc_to_page(addr); | ||
412 | if (!p1) | ||
413 | goto out_copy; | ||
414 | addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK); | ||
415 | } | ||
416 | |||
417 | dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir); | ||
418 | if (dma_mapping_error(&info->pdev->dev, dma_addr)) { | ||
419 | dev_err(&info->pdev->dev, | ||
420 | "Couldn't DMA map a %d byte buffer\n", len); | ||
421 | goto out_copy; | ||
422 | } | ||
423 | |||
424 | if (is_write) { | ||
425 | omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, | ||
426 | info->phys_base, 0, 0); | ||
427 | omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, | ||
428 | dma_addr, 0, 0); | ||
429 | omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, | ||
430 | 0x10, buf_len, OMAP_DMA_SYNC_FRAME, | ||
431 | OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC); | ||
432 | } else { | ||
433 | omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, | ||
434 | info->phys_base, 0, 0); | ||
435 | omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, | ||
436 | dma_addr, 0, 0); | ||
437 | omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, | ||
438 | 0x10, buf_len, OMAP_DMA_SYNC_FRAME, | ||
439 | OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC); | ||
440 | } | ||
441 | /* configure and start prefetch transfer */ | ||
442 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write); | ||
443 | if (ret) | ||
444 | /* PFPW engine is busy, use cpu copy methode */ | ||
445 | goto out_copy; | ||
446 | |||
447 | init_completion(&info->comp); | ||
448 | |||
449 | omap_start_dma(info->dma_ch); | ||
450 | |||
451 | /* setup and start DMA using dma_addr */ | ||
452 | wait_for_completion(&info->comp); | ||
453 | |||
454 | while (0x3fff & (prefetch_status = gpmc_prefetch_status())) | ||
455 | ; | ||
456 | /* disable and stop the PFPW engine */ | ||
457 | gpmc_prefetch_reset(); | ||
458 | |||
459 | dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); | ||
460 | return 0; | ||
461 | |||
462 | out_copy: | ||
463 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
464 | is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) | ||
465 | : omap_write_buf16(mtd, (u_char *) addr, len); | ||
466 | else | ||
467 | is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) | ||
468 | : omap_write_buf8(mtd, (u_char *) addr, len); | ||
469 | return 0; | ||
470 | } | ||
471 | #else | ||
472 | static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {} | ||
473 | static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, | ||
474 | unsigned int len, int is_write) | ||
475 | { | ||
476 | return 0; | ||
477 | } | ||
478 | #endif | ||
479 | |||
480 | /** | ||
481 | * omap_read_buf_dma_pref - read data from NAND controller into buffer | ||
482 | * @mtd: MTD device structure | ||
483 | * @buf: buffer to store date | ||
484 | * @len: number of bytes to read | ||
485 | */ | ||
486 | static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len) | ||
487 | { | ||
488 | if (len <= mtd->oobsize) | ||
489 | omap_read_buf_pref(mtd, buf, len); | ||
490 | else | ||
491 | /* start transfer in DMA mode */ | ||
492 | omap_nand_dma_transfer(mtd, buf, len, 0x0); | ||
493 | } | ||
494 | |||
495 | /** | ||
496 | * omap_write_buf_dma_pref - write buffer to NAND controller | ||
497 | * @mtd: MTD device structure | ||
498 | * @buf: data buffer | ||
499 | * @len: number of bytes to write | ||
500 | */ | ||
501 | static void omap_write_buf_dma_pref(struct mtd_info *mtd, | ||
502 | const u_char *buf, int len) | ||
503 | { | ||
504 | if (len <= mtd->oobsize) | ||
505 | omap_write_buf_pref(mtd, buf, len); | ||
506 | else | ||
507 | /* start transfer in DMA mode */ | ||
508 | omap_nand_dma_transfer(mtd, buf, len, 0x1); | ||
509 | } | ||
510 | |||
227 | /** | 511 | /** |
228 | * omap_verify_buf - Verify chip data against buffer | 512 | * omap_verify_buf - Verify chip data against buffer |
229 | * @mtd: MTD device structure | 513 | * @mtd: MTD device structure |
@@ -658,17 +942,12 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
658 | err = -ENOMEM; | 942 | err = -ENOMEM; |
659 | goto out_release_mem_region; | 943 | goto out_release_mem_region; |
660 | } | 944 | } |
945 | |||
661 | info->nand.controller = &info->controller; | 946 | info->nand.controller = &info->controller; |
662 | 947 | ||
663 | info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; | 948 | info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; |
664 | info->nand.cmd_ctrl = omap_hwcontrol; | 949 | info->nand.cmd_ctrl = omap_hwcontrol; |
665 | 950 | ||
666 | /* REVISIT: only supports 16-bit NAND flash */ | ||
667 | |||
668 | info->nand.read_buf = omap_read_buf16; | ||
669 | info->nand.write_buf = omap_write_buf16; | ||
670 | info->nand.verify_buf = omap_verify_buf; | ||
671 | |||
672 | /* | 951 | /* |
673 | * If RDY/BSY line is connected to OMAP then use the omap ready | 952 | * If RDY/BSY line is connected to OMAP then use the omap ready |
674 | * funcrtion and the generic nand_wait function which reads the status | 953 | * funcrtion and the generic nand_wait function which reads the status |
@@ -689,6 +968,40 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
689 | == 0x1000) | 968 | == 0x1000) |
690 | info->nand.options |= NAND_BUSWIDTH_16; | 969 | info->nand.options |= NAND_BUSWIDTH_16; |
691 | 970 | ||
971 | if (use_prefetch) { | ||
972 | /* copy the virtual address of nand base for fifo access */ | ||
973 | info->nand_pref_fifo_add = info->nand.IO_ADDR_R; | ||
974 | |||
975 | info->nand.read_buf = omap_read_buf_pref; | ||
976 | info->nand.write_buf = omap_write_buf_pref; | ||
977 | if (use_dma) { | ||
978 | err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND", | ||
979 | omap_nand_dma_cb, &info->comp, &info->dma_ch); | ||
980 | if (err < 0) { | ||
981 | info->dma_ch = -1; | ||
982 | printk(KERN_WARNING "DMA request failed." | ||
983 | " Non-dma data transfer mode\n"); | ||
984 | } else { | ||
985 | omap_set_dma_dest_burst_mode(info->dma_ch, | ||
986 | OMAP_DMA_DATA_BURST_16); | ||
987 | omap_set_dma_src_burst_mode(info->dma_ch, | ||
988 | OMAP_DMA_DATA_BURST_16); | ||
989 | |||
990 | info->nand.read_buf = omap_read_buf_dma_pref; | ||
991 | info->nand.write_buf = omap_write_buf_dma_pref; | ||
992 | } | ||
993 | } | ||
994 | } else { | ||
995 | if (info->nand.options & NAND_BUSWIDTH_16) { | ||
996 | info->nand.read_buf = omap_read_buf16; | ||
997 | info->nand.write_buf = omap_write_buf16; | ||
998 | } else { | ||
999 | info->nand.read_buf = omap_read_buf8; | ||
1000 | info->nand.write_buf = omap_write_buf8; | ||
1001 | } | ||
1002 | } | ||
1003 | info->nand.verify_buf = omap_verify_buf; | ||
1004 | |||
692 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC | 1005 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC |
693 | info->nand.ecc.bytes = 3; | 1006 | info->nand.ecc.bytes = 3; |
694 | info->nand.ecc.size = 512; | 1007 | info->nand.ecc.size = 512; |
@@ -744,9 +1057,12 @@ static int omap_nand_remove(struct platform_device *pdev) | |||
744 | struct omap_nand_info *info = mtd->priv; | 1057 | struct omap_nand_info *info = mtd->priv; |
745 | 1058 | ||
746 | platform_set_drvdata(pdev, NULL); | 1059 | platform_set_drvdata(pdev, NULL); |
1060 | if (use_dma) | ||
1061 | omap_free_dma(info->dma_ch); | ||
1062 | |||
747 | /* Release NAND device, its internal structures and partitions */ | 1063 | /* Release NAND device, its internal structures and partitions */ |
748 | nand_release(&info->mtd); | 1064 | nand_release(&info->mtd); |
749 | iounmap(info->nand.IO_ADDR_R); | 1065 | iounmap(info->nand_pref_fifo_add); |
750 | kfree(&info->mtd); | 1066 | kfree(&info->mtd); |
751 | return 0; | 1067 | return 0; |
752 | } | 1068 | } |
@@ -763,6 +1079,15 @@ static struct platform_driver omap_nand_driver = { | |||
763 | static int __init omap_nand_init(void) | 1079 | static int __init omap_nand_init(void) |
764 | { | 1080 | { |
765 | printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); | 1081 | printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); |
1082 | |||
1083 | /* This check is required if driver is being | ||
1084 | * loaded run time as a module | ||
1085 | */ | ||
1086 | if ((1 == use_dma) && (0 == use_prefetch)) { | ||
1087 | printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 " | ||
1088 | "without use_prefetch'. Prefetch will not be" | ||
1089 | " used in either mode (mpu or dma)\n"); | ||
1090 | } | ||
766 | return platform_driver_register(&omap_nand_driver); | 1091 | return platform_driver_register(&omap_nand_driver); |
767 | } | 1092 | } |
768 | 1093 | ||
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 0d9d4bc9c762..f59c07427af3 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c | |||
@@ -171,7 +171,6 @@ static int __devexit orion_nand_remove(struct platform_device *pdev) | |||
171 | } | 171 | } |
172 | 172 | ||
173 | static struct platform_driver orion_nand_driver = { | 173 | static struct platform_driver orion_nand_driver = { |
174 | .probe = orion_nand_probe, | ||
175 | .remove = __devexit_p(orion_nand_remove), | 174 | .remove = __devexit_p(orion_nand_remove), |
176 | .driver = { | 175 | .driver = { |
177 | .name = "orion_nand", | 176 | .name = "orion_nand", |
@@ -181,7 +180,7 @@ static struct platform_driver orion_nand_driver = { | |||
181 | 180 | ||
182 | static int __init orion_nand_init(void) | 181 | static int __init orion_nand_init(void) |
183 | { | 182 | { |
184 | return platform_driver_register(&orion_nand_driver); | 183 | return platform_driver_probe(&orion_nand_driver, orion_nand_probe); |
185 | } | 184 | } |
186 | 185 | ||
187 | static void __exit orion_nand_exit(void) | 186 | static void __exit orion_nand_exit(void) |
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 30a8ce6d3e69..6ea520ae2410 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c | |||
@@ -102,6 +102,7 @@ enum { | |||
102 | ERR_SENDCMD = -2, | 102 | ERR_SENDCMD = -2, |
103 | ERR_DBERR = -3, | 103 | ERR_DBERR = -3, |
104 | ERR_BBERR = -4, | 104 | ERR_BBERR = -4, |
105 | ERR_SBERR = -5, | ||
105 | }; | 106 | }; |
106 | 107 | ||
107 | enum { | 108 | enum { |
@@ -564,11 +565,13 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) | |||
564 | 565 | ||
565 | status = nand_readl(info, NDSR); | 566 | status = nand_readl(info, NDSR); |
566 | 567 | ||
567 | if (status & (NDSR_RDDREQ | NDSR_DBERR)) { | 568 | if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) { |
568 | if (status & NDSR_DBERR) | 569 | if (status & NDSR_DBERR) |
569 | info->retcode = ERR_DBERR; | 570 | info->retcode = ERR_DBERR; |
571 | else if (status & NDSR_SBERR) | ||
572 | info->retcode = ERR_SBERR; | ||
570 | 573 | ||
571 | disable_int(info, NDSR_RDDREQ | NDSR_DBERR); | 574 | disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); |
572 | 575 | ||
573 | if (info->use_dma) { | 576 | if (info->use_dma) { |
574 | info->state = STATE_DMA_READING; | 577 | info->state = STATE_DMA_READING; |
@@ -670,7 +673,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | |||
670 | if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) | 673 | if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) |
671 | break; | 674 | break; |
672 | 675 | ||
673 | pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); | 676 | pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); |
674 | 677 | ||
675 | /* We only are OOB, so if the data has error, does not matter */ | 678 | /* We only are OOB, so if the data has error, does not matter */ |
676 | if (info->retcode == ERR_DBERR) | 679 | if (info->retcode == ERR_DBERR) |
@@ -687,7 +690,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | |||
687 | if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) | 690 | if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) |
688 | break; | 691 | break; |
689 | 692 | ||
690 | pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); | 693 | pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); |
691 | 694 | ||
692 | if (info->retcode == ERR_DBERR) { | 695 | if (info->retcode == ERR_DBERR) { |
693 | /* for blank page (all 0xff), HW will calculate its ECC as | 696 | /* for blank page (all 0xff), HW will calculate its ECC as |
@@ -861,8 +864,12 @@ static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd, | |||
861 | * consider it as a ecc error which will tell the caller the | 864 | * consider it as a ecc error which will tell the caller the |
862 | * read fail We have distinguish all the errors, but the | 865 | * read fail We have distinguish all the errors, but the |
863 | * nand_read_ecc only check this function return value | 866 | * nand_read_ecc only check this function return value |
867 | * | ||
868 | * Corrected (single-bit) errors must also be noted. | ||
864 | */ | 869 | */ |
865 | if (info->retcode != ERR_NONE) | 870 | if (info->retcode == ERR_SBERR) |
871 | return 1; | ||
872 | else if (info->retcode != ERR_NONE) | ||
866 | return -1; | 873 | return -1; |
867 | 874 | ||
868 | return 0; | 875 | return 0; |
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 2bc896623e2d..02bef21f2e4b 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c | |||
@@ -329,7 +329,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va | |||
329 | } | 329 | } |
330 | 330 | ||
331 | static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | 331 | static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, |
332 | uint8_t *buf) | 332 | uint8_t *buf, int page) |
333 | { | 333 | { |
334 | int i, eccsize = chip->ecc.size; | 334 | int i, eccsize = chip->ecc.size; |
335 | int eccbytes = chip->ecc.bytes; | 335 | int eccbytes = chip->ecc.bytes; |
@@ -857,7 +857,6 @@ static int __exit flctl_remove(struct platform_device *pdev) | |||
857 | } | 857 | } |
858 | 858 | ||
859 | static struct platform_driver flctl_driver = { | 859 | static struct platform_driver flctl_driver = { |
860 | .probe = flctl_probe, | ||
861 | .remove = flctl_remove, | 860 | .remove = flctl_remove, |
862 | .driver = { | 861 | .driver = { |
863 | .name = "sh_flctl", | 862 | .name = "sh_flctl", |
@@ -867,7 +866,7 @@ static struct platform_driver flctl_driver = { | |||
867 | 866 | ||
868 | static int __init flctl_nand_init(void) | 867 | static int __init flctl_nand_init(void) |
869 | { | 868 | { |
870 | return platform_driver_register(&flctl_driver); | 869 | return platform_driver_probe(&flctl_driver, flctl_probe); |
871 | } | 870 | } |
872 | 871 | ||
873 | static void __exit flctl_nand_cleanup(void) | 872 | static void __exit flctl_nand_cleanup(void) |
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index daa6a4c3b8ce..92c73344a669 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c | |||
@@ -301,6 +301,21 @@ static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, | |||
301 | return 0; | 301 | return 0; |
302 | } | 302 | } |
303 | 303 | ||
304 | static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, | ||
305 | unsigned char *read_ecc, unsigned char *calc_ecc) | ||
306 | { | ||
307 | int r0, r1; | ||
308 | |||
309 | /* assume ecc.size = 512 and ecc.bytes = 6 */ | ||
310 | r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256); | ||
311 | if (r0 < 0) | ||
312 | return r0; | ||
313 | r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256); | ||
314 | if (r1 < 0) | ||
315 | return r1; | ||
316 | return r0 + r1; | ||
317 | } | ||
318 | |||
304 | static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) | 319 | static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) |
305 | { | 320 | { |
306 | struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; | 321 | struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; |
@@ -424,7 +439,7 @@ static int tmio_probe(struct platform_device *dev) | |||
424 | nand_chip->ecc.bytes = 6; | 439 | nand_chip->ecc.bytes = 6; |
425 | nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; | 440 | nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; |
426 | nand_chip->ecc.calculate = tmio_nand_calculate_ecc; | 441 | nand_chip->ecc.calculate = tmio_nand_calculate_ecc; |
427 | nand_chip->ecc.correct = nand_correct_data; | 442 | nand_chip->ecc.correct = tmio_nand_correct_data; |
428 | 443 | ||
429 | if (data) | 444 | if (data) |
430 | nand_chip->badblock_pattern = data->badblock_pattern; | 445 | nand_chip->badblock_pattern = data->badblock_pattern; |
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 488088eff2ca..73af8324d0d0 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c | |||
@@ -189,18 +189,43 @@ static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, | |||
189 | uint8_t *ecc_code) | 189 | uint8_t *ecc_code) |
190 | { | 190 | { |
191 | struct platform_device *dev = mtd_to_platdev(mtd); | 191 | struct platform_device *dev = mtd_to_platdev(mtd); |
192 | struct nand_chip *chip = mtd->priv; | ||
193 | int eccbytes; | ||
192 | u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); | 194 | u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); |
193 | 195 | ||
194 | mcr &= ~TXX9_NDFMCR_ECC_ALL; | 196 | mcr &= ~TXX9_NDFMCR_ECC_ALL; |
195 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); | 197 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); |
196 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); | 198 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); |
197 | ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); | 199 | for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) { |
198 | ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); | 200 | ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); |
199 | ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); | 201 | ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); |
202 | ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); | ||
203 | ecc_code += 3; | ||
204 | } | ||
200 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); | 205 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); |
201 | return 0; | 206 | return 0; |
202 | } | 207 | } |
203 | 208 | ||
209 | static int txx9ndfmc_correct_data(struct mtd_info *mtd, unsigned char *buf, | ||
210 | unsigned char *read_ecc, unsigned char *calc_ecc) | ||
211 | { | ||
212 | struct nand_chip *chip = mtd->priv; | ||
213 | int eccsize; | ||
214 | int corrected = 0; | ||
215 | int stat; | ||
216 | |||
217 | for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) { | ||
218 | stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256); | ||
219 | if (stat < 0) | ||
220 | return stat; | ||
221 | corrected += stat; | ||
222 | buf += 256; | ||
223 | read_ecc += 3; | ||
224 | calc_ecc += 3; | ||
225 | } | ||
226 | return corrected; | ||
227 | } | ||
228 | |||
204 | static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) | 229 | static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) |
205 | { | 230 | { |
206 | struct platform_device *dev = mtd_to_platdev(mtd); | 231 | struct platform_device *dev = mtd_to_platdev(mtd); |
@@ -244,6 +269,22 @@ static void txx9ndfmc_initialize(struct platform_device *dev) | |||
244 | #define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ | 269 | #define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ |
245 | DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) | 270 | DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) |
246 | 271 | ||
272 | static int txx9ndfmc_nand_scan(struct mtd_info *mtd) | ||
273 | { | ||
274 | struct nand_chip *chip = mtd->priv; | ||
275 | int ret; | ||
276 | |||
277 | ret = nand_scan_ident(mtd, 1); | ||
278 | if (!ret) { | ||
279 | if (mtd->writesize >= 512) { | ||
280 | chip->ecc.size = mtd->writesize; | ||
281 | chip->ecc.bytes = 3 * (mtd->writesize / 256); | ||
282 | } | ||
283 | ret = nand_scan_tail(mtd); | ||
284 | } | ||
285 | return ret; | ||
286 | } | ||
287 | |||
247 | static int __init txx9ndfmc_probe(struct platform_device *dev) | 288 | static int __init txx9ndfmc_probe(struct platform_device *dev) |
248 | { | 289 | { |
249 | struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; | 290 | struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; |
@@ -321,9 +362,10 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) | |||
321 | chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; | 362 | chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; |
322 | chip->dev_ready = txx9ndfmc_dev_ready; | 363 | chip->dev_ready = txx9ndfmc_dev_ready; |
323 | chip->ecc.calculate = txx9ndfmc_calculate_ecc; | 364 | chip->ecc.calculate = txx9ndfmc_calculate_ecc; |
324 | chip->ecc.correct = nand_correct_data; | 365 | chip->ecc.correct = txx9ndfmc_correct_data; |
325 | chip->ecc.hwctl = txx9ndfmc_enable_hwecc; | 366 | chip->ecc.hwctl = txx9ndfmc_enable_hwecc; |
326 | chip->ecc.mode = NAND_ECC_HW; | 367 | chip->ecc.mode = NAND_ECC_HW; |
368 | /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ | ||
327 | chip->ecc.size = 256; | 369 | chip->ecc.size = 256; |
328 | chip->ecc.bytes = 3; | 370 | chip->ecc.bytes = 3; |
329 | chip->chip_delay = 100; | 371 | chip->chip_delay = 100; |
@@ -349,7 +391,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) | |||
349 | if (plat->wide_mask & (1 << i)) | 391 | if (plat->wide_mask & (1 << i)) |
350 | chip->options |= NAND_BUSWIDTH_16; | 392 | chip->options |= NAND_BUSWIDTH_16; |
351 | 393 | ||
352 | if (nand_scan(mtd, 1)) { | 394 | if (txx9ndfmc_nand_scan(mtd)) { |
353 | kfree(txx9_priv->mtdname); | 395 | kfree(txx9_priv->mtdname); |
354 | kfree(txx9_priv); | 396 | kfree(txx9_priv); |
355 | continue; | 397 | continue; |
diff --git a/drivers/mtd/nand/w90p910_nand.c b/drivers/mtd/nand/w90p910_nand.c new file mode 100644 index 000000000000..7680e731348a --- /dev/null +++ b/drivers/mtd/nand/w90p910_nand.c | |||
@@ -0,0 +1,382 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2009 Nuvoton technology corporation. | ||
3 | * | ||
4 | * Wan ZongShun <mcuos.com@gmail.com> | ||
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 as published by | ||
8 | * the Free Software Foundation;version 2 of the License. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/slab.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/err.h> | ||
21 | |||
22 | #include <linux/mtd/mtd.h> | ||
23 | #include <linux/mtd/nand.h> | ||
24 | #include <linux/mtd/partitions.h> | ||
25 | |||
26 | #define REG_FMICSR 0x00 | ||
27 | #define REG_SMCSR 0xa0 | ||
28 | #define REG_SMISR 0xac | ||
29 | #define REG_SMCMD 0xb0 | ||
30 | #define REG_SMADDR 0xb4 | ||
31 | #define REG_SMDATA 0xb8 | ||
32 | |||
33 | #define RESET_FMI 0x01 | ||
34 | #define NAND_EN 0x08 | ||
35 | #define READYBUSY (0x01 << 18) | ||
36 | |||
37 | #define SWRST 0x01 | ||
38 | #define PSIZE (0x01 << 3) | ||
39 | #define DMARWEN (0x03 << 1) | ||
40 | #define BUSWID (0x01 << 4) | ||
41 | #define ECC4EN (0x01 << 5) | ||
42 | #define WP (0x01 << 24) | ||
43 | #define NANDCS (0x01 << 25) | ||
44 | #define ENDADDR (0x01 << 31) | ||
45 | |||
46 | #define read_data_reg(dev) \ | ||
47 | __raw_readl((dev)->reg + REG_SMDATA) | ||
48 | |||
49 | #define write_data_reg(dev, val) \ | ||
50 | __raw_writel((val), (dev)->reg + REG_SMDATA) | ||
51 | |||
52 | #define write_cmd_reg(dev, val) \ | ||
53 | __raw_writel((val), (dev)->reg + REG_SMCMD) | ||
54 | |||
55 | #define write_addr_reg(dev, val) \ | ||
56 | __raw_writel((val), (dev)->reg + REG_SMADDR) | ||
57 | |||
58 | struct w90p910_nand { | ||
59 | struct mtd_info mtd; | ||
60 | struct nand_chip chip; | ||
61 | void __iomem *reg; | ||
62 | struct clk *clk; | ||
63 | spinlock_t lock; | ||
64 | }; | ||
65 | |||
66 | static const struct mtd_partition partitions[] = { | ||
67 | { | ||
68 | .name = "NAND FS 0", | ||
69 | .offset = 0, | ||
70 | .size = 8 * 1024 * 1024 | ||
71 | }, | ||
72 | { | ||
73 | .name = "NAND FS 1", | ||
74 | .offset = MTDPART_OFS_APPEND, | ||
75 | .size = MTDPART_SIZ_FULL | ||
76 | } | ||
77 | }; | ||
78 | |||
79 | static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd) | ||
80 | { | ||
81 | unsigned char ret; | ||
82 | struct w90p910_nand *nand; | ||
83 | |||
84 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
85 | |||
86 | ret = (unsigned char)read_data_reg(nand); | ||
87 | |||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | static void w90p910_nand_read_buf(struct mtd_info *mtd, | ||
92 | unsigned char *buf, int len) | ||
93 | { | ||
94 | int i; | ||
95 | struct w90p910_nand *nand; | ||
96 | |||
97 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
98 | |||
99 | for (i = 0; i < len; i++) | ||
100 | buf[i] = (unsigned char)read_data_reg(nand); | ||
101 | } | ||
102 | |||
103 | static void w90p910_nand_write_buf(struct mtd_info *mtd, | ||
104 | const unsigned char *buf, int len) | ||
105 | { | ||
106 | int i; | ||
107 | struct w90p910_nand *nand; | ||
108 | |||
109 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
110 | |||
111 | for (i = 0; i < len; i++) | ||
112 | write_data_reg(nand, buf[i]); | ||
113 | } | ||
114 | |||
115 | static int w90p910_verify_buf(struct mtd_info *mtd, | ||
116 | const unsigned char *buf, int len) | ||
117 | { | ||
118 | int i; | ||
119 | struct w90p910_nand *nand; | ||
120 | |||
121 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
122 | |||
123 | for (i = 0; i < len; i++) { | ||
124 | if (buf[i] != (unsigned char)read_data_reg(nand)) | ||
125 | return -EFAULT; | ||
126 | } | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int w90p910_check_rb(struct w90p910_nand *nand) | ||
132 | { | ||
133 | unsigned int val; | ||
134 | spin_lock(&nand->lock); | ||
135 | val = __raw_readl(REG_SMISR); | ||
136 | val &= READYBUSY; | ||
137 | spin_unlock(&nand->lock); | ||
138 | |||
139 | return val; | ||
140 | } | ||
141 | |||
142 | static int w90p910_nand_devready(struct mtd_info *mtd) | ||
143 | { | ||
144 | struct w90p910_nand *nand; | ||
145 | int ready; | ||
146 | |||
147 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
148 | |||
149 | ready = (w90p910_check_rb(nand)) ? 1 : 0; | ||
150 | return ready; | ||
151 | } | ||
152 | |||
153 | static void w90p910_nand_command_lp(struct mtd_info *mtd, | ||
154 | unsigned int command, int column, int page_addr) | ||
155 | { | ||
156 | register struct nand_chip *chip = mtd->priv; | ||
157 | struct w90p910_nand *nand; | ||
158 | |||
159 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
160 | |||
161 | if (command == NAND_CMD_READOOB) { | ||
162 | column += mtd->writesize; | ||
163 | command = NAND_CMD_READ0; | ||
164 | } | ||
165 | |||
166 | write_cmd_reg(nand, command & 0xff); | ||
167 | |||
168 | if (column != -1 || page_addr != -1) { | ||
169 | |||
170 | if (column != -1) { | ||
171 | if (chip->options & NAND_BUSWIDTH_16) | ||
172 | column >>= 1; | ||
173 | write_addr_reg(nand, column); | ||
174 | write_addr_reg(nand, column >> 8 | ENDADDR); | ||
175 | } | ||
176 | if (page_addr != -1) { | ||
177 | write_addr_reg(nand, page_addr); | ||
178 | |||
179 | if (chip->chipsize > (128 << 20)) { | ||
180 | write_addr_reg(nand, page_addr >> 8); | ||
181 | write_addr_reg(nand, page_addr >> 16 | ENDADDR); | ||
182 | } else { | ||
183 | write_addr_reg(nand, page_addr >> 8 | ENDADDR); | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | switch (command) { | ||
189 | case NAND_CMD_CACHEDPROG: | ||
190 | case NAND_CMD_PAGEPROG: | ||
191 | case NAND_CMD_ERASE1: | ||
192 | case NAND_CMD_ERASE2: | ||
193 | case NAND_CMD_SEQIN: | ||
194 | case NAND_CMD_RNDIN: | ||
195 | case NAND_CMD_STATUS: | ||
196 | case NAND_CMD_DEPLETE1: | ||
197 | return; | ||
198 | |||
199 | case NAND_CMD_STATUS_ERROR: | ||
200 | case NAND_CMD_STATUS_ERROR0: | ||
201 | case NAND_CMD_STATUS_ERROR1: | ||
202 | case NAND_CMD_STATUS_ERROR2: | ||
203 | case NAND_CMD_STATUS_ERROR3: | ||
204 | udelay(chip->chip_delay); | ||
205 | return; | ||
206 | |||
207 | case NAND_CMD_RESET: | ||
208 | if (chip->dev_ready) | ||
209 | break; | ||
210 | udelay(chip->chip_delay); | ||
211 | |||
212 | write_cmd_reg(nand, NAND_CMD_STATUS); | ||
213 | write_cmd_reg(nand, command); | ||
214 | |||
215 | while (!w90p910_check_rb(nand)) | ||
216 | ; | ||
217 | |||
218 | return; | ||
219 | |||
220 | case NAND_CMD_RNDOUT: | ||
221 | write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); | ||
222 | return; | ||
223 | |||
224 | case NAND_CMD_READ0: | ||
225 | |||
226 | write_cmd_reg(nand, NAND_CMD_READSTART); | ||
227 | default: | ||
228 | |||
229 | if (!chip->dev_ready) { | ||
230 | udelay(chip->chip_delay); | ||
231 | return; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | /* Apply this short delay always to ensure that we do wait tWB in | ||
236 | * any case on any machine. */ | ||
237 | ndelay(100); | ||
238 | |||
239 | while (!chip->dev_ready(mtd)) | ||
240 | ; | ||
241 | } | ||
242 | |||
243 | |||
244 | static void w90p910_nand_enable(struct w90p910_nand *nand) | ||
245 | { | ||
246 | unsigned int val; | ||
247 | spin_lock(&nand->lock); | ||
248 | __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); | ||
249 | |||
250 | val = __raw_readl(nand->reg + REG_FMICSR); | ||
251 | |||
252 | if (!(val & NAND_EN)) | ||
253 | __raw_writel(val | NAND_EN, REG_FMICSR); | ||
254 | |||
255 | val = __raw_readl(nand->reg + REG_SMCSR); | ||
256 | |||
257 | val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); | ||
258 | val |= WP; | ||
259 | |||
260 | __raw_writel(val, nand->reg + REG_SMCSR); | ||
261 | |||
262 | spin_unlock(&nand->lock); | ||
263 | } | ||
264 | |||
265 | static int __devinit w90p910_nand_probe(struct platform_device *pdev) | ||
266 | { | ||
267 | struct w90p910_nand *w90p910_nand; | ||
268 | struct nand_chip *chip; | ||
269 | int retval; | ||
270 | struct resource *res; | ||
271 | |||
272 | retval = 0; | ||
273 | |||
274 | w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL); | ||
275 | if (!w90p910_nand) | ||
276 | return -ENOMEM; | ||
277 | chip = &(w90p910_nand->chip); | ||
278 | |||
279 | w90p910_nand->mtd.priv = chip; | ||
280 | w90p910_nand->mtd.owner = THIS_MODULE; | ||
281 | spin_lock_init(&w90p910_nand->lock); | ||
282 | |||
283 | w90p910_nand->clk = clk_get(&pdev->dev, NULL); | ||
284 | if (IS_ERR(w90p910_nand->clk)) { | ||
285 | retval = -ENOENT; | ||
286 | goto fail1; | ||
287 | } | ||
288 | clk_enable(w90p910_nand->clk); | ||
289 | |||
290 | chip->cmdfunc = w90p910_nand_command_lp; | ||
291 | chip->dev_ready = w90p910_nand_devready; | ||
292 | chip->read_byte = w90p910_nand_read_byte; | ||
293 | chip->write_buf = w90p910_nand_write_buf; | ||
294 | chip->read_buf = w90p910_nand_read_buf; | ||
295 | chip->verify_buf = w90p910_verify_buf; | ||
296 | chip->chip_delay = 50; | ||
297 | chip->options = 0; | ||
298 | chip->ecc.mode = NAND_ECC_SOFT; | ||
299 | |||
300 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
301 | if (!res) { | ||
302 | retval = -ENXIO; | ||
303 | goto fail1; | ||
304 | } | ||
305 | |||
306 | if (!request_mem_region(res->start, resource_size(res), pdev->name)) { | ||
307 | retval = -EBUSY; | ||
308 | goto fail1; | ||
309 | } | ||
310 | |||
311 | w90p910_nand->reg = ioremap(res->start, resource_size(res)); | ||
312 | if (!w90p910_nand->reg) { | ||
313 | retval = -ENOMEM; | ||
314 | goto fail2; | ||
315 | } | ||
316 | |||
317 | w90p910_nand_enable(w90p910_nand); | ||
318 | |||
319 | if (nand_scan(&(w90p910_nand->mtd), 1)) { | ||
320 | retval = -ENXIO; | ||
321 | goto fail3; | ||
322 | } | ||
323 | |||
324 | add_mtd_partitions(&(w90p910_nand->mtd), partitions, | ||
325 | ARRAY_SIZE(partitions)); | ||
326 | |||
327 | platform_set_drvdata(pdev, w90p910_nand); | ||
328 | |||
329 | return retval; | ||
330 | |||
331 | fail3: iounmap(w90p910_nand->reg); | ||
332 | fail2: release_mem_region(res->start, resource_size(res)); | ||
333 | fail1: kfree(w90p910_nand); | ||
334 | return retval; | ||
335 | } | ||
336 | |||
337 | static int __devexit w90p910_nand_remove(struct platform_device *pdev) | ||
338 | { | ||
339 | struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev); | ||
340 | struct resource *res; | ||
341 | |||
342 | iounmap(w90p910_nand->reg); | ||
343 | |||
344 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
345 | release_mem_region(res->start, resource_size(res)); | ||
346 | |||
347 | clk_disable(w90p910_nand->clk); | ||
348 | clk_put(w90p910_nand->clk); | ||
349 | |||
350 | kfree(w90p910_nand); | ||
351 | |||
352 | platform_set_drvdata(pdev, NULL); | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static struct platform_driver w90p910_nand_driver = { | ||
358 | .probe = w90p910_nand_probe, | ||
359 | .remove = __devexit_p(w90p910_nand_remove), | ||
360 | .driver = { | ||
361 | .name = "w90p910-fmi", | ||
362 | .owner = THIS_MODULE, | ||
363 | }, | ||
364 | }; | ||
365 | |||
366 | static int __init w90p910_nand_init(void) | ||
367 | { | ||
368 | return platform_driver_register(&w90p910_nand_driver); | ||
369 | } | ||
370 | |||
371 | static void __exit w90p910_nand_exit(void) | ||
372 | { | ||
373 | platform_driver_unregister(&w90p910_nand_driver); | ||
374 | } | ||
375 | |||
376 | module_init(w90p910_nand_init); | ||
377 | module_exit(w90p910_nand_exit); | ||
378 | |||
379 | MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); | ||
380 | MODULE_DESCRIPTION("w90p910 nand driver!"); | ||
381 | MODULE_LICENSE("GPL"); | ||
382 | MODULE_ALIAS("platform:w90p910-fmi"); | ||
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 3e164f0c9295..62d6a78c4eee 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c | |||
@@ -46,21 +46,12 @@ int __devinit of_mtd_parse_partitions(struct device *dev, | |||
46 | const u32 *reg; | 46 | const u32 *reg; |
47 | int len; | 47 | int len; |
48 | 48 | ||
49 | /* check if this is a partition node */ | 49 | reg = of_get_property(pp, "reg", &len); |
50 | partname = of_get_property(pp, "name", &len); | 50 | if (!reg) { |
51 | if (strcmp(partname, "partition") != 0) { | ||
52 | nr_parts--; | 51 | nr_parts--; |
53 | continue; | 52 | continue; |
54 | } | 53 | } |
55 | 54 | ||
56 | reg = of_get_property(pp, "reg", &len); | ||
57 | if (!reg || (len != 2 * sizeof(u32))) { | ||
58 | of_node_put(pp); | ||
59 | dev_err(dev, "Invalid 'reg' on %s\n", node->full_name); | ||
60 | kfree(*pparts); | ||
61 | *pparts = NULL; | ||
62 | return -EINVAL; | ||
63 | } | ||
64 | (*pparts)[i].offset = reg[0]; | 55 | (*pparts)[i].offset = reg[0]; |
65 | (*pparts)[i].size = reg[1]; | 56 | (*pparts)[i].size = reg[1]; |
66 | 57 | ||
@@ -75,6 +66,14 @@ int __devinit of_mtd_parse_partitions(struct device *dev, | |||
75 | i++; | 66 | i++; |
76 | } | 67 | } |
77 | 68 | ||
69 | if (!i) { | ||
70 | of_node_put(pp); | ||
71 | dev_err(dev, "No valid partition found on %s\n", node->full_name); | ||
72 | kfree(*pparts); | ||
73 | *pparts = NULL; | ||
74 | return -EINVAL; | ||
75 | } | ||
76 | |||
78 | return nr_parts; | 77 | return nr_parts; |
79 | } | 78 | } |
80 | EXPORT_SYMBOL(of_mtd_parse_partitions); | 79 | EXPORT_SYMBOL(of_mtd_parse_partitions); |
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 79fa79e8f8de..a38f580c2bb3 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig | |||
@@ -5,6 +5,7 @@ | |||
5 | menuconfig MTD_ONENAND | 5 | menuconfig MTD_ONENAND |
6 | tristate "OneNAND Device Support" | 6 | tristate "OneNAND Device Support" |
7 | depends on MTD | 7 | depends on MTD |
8 | select MTD_PARTITIONS | ||
8 | help | 9 | help |
9 | This enables support for accessing all type of OneNAND flash | 10 | This enables support for accessing all type of OneNAND flash |
10 | devices. For further information see | 11 | devices. For further information see |
@@ -23,7 +24,6 @@ config MTD_ONENAND_VERIFY_WRITE | |||
23 | 24 | ||
24 | config MTD_ONENAND_GENERIC | 25 | config MTD_ONENAND_GENERIC |
25 | tristate "OneNAND Flash device via platform device driver" | 26 | tristate "OneNAND Flash device via platform device driver" |
26 | depends on ARM | ||
27 | help | 27 | help |
28 | Support for OneNAND flash via platform device driver. | 28 | Support for OneNAND flash via platform device driver. |
29 | 29 | ||
@@ -66,7 +66,6 @@ config MTD_ONENAND_2X_PROGRAM | |||
66 | 66 | ||
67 | config MTD_ONENAND_SIM | 67 | config MTD_ONENAND_SIM |
68 | tristate "OneNAND simulator support" | 68 | tristate "OneNAND simulator support" |
69 | depends on MTD_PARTITIONS | ||
70 | help | 69 | help |
71 | The simulator may simulate various OneNAND flash chips for the | 70 | The simulator may simulate various OneNAND flash chips for the |
72 | OneNAND MTD layer. | 71 | OneNAND MTD layer. |
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 3a496c33fb52..e78914938c5c 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c | |||
@@ -19,12 +19,16 @@ | |||
19 | #include <linux/mtd/mtd.h> | 19 | #include <linux/mtd/mtd.h> |
20 | #include <linux/mtd/onenand.h> | 20 | #include <linux/mtd/onenand.h> |
21 | #include <linux/mtd/partitions.h> | 21 | #include <linux/mtd/partitions.h> |
22 | |||
23 | #include <asm/io.h> | 22 | #include <asm/io.h> |
24 | #include <asm/mach/flash.h> | ||
25 | |||
26 | #define DRIVER_NAME "onenand" | ||
27 | 23 | ||
24 | /* | ||
25 | * Note: Driver name and platform data format have been updated! | ||
26 | * | ||
27 | * This version of the driver is named "onenand-flash" and takes struct | ||
28 | * onenand_platform_data as platform data. The old ARM-specific version | ||
29 | * with the name "onenand" used to take struct flash_platform_data. | ||
30 | */ | ||
31 | #define DRIVER_NAME "onenand-flash" | ||
28 | 32 | ||
29 | #ifdef CONFIG_MTD_PARTITIONS | 33 | #ifdef CONFIG_MTD_PARTITIONS |
30 | static const char *part_probes[] = { "cmdlinepart", NULL, }; | 34 | static const char *part_probes[] = { "cmdlinepart", NULL, }; |
@@ -39,16 +43,16 @@ struct onenand_info { | |||
39 | static int __devinit generic_onenand_probe(struct platform_device *pdev) | 43 | static int __devinit generic_onenand_probe(struct platform_device *pdev) |
40 | { | 44 | { |
41 | struct onenand_info *info; | 45 | struct onenand_info *info; |
42 | struct flash_platform_data *pdata = pdev->dev.platform_data; | 46 | struct onenand_platform_data *pdata = pdev->dev.platform_data; |
43 | struct resource *res = pdev->resource; | 47 | struct resource *res = pdev->resource; |
44 | unsigned long size = res->end - res->start + 1; | 48 | unsigned long size = resource_size(res); |
45 | int err; | 49 | int err; |
46 | 50 | ||
47 | info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); | 51 | info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); |
48 | if (!info) | 52 | if (!info) |
49 | return -ENOMEM; | 53 | return -ENOMEM; |
50 | 54 | ||
51 | if (!request_mem_region(res->start, size, pdev->dev.driver->name)) { | 55 | if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) { |
52 | err = -EBUSY; | 56 | err = -EBUSY; |
53 | goto out_free_info; | 57 | goto out_free_info; |
54 | } | 58 | } |
@@ -59,7 +63,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) | |||
59 | goto out_release_mem_region; | 63 | goto out_release_mem_region; |
60 | } | 64 | } |
61 | 65 | ||
62 | info->onenand.mmcontrol = pdata->mmcontrol; | 66 | info->onenand.mmcontrol = pdata ? pdata->mmcontrol : 0; |
63 | info->onenand.irq = platform_get_irq(pdev, 0); | 67 | info->onenand.irq = platform_get_irq(pdev, 0); |
64 | 68 | ||
65 | info->mtd.name = dev_name(&pdev->dev); | 69 | info->mtd.name = dev_name(&pdev->dev); |
@@ -75,7 +79,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) | |||
75 | err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); | 79 | err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); |
76 | if (err > 0) | 80 | if (err > 0) |
77 | add_mtd_partitions(&info->mtd, info->parts, err); | 81 | add_mtd_partitions(&info->mtd, info->parts, err); |
78 | else if (err <= 0 && pdata->parts) | 82 | else if (err <= 0 && pdata && pdata->parts) |
79 | add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); | 83 | add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); |
80 | else | 84 | else |
81 | #endif | 85 | #endif |
@@ -99,7 +103,7 @@ static int __devexit generic_onenand_remove(struct platform_device *pdev) | |||
99 | { | 103 | { |
100 | struct onenand_info *info = platform_get_drvdata(pdev); | 104 | struct onenand_info *info = platform_get_drvdata(pdev); |
101 | struct resource *res = pdev->resource; | 105 | struct resource *res = pdev->resource; |
102 | unsigned long size = res->end - res->start + 1; | 106 | unsigned long size = resource_size(res); |
103 | 107 | ||
104 | platform_set_drvdata(pdev, NULL); | 108 | platform_set_drvdata(pdev, NULL); |
105 | 109 | ||
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 6e829095ea9d..ff66e4330aa7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -1191,7 +1191,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, | |||
1191 | /* | 1191 | /* |
1192 | * Chip boundary handling in DDP | 1192 | * Chip boundary handling in DDP |
1193 | * Now we issued chip 1 read and pointed chip 1 | 1193 | * Now we issued chip 1 read and pointed chip 1 |
1194 | * bufferam so we have to point chip 0 bufferam. | 1194 | * bufferram so we have to point chip 0 bufferram. |
1195 | */ | 1195 | */ |
1196 | if (ONENAND_IS_DDP(this) && | 1196 | if (ONENAND_IS_DDP(this) && |
1197 | unlikely(from == (this->chipsize >> 1))) { | 1197 | unlikely(from == (this->chipsize >> 1))) { |
@@ -1867,8 +1867,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, | |||
1867 | ONENAND_SET_NEXT_BUFFERRAM(this); | 1867 | ONENAND_SET_NEXT_BUFFERRAM(this); |
1868 | 1868 | ||
1869 | /* | 1869 | /* |
1870 | * 2 PLANE, MLC, and Flex-OneNAND doesn't support | 1870 | * 2 PLANE, MLC, and Flex-OneNAND do not support |
1871 | * write-while-programe feature. | 1871 | * write-while-program feature. |
1872 | */ | 1872 | */ |
1873 | if (!ONENAND_IS_2PLANE(this) && !first) { | 1873 | if (!ONENAND_IS_2PLANE(this) && !first) { |
1874 | ONENAND_SET_PREV_BUFFERRAM(this); | 1874 | ONENAND_SET_PREV_BUFFERRAM(this); |
@@ -1879,7 +1879,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, | |||
1879 | onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); | 1879 | onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); |
1880 | if (ret) { | 1880 | if (ret) { |
1881 | written -= prevlen; | 1881 | written -= prevlen; |
1882 | printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); | 1882 | printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret); |
1883 | break; | 1883 | break; |
1884 | } | 1884 | } |
1885 | 1885 | ||
@@ -1905,7 +1905,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, | |||
1905 | /* In partial page write we don't update bufferram */ | 1905 | /* In partial page write we don't update bufferram */ |
1906 | onenand_update_bufferram(mtd, to, !ret && !subpage); | 1906 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
1907 | if (ret) { | 1907 | if (ret) { |
1908 | printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); | 1908 | printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret); |
1909 | break; | 1909 | break; |
1910 | } | 1910 | } |
1911 | 1911 | ||
@@ -2201,7 +2201,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
2201 | /* Grab the lock and see if the device is available */ | 2201 | /* Grab the lock and see if the device is available */ |
2202 | onenand_get_device(mtd, FL_ERASING); | 2202 | onenand_get_device(mtd, FL_ERASING); |
2203 | 2203 | ||
2204 | /* Loop throught the pages */ | 2204 | /* Loop through the blocks */ |
2205 | instr->state = MTD_ERASING; | 2205 | instr->state = MTD_ERASING; |
2206 | 2206 | ||
2207 | while (len) { | 2207 | while (len) { |
@@ -2328,7 +2328,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
2328 | if (bbm->bbt) | 2328 | if (bbm->bbt) |
2329 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); | 2329 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); |
2330 | 2330 | ||
2331 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 2331 | /* We write two bytes, so we don't have to mess with 16-bit access */ |
2332 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | 2332 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); |
2333 | /* FIXME : What to do when marking SLC block in partition | 2333 | /* FIXME : What to do when marking SLC block in partition |
2334 | * with MLC erasesize? For now, it is not advisable to | 2334 | * with MLC erasesize? For now, it is not advisable to |
@@ -2557,7 +2557,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) | |||
2557 | 2557 | ||
2558 | #ifdef CONFIG_MTD_ONENAND_OTP | 2558 | #ifdef CONFIG_MTD_ONENAND_OTP |
2559 | 2559 | ||
2560 | /* Interal OTP operation */ | 2560 | /* Internal OTP operation */ |
2561 | typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, | 2561 | typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, |
2562 | size_t *retlen, u_char *buf); | 2562 | size_t *retlen, u_char *buf); |
2563 | 2563 | ||
@@ -2921,7 +2921,7 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
2921 | this->options |= ONENAND_HAS_2PLANE; | 2921 | this->options |= ONENAND_HAS_2PLANE; |
2922 | 2922 | ||
2923 | case ONENAND_DEVICE_DENSITY_2Gb: | 2923 | case ONENAND_DEVICE_DENSITY_2Gb: |
2924 | /* 2Gb DDP don't have 2 plane */ | 2924 | /* 2Gb DDP does not have 2 plane */ |
2925 | if (!ONENAND_IS_DDP(this)) | 2925 | if (!ONENAND_IS_DDP(this)) |
2926 | this->options |= ONENAND_HAS_2PLANE; | 2926 | this->options |= ONENAND_HAS_2PLANE; |
2927 | this->options |= ONENAND_HAS_UNLOCK_ALL; | 2927 | this->options |= ONENAND_HAS_UNLOCK_ALL; |
@@ -3364,7 +3364,7 @@ static int onenand_probe(struct mtd_info *mtd) | |||
3364 | /* It's real page size */ | 3364 | /* It's real page size */ |
3365 | this->writesize = mtd->writesize; | 3365 | this->writesize = mtd->writesize; |
3366 | 3366 | ||
3367 | /* REVIST: Multichip handling */ | 3367 | /* REVISIT: Multichip handling */ |
3368 | 3368 | ||
3369 | if (FLEXONENAND(this)) | 3369 | if (FLEXONENAND(this)) |
3370 | flexonenand_get_size(mtd); | 3370 | flexonenand_get_size(mtd); |
diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c index a18e8d2f2557..5553cd4eab20 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/mtd_oobtest.c | |||
@@ -512,7 +512,7 @@ static int __init mtd_oobtest_init(void) | |||
512 | goto out; | 512 | goto out; |
513 | 513 | ||
514 | addr0 = 0; | 514 | addr0 = 0; |
515 | for (i = 0; bbt[i] && i < ebcnt; ++i) | 515 | for (i = 0; i < ebcnt && bbt[i]; ++i) |
516 | addr0 += mtd->erasesize; | 516 | addr0 += mtd->erasesize; |
517 | 517 | ||
518 | /* Attempt to write off end of OOB */ | 518 | /* Attempt to write off end of OOB */ |
diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 9648818b9e2c..103cac480fee 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c | |||
@@ -116,11 +116,11 @@ static int verify_eraseblock(int ebnum) | |||
116 | loff_t addr = ebnum * mtd->erasesize; | 116 | loff_t addr = ebnum * mtd->erasesize; |
117 | 117 | ||
118 | addr0 = 0; | 118 | addr0 = 0; |
119 | for (i = 0; bbt[i] && i < ebcnt; ++i) | 119 | for (i = 0; i < ebcnt && bbt[i]; ++i) |
120 | addr0 += mtd->erasesize; | 120 | addr0 += mtd->erasesize; |
121 | 121 | ||
122 | addrn = mtd->size; | 122 | addrn = mtd->size; |
123 | for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) | 123 | for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) |
124 | addrn -= mtd->erasesize; | 124 | addrn -= mtd->erasesize; |
125 | 125 | ||
126 | set_random_data(writebuf, mtd->erasesize); | 126 | set_random_data(writebuf, mtd->erasesize); |
@@ -219,11 +219,11 @@ static int crosstest(void) | |||
219 | memset(pp1, 0, pgsize * 4); | 219 | memset(pp1, 0, pgsize * 4); |
220 | 220 | ||
221 | addr0 = 0; | 221 | addr0 = 0; |
222 | for (i = 0; bbt[i] && i < ebcnt; ++i) | 222 | for (i = 0; i < ebcnt && bbt[i]; ++i) |
223 | addr0 += mtd->erasesize; | 223 | addr0 += mtd->erasesize; |
224 | 224 | ||
225 | addrn = mtd->size; | 225 | addrn = mtd->size; |
226 | for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) | 226 | for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) |
227 | addrn -= mtd->erasesize; | 227 | addrn -= mtd->erasesize; |
228 | 228 | ||
229 | /* Read 2nd-to-last page to pp1 */ | 229 | /* Read 2nd-to-last page to pp1 */ |
@@ -317,7 +317,7 @@ static int erasecrosstest(void) | |||
317 | 317 | ||
318 | ebnum = 0; | 318 | ebnum = 0; |
319 | addr0 = 0; | 319 | addr0 = 0; |
320 | for (i = 0; bbt[i] && i < ebcnt; ++i) { | 320 | for (i = 0; i < ebcnt && bbt[i]; ++i) { |
321 | addr0 += mtd->erasesize; | 321 | addr0 += mtd->erasesize; |
322 | ebnum += 1; | 322 | ebnum += 1; |
323 | } | 323 | } |
@@ -413,7 +413,7 @@ static int erasetest(void) | |||
413 | 413 | ||
414 | ebnum = 0; | 414 | ebnum = 0; |
415 | addr0 = 0; | 415 | addr0 = 0; |
416 | for (i = 0; bbt[i] && i < ebcnt; ++i) { | 416 | for (i = 0; i < ebcnt && bbt[i]; ++i) { |
417 | addr0 += mtd->erasesize; | 417 | addr0 += mtd->erasesize; |
418 | ebnum += 1; | 418 | ebnum += 1; |
419 | } | 419 | } |
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index e9580104b6ba..3ff50da94789 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/completion.h> | 15 | #include <linux/completion.h> |
16 | #include <linux/sched.h> | 16 | #include <linux/sched.h> |
17 | #include <linux/freezer.h> | 17 | #include <linux/freezer.h> |
18 | #include <linux/kthread.h> | ||
18 | #include "nodelist.h" | 19 | #include "nodelist.h" |
19 | 20 | ||
20 | 21 | ||
@@ -31,7 +32,7 @@ void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) | |||
31 | /* This must only ever be called when no GC thread is currently running */ | 32 | /* This must only ever be called when no GC thread is currently running */ |
32 | int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) | 33 | int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) |
33 | { | 34 | { |
34 | pid_t pid; | 35 | struct task_struct *tsk; |
35 | int ret = 0; | 36 | int ret = 0; |
36 | 37 | ||
37 | BUG_ON(c->gc_task); | 38 | BUG_ON(c->gc_task); |
@@ -39,15 +40,16 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) | |||
39 | init_completion(&c->gc_thread_start); | 40 | init_completion(&c->gc_thread_start); |
40 | init_completion(&c->gc_thread_exit); | 41 | init_completion(&c->gc_thread_exit); |
41 | 42 | ||
42 | pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES); | 43 | tsk = kthread_run(jffs2_garbage_collect_thread, c, "jffs2_gcd_mtd%d", c->mtd->index); |
43 | if (pid < 0) { | 44 | if (IS_ERR(tsk)) { |
44 | printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid); | 45 | printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %ld\n", -PTR_ERR(tsk)); |
45 | complete(&c->gc_thread_exit); | 46 | complete(&c->gc_thread_exit); |
46 | ret = pid; | 47 | ret = PTR_ERR(tsk); |
47 | } else { | 48 | } else { |
48 | /* Wait for it... */ | 49 | /* Wait for it... */ |
49 | D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid)); | 50 | D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", tsk->pid)); |
50 | wait_for_completion(&c->gc_thread_start); | 51 | wait_for_completion(&c->gc_thread_start); |
52 | ret = tsk->pid; | ||
51 | } | 53 | } |
52 | 54 | ||
53 | return ret; | 55 | return ret; |
@@ -71,7 +73,6 @@ static int jffs2_garbage_collect_thread(void *_c) | |||
71 | { | 73 | { |
72 | struct jffs2_sb_info *c = _c; | 74 | struct jffs2_sb_info *c = _c; |
73 | 75 | ||
74 | daemonize("jffs2_gcd_mtd%d", c->mtd->index); | ||
75 | allow_signal(SIGKILL); | 76 | allow_signal(SIGKILL); |
76 | allow_signal(SIGSTOP); | 77 | allow_signal(SIGSTOP); |
77 | allow_signal(SIGCONT); | 78 | allow_signal(SIGCONT); |
@@ -107,6 +108,11 @@ static int jffs2_garbage_collect_thread(void *_c) | |||
107 | * the GC thread get there first. */ | 108 | * the GC thread get there first. */ |
108 | schedule_timeout_interruptible(msecs_to_jiffies(50)); | 109 | schedule_timeout_interruptible(msecs_to_jiffies(50)); |
109 | 110 | ||
111 | if (kthread_should_stop()) { | ||
112 | D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): kthread_stop() called.\n")); | ||
113 | goto die; | ||
114 | } | ||
115 | |||
110 | /* Put_super will send a SIGKILL and then wait on the sem. | 116 | /* Put_super will send a SIGKILL and then wait on the sem. |
111 | */ | 117 | */ |
112 | while (signal_pending(current) || freezing(current)) { | 118 | while (signal_pending(current) || freezing(current)) { |
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index 9eff2bdae8a7..c082868910f2 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c | |||
@@ -39,13 +39,13 @@ int __init jffs2_create_slab_caches(void) | |||
39 | 39 | ||
40 | raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", | 40 | raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", |
41 | sizeof(struct jffs2_raw_dirent), | 41 | sizeof(struct jffs2_raw_dirent), |
42 | 0, 0, NULL); | 42 | 0, SLAB_HWCACHE_ALIGN, NULL); |
43 | if (!raw_dirent_slab) | 43 | if (!raw_dirent_slab) |
44 | goto err; | 44 | goto err; |
45 | 45 | ||
46 | raw_inode_slab = kmem_cache_create("jffs2_raw_inode", | 46 | raw_inode_slab = kmem_cache_create("jffs2_raw_inode", |
47 | sizeof(struct jffs2_raw_inode), | 47 | sizeof(struct jffs2_raw_inode), |
48 | 0, 0, NULL); | 48 | 0, SLAB_HWCACHE_ALIGN, NULL); |
49 | if (!raw_inode_slab) | 49 | if (!raw_inode_slab) |
50 | goto err; | 50 | goto err; |
51 | 51 | ||
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 4030ebada49e..7a232a9bdd62 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
@@ -121,6 +121,7 @@ typedef enum { | |||
121 | NAND_ECC_SOFT, | 121 | NAND_ECC_SOFT, |
122 | NAND_ECC_HW, | 122 | NAND_ECC_HW, |
123 | NAND_ECC_HW_SYNDROME, | 123 | NAND_ECC_HW_SYNDROME, |
124 | NAND_ECC_HW_OOB_FIRST, | ||
124 | } nand_ecc_modes_t; | 125 | } nand_ecc_modes_t; |
125 | 126 | ||
126 | /* | 127 | /* |
@@ -271,13 +272,13 @@ struct nand_ecc_ctrl { | |||
271 | uint8_t *calc_ecc); | 272 | uint8_t *calc_ecc); |
272 | int (*read_page_raw)(struct mtd_info *mtd, | 273 | int (*read_page_raw)(struct mtd_info *mtd, |
273 | struct nand_chip *chip, | 274 | struct nand_chip *chip, |
274 | uint8_t *buf); | 275 | uint8_t *buf, int page); |
275 | void (*write_page_raw)(struct mtd_info *mtd, | 276 | void (*write_page_raw)(struct mtd_info *mtd, |
276 | struct nand_chip *chip, | 277 | struct nand_chip *chip, |
277 | const uint8_t *buf); | 278 | const uint8_t *buf); |
278 | int (*read_page)(struct mtd_info *mtd, | 279 | int (*read_page)(struct mtd_info *mtd, |
279 | struct nand_chip *chip, | 280 | struct nand_chip *chip, |
280 | uint8_t *buf); | 281 | uint8_t *buf, int page); |
281 | int (*read_subpage)(struct mtd_info *mtd, | 282 | int (*read_subpage)(struct mtd_info *mtd, |
282 | struct nand_chip *chip, | 283 | struct nand_chip *chip, |
283 | uint32_t offs, uint32_t len, | 284 | uint32_t offs, uint32_t len, |
diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index 090da505425d..052ea8ca2434 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h | |||
@@ -21,6 +21,12 @@ struct mtd_info; | |||
21 | int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); | 21 | int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); |
22 | 22 | ||
23 | /* | 23 | /* |
24 | * Detect and correct a 1 bit error for eccsize byte block | ||
25 | */ | ||
26 | int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc, | ||
27 | unsigned int eccsize); | ||
28 | |||
29 | /* | ||
24 | * Detect and correct a 1 bit error for 256 byte block | 30 | * Detect and correct a 1 bit error for 256 byte block |
25 | */ | 31 | */ |
26 | int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); | 32 | int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); |
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 8ed873374381..4e49f3350678 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h | |||
@@ -214,4 +214,12 @@ unsigned onenand_block(struct onenand_chip *this, loff_t addr); | |||
214 | loff_t onenand_addr(struct onenand_chip *this, int block); | 214 | loff_t onenand_addr(struct onenand_chip *this, int block); |
215 | int flexonenand_region(struct mtd_info *mtd, loff_t addr); | 215 | int flexonenand_region(struct mtd_info *mtd, loff_t addr); |
216 | 216 | ||
217 | struct mtd_partition; | ||
218 | |||
219 | struct onenand_platform_data { | ||
220 | void (*mmcontrol)(struct mtd_info *mtd, int sync_read); | ||
221 | struct mtd_partition *parts; | ||
222 | unsigned int nr_parts; | ||
223 | }; | ||
224 | |||
217 | #endif /* __LINUX_MTD_ONENAND_H */ | 225 | #endif /* __LINUX_MTD_ONENAND_H */ |
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 86a6bbef6465..acadbf53a69f 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h | |||
@@ -207,6 +207,9 @@ | |||
207 | #define ONENAND_ECC_2BIT (1 << 1) | 207 | #define ONENAND_ECC_2BIT (1 << 1) |
208 | #define ONENAND_ECC_2BIT_ALL (0xAAAA) | 208 | #define ONENAND_ECC_2BIT_ALL (0xAAAA) |
209 | #define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010) | 209 | #define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010) |
210 | #define ONENAND_ECC_3BIT (1 << 2) | ||
211 | #define ONENAND_ECC_4BIT (1 << 3) | ||
212 | #define ONENAND_ECC_4BIT_UNCORRECTABLE (0x1010) | ||
210 | 213 | ||
211 | /* | 214 | /* |
212 | * One-Time Programmable (OTP) | 215 | * One-Time Programmable (OTP) |