Add support for the SST sst26wf016 and sst26wf032 flash IC:
sst26wf*** flash series block protection implementation differs from other
SST series, so we add implementation for sst26wf*** lock/unlock/is_locked
functions.
Add sst26wf016 and sst26wf032 flash IC info to spi_flash_ids list.
NOTE:
these patches is basically following mine u-boot commits port:
http://git.denx.de/?p=u-boot.git;a=commitdiff;h=3d4fed87a5fa3ffedf64ff2811cd95c5ac4503ac
http://git.denx.de/?p=u-boot.git;a=commitdiff;h=a19e97157c3721ef9c4b15c68c1773467a3b4a98
Eugeniy Paltsev (2):
mtd: spi-nor: Add support of sst26wf* flash ICs protection ops
mtd: spi-nor: add support for sst26wf016, sst26wf032
drivers/mtd/spi-nor/spi-nor.c | 179 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/spi-nor.h | 4 +
2 files changed, 183 insertions(+)
--
2.14.4
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.
NOTE:
this patch is basically following mine u-boot commit port:
http://git.denx.de/?p=u-boot.git;a=commitdiff;h=3d4fed87a5fa3ffedf64ff2811cd95c5ac4503ac
Signed-off-by: Eugeniy Paltsev <[email protected]>
---
drivers/mtd/spi-nor/spi-nor.c | 177 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/spi-nor.h | 4 +
2 files changed, 181 insertions(+)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d9c368c44194..6f74c75c9ddc 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -598,6 +598,177 @@ static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask)
return ((ret & mask) != (status_new & mask)) ? -EIO : 0;
}
+/*
+ * sst26wf016/sst26wf032/sst26wf064 have next block protection:
+ * 4x - 8 KByte blocks - read & write protection bits - upper addresses
+ * 1x - 32 KByte blocks - write protection bits
+ * rest - 64 KByte blocks - write protection bits
+ * 1x - 32 KByte blocks - write protection bits
+ * 4x - 8 KByte blocks - read & write protection bits - lower addresses
+ *
+ * We'll support only per 64k lock/unlock so lower and upper 64 KByte region
+ * will be treated as single block.
+ */
+#define SST26_BPR_8K_NUM 4
+#define SST26_MAX_BPR_REG_LEN (18 + 1)
+#define SST26_BOUND_REG_SIZE ((32 + SST26_BPR_8K_NUM * 8) * SZ_1K)
+
+enum lock_ctl {
+ SST26_CTL_LOCK,
+ SST26_CTL_UNLOCK,
+ SST26_CTL_CHECK
+};
+
+static bool sst26_process_bpr(u32 bpr_size, u8 *cmd, u32 bit, enum lock_ctl ctl)
+{
+ switch (ctl) {
+ case SST26_CTL_LOCK:
+ cmd[bpr_size - (bit / 8) - 1] |= BIT(bit % 8);
+ break;
+ case SST26_CTL_UNLOCK:
+ cmd[bpr_size - (bit / 8) - 1] &= ~BIT(bit % 8);
+ break;
+ case SST26_CTL_CHECK:
+ return !!(cmd[bpr_size - (bit / 8) - 1] & BIT(bit % 8));
+ }
+
+ return false;
+}
+
+/*
+ * Lock, unlock or check lock status of the flash region of the flash (depending
+ * on the lock_ctl value)
+ */
+static int sst26_lock_ctl(struct spi_nor *nor, loff_t ofs, uint64_t len, enum lock_ctl ctl)
+{
+ struct mtd_info *mtd = &nor->mtd;
+ u32 i, bpr_ptr, rptr_64k, lptr_64k, bpr_size;
+ bool lower_64k = false, upper_64k = false;
+ u8 bpr_buff[SST26_MAX_BPR_REG_LEN] = {};
+ int ret;
+
+ /* Check length and offset for 64k alignment */
+ if ((ofs & (SZ_64K - 1)) || (len & (SZ_64K - 1))) {
+ dev_err(nor->dev, "length or offset is not 64KiB allighned\n");
+ return -EINVAL;
+ }
+
+ if (ofs + len > mtd->size) {
+ dev_err(nor->dev, "range is more than device size: %#llx + %#llx > %#llx\n",
+ ofs, len, mtd->size);
+ return -EINVAL;
+ }
+
+ /* SST26 family has only 16 Mbit, 32 Mbit and 64 Mbit IC */
+ if (mtd->size != SZ_2M &&
+ mtd->size != SZ_4M &&
+ mtd->size != SZ_8M)
+ return -EINVAL;
+
+ bpr_size = 2 + (mtd->size / SZ_64K / 8);
+
+ nor->read_reg(nor, SPINOR_OP_READ_BPR, bpr_buff, bpr_size);
+ if (ret < 0) {
+ dev_err(nor->dev, "fail to read block-protection register\n");
+ return ret;
+ }
+
+ rptr_64k = min_t(u32, ofs + len, mtd->size - SST26_BOUND_REG_SIZE);
+ lptr_64k = max_t(u32, ofs, SST26_BOUND_REG_SIZE);
+
+ upper_64k = ((ofs + len) > (mtd->size - SST26_BOUND_REG_SIZE));
+ lower_64k = (ofs < SST26_BOUND_REG_SIZE);
+
+ /* Lower bits in block-protection register are about 64k region */
+ bpr_ptr = lptr_64k / SZ_64K - 1;
+
+ /* Process 64K blocks region */
+ while (lptr_64k < rptr_64k) {
+ if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+ return EACCES;
+
+ bpr_ptr++;
+ lptr_64k += SZ_64K;
+ }
+
+ /* 32K and 8K region bits in BPR are after 64k region bits */
+ bpr_ptr = (mtd->size - 2 * SST26_BOUND_REG_SIZE) / SZ_64K;
+
+ /* Process lower 32K block region */
+ if (lower_64k)
+ if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+ return EACCES;
+
+ bpr_ptr++;
+
+ /* Process upper 32K block region */
+ if (upper_64k)
+ if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+ return EACCES;
+
+ bpr_ptr++;
+
+ /* Process lower 8K block regions */
+ for (i = 0; i < SST26_BPR_8K_NUM; i++) {
+ if (lower_64k)
+ if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+ return EACCES;
+
+ /* In 8K area BPR has both read and write protection bits */
+ bpr_ptr += 2;
+ }
+
+ /* Process upper 8K block regions */
+ for (i = 0; i < SST26_BPR_8K_NUM; i++) {
+ if (upper_64k)
+ if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+ return EACCES;
+
+ /* In 8K area BPR has both read and write protection bits */
+ bpr_ptr += 2;
+ }
+
+ /* If we check region status we don't need to write BPR back */
+ if (ctl == SST26_CTL_CHECK)
+ return 0;
+
+ nor->write_reg(nor, SPINOR_OP_WRITE_BPR, bpr_buff, bpr_size);
+ if (ret < 0) {
+ dev_err(nor->dev, "fail to write block-protection register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sst26_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+ return sst26_lock_ctl(nor, ofs, len, SST26_CTL_UNLOCK);
+}
+
+static int sst26_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+ return sst26_lock_ctl(nor, ofs, len, SST26_CTL_LOCK);
+}
+
+/*
+ * Returns EACCES (positive value) if region is locked, 0 if region is unlocked,
+ * and negative on errors.
+ */
+static int sst26_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+ /*
+ * is_locked function is used for check before reading or erasing flash
+ * region, so offset and length might be not 64k allighned, so adjust
+ * them to be 64k allighned as sst26_lock_ctl works only with 64k
+ * allighned regions.
+ */
+ ofs -= ofs & (SZ_64K - 1);
+ len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len;
+
+ return sst26_lock_ctl(nor, ofs, len, SST26_CTL_CHECK);
+}
+
static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
uint64_t *len)
{
@@ -2872,6 +3043,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
nor->flash_is_locked = stm_is_locked;
}
+ if (JEDEC_MFR(info) == SNOR_MFR_SST && info->id[1] == 0x26) {
+ nor->flash_lock = sst26_lock;
+ nor->flash_unlock = sst26_unlock;
+ nor->flash_is_locked = sst26_is_locked;
+ }
+
if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) {
mtd->_lock = spi_nor_lock;
mtd->_unlock = spi_nor_unlock;
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index e60da0d34cc1..246014a73f83 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -93,6 +93,10 @@
#define SPINOR_OP_WRDI 0x04 /* Write disable */
#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */
+/* Used for SST26* flashes only. */
+#define SPINOR_OP_READ_BPR 0x72 /* Read block protection register */
+#define SPINOR_OP_WRITE_BPR 0x42 /* Write block protection register */
+
/* Used for S3AN flashes only */
#define SPINOR_OP_XSE 0x50 /* Sector erase */
#define SPINOR_OP_XPP 0x82 /* Page program */
--
2.14.4
This commit adds support for the SST sst26wf016 and sst26wf032
flash IC.
NOTE:
this patch is basically following mine u-boot commit port:
http://git.denx.de/?p=u-boot.git;a=commitdiff;h=a19e97157c3721ef9c4b15c68c1773467a3b4a98
Signed-off-by: Eugeniy Paltsev <[email protected]>
---
drivers/mtd/spi-nor/spi-nor.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 6f74c75c9ddc..e1562363862a 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1333,6 +1333,8 @@ static const struct flash_info spi_nor_ids[] = {
{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) },
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
{ "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
+ { "sst26wf016", INFO(0xbf2651, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "sst26wf032", INFO(0xbf2622, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
/* ST Microelectronics -- newer production may have feature updates */
--
2.14.4
Hi Eugeniy,
I love your patch! Perhaps something to improve:
[auto build test WARNING on mtd/spi-nor/next]
[also build test WARNING on v4.19-rc2 next-20180906]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Eugeniy-Paltsev/MTD-spi-nor-add-support-for-sst26wf016-sst26wf032/20180907-001236
base: git://git.infradead.org/linux-mtd.git spi-nor/next
config: x86_64-randconfig-x015-201835 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
Note: it may well be a FALSE warning. FWIW you are at least aware of it now.
http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings
All warnings (new ones prefixed by >>):
drivers/mtd/spi-nor/spi-nor.c: In function 'sst26_lock_ctl':
>> drivers/mtd/spi-nor/spi-nor.c:671:5: warning: 'ret' may be used uninitialized in this function [-Wmaybe-uninitialized]
if (ret < 0) {
^
vim +/ret +671 drivers/mtd/spi-nor/spi-nor.c
637
638 /*
639 * Lock, unlock or check lock status of the flash region of the flash (depending
640 * on the lock_ctl value)
641 */
642 static int sst26_lock_ctl(struct spi_nor *nor, loff_t ofs, uint64_t len, enum lock_ctl ctl)
643 {
644 struct mtd_info *mtd = &nor->mtd;
645 u32 i, bpr_ptr, rptr_64k, lptr_64k, bpr_size;
646 bool lower_64k = false, upper_64k = false;
647 u8 bpr_buff[SST26_MAX_BPR_REG_LEN] = {};
648 int ret;
649
650 /* Check length and offset for 64k alignment */
651 if ((ofs & (SZ_64K - 1)) || (len & (SZ_64K - 1))) {
652 dev_err(nor->dev, "length or offset is not 64KiB allighned\n");
653 return -EINVAL;
654 }
655
656 if (ofs + len > mtd->size) {
657 dev_err(nor->dev, "range is more than device size: %#llx + %#llx > %#llx\n",
658 ofs, len, mtd->size);
659 return -EINVAL;
660 }
661
662 /* SST26 family has only 16 Mbit, 32 Mbit and 64 Mbit IC */
663 if (mtd->size != SZ_2M &&
664 mtd->size != SZ_4M &&
665 mtd->size != SZ_8M)
666 return -EINVAL;
667
668 bpr_size = 2 + (mtd->size / SZ_64K / 8);
669
670 nor->read_reg(nor, SPINOR_OP_READ_BPR, bpr_buff, bpr_size);
> 671 if (ret < 0) {
672 dev_err(nor->dev, "fail to read block-protection register\n");
673 return ret;
674 }
675
676 rptr_64k = min_t(u32, ofs + len, mtd->size - SST26_BOUND_REG_SIZE);
677 lptr_64k = max_t(u32, ofs, SST26_BOUND_REG_SIZE);
678
679 upper_64k = ((ofs + len) > (mtd->size - SST26_BOUND_REG_SIZE));
680 lower_64k = (ofs < SST26_BOUND_REG_SIZE);
681
682 /* Lower bits in block-protection register are about 64k region */
683 bpr_ptr = lptr_64k / SZ_64K - 1;
684
685 /* Process 64K blocks region */
686 while (lptr_64k < rptr_64k) {
687 if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
688 return EACCES;
689
690 bpr_ptr++;
691 lptr_64k += SZ_64K;
692 }
693
694 /* 32K and 8K region bits in BPR are after 64k region bits */
695 bpr_ptr = (mtd->size - 2 * SST26_BOUND_REG_SIZE) / SZ_64K;
696
697 /* Process lower 32K block region */
698 if (lower_64k)
699 if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
700 return EACCES;
701
702 bpr_ptr++;
703
704 /* Process upper 32K block region */
705 if (upper_64k)
706 if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
707 return EACCES;
708
709 bpr_ptr++;
710
711 /* Process lower 8K block regions */
712 for (i = 0; i < SST26_BPR_8K_NUM; i++) {
713 if (lower_64k)
714 if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
715 return EACCES;
716
717 /* In 8K area BPR has both read and write protection bits */
718 bpr_ptr += 2;
719 }
720
721 /* Process upper 8K block regions */
722 for (i = 0; i < SST26_BPR_8K_NUM; i++) {
723 if (upper_64k)
724 if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
725 return EACCES;
726
727 /* In 8K area BPR has both read and write protection bits */
728 bpr_ptr += 2;
729 }
730
731 /* If we check region status we don't need to write BPR back */
732 if (ctl == SST26_CTL_CHECK)
733 return 0;
734
735 nor->write_reg(nor, SPINOR_OP_WRITE_BPR, bpr_buff, bpr_size);
736 if (ret < 0) {
737 dev_err(nor->dev, "fail to write block-protection register\n");
738 return ret;
739 }
740
741 return 0;
742 }
743
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation