Commit 13e859745cf8cf0e6602759b1ef2abcb9ced7991
Committed by
David Woodhouse
1 parent
513d57e1db
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
mtd: use %*ph[CN] to dump small buffers
There is new format specified that helps to dump small buffers. It makes the code simpler and nicer. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Acked-by: Jiandong Zheng <jdzheng@broadcom.com> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Showing 4 changed files with 14 additions and 41 deletions Inline Diff
drivers/mtd/devices/doc2001plus.c
1 | /* | 1 | /* |
2 | * Linux driver for Disk-On-Chip Millennium Plus | 2 | * Linux driver for Disk-On-Chip Millennium Plus |
3 | * | 3 | * |
4 | * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com> | 4 | * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com> |
5 | * (c) 2002-2003 SnapGear Inc | 5 | * (c) 2002-2003 SnapGear Inc |
6 | * (c) 1999 Machine Vision Holdings, Inc. | 6 | * (c) 1999 Machine Vision Holdings, Inc. |
7 | * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> | 7 | * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> |
8 | * | 8 | * |
9 | * Released under GPL | 9 | * Released under GPL |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <asm/errno.h> | 14 | #include <asm/errno.h> |
15 | #include <asm/io.h> | 15 | #include <asm/io.h> |
16 | #include <asm/uaccess.h> | 16 | #include <asm/uaccess.h> |
17 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/types.h> | 20 | #include <linux/types.h> |
21 | #include <linux/bitops.h> | 21 | #include <linux/bitops.h> |
22 | 22 | ||
23 | #include <linux/mtd/mtd.h> | 23 | #include <linux/mtd/mtd.h> |
24 | #include <linux/mtd/nand.h> | 24 | #include <linux/mtd/nand.h> |
25 | #include <linux/mtd/doc2000.h> | 25 | #include <linux/mtd/doc2000.h> |
26 | 26 | ||
27 | /* #define ECC_DEBUG */ | 27 | /* #define ECC_DEBUG */ |
28 | 28 | ||
29 | /* I have no idea why some DoC chips can not use memcop_form|to_io(). | 29 | /* I have no idea why some DoC chips can not use memcop_form|to_io(). |
30 | * This may be due to the different revisions of the ASIC controller built-in or | 30 | * This may be due to the different revisions of the ASIC controller built-in or |
31 | * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment | 31 | * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment |
32 | * this:*/ | 32 | * this:*/ |
33 | #undef USE_MEMCPY | 33 | #undef USE_MEMCPY |
34 | 34 | ||
35 | static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, | 35 | static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, |
36 | size_t *retlen, u_char *buf); | 36 | size_t *retlen, u_char *buf); |
37 | static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, | 37 | static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, |
38 | size_t *retlen, const u_char *buf); | 38 | size_t *retlen, const u_char *buf); |
39 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, | 39 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, |
40 | struct mtd_oob_ops *ops); | 40 | struct mtd_oob_ops *ops); |
41 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, | 41 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, |
42 | struct mtd_oob_ops *ops); | 42 | struct mtd_oob_ops *ops); |
43 | static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); | 43 | static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); |
44 | 44 | ||
45 | static struct mtd_info *docmilpluslist = NULL; | 45 | static struct mtd_info *docmilpluslist = NULL; |
46 | 46 | ||
47 | 47 | ||
48 | /* Perform the required delay cycles by writing to the NOP register */ | 48 | /* Perform the required delay cycles by writing to the NOP register */ |
49 | static void DoC_Delay(void __iomem * docptr, int cycles) | 49 | static void DoC_Delay(void __iomem * docptr, int cycles) |
50 | { | 50 | { |
51 | int i; | 51 | int i; |
52 | 52 | ||
53 | for (i = 0; (i < cycles); i++) | 53 | for (i = 0; (i < cycles); i++) |
54 | WriteDOC(0, docptr, Mplus_NOP); | 54 | WriteDOC(0, docptr, Mplus_NOP); |
55 | } | 55 | } |
56 | 56 | ||
57 | #define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) | 57 | #define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) |
58 | 58 | ||
59 | /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ | 59 | /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ |
60 | static int _DoC_WaitReady(void __iomem * docptr) | 60 | static int _DoC_WaitReady(void __iomem * docptr) |
61 | { | 61 | { |
62 | unsigned int c = 0xffff; | 62 | unsigned int c = 0xffff; |
63 | 63 | ||
64 | pr_debug("_DoC_WaitReady called for out-of-line wait\n"); | 64 | pr_debug("_DoC_WaitReady called for out-of-line wait\n"); |
65 | 65 | ||
66 | /* Out-of-line routine to wait for chip response */ | 66 | /* Out-of-line routine to wait for chip response */ |
67 | while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c) | 67 | while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c) |
68 | ; | 68 | ; |
69 | 69 | ||
70 | if (c == 0) | 70 | if (c == 0) |
71 | pr_debug("_DoC_WaitReady timed out.\n"); | 71 | pr_debug("_DoC_WaitReady timed out.\n"); |
72 | 72 | ||
73 | return (c == 0); | 73 | return (c == 0); |
74 | } | 74 | } |
75 | 75 | ||
76 | static inline int DoC_WaitReady(void __iomem * docptr) | 76 | static inline int DoC_WaitReady(void __iomem * docptr) |
77 | { | 77 | { |
78 | /* This is inline, to optimise the common case, where it's ready instantly */ | 78 | /* This is inline, to optimise the common case, where it's ready instantly */ |
79 | int ret = 0; | 79 | int ret = 0; |
80 | 80 | ||
81 | /* read form NOP register should be issued prior to the read from CDSNControl | 81 | /* read form NOP register should be issued prior to the read from CDSNControl |
82 | see Software Requirement 11.4 item 2. */ | 82 | see Software Requirement 11.4 item 2. */ |
83 | DoC_Delay(docptr, 4); | 83 | DoC_Delay(docptr, 4); |
84 | 84 | ||
85 | if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) | 85 | if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) |
86 | /* Call the out-of-line routine to wait */ | 86 | /* Call the out-of-line routine to wait */ |
87 | ret = _DoC_WaitReady(docptr); | 87 | ret = _DoC_WaitReady(docptr); |
88 | 88 | ||
89 | return ret; | 89 | return ret; |
90 | } | 90 | } |
91 | 91 | ||
92 | /* For some reason the Millennium Plus seems to occasionally put itself | 92 | /* For some reason the Millennium Plus seems to occasionally put itself |
93 | * into reset mode. For me this happens randomly, with no pattern that I | 93 | * into reset mode. For me this happens randomly, with no pattern that I |
94 | * can detect. M-systems suggest always check this on any block level | 94 | * can detect. M-systems suggest always check this on any block level |
95 | * operation and setting to normal mode if in reset mode. | 95 | * operation and setting to normal mode if in reset mode. |
96 | */ | 96 | */ |
97 | static inline void DoC_CheckASIC(void __iomem * docptr) | 97 | static inline void DoC_CheckASIC(void __iomem * docptr) |
98 | { | 98 | { |
99 | /* Make sure the DoC is in normal mode */ | 99 | /* Make sure the DoC is in normal mode */ |
100 | if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { | 100 | if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { |
101 | WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl); | 101 | WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl); |
102 | WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm); | 102 | WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm); |
103 | } | 103 | } |
104 | } | 104 | } |
105 | 105 | ||
106 | /* DoC_Command: Send a flash command to the flash chip through the Flash | 106 | /* DoC_Command: Send a flash command to the flash chip through the Flash |
107 | * command register. Need 2 Write Pipeline Terminates to complete send. | 107 | * command register. Need 2 Write Pipeline Terminates to complete send. |
108 | */ | 108 | */ |
109 | static void DoC_Command(void __iomem * docptr, unsigned char command, | 109 | static void DoC_Command(void __iomem * docptr, unsigned char command, |
110 | unsigned char xtraflags) | 110 | unsigned char xtraflags) |
111 | { | 111 | { |
112 | WriteDOC(command, docptr, Mplus_FlashCmd); | 112 | WriteDOC(command, docptr, Mplus_FlashCmd); |
113 | WriteDOC(command, docptr, Mplus_WritePipeTerm); | 113 | WriteDOC(command, docptr, Mplus_WritePipeTerm); |
114 | WriteDOC(command, docptr, Mplus_WritePipeTerm); | 114 | WriteDOC(command, docptr, Mplus_WritePipeTerm); |
115 | } | 115 | } |
116 | 116 | ||
117 | /* DoC_Address: Set the current address for the flash chip through the Flash | 117 | /* DoC_Address: Set the current address for the flash chip through the Flash |
118 | * Address register. Need 2 Write Pipeline Terminates to complete send. | 118 | * Address register. Need 2 Write Pipeline Terminates to complete send. |
119 | */ | 119 | */ |
120 | static inline void DoC_Address(struct DiskOnChip *doc, int numbytes, | 120 | static inline void DoC_Address(struct DiskOnChip *doc, int numbytes, |
121 | unsigned long ofs, unsigned char xtraflags1, | 121 | unsigned long ofs, unsigned char xtraflags1, |
122 | unsigned char xtraflags2) | 122 | unsigned char xtraflags2) |
123 | { | 123 | { |
124 | void __iomem * docptr = doc->virtadr; | 124 | void __iomem * docptr = doc->virtadr; |
125 | 125 | ||
126 | /* Allow for possible Mill Plus internal flash interleaving */ | 126 | /* Allow for possible Mill Plus internal flash interleaving */ |
127 | ofs >>= doc->interleave; | 127 | ofs >>= doc->interleave; |
128 | 128 | ||
129 | switch (numbytes) { | 129 | switch (numbytes) { |
130 | case 1: | 130 | case 1: |
131 | /* Send single byte, bits 0-7. */ | 131 | /* Send single byte, bits 0-7. */ |
132 | WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); | 132 | WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); |
133 | break; | 133 | break; |
134 | case 2: | 134 | case 2: |
135 | /* Send bits 9-16 followed by 17-23 */ | 135 | /* Send bits 9-16 followed by 17-23 */ |
136 | WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); | 136 | WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); |
137 | WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); | 137 | WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); |
138 | break; | 138 | break; |
139 | case 3: | 139 | case 3: |
140 | /* Send 0-7, 9-16, then 17-23 */ | 140 | /* Send 0-7, 9-16, then 17-23 */ |
141 | WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); | 141 | WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); |
142 | WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); | 142 | WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); |
143 | WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); | 143 | WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); |
144 | break; | 144 | break; |
145 | default: | 145 | default: |
146 | return; | 146 | return; |
147 | } | 147 | } |
148 | 148 | ||
149 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); | 149 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); |
150 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); | 150 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); |
151 | } | 151 | } |
152 | 152 | ||
153 | /* DoC_SelectChip: Select a given flash chip within the current floor */ | 153 | /* DoC_SelectChip: Select a given flash chip within the current floor */ |
154 | static int DoC_SelectChip(void __iomem * docptr, int chip) | 154 | static int DoC_SelectChip(void __iomem * docptr, int chip) |
155 | { | 155 | { |
156 | /* No choice for flash chip on Millennium Plus */ | 156 | /* No choice for flash chip on Millennium Plus */ |
157 | return 0; | 157 | return 0; |
158 | } | 158 | } |
159 | 159 | ||
160 | /* DoC_SelectFloor: Select a given floor (bank of flash chips) */ | 160 | /* DoC_SelectFloor: Select a given floor (bank of flash chips) */ |
161 | static int DoC_SelectFloor(void __iomem * docptr, int floor) | 161 | static int DoC_SelectFloor(void __iomem * docptr, int floor) |
162 | { | 162 | { |
163 | WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); | 163 | WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); |
164 | return 0; | 164 | return 0; |
165 | } | 165 | } |
166 | 166 | ||
167 | /* | 167 | /* |
168 | * Translate the given offset into the appropriate command and offset. | 168 | * Translate the given offset into the appropriate command and offset. |
169 | * This does the mapping using the 16bit interleave layout defined by | 169 | * This does the mapping using the 16bit interleave layout defined by |
170 | * M-Systems, and looks like this for a sector pair: | 170 | * M-Systems, and looks like this for a sector pair: |
171 | * +-----------+-------+-------+-------+--------------+---------+-----------+ | 171 | * +-----------+-------+-------+-------+--------------+---------+-----------+ |
172 | * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055| | 172 | * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055| |
173 | * +-----------+-------+-------+-------+--------------+---------+-----------+ | 173 | * +-----------+-------+-------+-------+--------------+---------+-----------+ |
174 | * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | | 174 | * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | |
175 | * +-----------+-------+-------+-------+--------------+---------+-----------+ | 175 | * +-----------+-------+-------+-------+--------------+---------+-----------+ |
176 | */ | 176 | */ |
177 | /* FIXME: This lives in INFTL not here. Other users of flash devices | 177 | /* FIXME: This lives in INFTL not here. Other users of flash devices |
178 | may not want it */ | 178 | may not want it */ |
179 | static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) | 179 | static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) |
180 | { | 180 | { |
181 | struct DiskOnChip *this = mtd->priv; | 181 | struct DiskOnChip *this = mtd->priv; |
182 | 182 | ||
183 | if (this->interleave) { | 183 | if (this->interleave) { |
184 | unsigned int ofs = *from & 0x3ff; | 184 | unsigned int ofs = *from & 0x3ff; |
185 | unsigned int cmd; | 185 | unsigned int cmd; |
186 | 186 | ||
187 | if (ofs < 512) { | 187 | if (ofs < 512) { |
188 | cmd = NAND_CMD_READ0; | 188 | cmd = NAND_CMD_READ0; |
189 | ofs &= 0x1ff; | 189 | ofs &= 0x1ff; |
190 | } else if (ofs < 1014) { | 190 | } else if (ofs < 1014) { |
191 | cmd = NAND_CMD_READ1; | 191 | cmd = NAND_CMD_READ1; |
192 | ofs = (ofs & 0x1ff) + 10; | 192 | ofs = (ofs & 0x1ff) + 10; |
193 | } else { | 193 | } else { |
194 | cmd = NAND_CMD_READOOB; | 194 | cmd = NAND_CMD_READOOB; |
195 | ofs = ofs - 1014; | 195 | ofs = ofs - 1014; |
196 | } | 196 | } |
197 | 197 | ||
198 | *from = (*from & ~0x3ff) | ofs; | 198 | *from = (*from & ~0x3ff) | ofs; |
199 | return cmd; | 199 | return cmd; |
200 | } else { | 200 | } else { |
201 | /* No interleave */ | 201 | /* No interleave */ |
202 | if ((*from) & 0x100) | 202 | if ((*from) & 0x100) |
203 | return NAND_CMD_READ1; | 203 | return NAND_CMD_READ1; |
204 | return NAND_CMD_READ0; | 204 | return NAND_CMD_READ0; |
205 | } | 205 | } |
206 | } | 206 | } |
207 | 207 | ||
208 | static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) | 208 | static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) |
209 | { | 209 | { |
210 | unsigned int ofs, cmd; | 210 | unsigned int ofs, cmd; |
211 | 211 | ||
212 | if (*from & 0x200) { | 212 | if (*from & 0x200) { |
213 | cmd = NAND_CMD_READOOB; | 213 | cmd = NAND_CMD_READOOB; |
214 | ofs = 10 + (*from & 0xf); | 214 | ofs = 10 + (*from & 0xf); |
215 | } else { | 215 | } else { |
216 | cmd = NAND_CMD_READ1; | 216 | cmd = NAND_CMD_READ1; |
217 | ofs = (*from & 0xf); | 217 | ofs = (*from & 0xf); |
218 | } | 218 | } |
219 | 219 | ||
220 | *from = (*from & ~0x3ff) | ofs; | 220 | *from = (*from & ~0x3ff) | ofs; |
221 | return cmd; | 221 | return cmd; |
222 | } | 222 | } |
223 | 223 | ||
224 | static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from) | 224 | static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from) |
225 | { | 225 | { |
226 | unsigned int ofs, cmd; | 226 | unsigned int ofs, cmd; |
227 | 227 | ||
228 | cmd = NAND_CMD_READ1; | 228 | cmd = NAND_CMD_READ1; |
229 | ofs = (*from & 0x200) ? 8 : 6; | 229 | ofs = (*from & 0x200) ? 8 : 6; |
230 | *from = (*from & ~0x3ff) | ofs; | 230 | *from = (*from & ~0x3ff) | ofs; |
231 | return cmd; | 231 | return cmd; |
232 | } | 232 | } |
233 | 233 | ||
234 | static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from) | 234 | static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from) |
235 | { | 235 | { |
236 | unsigned int ofs, cmd; | 236 | unsigned int ofs, cmd; |
237 | 237 | ||
238 | cmd = NAND_CMD_READOOB; | 238 | cmd = NAND_CMD_READOOB; |
239 | ofs = (*from & 0x200) ? 24 : 16; | 239 | ofs = (*from & 0x200) ? 24 : 16; |
240 | *from = (*from & ~0x3ff) | ofs; | 240 | *from = (*from & ~0x3ff) | ofs; |
241 | return cmd; | 241 | return cmd; |
242 | } | 242 | } |
243 | 243 | ||
244 | static inline void MemReadDOC(void __iomem * docptr, unsigned char *buf, int len) | 244 | static inline void MemReadDOC(void __iomem * docptr, unsigned char *buf, int len) |
245 | { | 245 | { |
246 | #ifndef USE_MEMCPY | 246 | #ifndef USE_MEMCPY |
247 | int i; | 247 | int i; |
248 | for (i = 0; i < len; i++) | 248 | for (i = 0; i < len; i++) |
249 | buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); | 249 | buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); |
250 | #else | 250 | #else |
251 | memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len); | 251 | memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len); |
252 | #endif | 252 | #endif |
253 | } | 253 | } |
254 | 254 | ||
255 | static inline void MemWriteDOC(void __iomem * docptr, unsigned char *buf, int len) | 255 | static inline void MemWriteDOC(void __iomem * docptr, unsigned char *buf, int len) |
256 | { | 256 | { |
257 | #ifndef USE_MEMCPY | 257 | #ifndef USE_MEMCPY |
258 | int i; | 258 | int i; |
259 | for (i = 0; i < len; i++) | 259 | for (i = 0; i < len; i++) |
260 | WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); | 260 | WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); |
261 | #else | 261 | #else |
262 | memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); | 262 | memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); |
263 | #endif | 263 | #endif |
264 | } | 264 | } |
265 | 265 | ||
266 | /* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ | 266 | /* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ |
267 | static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) | 267 | static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) |
268 | { | 268 | { |
269 | int mfr, id, i, j; | 269 | int mfr, id, i, j; |
270 | volatile char dummy; | 270 | volatile char dummy; |
271 | void __iomem * docptr = doc->virtadr; | 271 | void __iomem * docptr = doc->virtadr; |
272 | 272 | ||
273 | /* Page in the required floor/chip */ | 273 | /* Page in the required floor/chip */ |
274 | DoC_SelectFloor(docptr, floor); | 274 | DoC_SelectFloor(docptr, floor); |
275 | DoC_SelectChip(docptr, chip); | 275 | DoC_SelectChip(docptr, chip); |
276 | 276 | ||
277 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ | 277 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ |
278 | WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); | 278 | WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); |
279 | 279 | ||
280 | /* Reset the chip, see Software Requirement 11.4 item 1. */ | 280 | /* Reset the chip, see Software Requirement 11.4 item 1. */ |
281 | DoC_Command(docptr, NAND_CMD_RESET, 0); | 281 | DoC_Command(docptr, NAND_CMD_RESET, 0); |
282 | DoC_WaitReady(docptr); | 282 | DoC_WaitReady(docptr); |
283 | 283 | ||
284 | /* Read the NAND chip ID: 1. Send ReadID command */ | 284 | /* Read the NAND chip ID: 1. Send ReadID command */ |
285 | DoC_Command(docptr, NAND_CMD_READID, 0); | 285 | DoC_Command(docptr, NAND_CMD_READID, 0); |
286 | 286 | ||
287 | /* Read the NAND chip ID: 2. Send address byte zero */ | 287 | /* Read the NAND chip ID: 2. Send address byte zero */ |
288 | DoC_Address(doc, 1, 0x00, 0, 0x00); | 288 | DoC_Address(doc, 1, 0x00, 0, 0x00); |
289 | 289 | ||
290 | WriteDOC(0, docptr, Mplus_FlashControl); | 290 | WriteDOC(0, docptr, Mplus_FlashControl); |
291 | DoC_WaitReady(docptr); | 291 | DoC_WaitReady(docptr); |
292 | 292 | ||
293 | /* Read the manufacturer and device id codes of the flash device through | 293 | /* Read the manufacturer and device id codes of the flash device through |
294 | CDSN IO register see Software Requirement 11.4 item 5.*/ | 294 | CDSN IO register see Software Requirement 11.4 item 5.*/ |
295 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); | 295 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); |
296 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); | 296 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); |
297 | 297 | ||
298 | mfr = ReadDOC(docptr, Mil_CDSN_IO); | 298 | mfr = ReadDOC(docptr, Mil_CDSN_IO); |
299 | if (doc->interleave) | 299 | if (doc->interleave) |
300 | dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ | 300 | dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ |
301 | 301 | ||
302 | id = ReadDOC(docptr, Mil_CDSN_IO); | 302 | id = ReadDOC(docptr, Mil_CDSN_IO); |
303 | if (doc->interleave) | 303 | if (doc->interleave) |
304 | dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ | 304 | dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ |
305 | 305 | ||
306 | dummy = ReadDOC(docptr, Mplus_LastDataRead); | 306 | dummy = ReadDOC(docptr, Mplus_LastDataRead); |
307 | dummy = ReadDOC(docptr, Mplus_LastDataRead); | 307 | dummy = ReadDOC(docptr, Mplus_LastDataRead); |
308 | 308 | ||
309 | /* Disable flash internally */ | 309 | /* Disable flash internally */ |
310 | WriteDOC(0, docptr, Mplus_FlashSelect); | 310 | WriteDOC(0, docptr, Mplus_FlashSelect); |
311 | 311 | ||
312 | /* No response - return failure */ | 312 | /* No response - return failure */ |
313 | if (mfr == 0xff || mfr == 0) | 313 | if (mfr == 0xff || mfr == 0) |
314 | return 0; | 314 | return 0; |
315 | 315 | ||
316 | for (i = 0; nand_flash_ids[i].name != NULL; i++) { | 316 | for (i = 0; nand_flash_ids[i].name != NULL; i++) { |
317 | if (id == nand_flash_ids[i].id) { | 317 | if (id == nand_flash_ids[i].id) { |
318 | /* Try to identify manufacturer */ | 318 | /* Try to identify manufacturer */ |
319 | for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { | 319 | for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { |
320 | if (nand_manuf_ids[j].id == mfr) | 320 | if (nand_manuf_ids[j].id == mfr) |
321 | break; | 321 | break; |
322 | } | 322 | } |
323 | printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " | 323 | printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " |
324 | "Chip ID: %2.2X (%s:%s)\n", mfr, id, | 324 | "Chip ID: %2.2X (%s:%s)\n", mfr, id, |
325 | nand_manuf_ids[j].name, nand_flash_ids[i].name); | 325 | nand_manuf_ids[j].name, nand_flash_ids[i].name); |
326 | doc->mfr = mfr; | 326 | doc->mfr = mfr; |
327 | doc->id = id; | 327 | doc->id = id; |
328 | doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; | 328 | doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; |
329 | doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; | 329 | doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; |
330 | break; | 330 | break; |
331 | } | 331 | } |
332 | } | 332 | } |
333 | 333 | ||
334 | if (nand_flash_ids[i].name == NULL) | 334 | if (nand_flash_ids[i].name == NULL) |
335 | return 0; | 335 | return 0; |
336 | return 1; | 336 | return 1; |
337 | } | 337 | } |
338 | 338 | ||
339 | /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ | 339 | /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ |
340 | static void DoC_ScanChips(struct DiskOnChip *this) | 340 | static void DoC_ScanChips(struct DiskOnChip *this) |
341 | { | 341 | { |
342 | int floor, chip; | 342 | int floor, chip; |
343 | int numchips[MAX_FLOORS_MPLUS]; | 343 | int numchips[MAX_FLOORS_MPLUS]; |
344 | int ret; | 344 | int ret; |
345 | 345 | ||
346 | this->numchips = 0; | 346 | this->numchips = 0; |
347 | this->mfr = 0; | 347 | this->mfr = 0; |
348 | this->id = 0; | 348 | this->id = 0; |
349 | 349 | ||
350 | /* Work out the intended interleave setting */ | 350 | /* Work out the intended interleave setting */ |
351 | this->interleave = 0; | 351 | this->interleave = 0; |
352 | if (this->ChipID == DOC_ChipID_DocMilPlus32) | 352 | if (this->ChipID == DOC_ChipID_DocMilPlus32) |
353 | this->interleave = 1; | 353 | this->interleave = 1; |
354 | 354 | ||
355 | /* Check the ASIC agrees */ | 355 | /* Check the ASIC agrees */ |
356 | if ( (this->interleave << 2) != | 356 | if ( (this->interleave << 2) != |
357 | (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) { | 357 | (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) { |
358 | u_char conf = ReadDOC(this->virtadr, Mplus_Configuration); | 358 | u_char conf = ReadDOC(this->virtadr, Mplus_Configuration); |
359 | printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", | 359 | printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", |
360 | this->interleave?"on (16-bit)":"off (8-bit)"); | 360 | this->interleave?"on (16-bit)":"off (8-bit)"); |
361 | conf ^= 4; | 361 | conf ^= 4; |
362 | WriteDOC(conf, this->virtadr, Mplus_Configuration); | 362 | WriteDOC(conf, this->virtadr, Mplus_Configuration); |
363 | } | 363 | } |
364 | 364 | ||
365 | /* For each floor, find the number of valid chips it contains */ | 365 | /* For each floor, find the number of valid chips it contains */ |
366 | for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { | 366 | for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { |
367 | numchips[floor] = 0; | 367 | numchips[floor] = 0; |
368 | for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) { | 368 | for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) { |
369 | ret = DoC_IdentChip(this, floor, chip); | 369 | ret = DoC_IdentChip(this, floor, chip); |
370 | if (ret) { | 370 | if (ret) { |
371 | numchips[floor]++; | 371 | numchips[floor]++; |
372 | this->numchips++; | 372 | this->numchips++; |
373 | } | 373 | } |
374 | } | 374 | } |
375 | } | 375 | } |
376 | /* If there are none at all that we recognise, bail */ | 376 | /* If there are none at all that we recognise, bail */ |
377 | if (!this->numchips) { | 377 | if (!this->numchips) { |
378 | printk("No flash chips recognised.\n"); | 378 | printk("No flash chips recognised.\n"); |
379 | return; | 379 | return; |
380 | } | 380 | } |
381 | 381 | ||
382 | /* Allocate an array to hold the information for each chip */ | 382 | /* Allocate an array to hold the information for each chip */ |
383 | this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); | 383 | this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); |
384 | if (!this->chips){ | 384 | if (!this->chips){ |
385 | printk("MTD: No memory for allocating chip info structures\n"); | 385 | printk("MTD: No memory for allocating chip info structures\n"); |
386 | return; | 386 | return; |
387 | } | 387 | } |
388 | 388 | ||
389 | /* Fill out the chip array with {floor, chipno} for each | 389 | /* Fill out the chip array with {floor, chipno} for each |
390 | * detected chip in the device. */ | 390 | * detected chip in the device. */ |
391 | for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) { | 391 | for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) { |
392 | for (chip = 0 ; chip < numchips[floor] ; chip++) { | 392 | for (chip = 0 ; chip < numchips[floor] ; chip++) { |
393 | this->chips[ret].floor = floor; | 393 | this->chips[ret].floor = floor; |
394 | this->chips[ret].chip = chip; | 394 | this->chips[ret].chip = chip; |
395 | this->chips[ret].curadr = 0; | 395 | this->chips[ret].curadr = 0; |
396 | this->chips[ret].curmode = 0x50; | 396 | this->chips[ret].curmode = 0x50; |
397 | ret++; | 397 | ret++; |
398 | } | 398 | } |
399 | } | 399 | } |
400 | 400 | ||
401 | /* Calculate and print the total size of the device */ | 401 | /* Calculate and print the total size of the device */ |
402 | this->totlen = this->numchips * (1 << this->chipshift); | 402 | this->totlen = this->numchips * (1 << this->chipshift); |
403 | printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", | 403 | printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", |
404 | this->numchips ,this->totlen >> 20); | 404 | this->numchips ,this->totlen >> 20); |
405 | } | 405 | } |
406 | 406 | ||
407 | static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) | 407 | static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) |
408 | { | 408 | { |
409 | int tmp1, tmp2, retval; | 409 | int tmp1, tmp2, retval; |
410 | 410 | ||
411 | if (doc1->physadr == doc2->physadr) | 411 | if (doc1->physadr == doc2->physadr) |
412 | return 1; | 412 | return 1; |
413 | 413 | ||
414 | /* Use the alias resolution register which was set aside for this | 414 | /* Use the alias resolution register which was set aside for this |
415 | * purpose. If it's value is the same on both chips, they might | 415 | * purpose. If it's value is the same on both chips, they might |
416 | * be the same chip, and we write to one and check for a change in | 416 | * be the same chip, and we write to one and check for a change in |
417 | * the other. It's unclear if this register is usuable in the | 417 | * the other. It's unclear if this register is usuable in the |
418 | * DoC 2000 (it's in the Millennium docs), but it seems to work. */ | 418 | * DoC 2000 (it's in the Millennium docs), but it seems to work. */ |
419 | tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution); | 419 | tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution); |
420 | tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); | 420 | tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); |
421 | if (tmp1 != tmp2) | 421 | if (tmp1 != tmp2) |
422 | return 0; | 422 | return 0; |
423 | 423 | ||
424 | WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution); | 424 | WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution); |
425 | tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); | 425 | tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); |
426 | if (tmp2 == (tmp1+1) % 0xff) | 426 | if (tmp2 == (tmp1+1) % 0xff) |
427 | retval = 1; | 427 | retval = 1; |
428 | else | 428 | else |
429 | retval = 0; | 429 | retval = 0; |
430 | 430 | ||
431 | /* Restore register contents. May not be necessary, but do it just to | 431 | /* Restore register contents. May not be necessary, but do it just to |
432 | * be safe. */ | 432 | * be safe. */ |
433 | WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution); | 433 | WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution); |
434 | 434 | ||
435 | return retval; | 435 | return retval; |
436 | } | 436 | } |
437 | 437 | ||
438 | /* This routine is found from the docprobe code by symbol_get(), | 438 | /* This routine is found from the docprobe code by symbol_get(), |
439 | * which will bump the use count of this module. */ | 439 | * which will bump the use count of this module. */ |
440 | void DoCMilPlus_init(struct mtd_info *mtd) | 440 | void DoCMilPlus_init(struct mtd_info *mtd) |
441 | { | 441 | { |
442 | struct DiskOnChip *this = mtd->priv; | 442 | struct DiskOnChip *this = mtd->priv; |
443 | struct DiskOnChip *old = NULL; | 443 | struct DiskOnChip *old = NULL; |
444 | 444 | ||
445 | /* We must avoid being called twice for the same device. */ | 445 | /* We must avoid being called twice for the same device. */ |
446 | if (docmilpluslist) | 446 | if (docmilpluslist) |
447 | old = docmilpluslist->priv; | 447 | old = docmilpluslist->priv; |
448 | 448 | ||
449 | while (old) { | 449 | while (old) { |
450 | if (DoCMilPlus_is_alias(this, old)) { | 450 | if (DoCMilPlus_is_alias(this, old)) { |
451 | printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " | 451 | printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " |
452 | "Plus at 0x%lX - already configured\n", | 452 | "Plus at 0x%lX - already configured\n", |
453 | this->physadr); | 453 | this->physadr); |
454 | iounmap(this->virtadr); | 454 | iounmap(this->virtadr); |
455 | kfree(mtd); | 455 | kfree(mtd); |
456 | return; | 456 | return; |
457 | } | 457 | } |
458 | if (old->nextdoc) | 458 | if (old->nextdoc) |
459 | old = old->nextdoc->priv; | 459 | old = old->nextdoc->priv; |
460 | else | 460 | else |
461 | old = NULL; | 461 | old = NULL; |
462 | } | 462 | } |
463 | 463 | ||
464 | mtd->name = "DiskOnChip Millennium Plus"; | 464 | mtd->name = "DiskOnChip Millennium Plus"; |
465 | printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " | 465 | printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " |
466 | "address 0x%lX\n", this->physadr); | 466 | "address 0x%lX\n", this->physadr); |
467 | 467 | ||
468 | mtd->type = MTD_NANDFLASH; | 468 | mtd->type = MTD_NANDFLASH; |
469 | mtd->flags = MTD_CAP_NANDFLASH; | 469 | mtd->flags = MTD_CAP_NANDFLASH; |
470 | mtd->writebufsize = mtd->writesize = 512; | 470 | mtd->writebufsize = mtd->writesize = 512; |
471 | mtd->oobsize = 16; | 471 | mtd->oobsize = 16; |
472 | mtd->ecc_strength = 2; | 472 | mtd->ecc_strength = 2; |
473 | mtd->owner = THIS_MODULE; | 473 | mtd->owner = THIS_MODULE; |
474 | mtd->_erase = doc_erase; | 474 | mtd->_erase = doc_erase; |
475 | mtd->_read = doc_read; | 475 | mtd->_read = doc_read; |
476 | mtd->_write = doc_write; | 476 | mtd->_write = doc_write; |
477 | mtd->_read_oob = doc_read_oob; | 477 | mtd->_read_oob = doc_read_oob; |
478 | mtd->_write_oob = doc_write_oob; | 478 | mtd->_write_oob = doc_write_oob; |
479 | this->curfloor = -1; | 479 | this->curfloor = -1; |
480 | this->curchip = -1; | 480 | this->curchip = -1; |
481 | 481 | ||
482 | /* Ident all the chips present. */ | 482 | /* Ident all the chips present. */ |
483 | DoC_ScanChips(this); | 483 | DoC_ScanChips(this); |
484 | 484 | ||
485 | if (!this->totlen) { | 485 | if (!this->totlen) { |
486 | kfree(mtd); | 486 | kfree(mtd); |
487 | iounmap(this->virtadr); | 487 | iounmap(this->virtadr); |
488 | } else { | 488 | } else { |
489 | this->nextdoc = docmilpluslist; | 489 | this->nextdoc = docmilpluslist; |
490 | docmilpluslist = mtd; | 490 | docmilpluslist = mtd; |
491 | mtd->size = this->totlen; | 491 | mtd->size = this->totlen; |
492 | mtd->erasesize = this->erasesize; | 492 | mtd->erasesize = this->erasesize; |
493 | mtd_device_register(mtd, NULL, 0); | 493 | mtd_device_register(mtd, NULL, 0); |
494 | return; | 494 | return; |
495 | } | 495 | } |
496 | } | 496 | } |
497 | EXPORT_SYMBOL_GPL(DoCMilPlus_init); | 497 | EXPORT_SYMBOL_GPL(DoCMilPlus_init); |
498 | 498 | ||
499 | #if 0 | 499 | #if 0 |
500 | static int doc_dumpblk(struct mtd_info *mtd, loff_t from) | 500 | static int doc_dumpblk(struct mtd_info *mtd, loff_t from) |
501 | { | 501 | { |
502 | int i; | 502 | int i; |
503 | loff_t fofs; | 503 | loff_t fofs; |
504 | struct DiskOnChip *this = mtd->priv; | 504 | struct DiskOnChip *this = mtd->priv; |
505 | void __iomem * docptr = this->virtadr; | 505 | void __iomem * docptr = this->virtadr; |
506 | struct Nand *mychip = &this->chips[from >> (this->chipshift)]; | 506 | struct Nand *mychip = &this->chips[from >> (this->chipshift)]; |
507 | unsigned char *bp, buf[1056]; | 507 | unsigned char *bp, buf[1056]; |
508 | char c[32]; | 508 | char c[32]; |
509 | 509 | ||
510 | from &= ~0x3ff; | 510 | from &= ~0x3ff; |
511 | 511 | ||
512 | /* Don't allow read past end of device */ | 512 | /* Don't allow read past end of device */ |
513 | if (from >= this->totlen) | 513 | if (from >= this->totlen) |
514 | return -EINVAL; | 514 | return -EINVAL; |
515 | 515 | ||
516 | DoC_CheckASIC(docptr); | 516 | DoC_CheckASIC(docptr); |
517 | 517 | ||
518 | /* Find the chip which is to be used and select it */ | 518 | /* Find the chip which is to be used and select it */ |
519 | if (this->curfloor != mychip->floor) { | 519 | if (this->curfloor != mychip->floor) { |
520 | DoC_SelectFloor(docptr, mychip->floor); | 520 | DoC_SelectFloor(docptr, mychip->floor); |
521 | DoC_SelectChip(docptr, mychip->chip); | 521 | DoC_SelectChip(docptr, mychip->chip); |
522 | } else if (this->curchip != mychip->chip) { | 522 | } else if (this->curchip != mychip->chip) { |
523 | DoC_SelectChip(docptr, mychip->chip); | 523 | DoC_SelectChip(docptr, mychip->chip); |
524 | } | 524 | } |
525 | this->curfloor = mychip->floor; | 525 | this->curfloor = mychip->floor; |
526 | this->curchip = mychip->chip; | 526 | this->curchip = mychip->chip; |
527 | 527 | ||
528 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ | 528 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ |
529 | WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); | 529 | WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); |
530 | 530 | ||
531 | /* Reset the chip, see Software Requirement 11.4 item 1. */ | 531 | /* Reset the chip, see Software Requirement 11.4 item 1. */ |
532 | DoC_Command(docptr, NAND_CMD_RESET, 0); | 532 | DoC_Command(docptr, NAND_CMD_RESET, 0); |
533 | DoC_WaitReady(docptr); | 533 | DoC_WaitReady(docptr); |
534 | 534 | ||
535 | fofs = from; | 535 | fofs = from; |
536 | DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); | 536 | DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); |
537 | DoC_Address(this, 3, fofs, 0, 0x00); | 537 | DoC_Address(this, 3, fofs, 0, 0x00); |
538 | WriteDOC(0, docptr, Mplus_FlashControl); | 538 | WriteDOC(0, docptr, Mplus_FlashControl); |
539 | DoC_WaitReady(docptr); | 539 | DoC_WaitReady(docptr); |
540 | 540 | ||
541 | /* disable the ECC engine */ | 541 | /* disable the ECC engine */ |
542 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); | 542 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); |
543 | 543 | ||
544 | ReadDOC(docptr, Mplus_ReadPipeInit); | 544 | ReadDOC(docptr, Mplus_ReadPipeInit); |
545 | ReadDOC(docptr, Mplus_ReadPipeInit); | 545 | ReadDOC(docptr, Mplus_ReadPipeInit); |
546 | 546 | ||
547 | /* Read the data via the internal pipeline through CDSN IO | 547 | /* Read the data via the internal pipeline through CDSN IO |
548 | register, see Pipelined Read Operations 11.3 */ | 548 | register, see Pipelined Read Operations 11.3 */ |
549 | MemReadDOC(docptr, buf, 1054); | 549 | MemReadDOC(docptr, buf, 1054); |
550 | buf[1054] = ReadDOC(docptr, Mplus_LastDataRead); | 550 | buf[1054] = ReadDOC(docptr, Mplus_LastDataRead); |
551 | buf[1055] = ReadDOC(docptr, Mplus_LastDataRead); | 551 | buf[1055] = ReadDOC(docptr, Mplus_LastDataRead); |
552 | 552 | ||
553 | memset(&c[0], 0, sizeof(c)); | 553 | memset(&c[0], 0, sizeof(c)); |
554 | printk("DUMP OFFSET=%x:\n", (int)from); | 554 | printk("DUMP OFFSET=%x:\n", (int)from); |
555 | 555 | ||
556 | for (i = 0, bp = &buf[0]; (i < 1056); i++) { | 556 | for (i = 0, bp = &buf[0]; (i < 1056); i++) { |
557 | if ((i % 16) == 0) | 557 | if ((i % 16) == 0) |
558 | printk("%08x: ", i); | 558 | printk("%08x: ", i); |
559 | printk(" %02x", *bp); | 559 | printk(" %02x", *bp); |
560 | c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.'; | 560 | c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.'; |
561 | bp++; | 561 | bp++; |
562 | if (((i + 1) % 16) == 0) | 562 | if (((i + 1) % 16) == 0) |
563 | printk(" %s\n", c); | 563 | printk(" %s\n", c); |
564 | } | 564 | } |
565 | printk("\n"); | 565 | printk("\n"); |
566 | 566 | ||
567 | /* Disable flash internally */ | 567 | /* Disable flash internally */ |
568 | WriteDOC(0, docptr, Mplus_FlashSelect); | 568 | WriteDOC(0, docptr, Mplus_FlashSelect); |
569 | 569 | ||
570 | return 0; | 570 | return 0; |
571 | } | 571 | } |
572 | #endif | 572 | #endif |
573 | 573 | ||
574 | static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, | 574 | static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, |
575 | size_t *retlen, u_char *buf) | 575 | size_t *retlen, u_char *buf) |
576 | { | 576 | { |
577 | int ret, i; | 577 | int ret, i; |
578 | volatile char dummy; | 578 | volatile char dummy; |
579 | loff_t fofs; | 579 | loff_t fofs; |
580 | unsigned char syndrome[6], eccbuf[6]; | 580 | unsigned char syndrome[6], eccbuf[6]; |
581 | struct DiskOnChip *this = mtd->priv; | 581 | struct DiskOnChip *this = mtd->priv; |
582 | void __iomem * docptr = this->virtadr; | 582 | void __iomem * docptr = this->virtadr; |
583 | struct Nand *mychip = &this->chips[from >> (this->chipshift)]; | 583 | struct Nand *mychip = &this->chips[from >> (this->chipshift)]; |
584 | 584 | ||
585 | /* Don't allow a single read to cross a 512-byte block boundary */ | 585 | /* Don't allow a single read to cross a 512-byte block boundary */ |
586 | if (from + len > ((from | 0x1ff) + 1)) | 586 | if (from + len > ((from | 0x1ff) + 1)) |
587 | len = ((from | 0x1ff) + 1) - from; | 587 | len = ((from | 0x1ff) + 1) - from; |
588 | 588 | ||
589 | DoC_CheckASIC(docptr); | 589 | DoC_CheckASIC(docptr); |
590 | 590 | ||
591 | /* Find the chip which is to be used and select it */ | 591 | /* Find the chip which is to be used and select it */ |
592 | if (this->curfloor != mychip->floor) { | 592 | if (this->curfloor != mychip->floor) { |
593 | DoC_SelectFloor(docptr, mychip->floor); | 593 | DoC_SelectFloor(docptr, mychip->floor); |
594 | DoC_SelectChip(docptr, mychip->chip); | 594 | DoC_SelectChip(docptr, mychip->chip); |
595 | } else if (this->curchip != mychip->chip) { | 595 | } else if (this->curchip != mychip->chip) { |
596 | DoC_SelectChip(docptr, mychip->chip); | 596 | DoC_SelectChip(docptr, mychip->chip); |
597 | } | 597 | } |
598 | this->curfloor = mychip->floor; | 598 | this->curfloor = mychip->floor; |
599 | this->curchip = mychip->chip; | 599 | this->curchip = mychip->chip; |
600 | 600 | ||
601 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ | 601 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ |
602 | WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); | 602 | WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); |
603 | 603 | ||
604 | /* Reset the chip, see Software Requirement 11.4 item 1. */ | 604 | /* Reset the chip, see Software Requirement 11.4 item 1. */ |
605 | DoC_Command(docptr, NAND_CMD_RESET, 0); | 605 | DoC_Command(docptr, NAND_CMD_RESET, 0); |
606 | DoC_WaitReady(docptr); | 606 | DoC_WaitReady(docptr); |
607 | 607 | ||
608 | fofs = from; | 608 | fofs = from; |
609 | DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); | 609 | DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); |
610 | DoC_Address(this, 3, fofs, 0, 0x00); | 610 | DoC_Address(this, 3, fofs, 0, 0x00); |
611 | WriteDOC(0, docptr, Mplus_FlashControl); | 611 | WriteDOC(0, docptr, Mplus_FlashControl); |
612 | DoC_WaitReady(docptr); | 612 | DoC_WaitReady(docptr); |
613 | 613 | ||
614 | /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ | 614 | /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ |
615 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); | 615 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); |
616 | WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); | 616 | WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); |
617 | 617 | ||
618 | /* Let the caller know we completed it */ | 618 | /* Let the caller know we completed it */ |
619 | *retlen = len; | 619 | *retlen = len; |
620 | ret = 0; | 620 | ret = 0; |
621 | 621 | ||
622 | ReadDOC(docptr, Mplus_ReadPipeInit); | 622 | ReadDOC(docptr, Mplus_ReadPipeInit); |
623 | ReadDOC(docptr, Mplus_ReadPipeInit); | 623 | ReadDOC(docptr, Mplus_ReadPipeInit); |
624 | 624 | ||
625 | /* Read the data via the internal pipeline through CDSN IO | 625 | /* Read the data via the internal pipeline through CDSN IO |
626 | register, see Pipelined Read Operations 11.3 */ | 626 | register, see Pipelined Read Operations 11.3 */ |
627 | MemReadDOC(docptr, buf, len); | 627 | MemReadDOC(docptr, buf, len); |
628 | 628 | ||
629 | /* Read the ECC data following raw data */ | 629 | /* Read the ECC data following raw data */ |
630 | MemReadDOC(docptr, eccbuf, 4); | 630 | MemReadDOC(docptr, eccbuf, 4); |
631 | eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead); | 631 | eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead); |
632 | eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead); | 632 | eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead); |
633 | 633 | ||
634 | /* Flush the pipeline */ | 634 | /* Flush the pipeline */ |
635 | dummy = ReadDOC(docptr, Mplus_ECCConf); | 635 | dummy = ReadDOC(docptr, Mplus_ECCConf); |
636 | dummy = ReadDOC(docptr, Mplus_ECCConf); | 636 | dummy = ReadDOC(docptr, Mplus_ECCConf); |
637 | 637 | ||
638 | /* Check the ECC Status */ | 638 | /* Check the ECC Status */ |
639 | if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) { | 639 | if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) { |
640 | int nb_errors; | 640 | int nb_errors; |
641 | /* There was an ECC error */ | 641 | /* There was an ECC error */ |
642 | #ifdef ECC_DEBUG | 642 | #ifdef ECC_DEBUG |
643 | printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); | 643 | printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); |
644 | #endif | 644 | #endif |
645 | /* Read the ECC syndrome through the DiskOnChip ECC logic. | 645 | /* Read the ECC syndrome through the DiskOnChip ECC logic. |
646 | These syndrome will be all ZERO when there is no error */ | 646 | These syndrome will be all ZERO when there is no error */ |
647 | for (i = 0; i < 6; i++) | 647 | for (i = 0; i < 6; i++) |
648 | syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); | 648 | syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); |
649 | 649 | ||
650 | nb_errors = doc_decode_ecc(buf, syndrome); | 650 | nb_errors = doc_decode_ecc(buf, syndrome); |
651 | #ifdef ECC_DEBUG | 651 | #ifdef ECC_DEBUG |
652 | printk("ECC Errors corrected: %x\n", nb_errors); | 652 | printk("ECC Errors corrected: %x\n", nb_errors); |
653 | #endif | 653 | #endif |
654 | if (nb_errors < 0) { | 654 | if (nb_errors < 0) { |
655 | /* We return error, but have actually done the | 655 | /* We return error, but have actually done the |
656 | read. Not that this can be told to user-space, via | 656 | read. Not that this can be told to user-space, via |
657 | sys_read(), but at least MTD-aware stuff can know | 657 | sys_read(), but at least MTD-aware stuff can know |
658 | about it by checking *retlen */ | 658 | about it by checking *retlen */ |
659 | #ifdef ECC_DEBUG | 659 | #ifdef ECC_DEBUG |
660 | printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", | 660 | printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", |
661 | __FILE__, __LINE__, (int)from); | 661 | __FILE__, __LINE__, (int)from); |
662 | printk(" syndrome= %02x:%02x:%02x:%02x:%02x:" | 662 | printk(" syndrome= %*phC\n", 6, syndrome); |
663 | "%02x\n", | 663 | printk(" eccbuf= %*phC\n", 6, eccbuf); |
664 | syndrome[0], syndrome[1], syndrome[2], | ||
665 | syndrome[3], syndrome[4], syndrome[5]); | ||
666 | printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:" | ||
667 | "%02x\n", | ||
668 | eccbuf[0], eccbuf[1], eccbuf[2], | ||
669 | eccbuf[3], eccbuf[4], eccbuf[5]); | ||
670 | #endif | 664 | #endif |
671 | ret = -EIO; | 665 | ret = -EIO; |
672 | } | 666 | } |
673 | } | 667 | } |
674 | 668 | ||
675 | #ifdef PSYCHO_DEBUG | 669 | #ifdef PSYCHO_DEBUG |
676 | printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", | 670 | printk("ECC DATA at %lx: %*ph\n", (long)from, 6, eccbuf); |
677 | (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], | ||
678 | eccbuf[4], eccbuf[5]); | ||
679 | #endif | 671 | #endif |
680 | /* disable the ECC engine */ | 672 | /* disable the ECC engine */ |
681 | WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf); | 673 | WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf); |
682 | 674 | ||
683 | /* Disable flash internally */ | 675 | /* Disable flash internally */ |
684 | WriteDOC(0, docptr, Mplus_FlashSelect); | 676 | WriteDOC(0, docptr, Mplus_FlashSelect); |
685 | 677 | ||
686 | return ret; | 678 | return ret; |
687 | } | 679 | } |
688 | 680 | ||
689 | static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, | 681 | static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, |
690 | size_t *retlen, const u_char *buf) | 682 | size_t *retlen, const u_char *buf) |
691 | { | 683 | { |
692 | int i, before, ret = 0; | 684 | int i, before, ret = 0; |
693 | loff_t fto; | 685 | loff_t fto; |
694 | volatile char dummy; | 686 | volatile char dummy; |
695 | char eccbuf[6]; | 687 | char eccbuf[6]; |
696 | struct DiskOnChip *this = mtd->priv; | 688 | struct DiskOnChip *this = mtd->priv; |
697 | void __iomem * docptr = this->virtadr; | 689 | void __iomem * docptr = this->virtadr; |
698 | struct Nand *mychip = &this->chips[to >> (this->chipshift)]; | 690 | struct Nand *mychip = &this->chips[to >> (this->chipshift)]; |
699 | 691 | ||
700 | /* Don't allow writes which aren't exactly one block (512 bytes) */ | 692 | /* Don't allow writes which aren't exactly one block (512 bytes) */ |
701 | if ((to & 0x1ff) || (len != 0x200)) | 693 | if ((to & 0x1ff) || (len != 0x200)) |
702 | return -EINVAL; | 694 | return -EINVAL; |
703 | 695 | ||
704 | /* Determine position of OOB flags, before or after data */ | 696 | /* Determine position of OOB flags, before or after data */ |
705 | before = (this->interleave && (to & 0x200)); | 697 | before = (this->interleave && (to & 0x200)); |
706 | 698 | ||
707 | DoC_CheckASIC(docptr); | 699 | DoC_CheckASIC(docptr); |
708 | 700 | ||
709 | /* Find the chip which is to be used and select it */ | 701 | /* Find the chip which is to be used and select it */ |
710 | if (this->curfloor != mychip->floor) { | 702 | if (this->curfloor != mychip->floor) { |
711 | DoC_SelectFloor(docptr, mychip->floor); | 703 | DoC_SelectFloor(docptr, mychip->floor); |
712 | DoC_SelectChip(docptr, mychip->chip); | 704 | DoC_SelectChip(docptr, mychip->chip); |
713 | } else if (this->curchip != mychip->chip) { | 705 | } else if (this->curchip != mychip->chip) { |
714 | DoC_SelectChip(docptr, mychip->chip); | 706 | DoC_SelectChip(docptr, mychip->chip); |
715 | } | 707 | } |
716 | this->curfloor = mychip->floor; | 708 | this->curfloor = mychip->floor; |
717 | this->curchip = mychip->chip; | 709 | this->curchip = mychip->chip; |
718 | 710 | ||
719 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ | 711 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ |
720 | WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); | 712 | WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); |
721 | 713 | ||
722 | /* Reset the chip, see Software Requirement 11.4 item 1. */ | 714 | /* Reset the chip, see Software Requirement 11.4 item 1. */ |
723 | DoC_Command(docptr, NAND_CMD_RESET, 0); | 715 | DoC_Command(docptr, NAND_CMD_RESET, 0); |
724 | DoC_WaitReady(docptr); | 716 | DoC_WaitReady(docptr); |
725 | 717 | ||
726 | /* Set device to appropriate plane of flash */ | 718 | /* Set device to appropriate plane of flash */ |
727 | fto = to; | 719 | fto = to; |
728 | WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); | 720 | WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); |
729 | 721 | ||
730 | /* On interleaved devices the flags for 2nd half 512 are before data */ | 722 | /* On interleaved devices the flags for 2nd half 512 are before data */ |
731 | if (before) | 723 | if (before) |
732 | fto -= 2; | 724 | fto -= 2; |
733 | 725 | ||
734 | /* issue the Serial Data In command to initial the Page Program process */ | 726 | /* issue the Serial Data In command to initial the Page Program process */ |
735 | DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); | 727 | DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); |
736 | DoC_Address(this, 3, fto, 0x00, 0x00); | 728 | DoC_Address(this, 3, fto, 0x00, 0x00); |
737 | 729 | ||
738 | /* Disable the ECC engine */ | 730 | /* Disable the ECC engine */ |
739 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); | 731 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); |
740 | 732 | ||
741 | if (before) { | 733 | if (before) { |
742 | /* Write the block status BLOCK_USED (0x5555) */ | 734 | /* Write the block status BLOCK_USED (0x5555) */ |
743 | WriteDOC(0x55, docptr, Mil_CDSN_IO); | 735 | WriteDOC(0x55, docptr, Mil_CDSN_IO); |
744 | WriteDOC(0x55, docptr, Mil_CDSN_IO); | 736 | WriteDOC(0x55, docptr, Mil_CDSN_IO); |
745 | } | 737 | } |
746 | 738 | ||
747 | /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ | 739 | /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ |
748 | WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); | 740 | WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); |
749 | 741 | ||
750 | MemWriteDOC(docptr, (unsigned char *) buf, len); | 742 | MemWriteDOC(docptr, (unsigned char *) buf, len); |
751 | 743 | ||
752 | /* Write ECC data to flash, the ECC info is generated by | 744 | /* Write ECC data to flash, the ECC info is generated by |
753 | the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ | 745 | the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ |
754 | DoC_Delay(docptr, 3); | 746 | DoC_Delay(docptr, 3); |
755 | 747 | ||
756 | /* Read the ECC data through the DiskOnChip ECC logic */ | 748 | /* Read the ECC data through the DiskOnChip ECC logic */ |
757 | for (i = 0; i < 6; i++) | 749 | for (i = 0; i < 6; i++) |
758 | eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); | 750 | eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); |
759 | 751 | ||
760 | /* disable the ECC engine */ | 752 | /* disable the ECC engine */ |
761 | WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); | 753 | WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); |
762 | 754 | ||
763 | /* Write the ECC data to flash */ | 755 | /* Write the ECC data to flash */ |
764 | MemWriteDOC(docptr, eccbuf, 6); | 756 | MemWriteDOC(docptr, eccbuf, 6); |
765 | 757 | ||
766 | if (!before) { | 758 | if (!before) { |
767 | /* Write the block status BLOCK_USED (0x5555) */ | 759 | /* Write the block status BLOCK_USED (0x5555) */ |
768 | WriteDOC(0x55, docptr, Mil_CDSN_IO+6); | 760 | WriteDOC(0x55, docptr, Mil_CDSN_IO+6); |
769 | WriteDOC(0x55, docptr, Mil_CDSN_IO+7); | 761 | WriteDOC(0x55, docptr, Mil_CDSN_IO+7); |
770 | } | 762 | } |
771 | 763 | ||
772 | #ifdef PSYCHO_DEBUG | 764 | #ifdef PSYCHO_DEBUG |
773 | printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", | 765 | printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", |
774 | (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], | 766 | (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], |
775 | eccbuf[4], eccbuf[5]); | 767 | eccbuf[4], eccbuf[5]); |
776 | #endif | 768 | #endif |
777 | 769 | ||
778 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); | 770 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); |
779 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); | 771 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); |
780 | 772 | ||
781 | /* Commit the Page Program command and wait for ready | 773 | /* Commit the Page Program command and wait for ready |
782 | see Software Requirement 11.4 item 1.*/ | 774 | see Software Requirement 11.4 item 1.*/ |
783 | DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); | 775 | DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); |
784 | DoC_WaitReady(docptr); | 776 | DoC_WaitReady(docptr); |
785 | 777 | ||
786 | /* Read the status of the flash device through CDSN IO register | 778 | /* Read the status of the flash device through CDSN IO register |
787 | see Software Requirement 11.4 item 5.*/ | 779 | see Software Requirement 11.4 item 5.*/ |
788 | DoC_Command(docptr, NAND_CMD_STATUS, 0); | 780 | DoC_Command(docptr, NAND_CMD_STATUS, 0); |
789 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); | 781 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); |
790 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); | 782 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); |
791 | DoC_Delay(docptr, 2); | 783 | DoC_Delay(docptr, 2); |
792 | if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { | 784 | if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { |
793 | printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to); | 785 | printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to); |
794 | /* Error in programming | 786 | /* Error in programming |
795 | FIXME: implement Bad Block Replacement (in nftl.c ??) */ | 787 | FIXME: implement Bad Block Replacement (in nftl.c ??) */ |
796 | ret = -EIO; | 788 | ret = -EIO; |
797 | } | 789 | } |
798 | dummy = ReadDOC(docptr, Mplus_LastDataRead); | 790 | dummy = ReadDOC(docptr, Mplus_LastDataRead); |
799 | 791 | ||
800 | /* Disable flash internally */ | 792 | /* Disable flash internally */ |
801 | WriteDOC(0, docptr, Mplus_FlashSelect); | 793 | WriteDOC(0, docptr, Mplus_FlashSelect); |
802 | 794 | ||
803 | /* Let the caller know we completed it */ | 795 | /* Let the caller know we completed it */ |
804 | *retlen = len; | 796 | *retlen = len; |
805 | 797 | ||
806 | return ret; | 798 | return ret; |
807 | } | 799 | } |
808 | 800 | ||
809 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, | 801 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, |
810 | struct mtd_oob_ops *ops) | 802 | struct mtd_oob_ops *ops) |
811 | { | 803 | { |
812 | loff_t fofs, base; | 804 | loff_t fofs, base; |
813 | struct DiskOnChip *this = mtd->priv; | 805 | struct DiskOnChip *this = mtd->priv; |
814 | void __iomem * docptr = this->virtadr; | 806 | void __iomem * docptr = this->virtadr; |
815 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; | 807 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; |
816 | size_t i, size, got, want; | 808 | size_t i, size, got, want; |
817 | uint8_t *buf = ops->oobbuf; | 809 | uint8_t *buf = ops->oobbuf; |
818 | size_t len = ops->len; | 810 | size_t len = ops->len; |
819 | 811 | ||
820 | BUG_ON(ops->mode != MTD_OPS_PLACE_OOB); | 812 | BUG_ON(ops->mode != MTD_OPS_PLACE_OOB); |
821 | 813 | ||
822 | ofs += ops->ooboffs; | 814 | ofs += ops->ooboffs; |
823 | 815 | ||
824 | DoC_CheckASIC(docptr); | 816 | DoC_CheckASIC(docptr); |
825 | 817 | ||
826 | /* Find the chip which is to be used and select it */ | 818 | /* Find the chip which is to be used and select it */ |
827 | if (this->curfloor != mychip->floor) { | 819 | if (this->curfloor != mychip->floor) { |
828 | DoC_SelectFloor(docptr, mychip->floor); | 820 | DoC_SelectFloor(docptr, mychip->floor); |
829 | DoC_SelectChip(docptr, mychip->chip); | 821 | DoC_SelectChip(docptr, mychip->chip); |
830 | } else if (this->curchip != mychip->chip) { | 822 | } else if (this->curchip != mychip->chip) { |
831 | DoC_SelectChip(docptr, mychip->chip); | 823 | DoC_SelectChip(docptr, mychip->chip); |
832 | } | 824 | } |
833 | this->curfloor = mychip->floor; | 825 | this->curfloor = mychip->floor; |
834 | this->curchip = mychip->chip; | 826 | this->curchip = mychip->chip; |
835 | 827 | ||
836 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ | 828 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ |
837 | WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); | 829 | WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); |
838 | 830 | ||
839 | /* disable the ECC engine */ | 831 | /* disable the ECC engine */ |
840 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); | 832 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); |
841 | DoC_WaitReady(docptr); | 833 | DoC_WaitReady(docptr); |
842 | 834 | ||
843 | /* Maximum of 16 bytes in the OOB region, so limit read to that */ | 835 | /* Maximum of 16 bytes in the OOB region, so limit read to that */ |
844 | if (len > 16) | 836 | if (len > 16) |
845 | len = 16; | 837 | len = 16; |
846 | got = 0; | 838 | got = 0; |
847 | want = len; | 839 | want = len; |
848 | 840 | ||
849 | for (i = 0; ((i < 3) && (want > 0)); i++) { | 841 | for (i = 0; ((i < 3) && (want > 0)); i++) { |
850 | /* Figure out which region we are accessing... */ | 842 | /* Figure out which region we are accessing... */ |
851 | fofs = ofs; | 843 | fofs = ofs; |
852 | base = ofs & 0xf; | 844 | base = ofs & 0xf; |
853 | if (!this->interleave) { | 845 | if (!this->interleave) { |
854 | DoC_Command(docptr, NAND_CMD_READOOB, 0); | 846 | DoC_Command(docptr, NAND_CMD_READOOB, 0); |
855 | size = 16 - base; | 847 | size = 16 - base; |
856 | } else if (base < 6) { | 848 | } else if (base < 6) { |
857 | DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); | 849 | DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); |
858 | size = 6 - base; | 850 | size = 6 - base; |
859 | } else if (base < 8) { | 851 | } else if (base < 8) { |
860 | DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0); | 852 | DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0); |
861 | size = 8 - base; | 853 | size = 8 - base; |
862 | } else { | 854 | } else { |
863 | DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0); | 855 | DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0); |
864 | size = 16 - base; | 856 | size = 16 - base; |
865 | } | 857 | } |
866 | if (size > want) | 858 | if (size > want) |
867 | size = want; | 859 | size = want; |
868 | 860 | ||
869 | /* Issue read command */ | 861 | /* Issue read command */ |
870 | DoC_Address(this, 3, fofs, 0, 0x00); | 862 | DoC_Address(this, 3, fofs, 0, 0x00); |
871 | WriteDOC(0, docptr, Mplus_FlashControl); | 863 | WriteDOC(0, docptr, Mplus_FlashControl); |
872 | DoC_WaitReady(docptr); | 864 | DoC_WaitReady(docptr); |
873 | 865 | ||
874 | ReadDOC(docptr, Mplus_ReadPipeInit); | 866 | ReadDOC(docptr, Mplus_ReadPipeInit); |
875 | ReadDOC(docptr, Mplus_ReadPipeInit); | 867 | ReadDOC(docptr, Mplus_ReadPipeInit); |
876 | MemReadDOC(docptr, &buf[got], size - 2); | 868 | MemReadDOC(docptr, &buf[got], size - 2); |
877 | buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead); | 869 | buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead); |
878 | buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead); | 870 | buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead); |
879 | 871 | ||
880 | ofs += size; | 872 | ofs += size; |
881 | got += size; | 873 | got += size; |
882 | want -= size; | 874 | want -= size; |
883 | } | 875 | } |
884 | 876 | ||
885 | /* Disable flash internally */ | 877 | /* Disable flash internally */ |
886 | WriteDOC(0, docptr, Mplus_FlashSelect); | 878 | WriteDOC(0, docptr, Mplus_FlashSelect); |
887 | 879 | ||
888 | ops->retlen = len; | 880 | ops->retlen = len; |
889 | return 0; | 881 | return 0; |
890 | } | 882 | } |
891 | 883 | ||
892 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, | 884 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, |
893 | struct mtd_oob_ops *ops) | 885 | struct mtd_oob_ops *ops) |
894 | { | 886 | { |
895 | volatile char dummy; | 887 | volatile char dummy; |
896 | loff_t fofs, base; | 888 | loff_t fofs, base; |
897 | struct DiskOnChip *this = mtd->priv; | 889 | struct DiskOnChip *this = mtd->priv; |
898 | void __iomem * docptr = this->virtadr; | 890 | void __iomem * docptr = this->virtadr; |
899 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; | 891 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; |
900 | size_t i, size, got, want; | 892 | size_t i, size, got, want; |
901 | int ret = 0; | 893 | int ret = 0; |
902 | uint8_t *buf = ops->oobbuf; | 894 | uint8_t *buf = ops->oobbuf; |
903 | size_t len = ops->len; | 895 | size_t len = ops->len; |
904 | 896 | ||
905 | BUG_ON(ops->mode != MTD_OPS_PLACE_OOB); | 897 | BUG_ON(ops->mode != MTD_OPS_PLACE_OOB); |
906 | 898 | ||
907 | ofs += ops->ooboffs; | 899 | ofs += ops->ooboffs; |
908 | 900 | ||
909 | DoC_CheckASIC(docptr); | 901 | DoC_CheckASIC(docptr); |
910 | 902 | ||
911 | /* Find the chip which is to be used and select it */ | 903 | /* Find the chip which is to be used and select it */ |
912 | if (this->curfloor != mychip->floor) { | 904 | if (this->curfloor != mychip->floor) { |
913 | DoC_SelectFloor(docptr, mychip->floor); | 905 | DoC_SelectFloor(docptr, mychip->floor); |
914 | DoC_SelectChip(docptr, mychip->chip); | 906 | DoC_SelectChip(docptr, mychip->chip); |
915 | } else if (this->curchip != mychip->chip) { | 907 | } else if (this->curchip != mychip->chip) { |
916 | DoC_SelectChip(docptr, mychip->chip); | 908 | DoC_SelectChip(docptr, mychip->chip); |
917 | } | 909 | } |
918 | this->curfloor = mychip->floor; | 910 | this->curfloor = mychip->floor; |
919 | this->curchip = mychip->chip; | 911 | this->curchip = mychip->chip; |
920 | 912 | ||
921 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ | 913 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ |
922 | WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); | 914 | WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); |
923 | 915 | ||
924 | 916 | ||
925 | /* Maximum of 16 bytes in the OOB region, so limit write to that */ | 917 | /* Maximum of 16 bytes in the OOB region, so limit write to that */ |
926 | if (len > 16) | 918 | if (len > 16) |
927 | len = 16; | 919 | len = 16; |
928 | got = 0; | 920 | got = 0; |
929 | want = len; | 921 | want = len; |
930 | 922 | ||
931 | for (i = 0; ((i < 3) && (want > 0)); i++) { | 923 | for (i = 0; ((i < 3) && (want > 0)); i++) { |
932 | /* Reset the chip, see Software Requirement 11.4 item 1. */ | 924 | /* Reset the chip, see Software Requirement 11.4 item 1. */ |
933 | DoC_Command(docptr, NAND_CMD_RESET, 0); | 925 | DoC_Command(docptr, NAND_CMD_RESET, 0); |
934 | DoC_WaitReady(docptr); | 926 | DoC_WaitReady(docptr); |
935 | 927 | ||
936 | /* Figure out which region we are accessing... */ | 928 | /* Figure out which region we are accessing... */ |
937 | fofs = ofs; | 929 | fofs = ofs; |
938 | base = ofs & 0x0f; | 930 | base = ofs & 0x0f; |
939 | if (!this->interleave) { | 931 | if (!this->interleave) { |
940 | WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); | 932 | WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); |
941 | size = 16 - base; | 933 | size = 16 - base; |
942 | } else if (base < 6) { | 934 | } else if (base < 6) { |
943 | WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); | 935 | WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); |
944 | size = 6 - base; | 936 | size = 6 - base; |
945 | } else if (base < 8) { | 937 | } else if (base < 8) { |
946 | WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); | 938 | WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); |
947 | size = 8 - base; | 939 | size = 8 - base; |
948 | } else { | 940 | } else { |
949 | WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); | 941 | WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); |
950 | size = 16 - base; | 942 | size = 16 - base; |
951 | } | 943 | } |
952 | if (size > want) | 944 | if (size > want) |
953 | size = want; | 945 | size = want; |
954 | 946 | ||
955 | /* Issue the Serial Data In command to initial the Page Program process */ | 947 | /* Issue the Serial Data In command to initial the Page Program process */ |
956 | DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); | 948 | DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); |
957 | DoC_Address(this, 3, fofs, 0, 0x00); | 949 | DoC_Address(this, 3, fofs, 0, 0x00); |
958 | 950 | ||
959 | /* Disable the ECC engine */ | 951 | /* Disable the ECC engine */ |
960 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); | 952 | WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); |
961 | 953 | ||
962 | /* Write the data via the internal pipeline through CDSN IO | 954 | /* Write the data via the internal pipeline through CDSN IO |
963 | register, see Pipelined Write Operations 11.2 */ | 955 | register, see Pipelined Write Operations 11.2 */ |
964 | MemWriteDOC(docptr, (unsigned char *) &buf[got], size); | 956 | MemWriteDOC(docptr, (unsigned char *) &buf[got], size); |
965 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); | 957 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); |
966 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); | 958 | WriteDOC(0x00, docptr, Mplus_WritePipeTerm); |
967 | 959 | ||
968 | /* Commit the Page Program command and wait for ready | 960 | /* Commit the Page Program command and wait for ready |
969 | see Software Requirement 11.4 item 1.*/ | 961 | see Software Requirement 11.4 item 1.*/ |
970 | DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); | 962 | DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); |
971 | DoC_WaitReady(docptr); | 963 | DoC_WaitReady(docptr); |
972 | 964 | ||
973 | /* Read the status of the flash device through CDSN IO register | 965 | /* Read the status of the flash device through CDSN IO register |
974 | see Software Requirement 11.4 item 5.*/ | 966 | see Software Requirement 11.4 item 5.*/ |
975 | DoC_Command(docptr, NAND_CMD_STATUS, 0x00); | 967 | DoC_Command(docptr, NAND_CMD_STATUS, 0x00); |
976 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); | 968 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); |
977 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); | 969 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); |
978 | DoC_Delay(docptr, 2); | 970 | DoC_Delay(docptr, 2); |
979 | if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { | 971 | if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { |
980 | printk("MTD: Error 0x%x programming oob at 0x%x\n", | 972 | printk("MTD: Error 0x%x programming oob at 0x%x\n", |
981 | dummy, (int)ofs); | 973 | dummy, (int)ofs); |
982 | /* FIXME: implement Bad Block Replacement */ | 974 | /* FIXME: implement Bad Block Replacement */ |
983 | ops->retlen = 0; | 975 | ops->retlen = 0; |
984 | ret = -EIO; | 976 | ret = -EIO; |
985 | } | 977 | } |
986 | dummy = ReadDOC(docptr, Mplus_LastDataRead); | 978 | dummy = ReadDOC(docptr, Mplus_LastDataRead); |
987 | 979 | ||
988 | ofs += size; | 980 | ofs += size; |
989 | got += size; | 981 | got += size; |
990 | want -= size; | 982 | want -= size; |
991 | } | 983 | } |
992 | 984 | ||
993 | /* Disable flash internally */ | 985 | /* Disable flash internally */ |
994 | WriteDOC(0, docptr, Mplus_FlashSelect); | 986 | WriteDOC(0, docptr, Mplus_FlashSelect); |
995 | 987 | ||
996 | ops->retlen = len; | 988 | ops->retlen = len; |
997 | return ret; | 989 | return ret; |
998 | } | 990 | } |
999 | 991 | ||
1000 | int doc_erase(struct mtd_info *mtd, struct erase_info *instr) | 992 | int doc_erase(struct mtd_info *mtd, struct erase_info *instr) |
1001 | { | 993 | { |
1002 | volatile char dummy; | 994 | volatile char dummy; |
1003 | struct DiskOnChip *this = mtd->priv; | 995 | struct DiskOnChip *this = mtd->priv; |
1004 | __u32 ofs = instr->addr; | 996 | __u32 ofs = instr->addr; |
1005 | __u32 len = instr->len; | 997 | __u32 len = instr->len; |
1006 | void __iomem * docptr = this->virtadr; | 998 | void __iomem * docptr = this->virtadr; |
1007 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; | 999 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; |
1008 | 1000 | ||
1009 | DoC_CheckASIC(docptr); | 1001 | DoC_CheckASIC(docptr); |
1010 | 1002 | ||
1011 | if (len != mtd->erasesize) | 1003 | if (len != mtd->erasesize) |
1012 | printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n", | 1004 | printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n", |
1013 | len, mtd->erasesize); | 1005 | len, mtd->erasesize); |
1014 | 1006 | ||
1015 | /* Find the chip which is to be used and select it */ | 1007 | /* Find the chip which is to be used and select it */ |
1016 | if (this->curfloor != mychip->floor) { | 1008 | if (this->curfloor != mychip->floor) { |
1017 | DoC_SelectFloor(docptr, mychip->floor); | 1009 | DoC_SelectFloor(docptr, mychip->floor); |
1018 | DoC_SelectChip(docptr, mychip->chip); | 1010 | DoC_SelectChip(docptr, mychip->chip); |
1019 | } else if (this->curchip != mychip->chip) { | 1011 | } else if (this->curchip != mychip->chip) { |
1020 | DoC_SelectChip(docptr, mychip->chip); | 1012 | DoC_SelectChip(docptr, mychip->chip); |
1021 | } | 1013 | } |
1022 | this->curfloor = mychip->floor; | 1014 | this->curfloor = mychip->floor; |
1023 | this->curchip = mychip->chip; | 1015 | this->curchip = mychip->chip; |
1024 | 1016 | ||
1025 | instr->state = MTD_ERASE_PENDING; | 1017 | instr->state = MTD_ERASE_PENDING; |
1026 | 1018 | ||
1027 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ | 1019 | /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ |
1028 | WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); | 1020 | WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); |
1029 | 1021 | ||
1030 | DoC_Command(docptr, NAND_CMD_RESET, 0x00); | 1022 | DoC_Command(docptr, NAND_CMD_RESET, 0x00); |
1031 | DoC_WaitReady(docptr); | 1023 | DoC_WaitReady(docptr); |
1032 | 1024 | ||
1033 | DoC_Command(docptr, NAND_CMD_ERASE1, 0); | 1025 | DoC_Command(docptr, NAND_CMD_ERASE1, 0); |
1034 | DoC_Address(this, 2, ofs, 0, 0x00); | 1026 | DoC_Address(this, 2, ofs, 0, 0x00); |
1035 | DoC_Command(docptr, NAND_CMD_ERASE2, 0); | 1027 | DoC_Command(docptr, NAND_CMD_ERASE2, 0); |
1036 | DoC_WaitReady(docptr); | 1028 | DoC_WaitReady(docptr); |
1037 | instr->state = MTD_ERASING; | 1029 | instr->state = MTD_ERASING; |
1038 | 1030 | ||
1039 | /* Read the status of the flash device through CDSN IO register | 1031 | /* Read the status of the flash device through CDSN IO register |
1040 | see Software Requirement 11.4 item 5. */ | 1032 | see Software Requirement 11.4 item 5. */ |
1041 | DoC_Command(docptr, NAND_CMD_STATUS, 0); | 1033 | DoC_Command(docptr, NAND_CMD_STATUS, 0); |
1042 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); | 1034 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); |
1043 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); | 1035 | dummy = ReadDOC(docptr, Mplus_ReadPipeInit); |
1044 | if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { | 1036 | if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { |
1045 | printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); | 1037 | printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); |
1046 | /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ | 1038 | /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ |
1047 | instr->state = MTD_ERASE_FAILED; | 1039 | instr->state = MTD_ERASE_FAILED; |
1048 | } else { | 1040 | } else { |
1049 | instr->state = MTD_ERASE_DONE; | 1041 | instr->state = MTD_ERASE_DONE; |
1050 | } | 1042 | } |
1051 | dummy = ReadDOC(docptr, Mplus_LastDataRead); | 1043 | dummy = ReadDOC(docptr, Mplus_LastDataRead); |
1052 | 1044 | ||
1053 | /* Disable flash internally */ | 1045 | /* Disable flash internally */ |
1054 | WriteDOC(0, docptr, Mplus_FlashSelect); | 1046 | WriteDOC(0, docptr, Mplus_FlashSelect); |
1055 | 1047 | ||
1056 | mtd_erase_callback(instr); | 1048 | mtd_erase_callback(instr); |
1057 | 1049 | ||
1058 | return 0; | 1050 | return 0; |
1059 | } | 1051 | } |
1060 | 1052 | ||
1061 | /**************************************************************************** | 1053 | /**************************************************************************** |
1062 | * | 1054 | * |
1063 | * Module stuff | 1055 | * Module stuff |
1064 | * | 1056 | * |
1065 | ****************************************************************************/ | 1057 | ****************************************************************************/ |
1066 | 1058 | ||
1067 | static void __exit cleanup_doc2001plus(void) | 1059 | static void __exit cleanup_doc2001plus(void) |
1068 | { | 1060 | { |
1069 | struct mtd_info *mtd; | 1061 | struct mtd_info *mtd; |
1070 | struct DiskOnChip *this; | 1062 | struct DiskOnChip *this; |
1071 | 1063 | ||
1072 | while ((mtd=docmilpluslist)) { | 1064 | while ((mtd=docmilpluslist)) { |
1073 | this = mtd->priv; | 1065 | this = mtd->priv; |
1074 | docmilpluslist = this->nextdoc; | 1066 | docmilpluslist = this->nextdoc; |
1075 | 1067 | ||
1076 | mtd_device_unregister(mtd); | 1068 | mtd_device_unregister(mtd); |
1077 | 1069 | ||
1078 | iounmap(this->virtadr); | 1070 | iounmap(this->virtadr); |
1079 | kfree(this->chips); | 1071 | kfree(this->chips); |
1080 | kfree(mtd); | 1072 | kfree(mtd); |
1081 | } | 1073 | } |
1082 | } | 1074 | } |
1083 | 1075 | ||
1084 | module_exit(cleanup_doc2001plus); | 1076 | module_exit(cleanup_doc2001plus); |
1085 | 1077 | ||
1086 | MODULE_LICENSE("GPL"); | 1078 | MODULE_LICENSE("GPL"); |
1087 | MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al."); | 1079 | MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al."); |
1088 | MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); | 1080 | MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); |
1089 | 1081 |
drivers/mtd/devices/docg3.c
1 | /* | 1 | /* |
2 | * Handles the M-Systems DiskOnChip G3 chip | 2 | * Handles the M-Systems DiskOnChip G3 chip |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Robert Jarzmik | 4 | * Copyright (C) 2011 Robert Jarzmik |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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 | 7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 | * | 19 | * |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/errno.h> | 24 | #include <linux/errno.h> |
25 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
26 | #include <linux/string.h> | 26 | #include <linux/string.h> |
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/io.h> | 28 | #include <linux/io.h> |
29 | #include <linux/delay.h> | 29 | #include <linux/delay.h> |
30 | #include <linux/mtd/mtd.h> | 30 | #include <linux/mtd/mtd.h> |
31 | #include <linux/mtd/partitions.h> | 31 | #include <linux/mtd/partitions.h> |
32 | #include <linux/bitmap.h> | 32 | #include <linux/bitmap.h> |
33 | #include <linux/bitrev.h> | 33 | #include <linux/bitrev.h> |
34 | #include <linux/bch.h> | 34 | #include <linux/bch.h> |
35 | 35 | ||
36 | #include <linux/debugfs.h> | 36 | #include <linux/debugfs.h> |
37 | #include <linux/seq_file.h> | 37 | #include <linux/seq_file.h> |
38 | 38 | ||
39 | #define CREATE_TRACE_POINTS | 39 | #define CREATE_TRACE_POINTS |
40 | #include "docg3.h" | 40 | #include "docg3.h" |
41 | 41 | ||
42 | /* | 42 | /* |
43 | * This driver handles the DiskOnChip G3 flash memory. | 43 | * This driver handles the DiskOnChip G3 flash memory. |
44 | * | 44 | * |
45 | * As no specification is available from M-Systems/Sandisk, this drivers lacks | 45 | * As no specification is available from M-Systems/Sandisk, this drivers lacks |
46 | * several functions available on the chip, as : | 46 | * several functions available on the chip, as : |
47 | * - IPL write | 47 | * - IPL write |
48 | * | 48 | * |
49 | * The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and | 49 | * The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and |
50 | * the driver assumes a 16bits data bus. | 50 | * the driver assumes a 16bits data bus. |
51 | * | 51 | * |
52 | * DocG3 relies on 2 ECC algorithms, which are handled in hardware : | 52 | * DocG3 relies on 2 ECC algorithms, which are handled in hardware : |
53 | * - a 1 byte Hamming code stored in the OOB for each page | 53 | * - a 1 byte Hamming code stored in the OOB for each page |
54 | * - a 7 bytes BCH code stored in the OOB for each page | 54 | * - a 7 bytes BCH code stored in the OOB for each page |
55 | * The BCH ECC is : | 55 | * The BCH ECC is : |
56 | * - BCH is in GF(2^14) | 56 | * - BCH is in GF(2^14) |
57 | * - BCH is over data of 520 bytes (512 page + 7 page_info bytes | 57 | * - BCH is over data of 520 bytes (512 page + 7 page_info bytes |
58 | * + 1 hamming byte) | 58 | * + 1 hamming byte) |
59 | * - BCH can correct up to 4 bits (t = 4) | 59 | * - BCH can correct up to 4 bits (t = 4) |
60 | * - BCH syndroms are calculated in hardware, and checked in hardware as well | 60 | * - BCH syndroms are calculated in hardware, and checked in hardware as well |
61 | * | 61 | * |
62 | */ | 62 | */ |
63 | 63 | ||
64 | static unsigned int reliable_mode; | 64 | static unsigned int reliable_mode; |
65 | module_param(reliable_mode, uint, 0); | 65 | module_param(reliable_mode, uint, 0); |
66 | MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, " | 66 | MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, " |
67 | "2=reliable) : MLC normal operations are in normal mode"); | 67 | "2=reliable) : MLC normal operations are in normal mode"); |
68 | 68 | ||
69 | /** | 69 | /** |
70 | * struct docg3_oobinfo - DiskOnChip G3 OOB layout | 70 | * struct docg3_oobinfo - DiskOnChip G3 OOB layout |
71 | * @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC) | 71 | * @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC) |
72 | * @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC) | 72 | * @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC) |
73 | * @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15 | 73 | * @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15 |
74 | * @oobavail: 8 available bytes remaining after ECC toll | 74 | * @oobavail: 8 available bytes remaining after ECC toll |
75 | */ | 75 | */ |
76 | static struct nand_ecclayout docg3_oobinfo = { | 76 | static struct nand_ecclayout docg3_oobinfo = { |
77 | .eccbytes = 8, | 77 | .eccbytes = 8, |
78 | .eccpos = {7, 8, 9, 10, 11, 12, 13, 14}, | 78 | .eccpos = {7, 8, 9, 10, 11, 12, 13, 14}, |
79 | .oobfree = {{0, 7}, {15, 1} }, | 79 | .oobfree = {{0, 7}, {15, 1} }, |
80 | .oobavail = 8, | 80 | .oobavail = 8, |
81 | }; | 81 | }; |
82 | 82 | ||
83 | static inline u8 doc_readb(struct docg3 *docg3, u16 reg) | 83 | static inline u8 doc_readb(struct docg3 *docg3, u16 reg) |
84 | { | 84 | { |
85 | u8 val = readb(docg3->cascade->base + reg); | 85 | u8 val = readb(docg3->cascade->base + reg); |
86 | 86 | ||
87 | trace_docg3_io(0, 8, reg, (int)val); | 87 | trace_docg3_io(0, 8, reg, (int)val); |
88 | return val; | 88 | return val; |
89 | } | 89 | } |
90 | 90 | ||
91 | static inline u16 doc_readw(struct docg3 *docg3, u16 reg) | 91 | static inline u16 doc_readw(struct docg3 *docg3, u16 reg) |
92 | { | 92 | { |
93 | u16 val = readw(docg3->cascade->base + reg); | 93 | u16 val = readw(docg3->cascade->base + reg); |
94 | 94 | ||
95 | trace_docg3_io(0, 16, reg, (int)val); | 95 | trace_docg3_io(0, 16, reg, (int)val); |
96 | return val; | 96 | return val; |
97 | } | 97 | } |
98 | 98 | ||
99 | static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg) | 99 | static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg) |
100 | { | 100 | { |
101 | writeb(val, docg3->cascade->base + reg); | 101 | writeb(val, docg3->cascade->base + reg); |
102 | trace_docg3_io(1, 8, reg, val); | 102 | trace_docg3_io(1, 8, reg, val); |
103 | } | 103 | } |
104 | 104 | ||
105 | static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg) | 105 | static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg) |
106 | { | 106 | { |
107 | writew(val, docg3->cascade->base + reg); | 107 | writew(val, docg3->cascade->base + reg); |
108 | trace_docg3_io(1, 16, reg, val); | 108 | trace_docg3_io(1, 16, reg, val); |
109 | } | 109 | } |
110 | 110 | ||
111 | static inline void doc_flash_command(struct docg3 *docg3, u8 cmd) | 111 | static inline void doc_flash_command(struct docg3 *docg3, u8 cmd) |
112 | { | 112 | { |
113 | doc_writeb(docg3, cmd, DOC_FLASHCOMMAND); | 113 | doc_writeb(docg3, cmd, DOC_FLASHCOMMAND); |
114 | } | 114 | } |
115 | 115 | ||
116 | static inline void doc_flash_sequence(struct docg3 *docg3, u8 seq) | 116 | static inline void doc_flash_sequence(struct docg3 *docg3, u8 seq) |
117 | { | 117 | { |
118 | doc_writeb(docg3, seq, DOC_FLASHSEQUENCE); | 118 | doc_writeb(docg3, seq, DOC_FLASHSEQUENCE); |
119 | } | 119 | } |
120 | 120 | ||
121 | static inline void doc_flash_address(struct docg3 *docg3, u8 addr) | 121 | static inline void doc_flash_address(struct docg3 *docg3, u8 addr) |
122 | { | 122 | { |
123 | doc_writeb(docg3, addr, DOC_FLASHADDRESS); | 123 | doc_writeb(docg3, addr, DOC_FLASHADDRESS); |
124 | } | 124 | } |
125 | 125 | ||
126 | static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL }; | 126 | static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL }; |
127 | 127 | ||
128 | static int doc_register_readb(struct docg3 *docg3, int reg) | 128 | static int doc_register_readb(struct docg3 *docg3, int reg) |
129 | { | 129 | { |
130 | u8 val; | 130 | u8 val; |
131 | 131 | ||
132 | doc_writew(docg3, reg, DOC_READADDRESS); | 132 | doc_writew(docg3, reg, DOC_READADDRESS); |
133 | val = doc_readb(docg3, reg); | 133 | val = doc_readb(docg3, reg); |
134 | doc_vdbg("Read register %04x : %02x\n", reg, val); | 134 | doc_vdbg("Read register %04x : %02x\n", reg, val); |
135 | return val; | 135 | return val; |
136 | } | 136 | } |
137 | 137 | ||
138 | static int doc_register_readw(struct docg3 *docg3, int reg) | 138 | static int doc_register_readw(struct docg3 *docg3, int reg) |
139 | { | 139 | { |
140 | u16 val; | 140 | u16 val; |
141 | 141 | ||
142 | doc_writew(docg3, reg, DOC_READADDRESS); | 142 | doc_writew(docg3, reg, DOC_READADDRESS); |
143 | val = doc_readw(docg3, reg); | 143 | val = doc_readw(docg3, reg); |
144 | doc_vdbg("Read register %04x : %04x\n", reg, val); | 144 | doc_vdbg("Read register %04x : %04x\n", reg, val); |
145 | return val; | 145 | return val; |
146 | } | 146 | } |
147 | 147 | ||
148 | /** | 148 | /** |
149 | * doc_delay - delay docg3 operations | 149 | * doc_delay - delay docg3 operations |
150 | * @docg3: the device | 150 | * @docg3: the device |
151 | * @nbNOPs: the number of NOPs to issue | 151 | * @nbNOPs: the number of NOPs to issue |
152 | * | 152 | * |
153 | * As no specification is available, the right timings between chip commands are | 153 | * As no specification is available, the right timings between chip commands are |
154 | * unknown. The only available piece of information are the observed nops on a | 154 | * unknown. The only available piece of information are the observed nops on a |
155 | * working docg3 chip. | 155 | * working docg3 chip. |
156 | * Therefore, doc_delay relies on a busy loop of NOPs, instead of scheduler | 156 | * Therefore, doc_delay relies on a busy loop of NOPs, instead of scheduler |
157 | * friendlier msleep() functions or blocking mdelay(). | 157 | * friendlier msleep() functions or blocking mdelay(). |
158 | */ | 158 | */ |
159 | static void doc_delay(struct docg3 *docg3, int nbNOPs) | 159 | static void doc_delay(struct docg3 *docg3, int nbNOPs) |
160 | { | 160 | { |
161 | int i; | 161 | int i; |
162 | 162 | ||
163 | doc_vdbg("NOP x %d\n", nbNOPs); | 163 | doc_vdbg("NOP x %d\n", nbNOPs); |
164 | for (i = 0; i < nbNOPs; i++) | 164 | for (i = 0; i < nbNOPs; i++) |
165 | doc_writeb(docg3, 0, DOC_NOP); | 165 | doc_writeb(docg3, 0, DOC_NOP); |
166 | } | 166 | } |
167 | 167 | ||
168 | static int is_prot_seq_error(struct docg3 *docg3) | 168 | static int is_prot_seq_error(struct docg3 *docg3) |
169 | { | 169 | { |
170 | int ctrl; | 170 | int ctrl; |
171 | 171 | ||
172 | ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); | 172 | ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); |
173 | return ctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR); | 173 | return ctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR); |
174 | } | 174 | } |
175 | 175 | ||
176 | static int doc_is_ready(struct docg3 *docg3) | 176 | static int doc_is_ready(struct docg3 *docg3) |
177 | { | 177 | { |
178 | int ctrl; | 178 | int ctrl; |
179 | 179 | ||
180 | ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); | 180 | ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); |
181 | return ctrl & DOC_CTRL_FLASHREADY; | 181 | return ctrl & DOC_CTRL_FLASHREADY; |
182 | } | 182 | } |
183 | 183 | ||
184 | static int doc_wait_ready(struct docg3 *docg3) | 184 | static int doc_wait_ready(struct docg3 *docg3) |
185 | { | 185 | { |
186 | int maxWaitCycles = 100; | 186 | int maxWaitCycles = 100; |
187 | 187 | ||
188 | do { | 188 | do { |
189 | doc_delay(docg3, 4); | 189 | doc_delay(docg3, 4); |
190 | cpu_relax(); | 190 | cpu_relax(); |
191 | } while (!doc_is_ready(docg3) && maxWaitCycles--); | 191 | } while (!doc_is_ready(docg3) && maxWaitCycles--); |
192 | doc_delay(docg3, 2); | 192 | doc_delay(docg3, 2); |
193 | if (maxWaitCycles > 0) | 193 | if (maxWaitCycles > 0) |
194 | return 0; | 194 | return 0; |
195 | else | 195 | else |
196 | return -EIO; | 196 | return -EIO; |
197 | } | 197 | } |
198 | 198 | ||
199 | static int doc_reset_seq(struct docg3 *docg3) | 199 | static int doc_reset_seq(struct docg3 *docg3) |
200 | { | 200 | { |
201 | int ret; | 201 | int ret; |
202 | 202 | ||
203 | doc_writeb(docg3, 0x10, DOC_FLASHCONTROL); | 203 | doc_writeb(docg3, 0x10, DOC_FLASHCONTROL); |
204 | doc_flash_sequence(docg3, DOC_SEQ_RESET); | 204 | doc_flash_sequence(docg3, DOC_SEQ_RESET); |
205 | doc_flash_command(docg3, DOC_CMD_RESET); | 205 | doc_flash_command(docg3, DOC_CMD_RESET); |
206 | doc_delay(docg3, 2); | 206 | doc_delay(docg3, 2); |
207 | ret = doc_wait_ready(docg3); | 207 | ret = doc_wait_ready(docg3); |
208 | 208 | ||
209 | doc_dbg("doc_reset_seq() -> isReady=%s\n", ret ? "false" : "true"); | 209 | doc_dbg("doc_reset_seq() -> isReady=%s\n", ret ? "false" : "true"); |
210 | return ret; | 210 | return ret; |
211 | } | 211 | } |
212 | 212 | ||
213 | /** | 213 | /** |
214 | * doc_read_data_area - Read data from data area | 214 | * doc_read_data_area - Read data from data area |
215 | * @docg3: the device | 215 | * @docg3: the device |
216 | * @buf: the buffer to fill in (might be NULL is dummy reads) | 216 | * @buf: the buffer to fill in (might be NULL is dummy reads) |
217 | * @len: the length to read | 217 | * @len: the length to read |
218 | * @first: first time read, DOC_READADDRESS should be set | 218 | * @first: first time read, DOC_READADDRESS should be set |
219 | * | 219 | * |
220 | * Reads bytes from flash data. Handles the single byte / even bytes reads. | 220 | * Reads bytes from flash data. Handles the single byte / even bytes reads. |
221 | */ | 221 | */ |
222 | static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, | 222 | static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, |
223 | int first) | 223 | int first) |
224 | { | 224 | { |
225 | int i, cdr, len4; | 225 | int i, cdr, len4; |
226 | u16 data16, *dst16; | 226 | u16 data16, *dst16; |
227 | u8 data8, *dst8; | 227 | u8 data8, *dst8; |
228 | 228 | ||
229 | doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len); | 229 | doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len); |
230 | cdr = len & 0x1; | 230 | cdr = len & 0x1; |
231 | len4 = len - cdr; | 231 | len4 = len - cdr; |
232 | 232 | ||
233 | if (first) | 233 | if (first) |
234 | doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS); | 234 | doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS); |
235 | dst16 = buf; | 235 | dst16 = buf; |
236 | for (i = 0; i < len4; i += 2) { | 236 | for (i = 0; i < len4; i += 2) { |
237 | data16 = doc_readw(docg3, DOC_IOSPACE_DATA); | 237 | data16 = doc_readw(docg3, DOC_IOSPACE_DATA); |
238 | if (dst16) { | 238 | if (dst16) { |
239 | *dst16 = data16; | 239 | *dst16 = data16; |
240 | dst16++; | 240 | dst16++; |
241 | } | 241 | } |
242 | } | 242 | } |
243 | 243 | ||
244 | if (cdr) { | 244 | if (cdr) { |
245 | doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE, | 245 | doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE, |
246 | DOC_READADDRESS); | 246 | DOC_READADDRESS); |
247 | doc_delay(docg3, 1); | 247 | doc_delay(docg3, 1); |
248 | dst8 = (u8 *)dst16; | 248 | dst8 = (u8 *)dst16; |
249 | for (i = 0; i < cdr; i++) { | 249 | for (i = 0; i < cdr; i++) { |
250 | data8 = doc_readb(docg3, DOC_IOSPACE_DATA); | 250 | data8 = doc_readb(docg3, DOC_IOSPACE_DATA); |
251 | if (dst8) { | 251 | if (dst8) { |
252 | *dst8 = data8; | 252 | *dst8 = data8; |
253 | dst8++; | 253 | dst8++; |
254 | } | 254 | } |
255 | } | 255 | } |
256 | } | 256 | } |
257 | } | 257 | } |
258 | 258 | ||
259 | /** | 259 | /** |
260 | * doc_write_data_area - Write data into data area | 260 | * doc_write_data_area - Write data into data area |
261 | * @docg3: the device | 261 | * @docg3: the device |
262 | * @buf: the buffer to get input bytes from | 262 | * @buf: the buffer to get input bytes from |
263 | * @len: the length to write | 263 | * @len: the length to write |
264 | * | 264 | * |
265 | * Writes bytes into flash data. Handles the single byte / even bytes writes. | 265 | * Writes bytes into flash data. Handles the single byte / even bytes writes. |
266 | */ | 266 | */ |
267 | static void doc_write_data_area(struct docg3 *docg3, const void *buf, int len) | 267 | static void doc_write_data_area(struct docg3 *docg3, const void *buf, int len) |
268 | { | 268 | { |
269 | int i, cdr, len4; | 269 | int i, cdr, len4; |
270 | u16 *src16; | 270 | u16 *src16; |
271 | u8 *src8; | 271 | u8 *src8; |
272 | 272 | ||
273 | doc_dbg("doc_write_data_area(buf=%p, len=%d)\n", buf, len); | 273 | doc_dbg("doc_write_data_area(buf=%p, len=%d)\n", buf, len); |
274 | cdr = len & 0x3; | 274 | cdr = len & 0x3; |
275 | len4 = len - cdr; | 275 | len4 = len - cdr; |
276 | 276 | ||
277 | doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS); | 277 | doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS); |
278 | src16 = (u16 *)buf; | 278 | src16 = (u16 *)buf; |
279 | for (i = 0; i < len4; i += 2) { | 279 | for (i = 0; i < len4; i += 2) { |
280 | doc_writew(docg3, *src16, DOC_IOSPACE_DATA); | 280 | doc_writew(docg3, *src16, DOC_IOSPACE_DATA); |
281 | src16++; | 281 | src16++; |
282 | } | 282 | } |
283 | 283 | ||
284 | src8 = (u8 *)src16; | 284 | src8 = (u8 *)src16; |
285 | for (i = 0; i < cdr; i++) { | 285 | for (i = 0; i < cdr; i++) { |
286 | doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE, | 286 | doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE, |
287 | DOC_READADDRESS); | 287 | DOC_READADDRESS); |
288 | doc_writeb(docg3, *src8, DOC_IOSPACE_DATA); | 288 | doc_writeb(docg3, *src8, DOC_IOSPACE_DATA); |
289 | src8++; | 289 | src8++; |
290 | } | 290 | } |
291 | } | 291 | } |
292 | 292 | ||
293 | /** | 293 | /** |
294 | * doc_set_data_mode - Sets the flash to normal or reliable data mode | 294 | * doc_set_data_mode - Sets the flash to normal or reliable data mode |
295 | * @docg3: the device | 295 | * @docg3: the device |
296 | * | 296 | * |
297 | * The reliable data mode is a bit slower than the fast mode, but less errors | 297 | * The reliable data mode is a bit slower than the fast mode, but less errors |
298 | * occur. Entering the reliable mode cannot be done without entering the fast | 298 | * occur. Entering the reliable mode cannot be done without entering the fast |
299 | * mode first. | 299 | * mode first. |
300 | * | 300 | * |
301 | * In reliable mode, pages 2*n and 2*n+1 are clones. Writing to page 0 of blocks | 301 | * In reliable mode, pages 2*n and 2*n+1 are clones. Writing to page 0 of blocks |
302 | * (4,5) make the hardware write also to page 1 of blocks blocks(4,5). Reading | 302 | * (4,5) make the hardware write also to page 1 of blocks blocks(4,5). Reading |
303 | * from page 0 of blocks (4,5) or from page 1 of blocks (4,5) gives the same | 303 | * from page 0 of blocks (4,5) or from page 1 of blocks (4,5) gives the same |
304 | * result, which is a logical and between bytes from page 0 and page 1 (which is | 304 | * result, which is a logical and between bytes from page 0 and page 1 (which is |
305 | * consistent with the fact that writing to a page is _clearing_ bits of that | 305 | * consistent with the fact that writing to a page is _clearing_ bits of that |
306 | * page). | 306 | * page). |
307 | */ | 307 | */ |
308 | static void doc_set_reliable_mode(struct docg3 *docg3) | 308 | static void doc_set_reliable_mode(struct docg3 *docg3) |
309 | { | 309 | { |
310 | static char *strmode[] = { "normal", "fast", "reliable", "invalid" }; | 310 | static char *strmode[] = { "normal", "fast", "reliable", "invalid" }; |
311 | 311 | ||
312 | doc_dbg("doc_set_reliable_mode(%s)\n", strmode[docg3->reliable]); | 312 | doc_dbg("doc_set_reliable_mode(%s)\n", strmode[docg3->reliable]); |
313 | switch (docg3->reliable) { | 313 | switch (docg3->reliable) { |
314 | case 0: | 314 | case 0: |
315 | break; | 315 | break; |
316 | case 1: | 316 | case 1: |
317 | doc_flash_sequence(docg3, DOC_SEQ_SET_FASTMODE); | 317 | doc_flash_sequence(docg3, DOC_SEQ_SET_FASTMODE); |
318 | doc_flash_command(docg3, DOC_CMD_FAST_MODE); | 318 | doc_flash_command(docg3, DOC_CMD_FAST_MODE); |
319 | break; | 319 | break; |
320 | case 2: | 320 | case 2: |
321 | doc_flash_sequence(docg3, DOC_SEQ_SET_RELIABLEMODE); | 321 | doc_flash_sequence(docg3, DOC_SEQ_SET_RELIABLEMODE); |
322 | doc_flash_command(docg3, DOC_CMD_FAST_MODE); | 322 | doc_flash_command(docg3, DOC_CMD_FAST_MODE); |
323 | doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE); | 323 | doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE); |
324 | break; | 324 | break; |
325 | default: | 325 | default: |
326 | doc_err("doc_set_reliable_mode(): invalid mode\n"); | 326 | doc_err("doc_set_reliable_mode(): invalid mode\n"); |
327 | break; | 327 | break; |
328 | } | 328 | } |
329 | doc_delay(docg3, 2); | 329 | doc_delay(docg3, 2); |
330 | } | 330 | } |
331 | 331 | ||
332 | /** | 332 | /** |
333 | * doc_set_asic_mode - Set the ASIC mode | 333 | * doc_set_asic_mode - Set the ASIC mode |
334 | * @docg3: the device | 334 | * @docg3: the device |
335 | * @mode: the mode | 335 | * @mode: the mode |
336 | * | 336 | * |
337 | * The ASIC can work in 3 modes : | 337 | * The ASIC can work in 3 modes : |
338 | * - RESET: all registers are zeroed | 338 | * - RESET: all registers are zeroed |
339 | * - NORMAL: receives and handles commands | 339 | * - NORMAL: receives and handles commands |
340 | * - POWERDOWN: minimal poweruse, flash parts shut off | 340 | * - POWERDOWN: minimal poweruse, flash parts shut off |
341 | */ | 341 | */ |
342 | static void doc_set_asic_mode(struct docg3 *docg3, u8 mode) | 342 | static void doc_set_asic_mode(struct docg3 *docg3, u8 mode) |
343 | { | 343 | { |
344 | int i; | 344 | int i; |
345 | 345 | ||
346 | for (i = 0; i < 12; i++) | 346 | for (i = 0; i < 12; i++) |
347 | doc_readb(docg3, DOC_IOSPACE_IPL); | 347 | doc_readb(docg3, DOC_IOSPACE_IPL); |
348 | 348 | ||
349 | mode |= DOC_ASICMODE_MDWREN; | 349 | mode |= DOC_ASICMODE_MDWREN; |
350 | doc_dbg("doc_set_asic_mode(%02x)\n", mode); | 350 | doc_dbg("doc_set_asic_mode(%02x)\n", mode); |
351 | doc_writeb(docg3, mode, DOC_ASICMODE); | 351 | doc_writeb(docg3, mode, DOC_ASICMODE); |
352 | doc_writeb(docg3, ~mode, DOC_ASICMODECONFIRM); | 352 | doc_writeb(docg3, ~mode, DOC_ASICMODECONFIRM); |
353 | doc_delay(docg3, 1); | 353 | doc_delay(docg3, 1); |
354 | } | 354 | } |
355 | 355 | ||
356 | /** | 356 | /** |
357 | * doc_set_device_id - Sets the devices id for cascaded G3 chips | 357 | * doc_set_device_id - Sets the devices id for cascaded G3 chips |
358 | * @docg3: the device | 358 | * @docg3: the device |
359 | * @id: the chip to select (amongst 0, 1, 2, 3) | 359 | * @id: the chip to select (amongst 0, 1, 2, 3) |
360 | * | 360 | * |
361 | * There can be 4 cascaded G3 chips. This function selects the one which will | 361 | * There can be 4 cascaded G3 chips. This function selects the one which will |
362 | * should be the active one. | 362 | * should be the active one. |
363 | */ | 363 | */ |
364 | static void doc_set_device_id(struct docg3 *docg3, int id) | 364 | static void doc_set_device_id(struct docg3 *docg3, int id) |
365 | { | 365 | { |
366 | u8 ctrl; | 366 | u8 ctrl; |
367 | 367 | ||
368 | doc_dbg("doc_set_device_id(%d)\n", id); | 368 | doc_dbg("doc_set_device_id(%d)\n", id); |
369 | doc_writeb(docg3, id, DOC_DEVICESELECT); | 369 | doc_writeb(docg3, id, DOC_DEVICESELECT); |
370 | ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); | 370 | ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); |
371 | 371 | ||
372 | ctrl &= ~DOC_CTRL_VIOLATION; | 372 | ctrl &= ~DOC_CTRL_VIOLATION; |
373 | ctrl |= DOC_CTRL_CE; | 373 | ctrl |= DOC_CTRL_CE; |
374 | doc_writeb(docg3, ctrl, DOC_FLASHCONTROL); | 374 | doc_writeb(docg3, ctrl, DOC_FLASHCONTROL); |
375 | } | 375 | } |
376 | 376 | ||
377 | /** | 377 | /** |
378 | * doc_set_extra_page_mode - Change flash page layout | 378 | * doc_set_extra_page_mode - Change flash page layout |
379 | * @docg3: the device | 379 | * @docg3: the device |
380 | * | 380 | * |
381 | * Normally, the flash page is split into the data (512 bytes) and the out of | 381 | * Normally, the flash page is split into the data (512 bytes) and the out of |
382 | * band data (16 bytes). For each, 4 more bytes can be accessed, where the wear | 382 | * band data (16 bytes). For each, 4 more bytes can be accessed, where the wear |
383 | * leveling counters are stored. To access this last area of 4 bytes, a special | 383 | * leveling counters are stored. To access this last area of 4 bytes, a special |
384 | * mode must be input to the flash ASIC. | 384 | * mode must be input to the flash ASIC. |
385 | * | 385 | * |
386 | * Returns 0 if no error occurred, -EIO else. | 386 | * Returns 0 if no error occurred, -EIO else. |
387 | */ | 387 | */ |
388 | static int doc_set_extra_page_mode(struct docg3 *docg3) | 388 | static int doc_set_extra_page_mode(struct docg3 *docg3) |
389 | { | 389 | { |
390 | int fctrl; | 390 | int fctrl; |
391 | 391 | ||
392 | doc_dbg("doc_set_extra_page_mode()\n"); | 392 | doc_dbg("doc_set_extra_page_mode()\n"); |
393 | doc_flash_sequence(docg3, DOC_SEQ_PAGE_SIZE_532); | 393 | doc_flash_sequence(docg3, DOC_SEQ_PAGE_SIZE_532); |
394 | doc_flash_command(docg3, DOC_CMD_PAGE_SIZE_532); | 394 | doc_flash_command(docg3, DOC_CMD_PAGE_SIZE_532); |
395 | doc_delay(docg3, 2); | 395 | doc_delay(docg3, 2); |
396 | 396 | ||
397 | fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); | 397 | fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); |
398 | if (fctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR)) | 398 | if (fctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR)) |
399 | return -EIO; | 399 | return -EIO; |
400 | else | 400 | else |
401 | return 0; | 401 | return 0; |
402 | } | 402 | } |
403 | 403 | ||
404 | /** | 404 | /** |
405 | * doc_setup_addr_sector - Setup blocks/page/ofs address for one plane | 405 | * doc_setup_addr_sector - Setup blocks/page/ofs address for one plane |
406 | * @docg3: the device | 406 | * @docg3: the device |
407 | * @sector: the sector | 407 | * @sector: the sector |
408 | */ | 408 | */ |
409 | static void doc_setup_addr_sector(struct docg3 *docg3, int sector) | 409 | static void doc_setup_addr_sector(struct docg3 *docg3, int sector) |
410 | { | 410 | { |
411 | doc_delay(docg3, 1); | 411 | doc_delay(docg3, 1); |
412 | doc_flash_address(docg3, sector & 0xff); | 412 | doc_flash_address(docg3, sector & 0xff); |
413 | doc_flash_address(docg3, (sector >> 8) & 0xff); | 413 | doc_flash_address(docg3, (sector >> 8) & 0xff); |
414 | doc_flash_address(docg3, (sector >> 16) & 0xff); | 414 | doc_flash_address(docg3, (sector >> 16) & 0xff); |
415 | doc_delay(docg3, 1); | 415 | doc_delay(docg3, 1); |
416 | } | 416 | } |
417 | 417 | ||
418 | /** | 418 | /** |
419 | * doc_setup_writeaddr_sector - Setup blocks/page/ofs address for one plane | 419 | * doc_setup_writeaddr_sector - Setup blocks/page/ofs address for one plane |
420 | * @docg3: the device | 420 | * @docg3: the device |
421 | * @sector: the sector | 421 | * @sector: the sector |
422 | * @ofs: the offset in the page, between 0 and (512 + 16 + 512) | 422 | * @ofs: the offset in the page, between 0 and (512 + 16 + 512) |
423 | */ | 423 | */ |
424 | static void doc_setup_writeaddr_sector(struct docg3 *docg3, int sector, int ofs) | 424 | static void doc_setup_writeaddr_sector(struct docg3 *docg3, int sector, int ofs) |
425 | { | 425 | { |
426 | ofs = ofs >> 2; | 426 | ofs = ofs >> 2; |
427 | doc_delay(docg3, 1); | 427 | doc_delay(docg3, 1); |
428 | doc_flash_address(docg3, ofs & 0xff); | 428 | doc_flash_address(docg3, ofs & 0xff); |
429 | doc_flash_address(docg3, sector & 0xff); | 429 | doc_flash_address(docg3, sector & 0xff); |
430 | doc_flash_address(docg3, (sector >> 8) & 0xff); | 430 | doc_flash_address(docg3, (sector >> 8) & 0xff); |
431 | doc_flash_address(docg3, (sector >> 16) & 0xff); | 431 | doc_flash_address(docg3, (sector >> 16) & 0xff); |
432 | doc_delay(docg3, 1); | 432 | doc_delay(docg3, 1); |
433 | } | 433 | } |
434 | 434 | ||
435 | /** | 435 | /** |
436 | * doc_seek - Set both flash planes to the specified block, page for reading | 436 | * doc_seek - Set both flash planes to the specified block, page for reading |
437 | * @docg3: the device | 437 | * @docg3: the device |
438 | * @block0: the first plane block index | 438 | * @block0: the first plane block index |
439 | * @block1: the second plane block index | 439 | * @block1: the second plane block index |
440 | * @page: the page index within the block | 440 | * @page: the page index within the block |
441 | * @wear: if true, read will occur on the 4 extra bytes of the wear area | 441 | * @wear: if true, read will occur on the 4 extra bytes of the wear area |
442 | * @ofs: offset in page to read | 442 | * @ofs: offset in page to read |
443 | * | 443 | * |
444 | * Programs the flash even and odd planes to the specific block and page. | 444 | * Programs the flash even and odd planes to the specific block and page. |
445 | * Alternatively, programs the flash to the wear area of the specified page. | 445 | * Alternatively, programs the flash to the wear area of the specified page. |
446 | */ | 446 | */ |
447 | static int doc_read_seek(struct docg3 *docg3, int block0, int block1, int page, | 447 | static int doc_read_seek(struct docg3 *docg3, int block0, int block1, int page, |
448 | int wear, int ofs) | 448 | int wear, int ofs) |
449 | { | 449 | { |
450 | int sector, ret = 0; | 450 | int sector, ret = 0; |
451 | 451 | ||
452 | doc_dbg("doc_seek(blocks=(%d,%d), page=%d, ofs=%d, wear=%d)\n", | 452 | doc_dbg("doc_seek(blocks=(%d,%d), page=%d, ofs=%d, wear=%d)\n", |
453 | block0, block1, page, ofs, wear); | 453 | block0, block1, page, ofs, wear); |
454 | 454 | ||
455 | if (!wear && (ofs < 2 * DOC_LAYOUT_PAGE_SIZE)) { | 455 | if (!wear && (ofs < 2 * DOC_LAYOUT_PAGE_SIZE)) { |
456 | doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1); | 456 | doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1); |
457 | doc_flash_command(docg3, DOC_CMD_READ_PLANE1); | 457 | doc_flash_command(docg3, DOC_CMD_READ_PLANE1); |
458 | doc_delay(docg3, 2); | 458 | doc_delay(docg3, 2); |
459 | } else { | 459 | } else { |
460 | doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2); | 460 | doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2); |
461 | doc_flash_command(docg3, DOC_CMD_READ_PLANE2); | 461 | doc_flash_command(docg3, DOC_CMD_READ_PLANE2); |
462 | doc_delay(docg3, 2); | 462 | doc_delay(docg3, 2); |
463 | } | 463 | } |
464 | 464 | ||
465 | doc_set_reliable_mode(docg3); | 465 | doc_set_reliable_mode(docg3); |
466 | if (wear) | 466 | if (wear) |
467 | ret = doc_set_extra_page_mode(docg3); | 467 | ret = doc_set_extra_page_mode(docg3); |
468 | if (ret) | 468 | if (ret) |
469 | goto out; | 469 | goto out; |
470 | 470 | ||
471 | doc_flash_sequence(docg3, DOC_SEQ_READ); | 471 | doc_flash_sequence(docg3, DOC_SEQ_READ); |
472 | sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); | 472 | sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); |
473 | doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); | 473 | doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); |
474 | doc_setup_addr_sector(docg3, sector); | 474 | doc_setup_addr_sector(docg3, sector); |
475 | 475 | ||
476 | sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); | 476 | sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); |
477 | doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); | 477 | doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); |
478 | doc_setup_addr_sector(docg3, sector); | 478 | doc_setup_addr_sector(docg3, sector); |
479 | doc_delay(docg3, 1); | 479 | doc_delay(docg3, 1); |
480 | 480 | ||
481 | out: | 481 | out: |
482 | return ret; | 482 | return ret; |
483 | } | 483 | } |
484 | 484 | ||
485 | /** | 485 | /** |
486 | * doc_write_seek - Set both flash planes to the specified block, page for writing | 486 | * doc_write_seek - Set both flash planes to the specified block, page for writing |
487 | * @docg3: the device | 487 | * @docg3: the device |
488 | * @block0: the first plane block index | 488 | * @block0: the first plane block index |
489 | * @block1: the second plane block index | 489 | * @block1: the second plane block index |
490 | * @page: the page index within the block | 490 | * @page: the page index within the block |
491 | * @ofs: offset in page to write | 491 | * @ofs: offset in page to write |
492 | * | 492 | * |
493 | * Programs the flash even and odd planes to the specific block and page. | 493 | * Programs the flash even and odd planes to the specific block and page. |
494 | * Alternatively, programs the flash to the wear area of the specified page. | 494 | * Alternatively, programs the flash to the wear area of the specified page. |
495 | */ | 495 | */ |
496 | static int doc_write_seek(struct docg3 *docg3, int block0, int block1, int page, | 496 | static int doc_write_seek(struct docg3 *docg3, int block0, int block1, int page, |
497 | int ofs) | 497 | int ofs) |
498 | { | 498 | { |
499 | int ret = 0, sector; | 499 | int ret = 0, sector; |
500 | 500 | ||
501 | doc_dbg("doc_write_seek(blocks=(%d,%d), page=%d, ofs=%d)\n", | 501 | doc_dbg("doc_write_seek(blocks=(%d,%d), page=%d, ofs=%d)\n", |
502 | block0, block1, page, ofs); | 502 | block0, block1, page, ofs); |
503 | 503 | ||
504 | doc_set_reliable_mode(docg3); | 504 | doc_set_reliable_mode(docg3); |
505 | 505 | ||
506 | if (ofs < 2 * DOC_LAYOUT_PAGE_SIZE) { | 506 | if (ofs < 2 * DOC_LAYOUT_PAGE_SIZE) { |
507 | doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1); | 507 | doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1); |
508 | doc_flash_command(docg3, DOC_CMD_READ_PLANE1); | 508 | doc_flash_command(docg3, DOC_CMD_READ_PLANE1); |
509 | doc_delay(docg3, 2); | 509 | doc_delay(docg3, 2); |
510 | } else { | 510 | } else { |
511 | doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2); | 511 | doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2); |
512 | doc_flash_command(docg3, DOC_CMD_READ_PLANE2); | 512 | doc_flash_command(docg3, DOC_CMD_READ_PLANE2); |
513 | doc_delay(docg3, 2); | 513 | doc_delay(docg3, 2); |
514 | } | 514 | } |
515 | 515 | ||
516 | doc_flash_sequence(docg3, DOC_SEQ_PAGE_SETUP); | 516 | doc_flash_sequence(docg3, DOC_SEQ_PAGE_SETUP); |
517 | doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); | 517 | doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); |
518 | 518 | ||
519 | sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); | 519 | sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); |
520 | doc_setup_writeaddr_sector(docg3, sector, ofs); | 520 | doc_setup_writeaddr_sector(docg3, sector, ofs); |
521 | 521 | ||
522 | doc_flash_command(docg3, DOC_CMD_PROG_CYCLE3); | 522 | doc_flash_command(docg3, DOC_CMD_PROG_CYCLE3); |
523 | doc_delay(docg3, 2); | 523 | doc_delay(docg3, 2); |
524 | ret = doc_wait_ready(docg3); | 524 | ret = doc_wait_ready(docg3); |
525 | if (ret) | 525 | if (ret) |
526 | goto out; | 526 | goto out; |
527 | 527 | ||
528 | doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); | 528 | doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); |
529 | sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); | 529 | sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); |
530 | doc_setup_writeaddr_sector(docg3, sector, ofs); | 530 | doc_setup_writeaddr_sector(docg3, sector, ofs); |
531 | doc_delay(docg3, 1); | 531 | doc_delay(docg3, 1); |
532 | 532 | ||
533 | out: | 533 | out: |
534 | return ret; | 534 | return ret; |
535 | } | 535 | } |
536 | 536 | ||
537 | 537 | ||
538 | /** | 538 | /** |
539 | * doc_read_page_ecc_init - Initialize hardware ECC engine | 539 | * doc_read_page_ecc_init - Initialize hardware ECC engine |
540 | * @docg3: the device | 540 | * @docg3: the device |
541 | * @len: the number of bytes covered by the ECC (BCH covered) | 541 | * @len: the number of bytes covered by the ECC (BCH covered) |
542 | * | 542 | * |
543 | * The function does initialize the hardware ECC engine to compute the Hamming | 543 | * The function does initialize the hardware ECC engine to compute the Hamming |
544 | * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). | 544 | * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). |
545 | * | 545 | * |
546 | * Return 0 if succeeded, -EIO on error | 546 | * Return 0 if succeeded, -EIO on error |
547 | */ | 547 | */ |
548 | static int doc_read_page_ecc_init(struct docg3 *docg3, int len) | 548 | static int doc_read_page_ecc_init(struct docg3 *docg3, int len) |
549 | { | 549 | { |
550 | doc_writew(docg3, DOC_ECCCONF0_READ_MODE | 550 | doc_writew(docg3, DOC_ECCCONF0_READ_MODE |
551 | | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE | 551 | | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE |
552 | | (len & DOC_ECCCONF0_DATA_BYTES_MASK), | 552 | | (len & DOC_ECCCONF0_DATA_BYTES_MASK), |
553 | DOC_ECCCONF0); | 553 | DOC_ECCCONF0); |
554 | doc_delay(docg3, 4); | 554 | doc_delay(docg3, 4); |
555 | doc_register_readb(docg3, DOC_FLASHCONTROL); | 555 | doc_register_readb(docg3, DOC_FLASHCONTROL); |
556 | return doc_wait_ready(docg3); | 556 | return doc_wait_ready(docg3); |
557 | } | 557 | } |
558 | 558 | ||
559 | /** | 559 | /** |
560 | * doc_write_page_ecc_init - Initialize hardware BCH ECC engine | 560 | * doc_write_page_ecc_init - Initialize hardware BCH ECC engine |
561 | * @docg3: the device | 561 | * @docg3: the device |
562 | * @len: the number of bytes covered by the ECC (BCH covered) | 562 | * @len: the number of bytes covered by the ECC (BCH covered) |
563 | * | 563 | * |
564 | * The function does initialize the hardware ECC engine to compute the Hamming | 564 | * The function does initialize the hardware ECC engine to compute the Hamming |
565 | * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). | 565 | * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). |
566 | * | 566 | * |
567 | * Return 0 if succeeded, -EIO on error | 567 | * Return 0 if succeeded, -EIO on error |
568 | */ | 568 | */ |
569 | static int doc_write_page_ecc_init(struct docg3 *docg3, int len) | 569 | static int doc_write_page_ecc_init(struct docg3 *docg3, int len) |
570 | { | 570 | { |
571 | doc_writew(docg3, DOC_ECCCONF0_WRITE_MODE | 571 | doc_writew(docg3, DOC_ECCCONF0_WRITE_MODE |
572 | | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE | 572 | | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE |
573 | | (len & DOC_ECCCONF0_DATA_BYTES_MASK), | 573 | | (len & DOC_ECCCONF0_DATA_BYTES_MASK), |
574 | DOC_ECCCONF0); | 574 | DOC_ECCCONF0); |
575 | doc_delay(docg3, 4); | 575 | doc_delay(docg3, 4); |
576 | doc_register_readb(docg3, DOC_FLASHCONTROL); | 576 | doc_register_readb(docg3, DOC_FLASHCONTROL); |
577 | return doc_wait_ready(docg3); | 577 | return doc_wait_ready(docg3); |
578 | } | 578 | } |
579 | 579 | ||
580 | /** | 580 | /** |
581 | * doc_ecc_disable - Disable Hamming and BCH ECC hardware calculator | 581 | * doc_ecc_disable - Disable Hamming and BCH ECC hardware calculator |
582 | * @docg3: the device | 582 | * @docg3: the device |
583 | * | 583 | * |
584 | * Disables the hardware ECC generator and checker, for unchecked reads (as when | 584 | * Disables the hardware ECC generator and checker, for unchecked reads (as when |
585 | * reading OOB only or write status byte). | 585 | * reading OOB only or write status byte). |
586 | */ | 586 | */ |
587 | static void doc_ecc_disable(struct docg3 *docg3) | 587 | static void doc_ecc_disable(struct docg3 *docg3) |
588 | { | 588 | { |
589 | doc_writew(docg3, DOC_ECCCONF0_READ_MODE, DOC_ECCCONF0); | 589 | doc_writew(docg3, DOC_ECCCONF0_READ_MODE, DOC_ECCCONF0); |
590 | doc_delay(docg3, 4); | 590 | doc_delay(docg3, 4); |
591 | } | 591 | } |
592 | 592 | ||
593 | /** | 593 | /** |
594 | * doc_hamming_ecc_init - Initialize hardware Hamming ECC engine | 594 | * doc_hamming_ecc_init - Initialize hardware Hamming ECC engine |
595 | * @docg3: the device | 595 | * @docg3: the device |
596 | * @nb_bytes: the number of bytes covered by the ECC (Hamming covered) | 596 | * @nb_bytes: the number of bytes covered by the ECC (Hamming covered) |
597 | * | 597 | * |
598 | * This function programs the ECC hardware to compute the hamming code on the | 598 | * This function programs the ECC hardware to compute the hamming code on the |
599 | * last provided N bytes to the hardware generator. | 599 | * last provided N bytes to the hardware generator. |
600 | */ | 600 | */ |
601 | static void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes) | 601 | static void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes) |
602 | { | 602 | { |
603 | u8 ecc_conf1; | 603 | u8 ecc_conf1; |
604 | 604 | ||
605 | ecc_conf1 = doc_register_readb(docg3, DOC_ECCCONF1); | 605 | ecc_conf1 = doc_register_readb(docg3, DOC_ECCCONF1); |
606 | ecc_conf1 &= ~DOC_ECCCONF1_HAMMING_BITS_MASK; | 606 | ecc_conf1 &= ~DOC_ECCCONF1_HAMMING_BITS_MASK; |
607 | ecc_conf1 |= (nb_bytes & DOC_ECCCONF1_HAMMING_BITS_MASK); | 607 | ecc_conf1 |= (nb_bytes & DOC_ECCCONF1_HAMMING_BITS_MASK); |
608 | doc_writeb(docg3, ecc_conf1, DOC_ECCCONF1); | 608 | doc_writeb(docg3, ecc_conf1, DOC_ECCCONF1); |
609 | } | 609 | } |
610 | 610 | ||
611 | /** | 611 | /** |
612 | * doc_ecc_bch_fix_data - Fix if need be read data from flash | 612 | * doc_ecc_bch_fix_data - Fix if need be read data from flash |
613 | * @docg3: the device | 613 | * @docg3: the device |
614 | * @buf: the buffer of read data (512 + 7 + 1 bytes) | 614 | * @buf: the buffer of read data (512 + 7 + 1 bytes) |
615 | * @hwecc: the hardware calculated ECC. | 615 | * @hwecc: the hardware calculated ECC. |
616 | * It's in fact recv_ecc ^ calc_ecc, where recv_ecc was read from OOB | 616 | * It's in fact recv_ecc ^ calc_ecc, where recv_ecc was read from OOB |
617 | * area data, and calc_ecc the ECC calculated by the hardware generator. | 617 | * area data, and calc_ecc the ECC calculated by the hardware generator. |
618 | * | 618 | * |
619 | * Checks if the received data matches the ECC, and if an error is detected, | 619 | * Checks if the received data matches the ECC, and if an error is detected, |
620 | * tries to fix the bit flips (at most 4) in the buffer buf. As the docg3 | 620 | * tries to fix the bit flips (at most 4) in the buffer buf. As the docg3 |
621 | * understands the (data, ecc, syndroms) in an inverted order in comparison to | 621 | * understands the (data, ecc, syndroms) in an inverted order in comparison to |
622 | * the BCH library, the function reverses the order of bits (ie. bit7 and bit0, | 622 | * the BCH library, the function reverses the order of bits (ie. bit7 and bit0, |
623 | * bit6 and bit 1, ...) for all ECC data. | 623 | * bit6 and bit 1, ...) for all ECC data. |
624 | * | 624 | * |
625 | * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch | 625 | * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch |
626 | * algorithm is used to decode this. However the hw operates on page | 626 | * algorithm is used to decode this. However the hw operates on page |
627 | * data in a bit order that is the reverse of that of the bch alg, | 627 | * data in a bit order that is the reverse of that of the bch alg, |
628 | * requiring that the bits be reversed on the result. Thanks to Ivan | 628 | * requiring that the bits be reversed on the result. Thanks to Ivan |
629 | * Djelic for his analysis. | 629 | * Djelic for his analysis. |
630 | * | 630 | * |
631 | * Returns number of fixed bits (0, 1, 2, 3, 4) or -EBADMSG if too many bit | 631 | * Returns number of fixed bits (0, 1, 2, 3, 4) or -EBADMSG if too many bit |
632 | * errors were detected and cannot be fixed. | 632 | * errors were detected and cannot be fixed. |
633 | */ | 633 | */ |
634 | static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc) | 634 | static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc) |
635 | { | 635 | { |
636 | u8 ecc[DOC_ECC_BCH_SIZE]; | 636 | u8 ecc[DOC_ECC_BCH_SIZE]; |
637 | int errorpos[DOC_ECC_BCH_T], i, numerrs; | 637 | int errorpos[DOC_ECC_BCH_T], i, numerrs; |
638 | 638 | ||
639 | for (i = 0; i < DOC_ECC_BCH_SIZE; i++) | 639 | for (i = 0; i < DOC_ECC_BCH_SIZE; i++) |
640 | ecc[i] = bitrev8(hwecc[i]); | 640 | ecc[i] = bitrev8(hwecc[i]); |
641 | numerrs = decode_bch(docg3->cascade->bch, NULL, | 641 | numerrs = decode_bch(docg3->cascade->bch, NULL, |
642 | DOC_ECC_BCH_COVERED_BYTES, | 642 | DOC_ECC_BCH_COVERED_BYTES, |
643 | NULL, ecc, NULL, errorpos); | 643 | NULL, ecc, NULL, errorpos); |
644 | BUG_ON(numerrs == -EINVAL); | 644 | BUG_ON(numerrs == -EINVAL); |
645 | if (numerrs < 0) | 645 | if (numerrs < 0) |
646 | goto out; | 646 | goto out; |
647 | 647 | ||
648 | for (i = 0; i < numerrs; i++) | 648 | for (i = 0; i < numerrs; i++) |
649 | errorpos[i] = (errorpos[i] & ~7) | (7 - (errorpos[i] & 7)); | 649 | errorpos[i] = (errorpos[i] & ~7) | (7 - (errorpos[i] & 7)); |
650 | for (i = 0; i < numerrs; i++) | 650 | for (i = 0; i < numerrs; i++) |
651 | if (errorpos[i] < DOC_ECC_BCH_COVERED_BYTES*8) | 651 | if (errorpos[i] < DOC_ECC_BCH_COVERED_BYTES*8) |
652 | /* error is located in data, correct it */ | 652 | /* error is located in data, correct it */ |
653 | change_bit(errorpos[i], buf); | 653 | change_bit(errorpos[i], buf); |
654 | out: | 654 | out: |
655 | doc_dbg("doc_ecc_bch_fix_data: flipped %d bits\n", numerrs); | 655 | doc_dbg("doc_ecc_bch_fix_data: flipped %d bits\n", numerrs); |
656 | return numerrs; | 656 | return numerrs; |
657 | } | 657 | } |
658 | 658 | ||
659 | 659 | ||
660 | /** | 660 | /** |
661 | * doc_read_page_prepare - Prepares reading data from a flash page | 661 | * doc_read_page_prepare - Prepares reading data from a flash page |
662 | * @docg3: the device | 662 | * @docg3: the device |
663 | * @block0: the first plane block index on flash memory | 663 | * @block0: the first plane block index on flash memory |
664 | * @block1: the second plane block index on flash memory | 664 | * @block1: the second plane block index on flash memory |
665 | * @page: the page index in the block | 665 | * @page: the page index in the block |
666 | * @offset: the offset in the page (must be a multiple of 4) | 666 | * @offset: the offset in the page (must be a multiple of 4) |
667 | * | 667 | * |
668 | * Prepares the page to be read in the flash memory : | 668 | * Prepares the page to be read in the flash memory : |
669 | * - tell ASIC to map the flash pages | 669 | * - tell ASIC to map the flash pages |
670 | * - tell ASIC to be in read mode | 670 | * - tell ASIC to be in read mode |
671 | * | 671 | * |
672 | * After a call to this method, a call to doc_read_page_finish is mandatory, | 672 | * After a call to this method, a call to doc_read_page_finish is mandatory, |
673 | * to end the read cycle of the flash. | 673 | * to end the read cycle of the flash. |
674 | * | 674 | * |
675 | * Read data from a flash page. The length to be read must be between 0 and | 675 | * Read data from a flash page. The length to be read must be between 0 and |
676 | * (page_size + oob_size + wear_size), ie. 532, and a multiple of 4 (because | 676 | * (page_size + oob_size + wear_size), ie. 532, and a multiple of 4 (because |
677 | * the extra bytes reading is not implemented). | 677 | * the extra bytes reading is not implemented). |
678 | * | 678 | * |
679 | * As pages are grouped by 2 (in 2 planes), reading from a page must be done | 679 | * As pages are grouped by 2 (in 2 planes), reading from a page must be done |
680 | * in two steps: | 680 | * in two steps: |
681 | * - one read of 512 bytes at offset 0 | 681 | * - one read of 512 bytes at offset 0 |
682 | * - one read of 512 bytes at offset 512 + 16 | 682 | * - one read of 512 bytes at offset 512 + 16 |
683 | * | 683 | * |
684 | * Returns 0 if successful, -EIO if a read error occurred. | 684 | * Returns 0 if successful, -EIO if a read error occurred. |
685 | */ | 685 | */ |
686 | static int doc_read_page_prepare(struct docg3 *docg3, int block0, int block1, | 686 | static int doc_read_page_prepare(struct docg3 *docg3, int block0, int block1, |
687 | int page, int offset) | 687 | int page, int offset) |
688 | { | 688 | { |
689 | int wear_area = 0, ret = 0; | 689 | int wear_area = 0, ret = 0; |
690 | 690 | ||
691 | doc_dbg("doc_read_page_prepare(blocks=(%d,%d), page=%d, ofsInPage=%d)\n", | 691 | doc_dbg("doc_read_page_prepare(blocks=(%d,%d), page=%d, ofsInPage=%d)\n", |
692 | block0, block1, page, offset); | 692 | block0, block1, page, offset); |
693 | if (offset >= DOC_LAYOUT_WEAR_OFFSET) | 693 | if (offset >= DOC_LAYOUT_WEAR_OFFSET) |
694 | wear_area = 1; | 694 | wear_area = 1; |
695 | if (!wear_area && offset > (DOC_LAYOUT_PAGE_OOB_SIZE * 2)) | 695 | if (!wear_area && offset > (DOC_LAYOUT_PAGE_OOB_SIZE * 2)) |
696 | return -EINVAL; | 696 | return -EINVAL; |
697 | 697 | ||
698 | doc_set_device_id(docg3, docg3->device_id); | 698 | doc_set_device_id(docg3, docg3->device_id); |
699 | ret = doc_reset_seq(docg3); | 699 | ret = doc_reset_seq(docg3); |
700 | if (ret) | 700 | if (ret) |
701 | goto err; | 701 | goto err; |
702 | 702 | ||
703 | /* Program the flash address block and page */ | 703 | /* Program the flash address block and page */ |
704 | ret = doc_read_seek(docg3, block0, block1, page, wear_area, offset); | 704 | ret = doc_read_seek(docg3, block0, block1, page, wear_area, offset); |
705 | if (ret) | 705 | if (ret) |
706 | goto err; | 706 | goto err; |
707 | 707 | ||
708 | doc_flash_command(docg3, DOC_CMD_READ_ALL_PLANES); | 708 | doc_flash_command(docg3, DOC_CMD_READ_ALL_PLANES); |
709 | doc_delay(docg3, 2); | 709 | doc_delay(docg3, 2); |
710 | doc_wait_ready(docg3); | 710 | doc_wait_ready(docg3); |
711 | 711 | ||
712 | doc_flash_command(docg3, DOC_CMD_SET_ADDR_READ); | 712 | doc_flash_command(docg3, DOC_CMD_SET_ADDR_READ); |
713 | doc_delay(docg3, 1); | 713 | doc_delay(docg3, 1); |
714 | if (offset >= DOC_LAYOUT_PAGE_SIZE * 2) | 714 | if (offset >= DOC_LAYOUT_PAGE_SIZE * 2) |
715 | offset -= 2 * DOC_LAYOUT_PAGE_SIZE; | 715 | offset -= 2 * DOC_LAYOUT_PAGE_SIZE; |
716 | doc_flash_address(docg3, offset >> 2); | 716 | doc_flash_address(docg3, offset >> 2); |
717 | doc_delay(docg3, 1); | 717 | doc_delay(docg3, 1); |
718 | doc_wait_ready(docg3); | 718 | doc_wait_ready(docg3); |
719 | 719 | ||
720 | doc_flash_command(docg3, DOC_CMD_READ_FLASH); | 720 | doc_flash_command(docg3, DOC_CMD_READ_FLASH); |
721 | 721 | ||
722 | return 0; | 722 | return 0; |
723 | err: | 723 | err: |
724 | doc_writeb(docg3, 0, DOC_DATAEND); | 724 | doc_writeb(docg3, 0, DOC_DATAEND); |
725 | doc_delay(docg3, 2); | 725 | doc_delay(docg3, 2); |
726 | return -EIO; | 726 | return -EIO; |
727 | } | 727 | } |
728 | 728 | ||
729 | /** | 729 | /** |
730 | * doc_read_page_getbytes - Reads bytes from a prepared page | 730 | * doc_read_page_getbytes - Reads bytes from a prepared page |
731 | * @docg3: the device | 731 | * @docg3: the device |
732 | * @len: the number of bytes to be read (must be a multiple of 4) | 732 | * @len: the number of bytes to be read (must be a multiple of 4) |
733 | * @buf: the buffer to be filled in (or NULL is forget bytes) | 733 | * @buf: the buffer to be filled in (or NULL is forget bytes) |
734 | * @first: 1 if first time read, DOC_READADDRESS should be set | 734 | * @first: 1 if first time read, DOC_READADDRESS should be set |
735 | * @last_odd: 1 if last read ended up on an odd byte | 735 | * @last_odd: 1 if last read ended up on an odd byte |
736 | * | 736 | * |
737 | * Reads bytes from a prepared page. There is a trickery here : if the last read | 737 | * Reads bytes from a prepared page. There is a trickery here : if the last read |
738 | * ended up on an odd offset in the 1024 bytes double page, ie. between the 2 | 738 | * ended up on an odd offset in the 1024 bytes double page, ie. between the 2 |
739 | * planes, the first byte must be read apart. If a word (16bit) read was used, | 739 | * planes, the first byte must be read apart. If a word (16bit) read was used, |
740 | * the read would return the byte of plane 2 as low *and* high endian, which | 740 | * the read would return the byte of plane 2 as low *and* high endian, which |
741 | * will mess the read. | 741 | * will mess the read. |
742 | * | 742 | * |
743 | */ | 743 | */ |
744 | static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf, | 744 | static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf, |
745 | int first, int last_odd) | 745 | int first, int last_odd) |
746 | { | 746 | { |
747 | if (last_odd && len > 0) { | 747 | if (last_odd && len > 0) { |
748 | doc_read_data_area(docg3, buf, 1, first); | 748 | doc_read_data_area(docg3, buf, 1, first); |
749 | doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0); | 749 | doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0); |
750 | } else { | 750 | } else { |
751 | doc_read_data_area(docg3, buf, len, first); | 751 | doc_read_data_area(docg3, buf, len, first); |
752 | } | 752 | } |
753 | doc_delay(docg3, 2); | 753 | doc_delay(docg3, 2); |
754 | return len; | 754 | return len; |
755 | } | 755 | } |
756 | 756 | ||
757 | /** | 757 | /** |
758 | * doc_write_page_putbytes - Writes bytes into a prepared page | 758 | * doc_write_page_putbytes - Writes bytes into a prepared page |
759 | * @docg3: the device | 759 | * @docg3: the device |
760 | * @len: the number of bytes to be written | 760 | * @len: the number of bytes to be written |
761 | * @buf: the buffer of input bytes | 761 | * @buf: the buffer of input bytes |
762 | * | 762 | * |
763 | */ | 763 | */ |
764 | static void doc_write_page_putbytes(struct docg3 *docg3, int len, | 764 | static void doc_write_page_putbytes(struct docg3 *docg3, int len, |
765 | const u_char *buf) | 765 | const u_char *buf) |
766 | { | 766 | { |
767 | doc_write_data_area(docg3, buf, len); | 767 | doc_write_data_area(docg3, buf, len); |
768 | doc_delay(docg3, 2); | 768 | doc_delay(docg3, 2); |
769 | } | 769 | } |
770 | 770 | ||
771 | /** | 771 | /** |
772 | * doc_get_bch_hw_ecc - Get hardware calculated BCH ECC | 772 | * doc_get_bch_hw_ecc - Get hardware calculated BCH ECC |
773 | * @docg3: the device | 773 | * @docg3: the device |
774 | * @hwecc: the array of 7 integers where the hardware ecc will be stored | 774 | * @hwecc: the array of 7 integers where the hardware ecc will be stored |
775 | */ | 775 | */ |
776 | static void doc_get_bch_hw_ecc(struct docg3 *docg3, u8 *hwecc) | 776 | static void doc_get_bch_hw_ecc(struct docg3 *docg3, u8 *hwecc) |
777 | { | 777 | { |
778 | int i; | 778 | int i; |
779 | 779 | ||
780 | for (i = 0; i < DOC_ECC_BCH_SIZE; i++) | 780 | for (i = 0; i < DOC_ECC_BCH_SIZE; i++) |
781 | hwecc[i] = doc_register_readb(docg3, DOC_BCH_HW_ECC(i)); | 781 | hwecc[i] = doc_register_readb(docg3, DOC_BCH_HW_ECC(i)); |
782 | } | 782 | } |
783 | 783 | ||
784 | /** | 784 | /** |
785 | * doc_page_finish - Ends reading/writing of a flash page | 785 | * doc_page_finish - Ends reading/writing of a flash page |
786 | * @docg3: the device | 786 | * @docg3: the device |
787 | */ | 787 | */ |
788 | static void doc_page_finish(struct docg3 *docg3) | 788 | static void doc_page_finish(struct docg3 *docg3) |
789 | { | 789 | { |
790 | doc_writeb(docg3, 0, DOC_DATAEND); | 790 | doc_writeb(docg3, 0, DOC_DATAEND); |
791 | doc_delay(docg3, 2); | 791 | doc_delay(docg3, 2); |
792 | } | 792 | } |
793 | 793 | ||
794 | /** | 794 | /** |
795 | * doc_read_page_finish - Ends reading of a flash page | 795 | * doc_read_page_finish - Ends reading of a flash page |
796 | * @docg3: the device | 796 | * @docg3: the device |
797 | * | 797 | * |
798 | * As a side effect, resets the chip selector to 0. This ensures that after each | 798 | * As a side effect, resets the chip selector to 0. This ensures that after each |
799 | * read operation, the floor 0 is selected. Therefore, if the systems halts, the | 799 | * read operation, the floor 0 is selected. Therefore, if the systems halts, the |
800 | * reboot will boot on floor 0, where the IPL is. | 800 | * reboot will boot on floor 0, where the IPL is. |
801 | */ | 801 | */ |
802 | static void doc_read_page_finish(struct docg3 *docg3) | 802 | static void doc_read_page_finish(struct docg3 *docg3) |
803 | { | 803 | { |
804 | doc_page_finish(docg3); | 804 | doc_page_finish(docg3); |
805 | doc_set_device_id(docg3, 0); | 805 | doc_set_device_id(docg3, 0); |
806 | } | 806 | } |
807 | 807 | ||
808 | /** | 808 | /** |
809 | * calc_block_sector - Calculate blocks, pages and ofs. | 809 | * calc_block_sector - Calculate blocks, pages and ofs. |
810 | 810 | ||
811 | * @from: offset in flash | 811 | * @from: offset in flash |
812 | * @block0: first plane block index calculated | 812 | * @block0: first plane block index calculated |
813 | * @block1: second plane block index calculated | 813 | * @block1: second plane block index calculated |
814 | * @page: page calculated | 814 | * @page: page calculated |
815 | * @ofs: offset in page | 815 | * @ofs: offset in page |
816 | * @reliable: 0 if docg3 in normal mode, 1 if docg3 in fast mode, 2 if docg3 in | 816 | * @reliable: 0 if docg3 in normal mode, 1 if docg3 in fast mode, 2 if docg3 in |
817 | * reliable mode. | 817 | * reliable mode. |
818 | * | 818 | * |
819 | * The calculation is based on the reliable/normal mode. In normal mode, the 64 | 819 | * The calculation is based on the reliable/normal mode. In normal mode, the 64 |
820 | * pages of a block are available. In reliable mode, as pages 2*n and 2*n+1 are | 820 | * pages of a block are available. In reliable mode, as pages 2*n and 2*n+1 are |
821 | * clones, only 32 pages per block are available. | 821 | * clones, only 32 pages per block are available. |
822 | */ | 822 | */ |
823 | static void calc_block_sector(loff_t from, int *block0, int *block1, int *page, | 823 | static void calc_block_sector(loff_t from, int *block0, int *block1, int *page, |
824 | int *ofs, int reliable) | 824 | int *ofs, int reliable) |
825 | { | 825 | { |
826 | uint sector, pages_biblock; | 826 | uint sector, pages_biblock; |
827 | 827 | ||
828 | pages_biblock = DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES; | 828 | pages_biblock = DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES; |
829 | if (reliable == 1 || reliable == 2) | 829 | if (reliable == 1 || reliable == 2) |
830 | pages_biblock /= 2; | 830 | pages_biblock /= 2; |
831 | 831 | ||
832 | sector = from / DOC_LAYOUT_PAGE_SIZE; | 832 | sector = from / DOC_LAYOUT_PAGE_SIZE; |
833 | *block0 = sector / pages_biblock * DOC_LAYOUT_NBPLANES; | 833 | *block0 = sector / pages_biblock * DOC_LAYOUT_NBPLANES; |
834 | *block1 = *block0 + 1; | 834 | *block1 = *block0 + 1; |
835 | *page = sector % pages_biblock; | 835 | *page = sector % pages_biblock; |
836 | *page /= DOC_LAYOUT_NBPLANES; | 836 | *page /= DOC_LAYOUT_NBPLANES; |
837 | if (reliable == 1 || reliable == 2) | 837 | if (reliable == 1 || reliable == 2) |
838 | *page *= 2; | 838 | *page *= 2; |
839 | if (sector % 2) | 839 | if (sector % 2) |
840 | *ofs = DOC_LAYOUT_PAGE_OOB_SIZE; | 840 | *ofs = DOC_LAYOUT_PAGE_OOB_SIZE; |
841 | else | 841 | else |
842 | *ofs = 0; | 842 | *ofs = 0; |
843 | } | 843 | } |
844 | 844 | ||
845 | /** | 845 | /** |
846 | * doc_read_oob - Read out of band bytes from flash | 846 | * doc_read_oob - Read out of band bytes from flash |
847 | * @mtd: the device | 847 | * @mtd: the device |
848 | * @from: the offset from first block and first page, in bytes, aligned on page | 848 | * @from: the offset from first block and first page, in bytes, aligned on page |
849 | * size | 849 | * size |
850 | * @ops: the mtd oob structure | 850 | * @ops: the mtd oob structure |
851 | * | 851 | * |
852 | * Reads flash memory OOB area of pages. | 852 | * Reads flash memory OOB area of pages. |
853 | * | 853 | * |
854 | * Returns 0 if read successful, of -EIO, -EINVAL if an error occurred | 854 | * Returns 0 if read successful, of -EIO, -EINVAL if an error occurred |
855 | */ | 855 | */ |
856 | static int doc_read_oob(struct mtd_info *mtd, loff_t from, | 856 | static int doc_read_oob(struct mtd_info *mtd, loff_t from, |
857 | struct mtd_oob_ops *ops) | 857 | struct mtd_oob_ops *ops) |
858 | { | 858 | { |
859 | struct docg3 *docg3 = mtd->priv; | 859 | struct docg3 *docg3 = mtd->priv; |
860 | int block0, block1, page, ret, skip, ofs = 0; | 860 | int block0, block1, page, ret, skip, ofs = 0; |
861 | u8 *oobbuf = ops->oobbuf; | 861 | u8 *oobbuf = ops->oobbuf; |
862 | u8 *buf = ops->datbuf; | 862 | u8 *buf = ops->datbuf; |
863 | size_t len, ooblen, nbdata, nboob; | 863 | size_t len, ooblen, nbdata, nboob; |
864 | u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; | 864 | u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; |
865 | int max_bitflips = 0; | 865 | int max_bitflips = 0; |
866 | 866 | ||
867 | if (buf) | 867 | if (buf) |
868 | len = ops->len; | 868 | len = ops->len; |
869 | else | 869 | else |
870 | len = 0; | 870 | len = 0; |
871 | if (oobbuf) | 871 | if (oobbuf) |
872 | ooblen = ops->ooblen; | 872 | ooblen = ops->ooblen; |
873 | else | 873 | else |
874 | ooblen = 0; | 874 | ooblen = 0; |
875 | 875 | ||
876 | if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) | 876 | if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) |
877 | oobbuf += ops->ooboffs; | 877 | oobbuf += ops->ooboffs; |
878 | 878 | ||
879 | doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", | 879 | doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", |
880 | from, ops->mode, buf, len, oobbuf, ooblen); | 880 | from, ops->mode, buf, len, oobbuf, ooblen); |
881 | if (ooblen % DOC_LAYOUT_OOB_SIZE) | 881 | if (ooblen % DOC_LAYOUT_OOB_SIZE) |
882 | return -EINVAL; | 882 | return -EINVAL; |
883 | 883 | ||
884 | if (from + len > mtd->size) | 884 | if (from + len > mtd->size) |
885 | return -EINVAL; | 885 | return -EINVAL; |
886 | 886 | ||
887 | ops->oobretlen = 0; | 887 | ops->oobretlen = 0; |
888 | ops->retlen = 0; | 888 | ops->retlen = 0; |
889 | ret = 0; | 889 | ret = 0; |
890 | skip = from % DOC_LAYOUT_PAGE_SIZE; | 890 | skip = from % DOC_LAYOUT_PAGE_SIZE; |
891 | mutex_lock(&docg3->cascade->lock); | 891 | mutex_lock(&docg3->cascade->lock); |
892 | while (ret >= 0 && (len > 0 || ooblen > 0)) { | 892 | while (ret >= 0 && (len > 0 || ooblen > 0)) { |
893 | calc_block_sector(from - skip, &block0, &block1, &page, &ofs, | 893 | calc_block_sector(from - skip, &block0, &block1, &page, &ofs, |
894 | docg3->reliable); | 894 | docg3->reliable); |
895 | nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); | 895 | nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); |
896 | nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE); | 896 | nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE); |
897 | ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); | 897 | ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); |
898 | if (ret < 0) | 898 | if (ret < 0) |
899 | goto out; | 899 | goto out; |
900 | ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); | 900 | ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); |
901 | if (ret < 0) | 901 | if (ret < 0) |
902 | goto err_in_read; | 902 | goto err_in_read; |
903 | ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0); | 903 | ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0); |
904 | if (ret < skip) | 904 | if (ret < skip) |
905 | goto err_in_read; | 905 | goto err_in_read; |
906 | ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2); | 906 | ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2); |
907 | if (ret < nbdata) | 907 | if (ret < nbdata) |
908 | goto err_in_read; | 908 | goto err_in_read; |
909 | doc_read_page_getbytes(docg3, | 909 | doc_read_page_getbytes(docg3, |
910 | DOC_LAYOUT_PAGE_SIZE - nbdata - skip, | 910 | DOC_LAYOUT_PAGE_SIZE - nbdata - skip, |
911 | NULL, 0, (skip + nbdata) % 2); | 911 | NULL, 0, (skip + nbdata) % 2); |
912 | ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0); | 912 | ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0); |
913 | if (ret < nboob) | 913 | if (ret < nboob) |
914 | goto err_in_read; | 914 | goto err_in_read; |
915 | doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, | 915 | doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, |
916 | NULL, 0, nboob % 2); | 916 | NULL, 0, nboob % 2); |
917 | 917 | ||
918 | doc_get_bch_hw_ecc(docg3, hwecc); | 918 | doc_get_bch_hw_ecc(docg3, hwecc); |
919 | eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); | 919 | eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); |
920 | 920 | ||
921 | if (nboob >= DOC_LAYOUT_OOB_SIZE) { | 921 | if (nboob >= DOC_LAYOUT_OOB_SIZE) { |
922 | doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", | 922 | doc_dbg("OOB - INFO: %*phC\n", 7, oobbuf); |
923 | oobbuf[0], oobbuf[1], oobbuf[2], oobbuf[3], | ||
924 | oobbuf[4], oobbuf[5], oobbuf[6]); | ||
925 | doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]); | 923 | doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]); |
926 | doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", | 924 | doc_dbg("OOB - BCH_ECC: %*phC\n", 7, oobbuf + 8); |
927 | oobbuf[8], oobbuf[9], oobbuf[10], oobbuf[11], | ||
928 | oobbuf[12], oobbuf[13], oobbuf[14]); | ||
929 | doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]); | 925 | doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]); |
930 | } | 926 | } |
931 | doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1); | 927 | doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1); |
932 | doc_dbg("ECC HW_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", | 928 | doc_dbg("ECC HW_ECC: %*phC\n", 7, hwecc); |
933 | hwecc[0], hwecc[1], hwecc[2], hwecc[3], hwecc[4], | ||
934 | hwecc[5], hwecc[6]); | ||
935 | 929 | ||
936 | ret = -EIO; | 930 | ret = -EIO; |
937 | if (is_prot_seq_error(docg3)) | 931 | if (is_prot_seq_error(docg3)) |
938 | goto err_in_read; | 932 | goto err_in_read; |
939 | ret = 0; | 933 | ret = 0; |
940 | if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) && | 934 | if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) && |
941 | (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) && | 935 | (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) && |
942 | (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) && | 936 | (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) && |
943 | (ops->mode != MTD_OPS_RAW) && | 937 | (ops->mode != MTD_OPS_RAW) && |
944 | (nbdata == DOC_LAYOUT_PAGE_SIZE)) { | 938 | (nbdata == DOC_LAYOUT_PAGE_SIZE)) { |
945 | ret = doc_ecc_bch_fix_data(docg3, buf, hwecc); | 939 | ret = doc_ecc_bch_fix_data(docg3, buf, hwecc); |
946 | if (ret < 0) { | 940 | if (ret < 0) { |
947 | mtd->ecc_stats.failed++; | 941 | mtd->ecc_stats.failed++; |
948 | ret = -EBADMSG; | 942 | ret = -EBADMSG; |
949 | } | 943 | } |
950 | if (ret > 0) { | 944 | if (ret > 0) { |
951 | mtd->ecc_stats.corrected += ret; | 945 | mtd->ecc_stats.corrected += ret; |
952 | max_bitflips = max(max_bitflips, ret); | 946 | max_bitflips = max(max_bitflips, ret); |
953 | ret = max_bitflips; | 947 | ret = max_bitflips; |
954 | } | 948 | } |
955 | } | 949 | } |
956 | 950 | ||
957 | doc_read_page_finish(docg3); | 951 | doc_read_page_finish(docg3); |
958 | ops->retlen += nbdata; | 952 | ops->retlen += nbdata; |
959 | ops->oobretlen += nboob; | 953 | ops->oobretlen += nboob; |
960 | buf += nbdata; | 954 | buf += nbdata; |
961 | oobbuf += nboob; | 955 | oobbuf += nboob; |
962 | len -= nbdata; | 956 | len -= nbdata; |
963 | ooblen -= nboob; | 957 | ooblen -= nboob; |
964 | from += DOC_LAYOUT_PAGE_SIZE; | 958 | from += DOC_LAYOUT_PAGE_SIZE; |
965 | skip = 0; | 959 | skip = 0; |
966 | } | 960 | } |
967 | 961 | ||
968 | out: | 962 | out: |
969 | mutex_unlock(&docg3->cascade->lock); | 963 | mutex_unlock(&docg3->cascade->lock); |
970 | return ret; | 964 | return ret; |
971 | err_in_read: | 965 | err_in_read: |
972 | doc_read_page_finish(docg3); | 966 | doc_read_page_finish(docg3); |
973 | goto out; | 967 | goto out; |
974 | } | 968 | } |
975 | 969 | ||
976 | /** | 970 | /** |
977 | * doc_read - Read bytes from flash | 971 | * doc_read - Read bytes from flash |
978 | * @mtd: the device | 972 | * @mtd: the device |
979 | * @from: the offset from first block and first page, in bytes, aligned on page | 973 | * @from: the offset from first block and first page, in bytes, aligned on page |
980 | * size | 974 | * size |
981 | * @len: the number of bytes to read (must be a multiple of 4) | 975 | * @len: the number of bytes to read (must be a multiple of 4) |
982 | * @retlen: the number of bytes actually read | 976 | * @retlen: the number of bytes actually read |
983 | * @buf: the filled in buffer | 977 | * @buf: the filled in buffer |
984 | * | 978 | * |
985 | * Reads flash memory pages. This function does not read the OOB chunk, but only | 979 | * Reads flash memory pages. This function does not read the OOB chunk, but only |
986 | * the page data. | 980 | * the page data. |
987 | * | 981 | * |
988 | * Returns 0 if read successful, of -EIO, -EINVAL if an error occurred | 982 | * Returns 0 if read successful, of -EIO, -EINVAL if an error occurred |
989 | */ | 983 | */ |
990 | static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, | 984 | static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, |
991 | size_t *retlen, u_char *buf) | 985 | size_t *retlen, u_char *buf) |
992 | { | 986 | { |
993 | struct mtd_oob_ops ops; | 987 | struct mtd_oob_ops ops; |
994 | size_t ret; | 988 | size_t ret; |
995 | 989 | ||
996 | memset(&ops, 0, sizeof(ops)); | 990 | memset(&ops, 0, sizeof(ops)); |
997 | ops.datbuf = buf; | 991 | ops.datbuf = buf; |
998 | ops.len = len; | 992 | ops.len = len; |
999 | ops.mode = MTD_OPS_AUTO_OOB; | 993 | ops.mode = MTD_OPS_AUTO_OOB; |
1000 | 994 | ||
1001 | ret = doc_read_oob(mtd, from, &ops); | 995 | ret = doc_read_oob(mtd, from, &ops); |
1002 | *retlen = ops.retlen; | 996 | *retlen = ops.retlen; |
1003 | return ret; | 997 | return ret; |
1004 | } | 998 | } |
1005 | 999 | ||
1006 | static int doc_reload_bbt(struct docg3 *docg3) | 1000 | static int doc_reload_bbt(struct docg3 *docg3) |
1007 | { | 1001 | { |
1008 | int block = DOC_LAYOUT_BLOCK_BBT; | 1002 | int block = DOC_LAYOUT_BLOCK_BBT; |
1009 | int ret = 0, nbpages, page; | 1003 | int ret = 0, nbpages, page; |
1010 | u_char *buf = docg3->bbt; | 1004 | u_char *buf = docg3->bbt; |
1011 | 1005 | ||
1012 | nbpages = DIV_ROUND_UP(docg3->max_block + 1, 8 * DOC_LAYOUT_PAGE_SIZE); | 1006 | nbpages = DIV_ROUND_UP(docg3->max_block + 1, 8 * DOC_LAYOUT_PAGE_SIZE); |
1013 | for (page = 0; !ret && (page < nbpages); page++) { | 1007 | for (page = 0; !ret && (page < nbpages); page++) { |
1014 | ret = doc_read_page_prepare(docg3, block, block + 1, | 1008 | ret = doc_read_page_prepare(docg3, block, block + 1, |
1015 | page + DOC_LAYOUT_PAGE_BBT, 0); | 1009 | page + DOC_LAYOUT_PAGE_BBT, 0); |
1016 | if (!ret) | 1010 | if (!ret) |
1017 | ret = doc_read_page_ecc_init(docg3, | 1011 | ret = doc_read_page_ecc_init(docg3, |
1018 | DOC_LAYOUT_PAGE_SIZE); | 1012 | DOC_LAYOUT_PAGE_SIZE); |
1019 | if (!ret) | 1013 | if (!ret) |
1020 | doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE, | 1014 | doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE, |
1021 | buf, 1, 0); | 1015 | buf, 1, 0); |
1022 | buf += DOC_LAYOUT_PAGE_SIZE; | 1016 | buf += DOC_LAYOUT_PAGE_SIZE; |
1023 | } | 1017 | } |
1024 | doc_read_page_finish(docg3); | 1018 | doc_read_page_finish(docg3); |
1025 | return ret; | 1019 | return ret; |
1026 | } | 1020 | } |
1027 | 1021 | ||
1028 | /** | 1022 | /** |
1029 | * doc_block_isbad - Checks whether a block is good or not | 1023 | * doc_block_isbad - Checks whether a block is good or not |
1030 | * @mtd: the device | 1024 | * @mtd: the device |
1031 | * @from: the offset to find the correct block | 1025 | * @from: the offset to find the correct block |
1032 | * | 1026 | * |
1033 | * Returns 1 if block is bad, 0 if block is good | 1027 | * Returns 1 if block is bad, 0 if block is good |
1034 | */ | 1028 | */ |
1035 | static int doc_block_isbad(struct mtd_info *mtd, loff_t from) | 1029 | static int doc_block_isbad(struct mtd_info *mtd, loff_t from) |
1036 | { | 1030 | { |
1037 | struct docg3 *docg3 = mtd->priv; | 1031 | struct docg3 *docg3 = mtd->priv; |
1038 | int block0, block1, page, ofs, is_good; | 1032 | int block0, block1, page, ofs, is_good; |
1039 | 1033 | ||
1040 | calc_block_sector(from, &block0, &block1, &page, &ofs, | 1034 | calc_block_sector(from, &block0, &block1, &page, &ofs, |
1041 | docg3->reliable); | 1035 | docg3->reliable); |
1042 | doc_dbg("doc_block_isbad(from=%lld) => block=(%d,%d), page=%d, ofs=%d\n", | 1036 | doc_dbg("doc_block_isbad(from=%lld) => block=(%d,%d), page=%d, ofs=%d\n", |
1043 | from, block0, block1, page, ofs); | 1037 | from, block0, block1, page, ofs); |
1044 | 1038 | ||
1045 | if (block0 < DOC_LAYOUT_BLOCK_FIRST_DATA) | 1039 | if (block0 < DOC_LAYOUT_BLOCK_FIRST_DATA) |
1046 | return 0; | 1040 | return 0; |
1047 | if (block1 > docg3->max_block) | 1041 | if (block1 > docg3->max_block) |
1048 | return -EINVAL; | 1042 | return -EINVAL; |
1049 | 1043 | ||
1050 | is_good = docg3->bbt[block0 >> 3] & (1 << (block0 & 0x7)); | 1044 | is_good = docg3->bbt[block0 >> 3] & (1 << (block0 & 0x7)); |
1051 | return !is_good; | 1045 | return !is_good; |
1052 | } | 1046 | } |
1053 | 1047 | ||
1054 | #if 0 | 1048 | #if 0 |
1055 | /** | 1049 | /** |
1056 | * doc_get_erase_count - Get block erase count | 1050 | * doc_get_erase_count - Get block erase count |
1057 | * @docg3: the device | 1051 | * @docg3: the device |
1058 | * @from: the offset in which the block is. | 1052 | * @from: the offset in which the block is. |
1059 | * | 1053 | * |
1060 | * Get the number of times a block was erased. The number is the maximum of | 1054 | * Get the number of times a block was erased. The number is the maximum of |
1061 | * erase times between first and second plane (which should be equal normally). | 1055 | * erase times between first and second plane (which should be equal normally). |
1062 | * | 1056 | * |
1063 | * Returns The number of erases, or -EINVAL or -EIO on error. | 1057 | * Returns The number of erases, or -EINVAL or -EIO on error. |
1064 | */ | 1058 | */ |
1065 | static int doc_get_erase_count(struct docg3 *docg3, loff_t from) | 1059 | static int doc_get_erase_count(struct docg3 *docg3, loff_t from) |
1066 | { | 1060 | { |
1067 | u8 buf[DOC_LAYOUT_WEAR_SIZE]; | 1061 | u8 buf[DOC_LAYOUT_WEAR_SIZE]; |
1068 | int ret, plane1_erase_count, plane2_erase_count; | 1062 | int ret, plane1_erase_count, plane2_erase_count; |
1069 | int block0, block1, page, ofs; | 1063 | int block0, block1, page, ofs; |
1070 | 1064 | ||
1071 | doc_dbg("doc_get_erase_count(from=%lld, buf=%p)\n", from, buf); | 1065 | doc_dbg("doc_get_erase_count(from=%lld, buf=%p)\n", from, buf); |
1072 | if (from % DOC_LAYOUT_PAGE_SIZE) | 1066 | if (from % DOC_LAYOUT_PAGE_SIZE) |
1073 | return -EINVAL; | 1067 | return -EINVAL; |
1074 | calc_block_sector(from, &block0, &block1, &page, &ofs, docg3->reliable); | 1068 | calc_block_sector(from, &block0, &block1, &page, &ofs, docg3->reliable); |
1075 | if (block1 > docg3->max_block) | 1069 | if (block1 > docg3->max_block) |
1076 | return -EINVAL; | 1070 | return -EINVAL; |
1077 | 1071 | ||
1078 | ret = doc_reset_seq(docg3); | 1072 | ret = doc_reset_seq(docg3); |
1079 | if (!ret) | 1073 | if (!ret) |
1080 | ret = doc_read_page_prepare(docg3, block0, block1, page, | 1074 | ret = doc_read_page_prepare(docg3, block0, block1, page, |
1081 | ofs + DOC_LAYOUT_WEAR_OFFSET, 0); | 1075 | ofs + DOC_LAYOUT_WEAR_OFFSET, 0); |
1082 | if (!ret) | 1076 | if (!ret) |
1083 | ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE, | 1077 | ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE, |
1084 | buf, 1, 0); | 1078 | buf, 1, 0); |
1085 | doc_read_page_finish(docg3); | 1079 | doc_read_page_finish(docg3); |
1086 | 1080 | ||
1087 | if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK)) | 1081 | if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK)) |
1088 | return -EIO; | 1082 | return -EIO; |
1089 | plane1_erase_count = (u8)(~buf[1]) | ((u8)(~buf[4]) << 8) | 1083 | plane1_erase_count = (u8)(~buf[1]) | ((u8)(~buf[4]) << 8) |
1090 | | ((u8)(~buf[5]) << 16); | 1084 | | ((u8)(~buf[5]) << 16); |
1091 | plane2_erase_count = (u8)(~buf[3]) | ((u8)(~buf[6]) << 8) | 1085 | plane2_erase_count = (u8)(~buf[3]) | ((u8)(~buf[6]) << 8) |
1092 | | ((u8)(~buf[7]) << 16); | 1086 | | ((u8)(~buf[7]) << 16); |
1093 | 1087 | ||
1094 | return max(plane1_erase_count, plane2_erase_count); | 1088 | return max(plane1_erase_count, plane2_erase_count); |
1095 | } | 1089 | } |
1096 | #endif | 1090 | #endif |
1097 | 1091 | ||
1098 | /** | 1092 | /** |
1099 | * doc_get_op_status - get erase/write operation status | 1093 | * doc_get_op_status - get erase/write operation status |
1100 | * @docg3: the device | 1094 | * @docg3: the device |
1101 | * | 1095 | * |
1102 | * Queries the status from the chip, and returns it | 1096 | * Queries the status from the chip, and returns it |
1103 | * | 1097 | * |
1104 | * Returns the status (bits DOC_PLANES_STATUS_*) | 1098 | * Returns the status (bits DOC_PLANES_STATUS_*) |
1105 | */ | 1099 | */ |
1106 | static int doc_get_op_status(struct docg3 *docg3) | 1100 | static int doc_get_op_status(struct docg3 *docg3) |
1107 | { | 1101 | { |
1108 | u8 status; | 1102 | u8 status; |
1109 | 1103 | ||
1110 | doc_flash_sequence(docg3, DOC_SEQ_PLANES_STATUS); | 1104 | doc_flash_sequence(docg3, DOC_SEQ_PLANES_STATUS); |
1111 | doc_flash_command(docg3, DOC_CMD_PLANES_STATUS); | 1105 | doc_flash_command(docg3, DOC_CMD_PLANES_STATUS); |
1112 | doc_delay(docg3, 5); | 1106 | doc_delay(docg3, 5); |
1113 | 1107 | ||
1114 | doc_ecc_disable(docg3); | 1108 | doc_ecc_disable(docg3); |
1115 | doc_read_data_area(docg3, &status, 1, 1); | 1109 | doc_read_data_area(docg3, &status, 1, 1); |
1116 | return status; | 1110 | return status; |
1117 | } | 1111 | } |
1118 | 1112 | ||
1119 | /** | 1113 | /** |
1120 | * doc_write_erase_wait_status - wait for write or erase completion | 1114 | * doc_write_erase_wait_status - wait for write or erase completion |
1121 | * @docg3: the device | 1115 | * @docg3: the device |
1122 | * | 1116 | * |
1123 | * Wait for the chip to be ready again after erase or write operation, and check | 1117 | * Wait for the chip to be ready again after erase or write operation, and check |
1124 | * erase/write status. | 1118 | * erase/write status. |
1125 | * | 1119 | * |
1126 | * Returns 0 if erase successful, -EIO if erase/write issue, -ETIMEOUT if | 1120 | * Returns 0 if erase successful, -EIO if erase/write issue, -ETIMEOUT if |
1127 | * timeout | 1121 | * timeout |
1128 | */ | 1122 | */ |
1129 | static int doc_write_erase_wait_status(struct docg3 *docg3) | 1123 | static int doc_write_erase_wait_status(struct docg3 *docg3) |
1130 | { | 1124 | { |
1131 | int i, status, ret = 0; | 1125 | int i, status, ret = 0; |
1132 | 1126 | ||
1133 | for (i = 0; !doc_is_ready(docg3) && i < 5; i++) | 1127 | for (i = 0; !doc_is_ready(docg3) && i < 5; i++) |
1134 | msleep(20); | 1128 | msleep(20); |
1135 | if (!doc_is_ready(docg3)) { | 1129 | if (!doc_is_ready(docg3)) { |
1136 | doc_dbg("Timeout reached and the chip is still not ready\n"); | 1130 | doc_dbg("Timeout reached and the chip is still not ready\n"); |
1137 | ret = -EAGAIN; | 1131 | ret = -EAGAIN; |
1138 | goto out; | 1132 | goto out; |
1139 | } | 1133 | } |
1140 | 1134 | ||
1141 | status = doc_get_op_status(docg3); | 1135 | status = doc_get_op_status(docg3); |
1142 | if (status & DOC_PLANES_STATUS_FAIL) { | 1136 | if (status & DOC_PLANES_STATUS_FAIL) { |
1143 | doc_dbg("Erase/Write failed on (a) plane(s), status = %x\n", | 1137 | doc_dbg("Erase/Write failed on (a) plane(s), status = %x\n", |
1144 | status); | 1138 | status); |
1145 | ret = -EIO; | 1139 | ret = -EIO; |
1146 | } | 1140 | } |
1147 | 1141 | ||
1148 | out: | 1142 | out: |
1149 | doc_page_finish(docg3); | 1143 | doc_page_finish(docg3); |
1150 | return ret; | 1144 | return ret; |
1151 | } | 1145 | } |
1152 | 1146 | ||
1153 | /** | 1147 | /** |
1154 | * doc_erase_block - Erase a couple of blocks | 1148 | * doc_erase_block - Erase a couple of blocks |
1155 | * @docg3: the device | 1149 | * @docg3: the device |
1156 | * @block0: the first block to erase (leftmost plane) | 1150 | * @block0: the first block to erase (leftmost plane) |
1157 | * @block1: the second block to erase (rightmost plane) | 1151 | * @block1: the second block to erase (rightmost plane) |
1158 | * | 1152 | * |
1159 | * Erase both blocks, and return operation status | 1153 | * Erase both blocks, and return operation status |
1160 | * | 1154 | * |
1161 | * Returns 0 if erase successful, -EIO if erase issue, -ETIMEOUT if chip not | 1155 | * Returns 0 if erase successful, -EIO if erase issue, -ETIMEOUT if chip not |
1162 | * ready for too long | 1156 | * ready for too long |
1163 | */ | 1157 | */ |
1164 | static int doc_erase_block(struct docg3 *docg3, int block0, int block1) | 1158 | static int doc_erase_block(struct docg3 *docg3, int block0, int block1) |
1165 | { | 1159 | { |
1166 | int ret, sector; | 1160 | int ret, sector; |
1167 | 1161 | ||
1168 | doc_dbg("doc_erase_block(blocks=(%d,%d))\n", block0, block1); | 1162 | doc_dbg("doc_erase_block(blocks=(%d,%d))\n", block0, block1); |
1169 | ret = doc_reset_seq(docg3); | 1163 | ret = doc_reset_seq(docg3); |
1170 | if (ret) | 1164 | if (ret) |
1171 | return -EIO; | 1165 | return -EIO; |
1172 | 1166 | ||
1173 | doc_set_reliable_mode(docg3); | 1167 | doc_set_reliable_mode(docg3); |
1174 | doc_flash_sequence(docg3, DOC_SEQ_ERASE); | 1168 | doc_flash_sequence(docg3, DOC_SEQ_ERASE); |
1175 | 1169 | ||
1176 | sector = block0 << DOC_ADDR_BLOCK_SHIFT; | 1170 | sector = block0 << DOC_ADDR_BLOCK_SHIFT; |
1177 | doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); | 1171 | doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); |
1178 | doc_setup_addr_sector(docg3, sector); | 1172 | doc_setup_addr_sector(docg3, sector); |
1179 | sector = block1 << DOC_ADDR_BLOCK_SHIFT; | 1173 | sector = block1 << DOC_ADDR_BLOCK_SHIFT; |
1180 | doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); | 1174 | doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); |
1181 | doc_setup_addr_sector(docg3, sector); | 1175 | doc_setup_addr_sector(docg3, sector); |
1182 | doc_delay(docg3, 1); | 1176 | doc_delay(docg3, 1); |
1183 | 1177 | ||
1184 | doc_flash_command(docg3, DOC_CMD_ERASECYCLE2); | 1178 | doc_flash_command(docg3, DOC_CMD_ERASECYCLE2); |
1185 | doc_delay(docg3, 2); | 1179 | doc_delay(docg3, 2); |
1186 | 1180 | ||
1187 | if (is_prot_seq_error(docg3)) { | 1181 | if (is_prot_seq_error(docg3)) { |
1188 | doc_err("Erase blocks %d,%d error\n", block0, block1); | 1182 | doc_err("Erase blocks %d,%d error\n", block0, block1); |
1189 | return -EIO; | 1183 | return -EIO; |
1190 | } | 1184 | } |
1191 | 1185 | ||
1192 | return doc_write_erase_wait_status(docg3); | 1186 | return doc_write_erase_wait_status(docg3); |
1193 | } | 1187 | } |
1194 | 1188 | ||
1195 | /** | 1189 | /** |
1196 | * doc_erase - Erase a portion of the chip | 1190 | * doc_erase - Erase a portion of the chip |
1197 | * @mtd: the device | 1191 | * @mtd: the device |
1198 | * @info: the erase info | 1192 | * @info: the erase info |
1199 | * | 1193 | * |
1200 | * Erase a bunch of contiguous blocks, by pairs, as a "mtd" page of 1024 is | 1194 | * Erase a bunch of contiguous blocks, by pairs, as a "mtd" page of 1024 is |
1201 | * split into 2 pages of 512 bytes on 2 contiguous blocks. | 1195 | * split into 2 pages of 512 bytes on 2 contiguous blocks. |
1202 | * | 1196 | * |
1203 | * Returns 0 if erase successful, -EINVAL if addressing error, -EIO if erase | 1197 | * Returns 0 if erase successful, -EINVAL if addressing error, -EIO if erase |
1204 | * issue | 1198 | * issue |
1205 | */ | 1199 | */ |
1206 | static int doc_erase(struct mtd_info *mtd, struct erase_info *info) | 1200 | static int doc_erase(struct mtd_info *mtd, struct erase_info *info) |
1207 | { | 1201 | { |
1208 | struct docg3 *docg3 = mtd->priv; | 1202 | struct docg3 *docg3 = mtd->priv; |
1209 | uint64_t len; | 1203 | uint64_t len; |
1210 | int block0, block1, page, ret, ofs = 0; | 1204 | int block0, block1, page, ret, ofs = 0; |
1211 | 1205 | ||
1212 | doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len); | 1206 | doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len); |
1213 | 1207 | ||
1214 | info->state = MTD_ERASE_PENDING; | 1208 | info->state = MTD_ERASE_PENDING; |
1215 | calc_block_sector(info->addr + info->len, &block0, &block1, &page, | 1209 | calc_block_sector(info->addr + info->len, &block0, &block1, &page, |
1216 | &ofs, docg3->reliable); | 1210 | &ofs, docg3->reliable); |
1217 | ret = -EINVAL; | 1211 | ret = -EINVAL; |
1218 | if (info->addr + info->len > mtd->size || page || ofs) | 1212 | if (info->addr + info->len > mtd->size || page || ofs) |
1219 | goto reset_err; | 1213 | goto reset_err; |
1220 | 1214 | ||
1221 | ret = 0; | 1215 | ret = 0; |
1222 | calc_block_sector(info->addr, &block0, &block1, &page, &ofs, | 1216 | calc_block_sector(info->addr, &block0, &block1, &page, &ofs, |
1223 | docg3->reliable); | 1217 | docg3->reliable); |
1224 | mutex_lock(&docg3->cascade->lock); | 1218 | mutex_lock(&docg3->cascade->lock); |
1225 | doc_set_device_id(docg3, docg3->device_id); | 1219 | doc_set_device_id(docg3, docg3->device_id); |
1226 | doc_set_reliable_mode(docg3); | 1220 | doc_set_reliable_mode(docg3); |
1227 | for (len = info->len; !ret && len > 0; len -= mtd->erasesize) { | 1221 | for (len = info->len; !ret && len > 0; len -= mtd->erasesize) { |
1228 | info->state = MTD_ERASING; | 1222 | info->state = MTD_ERASING; |
1229 | ret = doc_erase_block(docg3, block0, block1); | 1223 | ret = doc_erase_block(docg3, block0, block1); |
1230 | block0 += 2; | 1224 | block0 += 2; |
1231 | block1 += 2; | 1225 | block1 += 2; |
1232 | } | 1226 | } |
1233 | mutex_unlock(&docg3->cascade->lock); | 1227 | mutex_unlock(&docg3->cascade->lock); |
1234 | 1228 | ||
1235 | if (ret) | 1229 | if (ret) |
1236 | goto reset_err; | 1230 | goto reset_err; |
1237 | 1231 | ||
1238 | info->state = MTD_ERASE_DONE; | 1232 | info->state = MTD_ERASE_DONE; |
1239 | return 0; | 1233 | return 0; |
1240 | 1234 | ||
1241 | reset_err: | 1235 | reset_err: |
1242 | info->state = MTD_ERASE_FAILED; | 1236 | info->state = MTD_ERASE_FAILED; |
1243 | return ret; | 1237 | return ret; |
1244 | } | 1238 | } |
1245 | 1239 | ||
1246 | /** | 1240 | /** |
1247 | * doc_write_page - Write a single page to the chip | 1241 | * doc_write_page - Write a single page to the chip |
1248 | * @docg3: the device | 1242 | * @docg3: the device |
1249 | * @to: the offset from first block and first page, in bytes, aligned on page | 1243 | * @to: the offset from first block and first page, in bytes, aligned on page |
1250 | * size | 1244 | * size |
1251 | * @buf: buffer to get bytes from | 1245 | * @buf: buffer to get bytes from |
1252 | * @oob: buffer to get out of band bytes from (can be NULL if no OOB should be | 1246 | * @oob: buffer to get out of band bytes from (can be NULL if no OOB should be |
1253 | * written) | 1247 | * written) |
1254 | * @autoecc: if 0, all 16 bytes from OOB are taken, regardless of HW Hamming or | 1248 | * @autoecc: if 0, all 16 bytes from OOB are taken, regardless of HW Hamming or |
1255 | * BCH computations. If 1, only bytes 0-7 and byte 15 are taken, | 1249 | * BCH computations. If 1, only bytes 0-7 and byte 15 are taken, |
1256 | * remaining ones are filled with hardware Hamming and BCH | 1250 | * remaining ones are filled with hardware Hamming and BCH |
1257 | * computations. Its value is not meaningfull is oob == NULL. | 1251 | * computations. Its value is not meaningfull is oob == NULL. |
1258 | * | 1252 | * |
1259 | * Write one full page (ie. 1 page split on two planes), of 512 bytes, with the | 1253 | * Write one full page (ie. 1 page split on two planes), of 512 bytes, with the |
1260 | * OOB data. The OOB ECC is automatically computed by the hardware Hamming and | 1254 | * OOB data. The OOB ECC is automatically computed by the hardware Hamming and |
1261 | * BCH generator if autoecc is not null. | 1255 | * BCH generator if autoecc is not null. |
1262 | * | 1256 | * |
1263 | * Returns 0 if write successful, -EIO if write error, -EAGAIN if timeout | 1257 | * Returns 0 if write successful, -EIO if write error, -EAGAIN if timeout |
1264 | */ | 1258 | */ |
1265 | static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf, | 1259 | static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf, |
1266 | const u_char *oob, int autoecc) | 1260 | const u_char *oob, int autoecc) |
1267 | { | 1261 | { |
1268 | int block0, block1, page, ret, ofs = 0; | 1262 | int block0, block1, page, ret, ofs = 0; |
1269 | u8 hwecc[DOC_ECC_BCH_SIZE], hamming; | 1263 | u8 hwecc[DOC_ECC_BCH_SIZE], hamming; |
1270 | 1264 | ||
1271 | doc_dbg("doc_write_page(to=%lld)\n", to); | 1265 | doc_dbg("doc_write_page(to=%lld)\n", to); |
1272 | calc_block_sector(to, &block0, &block1, &page, &ofs, docg3->reliable); | 1266 | calc_block_sector(to, &block0, &block1, &page, &ofs, docg3->reliable); |
1273 | 1267 | ||
1274 | doc_set_device_id(docg3, docg3->device_id); | 1268 | doc_set_device_id(docg3, docg3->device_id); |
1275 | ret = doc_reset_seq(docg3); | 1269 | ret = doc_reset_seq(docg3); |
1276 | if (ret) | 1270 | if (ret) |
1277 | goto err; | 1271 | goto err; |
1278 | 1272 | ||
1279 | /* Program the flash address block and page */ | 1273 | /* Program the flash address block and page */ |
1280 | ret = doc_write_seek(docg3, block0, block1, page, ofs); | 1274 | ret = doc_write_seek(docg3, block0, block1, page, ofs); |
1281 | if (ret) | 1275 | if (ret) |
1282 | goto err; | 1276 | goto err; |
1283 | 1277 | ||
1284 | doc_write_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); | 1278 | doc_write_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); |
1285 | doc_delay(docg3, 2); | 1279 | doc_delay(docg3, 2); |
1286 | doc_write_page_putbytes(docg3, DOC_LAYOUT_PAGE_SIZE, buf); | 1280 | doc_write_page_putbytes(docg3, DOC_LAYOUT_PAGE_SIZE, buf); |
1287 | 1281 | ||
1288 | if (oob && autoecc) { | 1282 | if (oob && autoecc) { |
1289 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ, oob); | 1283 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ, oob); |
1290 | doc_delay(docg3, 2); | 1284 | doc_delay(docg3, 2); |
1291 | oob += DOC_LAYOUT_OOB_UNUSED_OFS; | 1285 | oob += DOC_LAYOUT_OOB_UNUSED_OFS; |
1292 | 1286 | ||
1293 | hamming = doc_register_readb(docg3, DOC_HAMMINGPARITY); | 1287 | hamming = doc_register_readb(docg3, DOC_HAMMINGPARITY); |
1294 | doc_delay(docg3, 2); | 1288 | doc_delay(docg3, 2); |
1295 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_HAMMING_SZ, | 1289 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_HAMMING_SZ, |
1296 | &hamming); | 1290 | &hamming); |
1297 | doc_delay(docg3, 2); | 1291 | doc_delay(docg3, 2); |
1298 | 1292 | ||
1299 | doc_get_bch_hw_ecc(docg3, hwecc); | 1293 | doc_get_bch_hw_ecc(docg3, hwecc); |
1300 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_BCH_SZ, hwecc); | 1294 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_BCH_SZ, hwecc); |
1301 | doc_delay(docg3, 2); | 1295 | doc_delay(docg3, 2); |
1302 | 1296 | ||
1303 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_UNUSED_SZ, oob); | 1297 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_UNUSED_SZ, oob); |
1304 | } | 1298 | } |
1305 | if (oob && !autoecc) | 1299 | if (oob && !autoecc) |
1306 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_SIZE, oob); | 1300 | doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_SIZE, oob); |
1307 | 1301 | ||
1308 | doc_delay(docg3, 2); | 1302 | doc_delay(docg3, 2); |
1309 | doc_page_finish(docg3); | 1303 | doc_page_finish(docg3); |
1310 | doc_delay(docg3, 2); | 1304 | doc_delay(docg3, 2); |
1311 | doc_flash_command(docg3, DOC_CMD_PROG_CYCLE2); | 1305 | doc_flash_command(docg3, DOC_CMD_PROG_CYCLE2); |
1312 | doc_delay(docg3, 2); | 1306 | doc_delay(docg3, 2); |
1313 | 1307 | ||
1314 | /* | 1308 | /* |
1315 | * The wait status will perform another doc_page_finish() call, but that | 1309 | * The wait status will perform another doc_page_finish() call, but that |
1316 | * seems to please the docg3, so leave it. | 1310 | * seems to please the docg3, so leave it. |
1317 | */ | 1311 | */ |
1318 | ret = doc_write_erase_wait_status(docg3); | 1312 | ret = doc_write_erase_wait_status(docg3); |
1319 | return ret; | 1313 | return ret; |
1320 | err: | 1314 | err: |
1321 | doc_read_page_finish(docg3); | 1315 | doc_read_page_finish(docg3); |
1322 | return ret; | 1316 | return ret; |
1323 | } | 1317 | } |
1324 | 1318 | ||
1325 | /** | 1319 | /** |
1326 | * doc_guess_autoecc - Guess autoecc mode from mbd_oob_ops | 1320 | * doc_guess_autoecc - Guess autoecc mode from mbd_oob_ops |
1327 | * @ops: the oob operations | 1321 | * @ops: the oob operations |
1328 | * | 1322 | * |
1329 | * Returns 0 or 1 if success, -EINVAL if invalid oob mode | 1323 | * Returns 0 or 1 if success, -EINVAL if invalid oob mode |
1330 | */ | 1324 | */ |
1331 | static int doc_guess_autoecc(struct mtd_oob_ops *ops) | 1325 | static int doc_guess_autoecc(struct mtd_oob_ops *ops) |
1332 | { | 1326 | { |
1333 | int autoecc; | 1327 | int autoecc; |
1334 | 1328 | ||
1335 | switch (ops->mode) { | 1329 | switch (ops->mode) { |
1336 | case MTD_OPS_PLACE_OOB: | 1330 | case MTD_OPS_PLACE_OOB: |
1337 | case MTD_OPS_AUTO_OOB: | 1331 | case MTD_OPS_AUTO_OOB: |
1338 | autoecc = 1; | 1332 | autoecc = 1; |
1339 | break; | 1333 | break; |
1340 | case MTD_OPS_RAW: | 1334 | case MTD_OPS_RAW: |
1341 | autoecc = 0; | 1335 | autoecc = 0; |
1342 | break; | 1336 | break; |
1343 | default: | 1337 | default: |
1344 | autoecc = -EINVAL; | 1338 | autoecc = -EINVAL; |
1345 | } | 1339 | } |
1346 | return autoecc; | 1340 | return autoecc; |
1347 | } | 1341 | } |
1348 | 1342 | ||
1349 | /** | 1343 | /** |
1350 | * doc_fill_autooob - Fill a 16 bytes OOB from 8 non-ECC bytes | 1344 | * doc_fill_autooob - Fill a 16 bytes OOB from 8 non-ECC bytes |
1351 | * @dst: the target 16 bytes OOB buffer | 1345 | * @dst: the target 16 bytes OOB buffer |
1352 | * @oobsrc: the source 8 bytes non-ECC OOB buffer | 1346 | * @oobsrc: the source 8 bytes non-ECC OOB buffer |
1353 | * | 1347 | * |
1354 | */ | 1348 | */ |
1355 | static void doc_fill_autooob(u8 *dst, u8 *oobsrc) | 1349 | static void doc_fill_autooob(u8 *dst, u8 *oobsrc) |
1356 | { | 1350 | { |
1357 | memcpy(dst, oobsrc, DOC_LAYOUT_OOB_PAGEINFO_SZ); | 1351 | memcpy(dst, oobsrc, DOC_LAYOUT_OOB_PAGEINFO_SZ); |
1358 | dst[DOC_LAYOUT_OOB_UNUSED_OFS] = oobsrc[DOC_LAYOUT_OOB_PAGEINFO_SZ]; | 1352 | dst[DOC_LAYOUT_OOB_UNUSED_OFS] = oobsrc[DOC_LAYOUT_OOB_PAGEINFO_SZ]; |
1359 | } | 1353 | } |
1360 | 1354 | ||
1361 | /** | 1355 | /** |
1362 | * doc_backup_oob - Backup OOB into docg3 structure | 1356 | * doc_backup_oob - Backup OOB into docg3 structure |
1363 | * @docg3: the device | 1357 | * @docg3: the device |
1364 | * @to: the page offset in the chip | 1358 | * @to: the page offset in the chip |
1365 | * @ops: the OOB size and buffer | 1359 | * @ops: the OOB size and buffer |
1366 | * | 1360 | * |
1367 | * As the docg3 should write a page with its OOB in one pass, and some userland | 1361 | * As the docg3 should write a page with its OOB in one pass, and some userland |
1368 | * applications do write_oob() to setup the OOB and then write(), store the OOB | 1362 | * applications do write_oob() to setup the OOB and then write(), store the OOB |
1369 | * into a temporary storage. This is very dangerous, as 2 concurrent | 1363 | * into a temporary storage. This is very dangerous, as 2 concurrent |
1370 | * applications could store an OOB, and then write their pages (which will | 1364 | * applications could store an OOB, and then write their pages (which will |
1371 | * result into one having its OOB corrupted). | 1365 | * result into one having its OOB corrupted). |
1372 | * | 1366 | * |
1373 | * The only reliable way would be for userland to call doc_write_oob() with both | 1367 | * The only reliable way would be for userland to call doc_write_oob() with both |
1374 | * the page data _and_ the OOB area. | 1368 | * the page data _and_ the OOB area. |
1375 | * | 1369 | * |
1376 | * Returns 0 if success, -EINVAL if ops content invalid | 1370 | * Returns 0 if success, -EINVAL if ops content invalid |
1377 | */ | 1371 | */ |
1378 | static int doc_backup_oob(struct docg3 *docg3, loff_t to, | 1372 | static int doc_backup_oob(struct docg3 *docg3, loff_t to, |
1379 | struct mtd_oob_ops *ops) | 1373 | struct mtd_oob_ops *ops) |
1380 | { | 1374 | { |
1381 | int ooblen = ops->ooblen, autoecc; | 1375 | int ooblen = ops->ooblen, autoecc; |
1382 | 1376 | ||
1383 | if (ooblen != DOC_LAYOUT_OOB_SIZE) | 1377 | if (ooblen != DOC_LAYOUT_OOB_SIZE) |
1384 | return -EINVAL; | 1378 | return -EINVAL; |
1385 | autoecc = doc_guess_autoecc(ops); | 1379 | autoecc = doc_guess_autoecc(ops); |
1386 | if (autoecc < 0) | 1380 | if (autoecc < 0) |
1387 | return autoecc; | 1381 | return autoecc; |
1388 | 1382 | ||
1389 | docg3->oob_write_ofs = to; | 1383 | docg3->oob_write_ofs = to; |
1390 | docg3->oob_autoecc = autoecc; | 1384 | docg3->oob_autoecc = autoecc; |
1391 | if (ops->mode == MTD_OPS_AUTO_OOB) { | 1385 | if (ops->mode == MTD_OPS_AUTO_OOB) { |
1392 | doc_fill_autooob(docg3->oob_write_buf, ops->oobbuf); | 1386 | doc_fill_autooob(docg3->oob_write_buf, ops->oobbuf); |
1393 | ops->oobretlen = 8; | 1387 | ops->oobretlen = 8; |
1394 | } else { | 1388 | } else { |
1395 | memcpy(docg3->oob_write_buf, ops->oobbuf, DOC_LAYOUT_OOB_SIZE); | 1389 | memcpy(docg3->oob_write_buf, ops->oobbuf, DOC_LAYOUT_OOB_SIZE); |
1396 | ops->oobretlen = DOC_LAYOUT_OOB_SIZE; | 1390 | ops->oobretlen = DOC_LAYOUT_OOB_SIZE; |
1397 | } | 1391 | } |
1398 | return 0; | 1392 | return 0; |
1399 | } | 1393 | } |
1400 | 1394 | ||
1401 | /** | 1395 | /** |
1402 | * doc_write_oob - Write out of band bytes to flash | 1396 | * doc_write_oob - Write out of band bytes to flash |
1403 | * @mtd: the device | 1397 | * @mtd: the device |
1404 | * @ofs: the offset from first block and first page, in bytes, aligned on page | 1398 | * @ofs: the offset from first block and first page, in bytes, aligned on page |
1405 | * size | 1399 | * size |
1406 | * @ops: the mtd oob structure | 1400 | * @ops: the mtd oob structure |
1407 | * | 1401 | * |
1408 | * Either write OOB data into a temporary buffer, for the subsequent write | 1402 | * Either write OOB data into a temporary buffer, for the subsequent write |
1409 | * page. The provided OOB should be 16 bytes long. If a data buffer is provided | 1403 | * page. The provided OOB should be 16 bytes long. If a data buffer is provided |
1410 | * as well, issue the page write. | 1404 | * as well, issue the page write. |
1411 | * Or provide data without OOB, and then a all zeroed OOB will be used (ECC will | 1405 | * Or provide data without OOB, and then a all zeroed OOB will be used (ECC will |
1412 | * still be filled in if asked for). | 1406 | * still be filled in if asked for). |
1413 | * | 1407 | * |
1414 | * Returns 0 is successful, EINVAL if length is not 14 bytes | 1408 | * Returns 0 is successful, EINVAL if length is not 14 bytes |
1415 | */ | 1409 | */ |
1416 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, | 1410 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, |
1417 | struct mtd_oob_ops *ops) | 1411 | struct mtd_oob_ops *ops) |
1418 | { | 1412 | { |
1419 | struct docg3 *docg3 = mtd->priv; | 1413 | struct docg3 *docg3 = mtd->priv; |
1420 | int ret, autoecc, oobdelta; | 1414 | int ret, autoecc, oobdelta; |
1421 | u8 *oobbuf = ops->oobbuf; | 1415 | u8 *oobbuf = ops->oobbuf; |
1422 | u8 *buf = ops->datbuf; | 1416 | u8 *buf = ops->datbuf; |
1423 | size_t len, ooblen; | 1417 | size_t len, ooblen; |
1424 | u8 oob[DOC_LAYOUT_OOB_SIZE]; | 1418 | u8 oob[DOC_LAYOUT_OOB_SIZE]; |
1425 | 1419 | ||
1426 | if (buf) | 1420 | if (buf) |
1427 | len = ops->len; | 1421 | len = ops->len; |
1428 | else | 1422 | else |
1429 | len = 0; | 1423 | len = 0; |
1430 | if (oobbuf) | 1424 | if (oobbuf) |
1431 | ooblen = ops->ooblen; | 1425 | ooblen = ops->ooblen; |
1432 | else | 1426 | else |
1433 | ooblen = 0; | 1427 | ooblen = 0; |
1434 | 1428 | ||
1435 | if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) | 1429 | if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) |
1436 | oobbuf += ops->ooboffs; | 1430 | oobbuf += ops->ooboffs; |
1437 | 1431 | ||
1438 | doc_dbg("doc_write_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", | 1432 | doc_dbg("doc_write_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", |
1439 | ofs, ops->mode, buf, len, oobbuf, ooblen); | 1433 | ofs, ops->mode, buf, len, oobbuf, ooblen); |
1440 | switch (ops->mode) { | 1434 | switch (ops->mode) { |
1441 | case MTD_OPS_PLACE_OOB: | 1435 | case MTD_OPS_PLACE_OOB: |
1442 | case MTD_OPS_RAW: | 1436 | case MTD_OPS_RAW: |
1443 | oobdelta = mtd->oobsize; | 1437 | oobdelta = mtd->oobsize; |
1444 | break; | 1438 | break; |
1445 | case MTD_OPS_AUTO_OOB: | 1439 | case MTD_OPS_AUTO_OOB: |
1446 | oobdelta = mtd->ecclayout->oobavail; | 1440 | oobdelta = mtd->ecclayout->oobavail; |
1447 | break; | 1441 | break; |
1448 | default: | 1442 | default: |
1449 | oobdelta = 0; | 1443 | oobdelta = 0; |
1450 | } | 1444 | } |
1451 | if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % oobdelta) || | 1445 | if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % oobdelta) || |
1452 | (ofs % DOC_LAYOUT_PAGE_SIZE)) | 1446 | (ofs % DOC_LAYOUT_PAGE_SIZE)) |
1453 | return -EINVAL; | 1447 | return -EINVAL; |
1454 | if (len && ooblen && | 1448 | if (len && ooblen && |
1455 | (len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta)) | 1449 | (len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta)) |
1456 | return -EINVAL; | 1450 | return -EINVAL; |
1457 | if (ofs + len > mtd->size) | 1451 | if (ofs + len > mtd->size) |
1458 | return -EINVAL; | 1452 | return -EINVAL; |
1459 | 1453 | ||
1460 | ops->oobretlen = 0; | 1454 | ops->oobretlen = 0; |
1461 | ops->retlen = 0; | 1455 | ops->retlen = 0; |
1462 | ret = 0; | 1456 | ret = 0; |
1463 | if (len == 0 && ooblen == 0) | 1457 | if (len == 0 && ooblen == 0) |
1464 | return -EINVAL; | 1458 | return -EINVAL; |
1465 | if (len == 0 && ooblen > 0) | 1459 | if (len == 0 && ooblen > 0) |
1466 | return doc_backup_oob(docg3, ofs, ops); | 1460 | return doc_backup_oob(docg3, ofs, ops); |
1467 | 1461 | ||
1468 | autoecc = doc_guess_autoecc(ops); | 1462 | autoecc = doc_guess_autoecc(ops); |
1469 | if (autoecc < 0) | 1463 | if (autoecc < 0) |
1470 | return autoecc; | 1464 | return autoecc; |
1471 | 1465 | ||
1472 | mutex_lock(&docg3->cascade->lock); | 1466 | mutex_lock(&docg3->cascade->lock); |
1473 | while (!ret && len > 0) { | 1467 | while (!ret && len > 0) { |
1474 | memset(oob, 0, sizeof(oob)); | 1468 | memset(oob, 0, sizeof(oob)); |
1475 | if (ofs == docg3->oob_write_ofs) | 1469 | if (ofs == docg3->oob_write_ofs) |
1476 | memcpy(oob, docg3->oob_write_buf, DOC_LAYOUT_OOB_SIZE); | 1470 | memcpy(oob, docg3->oob_write_buf, DOC_LAYOUT_OOB_SIZE); |
1477 | else if (ooblen > 0 && ops->mode == MTD_OPS_AUTO_OOB) | 1471 | else if (ooblen > 0 && ops->mode == MTD_OPS_AUTO_OOB) |
1478 | doc_fill_autooob(oob, oobbuf); | 1472 | doc_fill_autooob(oob, oobbuf); |
1479 | else if (ooblen > 0) | 1473 | else if (ooblen > 0) |
1480 | memcpy(oob, oobbuf, DOC_LAYOUT_OOB_SIZE); | 1474 | memcpy(oob, oobbuf, DOC_LAYOUT_OOB_SIZE); |
1481 | ret = doc_write_page(docg3, ofs, buf, oob, autoecc); | 1475 | ret = doc_write_page(docg3, ofs, buf, oob, autoecc); |
1482 | 1476 | ||
1483 | ofs += DOC_LAYOUT_PAGE_SIZE; | 1477 | ofs += DOC_LAYOUT_PAGE_SIZE; |
1484 | len -= DOC_LAYOUT_PAGE_SIZE; | 1478 | len -= DOC_LAYOUT_PAGE_SIZE; |
1485 | buf += DOC_LAYOUT_PAGE_SIZE; | 1479 | buf += DOC_LAYOUT_PAGE_SIZE; |
1486 | if (ooblen) { | 1480 | if (ooblen) { |
1487 | oobbuf += oobdelta; | 1481 | oobbuf += oobdelta; |
1488 | ooblen -= oobdelta; | 1482 | ooblen -= oobdelta; |
1489 | ops->oobretlen += oobdelta; | 1483 | ops->oobretlen += oobdelta; |
1490 | } | 1484 | } |
1491 | ops->retlen += DOC_LAYOUT_PAGE_SIZE; | 1485 | ops->retlen += DOC_LAYOUT_PAGE_SIZE; |
1492 | } | 1486 | } |
1493 | 1487 | ||
1494 | doc_set_device_id(docg3, 0); | 1488 | doc_set_device_id(docg3, 0); |
1495 | mutex_unlock(&docg3->cascade->lock); | 1489 | mutex_unlock(&docg3->cascade->lock); |
1496 | return ret; | 1490 | return ret; |
1497 | } | 1491 | } |
1498 | 1492 | ||
1499 | /** | 1493 | /** |
1500 | * doc_write - Write a buffer to the chip | 1494 | * doc_write - Write a buffer to the chip |
1501 | * @mtd: the device | 1495 | * @mtd: the device |
1502 | * @to: the offset from first block and first page, in bytes, aligned on page | 1496 | * @to: the offset from first block and first page, in bytes, aligned on page |
1503 | * size | 1497 | * size |
1504 | * @len: the number of bytes to write (must be a full page size, ie. 512) | 1498 | * @len: the number of bytes to write (must be a full page size, ie. 512) |
1505 | * @retlen: the number of bytes actually written (0 or 512) | 1499 | * @retlen: the number of bytes actually written (0 or 512) |
1506 | * @buf: the buffer to get bytes from | 1500 | * @buf: the buffer to get bytes from |
1507 | * | 1501 | * |
1508 | * Writes data to the chip. | 1502 | * Writes data to the chip. |
1509 | * | 1503 | * |
1510 | * Returns 0 if write successful, -EIO if write error | 1504 | * Returns 0 if write successful, -EIO if write error |
1511 | */ | 1505 | */ |
1512 | static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, | 1506 | static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, |
1513 | size_t *retlen, const u_char *buf) | 1507 | size_t *retlen, const u_char *buf) |
1514 | { | 1508 | { |
1515 | struct docg3 *docg3 = mtd->priv; | 1509 | struct docg3 *docg3 = mtd->priv; |
1516 | int ret; | 1510 | int ret; |
1517 | struct mtd_oob_ops ops; | 1511 | struct mtd_oob_ops ops; |
1518 | 1512 | ||
1519 | doc_dbg("doc_write(to=%lld, len=%zu)\n", to, len); | 1513 | doc_dbg("doc_write(to=%lld, len=%zu)\n", to, len); |
1520 | ops.datbuf = (char *)buf; | 1514 | ops.datbuf = (char *)buf; |
1521 | ops.len = len; | 1515 | ops.len = len; |
1522 | ops.mode = MTD_OPS_PLACE_OOB; | 1516 | ops.mode = MTD_OPS_PLACE_OOB; |
1523 | ops.oobbuf = NULL; | 1517 | ops.oobbuf = NULL; |
1524 | ops.ooblen = 0; | 1518 | ops.ooblen = 0; |
1525 | ops.ooboffs = 0; | 1519 | ops.ooboffs = 0; |
1526 | 1520 | ||
1527 | ret = doc_write_oob(mtd, to, &ops); | 1521 | ret = doc_write_oob(mtd, to, &ops); |
1528 | *retlen = ops.retlen; | 1522 | *retlen = ops.retlen; |
1529 | return ret; | 1523 | return ret; |
1530 | } | 1524 | } |
1531 | 1525 | ||
1532 | static struct docg3 *sysfs_dev2docg3(struct device *dev, | 1526 | static struct docg3 *sysfs_dev2docg3(struct device *dev, |
1533 | struct device_attribute *attr) | 1527 | struct device_attribute *attr) |
1534 | { | 1528 | { |
1535 | int floor; | 1529 | int floor; |
1536 | struct platform_device *pdev = to_platform_device(dev); | 1530 | struct platform_device *pdev = to_platform_device(dev); |
1537 | struct mtd_info **docg3_floors = platform_get_drvdata(pdev); | 1531 | struct mtd_info **docg3_floors = platform_get_drvdata(pdev); |
1538 | 1532 | ||
1539 | floor = attr->attr.name[1] - '0'; | 1533 | floor = attr->attr.name[1] - '0'; |
1540 | if (floor < 0 || floor >= DOC_MAX_NBFLOORS) | 1534 | if (floor < 0 || floor >= DOC_MAX_NBFLOORS) |
1541 | return NULL; | 1535 | return NULL; |
1542 | else | 1536 | else |
1543 | return docg3_floors[floor]->priv; | 1537 | return docg3_floors[floor]->priv; |
1544 | } | 1538 | } |
1545 | 1539 | ||
1546 | static ssize_t dps0_is_key_locked(struct device *dev, | 1540 | static ssize_t dps0_is_key_locked(struct device *dev, |
1547 | struct device_attribute *attr, char *buf) | 1541 | struct device_attribute *attr, char *buf) |
1548 | { | 1542 | { |
1549 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); | 1543 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); |
1550 | int dps0; | 1544 | int dps0; |
1551 | 1545 | ||
1552 | mutex_lock(&docg3->cascade->lock); | 1546 | mutex_lock(&docg3->cascade->lock); |
1553 | doc_set_device_id(docg3, docg3->device_id); | 1547 | doc_set_device_id(docg3, docg3->device_id); |
1554 | dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); | 1548 | dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); |
1555 | doc_set_device_id(docg3, 0); | 1549 | doc_set_device_id(docg3, 0); |
1556 | mutex_unlock(&docg3->cascade->lock); | 1550 | mutex_unlock(&docg3->cascade->lock); |
1557 | 1551 | ||
1558 | return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK)); | 1552 | return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK)); |
1559 | } | 1553 | } |
1560 | 1554 | ||
1561 | static ssize_t dps1_is_key_locked(struct device *dev, | 1555 | static ssize_t dps1_is_key_locked(struct device *dev, |
1562 | struct device_attribute *attr, char *buf) | 1556 | struct device_attribute *attr, char *buf) |
1563 | { | 1557 | { |
1564 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); | 1558 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); |
1565 | int dps1; | 1559 | int dps1; |
1566 | 1560 | ||
1567 | mutex_lock(&docg3->cascade->lock); | 1561 | mutex_lock(&docg3->cascade->lock); |
1568 | doc_set_device_id(docg3, docg3->device_id); | 1562 | doc_set_device_id(docg3, docg3->device_id); |
1569 | dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); | 1563 | dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); |
1570 | doc_set_device_id(docg3, 0); | 1564 | doc_set_device_id(docg3, 0); |
1571 | mutex_unlock(&docg3->cascade->lock); | 1565 | mutex_unlock(&docg3->cascade->lock); |
1572 | 1566 | ||
1573 | return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK)); | 1567 | return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK)); |
1574 | } | 1568 | } |
1575 | 1569 | ||
1576 | static ssize_t dps0_insert_key(struct device *dev, | 1570 | static ssize_t dps0_insert_key(struct device *dev, |
1577 | struct device_attribute *attr, | 1571 | struct device_attribute *attr, |
1578 | const char *buf, size_t count) | 1572 | const char *buf, size_t count) |
1579 | { | 1573 | { |
1580 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); | 1574 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); |
1581 | int i; | 1575 | int i; |
1582 | 1576 | ||
1583 | if (count != DOC_LAYOUT_DPS_KEY_LENGTH) | 1577 | if (count != DOC_LAYOUT_DPS_KEY_LENGTH) |
1584 | return -EINVAL; | 1578 | return -EINVAL; |
1585 | 1579 | ||
1586 | mutex_lock(&docg3->cascade->lock); | 1580 | mutex_lock(&docg3->cascade->lock); |
1587 | doc_set_device_id(docg3, docg3->device_id); | 1581 | doc_set_device_id(docg3, docg3->device_id); |
1588 | for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) | 1582 | for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) |
1589 | doc_writeb(docg3, buf[i], DOC_DPS0_KEY); | 1583 | doc_writeb(docg3, buf[i], DOC_DPS0_KEY); |
1590 | doc_set_device_id(docg3, 0); | 1584 | doc_set_device_id(docg3, 0); |
1591 | mutex_unlock(&docg3->cascade->lock); | 1585 | mutex_unlock(&docg3->cascade->lock); |
1592 | return count; | 1586 | return count; |
1593 | } | 1587 | } |
1594 | 1588 | ||
1595 | static ssize_t dps1_insert_key(struct device *dev, | 1589 | static ssize_t dps1_insert_key(struct device *dev, |
1596 | struct device_attribute *attr, | 1590 | struct device_attribute *attr, |
1597 | const char *buf, size_t count) | 1591 | const char *buf, size_t count) |
1598 | { | 1592 | { |
1599 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); | 1593 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); |
1600 | int i; | 1594 | int i; |
1601 | 1595 | ||
1602 | if (count != DOC_LAYOUT_DPS_KEY_LENGTH) | 1596 | if (count != DOC_LAYOUT_DPS_KEY_LENGTH) |
1603 | return -EINVAL; | 1597 | return -EINVAL; |
1604 | 1598 | ||
1605 | mutex_lock(&docg3->cascade->lock); | 1599 | mutex_lock(&docg3->cascade->lock); |
1606 | doc_set_device_id(docg3, docg3->device_id); | 1600 | doc_set_device_id(docg3, docg3->device_id); |
1607 | for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) | 1601 | for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) |
1608 | doc_writeb(docg3, buf[i], DOC_DPS1_KEY); | 1602 | doc_writeb(docg3, buf[i], DOC_DPS1_KEY); |
1609 | doc_set_device_id(docg3, 0); | 1603 | doc_set_device_id(docg3, 0); |
1610 | mutex_unlock(&docg3->cascade->lock); | 1604 | mutex_unlock(&docg3->cascade->lock); |
1611 | return count; | 1605 | return count; |
1612 | } | 1606 | } |
1613 | 1607 | ||
1614 | #define FLOOR_SYSFS(id) { \ | 1608 | #define FLOOR_SYSFS(id) { \ |
1615 | __ATTR(f##id##_dps0_is_keylocked, S_IRUGO, dps0_is_key_locked, NULL), \ | 1609 | __ATTR(f##id##_dps0_is_keylocked, S_IRUGO, dps0_is_key_locked, NULL), \ |
1616 | __ATTR(f##id##_dps1_is_keylocked, S_IRUGO, dps1_is_key_locked, NULL), \ | 1610 | __ATTR(f##id##_dps1_is_keylocked, S_IRUGO, dps1_is_key_locked, NULL), \ |
1617 | __ATTR(f##id##_dps0_protection_key, S_IWUGO, NULL, dps0_insert_key), \ | 1611 | __ATTR(f##id##_dps0_protection_key, S_IWUGO, NULL, dps0_insert_key), \ |
1618 | __ATTR(f##id##_dps1_protection_key, S_IWUGO, NULL, dps1_insert_key), \ | 1612 | __ATTR(f##id##_dps1_protection_key, S_IWUGO, NULL, dps1_insert_key), \ |
1619 | } | 1613 | } |
1620 | 1614 | ||
1621 | static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = { | 1615 | static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = { |
1622 | FLOOR_SYSFS(0), FLOOR_SYSFS(1), FLOOR_SYSFS(2), FLOOR_SYSFS(3) | 1616 | FLOOR_SYSFS(0), FLOOR_SYSFS(1), FLOOR_SYSFS(2), FLOOR_SYSFS(3) |
1623 | }; | 1617 | }; |
1624 | 1618 | ||
1625 | static int doc_register_sysfs(struct platform_device *pdev, | 1619 | static int doc_register_sysfs(struct platform_device *pdev, |
1626 | struct docg3_cascade *cascade) | 1620 | struct docg3_cascade *cascade) |
1627 | { | 1621 | { |
1628 | int ret = 0, floor, i = 0; | 1622 | int ret = 0, floor, i = 0; |
1629 | struct device *dev = &pdev->dev; | 1623 | struct device *dev = &pdev->dev; |
1630 | 1624 | ||
1631 | for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS && | 1625 | for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS && |
1632 | cascade->floors[floor]; floor++) | 1626 | cascade->floors[floor]; floor++) |
1633 | for (i = 0; !ret && i < 4; i++) | 1627 | for (i = 0; !ret && i < 4; i++) |
1634 | ret = device_create_file(dev, &doc_sys_attrs[floor][i]); | 1628 | ret = device_create_file(dev, &doc_sys_attrs[floor][i]); |
1635 | if (!ret) | 1629 | if (!ret) |
1636 | return 0; | 1630 | return 0; |
1637 | do { | 1631 | do { |
1638 | while (--i >= 0) | 1632 | while (--i >= 0) |
1639 | device_remove_file(dev, &doc_sys_attrs[floor][i]); | 1633 | device_remove_file(dev, &doc_sys_attrs[floor][i]); |
1640 | i = 4; | 1634 | i = 4; |
1641 | } while (--floor >= 0); | 1635 | } while (--floor >= 0); |
1642 | return ret; | 1636 | return ret; |
1643 | } | 1637 | } |
1644 | 1638 | ||
1645 | static void doc_unregister_sysfs(struct platform_device *pdev, | 1639 | static void doc_unregister_sysfs(struct platform_device *pdev, |
1646 | struct docg3_cascade *cascade) | 1640 | struct docg3_cascade *cascade) |
1647 | { | 1641 | { |
1648 | struct device *dev = &pdev->dev; | 1642 | struct device *dev = &pdev->dev; |
1649 | int floor, i; | 1643 | int floor, i; |
1650 | 1644 | ||
1651 | for (floor = 0; floor < DOC_MAX_NBFLOORS && cascade->floors[floor]; | 1645 | for (floor = 0; floor < DOC_MAX_NBFLOORS && cascade->floors[floor]; |
1652 | floor++) | 1646 | floor++) |
1653 | for (i = 0; i < 4; i++) | 1647 | for (i = 0; i < 4; i++) |
1654 | device_remove_file(dev, &doc_sys_attrs[floor][i]); | 1648 | device_remove_file(dev, &doc_sys_attrs[floor][i]); |
1655 | } | 1649 | } |
1656 | 1650 | ||
1657 | /* | 1651 | /* |
1658 | * Debug sysfs entries | 1652 | * Debug sysfs entries |
1659 | */ | 1653 | */ |
1660 | static int dbg_flashctrl_show(struct seq_file *s, void *p) | 1654 | static int dbg_flashctrl_show(struct seq_file *s, void *p) |
1661 | { | 1655 | { |
1662 | struct docg3 *docg3 = (struct docg3 *)s->private; | 1656 | struct docg3 *docg3 = (struct docg3 *)s->private; |
1663 | 1657 | ||
1664 | int pos = 0; | 1658 | int pos = 0; |
1665 | u8 fctrl; | 1659 | u8 fctrl; |
1666 | 1660 | ||
1667 | mutex_lock(&docg3->cascade->lock); | 1661 | mutex_lock(&docg3->cascade->lock); |
1668 | fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); | 1662 | fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); |
1669 | mutex_unlock(&docg3->cascade->lock); | 1663 | mutex_unlock(&docg3->cascade->lock); |
1670 | 1664 | ||
1671 | pos += seq_printf(s, | 1665 | pos += seq_printf(s, |
1672 | "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n", | 1666 | "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n", |
1673 | fctrl, | 1667 | fctrl, |
1674 | fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-", | 1668 | fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-", |
1675 | fctrl & DOC_CTRL_CE ? "active" : "inactive", | 1669 | fctrl & DOC_CTRL_CE ? "active" : "inactive", |
1676 | fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-", | 1670 | fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-", |
1677 | fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-", | 1671 | fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-", |
1678 | fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready"); | 1672 | fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready"); |
1679 | return pos; | 1673 | return pos; |
1680 | } | 1674 | } |
1681 | DEBUGFS_RO_ATTR(flashcontrol, dbg_flashctrl_show); | 1675 | DEBUGFS_RO_ATTR(flashcontrol, dbg_flashctrl_show); |
1682 | 1676 | ||
1683 | static int dbg_asicmode_show(struct seq_file *s, void *p) | 1677 | static int dbg_asicmode_show(struct seq_file *s, void *p) |
1684 | { | 1678 | { |
1685 | struct docg3 *docg3 = (struct docg3 *)s->private; | 1679 | struct docg3 *docg3 = (struct docg3 *)s->private; |
1686 | 1680 | ||
1687 | int pos = 0, pctrl, mode; | 1681 | int pos = 0, pctrl, mode; |
1688 | 1682 | ||
1689 | mutex_lock(&docg3->cascade->lock); | 1683 | mutex_lock(&docg3->cascade->lock); |
1690 | pctrl = doc_register_readb(docg3, DOC_ASICMODE); | 1684 | pctrl = doc_register_readb(docg3, DOC_ASICMODE); |
1691 | mode = pctrl & 0x03; | 1685 | mode = pctrl & 0x03; |
1692 | mutex_unlock(&docg3->cascade->lock); | 1686 | mutex_unlock(&docg3->cascade->lock); |
1693 | 1687 | ||
1694 | pos += seq_printf(s, | 1688 | pos += seq_printf(s, |
1695 | "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (", | 1689 | "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (", |
1696 | pctrl, | 1690 | pctrl, |
1697 | pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0, | 1691 | pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0, |
1698 | pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0, | 1692 | pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0, |
1699 | pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0, | 1693 | pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0, |
1700 | pctrl & DOC_ASICMODE_MDWREN ? 1 : 0, | 1694 | pctrl & DOC_ASICMODE_MDWREN ? 1 : 0, |
1701 | pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0, | 1695 | pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0, |
1702 | mode >> 1, mode & 0x1); | 1696 | mode >> 1, mode & 0x1); |
1703 | 1697 | ||
1704 | switch (mode) { | 1698 | switch (mode) { |
1705 | case DOC_ASICMODE_RESET: | 1699 | case DOC_ASICMODE_RESET: |
1706 | pos += seq_printf(s, "reset"); | 1700 | pos += seq_printf(s, "reset"); |
1707 | break; | 1701 | break; |
1708 | case DOC_ASICMODE_NORMAL: | 1702 | case DOC_ASICMODE_NORMAL: |
1709 | pos += seq_printf(s, "normal"); | 1703 | pos += seq_printf(s, "normal"); |
1710 | break; | 1704 | break; |
1711 | case DOC_ASICMODE_POWERDOWN: | 1705 | case DOC_ASICMODE_POWERDOWN: |
1712 | pos += seq_printf(s, "powerdown"); | 1706 | pos += seq_printf(s, "powerdown"); |
1713 | break; | 1707 | break; |
1714 | } | 1708 | } |
1715 | pos += seq_printf(s, ")\n"); | 1709 | pos += seq_printf(s, ")\n"); |
1716 | return pos; | 1710 | return pos; |
1717 | } | 1711 | } |
1718 | DEBUGFS_RO_ATTR(asic_mode, dbg_asicmode_show); | 1712 | DEBUGFS_RO_ATTR(asic_mode, dbg_asicmode_show); |
1719 | 1713 | ||
1720 | static int dbg_device_id_show(struct seq_file *s, void *p) | 1714 | static int dbg_device_id_show(struct seq_file *s, void *p) |
1721 | { | 1715 | { |
1722 | struct docg3 *docg3 = (struct docg3 *)s->private; | 1716 | struct docg3 *docg3 = (struct docg3 *)s->private; |
1723 | int pos = 0; | 1717 | int pos = 0; |
1724 | int id; | 1718 | int id; |
1725 | 1719 | ||
1726 | mutex_lock(&docg3->cascade->lock); | 1720 | mutex_lock(&docg3->cascade->lock); |
1727 | id = doc_register_readb(docg3, DOC_DEVICESELECT); | 1721 | id = doc_register_readb(docg3, DOC_DEVICESELECT); |
1728 | mutex_unlock(&docg3->cascade->lock); | 1722 | mutex_unlock(&docg3->cascade->lock); |
1729 | 1723 | ||
1730 | pos += seq_printf(s, "DeviceId = %d\n", id); | 1724 | pos += seq_printf(s, "DeviceId = %d\n", id); |
1731 | return pos; | 1725 | return pos; |
1732 | } | 1726 | } |
1733 | DEBUGFS_RO_ATTR(device_id, dbg_device_id_show); | 1727 | DEBUGFS_RO_ATTR(device_id, dbg_device_id_show); |
1734 | 1728 | ||
1735 | static int dbg_protection_show(struct seq_file *s, void *p) | 1729 | static int dbg_protection_show(struct seq_file *s, void *p) |
1736 | { | 1730 | { |
1737 | struct docg3 *docg3 = (struct docg3 *)s->private; | 1731 | struct docg3 *docg3 = (struct docg3 *)s->private; |
1738 | int pos = 0; | 1732 | int pos = 0; |
1739 | int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high; | 1733 | int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high; |
1740 | 1734 | ||
1741 | mutex_lock(&docg3->cascade->lock); | 1735 | mutex_lock(&docg3->cascade->lock); |
1742 | protect = doc_register_readb(docg3, DOC_PROTECTION); | 1736 | protect = doc_register_readb(docg3, DOC_PROTECTION); |
1743 | dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); | 1737 | dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); |
1744 | dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW); | 1738 | dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW); |
1745 | dps0_high = doc_register_readw(docg3, DOC_DPS0_ADDRHIGH); | 1739 | dps0_high = doc_register_readw(docg3, DOC_DPS0_ADDRHIGH); |
1746 | dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); | 1740 | dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); |
1747 | dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW); | 1741 | dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW); |
1748 | dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH); | 1742 | dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH); |
1749 | mutex_unlock(&docg3->cascade->lock); | 1743 | mutex_unlock(&docg3->cascade->lock); |
1750 | 1744 | ||
1751 | pos += seq_printf(s, "Protection = 0x%02x (", | 1745 | pos += seq_printf(s, "Protection = 0x%02x (", |
1752 | protect); | 1746 | protect); |
1753 | if (protect & DOC_PROTECT_FOUNDRY_OTP_LOCK) | 1747 | if (protect & DOC_PROTECT_FOUNDRY_OTP_LOCK) |
1754 | pos += seq_printf(s, "FOUNDRY_OTP_LOCK,"); | 1748 | pos += seq_printf(s, "FOUNDRY_OTP_LOCK,"); |
1755 | if (protect & DOC_PROTECT_CUSTOMER_OTP_LOCK) | 1749 | if (protect & DOC_PROTECT_CUSTOMER_OTP_LOCK) |
1756 | pos += seq_printf(s, "CUSTOMER_OTP_LOCK,"); | 1750 | pos += seq_printf(s, "CUSTOMER_OTP_LOCK,"); |
1757 | if (protect & DOC_PROTECT_LOCK_INPUT) | 1751 | if (protect & DOC_PROTECT_LOCK_INPUT) |
1758 | pos += seq_printf(s, "LOCK_INPUT,"); | 1752 | pos += seq_printf(s, "LOCK_INPUT,"); |
1759 | if (protect & DOC_PROTECT_STICKY_LOCK) | 1753 | if (protect & DOC_PROTECT_STICKY_LOCK) |
1760 | pos += seq_printf(s, "STICKY_LOCK,"); | 1754 | pos += seq_printf(s, "STICKY_LOCK,"); |
1761 | if (protect & DOC_PROTECT_PROTECTION_ENABLED) | 1755 | if (protect & DOC_PROTECT_PROTECTION_ENABLED) |
1762 | pos += seq_printf(s, "PROTECTION ON,"); | 1756 | pos += seq_printf(s, "PROTECTION ON,"); |
1763 | if (protect & DOC_PROTECT_IPL_DOWNLOAD_LOCK) | 1757 | if (protect & DOC_PROTECT_IPL_DOWNLOAD_LOCK) |
1764 | pos += seq_printf(s, "IPL_DOWNLOAD_LOCK,"); | 1758 | pos += seq_printf(s, "IPL_DOWNLOAD_LOCK,"); |
1765 | if (protect & DOC_PROTECT_PROTECTION_ERROR) | 1759 | if (protect & DOC_PROTECT_PROTECTION_ERROR) |
1766 | pos += seq_printf(s, "PROTECT_ERR,"); | 1760 | pos += seq_printf(s, "PROTECT_ERR,"); |
1767 | else | 1761 | else |
1768 | pos += seq_printf(s, "NO_PROTECT_ERR"); | 1762 | pos += seq_printf(s, "NO_PROTECT_ERR"); |
1769 | pos += seq_printf(s, ")\n"); | 1763 | pos += seq_printf(s, ")\n"); |
1770 | 1764 | ||
1771 | pos += seq_printf(s, "DPS0 = 0x%02x : " | 1765 | pos += seq_printf(s, "DPS0 = 0x%02x : " |
1772 | "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, " | 1766 | "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, " |
1773 | "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n", | 1767 | "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n", |
1774 | dps0, dps0_low, dps0_high, | 1768 | dps0, dps0_low, dps0_high, |
1775 | !!(dps0 & DOC_DPS_OTP_PROTECTED), | 1769 | !!(dps0 & DOC_DPS_OTP_PROTECTED), |
1776 | !!(dps0 & DOC_DPS_READ_PROTECTED), | 1770 | !!(dps0 & DOC_DPS_READ_PROTECTED), |
1777 | !!(dps0 & DOC_DPS_WRITE_PROTECTED), | 1771 | !!(dps0 & DOC_DPS_WRITE_PROTECTED), |
1778 | !!(dps0 & DOC_DPS_HW_LOCK_ENABLED), | 1772 | !!(dps0 & DOC_DPS_HW_LOCK_ENABLED), |
1779 | !!(dps0 & DOC_DPS_KEY_OK)); | 1773 | !!(dps0 & DOC_DPS_KEY_OK)); |
1780 | pos += seq_printf(s, "DPS1 = 0x%02x : " | 1774 | pos += seq_printf(s, "DPS1 = 0x%02x : " |
1781 | "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, " | 1775 | "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, " |
1782 | "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n", | 1776 | "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n", |
1783 | dps1, dps1_low, dps1_high, | 1777 | dps1, dps1_low, dps1_high, |
1784 | !!(dps1 & DOC_DPS_OTP_PROTECTED), | 1778 | !!(dps1 & DOC_DPS_OTP_PROTECTED), |
1785 | !!(dps1 & DOC_DPS_READ_PROTECTED), | 1779 | !!(dps1 & DOC_DPS_READ_PROTECTED), |
1786 | !!(dps1 & DOC_DPS_WRITE_PROTECTED), | 1780 | !!(dps1 & DOC_DPS_WRITE_PROTECTED), |
1787 | !!(dps1 & DOC_DPS_HW_LOCK_ENABLED), | 1781 | !!(dps1 & DOC_DPS_HW_LOCK_ENABLED), |
1788 | !!(dps1 & DOC_DPS_KEY_OK)); | 1782 | !!(dps1 & DOC_DPS_KEY_OK)); |
1789 | return pos; | 1783 | return pos; |
1790 | } | 1784 | } |
1791 | DEBUGFS_RO_ATTR(protection, dbg_protection_show); | 1785 | DEBUGFS_RO_ATTR(protection, dbg_protection_show); |
1792 | 1786 | ||
1793 | static int __init doc_dbg_register(struct docg3 *docg3) | 1787 | static int __init doc_dbg_register(struct docg3 *docg3) |
1794 | { | 1788 | { |
1795 | struct dentry *root, *entry; | 1789 | struct dentry *root, *entry; |
1796 | 1790 | ||
1797 | root = debugfs_create_dir("docg3", NULL); | 1791 | root = debugfs_create_dir("docg3", NULL); |
1798 | if (!root) | 1792 | if (!root) |
1799 | return -ENOMEM; | 1793 | return -ENOMEM; |
1800 | 1794 | ||
1801 | entry = debugfs_create_file("flashcontrol", S_IRUSR, root, docg3, | 1795 | entry = debugfs_create_file("flashcontrol", S_IRUSR, root, docg3, |
1802 | &flashcontrol_fops); | 1796 | &flashcontrol_fops); |
1803 | if (entry) | 1797 | if (entry) |
1804 | entry = debugfs_create_file("asic_mode", S_IRUSR, root, | 1798 | entry = debugfs_create_file("asic_mode", S_IRUSR, root, |
1805 | docg3, &asic_mode_fops); | 1799 | docg3, &asic_mode_fops); |
1806 | if (entry) | 1800 | if (entry) |
1807 | entry = debugfs_create_file("device_id", S_IRUSR, root, | 1801 | entry = debugfs_create_file("device_id", S_IRUSR, root, |
1808 | docg3, &device_id_fops); | 1802 | docg3, &device_id_fops); |
1809 | if (entry) | 1803 | if (entry) |
1810 | entry = debugfs_create_file("protection", S_IRUSR, root, | 1804 | entry = debugfs_create_file("protection", S_IRUSR, root, |
1811 | docg3, &protection_fops); | 1805 | docg3, &protection_fops); |
1812 | if (entry) { | 1806 | if (entry) { |
1813 | docg3->debugfs_root = root; | 1807 | docg3->debugfs_root = root; |
1814 | return 0; | 1808 | return 0; |
1815 | } else { | 1809 | } else { |
1816 | debugfs_remove_recursive(root); | 1810 | debugfs_remove_recursive(root); |
1817 | return -ENOMEM; | 1811 | return -ENOMEM; |
1818 | } | 1812 | } |
1819 | } | 1813 | } |
1820 | 1814 | ||
1821 | static void __exit doc_dbg_unregister(struct docg3 *docg3) | 1815 | static void __exit doc_dbg_unregister(struct docg3 *docg3) |
1822 | { | 1816 | { |
1823 | debugfs_remove_recursive(docg3->debugfs_root); | 1817 | debugfs_remove_recursive(docg3->debugfs_root); |
1824 | } | 1818 | } |
1825 | 1819 | ||
1826 | /** | 1820 | /** |
1827 | * doc_set_driver_info - Fill the mtd_info structure and docg3 structure | 1821 | * doc_set_driver_info - Fill the mtd_info structure and docg3 structure |
1828 | * @chip_id: The chip ID of the supported chip | 1822 | * @chip_id: The chip ID of the supported chip |
1829 | * @mtd: The structure to fill | 1823 | * @mtd: The structure to fill |
1830 | */ | 1824 | */ |
1831 | static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) | 1825 | static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) |
1832 | { | 1826 | { |
1833 | struct docg3 *docg3 = mtd->priv; | 1827 | struct docg3 *docg3 = mtd->priv; |
1834 | int cfg; | 1828 | int cfg; |
1835 | 1829 | ||
1836 | cfg = doc_register_readb(docg3, DOC_CONFIGURATION); | 1830 | cfg = doc_register_readb(docg3, DOC_CONFIGURATION); |
1837 | docg3->if_cfg = (cfg & DOC_CONF_IF_CFG ? 1 : 0); | 1831 | docg3->if_cfg = (cfg & DOC_CONF_IF_CFG ? 1 : 0); |
1838 | docg3->reliable = reliable_mode; | 1832 | docg3->reliable = reliable_mode; |
1839 | 1833 | ||
1840 | switch (chip_id) { | 1834 | switch (chip_id) { |
1841 | case DOC_CHIPID_G3: | 1835 | case DOC_CHIPID_G3: |
1842 | mtd->name = kasprintf(GFP_KERNEL, "docg3.%d", | 1836 | mtd->name = kasprintf(GFP_KERNEL, "docg3.%d", |
1843 | docg3->device_id); | 1837 | docg3->device_id); |
1844 | docg3->max_block = 2047; | 1838 | docg3->max_block = 2047; |
1845 | break; | 1839 | break; |
1846 | } | 1840 | } |
1847 | mtd->type = MTD_NANDFLASH; | 1841 | mtd->type = MTD_NANDFLASH; |
1848 | mtd->flags = MTD_CAP_NANDFLASH; | 1842 | mtd->flags = MTD_CAP_NANDFLASH; |
1849 | mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE; | 1843 | mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE; |
1850 | if (docg3->reliable == 2) | 1844 | if (docg3->reliable == 2) |
1851 | mtd->size /= 2; | 1845 | mtd->size /= 2; |
1852 | mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES; | 1846 | mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES; |
1853 | if (docg3->reliable == 2) | 1847 | if (docg3->reliable == 2) |
1854 | mtd->erasesize /= 2; | 1848 | mtd->erasesize /= 2; |
1855 | mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE; | 1849 | mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE; |
1856 | mtd->oobsize = DOC_LAYOUT_OOB_SIZE; | 1850 | mtd->oobsize = DOC_LAYOUT_OOB_SIZE; |
1857 | mtd->owner = THIS_MODULE; | 1851 | mtd->owner = THIS_MODULE; |
1858 | mtd->_erase = doc_erase; | 1852 | mtd->_erase = doc_erase; |
1859 | mtd->_read = doc_read; | 1853 | mtd->_read = doc_read; |
1860 | mtd->_write = doc_write; | 1854 | mtd->_write = doc_write; |
1861 | mtd->_read_oob = doc_read_oob; | 1855 | mtd->_read_oob = doc_read_oob; |
1862 | mtd->_write_oob = doc_write_oob; | 1856 | mtd->_write_oob = doc_write_oob; |
1863 | mtd->_block_isbad = doc_block_isbad; | 1857 | mtd->_block_isbad = doc_block_isbad; |
1864 | mtd->ecclayout = &docg3_oobinfo; | 1858 | mtd->ecclayout = &docg3_oobinfo; |
1865 | mtd->ecc_strength = DOC_ECC_BCH_T; | 1859 | mtd->ecc_strength = DOC_ECC_BCH_T; |
1866 | } | 1860 | } |
1867 | 1861 | ||
1868 | /** | 1862 | /** |
1869 | * doc_probe_device - Check if a device is available | 1863 | * doc_probe_device - Check if a device is available |
1870 | * @base: the io space where the device is probed | 1864 | * @base: the io space where the device is probed |
1871 | * @floor: the floor of the probed device | 1865 | * @floor: the floor of the probed device |
1872 | * @dev: the device | 1866 | * @dev: the device |
1873 | * @cascade: the cascade of chips this devices will belong to | 1867 | * @cascade: the cascade of chips this devices will belong to |
1874 | * | 1868 | * |
1875 | * Checks whether a device at the specified IO range, and floor is available. | 1869 | * Checks whether a device at the specified IO range, and floor is available. |
1876 | * | 1870 | * |
1877 | * Returns a mtd_info struct if there is a device, ENODEV if none found, ENOMEM | 1871 | * Returns a mtd_info struct if there is a device, ENODEV if none found, ENOMEM |
1878 | * if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is | 1872 | * if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is |
1879 | * launched. | 1873 | * launched. |
1880 | */ | 1874 | */ |
1881 | static struct mtd_info * __init | 1875 | static struct mtd_info * __init |
1882 | doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev) | 1876 | doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev) |
1883 | { | 1877 | { |
1884 | int ret, bbt_nbpages; | 1878 | int ret, bbt_nbpages; |
1885 | u16 chip_id, chip_id_inv; | 1879 | u16 chip_id, chip_id_inv; |
1886 | struct docg3 *docg3; | 1880 | struct docg3 *docg3; |
1887 | struct mtd_info *mtd; | 1881 | struct mtd_info *mtd; |
1888 | 1882 | ||
1889 | ret = -ENOMEM; | 1883 | ret = -ENOMEM; |
1890 | docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL); | 1884 | docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL); |
1891 | if (!docg3) | 1885 | if (!docg3) |
1892 | goto nomem1; | 1886 | goto nomem1; |
1893 | mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); | 1887 | mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); |
1894 | if (!mtd) | 1888 | if (!mtd) |
1895 | goto nomem2; | 1889 | goto nomem2; |
1896 | mtd->priv = docg3; | 1890 | mtd->priv = docg3; |
1897 | bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, | 1891 | bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, |
1898 | 8 * DOC_LAYOUT_PAGE_SIZE); | 1892 | 8 * DOC_LAYOUT_PAGE_SIZE); |
1899 | docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL); | 1893 | docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL); |
1900 | if (!docg3->bbt) | 1894 | if (!docg3->bbt) |
1901 | goto nomem3; | 1895 | goto nomem3; |
1902 | 1896 | ||
1903 | docg3->dev = dev; | 1897 | docg3->dev = dev; |
1904 | docg3->device_id = floor; | 1898 | docg3->device_id = floor; |
1905 | docg3->cascade = cascade; | 1899 | docg3->cascade = cascade; |
1906 | doc_set_device_id(docg3, docg3->device_id); | 1900 | doc_set_device_id(docg3, docg3->device_id); |
1907 | if (!floor) | 1901 | if (!floor) |
1908 | doc_set_asic_mode(docg3, DOC_ASICMODE_RESET); | 1902 | doc_set_asic_mode(docg3, DOC_ASICMODE_RESET); |
1909 | doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL); | 1903 | doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL); |
1910 | 1904 | ||
1911 | chip_id = doc_register_readw(docg3, DOC_CHIPID); | 1905 | chip_id = doc_register_readw(docg3, DOC_CHIPID); |
1912 | chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV); | 1906 | chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV); |
1913 | 1907 | ||
1914 | ret = 0; | 1908 | ret = 0; |
1915 | if (chip_id != (u16)(~chip_id_inv)) { | 1909 | if (chip_id != (u16)(~chip_id_inv)) { |
1916 | goto nomem3; | 1910 | goto nomem3; |
1917 | } | 1911 | } |
1918 | 1912 | ||
1919 | switch (chip_id) { | 1913 | switch (chip_id) { |
1920 | case DOC_CHIPID_G3: | 1914 | case DOC_CHIPID_G3: |
1921 | doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n", | 1915 | doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n", |
1922 | docg3->cascade->base, floor); | 1916 | docg3->cascade->base, floor); |
1923 | break; | 1917 | break; |
1924 | default: | 1918 | default: |
1925 | doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id); | 1919 | doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id); |
1926 | goto nomem3; | 1920 | goto nomem3; |
1927 | } | 1921 | } |
1928 | 1922 | ||
1929 | doc_set_driver_info(chip_id, mtd); | 1923 | doc_set_driver_info(chip_id, mtd); |
1930 | 1924 | ||
1931 | doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ); | 1925 | doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ); |
1932 | doc_reload_bbt(docg3); | 1926 | doc_reload_bbt(docg3); |
1933 | return mtd; | 1927 | return mtd; |
1934 | 1928 | ||
1935 | nomem3: | 1929 | nomem3: |
1936 | kfree(mtd); | 1930 | kfree(mtd); |
1937 | nomem2: | 1931 | nomem2: |
1938 | kfree(docg3); | 1932 | kfree(docg3); |
1939 | nomem1: | 1933 | nomem1: |
1940 | return ERR_PTR(ret); | 1934 | return ERR_PTR(ret); |
1941 | } | 1935 | } |
1942 | 1936 | ||
1943 | /** | 1937 | /** |
1944 | * doc_release_device - Release a docg3 floor | 1938 | * doc_release_device - Release a docg3 floor |
1945 | * @mtd: the device | 1939 | * @mtd: the device |
1946 | */ | 1940 | */ |
1947 | static void doc_release_device(struct mtd_info *mtd) | 1941 | static void doc_release_device(struct mtd_info *mtd) |
1948 | { | 1942 | { |
1949 | struct docg3 *docg3 = mtd->priv; | 1943 | struct docg3 *docg3 = mtd->priv; |
1950 | 1944 | ||
1951 | mtd_device_unregister(mtd); | 1945 | mtd_device_unregister(mtd); |
1952 | kfree(docg3->bbt); | 1946 | kfree(docg3->bbt); |
1953 | kfree(docg3); | 1947 | kfree(docg3); |
1954 | kfree(mtd->name); | 1948 | kfree(mtd->name); |
1955 | kfree(mtd); | 1949 | kfree(mtd); |
1956 | } | 1950 | } |
1957 | 1951 | ||
1958 | /** | 1952 | /** |
1959 | * docg3_resume - Awakens docg3 floor | 1953 | * docg3_resume - Awakens docg3 floor |
1960 | * @pdev: platfrom device | 1954 | * @pdev: platfrom device |
1961 | * | 1955 | * |
1962 | * Returns 0 (always successful) | 1956 | * Returns 0 (always successful) |
1963 | */ | 1957 | */ |
1964 | static int docg3_resume(struct platform_device *pdev) | 1958 | static int docg3_resume(struct platform_device *pdev) |
1965 | { | 1959 | { |
1966 | int i; | 1960 | int i; |
1967 | struct docg3_cascade *cascade; | 1961 | struct docg3_cascade *cascade; |
1968 | struct mtd_info **docg3_floors, *mtd; | 1962 | struct mtd_info **docg3_floors, *mtd; |
1969 | struct docg3 *docg3; | 1963 | struct docg3 *docg3; |
1970 | 1964 | ||
1971 | cascade = platform_get_drvdata(pdev); | 1965 | cascade = platform_get_drvdata(pdev); |
1972 | docg3_floors = cascade->floors; | 1966 | docg3_floors = cascade->floors; |
1973 | mtd = docg3_floors[0]; | 1967 | mtd = docg3_floors[0]; |
1974 | docg3 = mtd->priv; | 1968 | docg3 = mtd->priv; |
1975 | 1969 | ||
1976 | doc_dbg("docg3_resume()\n"); | 1970 | doc_dbg("docg3_resume()\n"); |
1977 | for (i = 0; i < 12; i++) | 1971 | for (i = 0; i < 12; i++) |
1978 | doc_readb(docg3, DOC_IOSPACE_IPL); | 1972 | doc_readb(docg3, DOC_IOSPACE_IPL); |
1979 | return 0; | 1973 | return 0; |
1980 | } | 1974 | } |
1981 | 1975 | ||
1982 | /** | 1976 | /** |
1983 | * docg3_suspend - Put in low power mode the docg3 floor | 1977 | * docg3_suspend - Put in low power mode the docg3 floor |
1984 | * @pdev: platform device | 1978 | * @pdev: platform device |
1985 | * @state: power state | 1979 | * @state: power state |
1986 | * | 1980 | * |
1987 | * Shuts off most of docg3 circuitery to lower power consumption. | 1981 | * Shuts off most of docg3 circuitery to lower power consumption. |
1988 | * | 1982 | * |
1989 | * Returns 0 if suspend succeeded, -EIO if chip refused suspend | 1983 | * Returns 0 if suspend succeeded, -EIO if chip refused suspend |
1990 | */ | 1984 | */ |
1991 | static int docg3_suspend(struct platform_device *pdev, pm_message_t state) | 1985 | static int docg3_suspend(struct platform_device *pdev, pm_message_t state) |
1992 | { | 1986 | { |
1993 | int floor, i; | 1987 | int floor, i; |
1994 | struct docg3_cascade *cascade; | 1988 | struct docg3_cascade *cascade; |
1995 | struct mtd_info **docg3_floors, *mtd; | 1989 | struct mtd_info **docg3_floors, *mtd; |
1996 | struct docg3 *docg3; | 1990 | struct docg3 *docg3; |
1997 | u8 ctrl, pwr_down; | 1991 | u8 ctrl, pwr_down; |
1998 | 1992 | ||
1999 | cascade = platform_get_drvdata(pdev); | 1993 | cascade = platform_get_drvdata(pdev); |
2000 | docg3_floors = cascade->floors; | 1994 | docg3_floors = cascade->floors; |
2001 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { | 1995 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { |
2002 | mtd = docg3_floors[floor]; | 1996 | mtd = docg3_floors[floor]; |
2003 | if (!mtd) | 1997 | if (!mtd) |
2004 | continue; | 1998 | continue; |
2005 | docg3 = mtd->priv; | 1999 | docg3 = mtd->priv; |
2006 | 2000 | ||
2007 | doc_writeb(docg3, floor, DOC_DEVICESELECT); | 2001 | doc_writeb(docg3, floor, DOC_DEVICESELECT); |
2008 | ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); | 2002 | ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); |
2009 | ctrl &= ~DOC_CTRL_VIOLATION & ~DOC_CTRL_CE; | 2003 | ctrl &= ~DOC_CTRL_VIOLATION & ~DOC_CTRL_CE; |
2010 | doc_writeb(docg3, ctrl, DOC_FLASHCONTROL); | 2004 | doc_writeb(docg3, ctrl, DOC_FLASHCONTROL); |
2011 | 2005 | ||
2012 | for (i = 0; i < 10; i++) { | 2006 | for (i = 0; i < 10; i++) { |
2013 | usleep_range(3000, 4000); | 2007 | usleep_range(3000, 4000); |
2014 | pwr_down = doc_register_readb(docg3, DOC_POWERMODE); | 2008 | pwr_down = doc_register_readb(docg3, DOC_POWERMODE); |
2015 | if (pwr_down & DOC_POWERDOWN_READY) | 2009 | if (pwr_down & DOC_POWERDOWN_READY) |
2016 | break; | 2010 | break; |
2017 | } | 2011 | } |
2018 | if (pwr_down & DOC_POWERDOWN_READY) { | 2012 | if (pwr_down & DOC_POWERDOWN_READY) { |
2019 | doc_dbg("docg3_suspend(): floor %d powerdown ok\n", | 2013 | doc_dbg("docg3_suspend(): floor %d powerdown ok\n", |
2020 | floor); | 2014 | floor); |
2021 | } else { | 2015 | } else { |
2022 | doc_err("docg3_suspend(): floor %d powerdown failed\n", | 2016 | doc_err("docg3_suspend(): floor %d powerdown failed\n", |
2023 | floor); | 2017 | floor); |
2024 | return -EIO; | 2018 | return -EIO; |
2025 | } | 2019 | } |
2026 | } | 2020 | } |
2027 | 2021 | ||
2028 | mtd = docg3_floors[0]; | 2022 | mtd = docg3_floors[0]; |
2029 | docg3 = mtd->priv; | 2023 | docg3 = mtd->priv; |
2030 | doc_set_asic_mode(docg3, DOC_ASICMODE_POWERDOWN); | 2024 | doc_set_asic_mode(docg3, DOC_ASICMODE_POWERDOWN); |
2031 | return 0; | 2025 | return 0; |
2032 | } | 2026 | } |
2033 | 2027 | ||
2034 | /** | 2028 | /** |
2035 | * doc_probe - Probe the IO space for a DiskOnChip G3 chip | 2029 | * doc_probe - Probe the IO space for a DiskOnChip G3 chip |
2036 | * @pdev: platform device | 2030 | * @pdev: platform device |
2037 | * | 2031 | * |
2038 | * Probes for a G3 chip at the specified IO space in the platform data | 2032 | * Probes for a G3 chip at the specified IO space in the platform data |
2039 | * ressources. The floor 0 must be available. | 2033 | * ressources. The floor 0 must be available. |
2040 | * | 2034 | * |
2041 | * Returns 0 on success, -ENOMEM, -ENXIO on error | 2035 | * Returns 0 on success, -ENOMEM, -ENXIO on error |
2042 | */ | 2036 | */ |
2043 | static int __init docg3_probe(struct platform_device *pdev) | 2037 | static int __init docg3_probe(struct platform_device *pdev) |
2044 | { | 2038 | { |
2045 | struct device *dev = &pdev->dev; | 2039 | struct device *dev = &pdev->dev; |
2046 | struct mtd_info *mtd; | 2040 | struct mtd_info *mtd; |
2047 | struct resource *ress; | 2041 | struct resource *ress; |
2048 | void __iomem *base; | 2042 | void __iomem *base; |
2049 | int ret, floor, found = 0; | 2043 | int ret, floor, found = 0; |
2050 | struct docg3_cascade *cascade; | 2044 | struct docg3_cascade *cascade; |
2051 | 2045 | ||
2052 | ret = -ENXIO; | 2046 | ret = -ENXIO; |
2053 | ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 2047 | ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
2054 | if (!ress) { | 2048 | if (!ress) { |
2055 | dev_err(dev, "No I/O memory resource defined\n"); | 2049 | dev_err(dev, "No I/O memory resource defined\n"); |
2056 | goto noress; | 2050 | goto noress; |
2057 | } | 2051 | } |
2058 | base = ioremap(ress->start, DOC_IOSPACE_SIZE); | 2052 | base = ioremap(ress->start, DOC_IOSPACE_SIZE); |
2059 | 2053 | ||
2060 | ret = -ENOMEM; | 2054 | ret = -ENOMEM; |
2061 | cascade = kzalloc(sizeof(*cascade) * DOC_MAX_NBFLOORS, | 2055 | cascade = kzalloc(sizeof(*cascade) * DOC_MAX_NBFLOORS, |
2062 | GFP_KERNEL); | 2056 | GFP_KERNEL); |
2063 | if (!cascade) | 2057 | if (!cascade) |
2064 | goto nomem1; | 2058 | goto nomem1; |
2065 | cascade->base = base; | 2059 | cascade->base = base; |
2066 | mutex_init(&cascade->lock); | 2060 | mutex_init(&cascade->lock); |
2067 | cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T, | 2061 | cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T, |
2068 | DOC_ECC_BCH_PRIMPOLY); | 2062 | DOC_ECC_BCH_PRIMPOLY); |
2069 | if (!cascade->bch) | 2063 | if (!cascade->bch) |
2070 | goto nomem2; | 2064 | goto nomem2; |
2071 | 2065 | ||
2072 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { | 2066 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { |
2073 | mtd = doc_probe_device(cascade, floor, dev); | 2067 | mtd = doc_probe_device(cascade, floor, dev); |
2074 | if (IS_ERR(mtd)) { | 2068 | if (IS_ERR(mtd)) { |
2075 | ret = PTR_ERR(mtd); | 2069 | ret = PTR_ERR(mtd); |
2076 | goto err_probe; | 2070 | goto err_probe; |
2077 | } | 2071 | } |
2078 | if (!mtd) { | 2072 | if (!mtd) { |
2079 | if (floor == 0) | 2073 | if (floor == 0) |
2080 | goto notfound; | 2074 | goto notfound; |
2081 | else | 2075 | else |
2082 | continue; | 2076 | continue; |
2083 | } | 2077 | } |
2084 | cascade->floors[floor] = mtd; | 2078 | cascade->floors[floor] = mtd; |
2085 | ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL, | 2079 | ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL, |
2086 | 0); | 2080 | 0); |
2087 | if (ret) | 2081 | if (ret) |
2088 | goto err_probe; | 2082 | goto err_probe; |
2089 | found++; | 2083 | found++; |
2090 | } | 2084 | } |
2091 | 2085 | ||
2092 | ret = doc_register_sysfs(pdev, cascade); | 2086 | ret = doc_register_sysfs(pdev, cascade); |
2093 | if (ret) | 2087 | if (ret) |
2094 | goto err_probe; | 2088 | goto err_probe; |
2095 | if (!found) | 2089 | if (!found) |
2096 | goto notfound; | 2090 | goto notfound; |
2097 | 2091 | ||
2098 | platform_set_drvdata(pdev, cascade); | 2092 | platform_set_drvdata(pdev, cascade); |
2099 | doc_dbg_register(cascade->floors[0]->priv); | 2093 | doc_dbg_register(cascade->floors[0]->priv); |
2100 | return 0; | 2094 | return 0; |
2101 | 2095 | ||
2102 | notfound: | 2096 | notfound: |
2103 | ret = -ENODEV; | 2097 | ret = -ENODEV; |
2104 | dev_info(dev, "No supported DiskOnChip found\n"); | 2098 | dev_info(dev, "No supported DiskOnChip found\n"); |
2105 | err_probe: | 2099 | err_probe: |
2106 | kfree(cascade->bch); | 2100 | kfree(cascade->bch); |
2107 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) | 2101 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) |
2108 | if (cascade->floors[floor]) | 2102 | if (cascade->floors[floor]) |
2109 | doc_release_device(cascade->floors[floor]); | 2103 | doc_release_device(cascade->floors[floor]); |
2110 | nomem2: | 2104 | nomem2: |
2111 | kfree(cascade); | 2105 | kfree(cascade); |
2112 | nomem1: | 2106 | nomem1: |
2113 | iounmap(base); | 2107 | iounmap(base); |
2114 | noress: | 2108 | noress: |
2115 | return ret; | 2109 | return ret; |
2116 | } | 2110 | } |
2117 | 2111 | ||
2118 | /** | 2112 | /** |
2119 | * docg3_release - Release the driver | 2113 | * docg3_release - Release the driver |
2120 | * @pdev: the platform device | 2114 | * @pdev: the platform device |
2121 | * | 2115 | * |
2122 | * Returns 0 | 2116 | * Returns 0 |
2123 | */ | 2117 | */ |
2124 | static int __exit docg3_release(struct platform_device *pdev) | 2118 | static int __exit docg3_release(struct platform_device *pdev) |
2125 | { | 2119 | { |
2126 | struct docg3_cascade *cascade = platform_get_drvdata(pdev); | 2120 | struct docg3_cascade *cascade = platform_get_drvdata(pdev); |
2127 | struct docg3 *docg3 = cascade->floors[0]->priv; | 2121 | struct docg3 *docg3 = cascade->floors[0]->priv; |
2128 | void __iomem *base = cascade->base; | 2122 | void __iomem *base = cascade->base; |
2129 | int floor; | 2123 | int floor; |
2130 | 2124 | ||
2131 | doc_unregister_sysfs(pdev, cascade); | 2125 | doc_unregister_sysfs(pdev, cascade); |
2132 | doc_dbg_unregister(docg3); | 2126 | doc_dbg_unregister(docg3); |
2133 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) | 2127 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) |
2134 | if (cascade->floors[floor]) | 2128 | if (cascade->floors[floor]) |
2135 | doc_release_device(cascade->floors[floor]); | 2129 | doc_release_device(cascade->floors[floor]); |
2136 | 2130 | ||
2137 | free_bch(docg3->cascade->bch); | 2131 | free_bch(docg3->cascade->bch); |
2138 | kfree(cascade); | 2132 | kfree(cascade); |
2139 | iounmap(base); | 2133 | iounmap(base); |
2140 | return 0; | 2134 | return 0; |
2141 | } | 2135 | } |
2142 | 2136 | ||
2143 | static struct platform_driver g3_driver = { | 2137 | static struct platform_driver g3_driver = { |
2144 | .driver = { | 2138 | .driver = { |
2145 | .name = "docg3", | 2139 | .name = "docg3", |
2146 | .owner = THIS_MODULE, | 2140 | .owner = THIS_MODULE, |
2147 | }, | 2141 | }, |
2148 | .suspend = docg3_suspend, | 2142 | .suspend = docg3_suspend, |
2149 | .resume = docg3_resume, | 2143 | .resume = docg3_resume, |
2150 | .remove = __exit_p(docg3_release), | 2144 | .remove = __exit_p(docg3_release), |
2151 | }; | 2145 | }; |
2152 | 2146 | ||
2153 | static int __init docg3_init(void) | 2147 | static int __init docg3_init(void) |
2154 | { | 2148 | { |
2155 | return platform_driver_probe(&g3_driver, docg3_probe); | 2149 | return platform_driver_probe(&g3_driver, docg3_probe); |
2156 | } | 2150 | } |
2157 | module_init(docg3_init); | 2151 | module_init(docg3_init); |
2158 | 2152 | ||
2159 | 2153 | ||
2160 | static void __exit docg3_exit(void) | 2154 | static void __exit docg3_exit(void) |
2161 | { | 2155 | { |
2162 | platform_driver_unregister(&g3_driver); | 2156 | platform_driver_unregister(&g3_driver); |
2163 | } | 2157 | } |
2164 | module_exit(docg3_exit); | 2158 | module_exit(docg3_exit); |
2165 | 2159 | ||
2166 | MODULE_LICENSE("GPL"); | 2160 | MODULE_LICENSE("GPL"); |
2167 | MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>"); | 2161 | MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>"); |
2168 | MODULE_DESCRIPTION("MTD driver for DiskOnChip G3"); | 2162 | MODULE_DESCRIPTION("MTD driver for DiskOnChip G3"); |
2169 | 2163 |
drivers/mtd/nand/bcm_umi_bch.c
1 | /***************************************************************************** | 1 | /***************************************************************************** |
2 | * Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. | 2 | * Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. |
3 | * | 3 | * |
4 | * Unless you and Broadcom execute a separate written software license | 4 | * Unless you and Broadcom execute a separate written software license |
5 | * agreement governing use of this software, this software is licensed to you | 5 | * agreement governing use of this software, this software is licensed to you |
6 | * under the terms of the GNU General Public License version 2, available at | 6 | * under the terms of the GNU General Public License version 2, available at |
7 | * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). | 7 | * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). |
8 | * | 8 | * |
9 | * Notwithstanding the above, under no circumstances may you combine this | 9 | * Notwithstanding the above, under no circumstances may you combine this |
10 | * software in any way with any other Broadcom software provided under a | 10 | * software in any way with any other Broadcom software provided under a |
11 | * license other than the GPL, without Broadcom's express prior written | 11 | * license other than the GPL, without Broadcom's express prior written |
12 | * consent. | 12 | * consent. |
13 | *****************************************************************************/ | 13 | *****************************************************************************/ |
14 | 14 | ||
15 | /* ---- Include Files ---------------------------------------------------- */ | 15 | /* ---- Include Files ---------------------------------------------------- */ |
16 | #include "nand_bcm_umi.h" | 16 | #include "nand_bcm_umi.h" |
17 | 17 | ||
18 | /* ---- External Variable Declarations ----------------------------------- */ | 18 | /* ---- External Variable Declarations ----------------------------------- */ |
19 | /* ---- External Function Prototypes ------------------------------------- */ | 19 | /* ---- External Function Prototypes ------------------------------------- */ |
20 | /* ---- Public Variables ------------------------------------------------- */ | 20 | /* ---- Public Variables ------------------------------------------------- */ |
21 | /* ---- Private Constants and Types -------------------------------------- */ | 21 | /* ---- Private Constants and Types -------------------------------------- */ |
22 | 22 | ||
23 | /* ---- Private Function Prototypes -------------------------------------- */ | 23 | /* ---- Private Function Prototypes -------------------------------------- */ |
24 | static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, | 24 | static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, |
25 | struct nand_chip *chip, uint8_t *buf, int oob_required, int page); | 25 | struct nand_chip *chip, uint8_t *buf, int oob_required, int page); |
26 | static int bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, | 26 | static int bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, |
27 | struct nand_chip *chip, const uint8_t *buf, int oob_required); | 27 | struct nand_chip *chip, const uint8_t *buf, int oob_required); |
28 | 28 | ||
29 | /* ---- Private Variables ------------------------------------------------ */ | 29 | /* ---- Private Variables ------------------------------------------------ */ |
30 | 30 | ||
31 | /* | 31 | /* |
32 | ** nand_hw_eccoob | 32 | ** nand_hw_eccoob |
33 | ** New oob placement block for use with hardware ecc generation. | 33 | ** New oob placement block for use with hardware ecc generation. |
34 | */ | 34 | */ |
35 | static struct nand_ecclayout nand_hw_eccoob_512 = { | 35 | static struct nand_ecclayout nand_hw_eccoob_512 = { |
36 | /* Reserve 5 for BI indicator */ | 36 | /* Reserve 5 for BI indicator */ |
37 | .oobfree = { | 37 | .oobfree = { |
38 | #if (NAND_ECC_NUM_BYTES > 3) | 38 | #if (NAND_ECC_NUM_BYTES > 3) |
39 | {.offset = 0, .length = 2} | 39 | {.offset = 0, .length = 2} |
40 | #else | 40 | #else |
41 | {.offset = 0, .length = 5}, | 41 | {.offset = 0, .length = 5}, |
42 | {.offset = 6, .length = 7} | 42 | {.offset = 6, .length = 7} |
43 | #endif | 43 | #endif |
44 | } | 44 | } |
45 | }; | 45 | }; |
46 | 46 | ||
47 | /* | 47 | /* |
48 | ** We treat the OOB for a 2K page as if it were 4 512 byte oobs, | 48 | ** We treat the OOB for a 2K page as if it were 4 512 byte oobs, |
49 | ** except the BI is at byte 0. | 49 | ** except the BI is at byte 0. |
50 | */ | 50 | */ |
51 | static struct nand_ecclayout nand_hw_eccoob_2048 = { | 51 | static struct nand_ecclayout nand_hw_eccoob_2048 = { |
52 | /* Reserve 0 as BI indicator */ | 52 | /* Reserve 0 as BI indicator */ |
53 | .oobfree = { | 53 | .oobfree = { |
54 | #if (NAND_ECC_NUM_BYTES > 10) | 54 | #if (NAND_ECC_NUM_BYTES > 10) |
55 | {.offset = 1, .length = 2}, | 55 | {.offset = 1, .length = 2}, |
56 | #elif (NAND_ECC_NUM_BYTES > 7) | 56 | #elif (NAND_ECC_NUM_BYTES > 7) |
57 | {.offset = 1, .length = 5}, | 57 | {.offset = 1, .length = 5}, |
58 | {.offset = 16, .length = 6}, | 58 | {.offset = 16, .length = 6}, |
59 | {.offset = 32, .length = 6}, | 59 | {.offset = 32, .length = 6}, |
60 | {.offset = 48, .length = 6} | 60 | {.offset = 48, .length = 6} |
61 | #else | 61 | #else |
62 | {.offset = 1, .length = 8}, | 62 | {.offset = 1, .length = 8}, |
63 | {.offset = 16, .length = 9}, | 63 | {.offset = 16, .length = 9}, |
64 | {.offset = 32, .length = 9}, | 64 | {.offset = 32, .length = 9}, |
65 | {.offset = 48, .length = 9} | 65 | {.offset = 48, .length = 9} |
66 | #endif | 66 | #endif |
67 | } | 67 | } |
68 | }; | 68 | }; |
69 | 69 | ||
70 | /* We treat the OOB for a 4K page as if it were 8 512 byte oobs, | 70 | /* We treat the OOB for a 4K page as if it were 8 512 byte oobs, |
71 | * except the BI is at byte 0. */ | 71 | * except the BI is at byte 0. */ |
72 | static struct nand_ecclayout nand_hw_eccoob_4096 = { | 72 | static struct nand_ecclayout nand_hw_eccoob_4096 = { |
73 | /* Reserve 0 as BI indicator */ | 73 | /* Reserve 0 as BI indicator */ |
74 | .oobfree = { | 74 | .oobfree = { |
75 | #if (NAND_ECC_NUM_BYTES > 10) | 75 | #if (NAND_ECC_NUM_BYTES > 10) |
76 | {.offset = 1, .length = 2}, | 76 | {.offset = 1, .length = 2}, |
77 | {.offset = 16, .length = 3}, | 77 | {.offset = 16, .length = 3}, |
78 | {.offset = 32, .length = 3}, | 78 | {.offset = 32, .length = 3}, |
79 | {.offset = 48, .length = 3}, | 79 | {.offset = 48, .length = 3}, |
80 | {.offset = 64, .length = 3}, | 80 | {.offset = 64, .length = 3}, |
81 | {.offset = 80, .length = 3}, | 81 | {.offset = 80, .length = 3}, |
82 | {.offset = 96, .length = 3}, | 82 | {.offset = 96, .length = 3}, |
83 | {.offset = 112, .length = 3} | 83 | {.offset = 112, .length = 3} |
84 | #else | 84 | #else |
85 | {.offset = 1, .length = 5}, | 85 | {.offset = 1, .length = 5}, |
86 | {.offset = 16, .length = 6}, | 86 | {.offset = 16, .length = 6}, |
87 | {.offset = 32, .length = 6}, | 87 | {.offset = 32, .length = 6}, |
88 | {.offset = 48, .length = 6}, | 88 | {.offset = 48, .length = 6}, |
89 | {.offset = 64, .length = 6}, | 89 | {.offset = 64, .length = 6}, |
90 | {.offset = 80, .length = 6}, | 90 | {.offset = 80, .length = 6}, |
91 | {.offset = 96, .length = 6}, | 91 | {.offset = 96, .length = 6}, |
92 | {.offset = 112, .length = 6} | 92 | {.offset = 112, .length = 6} |
93 | #endif | 93 | #endif |
94 | } | 94 | } |
95 | }; | 95 | }; |
96 | 96 | ||
97 | /* ---- Private Functions ------------------------------------------------ */ | 97 | /* ---- Private Functions ------------------------------------------------ */ |
98 | /* ==== Public Functions ================================================= */ | 98 | /* ==== Public Functions ================================================= */ |
99 | 99 | ||
100 | /**************************************************************************** | 100 | /**************************************************************************** |
101 | * | 101 | * |
102 | * bcm_umi_bch_read_page_hwecc - hardware ecc based page read function | 102 | * bcm_umi_bch_read_page_hwecc - hardware ecc based page read function |
103 | * @mtd: mtd info structure | 103 | * @mtd: mtd info structure |
104 | * @chip: nand chip info structure | 104 | * @chip: nand chip info structure |
105 | * @buf: buffer to store read data | 105 | * @buf: buffer to store read data |
106 | * @oob_required: caller expects OOB data read to chip->oob_poi | 106 | * @oob_required: caller expects OOB data read to chip->oob_poi |
107 | * | 107 | * |
108 | ***************************************************************************/ | 108 | ***************************************************************************/ |
109 | static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, | 109 | static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, |
110 | struct nand_chip *chip, uint8_t * buf, | 110 | struct nand_chip *chip, uint8_t * buf, |
111 | int oob_required, int page) | 111 | int oob_required, int page) |
112 | { | 112 | { |
113 | int sectorIdx = 0; | 113 | int sectorIdx = 0; |
114 | int eccsize = chip->ecc.size; | 114 | int eccsize = chip->ecc.size; |
115 | int eccsteps = chip->ecc.steps; | 115 | int eccsteps = chip->ecc.steps; |
116 | uint8_t *datap = buf; | 116 | uint8_t *datap = buf; |
117 | uint8_t eccCalc[NAND_ECC_NUM_BYTES]; | 117 | uint8_t eccCalc[NAND_ECC_NUM_BYTES]; |
118 | int sectorOobSize = mtd->oobsize / eccsteps; | 118 | int sectorOobSize = mtd->oobsize / eccsteps; |
119 | int stat; | 119 | int stat; |
120 | unsigned int max_bitflips = 0; | 120 | unsigned int max_bitflips = 0; |
121 | 121 | ||
122 | for (sectorIdx = 0; sectorIdx < eccsteps; | 122 | for (sectorIdx = 0; sectorIdx < eccsteps; |
123 | sectorIdx++, datap += eccsize) { | 123 | sectorIdx++, datap += eccsize) { |
124 | if (sectorIdx > 0) { | 124 | if (sectorIdx > 0) { |
125 | /* Seek to page location within sector */ | 125 | /* Seek to page location within sector */ |
126 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize, | 126 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize, |
127 | -1); | 127 | -1); |
128 | } | 128 | } |
129 | 129 | ||
130 | /* Enable hardware ECC before reading the buf */ | 130 | /* Enable hardware ECC before reading the buf */ |
131 | nand_bcm_umi_bch_enable_read_hwecc(); | 131 | nand_bcm_umi_bch_enable_read_hwecc(); |
132 | 132 | ||
133 | /* Read in data */ | 133 | /* Read in data */ |
134 | bcm_umi_nand_read_buf(mtd, datap, eccsize); | 134 | bcm_umi_nand_read_buf(mtd, datap, eccsize); |
135 | 135 | ||
136 | /* Pause hardware ECC after reading the buf */ | 136 | /* Pause hardware ECC after reading the buf */ |
137 | nand_bcm_umi_bch_pause_read_ecc_calc(); | 137 | nand_bcm_umi_bch_pause_read_ecc_calc(); |
138 | 138 | ||
139 | /* Read the OOB ECC */ | 139 | /* Read the OOB ECC */ |
140 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, | 140 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, |
141 | mtd->writesize + sectorIdx * sectorOobSize, -1); | 141 | mtd->writesize + sectorIdx * sectorOobSize, -1); |
142 | nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc, | 142 | nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc, |
143 | NAND_ECC_NUM_BYTES, | 143 | NAND_ECC_NUM_BYTES, |
144 | chip->oob_poi + | 144 | chip->oob_poi + |
145 | sectorIdx * sectorOobSize); | 145 | sectorIdx * sectorOobSize); |
146 | 146 | ||
147 | /* Correct any ECC detected errors */ | 147 | /* Correct any ECC detected errors */ |
148 | stat = | 148 | stat = |
149 | nand_bcm_umi_bch_correct_page(datap, eccCalc, | 149 | nand_bcm_umi_bch_correct_page(datap, eccCalc, |
150 | NAND_ECC_NUM_BYTES); | 150 | NAND_ECC_NUM_BYTES); |
151 | 151 | ||
152 | /* Update Stats */ | 152 | /* Update Stats */ |
153 | if (stat < 0) { | 153 | if (stat < 0) { |
154 | #if defined(NAND_BCM_UMI_DEBUG) | 154 | #if defined(NAND_BCM_UMI_DEBUG) |
155 | printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n", | 155 | printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n", |
156 | __func__, sectorIdx); | 156 | __func__, sectorIdx); |
157 | printk(KERN_WARNING | 157 | printk(KERN_WARNING "%s data %*ph\n", |
158 | "%s data %02x %02x %02x %02x " | 158 | __func__, 8, datap); |
159 | "%02x %02x %02x %02x\n", | 159 | printk(KERN_WARNING "%s ecc %*ph\n", |
160 | __func__, datap[0], datap[1], datap[2], datap[3], | 160 | __func__, 13, eccCalc); |
161 | datap[4], datap[5], datap[6], datap[7]); | ||
162 | printk(KERN_WARNING | ||
163 | "%s ecc %02x %02x %02x %02x " | ||
164 | "%02x %02x %02x %02x %02x %02x " | ||
165 | "%02x %02x %02x\n", | ||
166 | __func__, eccCalc[0], eccCalc[1], eccCalc[2], | ||
167 | eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6], | ||
168 | eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10], | ||
169 | eccCalc[11], eccCalc[12]); | ||
170 | BUG(); | 161 | BUG(); |
171 | #endif | 162 | #endif |
172 | mtd->ecc_stats.failed++; | 163 | mtd->ecc_stats.failed++; |
173 | } else { | 164 | } else { |
174 | #if defined(NAND_BCM_UMI_DEBUG) | 165 | #if defined(NAND_BCM_UMI_DEBUG) |
175 | if (stat > 0) { | 166 | if (stat > 0) { |
176 | printk(KERN_INFO | 167 | printk(KERN_INFO |
177 | "%s %d correctable_errors detected\n", | 168 | "%s %d correctable_errors detected\n", |
178 | __func__, stat); | 169 | __func__, stat); |
179 | } | 170 | } |
180 | #endif | 171 | #endif |
181 | mtd->ecc_stats.corrected += stat; | 172 | mtd->ecc_stats.corrected += stat; |
182 | max_bitflips = max_t(unsigned int, max_bitflips, stat); | 173 | max_bitflips = max_t(unsigned int, max_bitflips, stat); |
183 | } | 174 | } |
184 | } | 175 | } |
185 | return max_bitflips; | 176 | return max_bitflips; |
186 | } | 177 | } |
187 | 178 | ||
188 | /**************************************************************************** | 179 | /**************************************************************************** |
189 | * | 180 | * |
190 | * bcm_umi_bch_write_page_hwecc - hardware ecc based page write function | 181 | * bcm_umi_bch_write_page_hwecc - hardware ecc based page write function |
191 | * @mtd: mtd info structure | 182 | * @mtd: mtd info structure |
192 | * @chip: nand chip info structure | 183 | * @chip: nand chip info structure |
193 | * @buf: data buffer | 184 | * @buf: data buffer |
194 | * @oob_required: must write chip->oob_poi to OOB | 185 | * @oob_required: must write chip->oob_poi to OOB |
195 | * | 186 | * |
196 | ***************************************************************************/ | 187 | ***************************************************************************/ |
197 | static int bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, | 188 | static int bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, |
198 | struct nand_chip *chip, const uint8_t *buf, int oob_required) | 189 | struct nand_chip *chip, const uint8_t *buf, int oob_required) |
199 | { | 190 | { |
200 | int sectorIdx = 0; | 191 | int sectorIdx = 0; |
201 | int eccsize = chip->ecc.size; | 192 | int eccsize = chip->ecc.size; |
202 | int eccsteps = chip->ecc.steps; | 193 | int eccsteps = chip->ecc.steps; |
203 | const uint8_t *datap = buf; | 194 | const uint8_t *datap = buf; |
204 | uint8_t *oobp = chip->oob_poi; | 195 | uint8_t *oobp = chip->oob_poi; |
205 | int sectorOobSize = mtd->oobsize / eccsteps; | 196 | int sectorOobSize = mtd->oobsize / eccsteps; |
206 | 197 | ||
207 | for (sectorIdx = 0; sectorIdx < eccsteps; | 198 | for (sectorIdx = 0; sectorIdx < eccsteps; |
208 | sectorIdx++, datap += eccsize, oobp += sectorOobSize) { | 199 | sectorIdx++, datap += eccsize, oobp += sectorOobSize) { |
209 | /* Enable hardware ECC before writing the buf */ | 200 | /* Enable hardware ECC before writing the buf */ |
210 | nand_bcm_umi_bch_enable_write_hwecc(); | 201 | nand_bcm_umi_bch_enable_write_hwecc(); |
211 | bcm_umi_nand_write_buf(mtd, datap, eccsize); | 202 | bcm_umi_nand_write_buf(mtd, datap, eccsize); |
212 | nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp, | 203 | nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp, |
213 | NAND_ECC_NUM_BYTES); | 204 | NAND_ECC_NUM_BYTES); |
214 | } | 205 | } |
215 | 206 | ||
216 | bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); | 207 | bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); |
217 | 208 | ||
218 | return 0; | 209 | return 0; |
219 | } | 210 | } |
220 | 211 |
drivers/mtd/nand/s3c2410.c
1 | /* linux/drivers/mtd/nand/s3c2410.c | 1 | /* linux/drivers/mtd/nand/s3c2410.c |
2 | * | 2 | * |
3 | * Copyright ยฉ 2004-2008 Simtec Electronics | 3 | * Copyright ยฉ 2004-2008 Simtec Electronics |
4 | * http://armlinux.simtec.co.uk/ | 4 | * http://armlinux.simtec.co.uk/ |
5 | * Ben Dooks <ben@simtec.co.uk> | 5 | * Ben Dooks <ben@simtec.co.uk> |
6 | * | 6 | * |
7 | * Samsung S3C2410/S3C2440/S3C2412 NAND driver | 7 | * Samsung S3C2410/S3C2440/S3C2412 NAND driver |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation; either version 2 of the License, or | 11 | * the Free Software Foundation; either version 2 of the License, or |
12 | * (at your option) any later version. | 12 | * (at your option) any later version. |
13 | * | 13 | * |
14 | * This program is distributed in the hope that it will be useful, | 14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. | 17 | * GNU General Public License for more details. |
18 | * | 18 | * |
19 | * You should have received a copy of the GNU General Public License | 19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software | 20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define pr_fmt(fmt) "nand-s3c2410: " fmt | 24 | #define pr_fmt(fmt) "nand-s3c2410: " fmt |
25 | 25 | ||
26 | #ifdef CONFIG_MTD_NAND_S3C2410_DEBUG | 26 | #ifdef CONFIG_MTD_NAND_S3C2410_DEBUG |
27 | #define DEBUG | 27 | #define DEBUG |
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/types.h> | 31 | #include <linux/types.h> |
32 | #include <linux/init.h> | 32 | #include <linux/init.h> |
33 | #include <linux/kernel.h> | 33 | #include <linux/kernel.h> |
34 | #include <linux/string.h> | 34 | #include <linux/string.h> |
35 | #include <linux/io.h> | 35 | #include <linux/io.h> |
36 | #include <linux/ioport.h> | 36 | #include <linux/ioport.h> |
37 | #include <linux/platform_device.h> | 37 | #include <linux/platform_device.h> |
38 | #include <linux/delay.h> | 38 | #include <linux/delay.h> |
39 | #include <linux/err.h> | 39 | #include <linux/err.h> |
40 | #include <linux/slab.h> | 40 | #include <linux/slab.h> |
41 | #include <linux/clk.h> | 41 | #include <linux/clk.h> |
42 | #include <linux/cpufreq.h> | 42 | #include <linux/cpufreq.h> |
43 | 43 | ||
44 | #include <linux/mtd/mtd.h> | 44 | #include <linux/mtd/mtd.h> |
45 | #include <linux/mtd/nand.h> | 45 | #include <linux/mtd/nand.h> |
46 | #include <linux/mtd/nand_ecc.h> | 46 | #include <linux/mtd/nand_ecc.h> |
47 | #include <linux/mtd/partitions.h> | 47 | #include <linux/mtd/partitions.h> |
48 | 48 | ||
49 | #include <plat/regs-nand.h> | 49 | #include <plat/regs-nand.h> |
50 | #include <plat/nand.h> | 50 | #include <plat/nand.h> |
51 | 51 | ||
52 | /* new oob placement block for use with hardware ecc generation | 52 | /* new oob placement block for use with hardware ecc generation |
53 | */ | 53 | */ |
54 | 54 | ||
55 | static struct nand_ecclayout nand_hw_eccoob = { | 55 | static struct nand_ecclayout nand_hw_eccoob = { |
56 | .eccbytes = 3, | 56 | .eccbytes = 3, |
57 | .eccpos = {0, 1, 2}, | 57 | .eccpos = {0, 1, 2}, |
58 | .oobfree = {{8, 8}} | 58 | .oobfree = {{8, 8}} |
59 | }; | 59 | }; |
60 | 60 | ||
61 | /* controller and mtd information */ | 61 | /* controller and mtd information */ |
62 | 62 | ||
63 | struct s3c2410_nand_info; | 63 | struct s3c2410_nand_info; |
64 | 64 | ||
65 | /** | 65 | /** |
66 | * struct s3c2410_nand_mtd - driver MTD structure | 66 | * struct s3c2410_nand_mtd - driver MTD structure |
67 | * @mtd: The MTD instance to pass to the MTD layer. | 67 | * @mtd: The MTD instance to pass to the MTD layer. |
68 | * @chip: The NAND chip information. | 68 | * @chip: The NAND chip information. |
69 | * @set: The platform information supplied for this set of NAND chips. | 69 | * @set: The platform information supplied for this set of NAND chips. |
70 | * @info: Link back to the hardware information. | 70 | * @info: Link back to the hardware information. |
71 | * @scan_res: The result from calling nand_scan_ident(). | 71 | * @scan_res: The result from calling nand_scan_ident(). |
72 | */ | 72 | */ |
73 | struct s3c2410_nand_mtd { | 73 | struct s3c2410_nand_mtd { |
74 | struct mtd_info mtd; | 74 | struct mtd_info mtd; |
75 | struct nand_chip chip; | 75 | struct nand_chip chip; |
76 | struct s3c2410_nand_set *set; | 76 | struct s3c2410_nand_set *set; |
77 | struct s3c2410_nand_info *info; | 77 | struct s3c2410_nand_info *info; |
78 | int scan_res; | 78 | int scan_res; |
79 | }; | 79 | }; |
80 | 80 | ||
81 | enum s3c_cpu_type { | 81 | enum s3c_cpu_type { |
82 | TYPE_S3C2410, | 82 | TYPE_S3C2410, |
83 | TYPE_S3C2412, | 83 | TYPE_S3C2412, |
84 | TYPE_S3C2440, | 84 | TYPE_S3C2440, |
85 | }; | 85 | }; |
86 | 86 | ||
87 | enum s3c_nand_clk_state { | 87 | enum s3c_nand_clk_state { |
88 | CLOCK_DISABLE = 0, | 88 | CLOCK_DISABLE = 0, |
89 | CLOCK_ENABLE, | 89 | CLOCK_ENABLE, |
90 | CLOCK_SUSPEND, | 90 | CLOCK_SUSPEND, |
91 | }; | 91 | }; |
92 | 92 | ||
93 | /* overview of the s3c2410 nand state */ | 93 | /* overview of the s3c2410 nand state */ |
94 | 94 | ||
95 | /** | 95 | /** |
96 | * struct s3c2410_nand_info - NAND controller state. | 96 | * struct s3c2410_nand_info - NAND controller state. |
97 | * @mtds: An array of MTD instances on this controoler. | 97 | * @mtds: An array of MTD instances on this controoler. |
98 | * @platform: The platform data for this board. | 98 | * @platform: The platform data for this board. |
99 | * @device: The platform device we bound to. | 99 | * @device: The platform device we bound to. |
100 | * @area: The IO area resource that came from request_mem_region(). | 100 | * @area: The IO area resource that came from request_mem_region(). |
101 | * @clk: The clock resource for this controller. | 101 | * @clk: The clock resource for this controller. |
102 | * @regs: The area mapped for the hardware registers described by @area. | 102 | * @regs: The area mapped for the hardware registers described by @area. |
103 | * @sel_reg: Pointer to the register controlling the NAND selection. | 103 | * @sel_reg: Pointer to the register controlling the NAND selection. |
104 | * @sel_bit: The bit in @sel_reg to select the NAND chip. | 104 | * @sel_bit: The bit in @sel_reg to select the NAND chip. |
105 | * @mtd_count: The number of MTDs created from this controller. | 105 | * @mtd_count: The number of MTDs created from this controller. |
106 | * @save_sel: The contents of @sel_reg to be saved over suspend. | 106 | * @save_sel: The contents of @sel_reg to be saved over suspend. |
107 | * @clk_rate: The clock rate from @clk. | 107 | * @clk_rate: The clock rate from @clk. |
108 | * @clk_state: The current clock state. | 108 | * @clk_state: The current clock state. |
109 | * @cpu_type: The exact type of this controller. | 109 | * @cpu_type: The exact type of this controller. |
110 | */ | 110 | */ |
111 | struct s3c2410_nand_info { | 111 | struct s3c2410_nand_info { |
112 | /* mtd info */ | 112 | /* mtd info */ |
113 | struct nand_hw_control controller; | 113 | struct nand_hw_control controller; |
114 | struct s3c2410_nand_mtd *mtds; | 114 | struct s3c2410_nand_mtd *mtds; |
115 | struct s3c2410_platform_nand *platform; | 115 | struct s3c2410_platform_nand *platform; |
116 | 116 | ||
117 | /* device info */ | 117 | /* device info */ |
118 | struct device *device; | 118 | struct device *device; |
119 | struct resource *area; | 119 | struct resource *area; |
120 | struct clk *clk; | 120 | struct clk *clk; |
121 | void __iomem *regs; | 121 | void __iomem *regs; |
122 | void __iomem *sel_reg; | 122 | void __iomem *sel_reg; |
123 | int sel_bit; | 123 | int sel_bit; |
124 | int mtd_count; | 124 | int mtd_count; |
125 | unsigned long save_sel; | 125 | unsigned long save_sel; |
126 | unsigned long clk_rate; | 126 | unsigned long clk_rate; |
127 | enum s3c_nand_clk_state clk_state; | 127 | enum s3c_nand_clk_state clk_state; |
128 | 128 | ||
129 | enum s3c_cpu_type cpu_type; | 129 | enum s3c_cpu_type cpu_type; |
130 | 130 | ||
131 | #ifdef CONFIG_CPU_FREQ | 131 | #ifdef CONFIG_CPU_FREQ |
132 | struct notifier_block freq_transition; | 132 | struct notifier_block freq_transition; |
133 | #endif | 133 | #endif |
134 | }; | 134 | }; |
135 | 135 | ||
136 | /* conversion functions */ | 136 | /* conversion functions */ |
137 | 137 | ||
138 | static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) | 138 | static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) |
139 | { | 139 | { |
140 | return container_of(mtd, struct s3c2410_nand_mtd, mtd); | 140 | return container_of(mtd, struct s3c2410_nand_mtd, mtd); |
141 | } | 141 | } |
142 | 142 | ||
143 | static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) | 143 | static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) |
144 | { | 144 | { |
145 | return s3c2410_nand_mtd_toours(mtd)->info; | 145 | return s3c2410_nand_mtd_toours(mtd)->info; |
146 | } | 146 | } |
147 | 147 | ||
148 | static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) | 148 | static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) |
149 | { | 149 | { |
150 | return platform_get_drvdata(dev); | 150 | return platform_get_drvdata(dev); |
151 | } | 151 | } |
152 | 152 | ||
153 | static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) | 153 | static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) |
154 | { | 154 | { |
155 | return dev->dev.platform_data; | 155 | return dev->dev.platform_data; |
156 | } | 156 | } |
157 | 157 | ||
158 | static inline int allow_clk_suspend(struct s3c2410_nand_info *info) | 158 | static inline int allow_clk_suspend(struct s3c2410_nand_info *info) |
159 | { | 159 | { |
160 | #ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP | 160 | #ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP |
161 | return 1; | 161 | return 1; |
162 | #else | 162 | #else |
163 | return 0; | 163 | return 0; |
164 | #endif | 164 | #endif |
165 | } | 165 | } |
166 | 166 | ||
167 | /** | 167 | /** |
168 | * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock. | 168 | * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock. |
169 | * @info: The controller instance. | 169 | * @info: The controller instance. |
170 | * @new_state: State to which clock should be set. | 170 | * @new_state: State to which clock should be set. |
171 | */ | 171 | */ |
172 | static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info, | 172 | static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info, |
173 | enum s3c_nand_clk_state new_state) | 173 | enum s3c_nand_clk_state new_state) |
174 | { | 174 | { |
175 | if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND) | 175 | if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND) |
176 | return; | 176 | return; |
177 | 177 | ||
178 | if (info->clk_state == CLOCK_ENABLE) { | 178 | if (info->clk_state == CLOCK_ENABLE) { |
179 | if (new_state != CLOCK_ENABLE) | 179 | if (new_state != CLOCK_ENABLE) |
180 | clk_disable(info->clk); | 180 | clk_disable(info->clk); |
181 | } else { | 181 | } else { |
182 | if (new_state == CLOCK_ENABLE) | 182 | if (new_state == CLOCK_ENABLE) |
183 | clk_enable(info->clk); | 183 | clk_enable(info->clk); |
184 | } | 184 | } |
185 | 185 | ||
186 | info->clk_state = new_state; | 186 | info->clk_state = new_state; |
187 | } | 187 | } |
188 | 188 | ||
189 | /* timing calculations */ | 189 | /* timing calculations */ |
190 | 190 | ||
191 | #define NS_IN_KHZ 1000000 | 191 | #define NS_IN_KHZ 1000000 |
192 | 192 | ||
193 | /** | 193 | /** |
194 | * s3c_nand_calc_rate - calculate timing data. | 194 | * s3c_nand_calc_rate - calculate timing data. |
195 | * @wanted: The cycle time in nanoseconds. | 195 | * @wanted: The cycle time in nanoseconds. |
196 | * @clk: The clock rate in kHz. | 196 | * @clk: The clock rate in kHz. |
197 | * @max: The maximum divider value. | 197 | * @max: The maximum divider value. |
198 | * | 198 | * |
199 | * Calculate the timing value from the given parameters. | 199 | * Calculate the timing value from the given parameters. |
200 | */ | 200 | */ |
201 | static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) | 201 | static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) |
202 | { | 202 | { |
203 | int result; | 203 | int result; |
204 | 204 | ||
205 | result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ); | 205 | result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ); |
206 | 206 | ||
207 | pr_debug("result %d from %ld, %d\n", result, clk, wanted); | 207 | pr_debug("result %d from %ld, %d\n", result, clk, wanted); |
208 | 208 | ||
209 | if (result > max) { | 209 | if (result > max) { |
210 | pr_err("%d ns is too big for current clock rate %ld\n", | 210 | pr_err("%d ns is too big for current clock rate %ld\n", |
211 | wanted, clk); | 211 | wanted, clk); |
212 | return -1; | 212 | return -1; |
213 | } | 213 | } |
214 | 214 | ||
215 | if (result < 1) | 215 | if (result < 1) |
216 | result = 1; | 216 | result = 1; |
217 | 217 | ||
218 | return result; | 218 | return result; |
219 | } | 219 | } |
220 | 220 | ||
221 | #define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) | 221 | #define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) |
222 | 222 | ||
223 | /* controller setup */ | 223 | /* controller setup */ |
224 | 224 | ||
225 | /** | 225 | /** |
226 | * s3c2410_nand_setrate - setup controller timing information. | 226 | * s3c2410_nand_setrate - setup controller timing information. |
227 | * @info: The controller instance. | 227 | * @info: The controller instance. |
228 | * | 228 | * |
229 | * Given the information supplied by the platform, calculate and set | 229 | * Given the information supplied by the platform, calculate and set |
230 | * the necessary timing registers in the hardware to generate the | 230 | * the necessary timing registers in the hardware to generate the |
231 | * necessary timing cycles to the hardware. | 231 | * necessary timing cycles to the hardware. |
232 | */ | 232 | */ |
233 | static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) | 233 | static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) |
234 | { | 234 | { |
235 | struct s3c2410_platform_nand *plat = info->platform; | 235 | struct s3c2410_platform_nand *plat = info->platform; |
236 | int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; | 236 | int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; |
237 | int tacls, twrph0, twrph1; | 237 | int tacls, twrph0, twrph1; |
238 | unsigned long clkrate = clk_get_rate(info->clk); | 238 | unsigned long clkrate = clk_get_rate(info->clk); |
239 | unsigned long uninitialized_var(set), cfg, uninitialized_var(mask); | 239 | unsigned long uninitialized_var(set), cfg, uninitialized_var(mask); |
240 | unsigned long flags; | 240 | unsigned long flags; |
241 | 241 | ||
242 | /* calculate the timing information for the controller */ | 242 | /* calculate the timing information for the controller */ |
243 | 243 | ||
244 | info->clk_rate = clkrate; | 244 | info->clk_rate = clkrate; |
245 | clkrate /= 1000; /* turn clock into kHz for ease of use */ | 245 | clkrate /= 1000; /* turn clock into kHz for ease of use */ |
246 | 246 | ||
247 | if (plat != NULL) { | 247 | if (plat != NULL) { |
248 | tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max); | 248 | tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max); |
249 | twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8); | 249 | twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8); |
250 | twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8); | 250 | twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8); |
251 | } else { | 251 | } else { |
252 | /* default timings */ | 252 | /* default timings */ |
253 | tacls = tacls_max; | 253 | tacls = tacls_max; |
254 | twrph0 = 8; | 254 | twrph0 = 8; |
255 | twrph1 = 8; | 255 | twrph1 = 8; |
256 | } | 256 | } |
257 | 257 | ||
258 | if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { | 258 | if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { |
259 | dev_err(info->device, "cannot get suitable timings\n"); | 259 | dev_err(info->device, "cannot get suitable timings\n"); |
260 | return -EINVAL; | 260 | return -EINVAL; |
261 | } | 261 | } |
262 | 262 | ||
263 | dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", | 263 | dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", |
264 | tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), | 264 | tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), |
265 | twrph1, to_ns(twrph1, clkrate)); | 265 | twrph1, to_ns(twrph1, clkrate)); |
266 | 266 | ||
267 | switch (info->cpu_type) { | 267 | switch (info->cpu_type) { |
268 | case TYPE_S3C2410: | 268 | case TYPE_S3C2410: |
269 | mask = (S3C2410_NFCONF_TACLS(3) | | 269 | mask = (S3C2410_NFCONF_TACLS(3) | |
270 | S3C2410_NFCONF_TWRPH0(7) | | 270 | S3C2410_NFCONF_TWRPH0(7) | |
271 | S3C2410_NFCONF_TWRPH1(7)); | 271 | S3C2410_NFCONF_TWRPH1(7)); |
272 | set = S3C2410_NFCONF_EN; | 272 | set = S3C2410_NFCONF_EN; |
273 | set |= S3C2410_NFCONF_TACLS(tacls - 1); | 273 | set |= S3C2410_NFCONF_TACLS(tacls - 1); |
274 | set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); | 274 | set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); |
275 | set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); | 275 | set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); |
276 | break; | 276 | break; |
277 | 277 | ||
278 | case TYPE_S3C2440: | 278 | case TYPE_S3C2440: |
279 | case TYPE_S3C2412: | 279 | case TYPE_S3C2412: |
280 | mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | | 280 | mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | |
281 | S3C2440_NFCONF_TWRPH0(7) | | 281 | S3C2440_NFCONF_TWRPH0(7) | |
282 | S3C2440_NFCONF_TWRPH1(7)); | 282 | S3C2440_NFCONF_TWRPH1(7)); |
283 | 283 | ||
284 | set = S3C2440_NFCONF_TACLS(tacls - 1); | 284 | set = S3C2440_NFCONF_TACLS(tacls - 1); |
285 | set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); | 285 | set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); |
286 | set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); | 286 | set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); |
287 | break; | 287 | break; |
288 | 288 | ||
289 | default: | 289 | default: |
290 | BUG(); | 290 | BUG(); |
291 | } | 291 | } |
292 | 292 | ||
293 | local_irq_save(flags); | 293 | local_irq_save(flags); |
294 | 294 | ||
295 | cfg = readl(info->regs + S3C2410_NFCONF); | 295 | cfg = readl(info->regs + S3C2410_NFCONF); |
296 | cfg &= ~mask; | 296 | cfg &= ~mask; |
297 | cfg |= set; | 297 | cfg |= set; |
298 | writel(cfg, info->regs + S3C2410_NFCONF); | 298 | writel(cfg, info->regs + S3C2410_NFCONF); |
299 | 299 | ||
300 | local_irq_restore(flags); | 300 | local_irq_restore(flags); |
301 | 301 | ||
302 | dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); | 302 | dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); |
303 | 303 | ||
304 | return 0; | 304 | return 0; |
305 | } | 305 | } |
306 | 306 | ||
307 | /** | 307 | /** |
308 | * s3c2410_nand_inithw - basic hardware initialisation | 308 | * s3c2410_nand_inithw - basic hardware initialisation |
309 | * @info: The hardware state. | 309 | * @info: The hardware state. |
310 | * | 310 | * |
311 | * Do the basic initialisation of the hardware, using s3c2410_nand_setrate() | 311 | * Do the basic initialisation of the hardware, using s3c2410_nand_setrate() |
312 | * to setup the hardware access speeds and set the controller to be enabled. | 312 | * to setup the hardware access speeds and set the controller to be enabled. |
313 | */ | 313 | */ |
314 | static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) | 314 | static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) |
315 | { | 315 | { |
316 | int ret; | 316 | int ret; |
317 | 317 | ||
318 | ret = s3c2410_nand_setrate(info); | 318 | ret = s3c2410_nand_setrate(info); |
319 | if (ret < 0) | 319 | if (ret < 0) |
320 | return ret; | 320 | return ret; |
321 | 321 | ||
322 | switch (info->cpu_type) { | 322 | switch (info->cpu_type) { |
323 | case TYPE_S3C2410: | 323 | case TYPE_S3C2410: |
324 | default: | 324 | default: |
325 | break; | 325 | break; |
326 | 326 | ||
327 | case TYPE_S3C2440: | 327 | case TYPE_S3C2440: |
328 | case TYPE_S3C2412: | 328 | case TYPE_S3C2412: |
329 | /* enable the controller and de-assert nFCE */ | 329 | /* enable the controller and de-assert nFCE */ |
330 | 330 | ||
331 | writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); | 331 | writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); |
332 | } | 332 | } |
333 | 333 | ||
334 | return 0; | 334 | return 0; |
335 | } | 335 | } |
336 | 336 | ||
337 | /** | 337 | /** |
338 | * s3c2410_nand_select_chip - select the given nand chip | 338 | * s3c2410_nand_select_chip - select the given nand chip |
339 | * @mtd: The MTD instance for this chip. | 339 | * @mtd: The MTD instance for this chip. |
340 | * @chip: The chip number. | 340 | * @chip: The chip number. |
341 | * | 341 | * |
342 | * This is called by the MTD layer to either select a given chip for the | 342 | * This is called by the MTD layer to either select a given chip for the |
343 | * @mtd instance, or to indicate that the access has finished and the | 343 | * @mtd instance, or to indicate that the access has finished and the |
344 | * chip can be de-selected. | 344 | * chip can be de-selected. |
345 | * | 345 | * |
346 | * The routine ensures that the nFCE line is correctly setup, and any | 346 | * The routine ensures that the nFCE line is correctly setup, and any |
347 | * platform specific selection code is called to route nFCE to the specific | 347 | * platform specific selection code is called to route nFCE to the specific |
348 | * chip. | 348 | * chip. |
349 | */ | 349 | */ |
350 | static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) | 350 | static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) |
351 | { | 351 | { |
352 | struct s3c2410_nand_info *info; | 352 | struct s3c2410_nand_info *info; |
353 | struct s3c2410_nand_mtd *nmtd; | 353 | struct s3c2410_nand_mtd *nmtd; |
354 | struct nand_chip *this = mtd->priv; | 354 | struct nand_chip *this = mtd->priv; |
355 | unsigned long cur; | 355 | unsigned long cur; |
356 | 356 | ||
357 | nmtd = this->priv; | 357 | nmtd = this->priv; |
358 | info = nmtd->info; | 358 | info = nmtd->info; |
359 | 359 | ||
360 | if (chip != -1) | 360 | if (chip != -1) |
361 | s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); | 361 | s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); |
362 | 362 | ||
363 | cur = readl(info->sel_reg); | 363 | cur = readl(info->sel_reg); |
364 | 364 | ||
365 | if (chip == -1) { | 365 | if (chip == -1) { |
366 | cur |= info->sel_bit; | 366 | cur |= info->sel_bit; |
367 | } else { | 367 | } else { |
368 | if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { | 368 | if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { |
369 | dev_err(info->device, "invalid chip %d\n", chip); | 369 | dev_err(info->device, "invalid chip %d\n", chip); |
370 | return; | 370 | return; |
371 | } | 371 | } |
372 | 372 | ||
373 | if (info->platform != NULL) { | 373 | if (info->platform != NULL) { |
374 | if (info->platform->select_chip != NULL) | 374 | if (info->platform->select_chip != NULL) |
375 | (info->platform->select_chip) (nmtd->set, chip); | 375 | (info->platform->select_chip) (nmtd->set, chip); |
376 | } | 376 | } |
377 | 377 | ||
378 | cur &= ~info->sel_bit; | 378 | cur &= ~info->sel_bit; |
379 | } | 379 | } |
380 | 380 | ||
381 | writel(cur, info->sel_reg); | 381 | writel(cur, info->sel_reg); |
382 | 382 | ||
383 | if (chip == -1) | 383 | if (chip == -1) |
384 | s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); | 384 | s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); |
385 | } | 385 | } |
386 | 386 | ||
387 | /* s3c2410_nand_hwcontrol | 387 | /* s3c2410_nand_hwcontrol |
388 | * | 388 | * |
389 | * Issue command and address cycles to the chip | 389 | * Issue command and address cycles to the chip |
390 | */ | 390 | */ |
391 | 391 | ||
392 | static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd, | 392 | static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd, |
393 | unsigned int ctrl) | 393 | unsigned int ctrl) |
394 | { | 394 | { |
395 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 395 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
396 | 396 | ||
397 | if (cmd == NAND_CMD_NONE) | 397 | if (cmd == NAND_CMD_NONE) |
398 | return; | 398 | return; |
399 | 399 | ||
400 | if (ctrl & NAND_CLE) | 400 | if (ctrl & NAND_CLE) |
401 | writeb(cmd, info->regs + S3C2410_NFCMD); | 401 | writeb(cmd, info->regs + S3C2410_NFCMD); |
402 | else | 402 | else |
403 | writeb(cmd, info->regs + S3C2410_NFADDR); | 403 | writeb(cmd, info->regs + S3C2410_NFADDR); |
404 | } | 404 | } |
405 | 405 | ||
406 | /* command and control functions */ | 406 | /* command and control functions */ |
407 | 407 | ||
408 | static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd, | 408 | static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd, |
409 | unsigned int ctrl) | 409 | unsigned int ctrl) |
410 | { | 410 | { |
411 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 411 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
412 | 412 | ||
413 | if (cmd == NAND_CMD_NONE) | 413 | if (cmd == NAND_CMD_NONE) |
414 | return; | 414 | return; |
415 | 415 | ||
416 | if (ctrl & NAND_CLE) | 416 | if (ctrl & NAND_CLE) |
417 | writeb(cmd, info->regs + S3C2440_NFCMD); | 417 | writeb(cmd, info->regs + S3C2440_NFCMD); |
418 | else | 418 | else |
419 | writeb(cmd, info->regs + S3C2440_NFADDR); | 419 | writeb(cmd, info->regs + S3C2440_NFADDR); |
420 | } | 420 | } |
421 | 421 | ||
422 | /* s3c2410_nand_devready() | 422 | /* s3c2410_nand_devready() |
423 | * | 423 | * |
424 | * returns 0 if the nand is busy, 1 if it is ready | 424 | * returns 0 if the nand is busy, 1 if it is ready |
425 | */ | 425 | */ |
426 | 426 | ||
427 | static int s3c2410_nand_devready(struct mtd_info *mtd) | 427 | static int s3c2410_nand_devready(struct mtd_info *mtd) |
428 | { | 428 | { |
429 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 429 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
430 | return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; | 430 | return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; |
431 | } | 431 | } |
432 | 432 | ||
433 | static int s3c2440_nand_devready(struct mtd_info *mtd) | 433 | static int s3c2440_nand_devready(struct mtd_info *mtd) |
434 | { | 434 | { |
435 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 435 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
436 | return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; | 436 | return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; |
437 | } | 437 | } |
438 | 438 | ||
439 | static int s3c2412_nand_devready(struct mtd_info *mtd) | 439 | static int s3c2412_nand_devready(struct mtd_info *mtd) |
440 | { | 440 | { |
441 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 441 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
442 | return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY; | 442 | return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY; |
443 | } | 443 | } |
444 | 444 | ||
445 | /* ECC handling functions */ | 445 | /* ECC handling functions */ |
446 | 446 | ||
447 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, | 447 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, |
448 | u_char *read_ecc, u_char *calc_ecc) | 448 | u_char *read_ecc, u_char *calc_ecc) |
449 | { | 449 | { |
450 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 450 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
451 | unsigned int diff0, diff1, diff2; | 451 | unsigned int diff0, diff1, diff2; |
452 | unsigned int bit, byte; | 452 | unsigned int bit, byte; |
453 | 453 | ||
454 | pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); | 454 | pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); |
455 | 455 | ||
456 | diff0 = read_ecc[0] ^ calc_ecc[0]; | 456 | diff0 = read_ecc[0] ^ calc_ecc[0]; |
457 | diff1 = read_ecc[1] ^ calc_ecc[1]; | 457 | diff1 = read_ecc[1] ^ calc_ecc[1]; |
458 | diff2 = read_ecc[2] ^ calc_ecc[2]; | 458 | diff2 = read_ecc[2] ^ calc_ecc[2]; |
459 | 459 | ||
460 | pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n", | 460 | pr_debug("%s: rd %*phN calc %*phN diff %02x%02x%02x\n", |
461 | __func__, | 461 | __func__, 3, read_ecc, 3, calc_ecc, |
462 | read_ecc[0], read_ecc[1], read_ecc[2], | ||
463 | calc_ecc[0], calc_ecc[1], calc_ecc[2], | ||
464 | diff0, diff1, diff2); | 462 | diff0, diff1, diff2); |
465 | 463 | ||
466 | if (diff0 == 0 && diff1 == 0 && diff2 == 0) | 464 | if (diff0 == 0 && diff1 == 0 && diff2 == 0) |
467 | return 0; /* ECC is ok */ | 465 | return 0; /* ECC is ok */ |
468 | 466 | ||
469 | /* sometimes people do not think about using the ECC, so check | 467 | /* sometimes people do not think about using the ECC, so check |
470 | * to see if we have an 0xff,0xff,0xff read ECC and then ignore | 468 | * to see if we have an 0xff,0xff,0xff read ECC and then ignore |
471 | * the error, on the assumption that this is an un-eccd page. | 469 | * the error, on the assumption that this is an un-eccd page. |
472 | */ | 470 | */ |
473 | if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff | 471 | if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff |
474 | && info->platform->ignore_unset_ecc) | 472 | && info->platform->ignore_unset_ecc) |
475 | return 0; | 473 | return 0; |
476 | 474 | ||
477 | /* Can we correct this ECC (ie, one row and column change). | 475 | /* Can we correct this ECC (ie, one row and column change). |
478 | * Note, this is similar to the 256 error code on smartmedia */ | 476 | * Note, this is similar to the 256 error code on smartmedia */ |
479 | 477 | ||
480 | if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && | 478 | if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && |
481 | ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && | 479 | ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && |
482 | ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { | 480 | ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { |
483 | /* calculate the bit position of the error */ | 481 | /* calculate the bit position of the error */ |
484 | 482 | ||
485 | bit = ((diff2 >> 3) & 1) | | 483 | bit = ((diff2 >> 3) & 1) | |
486 | ((diff2 >> 4) & 2) | | 484 | ((diff2 >> 4) & 2) | |
487 | ((diff2 >> 5) & 4); | 485 | ((diff2 >> 5) & 4); |
488 | 486 | ||
489 | /* calculate the byte position of the error */ | 487 | /* calculate the byte position of the error */ |
490 | 488 | ||
491 | byte = ((diff2 << 7) & 0x100) | | 489 | byte = ((diff2 << 7) & 0x100) | |
492 | ((diff1 << 0) & 0x80) | | 490 | ((diff1 << 0) & 0x80) | |
493 | ((diff1 << 1) & 0x40) | | 491 | ((diff1 << 1) & 0x40) | |
494 | ((diff1 << 2) & 0x20) | | 492 | ((diff1 << 2) & 0x20) | |
495 | ((diff1 << 3) & 0x10) | | 493 | ((diff1 << 3) & 0x10) | |
496 | ((diff0 >> 4) & 0x08) | | 494 | ((diff0 >> 4) & 0x08) | |
497 | ((diff0 >> 3) & 0x04) | | 495 | ((diff0 >> 3) & 0x04) | |
498 | ((diff0 >> 2) & 0x02) | | 496 | ((diff0 >> 2) & 0x02) | |
499 | ((diff0 >> 1) & 0x01); | 497 | ((diff0 >> 1) & 0x01); |
500 | 498 | ||
501 | dev_dbg(info->device, "correcting error bit %d, byte %d\n", | 499 | dev_dbg(info->device, "correcting error bit %d, byte %d\n", |
502 | bit, byte); | 500 | bit, byte); |
503 | 501 | ||
504 | dat[byte] ^= (1 << bit); | 502 | dat[byte] ^= (1 << bit); |
505 | return 1; | 503 | return 1; |
506 | } | 504 | } |
507 | 505 | ||
508 | /* if there is only one bit difference in the ECC, then | 506 | /* if there is only one bit difference in the ECC, then |
509 | * one of only a row or column parity has changed, which | 507 | * one of only a row or column parity has changed, which |
510 | * means the error is most probably in the ECC itself */ | 508 | * means the error is most probably in the ECC itself */ |
511 | 509 | ||
512 | diff0 |= (diff1 << 8); | 510 | diff0 |= (diff1 << 8); |
513 | diff0 |= (diff2 << 16); | 511 | diff0 |= (diff2 << 16); |
514 | 512 | ||
515 | if ((diff0 & ~(1<<fls(diff0))) == 0) | 513 | if ((diff0 & ~(1<<fls(diff0))) == 0) |
516 | return 1; | 514 | return 1; |
517 | 515 | ||
518 | return -1; | 516 | return -1; |
519 | } | 517 | } |
520 | 518 | ||
521 | /* ECC functions | 519 | /* ECC functions |
522 | * | 520 | * |
523 | * These allow the s3c2410 and s3c2440 to use the controller's ECC | 521 | * These allow the s3c2410 and s3c2440 to use the controller's ECC |
524 | * generator block to ECC the data as it passes through] | 522 | * generator block to ECC the data as it passes through] |
525 | */ | 523 | */ |
526 | 524 | ||
527 | static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) | 525 | static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) |
528 | { | 526 | { |
529 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 527 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
530 | unsigned long ctrl; | 528 | unsigned long ctrl; |
531 | 529 | ||
532 | ctrl = readl(info->regs + S3C2410_NFCONF); | 530 | ctrl = readl(info->regs + S3C2410_NFCONF); |
533 | ctrl |= S3C2410_NFCONF_INITECC; | 531 | ctrl |= S3C2410_NFCONF_INITECC; |
534 | writel(ctrl, info->regs + S3C2410_NFCONF); | 532 | writel(ctrl, info->regs + S3C2410_NFCONF); |
535 | } | 533 | } |
536 | 534 | ||
537 | static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) | 535 | static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) |
538 | { | 536 | { |
539 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 537 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
540 | unsigned long ctrl; | 538 | unsigned long ctrl; |
541 | 539 | ||
542 | ctrl = readl(info->regs + S3C2440_NFCONT); | 540 | ctrl = readl(info->regs + S3C2440_NFCONT); |
543 | writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, | 541 | writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, |
544 | info->regs + S3C2440_NFCONT); | 542 | info->regs + S3C2440_NFCONT); |
545 | } | 543 | } |
546 | 544 | ||
547 | static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) | 545 | static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) |
548 | { | 546 | { |
549 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 547 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
550 | unsigned long ctrl; | 548 | unsigned long ctrl; |
551 | 549 | ||
552 | ctrl = readl(info->regs + S3C2440_NFCONT); | 550 | ctrl = readl(info->regs + S3C2440_NFCONT); |
553 | writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); | 551 | writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); |
554 | } | 552 | } |
555 | 553 | ||
556 | static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, | 554 | static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, |
557 | u_char *ecc_code) | 555 | u_char *ecc_code) |
558 | { | 556 | { |
559 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 557 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
560 | 558 | ||
561 | ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); | 559 | ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); |
562 | ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); | 560 | ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); |
563 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); | 561 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); |
564 | 562 | ||
565 | pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, | 563 | pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); |
566 | ecc_code[0], ecc_code[1], ecc_code[2]); | ||
567 | 564 | ||
568 | return 0; | 565 | return 0; |
569 | } | 566 | } |
570 | 567 | ||
571 | static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, | 568 | static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, |
572 | u_char *ecc_code) | 569 | u_char *ecc_code) |
573 | { | 570 | { |
574 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 571 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
575 | unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); | 572 | unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); |
576 | 573 | ||
577 | ecc_code[0] = ecc; | 574 | ecc_code[0] = ecc; |
578 | ecc_code[1] = ecc >> 8; | 575 | ecc_code[1] = ecc >> 8; |
579 | ecc_code[2] = ecc >> 16; | 576 | ecc_code[2] = ecc >> 16; |
580 | 577 | ||
581 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", | 578 | pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); |
582 | ecc_code[0], ecc_code[1], ecc_code[2]); | ||
583 | 579 | ||
584 | return 0; | 580 | return 0; |
585 | } | 581 | } |
586 | 582 | ||
587 | static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, | 583 | static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, |
588 | u_char *ecc_code) | 584 | u_char *ecc_code) |
589 | { | 585 | { |
590 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 586 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
591 | unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); | 587 | unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); |
592 | 588 | ||
593 | ecc_code[0] = ecc; | 589 | ecc_code[0] = ecc; |
594 | ecc_code[1] = ecc >> 8; | 590 | ecc_code[1] = ecc >> 8; |
595 | ecc_code[2] = ecc >> 16; | 591 | ecc_code[2] = ecc >> 16; |
596 | 592 | ||
597 | pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff); | 593 | pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff); |
598 | 594 | ||
599 | return 0; | 595 | return 0; |
600 | } | 596 | } |
601 | 597 | ||
602 | /* over-ride the standard functions for a little more speed. We can | 598 | /* over-ride the standard functions for a little more speed. We can |
603 | * use read/write block to move the data buffers to/from the controller | 599 | * use read/write block to move the data buffers to/from the controller |
604 | */ | 600 | */ |
605 | 601 | ||
606 | static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) | 602 | static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) |
607 | { | 603 | { |
608 | struct nand_chip *this = mtd->priv; | 604 | struct nand_chip *this = mtd->priv; |
609 | readsb(this->IO_ADDR_R, buf, len); | 605 | readsb(this->IO_ADDR_R, buf, len); |
610 | } | 606 | } |
611 | 607 | ||
612 | static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) | 608 | static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) |
613 | { | 609 | { |
614 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 610 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
615 | 611 | ||
616 | readsl(info->regs + S3C2440_NFDATA, buf, len >> 2); | 612 | readsl(info->regs + S3C2440_NFDATA, buf, len >> 2); |
617 | 613 | ||
618 | /* cleanup if we've got less than a word to do */ | 614 | /* cleanup if we've got less than a word to do */ |
619 | if (len & 3) { | 615 | if (len & 3) { |
620 | buf += len & ~3; | 616 | buf += len & ~3; |
621 | 617 | ||
622 | for (; len & 3; len--) | 618 | for (; len & 3; len--) |
623 | *buf++ = readb(info->regs + S3C2440_NFDATA); | 619 | *buf++ = readb(info->regs + S3C2440_NFDATA); |
624 | } | 620 | } |
625 | } | 621 | } |
626 | 622 | ||
627 | static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, | 623 | static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, |
628 | int len) | 624 | int len) |
629 | { | 625 | { |
630 | struct nand_chip *this = mtd->priv; | 626 | struct nand_chip *this = mtd->priv; |
631 | writesb(this->IO_ADDR_W, buf, len); | 627 | writesb(this->IO_ADDR_W, buf, len); |
632 | } | 628 | } |
633 | 629 | ||
634 | static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, | 630 | static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, |
635 | int len) | 631 | int len) |
636 | { | 632 | { |
637 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 633 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
638 | 634 | ||
639 | writesl(info->regs + S3C2440_NFDATA, buf, len >> 2); | 635 | writesl(info->regs + S3C2440_NFDATA, buf, len >> 2); |
640 | 636 | ||
641 | /* cleanup any fractional write */ | 637 | /* cleanup any fractional write */ |
642 | if (len & 3) { | 638 | if (len & 3) { |
643 | buf += len & ~3; | 639 | buf += len & ~3; |
644 | 640 | ||
645 | for (; len & 3; len--, buf++) | 641 | for (; len & 3; len--, buf++) |
646 | writeb(*buf, info->regs + S3C2440_NFDATA); | 642 | writeb(*buf, info->regs + S3C2440_NFDATA); |
647 | } | 643 | } |
648 | } | 644 | } |
649 | 645 | ||
650 | /* cpufreq driver support */ | 646 | /* cpufreq driver support */ |
651 | 647 | ||
652 | #ifdef CONFIG_CPU_FREQ | 648 | #ifdef CONFIG_CPU_FREQ |
653 | 649 | ||
654 | static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb, | 650 | static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb, |
655 | unsigned long val, void *data) | 651 | unsigned long val, void *data) |
656 | { | 652 | { |
657 | struct s3c2410_nand_info *info; | 653 | struct s3c2410_nand_info *info; |
658 | unsigned long newclk; | 654 | unsigned long newclk; |
659 | 655 | ||
660 | info = container_of(nb, struct s3c2410_nand_info, freq_transition); | 656 | info = container_of(nb, struct s3c2410_nand_info, freq_transition); |
661 | newclk = clk_get_rate(info->clk); | 657 | newclk = clk_get_rate(info->clk); |
662 | 658 | ||
663 | if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) || | 659 | if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) || |
664 | (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) { | 660 | (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) { |
665 | s3c2410_nand_setrate(info); | 661 | s3c2410_nand_setrate(info); |
666 | } | 662 | } |
667 | 663 | ||
668 | return 0; | 664 | return 0; |
669 | } | 665 | } |
670 | 666 | ||
671 | static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) | 667 | static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) |
672 | { | 668 | { |
673 | info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition; | 669 | info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition; |
674 | 670 | ||
675 | return cpufreq_register_notifier(&info->freq_transition, | 671 | return cpufreq_register_notifier(&info->freq_transition, |
676 | CPUFREQ_TRANSITION_NOTIFIER); | 672 | CPUFREQ_TRANSITION_NOTIFIER); |
677 | } | 673 | } |
678 | 674 | ||
679 | static inline void | 675 | static inline void |
680 | s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) | 676 | s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) |
681 | { | 677 | { |
682 | cpufreq_unregister_notifier(&info->freq_transition, | 678 | cpufreq_unregister_notifier(&info->freq_transition, |
683 | CPUFREQ_TRANSITION_NOTIFIER); | 679 | CPUFREQ_TRANSITION_NOTIFIER); |
684 | } | 680 | } |
685 | 681 | ||
686 | #else | 682 | #else |
687 | static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) | 683 | static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) |
688 | { | 684 | { |
689 | return 0; | 685 | return 0; |
690 | } | 686 | } |
691 | 687 | ||
692 | static inline void | 688 | static inline void |
693 | s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) | 689 | s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) |
694 | { | 690 | { |
695 | } | 691 | } |
696 | #endif | 692 | #endif |
697 | 693 | ||
698 | /* device management functions */ | 694 | /* device management functions */ |
699 | 695 | ||
700 | static int s3c24xx_nand_remove(struct platform_device *pdev) | 696 | static int s3c24xx_nand_remove(struct platform_device *pdev) |
701 | { | 697 | { |
702 | struct s3c2410_nand_info *info = to_nand_info(pdev); | 698 | struct s3c2410_nand_info *info = to_nand_info(pdev); |
703 | 699 | ||
704 | platform_set_drvdata(pdev, NULL); | 700 | platform_set_drvdata(pdev, NULL); |
705 | 701 | ||
706 | if (info == NULL) | 702 | if (info == NULL) |
707 | return 0; | 703 | return 0; |
708 | 704 | ||
709 | s3c2410_nand_cpufreq_deregister(info); | 705 | s3c2410_nand_cpufreq_deregister(info); |
710 | 706 | ||
711 | /* Release all our mtds and their partitions, then go through | 707 | /* Release all our mtds and their partitions, then go through |
712 | * freeing the resources used | 708 | * freeing the resources used |
713 | */ | 709 | */ |
714 | 710 | ||
715 | if (info->mtds != NULL) { | 711 | if (info->mtds != NULL) { |
716 | struct s3c2410_nand_mtd *ptr = info->mtds; | 712 | struct s3c2410_nand_mtd *ptr = info->mtds; |
717 | int mtdno; | 713 | int mtdno; |
718 | 714 | ||
719 | for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { | 715 | for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { |
720 | pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); | 716 | pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); |
721 | nand_release(&ptr->mtd); | 717 | nand_release(&ptr->mtd); |
722 | } | 718 | } |
723 | 719 | ||
724 | kfree(info->mtds); | 720 | kfree(info->mtds); |
725 | } | 721 | } |
726 | 722 | ||
727 | /* free the common resources */ | 723 | /* free the common resources */ |
728 | 724 | ||
729 | if (!IS_ERR(info->clk)) { | 725 | if (!IS_ERR(info->clk)) { |
730 | s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); | 726 | s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); |
731 | clk_put(info->clk); | 727 | clk_put(info->clk); |
732 | } | 728 | } |
733 | 729 | ||
734 | if (info->regs != NULL) { | 730 | if (info->regs != NULL) { |
735 | iounmap(info->regs); | 731 | iounmap(info->regs); |
736 | info->regs = NULL; | 732 | info->regs = NULL; |
737 | } | 733 | } |
738 | 734 | ||
739 | if (info->area != NULL) { | 735 | if (info->area != NULL) { |
740 | release_resource(info->area); | 736 | release_resource(info->area); |
741 | kfree(info->area); | 737 | kfree(info->area); |
742 | info->area = NULL; | 738 | info->area = NULL; |
743 | } | 739 | } |
744 | 740 | ||
745 | kfree(info); | 741 | kfree(info); |
746 | 742 | ||
747 | return 0; | 743 | return 0; |
748 | } | 744 | } |
749 | 745 | ||
750 | static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, | 746 | static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, |
751 | struct s3c2410_nand_mtd *mtd, | 747 | struct s3c2410_nand_mtd *mtd, |
752 | struct s3c2410_nand_set *set) | 748 | struct s3c2410_nand_set *set) |
753 | { | 749 | { |
754 | if (set) | 750 | if (set) |
755 | mtd->mtd.name = set->name; | 751 | mtd->mtd.name = set->name; |
756 | 752 | ||
757 | return mtd_device_parse_register(&mtd->mtd, NULL, NULL, | 753 | return mtd_device_parse_register(&mtd->mtd, NULL, NULL, |
758 | set->partitions, set->nr_partitions); | 754 | set->partitions, set->nr_partitions); |
759 | } | 755 | } |
760 | 756 | ||
761 | /** | 757 | /** |
762 | * s3c2410_nand_init_chip - initialise a single instance of an chip | 758 | * s3c2410_nand_init_chip - initialise a single instance of an chip |
763 | * @info: The base NAND controller the chip is on. | 759 | * @info: The base NAND controller the chip is on. |
764 | * @nmtd: The new controller MTD instance to fill in. | 760 | * @nmtd: The new controller MTD instance to fill in. |
765 | * @set: The information passed from the board specific platform data. | 761 | * @set: The information passed from the board specific platform data. |
766 | * | 762 | * |
767 | * Initialise the given @nmtd from the information in @info and @set. This | 763 | * Initialise the given @nmtd from the information in @info and @set. This |
768 | * readies the structure for use with the MTD layer functions by ensuring | 764 | * readies the structure for use with the MTD layer functions by ensuring |
769 | * all pointers are setup and the necessary control routines selected. | 765 | * all pointers are setup and the necessary control routines selected. |
770 | */ | 766 | */ |
771 | static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | 767 | static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, |
772 | struct s3c2410_nand_mtd *nmtd, | 768 | struct s3c2410_nand_mtd *nmtd, |
773 | struct s3c2410_nand_set *set) | 769 | struct s3c2410_nand_set *set) |
774 | { | 770 | { |
775 | struct nand_chip *chip = &nmtd->chip; | 771 | struct nand_chip *chip = &nmtd->chip; |
776 | void __iomem *regs = info->regs; | 772 | void __iomem *regs = info->regs; |
777 | 773 | ||
778 | chip->write_buf = s3c2410_nand_write_buf; | 774 | chip->write_buf = s3c2410_nand_write_buf; |
779 | chip->read_buf = s3c2410_nand_read_buf; | 775 | chip->read_buf = s3c2410_nand_read_buf; |
780 | chip->select_chip = s3c2410_nand_select_chip; | 776 | chip->select_chip = s3c2410_nand_select_chip; |
781 | chip->chip_delay = 50; | 777 | chip->chip_delay = 50; |
782 | chip->priv = nmtd; | 778 | chip->priv = nmtd; |
783 | chip->options = set->options; | 779 | chip->options = set->options; |
784 | chip->controller = &info->controller; | 780 | chip->controller = &info->controller; |
785 | 781 | ||
786 | switch (info->cpu_type) { | 782 | switch (info->cpu_type) { |
787 | case TYPE_S3C2410: | 783 | case TYPE_S3C2410: |
788 | chip->IO_ADDR_W = regs + S3C2410_NFDATA; | 784 | chip->IO_ADDR_W = regs + S3C2410_NFDATA; |
789 | info->sel_reg = regs + S3C2410_NFCONF; | 785 | info->sel_reg = regs + S3C2410_NFCONF; |
790 | info->sel_bit = S3C2410_NFCONF_nFCE; | 786 | info->sel_bit = S3C2410_NFCONF_nFCE; |
791 | chip->cmd_ctrl = s3c2410_nand_hwcontrol; | 787 | chip->cmd_ctrl = s3c2410_nand_hwcontrol; |
792 | chip->dev_ready = s3c2410_nand_devready; | 788 | chip->dev_ready = s3c2410_nand_devready; |
793 | break; | 789 | break; |
794 | 790 | ||
795 | case TYPE_S3C2440: | 791 | case TYPE_S3C2440: |
796 | chip->IO_ADDR_W = regs + S3C2440_NFDATA; | 792 | chip->IO_ADDR_W = regs + S3C2440_NFDATA; |
797 | info->sel_reg = regs + S3C2440_NFCONT; | 793 | info->sel_reg = regs + S3C2440_NFCONT; |
798 | info->sel_bit = S3C2440_NFCONT_nFCE; | 794 | info->sel_bit = S3C2440_NFCONT_nFCE; |
799 | chip->cmd_ctrl = s3c2440_nand_hwcontrol; | 795 | chip->cmd_ctrl = s3c2440_nand_hwcontrol; |
800 | chip->dev_ready = s3c2440_nand_devready; | 796 | chip->dev_ready = s3c2440_nand_devready; |
801 | chip->read_buf = s3c2440_nand_read_buf; | 797 | chip->read_buf = s3c2440_nand_read_buf; |
802 | chip->write_buf = s3c2440_nand_write_buf; | 798 | chip->write_buf = s3c2440_nand_write_buf; |
803 | break; | 799 | break; |
804 | 800 | ||
805 | case TYPE_S3C2412: | 801 | case TYPE_S3C2412: |
806 | chip->IO_ADDR_W = regs + S3C2440_NFDATA; | 802 | chip->IO_ADDR_W = regs + S3C2440_NFDATA; |
807 | info->sel_reg = regs + S3C2440_NFCONT; | 803 | info->sel_reg = regs + S3C2440_NFCONT; |
808 | info->sel_bit = S3C2412_NFCONT_nFCE0; | 804 | info->sel_bit = S3C2412_NFCONT_nFCE0; |
809 | chip->cmd_ctrl = s3c2440_nand_hwcontrol; | 805 | chip->cmd_ctrl = s3c2440_nand_hwcontrol; |
810 | chip->dev_ready = s3c2412_nand_devready; | 806 | chip->dev_ready = s3c2412_nand_devready; |
811 | 807 | ||
812 | if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT) | 808 | if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT) |
813 | dev_info(info->device, "System booted from NAND\n"); | 809 | dev_info(info->device, "System booted from NAND\n"); |
814 | 810 | ||
815 | break; | 811 | break; |
816 | } | 812 | } |
817 | 813 | ||
818 | chip->IO_ADDR_R = chip->IO_ADDR_W; | 814 | chip->IO_ADDR_R = chip->IO_ADDR_W; |
819 | 815 | ||
820 | nmtd->info = info; | 816 | nmtd->info = info; |
821 | nmtd->mtd.priv = chip; | 817 | nmtd->mtd.priv = chip; |
822 | nmtd->mtd.owner = THIS_MODULE; | 818 | nmtd->mtd.owner = THIS_MODULE; |
823 | nmtd->set = set; | 819 | nmtd->set = set; |
824 | 820 | ||
825 | #ifdef CONFIG_MTD_NAND_S3C2410_HWECC | 821 | #ifdef CONFIG_MTD_NAND_S3C2410_HWECC |
826 | chip->ecc.calculate = s3c2410_nand_calculate_ecc; | 822 | chip->ecc.calculate = s3c2410_nand_calculate_ecc; |
827 | chip->ecc.correct = s3c2410_nand_correct_data; | 823 | chip->ecc.correct = s3c2410_nand_correct_data; |
828 | chip->ecc.mode = NAND_ECC_HW; | 824 | chip->ecc.mode = NAND_ECC_HW; |
829 | chip->ecc.strength = 1; | 825 | chip->ecc.strength = 1; |
830 | 826 | ||
831 | switch (info->cpu_type) { | 827 | switch (info->cpu_type) { |
832 | case TYPE_S3C2410: | 828 | case TYPE_S3C2410: |
833 | chip->ecc.hwctl = s3c2410_nand_enable_hwecc; | 829 | chip->ecc.hwctl = s3c2410_nand_enable_hwecc; |
834 | chip->ecc.calculate = s3c2410_nand_calculate_ecc; | 830 | chip->ecc.calculate = s3c2410_nand_calculate_ecc; |
835 | break; | 831 | break; |
836 | 832 | ||
837 | case TYPE_S3C2412: | 833 | case TYPE_S3C2412: |
838 | chip->ecc.hwctl = s3c2412_nand_enable_hwecc; | 834 | chip->ecc.hwctl = s3c2412_nand_enable_hwecc; |
839 | chip->ecc.calculate = s3c2412_nand_calculate_ecc; | 835 | chip->ecc.calculate = s3c2412_nand_calculate_ecc; |
840 | break; | 836 | break; |
841 | 837 | ||
842 | case TYPE_S3C2440: | 838 | case TYPE_S3C2440: |
843 | chip->ecc.hwctl = s3c2440_nand_enable_hwecc; | 839 | chip->ecc.hwctl = s3c2440_nand_enable_hwecc; |
844 | chip->ecc.calculate = s3c2440_nand_calculate_ecc; | 840 | chip->ecc.calculate = s3c2440_nand_calculate_ecc; |
845 | break; | 841 | break; |
846 | } | 842 | } |
847 | #else | 843 | #else |
848 | chip->ecc.mode = NAND_ECC_SOFT; | 844 | chip->ecc.mode = NAND_ECC_SOFT; |
849 | #endif | 845 | #endif |
850 | 846 | ||
851 | if (set->ecc_layout != NULL) | 847 | if (set->ecc_layout != NULL) |
852 | chip->ecc.layout = set->ecc_layout; | 848 | chip->ecc.layout = set->ecc_layout; |
853 | 849 | ||
854 | if (set->disable_ecc) | 850 | if (set->disable_ecc) |
855 | chip->ecc.mode = NAND_ECC_NONE; | 851 | chip->ecc.mode = NAND_ECC_NONE; |
856 | 852 | ||
857 | switch (chip->ecc.mode) { | 853 | switch (chip->ecc.mode) { |
858 | case NAND_ECC_NONE: | 854 | case NAND_ECC_NONE: |
859 | dev_info(info->device, "NAND ECC disabled\n"); | 855 | dev_info(info->device, "NAND ECC disabled\n"); |
860 | break; | 856 | break; |
861 | case NAND_ECC_SOFT: | 857 | case NAND_ECC_SOFT: |
862 | dev_info(info->device, "NAND soft ECC\n"); | 858 | dev_info(info->device, "NAND soft ECC\n"); |
863 | break; | 859 | break; |
864 | case NAND_ECC_HW: | 860 | case NAND_ECC_HW: |
865 | dev_info(info->device, "NAND hardware ECC\n"); | 861 | dev_info(info->device, "NAND hardware ECC\n"); |
866 | break; | 862 | break; |
867 | default: | 863 | default: |
868 | dev_info(info->device, "NAND ECC UNKNOWN\n"); | 864 | dev_info(info->device, "NAND ECC UNKNOWN\n"); |
869 | break; | 865 | break; |
870 | } | 866 | } |
871 | 867 | ||
872 | /* If you use u-boot BBT creation code, specifying this flag will | 868 | /* If you use u-boot BBT creation code, specifying this flag will |
873 | * let the kernel fish out the BBT from the NAND, and also skip the | 869 | * let the kernel fish out the BBT from the NAND, and also skip the |
874 | * full NAND scan that can take 1/2s or so. Little things... */ | 870 | * full NAND scan that can take 1/2s or so. Little things... */ |
875 | if (set->flash_bbt) { | 871 | if (set->flash_bbt) { |
876 | chip->bbt_options |= NAND_BBT_USE_FLASH; | 872 | chip->bbt_options |= NAND_BBT_USE_FLASH; |
877 | chip->options |= NAND_SKIP_BBTSCAN; | 873 | chip->options |= NAND_SKIP_BBTSCAN; |
878 | } | 874 | } |
879 | } | 875 | } |
880 | 876 | ||
881 | /** | 877 | /** |
882 | * s3c2410_nand_update_chip - post probe update | 878 | * s3c2410_nand_update_chip - post probe update |
883 | * @info: The controller instance. | 879 | * @info: The controller instance. |
884 | * @nmtd: The driver version of the MTD instance. | 880 | * @nmtd: The driver version of the MTD instance. |
885 | * | 881 | * |
886 | * This routine is called after the chip probe has successfully completed | 882 | * This routine is called after the chip probe has successfully completed |
887 | * and the relevant per-chip information updated. This call ensure that | 883 | * and the relevant per-chip information updated. This call ensure that |
888 | * we update the internal state accordingly. | 884 | * we update the internal state accordingly. |
889 | * | 885 | * |
890 | * The internal state is currently limited to the ECC state information. | 886 | * The internal state is currently limited to the ECC state information. |
891 | */ | 887 | */ |
892 | static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, | 888 | static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, |
893 | struct s3c2410_nand_mtd *nmtd) | 889 | struct s3c2410_nand_mtd *nmtd) |
894 | { | 890 | { |
895 | struct nand_chip *chip = &nmtd->chip; | 891 | struct nand_chip *chip = &nmtd->chip; |
896 | 892 | ||
897 | dev_dbg(info->device, "chip %p => page shift %d\n", | 893 | dev_dbg(info->device, "chip %p => page shift %d\n", |
898 | chip, chip->page_shift); | 894 | chip, chip->page_shift); |
899 | 895 | ||
900 | if (chip->ecc.mode != NAND_ECC_HW) | 896 | if (chip->ecc.mode != NAND_ECC_HW) |
901 | return; | 897 | return; |
902 | 898 | ||
903 | /* change the behaviour depending on wether we are using | 899 | /* change the behaviour depending on wether we are using |
904 | * the large or small page nand device */ | 900 | * the large or small page nand device */ |
905 | 901 | ||
906 | if (chip->page_shift > 10) { | 902 | if (chip->page_shift > 10) { |
907 | chip->ecc.size = 256; | 903 | chip->ecc.size = 256; |
908 | chip->ecc.bytes = 3; | 904 | chip->ecc.bytes = 3; |
909 | } else { | 905 | } else { |
910 | chip->ecc.size = 512; | 906 | chip->ecc.size = 512; |
911 | chip->ecc.bytes = 3; | 907 | chip->ecc.bytes = 3; |
912 | chip->ecc.layout = &nand_hw_eccoob; | 908 | chip->ecc.layout = &nand_hw_eccoob; |
913 | } | 909 | } |
914 | } | 910 | } |
915 | 911 | ||
916 | /* s3c24xx_nand_probe | 912 | /* s3c24xx_nand_probe |
917 | * | 913 | * |
918 | * called by device layer when it finds a device matching | 914 | * called by device layer when it finds a device matching |
919 | * one our driver can handled. This code checks to see if | 915 | * one our driver can handled. This code checks to see if |
920 | * it can allocate all necessary resources then calls the | 916 | * it can allocate all necessary resources then calls the |
921 | * nand layer to look for devices | 917 | * nand layer to look for devices |
922 | */ | 918 | */ |
923 | static int s3c24xx_nand_probe(struct platform_device *pdev) | 919 | static int s3c24xx_nand_probe(struct platform_device *pdev) |
924 | { | 920 | { |
925 | struct s3c2410_platform_nand *plat = to_nand_plat(pdev); | 921 | struct s3c2410_platform_nand *plat = to_nand_plat(pdev); |
926 | enum s3c_cpu_type cpu_type; | 922 | enum s3c_cpu_type cpu_type; |
927 | struct s3c2410_nand_info *info; | 923 | struct s3c2410_nand_info *info; |
928 | struct s3c2410_nand_mtd *nmtd; | 924 | struct s3c2410_nand_mtd *nmtd; |
929 | struct s3c2410_nand_set *sets; | 925 | struct s3c2410_nand_set *sets; |
930 | struct resource *res; | 926 | struct resource *res; |
931 | int err = 0; | 927 | int err = 0; |
932 | int size; | 928 | int size; |
933 | int nr_sets; | 929 | int nr_sets; |
934 | int setno; | 930 | int setno; |
935 | 931 | ||
936 | cpu_type = platform_get_device_id(pdev)->driver_data; | 932 | cpu_type = platform_get_device_id(pdev)->driver_data; |
937 | 933 | ||
938 | pr_debug("s3c2410_nand_probe(%p)\n", pdev); | 934 | pr_debug("s3c2410_nand_probe(%p)\n", pdev); |
939 | 935 | ||
940 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 936 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
941 | if (info == NULL) { | 937 | if (info == NULL) { |
942 | dev_err(&pdev->dev, "no memory for flash info\n"); | 938 | dev_err(&pdev->dev, "no memory for flash info\n"); |
943 | err = -ENOMEM; | 939 | err = -ENOMEM; |
944 | goto exit_error; | 940 | goto exit_error; |
945 | } | 941 | } |
946 | 942 | ||
947 | platform_set_drvdata(pdev, info); | 943 | platform_set_drvdata(pdev, info); |
948 | 944 | ||
949 | spin_lock_init(&info->controller.lock); | 945 | spin_lock_init(&info->controller.lock); |
950 | init_waitqueue_head(&info->controller.wq); | 946 | init_waitqueue_head(&info->controller.wq); |
951 | 947 | ||
952 | /* get the clock source and enable it */ | 948 | /* get the clock source and enable it */ |
953 | 949 | ||
954 | info->clk = clk_get(&pdev->dev, "nand"); | 950 | info->clk = clk_get(&pdev->dev, "nand"); |
955 | if (IS_ERR(info->clk)) { | 951 | if (IS_ERR(info->clk)) { |
956 | dev_err(&pdev->dev, "failed to get clock\n"); | 952 | dev_err(&pdev->dev, "failed to get clock\n"); |
957 | err = -ENOENT; | 953 | err = -ENOENT; |
958 | goto exit_error; | 954 | goto exit_error; |
959 | } | 955 | } |
960 | 956 | ||
961 | s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); | 957 | s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); |
962 | 958 | ||
963 | /* allocate and map the resource */ | 959 | /* allocate and map the resource */ |
964 | 960 | ||
965 | /* currently we assume we have the one resource */ | 961 | /* currently we assume we have the one resource */ |
966 | res = pdev->resource; | 962 | res = pdev->resource; |
967 | size = resource_size(res); | 963 | size = resource_size(res); |
968 | 964 | ||
969 | info->area = request_mem_region(res->start, size, pdev->name); | 965 | info->area = request_mem_region(res->start, size, pdev->name); |
970 | 966 | ||
971 | if (info->area == NULL) { | 967 | if (info->area == NULL) { |
972 | dev_err(&pdev->dev, "cannot reserve register region\n"); | 968 | dev_err(&pdev->dev, "cannot reserve register region\n"); |
973 | err = -ENOENT; | 969 | err = -ENOENT; |
974 | goto exit_error; | 970 | goto exit_error; |
975 | } | 971 | } |
976 | 972 | ||
977 | info->device = &pdev->dev; | 973 | info->device = &pdev->dev; |
978 | info->platform = plat; | 974 | info->platform = plat; |
979 | info->regs = ioremap(res->start, size); | 975 | info->regs = ioremap(res->start, size); |
980 | info->cpu_type = cpu_type; | 976 | info->cpu_type = cpu_type; |
981 | 977 | ||
982 | if (info->regs == NULL) { | 978 | if (info->regs == NULL) { |
983 | dev_err(&pdev->dev, "cannot reserve register region\n"); | 979 | dev_err(&pdev->dev, "cannot reserve register region\n"); |
984 | err = -EIO; | 980 | err = -EIO; |
985 | goto exit_error; | 981 | goto exit_error; |
986 | } | 982 | } |
987 | 983 | ||
988 | dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs); | 984 | dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs); |
989 | 985 | ||
990 | /* initialise the hardware */ | 986 | /* initialise the hardware */ |
991 | 987 | ||
992 | err = s3c2410_nand_inithw(info); | 988 | err = s3c2410_nand_inithw(info); |
993 | if (err != 0) | 989 | if (err != 0) |
994 | goto exit_error; | 990 | goto exit_error; |
995 | 991 | ||
996 | sets = (plat != NULL) ? plat->sets : NULL; | 992 | sets = (plat != NULL) ? plat->sets : NULL; |
997 | nr_sets = (plat != NULL) ? plat->nr_sets : 1; | 993 | nr_sets = (plat != NULL) ? plat->nr_sets : 1; |
998 | 994 | ||
999 | info->mtd_count = nr_sets; | 995 | info->mtd_count = nr_sets; |
1000 | 996 | ||
1001 | /* allocate our information */ | 997 | /* allocate our information */ |
1002 | 998 | ||
1003 | size = nr_sets * sizeof(*info->mtds); | 999 | size = nr_sets * sizeof(*info->mtds); |
1004 | info->mtds = kzalloc(size, GFP_KERNEL); | 1000 | info->mtds = kzalloc(size, GFP_KERNEL); |
1005 | if (info->mtds == NULL) { | 1001 | if (info->mtds == NULL) { |
1006 | dev_err(&pdev->dev, "failed to allocate mtd storage\n"); | 1002 | dev_err(&pdev->dev, "failed to allocate mtd storage\n"); |
1007 | err = -ENOMEM; | 1003 | err = -ENOMEM; |
1008 | goto exit_error; | 1004 | goto exit_error; |
1009 | } | 1005 | } |
1010 | 1006 | ||
1011 | /* initialise all possible chips */ | 1007 | /* initialise all possible chips */ |
1012 | 1008 | ||
1013 | nmtd = info->mtds; | 1009 | nmtd = info->mtds; |
1014 | 1010 | ||
1015 | for (setno = 0; setno < nr_sets; setno++, nmtd++) { | 1011 | for (setno = 0; setno < nr_sets; setno++, nmtd++) { |
1016 | pr_debug("initialising set %d (%p, info %p)\n", | 1012 | pr_debug("initialising set %d (%p, info %p)\n", |
1017 | setno, nmtd, info); | 1013 | setno, nmtd, info); |
1018 | 1014 | ||
1019 | s3c2410_nand_init_chip(info, nmtd, sets); | 1015 | s3c2410_nand_init_chip(info, nmtd, sets); |
1020 | 1016 | ||
1021 | nmtd->scan_res = nand_scan_ident(&nmtd->mtd, | 1017 | nmtd->scan_res = nand_scan_ident(&nmtd->mtd, |
1022 | (sets) ? sets->nr_chips : 1, | 1018 | (sets) ? sets->nr_chips : 1, |
1023 | NULL); | 1019 | NULL); |
1024 | 1020 | ||
1025 | if (nmtd->scan_res == 0) { | 1021 | if (nmtd->scan_res == 0) { |
1026 | s3c2410_nand_update_chip(info, nmtd); | 1022 | s3c2410_nand_update_chip(info, nmtd); |
1027 | nand_scan_tail(&nmtd->mtd); | 1023 | nand_scan_tail(&nmtd->mtd); |
1028 | s3c2410_nand_add_partition(info, nmtd, sets); | 1024 | s3c2410_nand_add_partition(info, nmtd, sets); |
1029 | } | 1025 | } |
1030 | 1026 | ||
1031 | if (sets != NULL) | 1027 | if (sets != NULL) |
1032 | sets++; | 1028 | sets++; |
1033 | } | 1029 | } |
1034 | 1030 | ||
1035 | err = s3c2410_nand_cpufreq_register(info); | 1031 | err = s3c2410_nand_cpufreq_register(info); |
1036 | if (err < 0) { | 1032 | if (err < 0) { |
1037 | dev_err(&pdev->dev, "failed to init cpufreq support\n"); | 1033 | dev_err(&pdev->dev, "failed to init cpufreq support\n"); |
1038 | goto exit_error; | 1034 | goto exit_error; |
1039 | } | 1035 | } |
1040 | 1036 | ||
1041 | if (allow_clk_suspend(info)) { | 1037 | if (allow_clk_suspend(info)) { |
1042 | dev_info(&pdev->dev, "clock idle support enabled\n"); | 1038 | dev_info(&pdev->dev, "clock idle support enabled\n"); |
1043 | s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); | 1039 | s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); |
1044 | } | 1040 | } |
1045 | 1041 | ||
1046 | pr_debug("initialised ok\n"); | 1042 | pr_debug("initialised ok\n"); |
1047 | return 0; | 1043 | return 0; |
1048 | 1044 | ||
1049 | exit_error: | 1045 | exit_error: |
1050 | s3c24xx_nand_remove(pdev); | 1046 | s3c24xx_nand_remove(pdev); |
1051 | 1047 | ||
1052 | if (err == 0) | 1048 | if (err == 0) |
1053 | err = -EINVAL; | 1049 | err = -EINVAL; |
1054 | return err; | 1050 | return err; |
1055 | } | 1051 | } |
1056 | 1052 | ||
1057 | /* PM Support */ | 1053 | /* PM Support */ |
1058 | #ifdef CONFIG_PM | 1054 | #ifdef CONFIG_PM |
1059 | 1055 | ||
1060 | static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) | 1056 | static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) |
1061 | { | 1057 | { |
1062 | struct s3c2410_nand_info *info = platform_get_drvdata(dev); | 1058 | struct s3c2410_nand_info *info = platform_get_drvdata(dev); |
1063 | 1059 | ||
1064 | if (info) { | 1060 | if (info) { |
1065 | info->save_sel = readl(info->sel_reg); | 1061 | info->save_sel = readl(info->sel_reg); |
1066 | 1062 | ||
1067 | /* For the moment, we must ensure nFCE is high during | 1063 | /* For the moment, we must ensure nFCE is high during |
1068 | * the time we are suspended. This really should be | 1064 | * the time we are suspended. This really should be |
1069 | * handled by suspending the MTDs we are using, but | 1065 | * handled by suspending the MTDs we are using, but |
1070 | * that is currently not the case. */ | 1066 | * that is currently not the case. */ |
1071 | 1067 | ||
1072 | writel(info->save_sel | info->sel_bit, info->sel_reg); | 1068 | writel(info->save_sel | info->sel_bit, info->sel_reg); |
1073 | 1069 | ||
1074 | s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); | 1070 | s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); |
1075 | } | 1071 | } |
1076 | 1072 | ||
1077 | return 0; | 1073 | return 0; |
1078 | } | 1074 | } |
1079 | 1075 | ||
1080 | static int s3c24xx_nand_resume(struct platform_device *dev) | 1076 | static int s3c24xx_nand_resume(struct platform_device *dev) |
1081 | { | 1077 | { |
1082 | struct s3c2410_nand_info *info = platform_get_drvdata(dev); | 1078 | struct s3c2410_nand_info *info = platform_get_drvdata(dev); |
1083 | unsigned long sel; | 1079 | unsigned long sel; |
1084 | 1080 | ||
1085 | if (info) { | 1081 | if (info) { |
1086 | s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); | 1082 | s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); |
1087 | s3c2410_nand_inithw(info); | 1083 | s3c2410_nand_inithw(info); |
1088 | 1084 | ||
1089 | /* Restore the state of the nFCE line. */ | 1085 | /* Restore the state of the nFCE line. */ |
1090 | 1086 | ||
1091 | sel = readl(info->sel_reg); | 1087 | sel = readl(info->sel_reg); |
1092 | sel &= ~info->sel_bit; | 1088 | sel &= ~info->sel_bit; |
1093 | sel |= info->save_sel & info->sel_bit; | 1089 | sel |= info->save_sel & info->sel_bit; |
1094 | writel(sel, info->sel_reg); | 1090 | writel(sel, info->sel_reg); |
1095 | 1091 | ||
1096 | s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); | 1092 | s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); |
1097 | } | 1093 | } |
1098 | 1094 | ||
1099 | return 0; | 1095 | return 0; |
1100 | } | 1096 | } |
1101 | 1097 | ||
1102 | #else | 1098 | #else |
1103 | #define s3c24xx_nand_suspend NULL | 1099 | #define s3c24xx_nand_suspend NULL |
1104 | #define s3c24xx_nand_resume NULL | 1100 | #define s3c24xx_nand_resume NULL |
1105 | #endif | 1101 | #endif |
1106 | 1102 | ||
1107 | /* driver device registration */ | 1103 | /* driver device registration */ |
1108 | 1104 | ||
1109 | static struct platform_device_id s3c24xx_driver_ids[] = { | 1105 | static struct platform_device_id s3c24xx_driver_ids[] = { |
1110 | { | 1106 | { |
1111 | .name = "s3c2410-nand", | 1107 | .name = "s3c2410-nand", |
1112 | .driver_data = TYPE_S3C2410, | 1108 | .driver_data = TYPE_S3C2410, |
1113 | }, { | 1109 | }, { |
1114 | .name = "s3c2440-nand", | 1110 | .name = "s3c2440-nand", |
1115 | .driver_data = TYPE_S3C2440, | 1111 | .driver_data = TYPE_S3C2440, |
1116 | }, { | 1112 | }, { |
1117 | .name = "s3c2412-nand", | 1113 | .name = "s3c2412-nand", |
1118 | .driver_data = TYPE_S3C2412, | 1114 | .driver_data = TYPE_S3C2412, |
1119 | }, { | 1115 | }, { |
1120 | .name = "s3c6400-nand", | 1116 | .name = "s3c6400-nand", |
1121 | .driver_data = TYPE_S3C2412, /* compatible with 2412 */ | 1117 | .driver_data = TYPE_S3C2412, /* compatible with 2412 */ |
1122 | }, | 1118 | }, |
1123 | { } | 1119 | { } |
1124 | }; | 1120 | }; |
1125 | 1121 | ||
1126 | MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); | 1122 | MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); |
1127 | 1123 | ||
1128 | static struct platform_driver s3c24xx_nand_driver = { | 1124 | static struct platform_driver s3c24xx_nand_driver = { |
1129 | .probe = s3c24xx_nand_probe, | 1125 | .probe = s3c24xx_nand_probe, |
1130 | .remove = s3c24xx_nand_remove, | 1126 | .remove = s3c24xx_nand_remove, |
1131 | .suspend = s3c24xx_nand_suspend, | 1127 | .suspend = s3c24xx_nand_suspend, |
1132 | .resume = s3c24xx_nand_resume, | 1128 | .resume = s3c24xx_nand_resume, |
1133 | .id_table = s3c24xx_driver_ids, | 1129 | .id_table = s3c24xx_driver_ids, |
1134 | .driver = { | 1130 | .driver = { |
1135 | .name = "s3c24xx-nand", | 1131 | .name = "s3c24xx-nand", |
1136 | .owner = THIS_MODULE, | 1132 | .owner = THIS_MODULE, |
1137 | }, | 1133 | }, |
1138 | }; | 1134 | }; |
1139 | 1135 | ||
1140 | module_platform_driver(s3c24xx_nand_driver); | 1136 | module_platform_driver(s3c24xx_nand_driver); |
1141 | 1137 | ||
1142 | MODULE_LICENSE("GPL"); | 1138 | MODULE_LICENSE("GPL"); |
1143 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); | 1139 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); |
1144 | MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); | 1140 | MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); |
1145 | 1141 |