flash.h,flashchips.c: add writeprotect bit layout map to chips

This patch adds a register bit map `struct reg_bit_info`, with fields
for storing the register, bit index, and writability of each bit that
affects the chip's write protection. This allows writeprotect code to be
independent of the register layout of any specific chip. The new fields
have been filled out for example chips.

The representation is centered around describing how bits can be
accessed and modified, rather than the layout of registers. This is
generally easier to work with in code that needs to access specific bits
and typically requires specifying the locations of fewer bits overall.

Tested: flashrom --wp-{enable,disable,range,list,status} at end of patch series

Change-Id: Id08d77e6d4ca5109c0d698271146d026dbc21284
Signed-off-by: Nikolai Artemiev <nartemiev@google.com>
Original-Reviewed-on: https://review.coreboot.org/c/flashrom/+/58477
Original-Reviewed-by: Nico Huber <nico.h@gmx.de>
Original-Reviewed-by: Anastasia Klimchuk <aklm@chromium.org>
Reviewed-on: https://review.coreboot.org/c/flashrom-stable/+/70966
Reviewed-by: Nico Huber <nico.h@gmx.de>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/flash.h b/flash.h
index 4eae62b..9129335 100644
--- a/flash.h
+++ b/flash.h
@@ -36,6 +36,7 @@
 
 #include "libflashrom.h"
 #include "layout.h"
+#include "writeprotect.h"
 
 #define KiB (1024)
 #define MiB (1024 * KiB)
@@ -179,6 +180,24 @@
 	MAX_REGISTERS
 };
 
+struct reg_bit_info {
+	/* Register containing the bit */
+	enum flash_reg reg;
+
+	/* Bit index within register */
+	uint8_t bit_index;
+
+	/*
+	 * Writability of the bit. RW does not guarantee the bit will be
+	 * writable, for example if status register protection is enabled.
+	 */
+	enum {
+		RO, /* Read only */
+		RW, /* Readable and writable */
+		OTP /* One-time programmable */
+	} writability;
+};
+
 struct flashchip {
 	const char *vendor;
 	const char *name;
@@ -255,6 +274,38 @@
 
 	/* SPI specific options (TODO: Make it a union in case other bustypes get specific options.) */
 	uint8_t wrea_override; /**< override opcode for write extended address register */
+
+	struct reg_bit_map {
+		/* Status register protection bit (SRP) */
+		struct reg_bit_info srp;
+
+		/* Status register lock bit (SRP) */
+		struct reg_bit_info srl;
+
+		/*
+		 * Note: some datasheets refer to configuration bits that
+		 * function like TB/SEC/CMP bits as BP bits (e.g. BP3 for a bit
+		 * that functions like TB).
+		 *
+		 * As a convention, any config bit that functions like a
+		 * TB/SEC/CMP bit should be assigned to the respective
+		 * tb/sec/cmp field in this structure, even if the datasheet
+		 * uses a different name.
+		 */
+
+		/* Block protection bits (BP) */
+		/* Extra element for terminator */
+		struct reg_bit_info bp[MAX_BP_BITS + 1];
+
+		/* Top/bottom protection bit (TB) */
+		struct reg_bit_info tb;
+
+		/* Sector/block protection bit (SEC) */
+		struct reg_bit_info sec;
+
+		/* Complement bit (CMP) */
+		struct reg_bit_info cmp;
+	} reg_bits;
 };
 
 typedef int (*chip_restore_fn_cb_t)(struct flashctx *flash, uint8_t status);
diff --git a/flashchips.c b/flashchips.c
index 7ed2457..ab25079 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -6307,6 +6307,15 @@
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
 		.voltage	= {1695, 1950},
+		.reg_bits	=
+		{
+			.srp    = {STATUS1, 7, RW},
+			.srl    = {STATUS2, 0, RW},
+			.bp     = {{STATUS1, 2, RW}, {STATUS1, 3, RW}, {STATUS1, 4, RW}},
+			.tb     = {STATUS1, 5, RW}, /* Called BP3 in datasheet, acts like TB */
+			.sec    = {STATUS1, 6, RW}, /* Called BP4 in datasheet, acts like SEC */
+			.cmp    = {STATUS2, 6, RW},
+		},
 	},
 
 	{
@@ -6706,6 +6715,13 @@
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
 		.voltage	= {2700, 3600},
+		.reg_bits	=
+		{
+			.srp    = {STATUS1, 7, RW},
+			.srl    = {STATUS2, 6, RW},
+			.bp     = {{STATUS1, 2, RW}, {STATUS1, 3, RW}, {STATUS1, 4, RW}, {STATUS1, 5, RW}},
+			.tb     = {STATUS1, 6, RW},
+		},
 	},
 
 	{
@@ -6745,6 +6761,15 @@
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
 		.voltage	= {2700, 3600},
+		.reg_bits	=
+		{
+			.srp    = {STATUS1, 7, RW},
+			.srl    = {STATUS2, 0, RW},
+			.bp     = {{STATUS1, 2, RW}, {STATUS1, 3, RW}, {STATUS1, 4, RW}},
+			.tb     = {STATUS1, 5, RW}, /* Called BP3 in datasheet, acts like TB */
+			.sec    = {STATUS1, 6, RW}, /* Called BP4 in datasheet, acts like SEC */
+			.cmp    = {STATUS2, 6, RW},
+		},
 	},
 
 	{
diff --git a/writeprotect.h b/writeprotect.h
new file mode 100644
index 0000000..8510226
--- /dev/null
+++ b/writeprotect.h
@@ -0,0 +1,23 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __WRITEPROTECT_H__
+#define __WRITEPROTECT_H__ 1
+
+#define MAX_BP_BITS 4
+
+#endif /* !__WRITEPROTECT_H__ */