From: Fabio Estevam <festevam@gmail.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v8 3/4] spi: sf_ops: Add SPI protection mechanism from the kernel
Date: Fri, 16 Oct 2015 17:41:14 -0300 [thread overview]
Message-ID: <1445028075-5684-3-git-send-email-festevam@gmail.com> (raw)
In-Reply-To: <1445028075-5684-1-git-send-email-festevam@gmail.com>
From: Fabio Estevam <fabio.estevam@freescale.com>
Add the SPI NOR protection mechanism from the kernel.
This code is based on the work from Brian Norris <computersforpeace@gmail.com>
https://git.kernel.org/cgit/linux/kernel/git/next/linux-next.git/commit/drivers/mtd/spi-nor/spi-nor.c?id=62593cf40b23b523b9fc9334ca61ba6c595ebb09
Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
---
Changes since v7:
- Move BP macros to header file (Jagan)
- Remove else block (Jagan)
drivers/mtd/spi/sf_ops.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++
include/spi_flash.h | 4 ++
2 files changed, 174 insertions(+)
diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c
index 900ec1f..928f9c1 100644
--- a/drivers/mtd/spi/sf_ops.c
+++ b/drivers/mtd/spi/sf_ops.c
@@ -15,6 +15,7 @@
#include <spi_flash.h>
#include <watchdog.h>
#include <linux/compiler.h>
+#include <linux/log2.h>
#include "sf_internal.h"
@@ -573,3 +574,172 @@ int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len,
return ret;
}
#endif
+
+#ifdef CONFIG_SPI_FLASH_STMICRO
+static void stm_get_locked_range(struct spi_flash *flash, u8 sr, loff_t *ofs,
+ u32 *len)
+{
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ int shift = ffs(mask) - 1;
+ int pow;
+
+ if (!(sr & mask)) {
+ /* No protection */
+ *ofs = 0;
+ *len = 0;
+ } else {
+ pow = ((sr & mask) ^ mask) >> shift;
+ *len = flash->size >> pow;
+ *ofs = flash->size - *len;
+ }
+}
+
+/*
+ * Return 1 if the entire region is locked, 0 otherwise
+ */
+static int stm_is_locked_sr(struct spi_flash *flash, loff_t ofs, u32 len,
+ u8 sr)
+{
+ loff_t lock_offs;
+ u32 lock_len;
+
+ stm_get_locked_range(flash, sr, &lock_offs, &lock_len);
+
+ return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
+}
+
+/*
+ * 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.
+ */
+int stm_is_locked(struct spi_flash *flash, loff_t ofs, u32 len)
+{
+ int status;
+ u8 sr;
+
+ status = spi_flash_cmd_read_status(flash, &sr);
+ if (status < 0)
+ return status;
+
+ return stm_is_locked_sr(flash, ofs, len, sr);
+}
+
+/*
+ * Lock a region of the flash. Compatible with ST Micro and similar flash.
+ * Supports only the block protection bits BP{0,1,2} in the status register
+ * (SR). Does not support these features found in newer SR bitfields:
+ * - TB: top/bottom protect - only handle TB=0 (top protect)
+ * - SEC: sector/block protect - only handle SEC=0 (block protect)
+ * - CMP: complement protect - only support CMP=0 (range is not complemented)
+ *
+ * Sample table portion for 8MB flash (Winbond w25q64fw):
+ *
+ * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion
+ * --------------------------------------------------------------------------
+ * X | X | 0 | 0 | 0 | NONE | NONE
+ * 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64
+ * 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32
+ * 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16
+ * 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8
+ * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4
+ * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2
+ * X | X | 1 | 1 | 1 | 8 MB | ALL
+ *
+ * Returns negative on errors, 0 on success.
+ */
+int stm_lock(struct spi_flash *flash, u32 ofs, u32 len)
+{
+ u8 status_old, status_new;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ u8 shift = ffs(mask) - 1, pow, val;
+
+ spi_flash_cmd_read_status(flash, &status_old);
+
+ /* SPI NOR always locks to the end */
+ if (ofs + len != flash->size) {
+ /* Does combined region extend to end? */
+ if (!stm_is_locked_sr(flash, ofs + len, flash->size - ofs - len,
+ status_old))
+ return -EINVAL;
+ len = flash->size - ofs;
+ }
+
+ /*
+ * Need smallest pow such that:
+ *
+ * 1 / (2^pow) <= (len / size)
+ *
+ * so (assuming power-of-2 size) we do:
+ *
+ * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
+ */
+ pow = ilog2(flash->size) - ilog2(len);
+ val = mask - (pow << shift);
+ if (val & ~mask)
+ return -EINVAL;
+
+ /* Don't "lock" with no region! */
+ if (!(val & mask))
+ return -EINVAL;
+
+ status_new = (status_old & ~mask) | val;
+
+ /* Only modify protection if it will not unlock other areas */
+ if ((status_new & mask) <= (status_old & mask))
+ return -EINVAL;
+
+ spi_flash_cmd_write_status(flash, status_new);
+
+ return 0;
+}
+
+/*
+ * Unlock a region of the flash. See stm_lock() for more info
+ *
+ * Returns negative on errors, 0 on success.
+ */
+int stm_unlock(struct spi_flash *flash, u32 ofs, u32 len)
+{
+ uint8_t status_old, status_new;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ u8 shift = ffs(mask) - 1, pow, val;
+
+ spi_flash_cmd_read_status(flash, &status_old);
+
+ /* Cannot unlock; would unlock larger region than requested */
+ if (stm_is_locked_sr(flash, status_old, ofs - flash->erase_size,
+ flash->erase_size))
+ return -EINVAL;
+ /*
+ * Need largest pow such that:
+ *
+ * 1 / (2^pow) >= (len / size)
+ *
+ * so (assuming power-of-2 size) we do:
+ *
+ * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
+ */
+ pow = ilog2(flash->size) - order_base_2(flash->size - (ofs + len));
+ if (ofs + len == flash->size) {
+ val = 0; /* fully unlocked */
+ } else {
+ val = mask - (pow << shift);
+ /* Some power-of-two sizes are not supported */
+ if (val & ~mask)
+ return -EINVAL;
+ }
+
+ status_new = (status_old & ~mask) | val;
+
+ /* Only modify protection if it will not lock other areas */
+ if ((status_new & mask) >= (status_old & mask))
+ return -EINVAL;
+
+ spi_flash_cmd_write_status(flash, status_new);
+
+ return 0;
+}
+#endif /* CONFIG_SPI_FLASH_STMICRO */
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 3b2d555..73b2b0a 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -31,6 +31,10 @@
# define CONFIG_SF_DEFAULT_BUS 0
#endif
+#define SR_BP0 BIT(2) /* Block protect 0 */
+#define SR_BP1 BIT(3) /* Block protect 1 */
+#define SR_BP2 BIT(4) /* Block protect 2 */
+
struct spi_slave;
/**
--
1.9.1
next prev parent reply other threads:[~2015-10-16 20:41 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-10-16 20:41 [U-Boot] [PATCH v8 1/4] include: Add log2 and fls64 header fi Fabio Estevam
2015-10-16 20:41 ` [U-Boot] [PATCH v8 2/4] compat: Remove is_power_of_2() definition Fabio Estevam
2015-10-16 20:41 ` Fabio Estevam [this message]
2015-10-16 20:41 ` [U-Boot] [PATCH v8 4/4] spi: Add SPI NOR protection mechanism Fabio Estevam
2015-10-19 11:33 ` [U-Boot] [PATCH v8 1/4] include: Add log2 and fls64 header fi Jagan Teki
2015-10-19 23:06 ` Tom Rini
2015-10-22 21:30 ` Fabio Estevam
2015-10-22 22:26 ` Tom Rini
2015-10-23 4:36 ` Wolfgang Denk
2015-10-23 13:29 ` Tom Rini
2015-10-23 4:35 ` Wolfgang Denk
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1445028075-5684-3-git-send-email-festevam@gmail.com \
--to=festevam@gmail.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.