diff options
Diffstat (limited to 'drivers/mtd/devices')
-rw-r--r-- | drivers/mtd/devices/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mtd/devices/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/devices/lart.c | 6 | ||||
-rw-r--r-- | drivers/mtd/devices/m25p80.c | 137 | ||||
-rw-r--r-- | drivers/mtd/devices/mtd_dataflash.c | 4 | ||||
-rw-r--r-- | drivers/mtd/devices/phram.c | 25 | ||||
-rw-r--r-- | drivers/mtd/devices/slram.c | 2 | ||||
-rw-r--r-- | drivers/mtd/devices/sst25l.c | 512 |
8 files changed, 677 insertions, 20 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 325fab92a62c..c222514bb70d 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig | |||
@@ -104,6 +104,16 @@ config M25PXX_USE_FAST_READ | |||
104 | help | 104 | help |
105 | This option enables FAST_READ access supported by ST M25Pxx. | 105 | This option enables FAST_READ access supported by ST M25Pxx. |
106 | 106 | ||
107 | config MTD_SST25L | ||
108 | tristate "Support SST25L (non JEDEC) SPI Flash chips" | ||
109 | depends on SPI_MASTER | ||
110 | help | ||
111 | This enables access to the non JEDEC SST25L SPI flash chips, used | ||
112 | for program and data storage. | ||
113 | |||
114 | Set up your spi devices with the right board-specific platform data, | ||
115 | if you want to specify device partitioning. | ||
116 | |||
107 | config MTD_SLRAM | 117 | config MTD_SLRAM |
108 | tristate "Uncached system RAM" | 118 | tristate "Uncached system RAM" |
109 | help | 119 | help |
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0993d5cf3923..ab5c9b92ac82 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile | |||
@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o | |||
16 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o | 16 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
17 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o | 17 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
18 | obj-$(CONFIG_MTD_M25P80) += m25p80.o | 18 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
19 | obj-$(CONFIG_MTD_SST25L) += sst25l.o | ||
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 578de1c67bfe..f4359fe7150f 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c | |||
@@ -393,7 +393,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) | |||
393 | * erase range is aligned with the erase size which is in | 393 | * erase range is aligned with the erase size which is in |
394 | * effect here. | 394 | * effect here. |
395 | */ | 395 | */ |
396 | if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); | 396 | if (i < 0 || (instr->addr & (mtd->eraseregions[i].erasesize - 1))) |
397 | return -EINVAL; | ||
397 | 398 | ||
398 | /* Remember the erase region we start on */ | 399 | /* Remember the erase region we start on */ |
399 | first = i; | 400 | first = i; |
@@ -409,7 +410,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) | |||
409 | i--; | 410 | i--; |
410 | 411 | ||
411 | /* is the end aligned on a block boundary? */ | 412 | /* is the end aligned on a block boundary? */ |
412 | if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); | 413 | if (i < 0 || ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1))) |
414 | return -EINVAL; | ||
413 | 415 | ||
414 | addr = instr->addr; | 416 | addr = instr->addr; |
415 | len = instr->len; | 417 | len = instr->len; |
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index eb495d83064f..379c316f329e 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
@@ -44,6 +44,11 @@ | |||
44 | #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ | 44 | #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ |
45 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ | 45 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ |
46 | 46 | ||
47 | /* Used for SST flashes only. */ | ||
48 | #define OPCODE_BP 0x02 /* Byte program */ | ||
49 | #define OPCODE_WRDI 0x04 /* Write disable */ | ||
50 | #define OPCODE_AAI_WP 0xad /* Auto address increment word program */ | ||
51 | |||
47 | /* Status Register bits. */ | 52 | /* Status Register bits. */ |
48 | #define SR_WIP 1 /* Write in progress */ | 53 | #define SR_WIP 1 /* Write in progress */ |
49 | #define SR_WEL 2 /* Write enable latch */ | 54 | #define SR_WEL 2 /* Write enable latch */ |
@@ -132,6 +137,15 @@ static inline int write_enable(struct m25p *flash) | |||
132 | return spi_write_then_read(flash->spi, &code, 1, NULL, 0); | 137 | return spi_write_then_read(flash->spi, &code, 1, NULL, 0); |
133 | } | 138 | } |
134 | 139 | ||
140 | /* | ||
141 | * Send write disble instruction to the chip. | ||
142 | */ | ||
143 | static inline int write_disable(struct m25p *flash) | ||
144 | { | ||
145 | u8 code = OPCODE_WRDI; | ||
146 | |||
147 | return spi_write_then_read(flash->spi, &code, 1, NULL, 0); | ||
148 | } | ||
135 | 149 | ||
136 | /* | 150 | /* |
137 | * Service routine to read status register until ready, or timeout occurs. | 151 | * Service routine to read status register until ready, or timeout occurs. |
@@ -454,6 +468,111 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
454 | return 0; | 468 | return 0; |
455 | } | 469 | } |
456 | 470 | ||
471 | static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
472 | size_t *retlen, const u_char *buf) | ||
473 | { | ||
474 | struct m25p *flash = mtd_to_m25p(mtd); | ||
475 | struct spi_transfer t[2]; | ||
476 | struct spi_message m; | ||
477 | size_t actual; | ||
478 | int cmd_sz, ret; | ||
479 | |||
480 | if (retlen) | ||
481 | *retlen = 0; | ||
482 | |||
483 | /* sanity checks */ | ||
484 | if (!len) | ||
485 | return 0; | ||
486 | |||
487 | if (to + len > flash->mtd.size) | ||
488 | return -EINVAL; | ||
489 | |||
490 | spi_message_init(&m); | ||
491 | memset(t, 0, (sizeof t)); | ||
492 | |||
493 | t[0].tx_buf = flash->command; | ||
494 | t[0].len = CMD_SIZE; | ||
495 | spi_message_add_tail(&t[0], &m); | ||
496 | |||
497 | t[1].tx_buf = buf; | ||
498 | spi_message_add_tail(&t[1], &m); | ||
499 | |||
500 | mutex_lock(&flash->lock); | ||
501 | |||
502 | /* Wait until finished previous write command. */ | ||
503 | ret = wait_till_ready(flash); | ||
504 | if (ret) | ||
505 | goto time_out; | ||
506 | |||
507 | write_enable(flash); | ||
508 | |||
509 | actual = to % 2; | ||
510 | /* Start write from odd address. */ | ||
511 | if (actual) { | ||
512 | flash->command[0] = OPCODE_BP; | ||
513 | flash->command[1] = to >> 16; | ||
514 | flash->command[2] = to >> 8; | ||
515 | flash->command[3] = to; | ||
516 | |||
517 | /* write one byte. */ | ||
518 | t[1].len = 1; | ||
519 | spi_sync(flash->spi, &m); | ||
520 | ret = wait_till_ready(flash); | ||
521 | if (ret) | ||
522 | goto time_out; | ||
523 | *retlen += m.actual_length - CMD_SIZE; | ||
524 | } | ||
525 | to += actual; | ||
526 | |||
527 | flash->command[0] = OPCODE_AAI_WP; | ||
528 | flash->command[1] = to >> 16; | ||
529 | flash->command[2] = to >> 8; | ||
530 | flash->command[3] = to; | ||
531 | |||
532 | /* Write out most of the data here. */ | ||
533 | cmd_sz = CMD_SIZE; | ||
534 | for (; actual < len - 1; actual += 2) { | ||
535 | t[0].len = cmd_sz; | ||
536 | /* write two bytes. */ | ||
537 | t[1].len = 2; | ||
538 | t[1].tx_buf = buf + actual; | ||
539 | |||
540 | spi_sync(flash->spi, &m); | ||
541 | ret = wait_till_ready(flash); | ||
542 | if (ret) | ||
543 | goto time_out; | ||
544 | *retlen += m.actual_length - cmd_sz; | ||
545 | cmd_sz = 1; | ||
546 | to += 2; | ||
547 | } | ||
548 | write_disable(flash); | ||
549 | ret = wait_till_ready(flash); | ||
550 | if (ret) | ||
551 | goto time_out; | ||
552 | |||
553 | /* Write out trailing byte if it exists. */ | ||
554 | if (actual != len) { | ||
555 | write_enable(flash); | ||
556 | flash->command[0] = OPCODE_BP; | ||
557 | flash->command[1] = to >> 16; | ||
558 | flash->command[2] = to >> 8; | ||
559 | flash->command[3] = to; | ||
560 | t[0].len = CMD_SIZE; | ||
561 | t[1].len = 1; | ||
562 | t[1].tx_buf = buf + actual; | ||
563 | |||
564 | spi_sync(flash->spi, &m); | ||
565 | ret = wait_till_ready(flash); | ||
566 | if (ret) | ||
567 | goto time_out; | ||
568 | *retlen += m.actual_length - CMD_SIZE; | ||
569 | write_disable(flash); | ||
570 | } | ||
571 | |||
572 | time_out: | ||
573 | mutex_unlock(&flash->lock); | ||
574 | return ret; | ||
575 | } | ||
457 | 576 | ||
458 | /****************************************************************************/ | 577 | /****************************************************************************/ |
459 | 578 | ||
@@ -501,7 +620,10 @@ static struct flash_info __devinitdata m25p_data [] = { | |||
501 | { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, | 620 | { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, |
502 | 621 | ||
503 | /* Macronix */ | 622 | /* Macronix */ |
623 | { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, }, | ||
624 | { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, }, | ||
504 | { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, | 625 | { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, |
626 | { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, }, | ||
505 | 627 | ||
506 | /* Spansion -- single (large) sector size only, at least | 628 | /* Spansion -- single (large) sector size only, at least |
507 | * for the chips listed here (without boot sectors). | 629 | * for the chips listed here (without boot sectors). |
@@ -511,14 +633,20 @@ static struct flash_info __devinitdata m25p_data [] = { | |||
511 | { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, | 633 | { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, |
512 | { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, | 634 | { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, |
513 | { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, | 635 | { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, |
514 | { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, | 636 | { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, |
515 | { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, | 637 | { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, |
638 | { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, }, | ||
639 | { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, }, | ||
516 | 640 | ||
517 | /* SST -- large erase sizes are "overlays", "sectors" are 4K */ | 641 | /* SST -- large erase sizes are "overlays", "sectors" are 4K */ |
518 | { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, }, | 642 | { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, }, |
519 | { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, }, | 643 | { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, }, |
520 | { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, }, | 644 | { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, }, |
521 | { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, }, | 645 | { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, }, |
646 | { "sst25wf512", 0xbf2501, 0, 64 * 1024, 1, SECT_4K, }, | ||
647 | { "sst25wf010", 0xbf2502, 0, 64 * 1024, 2, SECT_4K, }, | ||
648 | { "sst25wf020", 0xbf2503, 0, 64 * 1024, 4, SECT_4K, }, | ||
649 | { "sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K, }, | ||
522 | 650 | ||
523 | /* ST Microelectronics -- newer production may have feature updates */ | 651 | /* ST Microelectronics -- newer production may have feature updates */ |
524 | { "m25p05", 0x202010, 0, 32 * 1024, 2, }, | 652 | { "m25p05", 0x202010, 0, 32 * 1024, 2, }, |
@@ -667,7 +795,12 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
667 | flash->mtd.size = info->sector_size * info->n_sectors; | 795 | flash->mtd.size = info->sector_size * info->n_sectors; |
668 | flash->mtd.erase = m25p80_erase; | 796 | flash->mtd.erase = m25p80_erase; |
669 | flash->mtd.read = m25p80_read; | 797 | flash->mtd.read = m25p80_read; |
670 | flash->mtd.write = m25p80_write; | 798 | |
799 | /* sst flash chips use AAI word program */ | ||
800 | if (info->jedec_id >> 16 == 0xbf) | ||
801 | flash->mtd.write = sst_write; | ||
802 | else | ||
803 | flash->mtd.write = m25p80_write; | ||
671 | 804 | ||
672 | /* prefer "small sector" erase if possible */ | 805 | /* prefer "small sector" erase if possible */ |
673 | if (info->flags & SECT_4K) { | 806 | if (info->flags & SECT_4K) { |
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 211c27acd01e..93e3627be74c 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c | |||
@@ -401,7 +401,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
401 | (void) dataflash_waitready(priv->spi); | 401 | (void) dataflash_waitready(priv->spi); |
402 | 402 | ||
403 | 403 | ||
404 | #ifdef CONFIG_MTD_DATAFLASH_VERIFY_WRITE | 404 | #ifdef CONFIG_MTD_DATAFLASH_WRITE_VERIFY |
405 | 405 | ||
406 | /* (3) Compare to Buffer1 */ | 406 | /* (3) Compare to Buffer1 */ |
407 | addr = pageaddr << priv->page_offset; | 407 | addr = pageaddr << priv->page_offset; |
@@ -430,7 +430,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
430 | } else | 430 | } else |
431 | status = 0; | 431 | status = 0; |
432 | 432 | ||
433 | #endif /* CONFIG_MTD_DATAFLASH_VERIFY_WRITE */ | 433 | #endif /* CONFIG_MTD_DATAFLASH_WRITE_VERIFY */ |
434 | 434 | ||
435 | remaining = remaining - writelen; | 435 | remaining = remaining - writelen; |
436 | pageaddr++; | 436 | pageaddr++; |
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 088fbb7595b5..1696bbecaa7e 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c | |||
@@ -14,6 +14,9 @@ | |||
14 | * Example: | 14 | * Example: |
15 | * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi | 15 | * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi |
16 | */ | 16 | */ |
17 | |||
18 | #define pr_fmt(fmt) "phram: " fmt | ||
19 | |||
17 | #include <asm/io.h> | 20 | #include <asm/io.h> |
18 | #include <linux/init.h> | 21 | #include <linux/init.h> |
19 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
@@ -23,8 +26,6 @@ | |||
23 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
24 | #include <linux/mtd/mtd.h> | 27 | #include <linux/mtd/mtd.h> |
25 | 28 | ||
26 | #define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) | ||
27 | |||
28 | struct phram_mtd_list { | 29 | struct phram_mtd_list { |
29 | struct mtd_info mtd; | 30 | struct mtd_info mtd; |
30 | struct list_head list; | 31 | struct list_head list; |
@@ -132,7 +133,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) | |||
132 | ret = -EIO; | 133 | ret = -EIO; |
133 | new->mtd.priv = ioremap(start, len); | 134 | new->mtd.priv = ioremap(start, len); |
134 | if (!new->mtd.priv) { | 135 | if (!new->mtd.priv) { |
135 | ERROR("ioremap failed\n"); | 136 | pr_err("ioremap failed\n"); |
136 | goto out1; | 137 | goto out1; |
137 | } | 138 | } |
138 | 139 | ||
@@ -152,7 +153,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) | |||
152 | 153 | ||
153 | ret = -EAGAIN; | 154 | ret = -EAGAIN; |
154 | if (add_mtd_device(&new->mtd)) { | 155 | if (add_mtd_device(&new->mtd)) { |
155 | ERROR("Failed to register new device\n"); | 156 | pr_err("Failed to register new device\n"); |
156 | goto out2; | 157 | goto out2; |
157 | } | 158 | } |
158 | 159 | ||
@@ -227,8 +228,8 @@ static inline void kill_final_newline(char *str) | |||
227 | 228 | ||
228 | 229 | ||
229 | #define parse_err(fmt, args...) do { \ | 230 | #define parse_err(fmt, args...) do { \ |
230 | ERROR(fmt , ## args); \ | 231 | pr_err(fmt , ## args); \ |
231 | return 0; \ | 232 | return 1; \ |
232 | } while (0) | 233 | } while (0) |
233 | 234 | ||
234 | static int phram_setup(const char *val, struct kernel_param *kp) | 235 | static int phram_setup(const char *val, struct kernel_param *kp) |
@@ -256,12 +257,8 @@ static int phram_setup(const char *val, struct kernel_param *kp) | |||
256 | parse_err("not enough arguments\n"); | 257 | parse_err("not enough arguments\n"); |
257 | 258 | ||
258 | ret = parse_name(&name, token[0]); | 259 | ret = parse_name(&name, token[0]); |
259 | if (ret == -ENOMEM) | ||
260 | parse_err("out of memory\n"); | ||
261 | if (ret == -ENOSPC) | ||
262 | parse_err("name too long\n"); | ||
263 | if (ret) | 260 | if (ret) |
264 | return 0; | 261 | return ret; |
265 | 262 | ||
266 | ret = parse_num32(&start, token[1]); | 263 | ret = parse_num32(&start, token[1]); |
267 | if (ret) { | 264 | if (ret) { |
@@ -275,9 +272,11 @@ static int phram_setup(const char *val, struct kernel_param *kp) | |||
275 | parse_err("illegal device length\n"); | 272 | parse_err("illegal device length\n"); |
276 | } | 273 | } |
277 | 274 | ||
278 | register_device(name, start, len); | 275 | ret = register_device(name, start, len); |
276 | if (!ret) | ||
277 | pr_info("%s device: %#x at %#x\n", name, len, start); | ||
279 | 278 | ||
280 | return 0; | 279 | return ret; |
281 | } | 280 | } |
282 | 281 | ||
283 | module_param_call(phram, phram_setup, NULL, NULL, 000); | 282 | module_param_call(phram, phram_setup, NULL, NULL, 000); |
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 7d846e9173da..3aa05cd18ea1 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c | |||
@@ -341,7 +341,7 @@ static int __init init_slram(void) | |||
341 | #else | 341 | #else |
342 | int count; | 342 | int count; |
343 | 343 | ||
344 | for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS); | 344 | for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count]; |
345 | count++) { | 345 | count++) { |
346 | } | 346 | } |
347 | 347 | ||
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c new file mode 100644 index 000000000000..c2baf3353f84 --- /dev/null +++ b/drivers/mtd/devices/sst25l.c | |||
@@ -0,0 +1,512 @@ | |||
1 | /* | ||
2 | * sst25l.c | ||
3 | * | ||
4 | * Driver for SST25L SPI Flash chips | ||
5 | * | ||
6 | * Copyright © 2009 Bluewater Systems Ltd | ||
7 | * Author: Andre Renaud <andre@bluewatersys.com> | ||
8 | * Author: Ryan Mallon <ryan@bluewatersys.com> | ||
9 | * | ||
10 | * Based on m25p80.c | ||
11 | * | ||
12 | * This code is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/init.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/mutex.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | |||
24 | #include <linux/mtd/mtd.h> | ||
25 | #include <linux/mtd/partitions.h> | ||
26 | |||
27 | #include <linux/spi/spi.h> | ||
28 | #include <linux/spi/flash.h> | ||
29 | |||
30 | /* Erases can take up to 3 seconds! */ | ||
31 | #define MAX_READY_WAIT_JIFFIES msecs_to_jiffies(3000) | ||
32 | |||
33 | #define SST25L_CMD_WRSR 0x01 /* Write status register */ | ||
34 | #define SST25L_CMD_WRDI 0x04 /* Write disable */ | ||
35 | #define SST25L_CMD_RDSR 0x05 /* Read status register */ | ||
36 | #define SST25L_CMD_WREN 0x06 /* Write enable */ | ||
37 | #define SST25L_CMD_READ 0x03 /* High speed read */ | ||
38 | |||
39 | #define SST25L_CMD_EWSR 0x50 /* Enable write status register */ | ||
40 | #define SST25L_CMD_SECTOR_ERASE 0x20 /* Erase sector */ | ||
41 | #define SST25L_CMD_READ_ID 0x90 /* Read device ID */ | ||
42 | #define SST25L_CMD_AAI_PROGRAM 0xaf /* Auto address increment */ | ||
43 | |||
44 | #define SST25L_STATUS_BUSY (1 << 0) /* Chip is busy */ | ||
45 | #define SST25L_STATUS_WREN (1 << 1) /* Write enabled */ | ||
46 | #define SST25L_STATUS_BP0 (1 << 2) /* Block protection 0 */ | ||
47 | #define SST25L_STATUS_BP1 (1 << 3) /* Block protection 1 */ | ||
48 | |||
49 | struct sst25l_flash { | ||
50 | struct spi_device *spi; | ||
51 | struct mutex lock; | ||
52 | struct mtd_info mtd; | ||
53 | |||
54 | int partitioned; | ||
55 | }; | ||
56 | |||
57 | struct flash_info { | ||
58 | const char *name; | ||
59 | uint16_t device_id; | ||
60 | unsigned page_size; | ||
61 | unsigned nr_pages; | ||
62 | unsigned erase_size; | ||
63 | }; | ||
64 | |||
65 | #define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd) | ||
66 | |||
67 | static struct flash_info __initdata sst25l_flash_info[] = { | ||
68 | {"sst25lf020a", 0xbf43, 256, 1024, 4096}, | ||
69 | {"sst25lf040a", 0xbf44, 256, 2048, 4096}, | ||
70 | }; | ||
71 | |||
72 | static int sst25l_status(struct sst25l_flash *flash, int *status) | ||
73 | { | ||
74 | unsigned char command, response; | ||
75 | int err; | ||
76 | |||
77 | command = SST25L_CMD_RDSR; | ||
78 | err = spi_write_then_read(flash->spi, &command, 1, &response, 1); | ||
79 | if (err < 0) | ||
80 | return err; | ||
81 | |||
82 | *status = response; | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static int sst25l_write_enable(struct sst25l_flash *flash, int enable) | ||
87 | { | ||
88 | unsigned char command[2]; | ||
89 | int status, err; | ||
90 | |||
91 | command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI; | ||
92 | err = spi_write(flash->spi, command, 1); | ||
93 | if (err) | ||
94 | return err; | ||
95 | |||
96 | command[0] = SST25L_CMD_EWSR; | ||
97 | err = spi_write(flash->spi, command, 1); | ||
98 | if (err) | ||
99 | return err; | ||
100 | |||
101 | command[0] = SST25L_CMD_WRSR; | ||
102 | command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1; | ||
103 | err = spi_write(flash->spi, command, 2); | ||
104 | if (err) | ||
105 | return err; | ||
106 | |||
107 | if (enable) { | ||
108 | err = sst25l_status(flash, &status); | ||
109 | if (err) | ||
110 | return err; | ||
111 | if (!(status & SST25L_STATUS_WREN)) | ||
112 | return -EROFS; | ||
113 | } | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int sst25l_wait_till_ready(struct sst25l_flash *flash) | ||
119 | { | ||
120 | unsigned long deadline; | ||
121 | int status, err; | ||
122 | |||
123 | deadline = jiffies + MAX_READY_WAIT_JIFFIES; | ||
124 | do { | ||
125 | err = sst25l_status(flash, &status); | ||
126 | if (err) | ||
127 | return err; | ||
128 | if (!(status & SST25L_STATUS_BUSY)) | ||
129 | return 0; | ||
130 | |||
131 | cond_resched(); | ||
132 | } while (!time_after_eq(jiffies, deadline)); | ||
133 | |||
134 | return -ETIMEDOUT; | ||
135 | } | ||
136 | |||
137 | static int sst25l_erase_sector(struct sst25l_flash *flash, uint32_t offset) | ||
138 | { | ||
139 | unsigned char command[4]; | ||
140 | int err; | ||
141 | |||
142 | err = sst25l_write_enable(flash, 1); | ||
143 | if (err) | ||
144 | return err; | ||
145 | |||
146 | command[0] = SST25L_CMD_SECTOR_ERASE; | ||
147 | command[1] = offset >> 16; | ||
148 | command[2] = offset >> 8; | ||
149 | command[3] = offset; | ||
150 | err = spi_write(flash->spi, command, 4); | ||
151 | if (err) | ||
152 | return err; | ||
153 | |||
154 | err = sst25l_wait_till_ready(flash); | ||
155 | if (err) | ||
156 | return err; | ||
157 | |||
158 | return sst25l_write_enable(flash, 0); | ||
159 | } | ||
160 | |||
161 | static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
162 | { | ||
163 | struct sst25l_flash *flash = to_sst25l_flash(mtd); | ||
164 | uint32_t addr, end; | ||
165 | int err; | ||
166 | |||
167 | /* Sanity checks */ | ||
168 | if (instr->addr + instr->len > flash->mtd.size) | ||
169 | return -EINVAL; | ||
170 | |||
171 | if ((uint32_t)instr->len % mtd->erasesize) | ||
172 | return -EINVAL; | ||
173 | |||
174 | if ((uint32_t)instr->addr % mtd->erasesize) | ||
175 | return -EINVAL; | ||
176 | |||
177 | addr = instr->addr; | ||
178 | end = addr + instr->len; | ||
179 | |||
180 | mutex_lock(&flash->lock); | ||
181 | |||
182 | err = sst25l_wait_till_ready(flash); | ||
183 | if (err) { | ||
184 | mutex_unlock(&flash->lock); | ||
185 | return err; | ||
186 | } | ||
187 | |||
188 | while (addr < end) { | ||
189 | err = sst25l_erase_sector(flash, addr); | ||
190 | if (err) { | ||
191 | mutex_unlock(&flash->lock); | ||
192 | instr->state = MTD_ERASE_FAILED; | ||
193 | dev_err(&flash->spi->dev, "Erase failed\n"); | ||
194 | return err; | ||
195 | } | ||
196 | |||
197 | addr += mtd->erasesize; | ||
198 | } | ||
199 | |||
200 | mutex_unlock(&flash->lock); | ||
201 | |||
202 | instr->state = MTD_ERASE_DONE; | ||
203 | mtd_erase_callback(instr); | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
208 | size_t *retlen, unsigned char *buf) | ||
209 | { | ||
210 | struct sst25l_flash *flash = to_sst25l_flash(mtd); | ||
211 | struct spi_transfer transfer[2]; | ||
212 | struct spi_message message; | ||
213 | unsigned char command[4]; | ||
214 | int ret; | ||
215 | |||
216 | /* Sanity checking */ | ||
217 | if (len == 0) | ||
218 | return 0; | ||
219 | |||
220 | if (from + len > flash->mtd.size) | ||
221 | return -EINVAL; | ||
222 | |||
223 | if (retlen) | ||
224 | *retlen = 0; | ||
225 | |||
226 | spi_message_init(&message); | ||
227 | memset(&transfer, 0, sizeof(transfer)); | ||
228 | |||
229 | command[0] = SST25L_CMD_READ; | ||
230 | command[1] = from >> 16; | ||
231 | command[2] = from >> 8; | ||
232 | command[3] = from; | ||
233 | |||
234 | transfer[0].tx_buf = command; | ||
235 | transfer[0].len = sizeof(command); | ||
236 | spi_message_add_tail(&transfer[0], &message); | ||
237 | |||
238 | transfer[1].rx_buf = buf; | ||
239 | transfer[1].len = len; | ||
240 | spi_message_add_tail(&transfer[1], &message); | ||
241 | |||
242 | mutex_lock(&flash->lock); | ||
243 | |||
244 | /* Wait for previous write/erase to complete */ | ||
245 | ret = sst25l_wait_till_ready(flash); | ||
246 | if (ret) { | ||
247 | mutex_unlock(&flash->lock); | ||
248 | return ret; | ||
249 | } | ||
250 | |||
251 | spi_sync(flash->spi, &message); | ||
252 | |||
253 | if (retlen && message.actual_length > sizeof(command)) | ||
254 | *retlen += message.actual_length - sizeof(command); | ||
255 | |||
256 | mutex_unlock(&flash->lock); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
261 | size_t *retlen, const unsigned char *buf) | ||
262 | { | ||
263 | struct sst25l_flash *flash = to_sst25l_flash(mtd); | ||
264 | int i, j, ret, bytes, copied = 0; | ||
265 | unsigned char command[5]; | ||
266 | |||
267 | /* Sanity checks */ | ||
268 | if (!len) | ||
269 | return 0; | ||
270 | |||
271 | if (to + len > flash->mtd.size) | ||
272 | return -EINVAL; | ||
273 | |||
274 | if ((uint32_t)to % mtd->writesize) | ||
275 | return -EINVAL; | ||
276 | |||
277 | mutex_lock(&flash->lock); | ||
278 | |||
279 | ret = sst25l_write_enable(flash, 1); | ||
280 | if (ret) | ||
281 | goto out; | ||
282 | |||
283 | for (i = 0; i < len; i += mtd->writesize) { | ||
284 | ret = sst25l_wait_till_ready(flash); | ||
285 | if (ret) | ||
286 | goto out; | ||
287 | |||
288 | /* Write the first byte of the page */ | ||
289 | command[0] = SST25L_CMD_AAI_PROGRAM; | ||
290 | command[1] = (to + i) >> 16; | ||
291 | command[2] = (to + i) >> 8; | ||
292 | command[3] = (to + i); | ||
293 | command[4] = buf[i]; | ||
294 | ret = spi_write(flash->spi, command, 5); | ||
295 | if (ret < 0) | ||
296 | goto out; | ||
297 | copied++; | ||
298 | |||
299 | /* | ||
300 | * Write the remaining bytes using auto address | ||
301 | * increment mode | ||
302 | */ | ||
303 | bytes = min_t(uint32_t, mtd->writesize, len - i); | ||
304 | for (j = 1; j < bytes; j++, copied++) { | ||
305 | ret = sst25l_wait_till_ready(flash); | ||
306 | if (ret) | ||
307 | goto out; | ||
308 | |||
309 | command[1] = buf[i + j]; | ||
310 | ret = spi_write(flash->spi, command, 2); | ||
311 | if (ret) | ||
312 | goto out; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | out: | ||
317 | ret = sst25l_write_enable(flash, 0); | ||
318 | |||
319 | if (retlen) | ||
320 | *retlen = copied; | ||
321 | |||
322 | mutex_unlock(&flash->lock); | ||
323 | return ret; | ||
324 | } | ||
325 | |||
326 | static struct flash_info *__init sst25l_match_device(struct spi_device *spi) | ||
327 | { | ||
328 | struct flash_info *flash_info = NULL; | ||
329 | unsigned char command[4], response; | ||
330 | int i, err; | ||
331 | uint16_t id; | ||
332 | |||
333 | command[0] = SST25L_CMD_READ_ID; | ||
334 | command[1] = 0; | ||
335 | command[2] = 0; | ||
336 | command[3] = 0; | ||
337 | err = spi_write_then_read(spi, command, sizeof(command), &response, 1); | ||
338 | if (err < 0) { | ||
339 | dev_err(&spi->dev, "error reading device id msb\n"); | ||
340 | return NULL; | ||
341 | } | ||
342 | |||
343 | id = response << 8; | ||
344 | |||
345 | command[0] = SST25L_CMD_READ_ID; | ||
346 | command[1] = 0; | ||
347 | command[2] = 0; | ||
348 | command[3] = 1; | ||
349 | err = spi_write_then_read(spi, command, sizeof(command), &response, 1); | ||
350 | if (err < 0) { | ||
351 | dev_err(&spi->dev, "error reading device id lsb\n"); | ||
352 | return NULL; | ||
353 | } | ||
354 | |||
355 | id |= response; | ||
356 | |||
357 | for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++) | ||
358 | if (sst25l_flash_info[i].device_id == id) | ||
359 | flash_info = &sst25l_flash_info[i]; | ||
360 | |||
361 | if (!flash_info) | ||
362 | dev_err(&spi->dev, "unknown id %.4x\n", id); | ||
363 | |||
364 | return flash_info; | ||
365 | } | ||
366 | |||
367 | static int __init sst25l_probe(struct spi_device *spi) | ||
368 | { | ||
369 | struct flash_info *flash_info; | ||
370 | struct sst25l_flash *flash; | ||
371 | struct flash_platform_data *data; | ||
372 | int ret, i; | ||
373 | |||
374 | flash_info = sst25l_match_device(spi); | ||
375 | if (!flash_info) | ||
376 | return -ENODEV; | ||
377 | |||
378 | flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL); | ||
379 | if (!flash) | ||
380 | return -ENOMEM; | ||
381 | |||
382 | flash->spi = spi; | ||
383 | mutex_init(&flash->lock); | ||
384 | dev_set_drvdata(&spi->dev, flash); | ||
385 | |||
386 | data = spi->dev.platform_data; | ||
387 | if (data && data->name) | ||
388 | flash->mtd.name = data->name; | ||
389 | else | ||
390 | flash->mtd.name = dev_name(&spi->dev); | ||
391 | |||
392 | flash->mtd.type = MTD_NORFLASH; | ||
393 | flash->mtd.flags = MTD_CAP_NORFLASH; | ||
394 | flash->mtd.erasesize = flash_info->erase_size; | ||
395 | flash->mtd.writesize = flash_info->page_size; | ||
396 | flash->mtd.size = flash_info->page_size * flash_info->nr_pages; | ||
397 | flash->mtd.erase = sst25l_erase; | ||
398 | flash->mtd.read = sst25l_read; | ||
399 | flash->mtd.write = sst25l_write; | ||
400 | |||
401 | dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name, | ||
402 | (long long)flash->mtd.size >> 10); | ||
403 | |||
404 | DEBUG(MTD_DEBUG_LEVEL2, | ||
405 | "mtd .name = %s, .size = 0x%llx (%lldMiB) " | ||
406 | ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", | ||
407 | flash->mtd.name, | ||
408 | (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), | ||
409 | flash->mtd.erasesize, flash->mtd.erasesize / 1024, | ||
410 | flash->mtd.numeraseregions); | ||
411 | |||
412 | if (flash->mtd.numeraseregions) | ||
413 | for (i = 0; i < flash->mtd.numeraseregions; i++) | ||
414 | DEBUG(MTD_DEBUG_LEVEL2, | ||
415 | "mtd.eraseregions[%d] = { .offset = 0x%llx, " | ||
416 | ".erasesize = 0x%.8x (%uKiB), " | ||
417 | ".numblocks = %d }\n", | ||
418 | i, (long long)flash->mtd.eraseregions[i].offset, | ||
419 | flash->mtd.eraseregions[i].erasesize, | ||
420 | flash->mtd.eraseregions[i].erasesize / 1024, | ||
421 | flash->mtd.eraseregions[i].numblocks); | ||
422 | |||
423 | if (mtd_has_partitions()) { | ||
424 | struct mtd_partition *parts = NULL; | ||
425 | int nr_parts = 0; | ||
426 | |||
427 | if (mtd_has_cmdlinepart()) { | ||
428 | static const char *part_probes[] = | ||
429 | {"cmdlinepart", NULL}; | ||
430 | |||
431 | nr_parts = parse_mtd_partitions(&flash->mtd, | ||
432 | part_probes, | ||
433 | &parts, 0); | ||
434 | } | ||
435 | |||
436 | if (nr_parts <= 0 && data && data->parts) { | ||
437 | parts = data->parts; | ||
438 | nr_parts = data->nr_parts; | ||
439 | } | ||
440 | |||
441 | if (nr_parts > 0) { | ||
442 | for (i = 0; i < nr_parts; i++) { | ||
443 | DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " | ||
444 | "{.name = %s, .offset = 0x%llx, " | ||
445 | ".size = 0x%llx (%lldKiB) }\n", | ||
446 | i, parts[i].name, | ||
447 | (long long)parts[i].offset, | ||
448 | (long long)parts[i].size, | ||
449 | (long long)(parts[i].size >> 10)); | ||
450 | } | ||
451 | |||
452 | flash->partitioned = 1; | ||
453 | return add_mtd_partitions(&flash->mtd, | ||
454 | parts, nr_parts); | ||
455 | } | ||
456 | |||
457 | } else if (data->nr_parts) { | ||
458 | dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", | ||
459 | data->nr_parts, data->name); | ||
460 | } | ||
461 | |||
462 | ret = add_mtd_device(&flash->mtd); | ||
463 | if (ret == 1) { | ||
464 | kfree(flash); | ||
465 | dev_set_drvdata(&spi->dev, NULL); | ||
466 | return -ENODEV; | ||
467 | } | ||
468 | |||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | static int __exit sst25l_remove(struct spi_device *spi) | ||
473 | { | ||
474 | struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); | ||
475 | int ret; | ||
476 | |||
477 | if (mtd_has_partitions() && flash->partitioned) | ||
478 | ret = del_mtd_partitions(&flash->mtd); | ||
479 | else | ||
480 | ret = del_mtd_device(&flash->mtd); | ||
481 | if (ret == 0) | ||
482 | kfree(flash); | ||
483 | return ret; | ||
484 | } | ||
485 | |||
486 | static struct spi_driver sst25l_driver = { | ||
487 | .driver = { | ||
488 | .name = "sst25l", | ||
489 | .bus = &spi_bus_type, | ||
490 | .owner = THIS_MODULE, | ||
491 | }, | ||
492 | .probe = sst25l_probe, | ||
493 | .remove = __exit_p(sst25l_remove), | ||
494 | }; | ||
495 | |||
496 | static int __init sst25l_init(void) | ||
497 | { | ||
498 | return spi_register_driver(&sst25l_driver); | ||
499 | } | ||
500 | |||
501 | static void __exit sst25l_exit(void) | ||
502 | { | ||
503 | spi_unregister_driver(&sst25l_driver); | ||
504 | } | ||
505 | |||
506 | module_init(sst25l_init); | ||
507 | module_exit(sst25l_exit); | ||
508 | |||
509 | MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips"); | ||
510 | MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, " | ||
511 | "Ryan Mallon <ryan@bluewatersys.com>"); | ||
512 | MODULE_LICENSE("GPL"); | ||