- 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 <[email protected]>
---
 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 --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 <[email protected]>
  * Copyright (C) 2012 Stefan Tauner
+ * Copyright (C) 2016 Hatim Kanchwala <[email protected]>
  *
  * 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 <[email protected]>
+ *
+ * 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__ */
-- 
2.7.4


_______________________________________________
flashrom mailing list
[email protected]
https://www.flashrom.org/mailman/listinfo/flashrom

Reply via email to