diff --git a/Makefile b/Makefile
index 4ebde1e..de08492 100644
--- a/Makefile
+++ b/Makefile
@@ -504,27 +504,27 @@ endif
 ifeq ($(CONFIG_IT8212), yes)
 UNSUPPORTED_FEATURES += CONFIG_IT8212=yes
 else
 override CONFIG_IT8212 = no
 endif
 endif
 
 ###############################################################################
 # Flash chip drivers and bus support infrastructure.
 
 CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \
 	sst28sf040.o 82802ab.o \
 	sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o spi25.o spi25_statusreg.o \
-	opaque.o sfdp.o en29lv640b.o at45db.o
+	writeprotect.o opaque.o sfdp.o en29lv640b.o at45db.o
 
 ###############################################################################
 # Library code.
 
 LIB_OBJS = layout.o flashrom.o udelay.o programmer.o helpers.o
 
 ###############################################################################
 # Frontend related stuff.
 
 CLI_OBJS = cli_classic.o cli_output.o cli_common.o print.o
 
 # Set the flashrom version string from the highest revision number of the checked out flashrom files.
 # Note to packagers: Any tree exported with "make export" or "make tarball"
diff --git a/chipdrivers.h b/chipdrivers.h
index 4c58f30..c60aff8 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -17,26 +17,27 @@
  * 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 */
