Commit 3d4fed87a5fa3ffedf64ff2811cd95c5ac4503ac
mtd: sf: Add support of sst26wf* flash ICs protection ops
sst26wf flash series block protection implementation differs from other SST series, so add specific implementation flash_lock/flash_unlock/flash_is_locked functions for sst26wf flash ICs. Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
Showing 2 changed files with 181 additions and 0 deletions Side-by-side Diff
| ... | ... | @@ -87,6 +87,19 @@ |
| 87 | 87 | |
| 88 | 88 | /* SST specific */ |
| 89 | 89 | #ifdef CONFIG_SPI_FLASH_SST |
| 90 | +#define SST26_CMD_READ_BPR 0x72 | |
| 91 | +#define SST26_CMD_WRITE_BPR 0x42 | |
| 92 | + | |
| 93 | +#define SST26_BPR_8K_NUM 4 | |
| 94 | +#define SST26_MAX_BPR_REG_LEN (18 + 1) | |
| 95 | +#define SST26_BOUND_REG_SIZE ((32 + SST26_BPR_8K_NUM * 8) * SZ_1K) | |
| 96 | + | |
| 97 | +enum lock_ctl { | |
| 98 | + SST26_CTL_LOCK, | |
| 99 | + SST26_CTL_UNLOCK, | |
| 100 | + SST26_CTL_CHECK | |
| 101 | +}; | |
| 102 | + | |
| 90 | 103 | # define CMD_SST_BP 0x02 /* Byte Program */ |
| 91 | 104 | # define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ |
| 92 | 105 |
| ... | ... | @@ -16,6 +16,7 @@ |
| 16 | 16 | #include <spi.h> |
| 17 | 17 | #include <spi_flash.h> |
| 18 | 18 | #include <linux/log2.h> |
| 19 | +#include <linux/sizes.h> | |
| 19 | 20 | #include <dma.h> |
| 20 | 21 | |
| 21 | 22 | #include "sf_internal.h" |
| ... | ... | @@ -541,6 +542,164 @@ |
| 541 | 542 | } |
| 542 | 543 | |
| 543 | 544 | #ifdef CONFIG_SPI_FLASH_SST |
| 545 | +static bool sst26_process_bpr(u32 bpr_size, u8 *cmd, u32 bit, enum lock_ctl ctl) | |
| 546 | +{ | |
| 547 | + switch (ctl) { | |
| 548 | + case SST26_CTL_LOCK: | |
| 549 | + cmd[bpr_size - (bit / 8) - 1] |= BIT(bit % 8); | |
| 550 | + break; | |
| 551 | + case SST26_CTL_UNLOCK: | |
| 552 | + cmd[bpr_size - (bit / 8) - 1] &= ~BIT(bit % 8); | |
| 553 | + break; | |
| 554 | + case SST26_CTL_CHECK: | |
| 555 | + return !!(cmd[bpr_size - (bit / 8) - 1] & BIT(bit % 8)); | |
| 556 | + } | |
| 557 | + | |
| 558 | + return false; | |
| 559 | +} | |
| 560 | + | |
| 561 | +/* | |
| 562 | + * sst26wf016/sst26wf032/sst26wf064 have next block protection: | |
| 563 | + * 4x - 8 KByte blocks - read & write protection bits - upper addresses | |
| 564 | + * 1x - 32 KByte blocks - write protection bits | |
| 565 | + * rest - 64 KByte blocks - write protection bits | |
| 566 | + * 1x - 32 KByte blocks - write protection bits | |
| 567 | + * 4x - 8 KByte blocks - read & write protection bits - lower addresses | |
| 568 | + * | |
| 569 | + * We'll support only per 64k lock/unlock so lower and upper 64 KByte region | |
| 570 | + * will be treated as single block. | |
| 571 | + */ | |
| 572 | + | |
| 573 | +/* | |
| 574 | + * Lock, unlock or check lock status of the flash region of the flash (depending | |
| 575 | + * on the lock_ctl value) | |
| 576 | + */ | |
| 577 | +static int sst26_lock_ctl(struct spi_flash *flash, u32 ofs, size_t len, enum lock_ctl ctl) | |
| 578 | +{ | |
| 579 | + u32 i, bpr_ptr, rptr_64k, lptr_64k, bpr_size; | |
| 580 | + bool lower_64k = false, upper_64k = false; | |
| 581 | + u8 cmd, bpr_buff[SST26_MAX_BPR_REG_LEN] = {}; | |
| 582 | + int ret; | |
| 583 | + | |
| 584 | + /* Check length and offset for 64k alignment */ | |
| 585 | + if ((ofs & (SZ_64K - 1)) || (len & (SZ_64K - 1))) | |
| 586 | + return -EINVAL; | |
| 587 | + | |
| 588 | + if (ofs + len > flash->size) | |
| 589 | + return -EINVAL; | |
| 590 | + | |
| 591 | + /* SST26 family has only 16 Mbit, 32 Mbit and 64 Mbit IC */ | |
| 592 | + if (flash->size != SZ_2M && | |
| 593 | + flash->size != SZ_4M && | |
| 594 | + flash->size != SZ_8M) | |
| 595 | + return -EINVAL; | |
| 596 | + | |
| 597 | + bpr_size = 2 + (flash->size / SZ_64K / 8); | |
| 598 | + | |
| 599 | + cmd = SST26_CMD_READ_BPR; | |
| 600 | + ret = spi_flash_read_common(flash, &cmd, 1, bpr_buff, bpr_size); | |
| 601 | + if (ret < 0) { | |
| 602 | + printf("SF: fail to read block-protection register\n"); | |
| 603 | + return ret; | |
| 604 | + } | |
| 605 | + | |
| 606 | + rptr_64k = min_t(u32, ofs + len , flash->size - SST26_BOUND_REG_SIZE); | |
| 607 | + lptr_64k = max_t(u32, ofs, SST26_BOUND_REG_SIZE); | |
| 608 | + | |
| 609 | + upper_64k = ((ofs + len) > (flash->size - SST26_BOUND_REG_SIZE)); | |
| 610 | + lower_64k = (ofs < SST26_BOUND_REG_SIZE); | |
| 611 | + | |
| 612 | + /* Lower bits in block-protection register are about 64k region */ | |
| 613 | + bpr_ptr = lptr_64k / SZ_64K - 1; | |
| 614 | + | |
| 615 | + /* Process 64K blocks region */ | |
| 616 | + while (lptr_64k < rptr_64k) { | |
| 617 | + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | |
| 618 | + return EACCES; | |
| 619 | + | |
| 620 | + bpr_ptr++; | |
| 621 | + lptr_64k += SZ_64K; | |
| 622 | + } | |
| 623 | + | |
| 624 | + /* 32K and 8K region bits in BPR are after 64k region bits */ | |
| 625 | + bpr_ptr = (flash->size - 2 * SST26_BOUND_REG_SIZE) / SZ_64K; | |
| 626 | + | |
| 627 | + /* Process lower 32K block region */ | |
| 628 | + if (lower_64k) | |
| 629 | + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | |
| 630 | + return EACCES; | |
| 631 | + | |
| 632 | + bpr_ptr++; | |
| 633 | + | |
| 634 | + /* Process upper 32K block region */ | |
| 635 | + if (upper_64k) | |
| 636 | + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | |
| 637 | + return EACCES; | |
| 638 | + | |
| 639 | + bpr_ptr++; | |
| 640 | + | |
| 641 | + /* Process lower 8K block regions */ | |
| 642 | + for (i = 0; i < SST26_BPR_8K_NUM; i++) { | |
| 643 | + if (lower_64k) | |
| 644 | + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | |
| 645 | + return EACCES; | |
| 646 | + | |
| 647 | + /* In 8K area BPR has both read and write protection bits */ | |
| 648 | + bpr_ptr += 2; | |
| 649 | + } | |
| 650 | + | |
| 651 | + /* Process upper 8K block regions */ | |
| 652 | + for (i = 0; i < SST26_BPR_8K_NUM; i++) { | |
| 653 | + if (upper_64k) | |
| 654 | + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | |
| 655 | + return EACCES; | |
| 656 | + | |
| 657 | + /* In 8K area BPR has both read and write protection bits */ | |
| 658 | + bpr_ptr += 2; | |
| 659 | + } | |
| 660 | + | |
| 661 | + /* If we check region status we don't need to write BPR back */ | |
| 662 | + if (ctl == SST26_CTL_CHECK) | |
| 663 | + return 0; | |
| 664 | + | |
| 665 | + cmd = SST26_CMD_WRITE_BPR; | |
| 666 | + ret = spi_flash_write_common(flash, &cmd, 1, bpr_buff, bpr_size); | |
| 667 | + if (ret < 0) { | |
| 668 | + printf("SF: fail to write block-protection register\n"); | |
| 669 | + return ret; | |
| 670 | + } | |
| 671 | + | |
| 672 | + return 0; | |
| 673 | +} | |
| 674 | + | |
| 675 | +static int sst26_unlock(struct spi_flash *flash, u32 ofs, size_t len) | |
| 676 | +{ | |
| 677 | + return sst26_lock_ctl(flash, ofs, len, SST26_CTL_UNLOCK); | |
| 678 | +} | |
| 679 | + | |
| 680 | +static int sst26_lock(struct spi_flash *flash, u32 ofs, size_t len) | |
| 681 | +{ | |
| 682 | + return sst26_lock_ctl(flash, ofs, len, SST26_CTL_LOCK); | |
| 683 | +} | |
| 684 | + | |
| 685 | +/* | |
| 686 | + * Returns EACCES (positive value) if region is locked, 0 if region is unlocked, | |
| 687 | + * and negative on errors. | |
| 688 | + */ | |
| 689 | +static int sst26_is_locked(struct spi_flash *flash, u32 ofs, size_t len) | |
| 690 | +{ | |
| 691 | + /* | |
| 692 | + * is_locked function is used for check before reading or erasing flash | |
| 693 | + * region, so offset and length might be not 64k allighned, so adjust | |
| 694 | + * them to be 64k allighned as sst26_lock_ctl works only with 64k | |
| 695 | + * allighned regions. | |
| 696 | + */ | |
| 697 | + ofs -= ofs & (SZ_64K - 1); | |
| 698 | + len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len; | |
| 699 | + | |
| 700 | + return sst26_lock_ctl(flash, ofs, len, SST26_CTL_CHECK); | |
| 701 | +} | |
| 702 | + | |
| 544 | 703 | static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) |
| 545 | 704 | { |
| 546 | 705 | struct spi_slave *spi = flash->spi; |
| ... | ... | @@ -1030,6 +1189,15 @@ |
| 1030 | 1189 | flash->flash_lock = stm_lock; |
| 1031 | 1190 | flash->flash_unlock = stm_unlock; |
| 1032 | 1191 | flash->flash_is_locked = stm_is_locked; |
| 1192 | + } | |
| 1193 | +#endif | |
| 1194 | + | |
| 1195 | +/* sst26wf series block protection implementation differs from other series */ | |
| 1196 | +#if defined(CONFIG_SPI_FLASH_SST) | |
| 1197 | + if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST && info->id[1] == 0x26) { | |
| 1198 | + flash->flash_lock = sst26_lock; | |
| 1199 | + flash->flash_unlock = sst26_unlock; | |
| 1200 | + flash->flash_is_locked = sst26_is_locked; | |
| 1033 | 1201 | } |
| 1034 | 1202 | #endif |
| 1035 | 1203 |
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8
-
mentioned in commit e0cacd
-
mentioned in commit 718fd8