Sergii Dmytruk has uploaded this change for review.

View Change

[RFC][OTP] spi25_statusreg: support reading/writing security register

Not to be confused with "secure registers" of OTP.

Security register is a dedicated status register for security-related
bits. You don't write its value directly, issuing corresponding write
command with no data just sets OTP bit to 1 automatically.

No WREN is necessary, but at least some datasheets indicate BUSY state
after write command.

Unlike cases where OTP bit is part of SR and can only be written while
in OTP mode, security register can only be written outside of the mode.

Change-Id: Iae1753ca4cb051127a5bcbeba7f064053adb8dae
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
---
M flash.h
M spi.h
M spi25_statusreg.c
3 files changed, 54 insertions(+), 16 deletions(-)

git pull ssh://review.coreboot.org:29418/flashrom refs/changes/09/59709/1
diff --git a/flash.h b/flash.h
index a277e43..1074d09 100644
--- a/flash.h
+++ b/flash.h
@@ -144,6 +144,8 @@
#define FEATURE_WRSR2 (1 << 20)
#define FEATURE_RDSR2 (1 << 21)

+/* Whether chip has security register (RDSCUR/WRSCUR commands) */
+#define FEATURE_SCUR (1 << 22)

#define ERASED_VALUE(flash) (((flash)->chip->feature_bits & FEATURE_ERASED_ZERO) ? 0x00 : 0xff)

@@ -177,8 +179,9 @@
STATUS1,
STATUS2,
CONFIG1,
+ SECREG, /* security register */
};
-#define MAX_REGISTERS 4
+#define MAX_REGISTERS 5

struct reg_bit_info {
/* Register containing the bit */
diff --git a/spi.h b/spi.h
index 36d83f5..07d131d 100644
--- a/spi.h
+++ b/spi.h
@@ -158,6 +158,16 @@
#define JEDEC_WRSR2_OUTSIZE 0x02
#define JEDEC_WRSR2_INSIZE 0x00

+/* Read Security Register */
+#define JEDEC_RDSCUR 0x2b
+#define JEDEC_RDSCUR_OUTSIZE 0x01
+#define JEDEC_RDSCUR_INSIZE 0x01
+
+/* Write Security Register */
+#define JEDEC_WRSCUR 0x2f
+#define JEDEC_WRSCUR_OUTSIZE 0x01
+#define JEDEC_WRSCUR_INSIZE 0x00
+
/* Enter 4-byte Address Mode */
#define JEDEC_ENTER_4_BYTE_ADDR_MODE 0xB7

diff --git a/spi25_statusreg.c b/spi25_statusreg.c
index f549d10..6ca2117 100644
--- a/spi25_statusreg.c
+++ b/spi25_statusreg.c
@@ -22,6 +22,25 @@
#include "spi.h"

/* === Generic functions === */
+static int spi_write_register_wait(const struct flashctx *flash)
+{
+ /* WRSR performs a self-timed erase before the changes take effect.
+ * This may take 50-85 ms in most cases, and some chips apparently
+ * allow running RDSR only once. Therefore pick an initial delay of
+ * 100 ms, then wait in 10 ms steps until a total of 5 s have elapsed.
+ */
+ int i = 0;
+ programmer_delay(100 * 1000);
+ while (spi_read_status_register(flash) & SPI_SR_WIP) {
+ if (++i > 490) {
+ msg_cerr("Error: WIP bit after WRSR never cleared\n");
+ return TIMEOUT_ERROR;
+ }
+ programmer_delay(10 * 1000);
+ }
+ return 0;
+}
+
static int spi_write_register_flag(const struct flashctx *flash, uint8_t enable_opcode, uint8_t *write_cmd, size_t write_cmd_len)
{
/*
@@ -56,21 +75,8 @@
*/
return result;
}
- /* WRSR performs a self-timed erase before the changes take effect.
- * This may take 50-85 ms in most cases, and some chips apparently
- * allow running RDSR only once. Therefore pick an initial delay of
- * 100 ms, then wait in 10 ms steps until a total of 5 s have elapsed.
- */
- int i = 0;
- programmer_delay(100 * 1000);
- while (spi_read_status_register(flash) & SPI_SR_WIP) {
- if (++i > 490) {
- msg_cerr("Error: WIP bit after WRSR never cleared\n");
- return TIMEOUT_ERROR;
- }
- programmer_delay(10 * 1000);
- }
- return 0;
+
+ return spi_write_register_wait(flash);
}

static const char *reg_name(enum flash_reg reg)
@@ -79,6 +85,7 @@
case STATUS1: return "SR1";
case STATUS2: return "SR2";
case CONFIG1: return "CR1";
+ case SECREG: return "SCR";
default: return "Unknown register";
}
}
@@ -116,12 +123,28 @@
write_cmd[1] = value;
write_cmd_len = JEDEC_WRSR2_OUTSIZE;
}
+ } else if (reg == SECREG) {
+ if (feature_bits & FEATURE_SCUR) {
+ /* This command accepts no value, it just sets OTP
+ * bit. */
+ write_cmd[0] = JEDEC_WRSCUR;
+ write_cmd_len = JEDEC_WRSCUR_OUTSIZE;
+ }
}
if (write_cmd_len == 0) {
msg_cerr("Chip does not support writing register %s.\n", reg_name(reg));
return 1;
}

+ /* No WREN is necessary for writing security register (at least for
+ * Macronix or Adesto), but might need to wait for WIP == 0. */
+ if (reg == SECREG) {
+ int ret = spi_send_command(flash, write_cmd_len, 0, write_cmd, NULL);
+ if (ret)
+ return ret;
+ return spi_write_register_wait(flash);
+ }
+
if (!(feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) {
msg_cdbg("Missing status register write definition, assuming "
"EWSR is needed\n");
@@ -145,6 +168,8 @@
read_cmd = JEDEC_RDSR;
} else if (reg == STATUS2 && (feature_bits & FEATURE_RDSR2)) {
read_cmd = JEDEC_RDSR2;
+ } else if (reg == SECREG && (feature_bits & FEATURE_SCUR)) {
+ read_cmd = JEDEC_RDSCUR;
} else {
msg_cerr("Chip does not support reading register %s.\n", reg_name(reg));
return 1;

To view, visit change 59709. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: Iae1753ca4cb051127a5bcbeba7f064053adb8dae
Gerrit-Change-Number: 59709
Gerrit-PatchSet: 1
Gerrit-Owner: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Gerrit-MessageType: newchange