blob: 57115618c4968903e0a4d70052bc6d54c6e64e3b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * MTD map driver for AMD compatible flash chips (non-CFI)
3 *
4 * Author: Jonas Holmberg <jonas.holmberg@axis.com>
5 *
Thomas Gleixner1f948b42005-11-07 11:15:37 +00006 * $Id: amd_flash.c,v 1.28 2005/11/07 11:14:22 gleixner Exp $
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
8 * Copyright (c) 2001 Axis Communications AB
9 *
10 * This file is under GPL.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/sched.h>
18#include <linux/errno.h>
19#include <linux/slab.h>
20#include <linux/delay.h>
21#include <linux/interrupt.h>
22#include <linux/init.h>
23#include <linux/mtd/map.h>
24#include <linux/mtd/mtd.h>
25#include <linux/mtd/flashchip.h>
26
27/* There's no limit. It exists only to avoid realloc. */
28#define MAX_AMD_CHIPS 8
29
30#define DEVICE_TYPE_X8 (8 / 8)
31#define DEVICE_TYPE_X16 (16 / 8)
32#define DEVICE_TYPE_X32 (32 / 8)
33
34/* Addresses */
35#define ADDR_MANUFACTURER 0x0000
36#define ADDR_DEVICE_ID 0x0001
37#define ADDR_SECTOR_LOCK 0x0002
38#define ADDR_HANDSHAKE 0x0003
39#define ADDR_UNLOCK_1 0x0555
40#define ADDR_UNLOCK_2 0x02AA
41
42/* Commands */
43#define CMD_UNLOCK_DATA_1 0x00AA
44#define CMD_UNLOCK_DATA_2 0x0055
45#define CMD_MANUFACTURER_UNLOCK_DATA 0x0090
46#define CMD_UNLOCK_BYPASS_MODE 0x0020
47#define CMD_PROGRAM_UNLOCK_DATA 0x00A0
48#define CMD_RESET_DATA 0x00F0
49#define CMD_SECTOR_ERASE_UNLOCK_DATA 0x0080
50#define CMD_SECTOR_ERASE_UNLOCK_DATA_2 0x0030
51
52#define CMD_UNLOCK_SECTOR 0x0060
53
54/* Manufacturers */
55#define MANUFACTURER_AMD 0x0001
56#define MANUFACTURER_ATMEL 0x001F
57#define MANUFACTURER_FUJITSU 0x0004
58#define MANUFACTURER_ST 0x0020
59#define MANUFACTURER_SST 0x00BF
60#define MANUFACTURER_TOSHIBA 0x0098
61
62/* AMD */
63#define AM29F800BB 0x2258
64#define AM29F800BT 0x22D6
65#define AM29LV800BB 0x225B
66#define AM29LV800BT 0x22DA
67#define AM29LV160DT 0x22C4
68#define AM29LV160DB 0x2249
69#define AM29BDS323D 0x22D1
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
71/* Atmel */
72#define AT49xV16x 0x00C0
73#define AT49xV16xT 0x00C2
74
75/* Fujitsu */
76#define MBM29LV160TE 0x22C4
77#define MBM29LV160BE 0x2249
78#define MBM29LV800BB 0x225B
79
80/* ST - www.st.com */
81#define M29W800T 0x00D7
82#define M29W160DT 0x22C4
83#define M29W160DB 0x2249
84
85/* SST */
86#define SST39LF800 0x2781
87#define SST39LF160 0x2782
88
89/* Toshiba */
90#define TC58FVT160 0x00C2
91#define TC58FVB160 0x0043
92
93#define D6_MASK 0x40
94
95struct amd_flash_private {
Thomas Gleixner1f948b42005-11-07 11:15:37 +000096 int device_type;
97 int interleave;
98 int numchips;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 unsigned long chipshift;
100// const char *im_name;
101 struct flchip chips[0];
102};
103
104struct amd_flash_info {
105 const __u16 mfr_id;
106 const __u16 dev_id;
107 const char *name;
108 const u_long size;
109 const int numeraseregions;
110 const struct mtd_erase_region_info regions[4];
111};
112
113
114
115static int amd_flash_read(struct mtd_info *, loff_t, size_t, size_t *,
116 u_char *);
117static int amd_flash_write(struct mtd_info *, loff_t, size_t, size_t *,
118 const u_char *);
119static int amd_flash_erase(struct mtd_info *, struct erase_info *);
120static void amd_flash_sync(struct mtd_info *);
121static int amd_flash_suspend(struct mtd_info *);
122static void amd_flash_resume(struct mtd_info *);
123static void amd_flash_destroy(struct mtd_info *);
124static struct mtd_info *amd_flash_probe(struct map_info *map);
125
126
127static struct mtd_chip_driver amd_flash_chipdrv = {
128 .probe = amd_flash_probe,
129 .destroy = amd_flash_destroy,
130 .name = "amd_flash",
131 .module = THIS_MODULE
132};
133
134
135
136static const char im_name[] = "amd_flash";
137
138
139
140static inline __u32 wide_read(struct map_info *map, __u32 addr)
141{
142 if (map->buswidth == 1) {
143 return map_read8(map, addr);
144 } else if (map->buswidth == 2) {
145 return map_read16(map, addr);
146 } else if (map->buswidth == 4) {
147 return map_read32(map, addr);
148 }
149
150 return 0;
151}
152
153static inline void wide_write(struct map_info *map, __u32 val, __u32 addr)
154{
155 if (map->buswidth == 1) {
156 map_write8(map, val, addr);
157 } else if (map->buswidth == 2) {
158 map_write16(map, val, addr);
159 } else if (map->buswidth == 4) {
160 map_write32(map, val, addr);
161 }
162}
163
164static inline __u32 make_cmd(struct map_info *map, __u32 cmd)
165{
166 const struct amd_flash_private *private = map->fldrv_priv;
167 if ((private->interleave == 2) &&
168 (private->device_type == DEVICE_TYPE_X16)) {
169 cmd |= (cmd << 16);
170 }
171
172 return cmd;
173}
174
175static inline void send_unlock(struct map_info *map, unsigned long base)
176{
177 wide_write(map, (CMD_UNLOCK_DATA_1 << 16) | CMD_UNLOCK_DATA_1,
178 base + (map->buswidth * ADDR_UNLOCK_1));
179 wide_write(map, (CMD_UNLOCK_DATA_2 << 16) | CMD_UNLOCK_DATA_2,
180 base + (map->buswidth * ADDR_UNLOCK_2));
181}
182
183static inline void send_cmd(struct map_info *map, unsigned long base, __u32 cmd)
184{
185 send_unlock(map, base);
186 wide_write(map, make_cmd(map, cmd),
187 base + (map->buswidth * ADDR_UNLOCK_1));
188}
189
190static inline void send_cmd_to_addr(struct map_info *map, unsigned long base,
191 __u32 cmd, unsigned long addr)
192{
193 send_unlock(map, base);
194 wide_write(map, make_cmd(map, cmd), addr);
195}
196
197static inline int flash_is_busy(struct map_info *map, unsigned long addr,
198 int interleave)
199{
200
201 if ((interleave == 2) && (map->buswidth == 4)) {
202 __u32 read1, read2;
203
204 read1 = wide_read(map, addr);
205 read2 = wide_read(map, addr);
206
207 return (((read1 >> 16) & D6_MASK) !=
208 ((read2 >> 16) & D6_MASK)) ||
209 (((read1 & 0xffff) & D6_MASK) !=
210 ((read2 & 0xffff) & D6_MASK));
211 }
212
213 return ((wide_read(map, addr) & D6_MASK) !=
214 (wide_read(map, addr) & D6_MASK));
215}
216
217static inline void unlock_sector(struct map_info *map, unsigned long sect_addr,
218 int unlock)
219{
220 /* Sector lock address. A6 = 1 for unlock, A6 = 0 for lock */
221 int SLA = unlock ?
222 (sect_addr | (0x40 * map->buswidth)) :
223 (sect_addr & ~(0x40 * map->buswidth)) ;
224
225 __u32 cmd = make_cmd(map, CMD_UNLOCK_SECTOR);
226
227 wide_write(map, make_cmd(map, CMD_RESET_DATA), 0);
228 wide_write(map, cmd, SLA); /* 1st cycle: write cmd to any address */
229 wide_write(map, cmd, SLA); /* 2nd cycle: write cmd to any address */
230 wide_write(map, cmd, SLA); /* 3rd cycle: write cmd to SLA */
231}
232
233static inline int is_sector_locked(struct map_info *map,
234 unsigned long sect_addr)
235{
236 int status;
237
238 wide_write(map, CMD_RESET_DATA, 0);
239 send_cmd(map, sect_addr, CMD_MANUFACTURER_UNLOCK_DATA);
240
241 /* status is 0x0000 for unlocked and 0x0001 for locked */
242 status = wide_read(map, sect_addr + (map->buswidth * ADDR_SECTOR_LOCK));
243 wide_write(map, CMD_RESET_DATA, 0);
244 return status;
245}
246
247static int amd_flash_do_unlock(struct mtd_info *mtd, loff_t ofs, size_t len,
248 int is_unlock)
249{
250 struct map_info *map;
251 struct mtd_erase_region_info *merip;
252 int eraseoffset, erasesize, eraseblocks;
253 int i;
254 int retval = 0;
255 int lock_status;
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000256
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 map = mtd->priv;
258
259 /* Pass the whole chip through sector by sector and check for each
260 sector if the sector and the given interval overlap */
261 for(i = 0; i < mtd->numeraseregions; i++) {
262 merip = &mtd->eraseregions[i];
263
264 eraseoffset = merip->offset;
265 erasesize = merip->erasesize;
266 eraseblocks = merip->numblocks;
267
268 if (ofs > eraseoffset + erasesize)
269 continue;
270
271 while (eraseblocks > 0) {
272 if (ofs < eraseoffset + erasesize && ofs + len > eraseoffset) {
273 unlock_sector(map, eraseoffset, is_unlock);
274
275 lock_status = is_sector_locked(map, eraseoffset);
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 if (is_unlock && lock_status) {
278 printk("Cannot unlock sector at address %x length %xx\n",
279 eraseoffset, merip->erasesize);
280 retval = -1;
281 } else if (!is_unlock && !lock_status) {
282 printk("Cannot lock sector at address %x length %x\n",
283 eraseoffset, merip->erasesize);
284 retval = -1;
285 }
286 }
287 eraseoffset += erasesize;
288 eraseblocks --;
289 }
290 }
291 return retval;
292}
293
294static int amd_flash_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
295{
296 return amd_flash_do_unlock(mtd, ofs, len, 1);
297}
298
299static int amd_flash_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
300{
301 return amd_flash_do_unlock(mtd, ofs, len, 0);
302}
303
304
305/*
306 * Reads JEDEC manufacturer ID and device ID and returns the index of the first
307 * matching table entry (-1 if not found or alias for already found chip).
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000308 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309static int probe_new_chip(struct mtd_info *mtd, __u32 base,
310 struct flchip *chips,
311 struct amd_flash_private *private,
312 const struct amd_flash_info *table, int table_size)
313{
314 __u32 mfr_id;
315 __u32 dev_id;
316 struct map_info *map = mtd->priv;
317 struct amd_flash_private temp;
318 int i;
319
320 temp.device_type = DEVICE_TYPE_X16; // Assume X16 (FIXME)
321 temp.interleave = 2;
322 map->fldrv_priv = &temp;
323
324 /* Enter autoselect mode. */
325 send_cmd(map, base, CMD_RESET_DATA);
326 send_cmd(map, base, CMD_MANUFACTURER_UNLOCK_DATA);
327
328 mfr_id = wide_read(map, base + (map->buswidth * ADDR_MANUFACTURER));
329 dev_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_ID));
330
331 if ((map->buswidth == 4) && ((mfr_id >> 16) == (mfr_id & 0xffff)) &&
332 ((dev_id >> 16) == (dev_id & 0xffff))) {
333 mfr_id &= 0xffff;
334 dev_id &= 0xffff;
335 } else {
336 temp.interleave = 1;
337 }
338
339 for (i = 0; i < table_size; i++) {
340 if ((mfr_id == table[i].mfr_id) &&
341 (dev_id == table[i].dev_id)) {
342 if (chips) {
343 int j;
344
345 /* Is this an alias for an already found chip?
346 * In that case that chip should be in
347 * autoselect mode now.
348 */
349 for (j = 0; j < private->numchips; j++) {
350 __u32 mfr_id_other;
351 __u32 dev_id_other;
352
353 mfr_id_other =
354 wide_read(map, chips[j].start +
355 (map->buswidth *
356 ADDR_MANUFACTURER
357 ));
358 dev_id_other =
359 wide_read(map, chips[j].start +
360 (map->buswidth *
361 ADDR_DEVICE_ID));
362 if (temp.interleave == 2) {
363 mfr_id_other &= 0xffff;
364 dev_id_other &= 0xffff;
365 }
366 if ((mfr_id_other == mfr_id) &&
367 (dev_id_other == dev_id)) {
368
369 /* Exit autoselect mode. */
370 send_cmd(map, base,
371 CMD_RESET_DATA);
372
373 return -1;
374 }
375 }
376
377 if (private->numchips == MAX_AMD_CHIPS) {
378 printk(KERN_WARNING
379 "%s: Too many flash chips "
380 "detected. Increase "
381 "MAX_AMD_CHIPS from %d.\n",
382 map->name, MAX_AMD_CHIPS);
383
384 return -1;
385 }
386
387 chips[private->numchips].start = base;
388 chips[private->numchips].state = FL_READY;
389 chips[private->numchips].mutex =
390 &chips[private->numchips]._spinlock;
391 private->numchips++;
392 }
393
394 printk("%s: Found %d x %ldMiB %s at 0x%x\n", map->name,
395 temp.interleave, (table[i].size)/(1024*1024),
396 table[i].name, base);
397
398 mtd->size += table[i].size * temp.interleave;
399 mtd->numeraseregions += table[i].numeraseregions;
400
401 break;
402 }
403 }
404
405 /* Exit autoselect mode. */
406 send_cmd(map, base, CMD_RESET_DATA);
407
408 if (i == table_size) {
409 printk(KERN_DEBUG "%s: unknown flash device at 0x%x, "
410 "mfr id 0x%x, dev id 0x%x\n", map->name,
411 base, mfr_id, dev_id);
412 map->fldrv_priv = NULL;
413
414 return -1;
415 }
416
417 private->device_type = temp.device_type;
418 private->interleave = temp.interleave;
419
420 return i;
421}
422
423
424
425static struct mtd_info *amd_flash_probe(struct map_info *map)
426{
427 static const struct amd_flash_info table[] = {
428 {
429 .mfr_id = MANUFACTURER_AMD,
430 .dev_id = AM29LV160DT,
431 .name = "AMD AM29LV160DT",
432 .size = 0x00200000,
433 .numeraseregions = 4,
434 .regions = {
435 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
436 { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
437 { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
438 { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
439 }
440 }, {
441 .mfr_id = MANUFACTURER_AMD,
442 .dev_id = AM29LV160DB,
443 .name = "AMD AM29LV160DB",
444 .size = 0x00200000,
445 .numeraseregions = 4,
446 .regions = {
447 { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
448 { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
449 { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
450 { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
451 }
452 }, {
453 .mfr_id = MANUFACTURER_TOSHIBA,
454 .dev_id = TC58FVT160,
455 .name = "Toshiba TC58FVT160",
456 .size = 0x00200000,
457 .numeraseregions = 4,
458 .regions = {
459 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
460 { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
461 { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
462 { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
463 }
464 }, {
465 .mfr_id = MANUFACTURER_FUJITSU,
466 .dev_id = MBM29LV160TE,
467 .name = "Fujitsu MBM29LV160TE",
468 .size = 0x00200000,
469 .numeraseregions = 4,
470 .regions = {
471 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
472 { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
473 { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
474 { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
475 }
476 }, {
477 .mfr_id = MANUFACTURER_TOSHIBA,
478 .dev_id = TC58FVB160,
479 .name = "Toshiba TC58FVB160",
480 .size = 0x00200000,
481 .numeraseregions = 4,
482 .regions = {
483 { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
484 { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
485 { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
486 { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
487 }
488 }, {
489 .mfr_id = MANUFACTURER_FUJITSU,
490 .dev_id = MBM29LV160BE,
491 .name = "Fujitsu MBM29LV160BE",
492 .size = 0x00200000,
493 .numeraseregions = 4,
494 .regions = {
495 { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
496 { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
497 { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
498 { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
499 }
500 }, {
501 .mfr_id = MANUFACTURER_AMD,
502 .dev_id = AM29LV800BB,
503 .name = "AMD AM29LV800BB",
504 .size = 0x00100000,
505 .numeraseregions = 4,
506 .regions = {
507 { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
508 { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
509 { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
510 { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
511 }
512 }, {
513 .mfr_id = MANUFACTURER_AMD,
514 .dev_id = AM29F800BB,
515 .name = "AMD AM29F800BB",
516 .size = 0x00100000,
517 .numeraseregions = 4,
518 .regions = {
519 { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
520 { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
521 { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
522 { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
523 }
524 }, {
525 .mfr_id = MANUFACTURER_AMD,
526 .dev_id = AM29LV800BT,
527 .name = "AMD AM29LV800BT",
528 .size = 0x00100000,
529 .numeraseregions = 4,
530 .regions = {
531 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
532 { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
533 { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
534 { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
535 }
536 }, {
537 .mfr_id = MANUFACTURER_AMD,
538 .dev_id = AM29F800BT,
539 .name = "AMD AM29F800BT",
540 .size = 0x00100000,
541 .numeraseregions = 4,
542 .regions = {
543 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
544 { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
545 { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
546 { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
547 }
548 }, {
549 .mfr_id = MANUFACTURER_AMD,
550 .dev_id = AM29LV800BB,
551 .name = "AMD AM29LV800BB",
552 .size = 0x00100000,
553 .numeraseregions = 4,
554 .regions = {
555 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
556 { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
557 { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
558 { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
559 }
560 }, {
561 .mfr_id = MANUFACTURER_FUJITSU,
562 .dev_id = MBM29LV800BB,
563 .name = "Fujitsu MBM29LV800BB",
564 .size = 0x00100000,
565 .numeraseregions = 4,
566 .regions = {
567 { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
568 { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
569 { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
570 { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
571 }
572 }, {
573 .mfr_id = MANUFACTURER_ST,
574 .dev_id = M29W800T,
575 .name = "ST M29W800T",
576 .size = 0x00100000,
577 .numeraseregions = 4,
578 .regions = {
579 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
580 { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
581 { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
582 { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
583 }
584 }, {
585 .mfr_id = MANUFACTURER_ST,
586 .dev_id = M29W160DT,
587 .name = "ST M29W160DT",
588 .size = 0x00200000,
589 .numeraseregions = 4,
590 .regions = {
591 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
592 { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
593 { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
594 { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
595 }
596 }, {
597 .mfr_id = MANUFACTURER_ST,
598 .dev_id = M29W160DB,
599 .name = "ST M29W160DB",
600 .size = 0x00200000,
601 .numeraseregions = 4,
602 .regions = {
603 { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
604 { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
605 { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
606 { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
607 }
608 }, {
609 .mfr_id = MANUFACTURER_AMD,
610 .dev_id = AM29BDS323D,
611 .name = "AMD AM29BDS323D",
612 .size = 0x00400000,
613 .numeraseregions = 3,
614 .regions = {
615 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 },
616 { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 },
617 { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 },
618 }
619 }, {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 .mfr_id = MANUFACTURER_ATMEL,
621 .dev_id = AT49xV16x,
622 .name = "Atmel AT49xV16x",
623 .size = 0x00200000,
624 .numeraseregions = 2,
625 .regions = {
626 { .offset = 0x000000, .erasesize = 0x02000, .numblocks = 8 },
627 { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
628 }
629 }, {
630 .mfr_id = MANUFACTURER_ATMEL,
631 .dev_id = AT49xV16xT,
632 .name = "Atmel AT49xV16xT",
633 .size = 0x00200000,
634 .numeraseregions = 2,
635 .regions = {
636 { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
637 { .offset = 0x1F0000, .erasesize = 0x02000, .numblocks = 8 }
638 }
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000639 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 };
641
642 struct mtd_info *mtd;
643 struct flchip chips[MAX_AMD_CHIPS];
644 int table_pos[MAX_AMD_CHIPS];
645 struct amd_flash_private temp;
646 struct amd_flash_private *private;
647 u_long size;
648 unsigned long base;
649 int i;
650 int reg_idx;
651 int offset;
652
653 mtd = (struct mtd_info*)kmalloc(sizeof(*mtd), GFP_KERNEL);
654 if (!mtd) {
655 printk(KERN_WARNING
656 "%s: kmalloc failed for info structure\n", map->name);
657 return NULL;
658 }
659 memset(mtd, 0, sizeof(*mtd));
660 mtd->priv = map;
661
662 memset(&temp, 0, sizeof(temp));
663
664 printk("%s: Probing for AMD compatible flash...\n", map->name);
665
666 if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table,
Tobias Klauser87d10f32006-03-31 02:29:45 -0800667 ARRAY_SIZE(table)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 == -1) {
669 printk(KERN_WARNING
670 "%s: Found no AMD compatible device at location zero\n",
671 map->name);
672 kfree(mtd);
673
674 return NULL;
675 }
676
677 chips[0].start = 0;
678 chips[0].state = FL_READY;
679 chips[0].mutex = &chips[0]._spinlock;
680 temp.numchips = 1;
681 for (size = mtd->size; size > 1; size >>= 1) {
682 temp.chipshift++;
683 }
684 switch (temp.interleave) {
685 case 2:
686 temp.chipshift += 1;
687 break;
688 case 4:
689 temp.chipshift += 2;
690 break;
691 }
692
693 /* Find out if there are any more chips in the map. */
694 for (base = (1 << temp.chipshift);
695 base < map->size;
696 base += (1 << temp.chipshift)) {
697 int numchips = temp.numchips;
698 table_pos[numchips] = probe_new_chip(mtd, base, chips,
Tobias Klauser87d10f32006-03-31 02:29:45 -0800699 &temp, table, ARRAY_SIZE(table));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 }
701
702 mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) *
703 mtd->numeraseregions, GFP_KERNEL);
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000704 if (!mtd->eraseregions) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 printk(KERN_WARNING "%s: Failed to allocate "
706 "memory for MTD erase region info\n", map->name);
707 kfree(mtd);
708 map->fldrv_priv = NULL;
709 return NULL;
710 }
711
712 reg_idx = 0;
713 offset = 0;
714 for (i = 0; i < temp.numchips; i++) {
715 int dev_size;
716 int j;
717
718 dev_size = 0;
719 for (j = 0; j < table[table_pos[i]].numeraseregions; j++) {
720 mtd->eraseregions[reg_idx].offset = offset +
721 (table[table_pos[i]].regions[j].offset *
722 temp.interleave);
723 mtd->eraseregions[reg_idx].erasesize =
724 table[table_pos[i]].regions[j].erasesize *
725 temp.interleave;
726 mtd->eraseregions[reg_idx].numblocks =
727 table[table_pos[i]].regions[j].numblocks;
728 if (mtd->erasesize <
729 mtd->eraseregions[reg_idx].erasesize) {
730 mtd->erasesize =
731 mtd->eraseregions[reg_idx].erasesize;
732 }
733 dev_size += mtd->eraseregions[reg_idx].erasesize *
734 mtd->eraseregions[reg_idx].numblocks;
735 reg_idx++;
736 }
737 offset += dev_size;
738 }
739 mtd->type = MTD_NORFLASH;
740 mtd->flags = MTD_CAP_NORFLASH;
741 mtd->name = map->name;
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000742 mtd->erase = amd_flash_erase;
743 mtd->read = amd_flash_read;
744 mtd->write = amd_flash_write;
745 mtd->sync = amd_flash_sync;
746 mtd->suspend = amd_flash_suspend;
747 mtd->resume = amd_flash_resume;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 mtd->lock = amd_flash_lock;
749 mtd->unlock = amd_flash_unlock;
750
751 private = kmalloc(sizeof(*private) + (sizeof(struct flchip) *
752 temp.numchips), GFP_KERNEL);
753 if (!private) {
754 printk(KERN_WARNING
755 "%s: kmalloc failed for private structure\n", map->name);
756 kfree(mtd);
757 map->fldrv_priv = NULL;
758 return NULL;
759 }
760 memcpy(private, &temp, sizeof(temp));
761 memcpy(private->chips, chips,
762 sizeof(struct flchip) * private->numchips);
763 for (i = 0; i < private->numchips; i++) {
764 init_waitqueue_head(&private->chips[i].wq);
765 spin_lock_init(&private->chips[i]._spinlock);
766 }
767
768 map->fldrv_priv = private;
769
770 map->fldrv = &amd_flash_chipdrv;
771
772 __module_get(THIS_MODULE);
773 return mtd;
774}
775
776
777
778static inline int read_one_chip(struct map_info *map, struct flchip *chip,
779 loff_t adr, size_t len, u_char *buf)
780{
781 DECLARE_WAITQUEUE(wait, current);
782 unsigned long timeo = jiffies + HZ;
783
784retry:
785 spin_lock_bh(chip->mutex);
786
787 if (chip->state != FL_READY){
788 printk(KERN_INFO "%s: waiting for chip to read, state = %d\n",
789 map->name, chip->state);
790 set_current_state(TASK_UNINTERRUPTIBLE);
791 add_wait_queue(&chip->wq, &wait);
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 spin_unlock_bh(chip->mutex);
794
795 schedule();
796 remove_wait_queue(&chip->wq, &wait);
797
798 if(signal_pending(current)) {
799 return -EINTR;
800 }
801
802 timeo = jiffies + HZ;
803
804 goto retry;
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000805 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
807 adr += chip->start;
808
809 chip->state = FL_READY;
810
811 map_copy_from(map, buf, adr, len);
812
813 wake_up(&chip->wq);
814 spin_unlock_bh(chip->mutex);
815
816 return 0;
817}
818
819
820
821static int amd_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
822 size_t *retlen, u_char *buf)
823{
824 struct map_info *map = mtd->priv;
825 struct amd_flash_private *private = map->fldrv_priv;
826 unsigned long ofs;
827 int chipnum;
828 int ret = 0;
829
830 if ((from + len) > mtd->size) {
831 printk(KERN_WARNING "%s: read request past end of device "
832 "(0x%lx)\n", map->name, (unsigned long)from + len);
833
834 return -EINVAL;
835 }
836
837 /* Offset within the first chip that the first read should start. */
838 chipnum = (from >> private->chipshift);
839 ofs = from - (chipnum << private->chipshift);
840
841 *retlen = 0;
842
843 while (len) {
844 unsigned long this_len;
845
846 if (chipnum >= private->numchips) {
847 break;
848 }
849
850 if ((len + ofs - 1) >> private->chipshift) {
851 this_len = (1 << private->chipshift) - ofs;
852 } else {
853 this_len = len;
854 }
855
856 ret = read_one_chip(map, &private->chips[chipnum], ofs,
857 this_len, buf);
858 if (ret) {
859 break;
860 }
861
862 *retlen += this_len;
863 len -= this_len;
864 buf += this_len;
865
866 ofs = 0;
867 chipnum++;
868 }
869
870 return ret;
871}
872
873
874
875static int write_one_word(struct map_info *map, struct flchip *chip,
876 unsigned long adr, __u32 datum)
877{
878 unsigned long timeo = jiffies + HZ;
879 struct amd_flash_private *private = map->fldrv_priv;
880 DECLARE_WAITQUEUE(wait, current);
881 int ret = 0;
882 int times_left;
883
884retry:
885 spin_lock_bh(chip->mutex);
886
887 if (chip->state != FL_READY){
888 printk("%s: waiting for chip to write, state = %d\n",
889 map->name, chip->state);
890 set_current_state(TASK_UNINTERRUPTIBLE);
891 add_wait_queue(&chip->wq, &wait);
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000892
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 spin_unlock_bh(chip->mutex);
894
895 schedule();
896 remove_wait_queue(&chip->wq, &wait);
897 printk(KERN_INFO "%s: woke up to write\n", map->name);
898 if(signal_pending(current))
899 return -EINTR;
900
901 timeo = jiffies + HZ;
902
903 goto retry;
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000904 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
906 chip->state = FL_WRITING;
907
908 adr += chip->start;
909 ENABLE_VPP(map);
910 send_cmd(map, chip->start, CMD_PROGRAM_UNLOCK_DATA);
911 wide_write(map, datum, adr);
912
913 times_left = 500000;
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000914 while (times_left-- && flash_is_busy(map, adr, private->interleave)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 if (need_resched()) {
916 spin_unlock_bh(chip->mutex);
917 schedule();
918 spin_lock_bh(chip->mutex);
919 }
920 }
921
922 if (!times_left) {
923 printk(KERN_WARNING "%s: write to 0x%lx timed out!\n",
924 map->name, adr);
925 ret = -EIO;
926 } else {
927 __u32 verify;
928 if ((verify = wide_read(map, adr)) != datum) {
929 printk(KERN_WARNING "%s: write to 0x%lx failed. "
930 "datum = %x, verify = %x\n",
931 map->name, adr, datum, verify);
932 ret = -EIO;
933 }
934 }
935
936 DISABLE_VPP(map);
937 chip->state = FL_READY;
938 wake_up(&chip->wq);
939 spin_unlock_bh(chip->mutex);
940
941 return ret;
942}
943
944
945
946static int amd_flash_write(struct mtd_info *mtd, loff_t to , size_t len,
947 size_t *retlen, const u_char *buf)
948{
949 struct map_info *map = mtd->priv;
950 struct amd_flash_private *private = map->fldrv_priv;
951 int ret = 0;
952 int chipnum;
953 unsigned long ofs;
954 unsigned long chipstart;
955
956 *retlen = 0;
957 if (!len) {
958 return 0;
959 }
960
961 chipnum = to >> private->chipshift;
962 ofs = to - (chipnum << private->chipshift);
963 chipstart = private->chips[chipnum].start;
964
965 /* If it's not bus-aligned, do the first byte write. */
966 if (ofs & (map->buswidth - 1)) {
967 unsigned long bus_ofs = ofs & ~(map->buswidth - 1);
968 int i = ofs - bus_ofs;
969 int n = 0;
970 u_char tmp_buf[4];
971 __u32 datum;
972
973 map_copy_from(map, tmp_buf,
974 bus_ofs + private->chips[chipnum].start,
975 map->buswidth);
976 while (len && i < map->buswidth)
977 tmp_buf[i++] = buf[n++], len--;
978
979 if (map->buswidth == 2) {
980 datum = *(__u16*)tmp_buf;
981 } else if (map->buswidth == 4) {
982 datum = *(__u32*)tmp_buf;
983 } else {
984 return -EINVAL; /* should never happen, but be safe */
985 }
986
987 ret = write_one_word(map, &private->chips[chipnum], bus_ofs,
988 datum);
989 if (ret) {
990 return ret;
991 }
Thomas Gleixner1f948b42005-11-07 11:15:37 +0000992
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 ofs += n;
994 buf += n;
995 (*retlen) += n;
996
997 if (ofs >> private->chipshift) {
998 chipnum++;
999 ofs = 0;
1000 if (chipnum == private->numchips) {
1001 return 0;
1002 }
1003 }
1004 }
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001005
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 /* We are now aligned, write as much as possible. */
1007 while(len >= map->buswidth) {
1008 __u32 datum;
1009
1010 if (map->buswidth == 1) {
1011 datum = *(__u8*)buf;
1012 } else if (map->buswidth == 2) {
1013 datum = *(__u16*)buf;
1014 } else if (map->buswidth == 4) {
1015 datum = *(__u32*)buf;
1016 } else {
1017 return -EINVAL;
1018 }
1019
1020 ret = write_one_word(map, &private->chips[chipnum], ofs, datum);
1021
1022 if (ret) {
1023 return ret;
1024 }
1025
1026 ofs += map->buswidth;
1027 buf += map->buswidth;
1028 (*retlen) += map->buswidth;
1029 len -= map->buswidth;
1030
1031 if (ofs >> private->chipshift) {
1032 chipnum++;
1033 ofs = 0;
1034 if (chipnum == private->numchips) {
1035 return 0;
1036 }
1037 chipstart = private->chips[chipnum].start;
1038 }
1039 }
1040
1041 if (len & (map->buswidth - 1)) {
1042 int i = 0, n = 0;
1043 u_char tmp_buf[2];
1044 __u32 datum;
1045
1046 map_copy_from(map, tmp_buf,
1047 ofs + private->chips[chipnum].start,
1048 map->buswidth);
1049 while (len--) {
1050 tmp_buf[i++] = buf[n++];
1051 }
1052
1053 if (map->buswidth == 2) {
1054 datum = *(__u16*)tmp_buf;
1055 } else if (map->buswidth == 4) {
1056 datum = *(__u32*)tmp_buf;
1057 } else {
1058 return -EINVAL; /* should never happen, but be safe */
1059 }
1060
1061 ret = write_one_word(map, &private->chips[chipnum], ofs, datum);
1062
1063 if (ret) {
1064 return ret;
1065 }
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001066
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 (*retlen) += n;
1068 }
1069
1070 return 0;
1071}
1072
1073
1074
1075static inline int erase_one_block(struct map_info *map, struct flchip *chip,
1076 unsigned long adr, u_long size)
1077{
1078 unsigned long timeo = jiffies + HZ;
1079 struct amd_flash_private *private = map->fldrv_priv;
1080 DECLARE_WAITQUEUE(wait, current);
1081
1082retry:
1083 spin_lock_bh(chip->mutex);
1084
1085 if (chip->state != FL_READY){
1086 set_current_state(TASK_UNINTERRUPTIBLE);
1087 add_wait_queue(&chip->wq, &wait);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001088
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 spin_unlock_bh(chip->mutex);
1090
1091 schedule();
1092 remove_wait_queue(&chip->wq, &wait);
1093
1094 if (signal_pending(current)) {
1095 return -EINTR;
1096 }
1097
1098 timeo = jiffies + HZ;
1099
1100 goto retry;
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001101 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
1103 chip->state = FL_ERASING;
1104
1105 adr += chip->start;
1106 ENABLE_VPP(map);
1107 send_cmd(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA);
1108 send_cmd_to_addr(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA_2, adr);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001109
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 timeo = jiffies + (HZ * 20);
1111
1112 spin_unlock_bh(chip->mutex);
1113 msleep(1000);
1114 spin_lock_bh(chip->mutex);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001115
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 while (flash_is_busy(map, adr, private->interleave)) {
1117
1118 if (chip->state != FL_ERASING) {
1119 /* Someone's suspended the erase. Sleep */
1120 set_current_state(TASK_UNINTERRUPTIBLE);
1121 add_wait_queue(&chip->wq, &wait);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001122
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 spin_unlock_bh(chip->mutex);
1124 printk(KERN_INFO "%s: erase suspended. Sleeping\n",
1125 map->name);
1126 schedule();
1127 remove_wait_queue(&chip->wq, &wait);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001128
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 if (signal_pending(current)) {
1130 return -EINTR;
1131 }
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001132
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 timeo = jiffies + (HZ*2); /* FIXME */
1134 spin_lock_bh(chip->mutex);
1135 continue;
1136 }
1137
1138 /* OK Still waiting */
1139 if (time_after(jiffies, timeo)) {
1140 chip->state = FL_READY;
1141 spin_unlock_bh(chip->mutex);
1142 printk(KERN_WARNING "%s: waiting for erase to complete "
1143 "timed out.\n", map->name);
1144 DISABLE_VPP(map);
1145
1146 return -EIO;
1147 }
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 /* Latency issues. Drop the lock, wait a while and retry */
1150 spin_unlock_bh(chip->mutex);
1151
1152 if (need_resched())
1153 schedule();
1154 else
1155 udelay(1);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001156
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 spin_lock_bh(chip->mutex);
1158 }
1159
1160 /* Verify every single word */
1161 {
1162 int address;
1163 int error = 0;
1164 __u8 verify;
1165
1166 for (address = adr; address < (adr + size); address++) {
1167 if ((verify = map_read8(map, address)) != 0xFF) {
1168 error = 1;
1169 break;
1170 }
1171 }
1172 if (error) {
1173 chip->state = FL_READY;
1174 spin_unlock_bh(chip->mutex);
1175 printk(KERN_WARNING
1176 "%s: verify error at 0x%x, size %ld.\n",
1177 map->name, address, size);
1178 DISABLE_VPP(map);
1179
1180 return -EIO;
1181 }
1182 }
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001183
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 DISABLE_VPP(map);
1185 chip->state = FL_READY;
1186 wake_up(&chip->wq);
1187 spin_unlock_bh(chip->mutex);
1188
1189 return 0;
1190}
1191
1192
1193
1194static int amd_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
1195{
1196 struct map_info *map = mtd->priv;
1197 struct amd_flash_private *private = map->fldrv_priv;
1198 unsigned long adr, len;
1199 int chipnum;
1200 int ret = 0;
1201 int i;
1202 int first;
1203 struct mtd_erase_region_info *regions = mtd->eraseregions;
1204
1205 if (instr->addr > mtd->size) {
1206 return -EINVAL;
1207 }
1208
1209 if ((instr->len + instr->addr) > mtd->size) {
1210 return -EINVAL;
1211 }
1212
1213 /* Check that both start and end of the requested erase are
1214 * aligned with the erasesize at the appropriate addresses.
1215 */
1216
1217 i = 0;
1218
1219 /* Skip all erase regions which are ended before the start of
1220 the requested erase. Actually, to save on the calculations,
1221 we skip to the first erase region which starts after the
1222 start of the requested erase, and then go back one.
1223 */
1224
1225 while ((i < mtd->numeraseregions) &&
1226 (instr->addr >= regions[i].offset)) {
1227 i++;
1228 }
1229 i--;
1230
1231 /* OK, now i is pointing at the erase region in which this
1232 * erase request starts. Check the start of the requested
1233 * erase range is aligned with the erase size which is in
1234 * effect here.
1235 */
1236
1237 if (instr->addr & (regions[i].erasesize-1)) {
1238 return -EINVAL;
1239 }
1240
1241 /* Remember the erase region we start on. */
1242
1243 first = i;
1244
1245 /* Next, check that the end of the requested erase is aligned
1246 * with the erase region at that address.
1247 */
1248
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001249 while ((i < mtd->numeraseregions) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 ((instr->addr + instr->len) >= regions[i].offset)) {
1251 i++;
1252 }
1253
1254 /* As before, drop back one to point at the region in which
1255 * the address actually falls.
1256 */
1257
1258 i--;
1259
1260 if ((instr->addr + instr->len) & (regions[i].erasesize-1)) {
1261 return -EINVAL;
1262 }
1263
1264 chipnum = instr->addr >> private->chipshift;
1265 adr = instr->addr - (chipnum << private->chipshift);
1266 len = instr->len;
1267
1268 i = first;
1269
1270 while (len) {
1271 ret = erase_one_block(map, &private->chips[chipnum], adr,
1272 regions[i].erasesize);
1273
1274 if (ret) {
1275 return ret;
1276 }
1277
1278 adr += regions[i].erasesize;
1279 len -= regions[i].erasesize;
1280
1281 if ((adr % (1 << private->chipshift)) ==
1282 ((regions[i].offset + (regions[i].erasesize *
1283 regions[i].numblocks))
1284 % (1 << private->chipshift))) {
1285 i++;
1286 }
1287
1288 if (adr >> private->chipshift) {
1289 adr = 0;
1290 chipnum++;
1291 if (chipnum >= private->numchips) {
1292 break;
1293 }
1294 }
1295 }
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001296
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 instr->state = MTD_ERASE_DONE;
1298 mtd_erase_callback(instr);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001299
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 return 0;
1301}
1302
1303
1304
1305static void amd_flash_sync(struct mtd_info *mtd)
1306{
1307 struct map_info *map = mtd->priv;
1308 struct amd_flash_private *private = map->fldrv_priv;
1309 int i;
1310 struct flchip *chip;
1311 int ret = 0;
1312 DECLARE_WAITQUEUE(wait, current);
1313
1314 for (i = 0; !ret && (i < private->numchips); i++) {
1315 chip = &private->chips[i];
1316
1317 retry:
1318 spin_lock_bh(chip->mutex);
1319
1320 switch(chip->state) {
1321 case FL_READY:
1322 case FL_STATUS:
1323 case FL_CFI_QUERY:
1324 case FL_JEDEC_QUERY:
1325 chip->oldstate = chip->state;
1326 chip->state = FL_SYNCING;
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001327 /* No need to wake_up() on this state change -
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 * as the whole point is that nobody can do anything
1329 * with the chip now anyway.
1330 */
1331 case FL_SYNCING:
1332 spin_unlock_bh(chip->mutex);
1333 break;
1334
1335 default:
1336 /* Not an idle state */
1337 add_wait_queue(&chip->wq, &wait);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001338
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 spin_unlock_bh(chip->mutex);
1340
1341 schedule();
1342
1343 remove_wait_queue(&chip->wq, &wait);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001344
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 goto retry;
1346 }
1347 }
1348
1349 /* Unlock the chips again */
1350 for (i--; i >= 0; i--) {
1351 chip = &private->chips[i];
1352
1353 spin_lock_bh(chip->mutex);
Thomas Gleixner1f948b42005-11-07 11:15:37 +00001354
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 if (chip->state == FL_SYNCING) {
1356 chip->state = chip->oldstate;
1357 wake_up(&chip->wq);
1358 }
1359 spin_unlock_bh(chip->mutex);
1360 }
1361}
1362
1363
1364
1365static int amd_flash_suspend(struct mtd_info *mtd)
1366{
1367printk("amd_flash_suspend(): not implemented!\n");
1368 return -EINVAL;
1369}
1370
1371
1372
1373static void amd_flash_resume(struct mtd_info *mtd)
1374{
1375printk("amd_flash_resume(): not implemented!\n");
1376}
1377
1378
1379
1380static void amd_flash_destroy(struct mtd_info *mtd)
1381{
1382 struct map_info *map = mtd->priv;
1383 struct amd_flash_private *private = map->fldrv_priv;
1384 kfree(private);
1385}
1386
1387int __init amd_flash_init(void)
1388{
1389 register_mtd_chip_driver(&amd_flash_chipdrv);
1390 return 0;
1391}
1392
1393void __exit amd_flash_exit(void)
1394{
1395 unregister_mtd_chip_driver(&amd_flash_chipdrv);
1396}
1397
1398module_init(amd_flash_init);
1399module_exit(amd_flash_exit);
1400
1401MODULE_LICENSE("GPL");
1402MODULE_AUTHOR("Jonas Holmberg <jonas.holmberg@axis.com>");
1403MODULE_DESCRIPTION("Old MTD chip driver for AMD flash chips");