All of lore.kernel.org
 help / color / mirror / Atom feed
From: Fabio Estevam <fabio.estevam@freescale.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v4 15/16] spi: sf_ops: Add SPI protection mechanism from the kernel
Date: Mon, 26 Oct 2015 14:11:57 -0200	[thread overview]
Message-ID: <1445875918-14777-15-git-send-email-fabio.estevam@freescale.com> (raw)
In-Reply-To: <1445875918-14777-1-git-send-email-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>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
---
Changes since v3:
- None

 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

  parent reply	other threads:[~2015-10-26 16:11 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-10-26 16:11 [U-Boot] [PATCH v4 01/16] include: Add log2 header from the kernel Fabio Estevam
2015-10-26 16:11 ` [U-Boot] [PATCH v4 02/16] include: Add generic bitops headers Fabio Estevam
2015-10-30  6:10   ` Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 03/16] ARM: bitops: Use the " Fabio Estevam
2015-10-30  6:11   ` Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 04/16] x86: " Fabio Estevam
2015-10-30  6:13   ` Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 05/16] m68k: " Fabio Estevam
2015-10-30  6:15   ` Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 06/16] blackfin: " Fabio Estevam
2015-10-30  6:16   ` Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 07/16] sh: " Fabio Estevam
2015-10-30  6:18   ` Jagan Teki
2015-10-30 14:55     ` Fabio Estevam
2015-10-26 16:11 ` [U-Boot] [PATCH v4 08/16] sandbox: " Fabio Estevam
2015-10-30  6:19   ` Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 09/16] sparc: " Fabio Estevam
2015-10-30  6:20   ` Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 10/16] openrisc: " Fabio Estevam
2015-10-30  6:23   ` Jagan Teki
2015-10-30 11:35     ` Fabio Estevam
2015-10-26 16:11 ` [U-Boot] [PATCH v4 11/16] nds32: " Fabio Estevam
2015-10-26 16:11 ` [U-Boot] [PATCH v4 12/16] nios2: " Fabio Estevam
2015-10-27  2:52   ` Thomas Chou
2015-10-30  6:24     ` Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 13/16] compat: Remove is_power_of_2() definition Fabio Estevam
2015-10-30  6:26   ` Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 14/16] powerpc: Remove __ilog2_u64 and ffs4 from bitops Fabio Estevam
2015-10-30  6:27   ` Jagan Teki
2015-10-26 16:11 ` Fabio Estevam [this message]
2015-10-30 16:29   ` [U-Boot] [PATCH v4 15/16] spi: sf_ops: Add SPI protection mechanism from the kernel Jagan Teki
2015-10-26 16:11 ` [U-Boot] [PATCH v4 16/16] spi: Add SPI NOR protection mechanism Fabio Estevam
2015-10-30 16:28   ` Jagan Teki
2015-10-30 16:51     ` Fabio Estevam
2015-10-30 18:23       ` Jagan Teki
2015-11-03  0:28         ` Fabio Estevam
2015-10-30  6:08 ` [U-Boot] [PATCH v4 01/16] include: Add log2 header from the kernel Jagan Teki

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=1445875918-14777-15-git-send-email-fabio.estevam@freescale.com \
    --to=fabio.estevam@freescale.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.