+#include "writeprotect.h"
 
 /* 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);
@@ -103,26 +104,39 @@ int spi_disable_blockprotect_at25f(struct flashctx *flash);
 int spi_disable_blockprotect_at25f512a(struct flashctx *flash);
 int spi_disable_blockprotect_at25f512b(struct flashctx *flash);
 int spi_disable_blockprotect_at25fs010(struct flashctx *flash);
 int spi_disable_blockprotect_at25fs040(struct flashctx *flash);
 int spi_prettyprint_status_register_en25s_wp(struct flashctx *flash);
 int spi_prettyprint_status_register_n25q(struct flashctx *flash);
 int spi_disable_blockprotect_n25q(struct flashctx *flash);
 int spi_prettyprint_status_register_bp2_ep_srwd(struct flashctx *flash);
 int spi_disable_blockprotect_bp2_ep_srwd(struct flashctx *flash);
 int spi_prettyprint_status_register_sst25(struct flashctx *flash);
 int spi_prettyprint_status_register_sst25vf016(struct flashctx *flash);
 int spi_prettyprint_status_register_sst25vf040b(struct flashctx *flash);
 
+/* writeprotect.c */
+struct range *sec_block_range_pattern(struct flashctx *flash);
+char get_cmp(struct flashctx *flash);
+int set_cmp(struct flashctx *flash, uint8_t cmp);
+uint32_t bp_bitmask_generic(struct flashctx *flash);
+struct range *bp_to_range(struct flashctx *flash, unsigned char bp_config);
+int range_to_bp_bitfield(struct flashctx *flash, uint32_t start, uint32_t len);
+int print_range_generic(struct flashctx *flash);
+int print_table_generic(struct flashctx *flash);
+int set_range_generic(struct flashctx *flash, uint32_t start, uint32_t len);
+int disable_generic(struct flashctx *flash);
+struct range *range_table_global(struct flashctx *flash);
+
 /* sfdp.c */
 int probe_spi_sfdp(struct flashctx *flash);
 
 /* opaque.c */
 int probe_opaque(struct flashctx *flash);
 int read_opaque(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
 int write_opaque(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
 int erase_opaque(struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen);
 
 /* at45db.c */
 int probe_spi_at45db(struct flashctx *flash);
 int spi_prettyprint_status_register_at45db(struct flashctx *flash);
 int spi_disable_blockprotect_at45db(struct flashctx *flash);
diff --git a/flash.h b/flash.h
index 566b709..631c1a8 100644
--- a/flash.h
+++ b/flash.h
@@ -28,26 +28,27 @@
 
 #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"
+#include "writeprotect.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. */
@@ -211,34 +212,54 @@ struct flashchip {
 		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 (*unlock) (struct flashctx *flash);		// TODO(hatim): This member should be decommissioned
 	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 wp {
+		/* WP range table, indexed by BP bit configuration. For chips
+		 * that also have a CMP bit, the most significant bit after
+		 * the highest BP bit should represent the CMP bit. For example,
+		 * a chip with BP[0..2] and CMP bits, the index mask is 0x0f and
+		 * the most significant 1 represents CMP. */
+		struct range *ranges;
+		/* Either ranges is assigned to or range_table is, NOT both. */
+		/* Return pointer to WP range table. */
+		struct range *(*range_table) (struct flashctx *flash);
+		/* Return BP(BP0, BP1, ... , SEC, TB) bit mask. */
+		uint32_t (*bp_bitmask) (struct flashctx *flash);
+		/* Given a range, set the corresponding BP and CMP bit (if present) in the status
+		 * register. If range is invalid, return -1 and abort writing to status register. */
+		int (*set_range) (struct flashctx *flash, uint32_t start, uint32_t len);
+		/* Disable any block protection in effect. */
+		int (*disable) (struct flashctx *flash);
+		int (*print_table) (struct flashctx *flash);
+	} *wp;
 };
 
 struct flashctx {
 	struct flashchip *chip;
 	/* FIXME: The memory mappings should be saved in a more structured way. */
 	/* The physical_* fields store the respective addresses in the physical address space of the CPU. */
 	uintptr_t physical_memory;
 	/* The virtual_* fields store where the respective physical address is mapped into flashrom's address
 	 * space. A value equivalent to (chipaddr)ERROR_PTR indicates an invalid mapping (or none at all). */
 	chipaddr virtual_memory;
 	/* Some flash devices have an additional register space; semantics are like above. */
 	uintptr_t physical_registers;
 	chipaddr virtual_registers;
diff --git a/spi25_statusreg.c b/spi25_statusreg.c
index ac77c1d..c1e0cd9 100644
--- a/spi25_statusreg.c
+++ b/spi25_statusreg.c
@@ -471,26 +471,27 @@ int set_wp_mode_generic(struct flashctx *flash, enum wp_mode wp_mode)
  * 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.
  *
  * To sum up:
  * bp_mask: set those bits that correspond to the bits in the status register that indicate an active protection
  *          (which should be unset after this function returns).
  * lock_mask: set the bits that correspond to the bits that lock changing the bits above.
  * wp_mask: set the bits that correspond to bits indicating non-software revocable protections.
  * unprotect_mask: set the bits that should be preserved if possible when unprotecting.
  */
+// TODO(hatim): This function should be decommissioned once integration is complete
 static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_mask, uint8_t lock_mask, uint8_t wp_mask, uint8_t unprotect_mask)
 {
 	uint8_t status;
 	int result;
 
 	status = spi_read_status_register(flash);
 	if ((status & bp_mask) == 0) {
 		msg_cdbg2("Block protection is disabled.\n");
 		return 0;
 	}
 
 	msg_cdbg("Some block protection in effect, disabling... ");
 	if ((status & lock_mask) != 0) {
@@ -519,55 +520,60 @@ static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_m
 		return result;
 	}
 	status = spi_read_status_register(flash);
 	if ((status & bp_mask) != 0) {
 		msg_cerr("Block protection could not be disabled!\n");
 		flash->chip->printlock(flash);
 		return 1;
 	}
 	msg_cdbg("disabled.\n");
 	return 0;
 }
 
 /* A common block protection disable that tries to unset the status register bits masked by 0x3C. */
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_disable_blockprotect(struct flashctx *flash)
 {
 	return spi_disable_blockprotect_generic(flash, 0x3C, 0, 0, 0xFF);
 }
 
 /* A common block protection disable that tries to unset the status register bits masked by 0x0C (BP0-1) and
  * protected/locked by bit #7. Useful when bits 4-5 may be non-0). */
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_disable_blockprotect_bp1_srwd(struct flashctx *flash)
 {
 	return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 0, 0xFF);
 }
 
 /* A common block protection disable that tries to unset the status register bits masked by 0x1C (BP0-2) and
  * protected/locked by bit #7. Useful when bit #5 is neither a protection bit nor reserved (and hence possibly
  * non-0). */
+// TODO(hatim): This function should be decommissioned once integration is complete
 int spi_disable_blockprotect_bp2_srwd(struct flashctx *flash)
 {
 	return spi_disable_blockprotect_generic(flash, 0x1C, 1 << 7, 0, 0xFF);
 }
 
 /* A common block protection disable that tries to unset the status register bits masked by 0x3C (BP0-3) and
  * protected/locked by bit #7. */
+// TODO(hatim): This function should be decommissioned once integration is complete
 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. */
+// TODO(hatim): This function should be decommissioned once integration is complete
 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
diff --git a/writeprotect.c b/writeprotect.c
new file mode 100644
index 0000000..e256ae4
--- /dev/null
+++ b/writeprotect.c
@@ -0,0 +1,386 @@
+/*
+ * 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
+ */
+
+#include "chipdrivers.h"
+#include "flash.h"
+#include "spi25_statusreg.h"
+#include "writeprotect.h"
+
+static uint32_t wp_range_table_id;
+static struct range *wp_range_table;
+
+// TODO(hatim): Update for chips with density greater than 128Mb
+static uint16_t const sec_block_multiples[7][8] = {
+					/* Chip densities */
+	{ 0, 1, 1, 1, 0, 1, 1, 1 },	/* = 64 kB */
+	{ 0, 2, 1, 1, 0, 2, 1, 1 },	/* = 128 kB */
+	{ 0, 4, 2, 1, 0, 4, 2, 1 },	/* = 256 kB */
+	{ 0, 8, 4, 2, 1, 1, 1, 1 },	/* = 512 kB */
+	{ 0, 16, 8, 4, 2, 1, 1, 1 },	/* = 1024 kB */
+	{ 0, 32, 16, 8, 4, 2, 1, 1 },	/* = 2 * 1024 kB */
+	{ 0, 64, 32, 16, 8, 4, 2, 1 },	/* >= 4 * 1024 kB and <= 16 * 1024 kB */
+};
+
+static char const *bit_string[3][32] = {
+	{ "0\t0\t0", "0\t0\t1", "0\t1\t0", "0\t1\t1", "1\t0\t0", "1\t0\t1", "1\t1\t0", "1\t1\t1" },
+	{ "0\t0\t0\t0", "0\t0\t0\t1", "0\t0\t1\t0", "0\t0\t1\t1", "0\t1\t0\t0", "0\t1\t0\t1", "0\t1\t1\t0",
+	  "0\t1\t1\t1", "1\t0\t0\t0", "1\t0\t0\t1", "1\t0\t1\t0", "1\t0\t1\t1", "1\t1\t0\t0", "1\t1\t0\t1",
+	  "1\t1\t1\t0", "1\t1\t1\t1" },
+	{ "0\t0\t0\t0\t0", "0\t0\t0\t0\t1", "0\t0\t0\t1\t0", "0\t0\t0\t1\t1", "0\t0\t1\t0\t0", "0\t0\t1\t0\t1",
+	  "0\t0\t1\t1\t0", "0\t0\t1\t1\t1", "0\t1\t0\t0\t0", "0\t1\t0\t0\t1", "0\t1\t0\t1\t0", "0\t1\t0\t1\t1",
+	  "0\t1\t1\t0\t0", "0\t1\t1\t0\t1", "0\t1\t1\t1\t0", "0\t1\t1\t1\t1", "1\t0\t0\t0\t0", "1\t0\t0\t0\t1",
+	  "1\t0\t0\t1\t0", "1\t0\t0\t1\t1", "1\t0\t1\t0\t0", "1\t0\t1\t0\t1", "1\t0\t1\t1\t0", "1\t0\t1\t1\t1",
+	  "1\t1\t0\t0\t0", "1\t1\t0\t0\t1", "1\t1\t0\t1\t0", "1\t1\t0\t1\t1", "1\t1\t1\t0\t0", "1\t1\t1\t0\t1",
+	  "1\t1\t1\t1\t0", "1\t1\t1\t1\t1" },
+};
+
+/* === Generic functions === */
+/* Most SPI chips (especially GigaDeice and Winbond) follow a specific
+ * pattern for their block protection ranges. This pattern is a function
+ * of BP and other modifier bits, and chip density. Quite a few AMIC, Eon,
+ * Fudan and Macronix chips also somewhat follow this pattern. Given the status
+ * register layout of a chip, the following returns the range table according
+ * to the pattern relevant to the chip. The presence of a CMP bit is handled
+ * automatically. Please see the note below for the standard for CMP bit.
+ *
+ * When adding support for newer chips that have protection ranges similar
+ * to the chips already using this pattern, consider using this function
+ * to generate the boilerplate range, and then modifying it appropriately. */
+// TODO(hatim): Make static (?)
+struct range *sec_block_range_pattern(struct flashctx *flash) {
+	/* Note that a couple of lines in the code below may seem missing,
+	 * but we get away with not writing them because ranges[] is static. */
+	static struct range ranges[LEN_RANGES];
+	uint8_t convention = 1, multiple = 0, cmp_mask_pos = 3;
+	/* cmp_mask_pos points to the bit position for CMP, if the chip has one. */
+	int tot_size = flash->chip->total_size;
+
+	// TODO(hatim): Add support for chips with density greater than 128Mb
+	switch (tot_size) {
+	case 64:
+		multiple = 0;
+		break;
+	case 128:
+		multiple = 1;
+		break;
+	case 256:
+		multiple = 2;
+		break;
+	case 512:
+		multiple = 3;
+		break;
+	case 1024:
+		multiple = 4;
+		convention = 0;
+		break;
+	case 2 * 1024:
+		multiple = 5;
+		convention = 0;
+		break;
+	case 4 * 1024:
+	case 8 * 1024:
+	case 16 * 1024:
+		multiple = 6;
+		break;
+	}
+	/* It has been observed that roles of SEC and TB bits are identical
+	 * to that of BP4 and BP3 bits respectively.
+	 * (BP2=0, BP1=0, BP1=0) and (BP2=1, BP1=1, BP1=1) almost always
+	 * correpsond to NONE and ALL protection range respectively. */
+	/* (SEC=0, TB=0) Uppper */
+	for (int i = 0; i < 8; i++) {
+		if (sec_block_multiples[multiple][i]) {
+			ranges[i].start = (tot_size - tot_size / sec_block_multiples[multiple][i]) * 1024;
+			ranges[i].len	= tot_size / sec_block_multiples[multiple][i];
+		}
+	}
+	/* No point in continuing if chip does NOT have TB (or BP3) bit. */
+	if (!(pos_bit(flash, TB) != -1 || pos_bit(flash, BP3) != -1))
+		goto compute_cmp_ranges;
+
+	/* (SEC=0, TB=1) Lower */
+	for (int i = 0; i < 8; i++) {
+		if (sec_block_multiples[multiple][i]) {
+			ranges[0x01 << 3 | i].len = tot_size / sec_block_multiples[multiple][i];
+		}
+	}
+	cmp_mask_pos++;
+	/* No point in continuing if chip does NOT have SEC (or BP4) bit. */
+	if (!(pos_bit(flash, SEC) != -1 || pos_bit(flash, BP4) != -1))
+		goto compute_cmp_ranges;
+
+	/* (SEC=1, TB=0) Uppper */
+	ranges[0x02 << 3 | 0x01].start	= (tot_size - 4) * 1024;
+	ranges[0x02 << 3 | 0x01].len	= 4;
+	ranges[0x02 << 3 | 0x02].start	= (tot_size - 8) * 1024;
+	ranges[0x02 << 3 | 0x02].len	= 8;
+	ranges[0x02 << 3 | 0x03].start	= (tot_size - 16) * 1024;
+	ranges[0x02 << 3 | 0x03].len	= 16;
+	ranges[0x02 << 3 | 0x04].start	= (tot_size - 32) * 1024;
+	ranges[0x02 << 3 | 0x04].len	= 32;
+	ranges[0x02 << 3 | 0x05].start	= (tot_size - 32) * 1024;
+	ranges[0x02 << 3 | 0x05].len	= 32;
+	if (convention) {
+		ranges[0x02 << 3 | 0x06].start	= (tot_size - 32) * 1024;
+		ranges[0x02 << 3 | 0x06].len	= 32;
+	} else
+		ranges[0x02 << 3 | 0x06].len	= tot_size;
+	ranges[0x02 << 3 | 0x07].len	= tot_size;
+
+	/* SEC, TB = (1, 1) Lower */
+	ranges[0x03 << 3 | 0x01].len	= 4;
+	ranges[0x03 << 3 | 0x02].len	= 8;
+	ranges[0x03 << 3 | 0x03].len	= 16;
+	ranges[0x03 << 3 | 0x04].len	= 32;
+	ranges[0x03 << 3 | 0x05].len	= 32;
+	if (convention)
+		ranges[0x02 << 3 | 0x06].len = 32;
+	else
+		ranges[0x02 << 3 | 0x06].len = tot_size;
+	ranges[0x03 << 3 | 0x07].len	= tot_size;
+	cmp_mask_pos++;
+
+compute_cmp_ranges:
+	/* For chips with a CMP bit, the first unused bit position (or the position just higher
+	 * than the most significant bit position in the bp_bitmask) is for the CMP bit. So for
+	 * instance a chip has BP[2..0] and CMP bits then, bits 0, 1 and 2 represent BP0, BP1
+	 * and BP2 respectively, and bit 3 represents CMP. We use bp_bitmask to index into
+	 * ranges[]. So, for CMP=1 and BP[2..0]=0x03, the valid range is given by ranges[0x0f]. */
+	if (pos_bit(flash, CMP) != -1) {
+		for (int i = 0, j = 1 << cmp_mask_pos; i < (1 << cmp_mask_pos); i++) {
+			ranges[j | i].len = tot_size - ranges[i].len;
+			if (ranges[i].len != tot_size && ranges[i].start == 0x000000)
+				ranges[j | i].start = ranges[i].len * 1024;
+		}
+	}
+
+	return ranges;
+}
+
+// TODO(hatim): Make static (?)
+char get_cmp(struct flashctx *flash) {
+	int pos_cmp = pos_bit(flash, CMP);
+	if (pos_cmp == -1)
+		return -1;
+	else {
+		enum status_register_num SRn = pos_cmp / 8;
+		pos_cmp -= SRn * 8;
+		uint8_t cmp_mask = 1 << pos_cmp;
+		uint8_t status = flash->chip->status_register->read(flash, SRn);
+		return status & cmp_mask;
+	}
+}
+
+// TODO(hatim): Make static (?)
+// TODO(hatim): Change type of cmp to enum bit_state
+int set_cmp(struct flashctx *flash, uint8_t cmp) {
+	int pos_cmp = pos_bit(flash, CMP);
+	if (pos_cmp == -1) {
+		msg_cdbg("Chip does not have CMP bit!\n");
+		msg_cerr("%s failed\n", __func__);
+		return -1;
+	} else {
+		enum status_register_num SRn = pos_cmp / 8;
+		pos_cmp -= SRn * 8;
+		uint8_t cmp_mask = 1 << pos_cmp;
+		uint8_t status = flash->chip->status_register->read(flash, SRn);
+		status = ((status & ~cmp_mask) | ((cmp ? 1 : 0) << pos_cmp)) & 0xff;
+		/* FIXME: Verify whether CMP was indeed written. */
+		return flash->chip->status_register->write(flash, SRn, status);
+	}
+}
+
+/* Wrapper for flash->chip->wp->range_table(). Returns the WP range table member of wp,
+ * or, generates (then stores for later use) and returns one using range_table member
+ * of wp. */
+struct range *range_table_global(struct flashctx *flash) {
+	if (flash->chip->wp->ranges) {
+		return flash->chip->wp->ranges;
+	} else {
+		if (wp_range_table_id != flash->chip->model_id) {
+			wp_range_table = flash->chip->wp->range_table(flash);
+			wp_range_table_id = flash->chip->model_id;
+		}
+		return wp_range_table;
+	}
+}
+
+/* Return BP bit mask. */
+uint32_t bp_bitmask_generic(struct flashctx *flash) {
+	/* The following code relies on the assumption that BP bits are present
+	 * in continuity (back-to-back) in the first status register. Correct
+	 * bitmask will NOT be returned if, say TB bit, is separately located
+	 * from the rest of the BP bits. */
+	unsigned char bitmask = 0x00;
+	enum status_register_bit bits[] = { BP0, BP1, BP2, BP3, BP4, SEC, TB };
+
+	for (int i = 0; i < ARRAY_SIZE(bits); i++) {
+		if (pos_bit(flash, bits[i]) != -1)
+			bitmask |= 1 << pos_bit(flash, bits[i]);
+	}
+	return bitmask;
+}
+
+/* Given BP bits' configuration, return the corresponding range.
+ * For chips with a CMP bit, read the CMP value and automatically adjust. */
+// TODO(hatim): Change type of bp_config to uint8_t
+struct range *bp_to_range(struct flashctx *flash, unsigned char bp_config) {
+	if (pos_bit(flash, CMP) == -1) {
+		return &(range_table_global(flash)[bp_config]);
+	} else {
+		uint32_t cmp_mask = (flash->chip->wp->bp_bitmask(flash) >> pos_bit(flash, BP0)) + 1;
+		cmp_mask = (get_cmp(flash) ? 0xffff : 0x0000) & cmp_mask;
+		return &(range_table_global(flash)[cmp_mask | bp_config]);
+	}
+}
+
+/* Given WP range, return BP bits' configuration (or -1, if range is invalid).
+ * FIXME(hatim): This does NOT handle CMP bit automatically (yet) */
+int range_to_bp_bitfield(struct flashctx *flash, uint32_t start, uint32_t len) {
+	struct range *table = range_table_global(flash);
+	int limit = 0, bitmask = flash->chip->wp->bp_bitmask(flash);
+
+	for (int i = 7; i >= 0; i--)
+		if (bitmask & (1 << i))
+			limit++;
+	limit = 1 << limit;
+	for (int i = 0; i < limit * ((pos_bit(flash, CMP) == -1) ? 1 : 2); i++) {
+		if (table[i].start == start && table[i].len == len)
+			return i;
+	}
+	return -1;
+}
+
+int print_range_generic(struct flashctx *flash) {
+	uint8_t status = flash->chip->status_register->read(flash, SR1);
+	uint8_t bp_config = (status & flash->chip->wp->bp_bitmask(flash)) >> pos_bit(flash, BP0);
+
+	struct range *range = bp_to_range(flash, bp_config);
+
+	if (range->len == flash->chip->total_size)
+		msg_cdbg("Entire memory range is protected against erases and writes\n");
+	else if (range->len != 0)
+		msg_cdbg("%d kB, starting from address 0x%06x, is protected against erases and writes\n",
+			range->len, range->start);
+	else
+		msg_cdbg("Entire memory range is open to erases and writes\n");
+	return 0;
+}
+
+int print_table_generic(struct flashctx *flash) {
+	uint32_t bitmask = flash->chip->wp->bp_bitmask(flash);
+	int limit = 0, pos_cmp = pos_bit(flash, CMP);
+	/* FIXME: The following relies on the assumption that the entire
+	 * BP bitmask (BP0..4, SEC, TB) is spread across only the first
+	 * status register, hence 8 iterations for the following loop. */
+	if (pos_cmp != -1)
+		msg_cdbg("%s\t", statreg_bit_desc[CMP][0]);
+	for (int i = 7; i >= 0; i--) {
+		if (bitmask & (1 << i)) {
+			limit++;
+			msg_cdbg("%s\t", statreg_bit_desc[flash->chip->status_register->layout[SR1][i]][0]);
+		}
+	}
+	msg_cdbg("START ADDR\tLENGTH (kB)\n");
+
+	limit = 1 << limit;
+	uint8_t j = (limit == 32) ? 2 : (limit == 16) ? 1 : 0;
+	struct range *table = range_table_global(flash);
+
+	for (int i = 0; i < limit * ((pos_cmp == -1) ? 1 : 2); i++)
+		msg_cdbg("%s%s\t0x%06x\t%6d\n",
+			(pos_cmp == -1) ? "" : (i >= limit) ? "1\t" : "0\t",
+			bit_string[j][(i >= limit) ? i - limit : i],
+			table[i].start, table[i].len);
+	return 0;
+}
+
+/* Given a range, set the corresponding BP and CMP bit (if present) in the status
+ * register. If range is invalid, return -1 and abort writing to status register. */
+int set_range_generic(struct flashctx *flash, uint32_t start, uint32_t len) {
+	int bitfield = range_to_bp_bitfield(flash, start, len);
+	if (bitfield == -1) {
+		msg_cdbg("Invalid block protection range start=0x%06x, len=%d kB\n", start, len);
+		msg_cerr("%s failed\n", __func__);
+		return -1;
+	}
+
+	int status = flash->chip->status_register->read(flash, SR1);
+	int bitmask = flash->chip->wp->bp_bitmask(flash), result;
+
+	status = (((bitfield << pos_bit(flash, BP0)) & bitmask) | (status & ~bitmask)) & 0xff;
+	result = flash->chip->status_register->write(flash, SR1, (uint8_t)status);
+	if (pos_bit(flash, CMP) != -1) {
+		int cmp_mask = (flash->chip->wp->bp_bitmask(flash) >> pos_bit(flash, BP0)) + 1;
+		result &= set_cmp(flash, bitfield & cmp_mask);
+	}
+	if (result)
+		msg_cerr("%s failed\n", __func__);
+	// FIXME(hatim): Perform a read to verify BP bitfield
+	return result;
+}
+
+/* TODO: This should eventually replace functionality of (almost all) variants of blockprotect
+ * from spi25_statusreg.c */
+int disable_generic(struct flashctx *flash) {
+	/* The following code assumes that all of the BP and modifier bits (SEC, TB) are present
+	 * only in the first status register, and consequently the BP bitmask only masks bits
+	 * from the first status register. */
+	uint32_t bitmask = flash->chip->wp->bp_bitmask(flash);
+	uint8_t status_sr1 = flash->chip->status_register->read(flash, SR1);
+	int result;
+	if (!(status_sr1 & bitmask)) {
+		msg_cdbg("No block protection is in effect\n");
+		return 0;
+	}
+
+	msg_cdbg("Some block protection is in effect, trying to disable...\n");
+	switch (flash->chip->status_register->get_wp_mode(flash)) {
+	case WP_MODE_HARDWARE_PROTECTED:
+	case WP_MODE_POWER_CYCLE:
+	case WP_MODE_PERMANENT:
+		msg_cdbg("Write protection mode for status register(s) disallows writing to it, disabling failed\n");
+		msg_cerr("%s failed\n", __func__);
+		return 1;
+	case WP_MODE_INVALID:
+		msg_cdbg("Invalid write protection mode for status register(s), disabling failed\n");
+		msg_cerr("%s failed\n", __func__);
+		return 1;
+	default:
+		break;
+	}
+	/* It is observed that usually unsetting BP[0..2] corresponds to NO protection. We could use
+	 * set_range with range (start=0, len=0) but use of the aformentioned observation avoids
+	 * any overheads. */
+	status_sr1 = (status_sr1 & ~bitmask) & 0xff;
+	result = flash->chip->status_register->write(flash, SR1, (uint8_t)status_sr1);
+	if (result) {
+		msg_cerr("%s failed\n", __func__);
+		return result;
+	}
+	status_sr1 = flash->chip->status_register->read(flash, SR1);
+	if (status_sr1 & bitmask) {
+		msg_cdbg("Could not write configuration to status register to disable block protection\n");
+		msg_cerr("%s failed\n", __func__);
+		return 1;
+	}
+	msg_cdbg("Disabled any block protection in effect\n");
+	return result;
+}
diff --git a/writeprotect.h b/writeprotect.h
new file mode 100644
index 0000000..41d9cf0
--- /dev/null
+++ b/writeprotect.h
@@ -0,0 +1,33 @@
+/*
+ * 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 __WRITEPROTECT_H__
+#define __WRITEPROTECT_H__ 1
+
+#include "flash.h"
+
+#define LEN_RANGES 64
+
+struct range {
+	uint32_t start;
+	uint32_t len;	/* in kB */
+};
+
+#endif		/* !__WRITEPROTECT_H__ */
