diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /include/linux/mtd/cfi.h |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'include/linux/mtd/cfi.h')
-rw-r--r-- | include/linux/mtd/cfi.h | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h new file mode 100644 index 000000000000..2ed8c585021e --- /dev/null +++ b/include/linux/mtd/cfi.h | |||
@@ -0,0 +1,394 @@ | |||
1 | |||
2 | /* Common Flash Interface structures | ||
3 | * See http://support.intel.com/design/flash/technote/index.htm | ||
4 | * $Id: cfi.h,v 1.50 2004/11/20 12:46:51 dwmw2 Exp $ | ||
5 | */ | ||
6 | |||
7 | #ifndef __MTD_CFI_H__ | ||
8 | #define __MTD_CFI_H__ | ||
9 | |||
10 | #include <linux/config.h> | ||
11 | #include <linux/version.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/mtd/flashchip.h> | ||
16 | #include <linux/mtd/map.h> | ||
17 | #include <linux/mtd/cfi_endian.h> | ||
18 | |||
19 | #ifdef CONFIG_MTD_CFI_I1 | ||
20 | #define cfi_interleave(cfi) 1 | ||
21 | #define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1) | ||
22 | #else | ||
23 | #define cfi_interleave_is_1(cfi) (0) | ||
24 | #endif | ||
25 | |||
26 | #ifdef CONFIG_MTD_CFI_I2 | ||
27 | # ifdef cfi_interleave | ||
28 | # undef cfi_interleave | ||
29 | # define cfi_interleave(cfi) ((cfi)->interleave) | ||
30 | # else | ||
31 | # define cfi_interleave(cfi) 2 | ||
32 | # endif | ||
33 | #define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2) | ||
34 | #else | ||
35 | #define cfi_interleave_is_2(cfi) (0) | ||
36 | #endif | ||
37 | |||
38 | #ifdef CONFIG_MTD_CFI_I4 | ||
39 | # ifdef cfi_interleave | ||
40 | # undef cfi_interleave | ||
41 | # define cfi_interleave(cfi) ((cfi)->interleave) | ||
42 | # else | ||
43 | # define cfi_interleave(cfi) 4 | ||
44 | # endif | ||
45 | #define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4) | ||
46 | #else | ||
47 | #define cfi_interleave_is_4(cfi) (0) | ||
48 | #endif | ||
49 | |||
50 | #ifdef CONFIG_MTD_CFI_I8 | ||
51 | # ifdef cfi_interleave | ||
52 | # undef cfi_interleave | ||
53 | # define cfi_interleave(cfi) ((cfi)->interleave) | ||
54 | # else | ||
55 | # define cfi_interleave(cfi) 8 | ||
56 | # endif | ||
57 | #define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8) | ||
58 | #else | ||
59 | #define cfi_interleave_is_8(cfi) (0) | ||
60 | #endif | ||
61 | |||
62 | static inline int cfi_interleave_supported(int i) | ||
63 | { | ||
64 | switch (i) { | ||
65 | #ifdef CONFIG_MTD_CFI_I1 | ||
66 | case 1: | ||
67 | #endif | ||
68 | #ifdef CONFIG_MTD_CFI_I2 | ||
69 | case 2: | ||
70 | #endif | ||
71 | #ifdef CONFIG_MTD_CFI_I4 | ||
72 | case 4: | ||
73 | #endif | ||
74 | #ifdef CONFIG_MTD_CFI_I8 | ||
75 | case 8: | ||
76 | #endif | ||
77 | return 1; | ||
78 | |||
79 | default: | ||
80 | return 0; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | |||
85 | /* NB: these values must represents the number of bytes needed to meet the | ||
86 | * device type (x8, x16, x32). Eg. a 32 bit device is 4 x 8 bytes. | ||
87 | * These numbers are used in calculations. | ||
88 | */ | ||
89 | #define CFI_DEVICETYPE_X8 (8 / 8) | ||
90 | #define CFI_DEVICETYPE_X16 (16 / 8) | ||
91 | #define CFI_DEVICETYPE_X32 (32 / 8) | ||
92 | #define CFI_DEVICETYPE_X64 (64 / 8) | ||
93 | |||
94 | /* NB: We keep these structures in memory in HOST byteorder, except | ||
95 | * where individually noted. | ||
96 | */ | ||
97 | |||
98 | /* Basic Query Structure */ | ||
99 | struct cfi_ident { | ||
100 | uint8_t qry[3]; | ||
101 | uint16_t P_ID; | ||
102 | uint16_t P_ADR; | ||
103 | uint16_t A_ID; | ||
104 | uint16_t A_ADR; | ||
105 | uint8_t VccMin; | ||
106 | uint8_t VccMax; | ||
107 | uint8_t VppMin; | ||
108 | uint8_t VppMax; | ||
109 | uint8_t WordWriteTimeoutTyp; | ||
110 | uint8_t BufWriteTimeoutTyp; | ||
111 | uint8_t BlockEraseTimeoutTyp; | ||
112 | uint8_t ChipEraseTimeoutTyp; | ||
113 | uint8_t WordWriteTimeoutMax; | ||
114 | uint8_t BufWriteTimeoutMax; | ||
115 | uint8_t BlockEraseTimeoutMax; | ||
116 | uint8_t ChipEraseTimeoutMax; | ||
117 | uint8_t DevSize; | ||
118 | uint16_t InterfaceDesc; | ||
119 | uint16_t MaxBufWriteSize; | ||
120 | uint8_t NumEraseRegions; | ||
121 | uint32_t EraseRegionInfo[0]; /* Not host ordered */ | ||
122 | } __attribute__((packed)); | ||
123 | |||
124 | /* Extended Query Structure for both PRI and ALT */ | ||
125 | |||
126 | struct cfi_extquery { | ||
127 | uint8_t pri[3]; | ||
128 | uint8_t MajorVersion; | ||
129 | uint8_t MinorVersion; | ||
130 | } __attribute__((packed)); | ||
131 | |||
132 | /* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */ | ||
133 | |||
134 | struct cfi_pri_intelext { | ||
135 | uint8_t pri[3]; | ||
136 | uint8_t MajorVersion; | ||
137 | uint8_t MinorVersion; | ||
138 | uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature | ||
139 | block follows - FIXME - not currently supported */ | ||
140 | uint8_t SuspendCmdSupport; | ||
141 | uint16_t BlkStatusRegMask; | ||
142 | uint8_t VccOptimal; | ||
143 | uint8_t VppOptimal; | ||
144 | uint8_t NumProtectionFields; | ||
145 | uint16_t ProtRegAddr; | ||
146 | uint8_t FactProtRegSize; | ||
147 | uint8_t UserProtRegSize; | ||
148 | uint8_t extra[0]; | ||
149 | } __attribute__((packed)); | ||
150 | |||
151 | struct cfi_intelext_blockinfo { | ||
152 | uint16_t NumIdentBlocks; | ||
153 | uint16_t BlockSize; | ||
154 | uint16_t MinBlockEraseCycles; | ||
155 | uint8_t BitsPerCell; | ||
156 | uint8_t BlockCap; | ||
157 | } __attribute__((packed)); | ||
158 | |||
159 | struct cfi_intelext_regioninfo { | ||
160 | uint16_t NumIdentPartitions; | ||
161 | uint8_t NumOpAllowed; | ||
162 | uint8_t NumOpAllowedSimProgMode; | ||
163 | uint8_t NumOpAllowedSimEraMode; | ||
164 | uint8_t NumBlockTypes; | ||
165 | struct cfi_intelext_blockinfo BlockTypes[1]; | ||
166 | } __attribute__((packed)); | ||
167 | |||
168 | /* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */ | ||
169 | |||
170 | struct cfi_pri_amdstd { | ||
171 | uint8_t pri[3]; | ||
172 | uint8_t MajorVersion; | ||
173 | uint8_t MinorVersion; | ||
174 | uint8_t SiliconRevision; /* bits 1-0: Address Sensitive Unlock */ | ||
175 | uint8_t EraseSuspend; | ||
176 | uint8_t BlkProt; | ||
177 | uint8_t TmpBlkUnprotect; | ||
178 | uint8_t BlkProtUnprot; | ||
179 | uint8_t SimultaneousOps; | ||
180 | uint8_t BurstMode; | ||
181 | uint8_t PageMode; | ||
182 | uint8_t VppMin; | ||
183 | uint8_t VppMax; | ||
184 | uint8_t TopBottom; | ||
185 | } __attribute__((packed)); | ||
186 | |||
187 | struct cfi_pri_query { | ||
188 | uint8_t NumFields; | ||
189 | uint32_t ProtField[1]; /* Not host ordered */ | ||
190 | } __attribute__((packed)); | ||
191 | |||
192 | struct cfi_bri_query { | ||
193 | uint8_t PageModeReadCap; | ||
194 | uint8_t NumFields; | ||
195 | uint32_t ConfField[1]; /* Not host ordered */ | ||
196 | } __attribute__((packed)); | ||
197 | |||
198 | #define P_ID_NONE 0x0000 | ||
199 | #define P_ID_INTEL_EXT 0x0001 | ||
200 | #define P_ID_AMD_STD 0x0002 | ||
201 | #define P_ID_INTEL_STD 0x0003 | ||
202 | #define P_ID_AMD_EXT 0x0004 | ||
203 | #define P_ID_WINBOND 0x0006 | ||
204 | #define P_ID_ST_ADV 0x0020 | ||
205 | #define P_ID_MITSUBISHI_STD 0x0100 | ||
206 | #define P_ID_MITSUBISHI_EXT 0x0101 | ||
207 | #define P_ID_SST_PAGE 0x0102 | ||
208 | #define P_ID_INTEL_PERFORMANCE 0x0200 | ||
209 | #define P_ID_INTEL_DATA 0x0210 | ||
210 | #define P_ID_RESERVED 0xffff | ||
211 | |||
212 | |||
213 | #define CFI_MODE_CFI 1 | ||
214 | #define CFI_MODE_JEDEC 0 | ||
215 | |||
216 | struct cfi_private { | ||
217 | uint16_t cmdset; | ||
218 | void *cmdset_priv; | ||
219 | int interleave; | ||
220 | int device_type; | ||
221 | int cfi_mode; /* Are we a JEDEC device pretending to be CFI? */ | ||
222 | int addr_unlock1; | ||
223 | int addr_unlock2; | ||
224 | struct mtd_info *(*cmdset_setup)(struct map_info *); | ||
225 | struct cfi_ident *cfiq; /* For now only one. We insist that all devs | ||
226 | must be of the same type. */ | ||
227 | int mfr, id; | ||
228 | int numchips; | ||
229 | unsigned long chipshift; /* Because they're of the same type */ | ||
230 | const char *im_name; /* inter_module name for cmdset_setup */ | ||
231 | struct flchip chips[0]; /* per-chip data structure for each chip */ | ||
232 | }; | ||
233 | |||
234 | /* | ||
235 | * Returns the command address according to the given geometry. | ||
236 | */ | ||
237 | static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type) | ||
238 | { | ||
239 | return (cmd_ofs * type) * interleave; | ||
240 | } | ||
241 | |||
242 | /* | ||
243 | * Transforms the CFI command for the given geometry (bus width & interleave). | ||
244 | * It looks too long to be inline, but in the common case it should almost all | ||
245 | * get optimised away. | ||
246 | */ | ||
247 | static inline map_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi) | ||
248 | { | ||
249 | map_word val = { {0} }; | ||
250 | int wordwidth, words_per_bus, chip_mode, chips_per_word; | ||
251 | unsigned long onecmd; | ||
252 | int i; | ||
253 | |||
254 | /* We do it this way to give the compiler a fighting chance | ||
255 | of optimising away all the crap for 'bankwidth' larger than | ||
256 | an unsigned long, in the common case where that support is | ||
257 | disabled */ | ||
258 | if (map_bankwidth_is_large(map)) { | ||
259 | wordwidth = sizeof(unsigned long); | ||
260 | words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1 | ||
261 | } else { | ||
262 | wordwidth = map_bankwidth(map); | ||
263 | words_per_bus = 1; | ||
264 | } | ||
265 | |||
266 | chip_mode = map_bankwidth(map) / cfi_interleave(cfi); | ||
267 | chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map); | ||
268 | |||
269 | /* First, determine what the bit-pattern should be for a single | ||
270 | device, according to chip mode and endianness... */ | ||
271 | switch (chip_mode) { | ||
272 | default: BUG(); | ||
273 | case 1: | ||
274 | onecmd = cmd; | ||
275 | break; | ||
276 | case 2: | ||
277 | onecmd = cpu_to_cfi16(cmd); | ||
278 | break; | ||
279 | case 4: | ||
280 | onecmd = cpu_to_cfi32(cmd); | ||
281 | break; | ||
282 | } | ||
283 | |||
284 | /* Now replicate it across the size of an unsigned long, or | ||
285 | just to the bus width as appropriate */ | ||
286 | switch (chips_per_word) { | ||
287 | default: BUG(); | ||
288 | #if BITS_PER_LONG >= 64 | ||
289 | case 8: | ||
290 | onecmd |= (onecmd << (chip_mode * 32)); | ||
291 | #endif | ||
292 | case 4: | ||
293 | onecmd |= (onecmd << (chip_mode * 16)); | ||
294 | case 2: | ||
295 | onecmd |= (onecmd << (chip_mode * 8)); | ||
296 | case 1: | ||
297 | ; | ||
298 | } | ||
299 | |||
300 | /* And finally, for the multi-word case, replicate it | ||
301 | in all words in the structure */ | ||
302 | for (i=0; i < words_per_bus; i++) { | ||
303 | val.x[i] = onecmd; | ||
304 | } | ||
305 | |||
306 | return val; | ||
307 | } | ||
308 | #define CMD(x) cfi_build_cmd((x), map, cfi) | ||
309 | |||
310 | /* | ||
311 | * Sends a CFI command to a bank of flash for the given geometry. | ||
312 | * | ||
313 | * Returns the offset in flash where the command was written. | ||
314 | * If prev_val is non-null, it will be set to the value at the command address, | ||
315 | * before the command was written. | ||
316 | */ | ||
317 | static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base, | ||
318 | struct map_info *map, struct cfi_private *cfi, | ||
319 | int type, map_word *prev_val) | ||
320 | { | ||
321 | map_word val; | ||
322 | uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type); | ||
323 | |||
324 | val = cfi_build_cmd(cmd, map, cfi); | ||
325 | |||
326 | if (prev_val) | ||
327 | *prev_val = map_read(map, addr); | ||
328 | |||
329 | map_write(map, val, addr); | ||
330 | |||
331 | return addr - base; | ||
332 | } | ||
333 | |||
334 | static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr) | ||
335 | { | ||
336 | map_word val = map_read(map, addr); | ||
337 | |||
338 | if (map_bankwidth_is_1(map)) { | ||
339 | return val.x[0]; | ||
340 | } else if (map_bankwidth_is_2(map)) { | ||
341 | return cfi16_to_cpu(val.x[0]); | ||
342 | } else { | ||
343 | /* No point in a 64-bit byteswap since that would just be | ||
344 | swapping the responses from different chips, and we are | ||
345 | only interested in one chip (a representative sample) */ | ||
346 | return cfi32_to_cpu(val.x[0]); | ||
347 | } | ||
348 | } | ||
349 | |||
350 | static inline void cfi_udelay(int us) | ||
351 | { | ||
352 | if (us >= 1000) { | ||
353 | msleep((us+999)/1000); | ||
354 | } else { | ||
355 | udelay(us); | ||
356 | cond_resched(); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | static inline void cfi_spin_lock(spinlock_t *mutex) | ||
361 | { | ||
362 | spin_lock_bh(mutex); | ||
363 | } | ||
364 | |||
365 | static inline void cfi_spin_unlock(spinlock_t *mutex) | ||
366 | { | ||
367 | spin_unlock_bh(mutex); | ||
368 | } | ||
369 | |||
370 | struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size, | ||
371 | const char* name); | ||
372 | struct cfi_fixup { | ||
373 | uint16_t mfr; | ||
374 | uint16_t id; | ||
375 | void (*fixup)(struct mtd_info *mtd, void* param); | ||
376 | void* param; | ||
377 | }; | ||
378 | |||
379 | #define CFI_MFR_ANY 0xffff | ||
380 | #define CFI_ID_ANY 0xffff | ||
381 | |||
382 | #define CFI_MFR_AMD 0x0001 | ||
383 | #define CFI_MFR_ST 0x0020 /* STMicroelectronics */ | ||
384 | |||
385 | void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups); | ||
386 | |||
387 | typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, | ||
388 | unsigned long adr, int len, void *thunk); | ||
389 | |||
390 | int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, | ||
391 | loff_t ofs, size_t len, void *thunk); | ||
392 | |||
393 | |||
394 | #endif /* __MTD_CFI_H__ */ | ||