writeprotect.c: skip unnecessary writes

* Don't write register because of RO and OTP bits.
* Skip the write of RW bits if register state wouldn't change by it.

Change-Id: I81d2d3fc0a103ee00ced78838d77fe33a9d3056a
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Original-Reviewed-on: https://review.coreboot.org/c/flashrom/+/66754
Original-Reviewed-by: Edward O'Callaghan <quasisec@chromium.org>
Original-Reviewed-by: Nikolai Artemiev <nartemiev@google.com>
Reviewed-on: https://review.coreboot.org/c/flashrom-stable/+/71004
Reviewed-by: Nico Huber <nico.h@gmx.de>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/writeprotect.c b/writeprotect.c
index 11219e2..48c5ae6 100644
--- a/writeprotect.c
+++ b/writeprotect.c
@@ -90,12 +90,16 @@
 
 /** Helper function for write_wp_bits(). */
 static void set_reg_bit(
-		uint8_t *reg_values, uint8_t *write_masks,
+		uint8_t *reg_values, uint8_t *bit_masks, uint8_t *write_masks,
 		struct reg_bit_info bit, uint8_t value)
 {
 	if (bit.reg != INVALID_REG) {
 		reg_values[bit.reg] |= value << bit.bit_index;
-		write_masks[bit.reg] |= 1 << bit.bit_index;
+		bit_masks[bit.reg] |= 1 << bit.bit_index;
+
+		/* Avoid RO and OTP bits causing a register update */
+		if (bit.writability == RW)
+			write_masks[bit.reg] |= 1 << bit.bit_index;
 	}
 }
 
@@ -106,22 +110,23 @@
 	enum flash_reg reg;
 	const struct reg_bit_map *reg_bits = &flash->chip->reg_bits;
 
-	/* Convert wp_bits to register values and write masks */
+	/* Convert wp_bits to register values and masks */
 	uint8_t reg_values[MAX_REGISTERS] = {0};
-	uint8_t write_masks[MAX_REGISTERS] = {0};
+	uint8_t bit_masks[MAX_REGISTERS] = {0};		/* masks of valid bits */
+	uint8_t write_masks[MAX_REGISTERS] = {0};	/* masks of written bits */
 
 	for (i = 0; i < bits.bp_bit_count; i++)
-		set_reg_bit(reg_values, write_masks, reg_bits->bp[i], bits.bp[i]);
+		set_reg_bit(reg_values, bit_masks, write_masks, reg_bits->bp[i], bits.bp[i]);
 
-	set_reg_bit(reg_values, write_masks, reg_bits->tb,  bits.tb);
-	set_reg_bit(reg_values, write_masks, reg_bits->sec, bits.sec);
-	set_reg_bit(reg_values, write_masks, reg_bits->cmp, bits.cmp);
-	set_reg_bit(reg_values, write_masks, reg_bits->srp, bits.srp);
-	set_reg_bit(reg_values, write_masks, reg_bits->srl, bits.srl);
+	set_reg_bit(reg_values, bit_masks, write_masks, reg_bits->tb,  bits.tb);
+	set_reg_bit(reg_values, bit_masks, write_masks, reg_bits->sec, bits.sec);
+	set_reg_bit(reg_values, bit_masks, write_masks, reg_bits->cmp, bits.cmp);
+	set_reg_bit(reg_values, bit_masks, write_masks, reg_bits->srp, bits.srp);
+	set_reg_bit(reg_values, bit_masks, write_masks, reg_bits->srl, bits.srl);
 	/* Note: always setting WPS bit to zero until its fully supported. */
-	set_reg_bit(reg_values, write_masks, reg_bits->wps, 0);
+	set_reg_bit(reg_values, bit_masks, write_masks, reg_bits->wps, 0);
 
-	/* Write each register */
+	/* Write each register whose value was updated */
 	for (reg = STATUS1; reg < MAX_REGISTERS; reg++) {
 		if (!write_masks[reg])
 			continue;
@@ -130,16 +135,22 @@
 		if (spi_read_register(flash, reg, &value))
 			return FLASHROM_WP_ERR_READ_FAILED;
 
-		value = (value & ~write_masks[reg]) | (reg_values[reg] & write_masks[reg]);
+		/* Skip unnecessary register writes */
+		uint8_t actual = value & write_masks[reg];
+		uint8_t expected = reg_values[reg] & write_masks[reg];
+		if (actual == expected)
+			continue;
+
+		value = (value & ~write_masks[reg]) | expected;
 
 		if (spi_write_register(flash, reg, value))
 			return FLASHROM_WP_ERR_WRITE_FAILED;
 	}
 
 	enum flashrom_wp_result ret = FLASHROM_WP_OK;
-	/* Verify each register */
+	/* Verify each register even if write to it was skipped */
 	for (reg = STATUS1; reg < MAX_REGISTERS; reg++) {
-		if (!write_masks[reg])
+		if (!bit_masks[reg])
 			continue;
 
 		uint8_t value;
@@ -147,8 +158,8 @@
 			return FLASHROM_WP_ERR_READ_FAILED;
 
 		msg_cdbg2("%s: wp_verify reg:%u value:0x%x\n", __func__, reg, value);
-		uint8_t actual = value & write_masks[reg];
-		uint8_t expected = reg_values[reg] & write_masks[reg];
+		uint8_t actual = value & bit_masks[reg];
+		uint8_t expected = reg_values[reg] & bit_masks[reg];
 
 		if (actual != expected) {
 			msg_cdbg("%s: wp_verify failed: reg:%u actual:0x%x expected:0x%x\n",