diff mbox

[1/6] Status register infrastructure

Message ID 1467750485-29158-2-git-send-email-hatim@hatimak.me
State New
Headers show

Commit Message

Hatim Kanchwala July 5, 2016, 8:28 p.m. UTC
- Read and write multiple status registers
  - Read is straightforward and most chips share same opcodes for RDSR1, RDS2 and RDSR3
  - For chips with 2 status registers, WRSR takes either 1 or 2 bytes. When only 1 byte is supplied (which was the preious behaviour), the 2nd status register is cleared. (Our code automatically takes care of this.)
  - For chips with 3 status registers, each register is separately written to and we have many chips sharing opcodes for WRSR1, WRSR2 and WRSR3
- Get, set or prettyprint write protection mode for status register(s). Functionality exposed through struct status_register member.
  - This is controlled by SRP/SRWD or SRP0/SRP1 bit(s). Chips with SRP0 and SRP1 will most likely have at least 2 status registers.
  - For chips with SRP/SRWD bit, we can get/set SOFTWARE (status register unlocked) or HARDWARE (status register locked/unlocked subject to WP#) protection modes.
  - For chips with SRP0 and SRP1, we can additionally get/set POWER_CYCLE (status registers locked until next power down-up cycle) or PERMANENT modes.
  - We can also automatically detect how WP# affects the HARDWARE write protection mode.
- struct flashchip contains pointer to a struct status_register (for allowing flexibility of reuse), which in turn has members to represent layout of status register(s) and, pointers to functions to read, write, print, set_wp_mode, get_wp_mode, print_wp_mode. Function pointers allow flexibility to assign chip specific routines for exotic cases.
- Prettyprinting of different status register bits is unified. Newer bits can be defined in enum status_register_bit and description written in statreg_bit_desc[][]. Functionality exposed through struct status_register member.

Signed-off-by: Hatim Kanchwala <hatim@hatimak.me>
---
 chipdrivers.h     |   9 ++
 flash.h           |  23 ++-
 spi.h             |   8 +-
 spi25_statusreg.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 spi25_statusreg.h |  79 +++++++++++
 5 files changed, 527 insertions(+), 2 deletions(-)
 create mode 100644 spi25_statusreg.h
diff mbox

Patch

diff --git a/chipdrivers.h b/chipdrivers.h
index c85eac9..4c58f30 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -16,26 +16,27 @@ 
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  *
  * Header file for flash chip drivers. Included from flash.h.
  * As a general rule, every function listed here should take a pointer to
  * struct flashctx as first parameter.
  */
 
 #ifndef __CHIPDRIVERS_H__
 #define __CHIPDRIVERS_H__ 1
 
 #include "flash.h"	/* for chipaddr and flashctx */
+#include "spi25_statusreg.h"	/* For enum status_register_num */
 
 /* spi.c */
 int spi_aai_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
 int spi_chip_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
 int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, int unsigned len);
 
 /* spi25.c */
 int probe_spi_rdid(struct flashctx *flash);
 int probe_spi_rdid4(struct flashctx *flash);
 int probe_spi_rems(struct flashctx *flash);
 int probe_spi_res1(struct flashctx *flash);
 int probe_spi_res2(struct flashctx *flash);
 int probe_spi_res3(struct flashctx *flash);
