diff options
| -rw-r--r-- | drivers/mtd/devices/m25p80.c | 126 |
1 files changed, 125 insertions, 1 deletions
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 6d8d265ae91a..53de9f05ad50 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 | ||
| @@ -670,7 +789,12 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
| 670 | flash->mtd.size = info->sector_size * info->n_sectors; | 789 | flash->mtd.size = info->sector_size * info->n_sectors; |
| 671 | flash->mtd.erase = m25p80_erase; | 790 | flash->mtd.erase = m25p80_erase; |
| 672 | flash->mtd.read = m25p80_read; | 791 | flash->mtd.read = m25p80_read; |
| 673 | flash->mtd.write = m25p80_write; | 792 | |
| 793 | /* sst flash chips use AAI word program */ | ||
| 794 | if (info->jedec_id >> 16 == 0xbf) | ||
| 795 | flash->mtd.write = sst_write; | ||
| 796 | else | ||
| 797 | flash->mtd.write = m25p80_write; | ||
| 674 | 798 | ||
| 675 | /* prefer "small sector" erase if possible */ | 799 | /* prefer "small sector" erase if possible */ |
| 676 | if (info->flags & SECT_4K) { | 800 | if (info->flags & SECT_4K) { |
