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 /drivers/mtd/devices/lart.c |
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 'drivers/mtd/devices/lart.c')
-rw-r--r-- | drivers/mtd/devices/lart.c | 711 |
1 files changed, 711 insertions, 0 deletions
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c new file mode 100644 index 000000000000..dfd335e4a2a8 --- /dev/null +++ b/drivers/mtd/devices/lart.c | |||
@@ -0,0 +1,711 @@ | |||
1 | |||
2 | /* | ||
3 | * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. | ||
4 | * | ||
5 | * $Id: lart.c,v 1.7 2004/08/09 13:19:44 dwmw2 Exp $ | ||
6 | * | ||
7 | * Author: Abraham vd Merwe <abraham@2d3d.co.za> | ||
8 | * | ||
9 | * Copyright (c) 2001, 2d3D, Inc. | ||
10 | * | ||
11 | * This code is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | * References: | ||
16 | * | ||
17 | * [1] 3 Volt Fast Boot Block Flash Memory" Intel Datasheet | ||
18 | * - Order Number: 290644-005 | ||
19 | * - January 2000 | ||
20 | * | ||
21 | * [2] MTD internal API documentation | ||
22 | * - http://www.linux-mtd.infradead.org/tech/ | ||
23 | * | ||
24 | * Limitations: | ||
25 | * | ||
26 | * Even though this driver is written for 3 Volt Fast Boot | ||
27 | * Block Flash Memory, it is rather specific to LART. With | ||
28 | * Minor modifications, notably the without data/address line | ||
29 | * mangling and different bus settings, etc. it should be | ||
30 | * trivial to adapt to other platforms. | ||
31 | * | ||
32 | * If somebody would sponsor me a different board, I'll | ||
33 | * adapt the driver (: | ||
34 | */ | ||
35 | |||
36 | /* debugging */ | ||
37 | //#define LART_DEBUG | ||
38 | |||
39 | /* partition support */ | ||
40 | #define HAVE_PARTITIONS | ||
41 | |||
42 | #include <linux/kernel.h> | ||
43 | #include <linux/module.h> | ||
44 | #include <linux/types.h> | ||
45 | #include <linux/init.h> | ||
46 | #include <linux/errno.h> | ||
47 | #include <linux/mtd/mtd.h> | ||
48 | #ifdef HAVE_PARTITIONS | ||
49 | #include <linux/mtd/partitions.h> | ||
50 | #endif | ||
51 | |||
52 | #ifndef CONFIG_SA1100_LART | ||
53 | #error This is for LART architecture only | ||
54 | #endif | ||
55 | |||
56 | static char module_name[] = "lart"; | ||
57 | |||
58 | /* | ||
59 | * These values is specific to 28Fxxxx3 flash memory. | ||
60 | * See section 2.3.1 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet | ||
61 | */ | ||
62 | #define FLASH_BLOCKSIZE_PARAM (4096 * BUSWIDTH) | ||
63 | #define FLASH_NUMBLOCKS_16m_PARAM 8 | ||
64 | #define FLASH_NUMBLOCKS_8m_PARAM 8 | ||
65 | |||
66 | /* | ||
67 | * These values is specific to 28Fxxxx3 flash memory. | ||
68 | * See section 2.3.2 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet | ||
69 | */ | ||
70 | #define FLASH_BLOCKSIZE_MAIN (32768 * BUSWIDTH) | ||
71 | #define FLASH_NUMBLOCKS_16m_MAIN 31 | ||
72 | #define FLASH_NUMBLOCKS_8m_MAIN 15 | ||
73 | |||
74 | /* | ||
75 | * These values are specific to LART | ||
76 | */ | ||
77 | |||
78 | /* general */ | ||
79 | #define BUSWIDTH 4 /* don't change this - a lot of the code _will_ break if you change this */ | ||
80 | #define FLASH_OFFSET 0xe8000000 /* see linux/arch/arm/mach-sa1100/lart.c */ | ||
81 | |||
82 | /* blob */ | ||
83 | #define NUM_BLOB_BLOCKS FLASH_NUMBLOCKS_16m_PARAM | ||
84 | #define BLOB_START 0x00000000 | ||
85 | #define BLOB_LEN (NUM_BLOB_BLOCKS * FLASH_BLOCKSIZE_PARAM) | ||
86 | |||
87 | /* kernel */ | ||
88 | #define NUM_KERNEL_BLOCKS 7 | ||
89 | #define KERNEL_START (BLOB_START + BLOB_LEN) | ||
90 | #define KERNEL_LEN (NUM_KERNEL_BLOCKS * FLASH_BLOCKSIZE_MAIN) | ||
91 | |||
92 | /* initial ramdisk */ | ||
93 | #define NUM_INITRD_BLOCKS 24 | ||
94 | #define INITRD_START (KERNEL_START + KERNEL_LEN) | ||
95 | #define INITRD_LEN (NUM_INITRD_BLOCKS * FLASH_BLOCKSIZE_MAIN) | ||
96 | |||
97 | /* | ||
98 | * See section 4.0 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet | ||
99 | */ | ||
100 | #define READ_ARRAY 0x00FF00FF /* Read Array/Reset */ | ||
101 | #define READ_ID_CODES 0x00900090 /* Read Identifier Codes */ | ||
102 | #define ERASE_SETUP 0x00200020 /* Block Erase */ | ||
103 | #define ERASE_CONFIRM 0x00D000D0 /* Block Erase and Program Resume */ | ||
104 | #define PGM_SETUP 0x00400040 /* Program */ | ||
105 | #define STATUS_READ 0x00700070 /* Read Status Register */ | ||
106 | #define STATUS_CLEAR 0x00500050 /* Clear Status Register */ | ||
107 | #define STATUS_BUSY 0x00800080 /* Write State Machine Status (WSMS) */ | ||
108 | #define STATUS_ERASE_ERR 0x00200020 /* Erase Status (ES) */ | ||
109 | #define STATUS_PGM_ERR 0x00100010 /* Program Status (PS) */ | ||
110 | |||
111 | /* | ||
112 | * See section 4.2 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet | ||
113 | */ | ||
114 | #define FLASH_MANUFACTURER 0x00890089 | ||
115 | #define FLASH_DEVICE_8mbit_TOP 0x88f188f1 | ||
116 | #define FLASH_DEVICE_8mbit_BOTTOM 0x88f288f2 | ||
117 | #define FLASH_DEVICE_16mbit_TOP 0x88f388f3 | ||
118 | #define FLASH_DEVICE_16mbit_BOTTOM 0x88f488f4 | ||
119 | |||
120 | /***************************************************************************************************/ | ||
121 | |||
122 | /* | ||
123 | * The data line mapping on LART is as follows: | ||
124 | * | ||
125 | * U2 CPU | U3 CPU | ||
126 | * ------------------- | ||
127 | * 0 20 | 0 12 | ||
128 | * 1 22 | 1 14 | ||
129 | * 2 19 | 2 11 | ||
130 | * 3 17 | 3 9 | ||
131 | * 4 24 | 4 0 | ||
132 | * 5 26 | 5 2 | ||
133 | * 6 31 | 6 7 | ||
134 | * 7 29 | 7 5 | ||
135 | * 8 21 | 8 13 | ||
136 | * 9 23 | 9 15 | ||
137 | * 10 18 | 10 10 | ||
138 | * 11 16 | 11 8 | ||
139 | * 12 25 | 12 1 | ||
140 | * 13 27 | 13 3 | ||
141 | * 14 30 | 14 6 | ||
142 | * 15 28 | 15 4 | ||
143 | */ | ||
144 | |||
145 | /* Mangle data (x) */ | ||
146 | #define DATA_TO_FLASH(x) \ | ||
147 | ( \ | ||
148 | (((x) & 0x08009000) >> 11) + \ | ||
149 | (((x) & 0x00002000) >> 10) + \ | ||
150 | (((x) & 0x04004000) >> 8) + \ | ||
151 | (((x) & 0x00000010) >> 4) + \ | ||
152 | (((x) & 0x91000820) >> 3) + \ | ||
153 | (((x) & 0x22080080) >> 2) + \ | ||
154 | ((x) & 0x40000400) + \ | ||
155 | (((x) & 0x00040040) << 1) + \ | ||
156 | (((x) & 0x00110000) << 4) + \ | ||
157 | (((x) & 0x00220100) << 5) + \ | ||
158 | (((x) & 0x00800208) << 6) + \ | ||
159 | (((x) & 0x00400004) << 9) + \ | ||
160 | (((x) & 0x00000001) << 12) + \ | ||
161 | (((x) & 0x00000002) << 13) \ | ||
162 | ) | ||
163 | |||
164 | /* Unmangle data (x) */ | ||
165 | #define FLASH_TO_DATA(x) \ | ||
166 | ( \ | ||
167 | (((x) & 0x00010012) << 11) + \ | ||
168 | (((x) & 0x00000008) << 10) + \ | ||
169 | (((x) & 0x00040040) << 8) + \ | ||
170 | (((x) & 0x00000001) << 4) + \ | ||
171 | (((x) & 0x12200104) << 3) + \ | ||
172 | (((x) & 0x08820020) << 2) + \ | ||
173 | ((x) & 0x40000400) + \ | ||
174 | (((x) & 0x00080080) >> 1) + \ | ||
175 | (((x) & 0x01100000) >> 4) + \ | ||
176 | (((x) & 0x04402000) >> 5) + \ | ||
177 | (((x) & 0x20008200) >> 6) + \ | ||
178 | (((x) & 0x80000800) >> 9) + \ | ||
179 | (((x) & 0x00001000) >> 12) + \ | ||
180 | (((x) & 0x00004000) >> 13) \ | ||
181 | ) | ||
182 | |||
183 | /* | ||
184 | * The address line mapping on LART is as follows: | ||
185 | * | ||
186 | * U3 CPU | U2 CPU | ||
187 | * ------------------- | ||
188 | * 0 2 | 0 2 | ||
189 | * 1 3 | 1 3 | ||
190 | * 2 9 | 2 9 | ||
191 | * 3 13 | 3 8 | ||
192 | * 4 8 | 4 7 | ||
193 | * 5 12 | 5 6 | ||
194 | * 6 11 | 6 5 | ||
195 | * 7 10 | 7 4 | ||
196 | * 8 4 | 8 10 | ||
197 | * 9 5 | 9 11 | ||
198 | * 10 6 | 10 12 | ||
199 | * 11 7 | 11 13 | ||
200 | * | ||
201 | * BOOT BLOCK BOUNDARY | ||
202 | * | ||
203 | * 12 15 | 12 15 | ||
204 | * 13 14 | 13 14 | ||
205 | * 14 16 | 14 16 | ||
206 | * | ||
207 | * MAIN BLOCK BOUNDARY | ||
208 | * | ||
209 | * 15 17 | 15 18 | ||
210 | * 16 18 | 16 17 | ||
211 | * 17 20 | 17 20 | ||
212 | * 18 19 | 18 19 | ||
213 | * 19 21 | 19 21 | ||
214 | * | ||
215 | * As we can see from above, the addresses aren't mangled across | ||
216 | * block boundaries, so we don't need to worry about address | ||
217 | * translations except for sending/reading commands during | ||
218 | * initialization | ||
219 | */ | ||
220 | |||
221 | /* Mangle address (x) on chip U2 */ | ||
222 | #define ADDR_TO_FLASH_U2(x) \ | ||
223 | ( \ | ||
224 | (((x) & 0x00000f00) >> 4) + \ | ||
225 | (((x) & 0x00042000) << 1) + \ | ||
226 | (((x) & 0x0009c003) << 2) + \ | ||
227 | (((x) & 0x00021080) << 3) + \ | ||
228 | (((x) & 0x00000010) << 4) + \ | ||
229 | (((x) & 0x00000040) << 5) + \ | ||
230 | (((x) & 0x00000024) << 7) + \ | ||
231 | (((x) & 0x00000008) << 10) \ | ||
232 | ) | ||
233 | |||
234 | /* Unmangle address (x) on chip U2 */ | ||
235 | #define FLASH_U2_TO_ADDR(x) \ | ||
236 | ( \ | ||
237 | (((x) << 4) & 0x00000f00) + \ | ||
238 | (((x) >> 1) & 0x00042000) + \ | ||
239 | (((x) >> 2) & 0x0009c003) + \ | ||
240 | (((x) >> 3) & 0x00021080) + \ | ||
241 | (((x) >> 4) & 0x00000010) + \ | ||
242 | (((x) >> 5) & 0x00000040) + \ | ||
243 | (((x) >> 7) & 0x00000024) + \ | ||
244 | (((x) >> 10) & 0x00000008) \ | ||
245 | ) | ||
246 | |||
247 | /* Mangle address (x) on chip U3 */ | ||
248 | #define ADDR_TO_FLASH_U3(x) \ | ||
249 | ( \ | ||
250 | (((x) & 0x00000080) >> 3) + \ | ||
251 | (((x) & 0x00000040) >> 1) + \ | ||
252 | (((x) & 0x00052020) << 1) + \ | ||
253 | (((x) & 0x00084f03) << 2) + \ | ||
254 | (((x) & 0x00029010) << 3) + \ | ||
255 | (((x) & 0x00000008) << 5) + \ | ||
256 | (((x) & 0x00000004) << 7) \ | ||
257 | ) | ||
258 | |||
259 | /* Unmangle address (x) on chip U3 */ | ||
260 | #define FLASH_U3_TO_ADDR(x) \ | ||
261 | ( \ | ||
262 | (((x) << 3) & 0x00000080) + \ | ||
263 | (((x) << 1) & 0x00000040) + \ | ||
264 | (((x) >> 1) & 0x00052020) + \ | ||
265 | (((x) >> 2) & 0x00084f03) + \ | ||
266 | (((x) >> 3) & 0x00029010) + \ | ||
267 | (((x) >> 5) & 0x00000008) + \ | ||
268 | (((x) >> 7) & 0x00000004) \ | ||
269 | ) | ||
270 | |||
271 | /***************************************************************************************************/ | ||
272 | |||
273 | static __u8 read8 (__u32 offset) | ||
274 | { | ||
275 | volatile __u8 *data = (__u8 *) (FLASH_OFFSET + offset); | ||
276 | #ifdef LART_DEBUG | ||
277 | printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.2x\n",__FUNCTION__,offset,*data); | ||
278 | #endif | ||
279 | return (*data); | ||
280 | } | ||
281 | |||
282 | static __u32 read32 (__u32 offset) | ||
283 | { | ||
284 | volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset); | ||
285 | #ifdef LART_DEBUG | ||
286 | printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.8x\n",__FUNCTION__,offset,*data); | ||
287 | #endif | ||
288 | return (*data); | ||
289 | } | ||
290 | |||
291 | static void write32 (__u32 x,__u32 offset) | ||
292 | { | ||
293 | volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset); | ||
294 | *data = x; | ||
295 | #ifdef LART_DEBUG | ||
296 | printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n",__FUNCTION__,offset,*data); | ||
297 | #endif | ||
298 | } | ||
299 | |||
300 | /***************************************************************************************************/ | ||
301 | |||
302 | /* | ||
303 | * Probe for 16mbit flash memory on a LART board without doing | ||
304 | * too much damage. Since we need to write 1 dword to memory, | ||
305 | * we're f**cked if this happens to be DRAM since we can't | ||
306 | * restore the memory (otherwise we might exit Read Array mode). | ||
307 | * | ||
308 | * Returns 1 if we found 16mbit flash memory on LART, 0 otherwise. | ||
309 | */ | ||
310 | static int flash_probe (void) | ||
311 | { | ||
312 | __u32 manufacturer,devtype; | ||
313 | |||
314 | /* setup "Read Identifier Codes" mode */ | ||
315 | write32 (DATA_TO_FLASH (READ_ID_CODES),0x00000000); | ||
316 | |||
317 | /* probe U2. U2/U3 returns the same data since the first 3 | ||
318 | * address lines is mangled in the same way */ | ||
319 | manufacturer = FLASH_TO_DATA (read32 (ADDR_TO_FLASH_U2 (0x00000000))); | ||
320 | devtype = FLASH_TO_DATA (read32 (ADDR_TO_FLASH_U2 (0x00000001))); | ||
321 | |||
322 | /* put the flash back into command mode */ | ||
323 | write32 (DATA_TO_FLASH (READ_ARRAY),0x00000000); | ||
324 | |||
325 | return (manufacturer == FLASH_MANUFACTURER && (devtype == FLASH_DEVICE_16mbit_TOP || FLASH_DEVICE_16mbit_BOTTOM)); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * Erase one block of flash memory at offset ``offset'' which is any | ||
330 | * address within the block which should be erased. | ||
331 | * | ||
332 | * Returns 1 if successful, 0 otherwise. | ||
333 | */ | ||
334 | static inline int erase_block (__u32 offset) | ||
335 | { | ||
336 | __u32 status; | ||
337 | |||
338 | #ifdef LART_DEBUG | ||
339 | printk (KERN_DEBUG "%s(): 0x%.8x\n",__FUNCTION__,offset); | ||
340 | #endif | ||
341 | |||
342 | /* erase and confirm */ | ||
343 | write32 (DATA_TO_FLASH (ERASE_SETUP),offset); | ||
344 | write32 (DATA_TO_FLASH (ERASE_CONFIRM),offset); | ||
345 | |||
346 | /* wait for block erase to finish */ | ||
347 | do | ||
348 | { | ||
349 | write32 (DATA_TO_FLASH (STATUS_READ),offset); | ||
350 | status = FLASH_TO_DATA (read32 (offset)); | ||
351 | } | ||
352 | while ((~status & STATUS_BUSY) != 0); | ||
353 | |||
354 | /* put the flash back into command mode */ | ||
355 | write32 (DATA_TO_FLASH (READ_ARRAY),offset); | ||
356 | |||
357 | /* was the erase successfull? */ | ||
358 | if ((status & STATUS_ERASE_ERR)) | ||
359 | { | ||
360 | printk (KERN_WARNING "%s: erase error at address 0x%.8x.\n",module_name,offset); | ||
361 | return (0); | ||
362 | } | ||
363 | |||
364 | return (1); | ||
365 | } | ||
366 | |||
367 | static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) | ||
368 | { | ||
369 | __u32 addr,len; | ||
370 | int i,first; | ||
371 | |||
372 | #ifdef LART_DEBUG | ||
373 | printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n",__FUNCTION__,instr->addr,instr->len); | ||
374 | #endif | ||
375 | |||
376 | /* sanity checks */ | ||
377 | if (instr->addr + instr->len > mtd->size) return (-EINVAL); | ||
378 | |||
379 | /* | ||
380 | * check that both start and end of the requested erase are | ||
381 | * aligned with the erasesize at the appropriate addresses. | ||
382 | * | ||
383 | * skip all erase regions which are ended before the start of | ||
384 | * the requested erase. Actually, to save on the calculations, | ||
385 | * we skip to the first erase region which starts after the | ||
386 | * start of the requested erase, and then go back one. | ||
387 | */ | ||
388 | for (i = 0; i < mtd->numeraseregions && instr->addr >= mtd->eraseregions[i].offset; i++) ; | ||
389 | i--; | ||
390 | |||
391 | /* | ||
392 | * ok, now i is pointing at the erase region in which this | ||
393 | * erase request starts. Check the start of the requested | ||
394 | * erase range is aligned with the erase size which is in | ||
395 | * effect here. | ||
396 | */ | ||
397 | if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); | ||
398 | |||
399 | /* Remember the erase region we start on */ | ||
400 | first = i; | ||
401 | |||
402 | /* | ||
403 | * next, check that the end of the requested erase is aligned | ||
404 | * with the erase region at that address. | ||
405 | * | ||
406 | * as before, drop back one to point at the region in which | ||
407 | * the address actually falls | ||
408 | */ | ||
409 | for (; i < mtd->numeraseregions && instr->addr + instr->len >= mtd->eraseregions[i].offset; i++) ; | ||
410 | i--; | ||
411 | |||
412 | /* is the end aligned on a block boundary? */ | ||
413 | if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); | ||
414 | |||
415 | addr = instr->addr; | ||
416 | len = instr->len; | ||
417 | |||
418 | i = first; | ||
419 | |||
420 | /* now erase those blocks */ | ||
421 | while (len) | ||
422 | { | ||
423 | if (!erase_block (addr)) | ||
424 | { | ||
425 | instr->state = MTD_ERASE_FAILED; | ||
426 | return (-EIO); | ||
427 | } | ||
428 | |||
429 | addr += mtd->eraseregions[i].erasesize; | ||
430 | len -= mtd->eraseregions[i].erasesize; | ||
431 | |||
432 | if (addr == mtd->eraseregions[i].offset + (mtd->eraseregions[i].erasesize * mtd->eraseregions[i].numblocks)) i++; | ||
433 | } | ||
434 | |||
435 | instr->state = MTD_ERASE_DONE; | ||
436 | mtd_erase_callback(instr); | ||
437 | |||
438 | return (0); | ||
439 | } | ||
440 | |||
441 | static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retlen,u_char *buf) | ||
442 | { | ||
443 | #ifdef LART_DEBUG | ||
444 | printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n",__FUNCTION__,(__u32) from,len); | ||
445 | #endif | ||
446 | |||
447 | /* sanity checks */ | ||
448 | if (!len) return (0); | ||
449 | if (from + len > mtd->size) return (-EINVAL); | ||
450 | |||
451 | /* we always read len bytes */ | ||
452 | *retlen = len; | ||
453 | |||
454 | /* first, we read bytes until we reach a dword boundary */ | ||
455 | if (from & (BUSWIDTH - 1)) | ||
456 | { | ||
457 | int gap = BUSWIDTH - (from & (BUSWIDTH - 1)); | ||
458 | |||
459 | while (len && gap--) *buf++ = read8 (from++), len--; | ||
460 | } | ||
461 | |||
462 | /* now we read dwords until we reach a non-dword boundary */ | ||
463 | while (len >= BUSWIDTH) | ||
464 | { | ||
465 | *((__u32 *) buf) = read32 (from); | ||
466 | |||
467 | buf += BUSWIDTH; | ||
468 | from += BUSWIDTH; | ||
469 | len -= BUSWIDTH; | ||
470 | } | ||
471 | |||
472 | /* top up the last unaligned bytes */ | ||
473 | if (len & (BUSWIDTH - 1)) | ||
474 | while (len--) *buf++ = read8 (from++); | ||
475 | |||
476 | return (0); | ||
477 | } | ||
478 | |||
479 | /* | ||
480 | * Write one dword ``x'' to flash memory at offset ``offset''. ``offset'' | ||
481 | * must be 32 bits, i.e. it must be on a dword boundary. | ||
482 | * | ||
483 | * Returns 1 if successful, 0 otherwise. | ||
484 | */ | ||
485 | static inline int write_dword (__u32 offset,__u32 x) | ||
486 | { | ||
487 | __u32 status; | ||
488 | |||
489 | #ifdef LART_DEBUG | ||
490 | printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n",__FUNCTION__,offset,x); | ||
491 | #endif | ||
492 | |||
493 | /* setup writing */ | ||
494 | write32 (DATA_TO_FLASH (PGM_SETUP),offset); | ||
495 | |||
496 | /* write the data */ | ||
497 | write32 (x,offset); | ||
498 | |||
499 | /* wait for the write to finish */ | ||
500 | do | ||
501 | { | ||
502 | write32 (DATA_TO_FLASH (STATUS_READ),offset); | ||
503 | status = FLASH_TO_DATA (read32 (offset)); | ||
504 | } | ||
505 | while ((~status & STATUS_BUSY) != 0); | ||
506 | |||
507 | /* put the flash back into command mode */ | ||
508 | write32 (DATA_TO_FLASH (READ_ARRAY),offset); | ||
509 | |||
510 | /* was the write successfull? */ | ||
511 | if ((status & STATUS_PGM_ERR) || read32 (offset) != x) | ||
512 | { | ||
513 | printk (KERN_WARNING "%s: write error at address 0x%.8x.\n",module_name,offset); | ||
514 | return (0); | ||
515 | } | ||
516 | |||
517 | return (1); | ||
518 | } | ||
519 | |||
520 | static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen,const u_char *buf) | ||
521 | { | ||
522 | __u8 tmp[4]; | ||
523 | int i,n; | ||
524 | |||
525 | #ifdef LART_DEBUG | ||
526 | printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n",__FUNCTION__,(__u32) to,len); | ||
527 | #endif | ||
528 | |||
529 | *retlen = 0; | ||
530 | |||
531 | /* sanity checks */ | ||
532 | if (!len) return (0); | ||
533 | if (to + len > mtd->size) return (-EINVAL); | ||
534 | |||
535 | /* first, we write a 0xFF.... padded byte until we reach a dword boundary */ | ||
536 | if (to & (BUSWIDTH - 1)) | ||
537 | { | ||
538 | __u32 aligned = to & ~(BUSWIDTH - 1); | ||
539 | int gap = to - aligned; | ||
540 | |||
541 | i = n = 0; | ||
542 | |||
543 | while (gap--) tmp[i++] = 0xFF; | ||
544 | while (len && i < BUSWIDTH) tmp[i++] = buf[n++], len--; | ||
545 | while (i < BUSWIDTH) tmp[i++] = 0xFF; | ||
546 | |||
547 | if (!write_dword (aligned,*((__u32 *) tmp))) return (-EIO); | ||
548 | |||
549 | to += n; | ||
550 | buf += n; | ||
551 | *retlen += n; | ||
552 | } | ||
553 | |||
554 | /* now we write dwords until we reach a non-dword boundary */ | ||
555 | while (len >= BUSWIDTH) | ||
556 | { | ||
557 | if (!write_dword (to,*((__u32 *) buf))) return (-EIO); | ||
558 | |||
559 | to += BUSWIDTH; | ||
560 | buf += BUSWIDTH; | ||
561 | *retlen += BUSWIDTH; | ||
562 | len -= BUSWIDTH; | ||
563 | } | ||
564 | |||
565 | /* top up the last unaligned bytes, padded with 0xFF.... */ | ||
566 | if (len & (BUSWIDTH - 1)) | ||
567 | { | ||
568 | i = n = 0; | ||
569 | |||
570 | while (len--) tmp[i++] = buf[n++]; | ||
571 | while (i < BUSWIDTH) tmp[i++] = 0xFF; | ||
572 | |||
573 | if (!write_dword (to,*((__u32 *) tmp))) return (-EIO); | ||
574 | |||
575 | *retlen += n; | ||
576 | } | ||
577 | |||
578 | return (0); | ||
579 | } | ||
580 | |||
581 | /***************************************************************************************************/ | ||
582 | |||
583 | #define NB_OF(x) (sizeof (x) / sizeof (x[0])) | ||
584 | |||
585 | static struct mtd_info mtd; | ||
586 | |||
587 | static struct mtd_erase_region_info erase_regions[] = { | ||
588 | /* parameter blocks */ | ||
589 | { | ||
590 | .offset = 0x00000000, | ||
591 | .erasesize = FLASH_BLOCKSIZE_PARAM, | ||
592 | .numblocks = FLASH_NUMBLOCKS_16m_PARAM, | ||
593 | }, | ||
594 | /* main blocks */ | ||
595 | { | ||
596 | .offset = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM, | ||
597 | .erasesize = FLASH_BLOCKSIZE_MAIN, | ||
598 | .numblocks = FLASH_NUMBLOCKS_16m_MAIN, | ||
599 | } | ||
600 | }; | ||
601 | |||
602 | #ifdef HAVE_PARTITIONS | ||
603 | static struct mtd_partition lart_partitions[] = { | ||
604 | /* blob */ | ||
605 | { | ||
606 | .name = "blob", | ||
607 | .offset = BLOB_START, | ||
608 | .size = BLOB_LEN, | ||
609 | }, | ||
610 | /* kernel */ | ||
611 | { | ||
612 | .name = "kernel", | ||
613 | .offset = KERNEL_START, /* MTDPART_OFS_APPEND */ | ||
614 | .size = KERNEL_LEN, | ||
615 | }, | ||
616 | /* initial ramdisk / file system */ | ||
617 | { | ||
618 | .name = "file system", | ||
619 | .offset = INITRD_START, /* MTDPART_OFS_APPEND */ | ||
620 | .size = INITRD_LEN, /* MTDPART_SIZ_FULL */ | ||
621 | } | ||
622 | }; | ||
623 | #endif | ||
624 | |||
625 | int __init lart_flash_init (void) | ||
626 | { | ||
627 | int result; | ||
628 | memset (&mtd,0,sizeof (mtd)); | ||
629 | printk ("MTD driver for LART. Written by Abraham vd Merwe <abraham@2d3d.co.za>\n"); | ||
630 | printk ("%s: Probing for 28F160x3 flash on LART...\n",module_name); | ||
631 | if (!flash_probe ()) | ||
632 | { | ||
633 | printk (KERN_WARNING "%s: Found no LART compatible flash device\n",module_name); | ||
634 | return (-ENXIO); | ||
635 | } | ||
636 | printk ("%s: This looks like a LART board to me.\n",module_name); | ||
637 | mtd.name = module_name; | ||
638 | mtd.type = MTD_NORFLASH; | ||
639 | mtd.flags = MTD_CAP_NORFLASH; | ||
640 | mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN; | ||
641 | mtd.erasesize = FLASH_BLOCKSIZE_MAIN; | ||
642 | mtd.numeraseregions = NB_OF (erase_regions); | ||
643 | mtd.eraseregions = erase_regions; | ||
644 | mtd.erase = flash_erase; | ||
645 | mtd.read = flash_read; | ||
646 | mtd.write = flash_write; | ||
647 | mtd.owner = THIS_MODULE; | ||
648 | |||
649 | #ifdef LART_DEBUG | ||
650 | printk (KERN_DEBUG | ||
651 | "mtd.name = %s\n" | ||
652 | "mtd.size = 0x%.8x (%uM)\n" | ||
653 | "mtd.erasesize = 0x%.8x (%uK)\n" | ||
654 | "mtd.numeraseregions = %d\n", | ||
655 | mtd.name, | ||
656 | mtd.size,mtd.size / (1024*1024), | ||
657 | mtd.erasesize,mtd.erasesize / 1024, | ||
658 | mtd.numeraseregions); | ||
659 | |||
660 | if (mtd.numeraseregions) | ||
661 | for (result = 0; result < mtd.numeraseregions; result++) | ||
662 | printk (KERN_DEBUG | ||
663 | "\n\n" | ||
664 | "mtd.eraseregions[%d].offset = 0x%.8x\n" | ||
665 | "mtd.eraseregions[%d].erasesize = 0x%.8x (%uK)\n" | ||
666 | "mtd.eraseregions[%d].numblocks = %d\n", | ||
667 | result,mtd.eraseregions[result].offset, | ||
668 | result,mtd.eraseregions[result].erasesize,mtd.eraseregions[result].erasesize / 1024, | ||
669 | result,mtd.eraseregions[result].numblocks); | ||
670 | |||
671 | #ifdef HAVE_PARTITIONS | ||
672 | printk ("\npartitions = %d\n",NB_OF (lart_partitions)); | ||
673 | |||
674 | for (result = 0; result < NB_OF (lart_partitions); result++) | ||
675 | printk (KERN_DEBUG | ||
676 | "\n\n" | ||
677 | "lart_partitions[%d].name = %s\n" | ||
678 | "lart_partitions[%d].offset = 0x%.8x\n" | ||
679 | "lart_partitions[%d].size = 0x%.8x (%uK)\n", | ||
680 | result,lart_partitions[result].name, | ||
681 | result,lart_partitions[result].offset, | ||
682 | result,lart_partitions[result].size,lart_partitions[result].size / 1024); | ||
683 | #endif | ||
684 | #endif | ||
685 | |||
686 | #ifndef HAVE_PARTITIONS | ||
687 | result = add_mtd_device (&mtd); | ||
688 | #else | ||
689 | result = add_mtd_partitions (&mtd,lart_partitions,NB_OF (lart_partitions)); | ||
690 | #endif | ||
691 | |||
692 | return (result); | ||
693 | } | ||
694 | |||
695 | void __exit lart_flash_exit (void) | ||
696 | { | ||
697 | #ifndef HAVE_PARTITIONS | ||
698 | del_mtd_device (&mtd); | ||
699 | #else | ||
700 | del_mtd_partitions (&mtd); | ||
701 | #endif | ||
702 | } | ||
703 | |||
704 | module_init (lart_flash_init); | ||
705 | module_exit (lart_flash_exit); | ||
706 | |||
707 | MODULE_LICENSE("GPL"); | ||
708 | MODULE_AUTHOR("Abraham vd Merwe <abraham@2d3d.co.za>"); | ||
709 | MODULE_DESCRIPTION("MTD driver for Intel 28F160F3 on LART board"); | ||
710 | |||
711 | |||