diff --git a/cmd/sf.c b/cmd/sf.c
index ecd2918cbc..664442813d 100644
--- a/cmd/sf.c
+++ b/cmd/sf.c
@@ -355,6 +355,7 @@ static int do_spi_protect(int argc, char *const argv[])
 	int ret = 0;
 	loff_t start, len;
 	bool prot = false;
+	struct lock_info info;
 
 	if (argc != 4)
 		return -1;
@@ -369,6 +370,24 @@ static int do_spi_protect(int argc, char *const argv[])
 		return 1;
 	}
 
+	if (strcmp(argv[1], "check") == 0) {
+		info.ofs = start;
+		info.len = len;
+		ret = spi_flash_is_protected(flash, &info);
+		if (ret != 0) {
+			printf("ERROR: operation failed (%d)\n", ret);
+			return 1;
+		}
+
+		printf("region [0x%06x, 0x%06x] is %sfully write-protected\n",
+		       info.ofs, (info.ofs + info.len - 1),
+		       (info.is_locked ? "" : "NOT "));
+		printf("hardware write protection is %sactive\n",
+		       (info.hw_protection ? "" : "NOT "));
+
+		return 0;
+	}
+
 	if (strcmp(argv[1], "lock") == 0)
 		prot = true;
 	else if (strcmp(argv[1], "unlock") == 0)
@@ -612,8 +631,8 @@ U_BOOT_CMD(
 	"sf update addr offset|partition len    - erase and write 'len' bytes from memory\n"
 	"                                         at 'addr' to flash at 'offset'\n"
 	"                                         or to start of mtd 'partition'\n"
-	"sf protect lock|unlock sector len      - protect/unprotect 'len' bytes starting\n"
-	"                                         at address 'sector'\n"
+	"sf protect lock|unlock|check offs len  - (un)protect or check protection of 'len'\n"
+	"                                         bytes starting at address 'offs'\n"
 #ifdef CONFIG_CMD_SF_TEST
 	"sf test offset len                     - run a very basic destructive test\n"
 	"                                         ranging 'len' bytes from 'offset'\n"
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 0b48e068be..f77f22684d 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -869,18 +869,25 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
  * Check if a region of the flash is (completely) locked. See stm_lock() for
  * more info.
  *
- * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
- * negative on errors.
+ * Updates struct lock_info in-place to reflect protection state.
  */
-static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int stm_is_locked(struct spi_nor *nor, struct lock_info *info)
 {
-	int status;
+	int status, ret;
 
 	status = read_sr(nor);
 	if (status < 0)
 		return status;
 
-	return stm_is_locked_sr(nor, ofs, len, status);
+	info->hw_protection = ((status & SR_SRWD) > 0);
+
+	ret = stm_is_locked_sr(nor, info->ofs, info->len, status);
+	if (ret < 0)
+		return ret;
+
+	info->is_locked = (ret > 0);
+
+	return 0;
 }
 #endif /* CONFIG_SPI_FLASH_STMICRO */
 
@@ -1208,18 +1215,32 @@ static int sst26_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
 	return sst26_lock_ctl(nor, ofs, len, SST26_CTL_LOCK);
 }
 
-static int sst26_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int sst26_is_locked(struct spi_nor *nor, struct lock_info *info)
 {
+	int ret;
+
 	/*
 	 * 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.
+	 * region, so offset and length might be not 64k aligned, so adjust
+	 * them to be 64k aligned as sst26_lock_ctl works only with 64k
+	 * aligned regions.
 	 */
-	ofs -= ofs & (SZ_64K - 1);
-	len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len;
+	info->ofs -= info->ofs & (SZ_64K - 1);
+	info->len = info->len & (SZ_64K - 1) ? (info->len & ~(SZ_64K - 1)) + SZ_64K : info->len;
 
-	return sst26_lock_ctl(nor, ofs, len, SST26_CTL_CHECK);
+	ret = sst26_lock_ctl(nor, info->ofs, info->len, SST26_CTL_CHECK);
+	if (ret < 0)
+		return ret;
+
+	info->is_locked = (ret == 0);
+
+	ret = read_cr(nor);
+	if (ret < 0)
+		return ret;
+
+	info->hw_protection = ((ret & (CR_WPEN | CR_QUAD_EN_SPAN)) == CR_WPEN);
+
+	return 0;
 }
 
 static int sst_write_byteprogram(struct spi_nor *nor, loff_t to, size_t len,
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index ee5a59cf0a..bfd503b1b2 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -256,6 +256,21 @@ enum spi_nor_option_flags {
  */
 struct flash_info;
 
+/**
+ * struct lock_info - structure describing flash protection
+ * @ofs:            start address of region
+ * @len:            length of region
+ * @is_locked:      true if the region is write-protected (locked)
+ * @hw_protection:  true if the write-protection is backed by hardware, e.g.,
+ *                  the lock can only be removed by setting WP# pin high
+ */
+struct lock_info {
+	u32 ofs;
+	u32 len;
+	bool is_locked;
+	bool hw_protection;
+};
+
 /*
  * TODO: Remove, once all users of spi_flash interface are moved to MTD
  *
@@ -344,7 +359,7 @@ struct spi_nor {
 
 	int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
 	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
-	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
+	int (*flash_is_locked)(struct spi_nor *nor, struct lock_info *lock);
 	int (*quad_enable)(struct spi_nor *nor);
 
 	void *priv;
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 85cae32cc7..09a4fdad1c 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -179,4 +179,12 @@ static inline int spi_flash_protect(struct spi_flash *flash, u32 ofs, u32 len,
 		return flash->flash_unlock(flash, ofs, len);
 }
 
+static inline int spi_flash_is_protected(struct spi_flash *flash, struct lock_info *info)
+{
+	if (!flash->flash_is_locked)
+		return -EOPNOTSUPP;
+
+	return flash->flash_is_locked(flash, info);
+}
+
 #endif /* _SPI_FLASH_H_ */
