@@ -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);
@@ -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. */
@@ -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
@@ -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);
new file mode 100644
@@ -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__ */
- 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