diff options
author | David Brownell <david-b@pacbell.net> | 2007-06-24 18:12:35 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-06-28 17:37:36 -0400 |
commit | fa0a8c71f352d89c54f2d3a92f7a8a97cdb7d9a4 (patch) | |
tree | 76e6f0d1ffe0bd02d7d38c3f7c2902d0b140fe18 /drivers/mtd/devices | |
parent | 7d5230ea3987ea3eaa03601fe429cb69f87de3e3 (diff) |
[MTD] m25p80 handles more chips, uses JEDEC ids and small eraseblocks
Update chip ID tables in m25p80 to handle more SPI flash chips, matching
datasheets. All of these can use the same core operations and are newer
chips that support the JEDEC "read id" instruction:
- Atmel AT25 and AT26 (seven chips)
- Spansion S25SL (five chips)
- SST 25VF (four chips)
- ST M25, M45 (five more chips)
- Winbond W25X series (seven chips)
That JEDEC instruction is now used, either to support a sanity check on the
platform data holding board configuration data, or to determine chip type
when it's not included in platform data. In fact, boards that don't need a
standard partition table may not need that platform data any more.
For chips that support 4KiB erase units, use that smaller block size instead
of the larger size (usually 64KiB); it's less wasteful. (Tested on W25X80.)
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers/mtd/devices')
-rw-r--r-- | drivers/mtd/devices/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mtd/devices/m25p80.c | 234 |
2 files changed, 194 insertions, 57 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index ff642f8fbee7..b4ea64dc9360 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig | |||
@@ -69,12 +69,21 @@ config MTD_DATAFLASH26 | |||
69 | If you have such a board and such a DataFlash, say 'Y'. | 69 | If you have such a board and such a DataFlash, say 'Y'. |
70 | 70 | ||
71 | config MTD_M25P80 | 71 | config MTD_M25P80 |
72 | tristate "Support for M25 SPI Flash" | 72 | tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" |
73 | depends on SPI_MASTER && EXPERIMENTAL | 73 | depends on SPI_MASTER && EXPERIMENTAL |
74 | help | 74 | help |
75 | This enables access to ST M25P80 and similar SPI flash chips, | 75 | This enables access to most modern SPI flash chips, used for |
76 | used for program and data storage. Set up your spi devices | 76 | program and data storage. Series supported include Atmel AT26DF, |
77 | with the right board-specific platform data. | 77 | Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips |
78 | are supported as well. See the driver source for the current list, | ||
79 | or to add other chips. | ||
80 | |||
81 | Note that the original DataFlash chips (AT45 series, not AT26DF), | ||
82 | need an entirely different driver. | ||
83 | |||
84 | Set up your spi devices with the right board-specific platform data, | ||
85 | if you want to specify device partitioning or to use a device which | ||
86 | doesn't support the JEDEC ID instruction. | ||
78 | 87 | ||
79 | config MTD_SLRAM | 88 | config MTD_SLRAM |
80 | tristate "Uncached system RAM" | 89 | tristate "Uncached system RAM" |
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 7eaa61862a00..6668a8c27cb7 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * MTD SPI driver for ST M25Pxx flash chips | 2 | * MTD SPI driver for ST M25Pxx (and similar) serial flash chips |
3 | * | 3 | * |
4 | * Author: Mike Lavender, mike@steroidmicros.com | 4 | * Author: Mike Lavender, mike@steroidmicros.com |
5 | * | 5 | * |
@@ -28,25 +28,23 @@ | |||
28 | #include <linux/spi/flash.h> | 28 | #include <linux/spi/flash.h> |
29 | 29 | ||
30 | 30 | ||
31 | /* NOTE: AT 25F and SST 25LF series are very similar, | ||
32 | * as are other newer Atmel dataflash chips (AT26), | ||
33 | * but commands for sector erase and chip id differ... | ||
34 | */ | ||
35 | |||
36 | #define FLASH_PAGESIZE 256 | 31 | #define FLASH_PAGESIZE 256 |
37 | 32 | ||
38 | /* Flash opcodes. */ | 33 | /* Flash opcodes. */ |
39 | #define OPCODE_WREN 6 /* Write enable */ | 34 | #define OPCODE_WREN 0x06 /* Write enable */ |
40 | #define OPCODE_RDSR 5 /* Read status register */ | 35 | #define OPCODE_RDSR 0x05 /* Read status register */ |
41 | #define OPCODE_READ 3 /* Read data bytes */ | 36 | #define OPCODE_READ 0x03 /* Read data bytes (low frequency) */ |
42 | #define OPCODE_PP 2 /* Page program */ | 37 | #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ |
43 | #define OPCODE_SE 0xd8 /* Sector erase */ | 38 | #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ |
44 | #define OPCODE_RES 0xab /* Read Electronic Signature */ | 39 | #define OPCODE_BE_4K 0x20 /* Erase 4K block */ |
40 | #define OPCODE_BE_32K 0x52 /* Erase 32K block */ | ||
41 | #define OPCODE_SE 0xd8 /* Sector erase (usually 64K) */ | ||
45 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ | 42 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ |
46 | 43 | ||
47 | /* Status Register bits. */ | 44 | /* Status Register bits. */ |
48 | #define SR_WIP 1 /* Write in progress */ | 45 | #define SR_WIP 1 /* Write in progress */ |
49 | #define SR_WEL 2 /* Write enable latch */ | 46 | #define SR_WEL 2 /* Write enable latch */ |
47 | /* meaning of other SR_* bits may differ between vendors */ | ||
50 | #define SR_BP0 4 /* Block protect 0 */ | 48 | #define SR_BP0 4 /* Block protect 0 */ |
51 | #define SR_BP1 8 /* Block protect 1 */ | 49 | #define SR_BP1 8 /* Block protect 1 */ |
52 | #define SR_BP2 0x10 /* Block protect 2 */ | 50 | #define SR_BP2 0x10 /* Block protect 2 */ |
@@ -68,7 +66,8 @@ struct m25p { | |||
68 | struct spi_device *spi; | 66 | struct spi_device *spi; |
69 | struct mutex lock; | 67 | struct mutex lock; |
70 | struct mtd_info mtd; | 68 | struct mtd_info mtd; |
71 | unsigned partitioned; | 69 | unsigned partitioned:1; |
70 | u8 erase_opcode; | ||
72 | u8 command[4]; | 71 | u8 command[4]; |
73 | }; | 72 | }; |
74 | 73 | ||
@@ -151,8 +150,9 @@ static int wait_till_ready(struct m25p *flash) | |||
151 | */ | 150 | */ |
152 | static int erase_sector(struct m25p *flash, u32 offset) | 151 | static int erase_sector(struct m25p *flash, u32 offset) |
153 | { | 152 | { |
154 | DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id, | 153 | DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dK at 0x%08x\n", |
155 | __FUNCTION__, offset); | 154 | flash->spi->dev.bus_id, __FUNCTION__, |
155 | flash->mtd.erasesize / 1024, offset); | ||
156 | 156 | ||
157 | /* Wait until finished previous write command. */ | 157 | /* Wait until finished previous write command. */ |
158 | if (wait_till_ready(flash)) | 158 | if (wait_till_ready(flash)) |
@@ -162,7 +162,7 @@ static int erase_sector(struct m25p *flash, u32 offset) | |||
162 | write_enable(flash); | 162 | write_enable(flash); |
163 | 163 | ||
164 | /* Set up command buffer. */ | 164 | /* Set up command buffer. */ |
165 | flash->command[0] = OPCODE_SE; | 165 | flash->command[0] = flash->erase_opcode; |
166 | flash->command[1] = offset >> 16; | 166 | flash->command[1] = offset >> 16; |
167 | flash->command[2] = offset >> 8; | 167 | flash->command[2] = offset >> 8; |
168 | flash->command[3] = offset; | 168 | flash->command[3] = offset; |
@@ -204,6 +204,10 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
204 | 204 | ||
205 | mutex_lock(&flash->lock); | 205 | mutex_lock(&flash->lock); |
206 | 206 | ||
207 | /* REVISIT in some cases we could speed up erasing large regions | ||
208 | * by using OPCODE_SE instead of OPCODE_BE_4K | ||
209 | */ | ||
210 | |||
207 | /* now erase those sectors */ | 211 | /* now erase those sectors */ |
208 | while (len) { | 212 | while (len) { |
209 | if (erase_sector(flash, addr)) { | 213 | if (erase_sector(flash, addr)) { |
@@ -270,7 +274,10 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
270 | return 1; | 274 | return 1; |
271 | } | 275 | } |
272 | 276 | ||
273 | /* NOTE: OPCODE_FAST_READ (if available) is faster... */ | 277 | /* FIXME switch to OPCODE_FAST_READ. It's required for higher |
278 | * clocks; and at this writing, every chip this driver handles | ||
279 | * supports that opcode. | ||
280 | */ | ||
274 | 281 | ||
275 | /* Set up the write data buffer. */ | 282 | /* Set up the write data buffer. */ |
276 | flash->command[0] = OPCODE_READ; | 283 | flash->command[0] = OPCODE_READ; |
@@ -399,24 +406,118 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
399 | 406 | ||
400 | struct flash_info { | 407 | struct flash_info { |
401 | char *name; | 408 | char *name; |
402 | u8 id; | 409 | |
403 | u16 jedec_id; | 410 | /* JEDEC id zero means "no ID" (most older chips); otherwise it has |
411 | * a high byte of zero plus three data bytes: the manufacturer id, | ||
412 | * then a two byte device id. | ||
413 | */ | ||
414 | u32 jedec_id; | ||
415 | |||
416 | /* The size listed here is what works with OPCODE_SE, which isn't | ||
417 | * necessarily called a "sector" by the vendor. | ||
418 | */ | ||
404 | unsigned sector_size; | 419 | unsigned sector_size; |
405 | unsigned n_sectors; | 420 | u16 n_sectors; |
421 | |||
422 | u16 flags; | ||
423 | #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ | ||
406 | }; | 424 | }; |
407 | 425 | ||
426 | |||
427 | /* NOTE: double check command sets and memory organization when you add | ||
428 | * more flash chips. This current list focusses on newer chips, which | ||
429 | * have been converging on command sets which including JEDEC ID. | ||
430 | */ | ||
408 | static struct flash_info __devinitdata m25p_data [] = { | 431 | static struct flash_info __devinitdata m25p_data [] = { |
409 | /* JEDEC id zero means "has no ID" */ | 432 | |
410 | { "m25p05", 0x05, 0x2010, 32 * 1024, 2 }, | 433 | /* Atmel -- some are (confusingly) marketed as "DataFlash" */ |
411 | { "m25p10", 0x10, 0x2011, 32 * 1024, 4 }, | 434 | { "at25fs010", 0x1f6601, 32 * 1024, 4, SECT_4K, }, |
412 | { "m25p20", 0x11, 0x2012, 64 * 1024, 4 }, | 435 | { "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, }, |
413 | { "m25p40", 0x12, 0x2013, 64 * 1024, 8 }, | 436 | |
414 | { "m25p80", 0x13, 0x0000, 64 * 1024, 16 }, | 437 | { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, }, |
415 | { "m25p16", 0x14, 0x2015, 64 * 1024, 32 }, | 438 | |
416 | { "m25p32", 0x15, 0x2016, 64 * 1024, 64 }, | 439 | { "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, }, |
417 | { "m25p64", 0x16, 0x2017, 64 * 1024, 128 }, | 440 | { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, }, |
441 | { "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, }, | ||
442 | { "at26df321", 0x1f4701, 64 * 1024, 64, SECT_4K, }, | ||
443 | |||
444 | /* Spansion -- single (large) sector size only, at least | ||
445 | * for the chips listed here (without boot sectors). | ||
446 | */ | ||
447 | { "s25sl004a", 0x010212, 64 * 1024, 8, }, | ||
448 | { "s25sl008a", 0x010213, 64 * 1024, 16, }, | ||
449 | { "s25sl016a", 0x010214, 64 * 1024, 32, }, | ||
450 | { "s25sl032a", 0x010215, 64 * 1024, 64, }, | ||
451 | { "s25sl064a", 0x010216, 64 * 1024, 128, }, | ||
452 | |||
453 | /* SST -- large erase sizes are "overlays", "sectors" are 4K */ | ||
454 | { "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, }, | ||
455 | { "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, }, | ||
456 | { "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, }, | ||
457 | { "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, }, | ||
458 | |||
459 | /* ST Microelectronics -- newer production may have feature updates */ | ||
460 | { "m25p05", 0x202010, 32 * 1024, 2, }, | ||
461 | { "m25p10", 0x202011, 32 * 1024, 4, }, | ||
462 | { "m25p20", 0x202012, 64 * 1024, 4, }, | ||
463 | { "m25p40", 0x202013, 64 * 1024, 8, }, | ||
464 | { "m25p80", 0, 64 * 1024, 16, }, | ||
465 | { "m25p16", 0x202015, 64 * 1024, 32, }, | ||
466 | { "m25p32", 0x202016, 64 * 1024, 64, }, | ||
467 | { "m25p64", 0x202017, 64 * 1024, 128, }, | ||
468 | { "m25p128", 0x202018, 256 * 1024, 64, }, | ||
469 | |||
470 | { "m45pe80", 0x204014, 64 * 1024, 16, }, | ||
471 | { "m45pe16", 0x204015, 64 * 1024, 32, }, | ||
472 | |||
473 | { "m25pe80", 0x208014, 64 * 1024, 16, }, | ||
474 | { "m25pe16", 0x208015, 64 * 1024, 32, SECT_4K, }, | ||
475 | |||
476 | /* Winbond -- w25x "blocks" are 64K, "sectors" are 4K */ | ||
477 | { "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, }, | ||
478 | { "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, }, | ||
479 | { "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, }, | ||
480 | { "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, }, | ||
481 | { "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, }, | ||
482 | { "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, }, | ||
483 | { "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, }, | ||
418 | }; | 484 | }; |
419 | 485 | ||
486 | static struct flash_info *__devinit jedec_probe(struct spi_device *spi) | ||
487 | { | ||
488 | int tmp; | ||
489 | u8 code = OPCODE_RDID; | ||
490 | u8 id[3]; | ||
491 | u32 jedec; | ||
492 | struct flash_info *info; | ||
493 | |||
494 | /* JEDEC also defines an optional "extended device information" | ||
495 | * string for after vendor-specific data, after the three bytes | ||
496 | * we use here. Supporting some chips might require using it. | ||
497 | */ | ||
498 | tmp = spi_write_then_read(spi, &code, 1, id, 3); | ||
499 | if (tmp < 0) { | ||
500 | DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", | ||
501 | spi->dev.bus_id, tmp); | ||
502 | return NULL; | ||
503 | } | ||
504 | jedec = id[0]; | ||
505 | jedec = jedec << 8; | ||
506 | jedec |= id[1]; | ||
507 | jedec = jedec << 8; | ||
508 | jedec |= id[2]; | ||
509 | |||
510 | for (tmp = 0, info = m25p_data; | ||
511 | tmp < ARRAY_SIZE(m25p_data); | ||
512 | tmp++, info++) { | ||
513 | if (info->jedec_id == jedec) | ||
514 | return info; | ||
515 | } | ||
516 | dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); | ||
517 | return NULL; | ||
518 | } | ||
519 | |||
520 | |||
420 | /* | 521 | /* |
421 | * board specific setup should have ensured the SPI clock used here | 522 | * board specific setup should have ensured the SPI clock used here |
422 | * matches what the READ command supports, at least until this driver | 523 | * matches what the READ command supports, at least until this driver |
@@ -430,27 +531,41 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
430 | unsigned i; | 531 | unsigned i; |
431 | 532 | ||
432 | /* Platform data helps sort out which chip type we have, as | 533 | /* Platform data helps sort out which chip type we have, as |
433 | * well as how this board partitions it. | 534 | * well as how this board partitions it. If we don't have |
535 | * a chip ID, try the JEDEC id commands; they'll work for most | ||
536 | * newer chips, even if we don't recognize the particular chip. | ||
434 | */ | 537 | */ |
435 | data = spi->dev.platform_data; | 538 | data = spi->dev.platform_data; |
436 | if (!data || !data->type) { | 539 | if (data && data->type) { |
437 | /* FIXME some chips can identify themselves with RES | 540 | for (i = 0, info = m25p_data; |
438 | * or JEDEC get-id commands. Try them ... | 541 | i < ARRAY_SIZE(m25p_data); |
439 | */ | 542 | i++, info++) { |
440 | DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n", | 543 | if (strcmp(data->type, info->name) == 0) |
441 | spi->dev.bus_id); | 544 | break; |
442 | return -ENODEV; | 545 | } |
443 | } | ||
444 | 546 | ||
445 | for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) { | 547 | /* unrecognized chip? */ |
446 | if (strcmp(data->type, info->name) == 0) | 548 | if (i == ARRAY_SIZE(m25p_data)) { |
447 | break; | 549 | DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", |
448 | } | 550 | spi->dev.bus_id, data->type); |
449 | if (i == ARRAY_SIZE(m25p_data)) { | 551 | info = NULL; |
450 | DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n", | 552 | |
451 | spi->dev.bus_id, data->type); | 553 | /* recognized; is that chip really what's there? */ |
554 | } else if (info->jedec_id) { | ||
555 | struct flash_info *chip = jedec_probe(spi); | ||
556 | |||
557 | if (!chip || chip != info) { | ||
558 | dev_warn(&spi->dev, "found %s, expected %s\n", | ||
559 | chip ? chip->name : "UNKNOWN", | ||
560 | info->name); | ||
561 | info = NULL; | ||
562 | } | ||
563 | } | ||
564 | } else | ||
565 | info = jedec_probe(spi); | ||
566 | |||
567 | if (!info) | ||
452 | return -ENODEV; | 568 | return -ENODEV; |
453 | } | ||
454 | 569 | ||
455 | flash = kzalloc(sizeof *flash, GFP_KERNEL); | 570 | flash = kzalloc(sizeof *flash, GFP_KERNEL); |
456 | if (!flash) | 571 | if (!flash) |
@@ -460,7 +575,7 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
460 | mutex_init(&flash->lock); | 575 | mutex_init(&flash->lock); |
461 | dev_set_drvdata(&spi->dev, flash); | 576 | dev_set_drvdata(&spi->dev, flash); |
462 | 577 | ||
463 | if (data->name) | 578 | if (data && data->name) |
464 | flash->mtd.name = data->name; | 579 | flash->mtd.name = data->name; |
465 | else | 580 | else |
466 | flash->mtd.name = spi->dev.bus_id; | 581 | flash->mtd.name = spi->dev.bus_id; |
@@ -469,11 +584,19 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
469 | flash->mtd.writesize = 1; | 584 | flash->mtd.writesize = 1; |
470 | flash->mtd.flags = MTD_CAP_NORFLASH; | 585 | flash->mtd.flags = MTD_CAP_NORFLASH; |
471 | flash->mtd.size = info->sector_size * info->n_sectors; | 586 | flash->mtd.size = info->sector_size * info->n_sectors; |
472 | flash->mtd.erasesize = info->sector_size; | ||
473 | flash->mtd.erase = m25p80_erase; | 587 | flash->mtd.erase = m25p80_erase; |
474 | flash->mtd.read = m25p80_read; | 588 | flash->mtd.read = m25p80_read; |
475 | flash->mtd.write = m25p80_write; | 589 | flash->mtd.write = m25p80_write; |
476 | 590 | ||
591 | /* prefer "small sector" erase if possible */ | ||
592 | if (info->flags & SECT_4K) { | ||
593 | flash->erase_opcode = OPCODE_BE_4K; | ||
594 | flash->mtd.erasesize = 4096; | ||
595 | } else { | ||
596 | flash->erase_opcode = OPCODE_SE; | ||
597 | flash->mtd.erasesize = info->sector_size; | ||
598 | } | ||
599 | |||
477 | dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, | 600 | dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, |
478 | flash->mtd.size / 1024); | 601 | flash->mtd.size / 1024); |
479 | 602 | ||
@@ -517,14 +640,14 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
517 | } | 640 | } |
518 | 641 | ||
519 | if (nr_parts > 0) { | 642 | if (nr_parts > 0) { |
520 | for (i = 0; i < data->nr_parts; i++) { | 643 | for (i = 0; i < nr_parts; i++) { |
521 | DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " | 644 | DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " |
522 | "{.name = %s, .offset = 0x%.8x, " | 645 | "{.name = %s, .offset = 0x%.8x, " |
523 | ".size = 0x%.8x (%uK) }\n", | 646 | ".size = 0x%.8x (%uK) }\n", |
524 | i, data->parts[i].name, | 647 | i, parts[i].name, |
525 | data->parts[i].offset, | 648 | parts[i].offset, |
526 | data->parts[i].size, | 649 | parts[i].size, |
527 | data->parts[i].size / 1024); | 650 | parts[i].size / 1024); |
528 | } | 651 | } |
529 | flash->partitioned = 1; | 652 | flash->partitioned = 1; |
530 | return add_mtd_partitions(&flash->mtd, parts, nr_parts); | 653 | return add_mtd_partitions(&flash->mtd, parts, nr_parts); |
@@ -561,6 +684,11 @@ static struct spi_driver m25p80_driver = { | |||
561 | }, | 684 | }, |
562 | .probe = m25p_probe, | 685 | .probe = m25p_probe, |
563 | .remove = __devexit_p(m25p_remove), | 686 | .remove = __devexit_p(m25p_remove), |
687 | |||
688 | /* REVISIT: many of these chips have deep power-down modes, which | ||
689 | * should clearly be entered on suspend() to minimize power use. | ||
690 | * And also when they're otherwise idle... | ||
691 | */ | ||
564 | }; | 692 | }; |
565 | 693 | ||
566 | 694 | ||