@@ -53,27 +54,35 @@  int spi_block_erase_c7(struct flashctx *flash, unsigned int addr, unsigned int b
 int spi_block_erase_d7(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
 int spi_block_erase_d8(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
 int spi_block_erase_db(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
 erasefunc_t *spi_get_erasefn_from_opcode(uint8_t opcode);
 int spi_chip_write_1(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
 int spi_byte_program(struct flashctx *flash, unsigned int addr, uint8_t databyte);
 int spi_nbyte_program(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
 int spi_nbyte_read(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
 int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize);
 int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize);
 
 /* spi25_statusreg.c */
 uint8_t spi_read_status_register(struct flashctx *flash);
+uint8_t spi_read_status_register_generic(struct flashctx *flash, enum status_register_num SRn);
 int spi_write_status_register(struct flashctx *flash, int status);
+int spi_write_status_register_generic(struct flashctx *flash, enum status_register_num SRn, uint8_t status);
+enum status_register_num top_status_register(struct flashctx *flash);
+char pos_bit(struct flashctx *flash, enum status_register_bit bit);
+enum wp_mode get_wp_mode_generic(struct flashctx *flash);
+int set_wp_mode_generic(struct flashctx *flash, enum wp_mode wp_mode);
+int spi_prettyprint_status_register_generic(struct flashctx *flash, enum status_register_num SRn);
+int spi_prettyprint_status_register_wp_generic(struct flashctx *flash);
 void spi_prettyprint_status_register_bit(uint8_t status, int bit);
 int spi_prettyprint_status_register_plain(struct flashctx *flash);
 int spi_prettyprint_status_register_default_welwip(struct flashctx *flash);
 int spi_prettyprint_status_register_bp1_srwd(struct flashctx *flash);
 int spi_prettyprint_status_register_bp2_srwd(struct flashctx *flash);
 int spi_prettyprint_status_register_bp3_srwd(struct flashctx *flash);
 int spi_prettyprint_status_register_bp4_srwd(struct flashctx *flash);
 int spi_prettyprint_status_register_bp2_bpl(struct flashctx *flash);
 int spi_prettyprint_status_register_bp2_tb_bpl(struct flashctx *flash);
 int spi_disable_blockprotect(struct flashctx *flash);
 int spi_disable_blockprotect_bp1_srwd(struct flashctx *flash);
 int spi_disable_blockprotect_bp2_srwd(struct flashctx *flash);
 int spi_disable_blockprotect_bp3_srwd(struct flashctx *flash);
diff --git a/flash.h b/flash.h
index da049d1..566b709 100644
--- a/flash.h
+++ b/flash.h
@@ -27,26 +27,28 @@ 
 #include "platform.h"
 
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stddef.h>
 #include <stdbool.h>
 #if IS_WINDOWS
 #include <windows.h>
 #undef min
 #undef max
 #endif
 
+#include "spi25_statusreg.h"
+
 #define ERROR_PTR ((void*)-1)
 
 /* Error codes */
 #define ERROR_OOM	-100
 #define TIMEOUT_ERROR	-101
 
 /* TODO: check using code for correct usage of types */
 typedef uintptr_t chipaddr;
 #define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))
 
 /* Types and macros regarding the maximum flash space size supported by generic code. */
 typedef uint32_t chipoff_t; /* Able to store any addressable offset within a supported flash memory. */
 typedef uint32_t chipsize_t; /* Able to store the number of bytes of any supported flash memory. */
@@ -189,27 +191,46 @@  struct flashchip {
 	 * influence that behaviour. For testing just comment out the other
 	 * elements or set the function pointer to NULL.
 	 */
 	struct block_eraser {
 		struct eraseblock {
 			unsigned int size; /* Eraseblock size in bytes */
 			unsigned int count; /* Number of contiguous blocks with that size */
 		} eraseblocks[NUM_ERASEREGIONS];
 		/* a block_erase function should try to erase one block of size
 		 * 'blocklen' at address 'blockaddr' and return 0 on success. */
 		int (*block_erase) (struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen);
 	} block_erasers[NUM_ERASEFUNCTIONS];
 
-	int (*printlock) (struct flashctx *flash);
+	/* The following struct represents the status register(s). Each status register
+	 * is a member of the layout array at the corresponding index (starting at SR1=0). */
+	struct status_register {
+		/* We need one more than MAX_STATUS_REGISTERS */
+		enum status_register_bit layout[MAX_STATUS_REGISTERS + 1][8];
+
+		/* Return value of status register SRn. */
+		uint8_t (*read) (struct flashctx *flash, enum status_register_num SRn);
+		/* Set value of status register SRn to status. */
+		int (*write) (struct flashctx *flash, enum status_register_num SRn, uint8_t status);
+		/* Print the contents of status register SRn. */
+		int (*print) (struct flashctx *flash, enum status_register_num SRn);
+		/* Get mode of write protection currently in effect against status register. */
+		enum wp_mode (*get_wp_mode) (struct flashctx *flash);
+		/* Set mode of write protection against status register. */
+		int (*set_wp_mode) (struct flashctx *flash, enum wp_mode wp_mode);
+		int (*print_wp_mode) (struct flashctx *flash);
+	} *status_register;
+
+	int (*printlock) (struct flashctx *flash);	// TODO(hatim): This member should be decommissioned
 	int (*unlock) (struct flashctx *flash);
 	int (*write) (struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
 	int (*read) (struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
 	struct voltage {
 		uint16_t min;
 		uint16_t max;
 	} voltage;
 	enum write_granularity gran;
 };
 
 struct flashctx {
 	struct flashchip *chip;
 	/* FIXME: The memory mappings should be saved in a more structured way. */
diff --git a/spi.h b/spi.h
index de5b3be..f061041 100644
--- a/spi.h
+++ b/spi.h
@@ -106,45 +106,51 @@ 
 #define JEDEC_BE_D7_OUTSIZE	0x04
 #define JEDEC_BE_D7_INSIZE	0x00
 
 /* Sector Erase 0x20 is supported by Macronix/SST chips. */
 #define JEDEC_SE		0x20
 #define JEDEC_SE_OUTSIZE	0x04
 #define JEDEC_SE_INSIZE		0x00
 
 /* Page Erase 0xDB */
 #define JEDEC_PE		0xDB
 #define JEDEC_PE_OUTSIZE	0x04
 #define JEDEC_PE_INSIZE		0x00
 
-/* Read Status Register */
+/* Read Status Register(s) */
 #define JEDEC_RDSR		0x05
 #define JEDEC_RDSR_OUTSIZE	0x01
 #define JEDEC_RDSR_INSIZE	0x01
+#define JEDEC_RDSR2		0x35
+#define JEDEC_RDSR3		0x15
 
 /* Status Register Bits */
 #define SPI_SR_WIP	(0x01 << 0)
 #define SPI_SR_WEL	(0x01 << 1)
 #define SPI_SR_AAI	(0x01 << 6)
 
 /* Write Status Enable */
 #define JEDEC_EWSR		0x50
 #define JEDEC_EWSR_OUTSIZE	0x01
 #define JEDEC_EWSR_INSIZE	0x00
 
 /* Write Status Register */
 #define JEDEC_WRSR		0x01
 #define JEDEC_WRSR_OUTSIZE	0x02
 #define JEDEC_WRSR_INSIZE	0x00
+#define JEDEC_WRSR_2_OUTSIZE	0x03
+#define JEDEC_WRSR1		0x01
+#define JEDEC_WRSR2		0x31
+#define JEDEC_WRSR3		0x11
 
 /* Read the memory */
 #define JEDEC_READ		0x03
 #define JEDEC_READ_OUTSIZE	0x04
 /*      JEDEC_READ_INSIZE : any length */
 
 /* Write memory byte */
 #define JEDEC_BYTE_PROGRAM		0x02
 #define JEDEC_BYTE_PROGRAM_OUTSIZE	0x05
 #define JEDEC_BYTE_PROGRAM_INSIZE	0x00
 
 /* Write AAI word (SST25VF080B) */
 #define JEDEC_AAI_WORD_PROGRAM			0xad
diff --git a/spi25_statusreg.c b/spi25_statusreg.c
index 01a6862..ac77c1d 100644
--- a/spi25_statusreg.c
+++ b/spi25_statusreg.c
@@ -1,39 +1,82 @@ 
 /*
  * This file is part of the flashrom project.
  * It handles everything related to status registers of the JEDEC family 25.
  *
  * Copyright (C) 2007, 2008, 2009, 2010 Carl-Daniel Hailfinger
  * Copyright (C) 2008 coresystems GmbH
  * Copyright (C) 2008 Ronald Hoogenboom <ronald@zonnet.nl>
  * Copyright (C) 2012 Stefan Tauner
+ * Copyright (C) 2016 Hatim Kanchwala <hatim@hatimak.me>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; version 2 of the License.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
 #include "flash.h"
 #include "chipdrivers.h"
 #include "spi.h"
+#include "spi25_statusreg.h"
+
+/* The following multi-array contains descriptions of corresponding bits defined
+ * in enum status_register_bit. It is imperative that the correspondence between
+ * the two definitions remain intact. The first index defines the correspondence. */
+char *statreg_bit_desc[][2] = {
+		/* Name,	Description */
+		{ "",		"" },	/* Corresponds to INVALID_BIT. */
+		{ "RESV",	"" },	/* Corresponds to RESV. */
+		{ "WIP",	"BUSY/Write In Progress (WIP)" },
+		{ "WEL",	"Write Enable Latch (WEL)" },
+		{ "SRP0",	"Status Register Write Disable (SRWD)/Software Register Protect (SRP/SRP0)" },
+		{ "SRP1",	"Software Register Protect 1 (SRP1)" },
+		{ "BPL",	"Block Protect Write Disable (BPL)" },
+		{ "WP",		"WP# Disable (WPDIS)" },
+		{ "CMP",	"Complement Protect (CMP)" },
+		{ "WPS",	"Write Protect Scheme (WPS)" },
+		{ "QE",		"Quad Enable (QE)" },
+		{ "SUS",	"Erase/Program Suspend (SUS)" },
+		{ "SUS1",	"Erase Suspend (SUS1)" },
+		{ "SUS2",	"Program Suspend (SUS2)" },
+		{ "DRV0",	"Output Driver Strength (DRV0)" },
+		{ "DRV1",	"Output Driver Strength (DRV1)" },
+		{ "RST",	"HOLD/Reset (RST)" },
+		{ "HPF",	"HPF/HPM (High Performance Flag)" },
+		{ "LPE",	"Low Power Enable (LPE)" },
+		{ "AAI",	"Auto Address Increment (AAI)" },
+		{ "APT",	"All Protect (APT)" },
+		{ "CP",		"Continuously Program mode (CP)" },
+		/* The order of the following bits must not be altered and
+		 * newer entries must not be inserted between them. */
+		{ "BP0",	"Block Protect 0 (BP0)" },
+		{ "BP1",	"Block Protect 1 (BP1)" },
+		{ "BP2",	"Block Protect 2 (BP2)" },
+		{ "BP3",	"Block Protect 3 (BP3)" },
+		{ "BP4",	"Block Protect 4 (BP4)" },
+		{ "TB",		"Top/Bottom Block Protect (TB)" },
+		{ "SEC",	"Sector/Block Protect (SEC)" },
+		{ "LB1",	"Security Register Lock (LB/LB1)" },
+		{ "LB2",	"Security Register Lock (LB2)" },
+		{ "LB3",	"Security Register Lock (LB3)" },
+};
 
 /* === Generic functions === */
 int spi_write_status_enable(struct flashctx *flash)
 {
 	static const unsigned char cmd[JEDEC_EWSR_OUTSIZE] = { JEDEC_EWSR };
 	int result;
 
 	/* Send EWSR (Enable Write Status Register). */
 	result = spi_send_command(flash, sizeof(cmd), JEDEC_EWSR_INSIZE, cmd, NULL);
 
 	if (result)
 		msg_cerr("%s failed\n", __func__);
 
@@ -98,41 +141,336 @@  int spi_write_status_register(struct flashctx *flash, int status)
 
 	if (!(feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) {
 		msg_cdbg("Missing status register write definition, assuming "
 			 "EWSR is needed\n");
 		feature_bits |= FEATURE_WRSR_EWSR;
 	}
 	if (feature_bits & FEATURE_WRSR_WREN)
 		ret = spi_write_status_register_flag(flash, status, JEDEC_WREN);
 	if (ret && (feature_bits & FEATURE_WRSR_EWSR))
 		ret = spi_write_status_register_flag(flash, status, JEDEC_EWSR);
 	return ret;
 }
 
+// TODO(hatim): This function should be decommissioned once integration is complete
 uint8_t spi_read_status_register(struct flashctx *flash)
 {
 	static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { JEDEC_RDSR };
 	/* FIXME: No workarounds for driver/hardware bugs in generic code. */
 	unsigned char readarr[2]; /* JEDEC_RDSR_INSIZE=1 but wbsio needs 2 */
 	int ret;
 
 	/* Read Status Register */
 	ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
 	if (ret)
 		msg_cerr("RDSR failed!\n");
 
 	return readarr[0];
 }
 
+/* Generic function to read status register SRn (n = 1, 2, or 3). */
+// TODO(hatim): This should eventually replace calls to spi_read_status_register().
+uint8_t spi_read_status_register_generic(struct flashctx *flash, enum status_register_num SRn)
+{
+	// TODO(hatim): Check whether flash has SRn in the first place
+	static unsigned char const cmd[MAX_STATUS_REGISTERS][JEDEC_RDSR_OUTSIZE] = {
+		{ JEDEC_RDSR }, { JEDEC_RDSR2 }, { JEDEC_RDSR3 }
+	};
+	unsigned char readarr[JEDEC_RDSR_INSIZE];
+
+	if (spi_send_command(flash, sizeof(cmd[SRn]), sizeof(readarr), cmd[SRn], readarr))
+		msg_cerr("%s for SR%d failed.\n", __func__, SRn + 1);
+	return (uint8_t)readarr[0];
+}
+
+/* Called by spi_write_status_register_generic() to set status for chips with
+ * exactly 2 status registers. */
+static int spi_write_status_generic_2(struct flashctx *flash, uint16_t status)
+{
+	int result = spi_write_enable(flash);
+	if (result) {
+		msg_cerr("%s failed\n", __func__);
+		return result;
+	}
+
+	unsigned char cmd[JEDEC_WRSR_2_OUTSIZE] = { JEDEC_WRSR, status & 0xff, (status >> 8) & 0xff };
+	result = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+	if (result)
+		msg_cerr("%s failed\n", __func__);
+	// FIXME(hatim): Verify whether status was indeed written (?)
+	return result;
+}
+
+/* Called by spi_write_status_register_generic() to set status for chips with
+ * exactly 3 status registers. */
+static int spi_write_status_generic_3(struct flashctx *flash, enum status_register_num SRn, uint8_t status)
+{
+	int result = spi_write_enable(flash);
+	if (result) {
+		msg_cerr("%s failed\n", __func__);
+		return result;
+	}
+
+	unsigned char cmd[JEDEC_WRSR_OUTSIZE];
+	switch (SRn) {
+	case SR1:
+		cmd[0] = JEDEC_WRSR1;
+		break;
+	case SR2:
+		cmd[0] = JEDEC_WRSR2;
+		break;
+	case SR3:
+		cmd[0] = JEDEC_WRSR3;
+		break;
+	}
+	cmd[1] = status;
+	result = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+	if (result)
+		msg_cerr("%s failed\n", __func__);
+	// FIXME(hatim): Verify whether status was indeed written (?)
+	return result;
+}
+
+/* Generic function to set status register SRn (n = 1, 2, or 3) to status. */
+// TODO(hatim): This should eventually replace calls to spi_write_status_register()
+// and, consequently spi_write_status_register() should be made static.
+int spi_write_status_register_generic(struct flashctx *flash, enum status_register_num SRn, uint8_t status)
+{
+	// TODO(hatim): Check whether flash has SRn in the first place
+	int result = 0;
+	uint8_t other_status;
+	uint16_t total_status;
+
+	switch (top_status_register(flash)) {
+	case SR1:
+		result = spi_write_status_register(flash, (int)status);
+		break;
+	case SR2:
+		/* Use specific read function if available */
+		if (flash->chip->status_register->read)
+			other_status = flash->chip->status_register->read(flash, SRn);
+		else
+			other_status = spi_read_status_register_generic(flash, (SRn == SR1) ? SR2 : SR1);
+		total_status = (SRn == SR1) ? ((other_status << 8) | status) : ((status << 8) | other_status);
+		result = spi_write_status_generic_2(flash, total_status);
+		break;
+	case SR3:
+		result = spi_write_status_generic_3(flash, SRn, status);
+		break;
+	}
+	if (result)
+		msg_cerr("%s failed\n", __func__);
+	return result;
+}
+
+/* Return top-most (highest) status register. */
+enum status_register_num top_status_register(struct flashctx *flash) {
+	enum status_register_num SRn = SR1;
+	enum status_register_bit (*layout)[8] = flash->chip->status_register->layout;
+
+	while (layout[SRn++][0] != INVALID_BIT)
+		;
+	return SRn - 2;
+}
+
+/* Given a bit, return position of the bit in status register or -1 if not found.
+ * Counting starts from 0 for bit_0 of SR1, moves to 8 for bit_0 of SR2 and,
+ * finally moves to 23 for bit_7 of SR3. */
+char pos_bit(struct flashctx *flash, enum status_register_bit bit) {
+	char pos = -1;
+
+	for (enum status_register_num SRn = SR1; SRn <= top_status_register(flash); SRn++) {
+		for (int j = 0; j < 8; j++) {
+			if (flash->chip->status_register->layout[SRn][j] == bit) {
+				pos = (8 * SRn) + j;
+				break;
+			}
+		}
+	}
+	return pos;
+}
+
+// TODO(hatim): Improve code, merge different switch-cases into one
+enum wp_mode get_wp_mode_generic(struct flashctx *flash)
+{
+	// TODO(hatim): Check whether chip has said status register and bits in the first place
+	int result, status, status_tmp, srp_mask;
+
+	if (pos_bit(flash, SRP1) != -1) {
+		/* The following code assumes that SRP0 and SRP1 are present in the first and second
+		 * status registers as bit_7 and bit_8 respectively. */
+		status = flash->chip->status_register->read(flash, SR1);
+		status |= flash->chip->status_register->read(flash, SR2) << 8;
+		srp_mask = 1 << 7 | 1 << 8;
+
+		switch ((status & srp_mask) >> 7) {
+		case 0x00:
+			return WP_MODE_SOFTWARE;
+		case 0x01:
+			/* Make (SRP1, SRP0) = (0, 0). */
+			status_tmp = (status & ~srp_mask) & 0xffff;
+			/* Only need to write SRP0=0, which is always present in the first status register. */
+			if (flash->chip->status_register->write(flash, SR1, (uint8_t)(status_tmp & 0xff))) {
+				/* FIXME: If flash->chip->status_register->write() verifies whether status
+				 * supplied to it is indeed written, then, if we are in this block, we should
+				 * simply return WP_MODE_HARDWARE_PROTECTED because failure is a sufficient
+				 * indication for hardware protection mode to be on. */
+				msg_cerr("%s failed\n", __func__);
+				return WP_MODE_INVALID;
+			}
+			status_tmp = flash->chip->status_register->read(flash, SR1);
+			status_tmp |= flash->chip->status_register->read(flash, SR2) << 8;
+			result = (status_tmp & srp_mask) >> 7;
+			if (result == 0x01) {
+				return WP_MODE_HARDWARE_PROTECTED;
+			} else if (result == 0x00) {
+				status_tmp = ((status_tmp & ~srp_mask) | (0x01 << 7)) & 0xffff;
+				if (flash->chip->status_register->write(flash, SR1,
+					(uint8_t)(status_tmp & 0xff))) {
+					msg_cerr("%s failed\n", __func__);
+					return WP_MODE_SOFTWARE;
+				}
+				return WP_MODE_HARDWARE_UNPROTECTED;
+			} else {
+				return WP_MODE_INVALID;
+			}
+		case 0x02:
+			return WP_MODE_POWER_CYCLE;
+		case 0x03:
+			return WP_MODE_PERMANENT;
+		default:
+			return WP_MODE_INVALID;
+		}
+	} else {
+		status = flash->chip->status_register->read(flash, SR1);
+		srp_mask = 1 << 7;
+
+		switch ((status & srp_mask) >> 7) {
+		case 0x00:
+			return WP_MODE_SOFTWARE;
+		case 0x01:
+			/* Make SRP0 = 0. */
+			status_tmp = (status & ~srp_mask) & 0xff;
+			result = flash->chip->status_register->write(flash, SR1, (uint8_t)status_tmp);
+			if (result) {
+				/* FIXME: If flash->chip->status_register->write() verifies whether status
+				 * supplied to it is indeed written, then, if we are in this block, we should
+				 * simply return WP_MODE_HARDWARE_PROTECTED because failure is a sufficient
+				 * indication for hardware protection mode to be on. */
+				msg_cerr("%s failed\n", __func__);
+				return WP_MODE_INVALID;
+			}
+			status_tmp = flash->chip->status_register->read(flash, SR1);
+			result = (status_tmp & srp_mask) >> 7;
+			if (result == 0x01) {
+				return WP_MODE_HARDWARE_PROTECTED;
+			} else if (result == 0x00) {
+				status_tmp = ((status_tmp & ~srp_mask) | (0x01 << 7)) & 0xff;
+				if (flash->chip->status_register->write(flash, SR1, (uint8_t)status_tmp)) {
+					msg_cerr("%s failed\n", __func__);
+					return WP_MODE_SOFTWARE;
+				}
+				return WP_MODE_HARDWARE_UNPROTECTED;
+			} else {
+				return WP_MODE_INVALID;
+			}
+		default:
+			return WP_MODE_INVALID;
+		}
+	}
+}
+
+// TODO(hatim): Improve code, merge different switch-cases into one
+int set_wp_mode_generic(struct flashctx *flash, enum wp_mode wp_mode)
+{
+	int srp_mask = 1 << 7, srp_bitfield, srp_bitfield_new, result;
+	int status = flash->chip->status_register->read(flash, SR1);
+
+	if (pos_bit(flash, SRP1) != -1) {
+		/* The following code assumes that SRP0 and SRP1 are present and that they
+		 * are present in the first and second status registers as bit_7 and bit_8
+		 * respectively. */
+		status |= flash->chip->status_register->read(flash, SR2) << 8;
+		/* SRP1 is bit_8. */
+		srp_mask |= 1 << 8;
+		switch (wp_mode) {
+		case WP_MODE_SOFTWARE:
+			srp_bitfield = 0x00;
+			break;
+		case WP_MODE_HARDWARE_UNPROTECTED:
+			srp_bitfield = 0x01;
+			break;
+		case WP_MODE_POWER_CYCLE:
+			srp_bitfield = 0x02;
+			break;
+		case WP_MODE_PERMANENT:
+			srp_bitfield = 0x03;
+			break;
+		default:
+			msg_cdbg("Invalid write protection mode for status register(s)\n");
+			msg_cerr("%s failed\n", __func__);
+			return -1;
+		}
+		status = ((status & ~srp_mask) | (srp_bitfield << 7)) & 0xffff;
+		result = flash->chip->status_register->write(flash, SR1, (uint8_t)(status & 0xff));
+		if (result) {
+			msg_cerr("%s failed", __func__);
+			return result;
+		}
+		result = flash->chip->status_register->write(flash, SR2, (uint8_t)((status >> 8) & 0xff));
+		if (result) {
+			msg_cerr("%s failed", __func__);
+			return result;
+		}
+		status = flash->chip->status_register->read(flash, SR1);
+		status |= flash->chip->status_register->read(flash, SR2) << 8;
+		srp_bitfield_new = (status & srp_mask ) >> 7;
+		if (srp_bitfield_new != srp_bitfield) {
+			msg_cdbg("Setting write protection mode for status register(s) failed\n");
+			msg_cerr("%s failed\n", __func__);
+			return -1;
+		}
+	} else {
+		switch (wp_mode) {
+		case WP_MODE_SOFTWARE:
+			srp_bitfield = 0x00;
+			break;
+		case WP_MODE_HARDWARE_UNPROTECTED:
+			srp_bitfield = 0x01;
+			break;
+		case WP_MODE_POWER_CYCLE:
+		case WP_MODE_PERMANENT:
+		default:
+			msg_cdbg("Invalid write protection mode for status register(s)\n");
+			msg_cerr("%s failed\n", __func__);
+			return -1;
+		}
+		status = ((status & ~srp_mask) | (srp_bitfield << 7)) & 0xff;
+		result = flash->chip->status_register->write(flash, SR1, (uint8_t)status);
+		if (result) {
+			msg_cerr("%s failed", __func__);
+			return result;
+		}
+		status = flash->chip->status_register->read(flash, SR1);
+		srp_bitfield_new = (status & srp_mask ) >> 7;
+		if (srp_bitfield_new != srp_bitfield) {
+			msg_cdbg("Setting write protection mode for status register(s) failed\n");
+			msg_cerr("%s failed\n", __func__);
+			return -1;
+		}
+	}
+	return 0;
+}
+
 /* A generic block protection disable.
  * Tests if a protection is enabled with the block protection mask (bp_mask) and returns success otherwise.
  * Tests if the register bits are locked with the lock_mask (lock_mask).
  * Tests if a hardware protection is active (i.e. low pin/high bit value) with the write protection mask
  * (wp_mask) and bails out in that case.
  * If there are register lock bits set we try to disable them by unsetting those bits of the previous register
  * contents that are set in the lock_mask. We then check if removing the lock bits has worked and continue as if
  * they never had been engaged:
  * If the lock bits are out of the way try to disable engaged protections.
  * To support uncommon global unprotects (e.g. on most AT2[56]xx1(A)) unprotect_mask can be used to force
  * bits to 0 additionally to those set in bp_mask and lock_mask. Only bits set in unprotect_mask are potentially
  * preserved when doing the final unprotect.
  *
@@ -215,188 +553,260 @@  int spi_disable_blockprotect_bp2_srwd(struct flashctx *flash)
  * protected/locked by bit #7. */
 int spi_disable_blockprotect_bp3_srwd(struct flashctx *flash)
 {
 	return spi_disable_blockprotect_generic(flash, 0x3C, 1 << 7, 0, 0xFF);
 }
 
 /* A common block protection disable that tries to unset the status register bits masked by 0x7C (BP0-4) and
  * protected/locked by bit #7. */
 int spi_disable_blockprotect_bp4_srwd(struct flashctx *flash)
 {
 	return spi_disable_blockprotect_generic(flash, 0x7C, 1 << 7, 0, 0xFF);
 }
 
+// TODO(hatim): This function should be decommissioned once integration is complete
 static void spi_prettyprint_status_register_hex(uint8_t status)
 {
 	msg_cdbg("Chip status register is 0x%02x.\n", status);
 }
 
 /* Common highest bit: Status Register Write Disable (SRWD) or Status Register Protect (SRP). */
+// TODO(hatim): This function should be decommissioned once integration is complete
 static void spi_prettyprint_status_register_srwd(uint8_t status)
 {
 	msg_cdbg("Chip status register: Status Register Write Disable (SRWD, SRP, ...) is %sset\n",
 		 (status & (1 << 7)) ? "" : "not ");
 }
 
 /* Common highest bit: Block Protect Write Disable (BPL). */
+// TODO(hatim): This function should be decommissioned once integration is complete
 static void spi_prettyprint_status_register_bpl(uint8_t status)
 {
 	msg_cdbg("Chip status register: Block Protect Write Disable (BPL) is %sset\n",
 		 (status & (1 << 7)) ? "" : "not ");
 }
 
 /* Common lowest 2 bits: WEL and WIP. */
+// TODO(hatim): This function should be decommissioned once integration is complete
 static void spi_prettyprint_status_register_welwip(uint8_t status)
 {
 	msg_cdbg("Chip status register: Write Enable Latch (WEL) is %sset\n",
 		 (status & (1 << 1)) ? "" : "not ");
 	msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is %sset\n",
 		 (status & (1 << 0)) ? "" : "not ");
 }
 
 /* Common block protection (BP) bits. */
+// TODO(hatim): This function should be decommissioned once integration is complete
 static void spi_prettyprint_status_register_bp(uint8_t status, int bp)
 {
 	switch (bp) {
 	/* Fall through. */
 	case 4:
 		msg_cdbg("Chip status register: Block Protect 4 (BP4) is %sset\n",
 			 (status & (1 << 6)) ? "" : "not ");
 	case 3:
 		msg_cdbg("Chip status register: Block Protect 3 (BP3) is %sset\n",
 			 (status & (1 << 5)) ? "" : "not ");
 	case 2:
 		msg_cdbg("Chip status register: Block Protect 2 (BP2) is %sset\n",
 			 (status & (1 << 4)) ? "" : "not ");
 	case 1:
 		msg_cdbg("Chip status register: Block Protect 1 (BP1) is %sset\n",
 			 (status & (1 << 3)) ? "" : "not ");
 	case 0:
 		msg_cdbg("Chip status register: Block Protect 0 (BP0) is %sset\n",
 			 (status & (1 << 2)) ? "" : "not ");
 	}
 }
 
 /* Unnamed bits. */
+// TODO(hatim): This function should be decommissioned once integration is complete
 void spi_prettyprint_status_register_bit(uint8_t status, int bit)
 {
 	msg_cdbg("Chip status register: Bit %i is %sset\n", bit, (status & (1 << bit)) ? "" : "not ");
 }
 
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_prettyprint_status_register_plain(struct flashctx *flash)
 {
 	uint8_t status = spi_read_status_register(flash);
 	spi_prettyprint_status_register_hex(status);
 	return 0;
 }
 
 /* Print the plain hex value and the welwip bits only. */
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_prettyprint_status_register_default_welwip(struct flashctx *flash)
 {
 	uint8_t status = spi_read_status_register(flash);
 	spi_prettyprint_status_register_hex(status);
 
 	spi_prettyprint_status_register_welwip(status);
 	return 0;
 }
 
 /* Works for many chips of the
  * AMIC A25L series
  * and MX MX25L512
  */
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_prettyprint_status_register_bp1_srwd(struct flashctx *flash)
 {
 	uint8_t status = spi_read_status_register(flash);
 	spi_prettyprint_status_register_hex(status);
 
 	spi_prettyprint_status_register_srwd(status);
 	spi_prettyprint_status_register_bit(status, 6);
 	spi_prettyprint_status_register_bit(status, 5);
 	spi_prettyprint_status_register_bit(status, 4);
 	spi_prettyprint_status_register_bp(status, 1);
 	spi_prettyprint_status_register_welwip(status);
 	return 0;
 }
 
 /* Works for many chips of the
  * AMIC A25L series
  * PMC Pm25LD series
  */
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_prettyprint_status_register_bp2_srwd(struct flashctx *flash)
 {
 	uint8_t status = spi_read_status_register(flash);
 	spi_prettyprint_status_register_hex(status);
 
 	spi_prettyprint_status_register_srwd(status);
 	spi_prettyprint_status_register_bit(status, 6);
 	spi_prettyprint_status_register_bit(status, 5);
 	spi_prettyprint_status_register_bp(status, 2);
 	spi_prettyprint_status_register_welwip(status);
 	return 0;
 }
 
 /* Works for many chips of the
  * ST M25P series
  * MX MX25L series
  */
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_prettyprint_status_register_bp3_srwd(struct flashctx *flash)
 {
 	uint8_t status = spi_read_status_register(flash);
 	spi_prettyprint_status_register_hex(status);
 
 	spi_prettyprint_status_register_srwd(status);
 	spi_prettyprint_status_register_bit(status, 6);
 	spi_prettyprint_status_register_bp(status, 3);
 	spi_prettyprint_status_register_welwip(status);
 	return 0;
 }
 
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_prettyprint_status_register_bp4_srwd(struct flashctx *flash)
 {
 	uint8_t status = spi_read_status_register(flash);
 	spi_prettyprint_status_register_hex(status);
 
 	spi_prettyprint_status_register_srwd(status);
 	spi_prettyprint_status_register_bp(status, 4);
 	spi_prettyprint_status_register_welwip(status);
 	return 0;
 }
 
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_prettyprint_status_register_bp2_bpl(struct flashctx *flash)
 {
 	uint8_t status = spi_read_status_register(flash);
 	spi_prettyprint_status_register_hex(status);
 
 	spi_prettyprint_status_register_bpl(status);
 	spi_prettyprint_status_register_bit(status, 6);
 	spi_prettyprint_status_register_bit(status, 5);
 	spi_prettyprint_status_register_bp(status, 2);
 	spi_prettyprint_status_register_welwip(status);
 	return 0;
 }
 
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_prettyprint_status_register_bp2_tb_bpl(struct flashctx *flash)
 {
 	uint8_t status = spi_read_status_register(flash);
 	spi_prettyprint_status_register_hex(status);
 
 	spi_prettyprint_status_register_bpl(status);
 	spi_prettyprint_status_register_bit(status, 6);
 	msg_cdbg("Chip status register: Top/Bottom (TB) is %s\n", (status & (1 << 5)) ? "bottom" : "top");
 	spi_prettyprint_status_register_bp(status, 2);
 	spi_prettyprint_status_register_welwip(status);
 	return 0;
 }
 
+/* TODO: Use in place of printlock, after asigning a struct status_register member. This supersedes
+ * functionality of all prettyprint functions defined above. */
+int spi_prettyprint_status_register_generic(struct flashctx *flash, enum status_register_num SRn)
+{
+	uint8_t status = flash->chip->status_register->read(flash, SRn);
+	enum status_register_bit bit;
+
+	msg_cdbg("Chip status register %d is 0x%02x.\n", SRn + 1, status);
+	for (int i = 7; i >= 0; i--) {
+		switch (bit = flash->chip->status_register->layout[SRn][i]) {
+		case RESV:
+			msg_cdbg("Chip status register %d: Bit %d is reserved\n", SRn + 1, i);
+			break;
+		case TB:
+			msg_cdbg("Chip status register %d : %s is %s\n", SRn + 1, statreg_bit_desc[bit][1],
+				(status & (1 << i)) ? "bottom" : "top");
+			break;
+		case SEC:
+			msg_cdbg("Chip status register %d: %s is %s\n", SRn + 1, statreg_bit_desc[bit][1],
+				(status & (1 << i)) ? "sectors" : "blocks");
+		default:
+			msg_cdbg("Chip status register %d: %s is %sset\n", SRn + 1, statreg_bit_desc[bit][1],
+				(status & (1 << i)) ? "" : "not ");
+			break;
+		}
+	}
+	return 0;
+}
+
+int spi_prettyprint_status_register_wp_generic(struct flashctx *flash)
+{
+	uint8_t multiple = (top_status_register(flash) == SR1) ? 0 : 1;
+	switch (flash->chip->status_register->get_wp_mode(flash)) {
+	case WP_MODE_INVALID:
+		msg_cdbg("Invalid write protection mode for status register%s\n", (multiple) ? "s" : "");
+		break;
+	case WP_MODE_POWER_CYCLE:
+		msg_cdbg("Power supply lock down, cannot write to status register%s until next power "
+			"down-up cycle\n", (multiple) ? "s" : "");
+		break;
+	case WP_MODE_PERMANENT:
+		msg_cdbg("Status register%s permanently locked\n", (multiple) ? "s are" : " is");
+		break;
+	case WP_MODE_HARDWARE_PROTECTED:
+		msg_cerr("Hardware protection of status register%s is active (WP# pin low),"
+			" disabling impossible\n", (multiple) ? "s" : "");
+		break;
+	case WP_MODE_HARDWARE_UNPROTECTED:
+		msg_cdbg("Hardware protection is active (WP# pin high), writes to status "
+			"register%s still possible\n", (multiple) ? "s are" : " is");
+		break;
+	case WP_MODE_SOFTWARE:
+		msg_cdbg("Write protection for status register%s is NOT in effect\n", (multiple) ? "s" : "");
+		break;
+	}
+	return 0;
+}
+
 /* === Amic ===
  * FIXME: spi_disable_blockprotect is incorrect but works fine for chips using
  * spi_prettyprint_status_register_bp1_srwd or
  * spi_prettyprint_status_register_bp2_srwd.
  * FIXME: spi_disable_blockprotect is incorrect and will fail for chips using
  * spi_prettyprint_status_register_amic_a25l032 if those have locks controlled
  * by the second status register.
  */
 
 int spi_prettyprint_status_register_amic_a25l032(struct flashctx *flash)
 {
 	uint8_t status = spi_read_status_register(flash);
 	spi_prettyprint_status_register_hex(status);
diff --git a/spi25_statusreg.h b/spi25_statusreg.h
new file mode 100644
index 0000000..b082f45
--- /dev/null
+++ b/spi25_statusreg.h
@@ -0,0 +1,79 @@ 
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2016 Hatim Kanchwala <hatim@hatimak.me>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#ifndef __SPI25_STATUSREG_H__
+#define __SPI25_STATUSREG_H__ 1
+
+#include "flash.h"
+
+/* It has been observed that chips have at most 3 status registers. Please
+ * update the define if a chip with higher status registers is found. */
+#define MAX_STATUS_REGISTERS 3
+
+enum bit_state {
+	DONT_CARE = -1,	/* Don't care */
+	OFF = 0,
+	ON = 1,
+};
+
+/* The following enum defines bits found in status registers. Datasheets
+ * will mostly name bits in the same manner. */
+// TODO(hatim): Add other remaining bits
+enum status_register_bit {
+	INVALID_BIT = 0,	/* This must always stay at the top. */
+	RESV, WIP, WEL,		/* WIP is also referred as BUSY. */
+	/* SRP0 is same as SRP and SRWD. */
+	SRP0, SRP1, BPL, WP, CMP, WPS, QE, SUS, SUS1, SUS2, DRV0, DRV1, RST, HPF, LPE, AAI,
+	APT, CP,
+	/* The order of the following bits must not be altered and newer entries must not
+	 * be inserted in between them. */
+	BP0, BP1, BP2, BP3, BP4, TB, SEC, LB1, LB2, LB3 /* LB1 is same as LB. */
+};
+
+enum status_register_num { SR1 = 0, SR2, SR3 };
+
+/* The following enum defines write protection modes available for status registers. */
+enum wp_mode {
+	WP_MODE_INVALID = 0,
+
+	/* Status register is unlocked and can be written to after a WREN. */
+	WP_MODE_SOFTWARE,
+
+	/* When WP# is low, status register is locked and can not be written to.
+	 * In this mode SRP0=1, and SRP1=1 if present. */
+	WP_MODE_HARDWARE_PROTECTED,
+
+	/* When WP# is high status register is unlocked and can be written
+	 * to after WREN. In this mode SRP0=1. */
+	WP_MODE_HARDWARE_UNPROTECTED,
+
+	/* Status register is protected and can not be written to until next
+	 * power down/up cycle, post which status register will be unlocked
+	 * and can be written to after a WREN. */
+	WP_MODE_POWER_CYCLE,
+
+	/* Status register is permanently protected and cannot be written to. */
+	WP_MODE_PERMANENT,
+};
+
+/* Describes corresponding bits from enum status_register_bit */
+extern char *statreg_bit_desc[][2];
+
+#endif		/* !__SPI25_STATUSREG_H__ */