flashrom-gerrit
Threads by month
- ----- 2026 -----
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
July 2020
- 1 participants
- 95 discussions
Change in ...flashrom[master]: manibuilder: Enable CONFIG_EVERYTHING=yes
by Nico Huber (Code Review) March 16, 2021
by Nico Huber (Code Review) March 16, 2021
March 16, 2021
Nico Huber has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/33337
Change subject: manibuilder: Enable CONFIG_EVERYTHING=yes
......................................................................
manibuilder: Enable CONFIG_EVERYTHING=yes
Change-Id: I4651b55744d730956aa8fda8fdfccbbd68cdda19
Signed-off-by: Nico Huber <nico.h(a)gmx.de>
---
M util/manibuilder/Makefile
1 file changed, 3 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/37/33337/1
diff --git a/util/manibuilder/Makefile b/util/manibuilder/Makefile
index b1e161c..9b87f45 100644
--- a/util/manibuilder/Makefile
+++ b/util/manibuilder/Makefile
@@ -4,6 +4,7 @@
CC := ccache cc
MAKECMD := make
+MAKEARGS := CONFIG_EVERYTHING=yes
spc :=
spc +=
@@ -50,7 +51,7 @@
djgpp\:6.1.0: CC=ccache i586-pc-msdosdjgpp-gcc
djgpp\:6.1.0: STRIP=i586-pc-msdosdjgpp-strip
djgpp\:6.1.0: LIBS_BASE=../
-djgpp\:6.1.0: TARGET=strip
+djgpp\:6.1.0: MAKEARGS+=strip
$(ANITA_TAGS): MAKECMD=gmake
$(ALL_TAGS): export QUIET_SETUP=$(QUIET_TEST)
$(ALL_TAGS): %: %-check-build
@@ -65,7 +66,7 @@
$(MAKECMD) clean && $(MAKECMD) -j$${CPUS:-1} CC='$(CC)' \
$(if $(STRIP),STRIP='$(STRIP)') \
$(if $(LIBS_BASE),LIBS_BASE='$(LIBS_BASE)') \
- $(TARGET)" \
+ $(MAKEARGS)" \
$(if $(QUIET_TEST),>/dev/null 2>&1) || echo $*: $$?
$(addsuffix -shell,$(ALL_TAGS)): %-shell: %-check-build
--
To view, visit https://review.coreboot.org/c/flashrom/+/33337
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I4651b55744d730956aa8fda8fdfccbbd68cdda19
Gerrit-Change-Number: 33337
Gerrit-PatchSet: 1
Gerrit-Owner: Nico Huber <nico.h(a)gmx.de>
Gerrit-MessageType: newchange
3
4
Change in flashrom[master]: libflashrom: add querying functions with meson integration
by Michał Żygowski (Code Review) March 13, 2021
by Michał Żygowski (Code Review) March 13, 2021
March 13, 2021
Hello ?ukasz Dmitrowski,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/flashrom/+/34363
to review the following change.
Change subject: libflashrom: add querying functions with meson integration
......................................................................
libflashrom: add querying functions with meson integration
Work based on lukasz.dmitrowski(a)gmail.com code
Change-Id: I49041b8fa5700dabe59fef0d2337339d34cd6c6f
Signed-off-by: Artur Raglis <artur.raglis(a)3mdeb.com>
Signed-off-by: Lukasz Dmitrowski <lukasz.dmitrowski(a)gmail.com>
---
M libflashrom.c
M libflashrom.h
M libflashrom.map
M meson.build
4 files changed, 187 insertions(+), 3 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/63/34363/1
diff --git a/libflashrom.c b/libflashrom.c
index f90a22c..cc26835 100644
--- a/libflashrom.c
+++ b/libflashrom.c
@@ -103,7 +103,142 @@
* @{
*/
-/* TBD */
+/**
+ * @brief Returns flashrom version
+ * @return Flashrom version
+ */
+const char *flashrom_version_info(void)
+{
+ return flashrom_version;
+}
+
+/**
+ * @brief Returns list of supported programmers
+ * @return List of supported programmers
+ */
+const char **flashrom_supported_programmers(void)
+{
+ enum programmer p = 0;
+ const char **supported_programmers = NULL;
+ supported_programmers = malloc((PROGRAMMER_INVALID + 1) * sizeof(char*));
+
+ if (supported_programmers != NULL) {
+ for (; p < PROGRAMMER_INVALID; ++p) {
+ supported_programmers[p] = programmer_table[p].name;
+ }
+ } else {
+ msg_gerr("Memory allocation error!\n");
+ }
+
+ return supported_programmers;
+}
+
+/**
+ * @brief Returns list of supported flash chips
+ * @return List of supported flash chips
+ */
+flashrom_flashchip_info *flashrom_supported_flash_chips(void)
+{
+ int i = 0;
+ flashrom_flashchip_info *supported_flashchips = malloc(flashchips_size * sizeof(flashrom_flashchip_info));
+
+ if (supported_flashchips != NULL) {
+ for (; i < flashchips_size; ++i) {
+ supported_flashchips[i].vendor = flashchips[i].vendor;
+ supported_flashchips[i].name = flashchips[i].name;
+ supported_flashchips[i].tested.erase = (flashrom_test_state) flashchips[i].tested.erase;
+ supported_flashchips[i].tested.probe = (flashrom_test_state) flashchips[i].tested.probe;
+ supported_flashchips[i].tested.read = (flashrom_test_state) flashchips[i].tested.read;
+ supported_flashchips[i].tested.write = (flashrom_test_state) flashchips[i].tested.write;
+ supported_flashchips[i].total_size = flashchips[i].total_size;
+ }
+ } else {
+ msg_gerr("Memory allocation error!\n");
+ }
+
+ return supported_flashchips;
+}
+
+/**
+ * @brief Returns list of supported mainboards
+ * @return List of supported mainboards
+ */
+flashrom_board_info *flashrom_supported_boards(void)
+{
+ int boards_known_size = 0;
+ int i = 0;
+ const struct board_info *binfo = boards_known;
+
+ while ((binfo++)->vendor)
+ ++boards_known_size;
+ binfo = boards_known;
+ /* add place for {0} */
+ ++boards_known_size;
+
+ flashrom_board_info *supported_boards = malloc(boards_known_size * sizeof(flashrom_board_info));
+
+ if (supported_boards != NULL) {
+ for (; i < boards_known_size; ++i) {
+ supported_boards[i].vendor = binfo[i].vendor;
+ supported_boards[i].name = binfo[i].name;
+ supported_boards[i].working = binfo[i].working;
+ }
+ } else {
+ msg_gerr("Memory allocation error!\n");
+ }
+
+ return supported_boards;
+}
+
+/**
+ * @brief Returns list of supported chipsets
+ * @return List of supported chipsets
+ */
+flashrom_chipset_info *flashrom_supported_chipsets(void)
+{
+ int chipset_enables_size = 0;
+ int i = 0;
+ const struct penable *chipset = chipset_enables;
+
+ while ((chipset++)->vendor_name)
+ ++chipset_enables_size;
+ chipset = chipset_enables;
+ /* add place for {0}*/
+ ++chipset_enables_size;
+
+ flashrom_chipset_info *supported_chipsets = malloc(chipset_enables_size * sizeof(flashrom_chipset_info));
+
+ if (supported_chipsets != NULL) {
+ for (; i < chipset_enables_size; ++i) {
+ supported_chipsets[i].vendor = chipset[i].vendor_name;
+ supported_chipsets[i].chipset = chipset[i].device_name;
+ supported_chipsets[i].vendor_id = chipset[i].vendor_id;
+ supported_chipsets[i].chipset_id = chipset[i].device_id;
+ supported_chipsets[i].status = chipset[i].status;
+ }
+ } else {
+ msg_gerr("Memory allocation error!\n");
+ }
+
+ return supported_chipsets;
+}
+
+/**
+ * @brief Frees memory allocated by libflashrom API
+ * @param Pointer to block of memory which should be freed
+ * @return 0 on success
+ * 1 on null pointer error
+ */
+int flashrom_data_free(void *const p)
+{
+ if (!p) {
+ msg_gerr("flashrom_data_free - Null pointer!\n");
+ return 1;
+ } else {
+ free(p);
+ return 0;
+ }
+}
/** @} */ /* end flashrom-query */
diff --git a/libflashrom.h b/libflashrom.h
index 38c95d2..ebba16b 100644
--- a/libflashrom.h
+++ b/libflashrom.h
@@ -37,6 +37,50 @@
typedef int(flashrom_log_callback)(enum flashrom_log_level, const char *format, va_list);
void flashrom_set_log_callback(flashrom_log_callback *);
+/** @ingroup flashrom-query */
+typedef enum {
+ FLASHROM_TESTED_OK = 0,
+ FLASHROM_TESTED_NT = 1,
+ FLASHROM_TESTED_BAD = 2,
+ FLASHROM_TESTED_DEP = 3,
+ FLASHROM_TESTED_NA = 4,
+} flashrom_test_state;
+
+typedef struct flashrom_flashchip_info {
+ const char *vendor;
+ const char *name;
+ unsigned int total_size;
+ struct flashrom_tested {
+ flashrom_test_state probe;
+ flashrom_test_state read;
+ flashrom_test_state erase;
+ flashrom_test_state write;
+ } tested;
+} flashrom_flashchip_info;
+
+typedef struct flashrom_board_info {
+ const char *vendor;
+ const char *name;
+ flashrom_test_state working;
+} flashrom_board_info;
+
+typedef struct flashrom_chipset_info {
+ const char *vendor;
+ const char *chipset;
+ uint16_t vendor_id;
+ uint16_t chipset_id;
+ flashrom_test_state status;
+} flashrom_chipset_info;
+
+const char *flashrom_version_info(void);
+void flashrom_system_info(void);
+const char **flashrom_supported_programmers(void);
+flashrom_flashchip_info *flashrom_supported_flash_chips(void);
+flashrom_board_info *flashrom_supported_boards(void);
+flashrom_chipset_info *flashrom_supported_chipsets(void);
+int flashrom_data_free(void *const p);
+
+/** @ingroup flashrom-prog */
struct flashrom_programmer;
int flashrom_programmer_init(struct flashrom_programmer **, const char *prog_name, const char *prog_params);
int flashrom_programmer_shutdown(struct flashrom_programmer *);
diff --git a/libflashrom.map b/libflashrom.map
index 3c287ff..d6dd24d 100644
--- a/libflashrom.map
+++ b/libflashrom.map
@@ -1,7 +1,11 @@
LIBFLASHROM_1.0 {
global:
+ flashrom_board_info;
+ flashrom_chipset_info;
+ flashrom_data_free;
flashrom_flag_get;
flashrom_flag_set;
+ flashrom_flashchip_info;
flashrom_flash_erase;
flashrom_flash_getsize;
flashrom_flash_probe;
@@ -20,5 +24,8 @@
flashrom_programmer_shutdown;
flashrom_set_log_callback;
flashrom_shutdown;
+ flashrom_supported_programmers;
+ flashrom_system_info;
+ flashrom_version_info;
local: *;
};
diff --git a/meson.build b/meson.build
index 1923fdf..00f4a2f 100644
--- a/meson.build
+++ b/meson.build
@@ -68,8 +68,6 @@
srcs = []
need_libusb0 = false
-need_raw_access = false
-need_serial = false
# check for required symbols
if cc.has_function('clock_gettime')
--
To view, visit https://review.coreboot.org/c/flashrom/+/34363
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I49041b8fa5700dabe59fef0d2337339d34cd6c6f
Gerrit-Change-Number: 34363
Gerrit-PatchSet: 1
Gerrit-Owner: Michał Żygowski <michal.zygowski(a)3mdeb.com>
Gerrit-Reviewer: ?ukasz Dmitrowski <lukasz.dmitrowski(a)gmail.com>
Gerrit-MessageType: newchange
5
23
March 7, 2021
Martijn Berger has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/39966 )
Change subject: pcidev: Fix typo
......................................................................
pcidev: Fix typo
Fix typo
Change-Id: If70834715a51dea4111ba56e85df0680ed25c7a4
Signed-off-by: Martijn Berger <martijn.berger(a)gmail.com>
---
M pcidev.c
M programmer.h
2 files changed, 3 insertions(+), 3 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/66/39966/1
diff --git a/pcidev.c b/pcidev.c
index 973acde..38b9380 100644
--- a/pcidev.c
+++ b/pcidev.c
@@ -30,7 +30,7 @@
TYPE_UNKNOWN
};
-int pci_bar_nuber_from_offset(int offset)
+int pci_bar_number_from_offset(int offset)
{
switch (offset) {
case PCI_BASE_ADDRESS_0:
@@ -63,7 +63,7 @@
headertype = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7f;
msg_pspew("PCI header type 0x%02x\n", headertype);
- bar_number = pci_bar_nuber_from_offset(bar);
+ bar_number = pci_bar_number_from_offset(bar);
if (PCI_LIB_VERSION >= 0x030500 && bar_number > -1) {
addr = dev->base_addr[bar_number];
diff --git a/programmer.h b/programmer.h
index 5d4c1d7..42f646b 100644
--- a/programmer.h
+++ b/programmer.h
@@ -190,7 +190,7 @@
// FIXME: This needs to be local, not global(?)
extern struct pci_access *pacc;
int pci_init_common(void);
-int pci_bar_nuber_from_offset(int offset);
+int pci_bar_number_from_offset(int offset);
uintptr_t pcidev_readbar(struct pci_dev *dev, int bar);
struct pci_dev *pcidev_init(const struct dev_entry *devs, int bar);
/* rpci_write_* are reversible writes. The original PCI config space register
--
To view, visit https://review.coreboot.org/c/flashrom/+/39966
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: If70834715a51dea4111ba56e85df0680ed25c7a4
Gerrit-Change-Number: 39966
Gerrit-PatchSet: 1
Gerrit-Owner: Martijn Berger
Gerrit-MessageType: newchange
4
8
Change in flashrom[master]: Add writeprotect support
by Edward O'Callaghan (Code Review) Feb. 28, 2021
by Edward O'Callaghan (Code Review) Feb. 28, 2021
Feb. 28, 2021
Edward O'Callaghan has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/40325 )
Change subject: Add writeprotect support
......................................................................
Add writeprotect support
[NOT-FOR-MERGE-(yet)]: PoC WIP.
BUG=b:153800563
BRANCH=none
TEST=builds
Change-Id: Id93b5a1cb2da476fa8a7dde41d7b963024117474
Signed-off-by: Edward O'Callaghan <quasisec(a)google.com>
---
M Makefile
M cli_classic.c
M flash.h
M meson.build
A writeprotect.c
A writeprotect.h
6 files changed, 2,722 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/25/40325/1
diff --git a/Makefile b/Makefile
index a533acf..a1bf898 100644
--- a/Makefile
+++ b/Makefile
@@ -589,7 +589,7 @@
CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \
sst28sf040.o 82802ab.o \
sst49lfxxxc.o sst_fwhub.o edi.o flashchips.o spi.o spi25.o spi25_statusreg.o \
- spi95.o opaque.o sfdp.o en29lv640b.o at45db.o
+ spi95.o opaque.o sfdp.o en29lv640b.o at45db.o writeprotect.o
###############################################################################
# Library code.
diff --git a/cli_classic.c b/cli_classic.c
index 73cc417..9e2d8e8 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -17,6 +17,7 @@
* GNU General Public License for more details.
*/
+#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -27,6 +28,7 @@
#include "flashchips.h"
#include "fmap.h"
#include "programmer.h"
+#include "writeprotect.h"
#include "libflashrom.h"
static void cli_classic_usage(const char *name)
@@ -116,6 +118,8 @@
int list_supported_wiki = 0;
#endif
int flash_name = 0, flash_size = 0;
+ int set_wp_range = 0, set_wp_region = 0, set_wp_enable = 0,
+ set_wp_disable = 0, wp_status = 0, wp_list = 0;
int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
int dont_verify_it = 0, dont_verify_all = 0, list_supported = 0, operation_specified = 0;
struct flashrom_layout *layout = NULL;
@@ -127,6 +131,12 @@
OPTION_FLASH_CONTENTS,
OPTION_FLASH_NAME,
OPTION_FLASH_SIZE,
+ OPTION_WP_STATUS,
+ OPTION_WP_SET_RANGE,
+ OPTION_WP_SET_REGION,
+ OPTION_WP_ENABLE,
+ OPTION_WP_DISABLE,
+ OPTION_WP_LIST,
};
int ret = 0;
@@ -150,6 +160,12 @@
{"flash-name", 0, NULL, OPTION_FLASH_NAME},
{"flash-size", 0, NULL, OPTION_FLASH_SIZE},
{"get-size", 0, NULL, OPTION_FLASH_SIZE}, // (deprecated): back compatibility.
+ {"wp-status", 0, 0, OPTION_WP_STATUS},
+ {"wp-range", 0, 0, OPTION_WP_SET_RANGE},
+ {"wp-region", 1, 0, OPTION_WP_SET_REGION},
+ {"wp-enable", optional_argument, 0, OPTION_WP_ENABLE},
+ {"wp-disable", 0, 0, OPTION_WP_DISABLE},
+ {"wp-list", 0, 0, OPTION_WP_LIST},
{"list-supported", 0, NULL, 'L'},
{"list-supported-wiki", 0, NULL, 'z'},
{"programmer", 1, NULL, 'p'},
@@ -169,6 +185,7 @@
char *tempstr = NULL;
char *pparam = NULL;
struct layout_include_args *include_args = NULL;
+ char *wp_mode_opt = NULL;
flashrom_set_log_callback((flashrom_log_callback *)&flashrom_print_cb);
@@ -283,6 +300,23 @@
cli_classic_validate_singleop(&operation_specified);
flash_size = 1;
break;
+ case OPTION_WP_STATUS:
+ wp_status = 1;
+ break;
+ case OPTION_WP_LIST:
+ wp_list = 1;
+ break;
+ case OPTION_WP_SET_RANGE:
+ set_wp_range = 1;
+ break;
+ case OPTION_WP_ENABLE:
+ set_wp_enable = 1;
+ if (optarg)
+ wp_mode_opt = strdup(optarg);
+ break;
+ case OPTION_WP_DISABLE:
+ set_wp_disable = 1;
+ break;
case 'L':
cli_classic_validate_singleop(&operation_specified);
list_supported = 1;
@@ -562,11 +596,19 @@
goto out_shutdown;
}
- if (!(read_it | write_it | verify_it | erase_it | flash_name | flash_size)) {
+ if (!(read_it | write_it | verify_it | erase_it | flash_name | flash_size
+ | set_wp_range | set_wp_region | set_wp_enable |
+ set_wp_disable | wp_status | wp_list )) {
msg_ginfo("No operations were specified.\n");
goto out_shutdown;
}
+ if (set_wp_enable && set_wp_disable) {
+ msg_ginfo("Error: --wp-enable and --wp-disable are mutually exclusive\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+
if (flash_name) {
if (fill_flash->chip->vendor && fill_flash->chip->name) {
printf("vendor=\"%s\" name=\"%s\"\n",
@@ -583,6 +625,110 @@
goto out_shutdown;
}
+ if (wp_status) {
+ if (fill_flash->chip->wp && fill_flash->chip->wp->wp_status) {
+ ret |= fill_flash->chip->wp->wp_status(fill_flash);
+ } else {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ }
+ goto out_shutdown;
+ }
+
+ /* Note: set_wp_disable should be done before setting the range */
+ if (set_wp_disable) {
+ if (fill_flash->chip->wp && fill_flash->chip->wp->disable) {
+ ret |= fill_flash->chip->wp->disable(fill_flash);
+ } else {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+ }
+
+ if (!ret && set_wp_enable) {
+ enum wp_mode wp_mode;
+
+ if (wp_mode_opt)
+ wp_mode = get_wp_mode(wp_mode_opt);
+ else
+ wp_mode = WP_MODE_HARDWARE; /* default */
+
+ if (wp_mode == WP_MODE_UNKNOWN) {
+ msg_gerr("Error: Invalid WP mode: \"%s\"\n", wp_mode_opt);
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ if (fill_flash->chip->wp && fill_flash->chip->wp->enable) {
+ ret |= fill_flash->chip->wp->enable(fill_flash, wp_mode);
+ } else {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+ }
+
+ if (wp_list) {
+ msg_ginfo("Valid write protection ranges:\n");
+ if (fill_flash->chip->wp && fill_flash->chip->wp->list_ranges) {
+ ret |= fill_flash->chip->wp->list_ranges(fill_flash);
+ } else {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ }
+ goto out_shutdown;
+ }
+
+ if (set_wp_range || set_wp_region) {
+ if (set_wp_range && set_wp_region) {
+ msg_gerr("Error: Cannot use both --wp-range and "
+ "--wp-region simultaneously.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ if (!fill_flash->chip->wp || !fill_flash->chip->wp->set_range) {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+ }
+
+ /* Note: set_wp_range must happen before set_wp_enable */
+ if (set_wp_range) {
+ unsigned int start, len;
+ char *endptr = NULL;
+
+ if ((argc - optind) != 2) {
+ msg_gerr("Error: invalid number of arguments\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ /* FIXME: add some error checking */
+ start = strtoul(argv[optind], &endptr, 0);
+ if (errno == ERANGE || errno == EINVAL || *endptr != '\0') {
+ msg_gerr("Error: value \"%s\" invalid\n", argv[optind]);
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ len = strtoul(argv[optind + 1], &endptr, 0);
+ if (errno == ERANGE || errno == EINVAL || *endptr != '\0') {
+ msg_gerr("Error: value \"%s\" invalid\n", argv[optind + 1]);
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ ret |= fill_flash->chip->wp->set_range(fill_flash, start, len);
+ }
+
if (layoutfile) {
layout = get_global_layout();
} else if (ifd && (flashrom_layout_read_from_ifd(&layout, fill_flash, NULL, 0) ||
diff --git a/flash.h b/flash.h
index 2f0143b..fefca9d 100644
--- a/flash.h
+++ b/flash.h
@@ -235,6 +235,8 @@
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);
+ uint8_t (*read_status) (const struct flashctx *flash);
+ int (*write_status) (const struct flashctx *flash, int status);
struct voltage {
uint16_t min;
uint16_t max;
@@ -243,6 +245,8 @@
/* 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 wp *wp;
};
struct flashrom_flashctx {
diff --git a/meson.build b/meson.build
index 699370a..38bf671 100644
--- a/meson.build
+++ b/meson.build
@@ -350,6 +350,7 @@
srcs += 'udelay.c'
srcs += 'w29ee011.c'
srcs += 'w39.c'
+srcs += 'writeprotect.c'
mapfile = 'libflashrom.map'
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
diff --git a/writeprotect.c b/writeprotect.c
new file mode 100644
index 0000000..f54ccad
--- /dev/null
+++ b/writeprotect.c
@@ -0,0 +1,2512 @@
+/*
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#include "flash.h"
+#include "flashchips.h"
+#include "chipdrivers.h"
+#include "spi.h"
+#include "writeprotect.h"
+
+/*
+ * The following procedures rely on look-up tables to match the user-specified
+ * range with the chip's supported ranges. This turned out to be the most
+ * elegant approach since diferent flash chips use different levels of
+ * granularity and methods to determine protected ranges. In other words,
+ * be stupid and simple since clever arithmetic will not work for many chips.
+ */
+
+struct wp_range {
+ unsigned int start; /* starting address */
+ unsigned int len; /* len */
+};
+
+enum bit_state {
+ OFF = 0,
+ ON = 1,
+ X = -1 /* don't care. Must be bigger than max # of bp. */
+};
+
+/*
+ * Generic write-protection schema for 25-series SPI flash chips. This assumes
+ * there is a status register that contains one or more consecutive bits which
+ * determine which address range is protected.
+ */
+
+struct status_register_layout {
+ int bp0_pos; /* position of BP0 */
+ int bp_bits; /* number of block protect bits */
+ int srp_pos; /* position of status register protect enable bit */
+};
+
+/*
+ * The following ranges and functions are useful for representing the
+ * writeprotect schema in which there are typically 5 bits of
+ * relevant information stored in status register 1:
+ * m.sec: This bit indicates the units (sectors vs. blocks)
+ * m.tb: The top-bottom bit indicates if the affected range is at the top of
+ * the flash memory's address space or at the bottom.
+ * bp: Bitmask representing the number of affected sectors/blocks.
+ */
+struct wp_range_descriptor {
+ struct modifier_bits m;
+ unsigned int bp; /* block protect bitfield */
+ struct wp_range range;
+};
+
+struct wp_context {
+ struct status_register_layout sr1; /* status register 1 */
+ struct wp_range_descriptor *descrs;
+
+ /*
+ * Some chips store modifier bits in one or more special control
+ * registers instead of the status register like many older SPI NOR
+ * flash chips did. get_modifier_bits() and set_modifier_bits() will do
+ * any chip-specific operations necessary to get/set these bit values.
+ */
+ int (*get_modifier_bits)(const struct flashctx *flash,
+ struct modifier_bits *m);
+ int (*set_modifier_bits)(const struct flashctx *flash,
+ struct modifier_bits *m);
+};
+
+struct w25q_status {
+ /* this maps to register layout -- do not change ordering */
+ unsigned char busy : 1;
+ unsigned char wel : 1;
+ unsigned char bp0 : 1;
+ unsigned char bp1 : 1;
+ unsigned char bp2 : 1;
+ unsigned char tb : 1;
+ unsigned char sec : 1;
+ unsigned char srp0 : 1;
+} __attribute__ ((packed));
+
+/* Status register for large flash layouts with 4 BP bits */
+struct w25q_status_large {
+ unsigned char busy : 1;
+ unsigned char wel : 1;
+ unsigned char bp0 : 1;
+ unsigned char bp1 : 1;
+ unsigned char bp2 : 1;
+ unsigned char bp3 : 1;
+ unsigned char tb : 1;
+ unsigned char srp0 : 1;
+} __attribute__ ((packed));
+
+struct w25q_status_2 {
+ unsigned char srp1 : 1;
+ unsigned char qe : 1;
+ unsigned char rsvd : 6;
+} __attribute__ ((packed));
+
+int w25_range_to_status(const struct flashctx *flash,
+ unsigned int start, unsigned int len,
+ struct w25q_status *status);
+int w25_status_to_range(const struct flashctx *flash,
+ const struct w25q_status *status,
+ unsigned int *start, unsigned int *len);
+
+/*
+ * Mask to extract write-protect enable and range bits
+ * Status register 1:
+ * SRP0: bit 7
+ * range(BP2-BP0): bit 4-2
+ * range(BP3-BP0): bit 5-2 (large chips)
+ * Status register 2:
+ * SRP1: bit 1
+ */
+#define MASK_WP_AREA (0x9C)
+#define MASK_WP_AREA_LARGE (0x9C)
+#define MASK_WP2_AREA (0x01)
+
+struct wp_range_descriptor en25f40_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 448 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 384 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 512 * 1024} },
+};
+
+struct wp_range_descriptor en25q40_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 448 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 384 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
+};
+
+struct wp_range_descriptor en25q80_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 1016 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 1008 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 992 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 960 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 896 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 768 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 1024 * 1024} },
+};
+
+struct wp_range_descriptor en25q32_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 4032 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 3968 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 3840 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 3584 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 3072 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 4032 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 3968 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 3840 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 3584 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 3072 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
+};
+
+struct wp_range_descriptor en25q64_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 8064 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7936 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7680 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 7168 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 6144 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 8128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 8064 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 7936 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 7680 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 7168 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 6144 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
+};
+
+struct wp_range_descriptor en25q128_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16320 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 16256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 16128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 15872 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 15360 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 14336 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 16384 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 16320 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 16256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 16128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 15872 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 15360 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 14336 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 16384 * 1024} },
+};
+
+struct wp_range_descriptor en25s64_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8064 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 7936 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7680 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7168 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 6144 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 4096 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x7e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x7c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x780000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x700000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x600000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x400000, 4096 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
+};
+
+/* mx25l1005 ranges also work for the mx25l1005c */
+static struct wp_range_descriptor mx25l1005_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x010000, 64 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
+};
+
+static struct wp_range_descriptor mx25l2005_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x030000, 64 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x2, {0x020000, 128 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 256 * 1024} },
+};
+
+static struct wp_range_descriptor mx25l4005_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x070000, 64 * 1 * 1024} }, /* block 7 */
+ { .m = { .sec = X, .tb = X }, 0x2, {0x060000, 64 * 2 * 1024} }, /* blocks 6-7 */
+ { .m = { .sec = X, .tb = X }, 0x3, {0x040000, 64 * 4 * 1024} }, /* blocks 4-7 */
+ { .m = { .sec = X, .tb = X }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 512 * 1024} },
+};
+
+static struct wp_range_descriptor mx25l8005_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x0f0000, 64 * 1 * 1024} }, /* block 15 */
+ { .m = { .sec = X, .tb = X }, 0x2, {0x0e0000, 64 * 2 * 1024} }, /* blocks 14-15 */
+ { .m = { .sec = X, .tb = X }, 0x3, {0x0c0000, 64 * 4 * 1024} }, /* blocks 12-15 */
+ { .m = { .sec = X, .tb = X }, 0x4, {0x080000, 64 * 8 * 1024} }, /* blocks 8-15 */
+ { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
+};
+
+static struct wp_range_descriptor mx25l1605d_ranges[] = {
+ { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = 0 }, 0x1, {0x1f0000, 64 * 1 * 1024} }, /* block 31 */
+ { .m = { .sec = X, .tb = 0 }, 0x2, {0x1e0000, 64 * 2 * 1024} }, /* blocks 30-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x3, {0x1c0000, 64 * 4 * 1024} }, /* blocks 28-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x4, {0x180000, 64 * 8 * 1024} }, /* blocks 24-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x5, {0x100000, 64 * 16 * 1024} }, /* blocks 16-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+
+ { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 64 * 24 * 1024} }, /* blocks 0-23 */
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 64 * 28 * 1024} }, /* blocks 0-27 */
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 64 * 30 * 1024} }, /* blocks 0-29 */
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 64 * 31 * 1024} }, /* blocks 0-30 */
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+};
+
+/* FIXME: Is there an mx25l3205 (without a trailing letter)? */
+static struct wp_range_descriptor mx25l3205d_ranges[] = {
+ { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
+
+ { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
+};
+
+static struct wp_range_descriptor mx25u3235e_ranges[] = {
+ { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
+};
+
+static struct wp_range_descriptor mx25u6435e_ranges[] = {
+ { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 1 * 64 * 1024} }, /* block 127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
+
+ { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 96 * 64 * 1024} }, /* blocks 0-95 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 112 * 64 * 1024} }, /* blocks 0-111 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 120 * 64 * 1024} }, /* blocks 0-119 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 124 * 64 * 1024} }, /* blocks 0-123 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 126 * 64 * 1024} }, /* blocks 0-125 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 127 * 64 * 1024} }, /* blocks 0-126 */
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
+};
+
+#define MX25U12835E_TB (1 << 3)
+static struct wp_range_descriptor mx25u12835e_tb0_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0xff0000, 1 * 64 * 1024} }, /* block 255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0xfe0000, 2 * 64 * 1024} }, /* blocks 254-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0xfc0000, 4 * 64 * 1024} }, /* blocks 252-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0xf80000, 8 * 64 * 1024} }, /* blocks 248-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0xf00000, 16 * 64 * 1024} }, /* blocks 240-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0xe00000, 32 * 64 * 1024} }, /* blocks 224-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0xc00000, 64 * 64 * 1024} }, /* blocks 192-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x8, {0x800000, 128 * 64 * 1024} }, /* blocks 128-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+};
+
+static struct wp_range_descriptor mx25u12835e_tb1_ranges[] = {
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 1 * 64 * 1024} }, /* block 0 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { .m = { .sec = 0, .tb = 1 }, 0x8, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
+ { .m = { .sec = 0, .tb = 1 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+};
+
+static struct wp_range_descriptor n25q064_ranges[] = {
+ /*
+ * Note: For N25Q064, sec (usually in bit position 6) is called BP3
+ * (block protect bit 3). It is only useful when all blocks are to
+ * be write-protected.
+ */
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 64 * 1024} }, /* block 127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, /* block 0 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+
+ { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* all */
+};
+
+static struct wp_range_descriptor w25q16_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x1f0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x1e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x1c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x180000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x100000, 1024 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 2048 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor w25q32_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 4096 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x3ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x3fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x3fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x3f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x3f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor w25q80_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0f0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor w25q64_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor w25rq128_cmp0_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* NONE */
+
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* Upper 1/64 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* Upper 1/32 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* Upper 1/16 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* Upper 1/8 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* Upper 1/4 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* Upper 1/2 */
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* Lower 1/64 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* Lower 1/32 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* Lower 1/16 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* Lower 1/8 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* Lower 1/4 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* Lower 1/2 */
+
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 16384 * 1024} }, /* ALL */
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0xfff000, 4 * 1024} }, /* Upper 1/4096 */
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0xffe000, 8 * 1024} }, /* Upper 1/2048 */
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0xffc000, 16 * 1024} }, /* Upper 1/1024 */
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, /* Lower 1/4096 */
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, /* Lower 1/2048 */
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, /* Lower 1/1024 */
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, /* Lower 1/512 */
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, /* Lower 1/512 */
+};
+
+static struct wp_range_descriptor w25rq128_cmp1_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 16 * 1024 * 1024} }, /* ALL */
+
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16128 * 1024} }, /* Lower 63/64 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 15872 * 1024} }, /* Lower 31/32 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 15 * 1024 * 1024} }, /* Lower 15/16 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 14 * 1024 * 1024} }, /* Lower 7/8 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 12 * 1024 * 1024} }, /* Lower 3/4 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 8 * 1024 * 1024} }, /* Lower 1/2 */
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x040000, 16128 * 1024} }, /* Upper 63/64 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x080000, 15872 * 1024} }, /* Upper 31/32 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x100000, 15 * 1024 * 1024} }, /* Upper 15/16 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x200000, 14 * 1024 * 1024} }, /* Upper 7/8 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x400000, 12 * 1024 * 1024} }, /* Upper 3/4 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x800000, 8 * 1024 * 1024} }, /* Upper 1/2 */
+
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 0} }, /* NONE */
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 16380 * 1024} }, /* Lower 4095/4096 */
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 16376 * 1024} }, /* Lower 2048/2048 */
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 16368 * 1024} }, /* Lower 1023/1024 */
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 16380 * 1024} }, /* Upper 4095/4096 */
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 16376 * 1024} }, /* Upper 2047/2048 */
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 16368 * 1024} }, /* Upper 1023/1024 */
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
+};
+
+static struct wp_range_descriptor w25rq256_cmp0_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 0x0000000} }, /* NONE */
+
+ { .m = { .sec = X, .tb = 0 }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* Upper 1/512 */
+ { .m = { .sec = X, .tb = 0 }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* Upper 1/256 */
+ { .m = { .sec = X, .tb = 0 }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* Upper 1/128 */
+ { .m = { .sec = X, .tb = 0 }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* Upper 1/64 */
+ { .m = { .sec = X, .tb = 0 }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* Upper 1/32 */
+ { .m = { .sec = X, .tb = 0 }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* Upper 1/16 */
+ { .m = { .sec = X, .tb = 0 }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* Upper 1/8 */
+ { .m = { .sec = X, .tb = 0 }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* Upper 1/4 */
+ { .m = { .sec = X, .tb = 0 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
+
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x0000000, 64 * 1 * 1024} }, /* Lower 1/512 */
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x0000000, 64 * 2 * 1024} }, /* Lower 1/256 */
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x0000000, 64 * 4 * 1024} }, /* Lower 1/128 */
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x0000000, 64 * 8 * 1024} }, /* Lower 1/64 */
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x0000000, 64 * 16 * 1024} }, /* Lower 1/32 */
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x0000000, 64 * 32 * 1024} }, /* Lower 1/16 */
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x0000000, 64 * 64 * 1024} }, /* Lower 1/8 */
+ { .m = { .sec = X, .tb = 1 }, 0x8, {0x0000000, 64 * 128 * 1024} }, /* Lower 1/4 */
+ { .m = { .sec = X, .tb = 1 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
+
+ { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+};
+
+static struct wp_range_descriptor w25rq256_cmp1_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+
+ { .m = { .sec = X, .tb = 0 }, 0x1, {0x0000000, 64 * 511 * 1024} }, /* Lower 511/512 */
+ { .m = { .sec = X, .tb = 0 }, 0x2, {0x0000000, 64 * 510 * 1024} }, /* Lower 255/256 */
+ { .m = { .sec = X, .tb = 0 }, 0x3, {0x0000000, 64 * 508 * 1024} }, /* Lower 127/128 */
+ { .m = { .sec = X, .tb = 0 }, 0x4, {0x0000000, 64 * 504 * 1024} }, /* Lower 63/64 */
+ { .m = { .sec = X, .tb = 0 }, 0x5, {0x0000000, 64 * 496 * 1024} }, /* Lower 31/32 */
+ { .m = { .sec = X, .tb = 0 }, 0x6, {0x0000000, 64 * 480 * 1024} }, /* Lower 15/16 */
+ { .m = { .sec = X, .tb = 0 }, 0x7, {0x0000000, 64 * 448 * 1024} }, /* Lower 7/8 */
+ { .m = { .sec = X, .tb = 0 }, 0x8, {0x0000000, 64 * 384 * 1024} }, /* Lower 3/4 */
+ { .m = { .sec = X, .tb = 0 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
+
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x0010000, 64 * 511 * 1024} }, /* Upper 511/512 */
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x0020000, 64 * 510 * 1024} }, /* Upper 255/256 */
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x0040000, 64 * 508 * 1024} }, /* Upper 127/128 */
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x0080000, 64 * 504 * 1024} }, /* Upper 63/64 */
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x0100000, 64 * 496 * 1024} }, /* Upper 31/32 */
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x0200000, 64 * 480 * 1024} }, /* Upper 15/16 */
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x0400000, 64 * 448 * 1024} }, /* Upper 7/8 */
+ { .m = { .sec = X, .tb = 1 }, 0x8, {0x0800000, 64 * 384 * 1024} }, /* Upper 3/4 */
+ { .m = { .sec = X, .tb = 1 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
+
+ { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 0x0000000} }, /* NONE */
+};
+
+struct wp_range_descriptor w25x10_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x010000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
+};
+
+struct wp_range_descriptor w25x20_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x030000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x020000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x3, {0x000000, 256 * 1024} },
+};
+
+struct wp_range_descriptor w25x40_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} },
+};
+
+struct wp_range_descriptor w25x80_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0F0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0E0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0C0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
+};
+
+static struct wp_range_descriptor gd25q40_cmp0_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* None */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} }, /* All */
+ { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} }, /* All */
+ { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} }, /* All */
+ { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x07F000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x07E000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x07C000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x078000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x078000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x6, {0x078000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
+};
+
+static struct wp_range_descriptor gd25q40_cmp1_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 512 * 1024} }, /* ALL */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 448 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 384 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 256 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 448 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 384 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 256 * 1024} },
+
+ { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 0} }, /* None */
+ { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 0} }, /* None */
+ { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 0} }, /* None */
+ { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 0} }, /* None */
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 508 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 504 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 496 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 480 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 480 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x6, {0x000000, 480 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 508 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 504 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 496 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 480 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 480 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x6, {0x008000, 480 * 1024} },
+
+ { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 0} }, /* None */
+};
+
+static struct wp_range_descriptor gd25q64_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x6, {0x7f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor a25l040_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x70000, 64 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x2, {0x60000, 128 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x3, {0x40000, 256 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x4, {0x00000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x5, {0x00000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x00000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x00000, 512 * 1024} },
+};
+
+static uint8_t do_read_status(const struct flashctx *flash)
+{
+ if (flash->chip->read_status)
+ return flash->chip->read_status(flash);
+ else
+ return spi_read_status_register(flash);
+}
+
+static int do_write_status(const struct flashctx *flash, int status)
+{
+ if (flash->chip->write_status)
+ return flash->chip->write_status(flash, status);
+ else
+ return spi_write_status_register(flash, status);
+}
+
+/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
+static uint8_t w25q_read_status_register_2(const struct flashctx *flash)
+{
+ static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 };
+ unsigned char readarr[2];
+ int ret;
+
+ /* Read Status Register */
+ ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
+ if (ret) {
+ /*
+ * FIXME: make this a benign failure for now in case we are
+ * unable to execute the opcode
+ */
+ msg_cdbg("RDSR2 failed!\n");
+ readarr[0] = 0x00;
+ }
+
+ return readarr[0];
+}
+
+/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
+uint8_t mx25l_read_config_register(const struct flashctx *flash);//XXX
+uint8_t mx25l_read_config_register(const struct flashctx *flash)
+{
+ static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x15 };
+ unsigned char readarr[2]; /* leave room for dummy byte */
+ int ret;
+
+ ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
+ if (ret) {
+ msg_cdbg("RDCR failed!\n");
+ readarr[0] = 0x00;
+ }
+
+ return readarr[0];
+}
+
+/* Given a flash chip, this function returns its range table. */
+static int w25_range_table(const struct flashctx *flash,
+ struct wp_range_descriptor **descrs,
+ int *num_entries)
+{
+ uint8_t cr;
+
+ *descrs = 0;
+ *num_entries = 0;
+
+ switch (flash->chip->manufacture_id) {
+ case WINBOND_NEX_ID:
+ switch(flash->chip->model_id) {
+ case WINBOND_NEX_W25X10:
+ *descrs = w25x10_ranges;
+ *num_entries = ARRAY_SIZE(w25x10_ranges);
+ break;
+ case WINBOND_NEX_W25X20:
+ *descrs = w25x20_ranges;
+ *num_entries = ARRAY_SIZE(w25x20_ranges);
+ break;
+ case WINBOND_NEX_W25X40:
+ *descrs = w25x40_ranges;
+ *num_entries = ARRAY_SIZE(w25x40_ranges);
+ break;
+ case WINBOND_NEX_W25X80:
+ *descrs = w25x80_ranges;
+ *num_entries = ARRAY_SIZE(w25x80_ranges);
+ break;
+ case WINBOND_NEX_W25Q80_V:
+ *descrs = w25q80_ranges;
+ *num_entries = ARRAY_SIZE(w25q80_ranges);
+ break;
+ case WINBOND_NEX_W25Q16_V:
+ *descrs = w25q16_ranges;
+ *num_entries = ARRAY_SIZE(w25q16_ranges);
+ break;
+ case WINBOND_NEX_W25Q32_V:
+ case WINBOND_NEX_W25Q32_W:
+ //case WINBOND_NEX_W25Q32JW:
+ *descrs = w25q32_ranges;
+ *num_entries = ARRAY_SIZE(w25q32_ranges);
+ break;
+ case WINBOND_NEX_W25Q64_V:
+ case WINBOND_NEX_W25Q64_W:
+ *descrs = w25q64_ranges;
+ *num_entries = ARRAY_SIZE(w25q64_ranges);
+ break;
+ case WINBOND_NEX_W25Q128_DTR:
+ case WINBOND_NEX_W25Q128_V_M:
+ case WINBOND_NEX_W25Q128_V:
+ case WINBOND_NEX_W25Q128_W:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = w25rq128_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
+ } else {
+ /* CMP == 0 */
+ *descrs = w25rq128_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
+ }
+ break;
+ case WINBOND_NEX_W25Q256JV_M:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = w25rq256_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges);
+ } else {
+ /* CMP == 0 */
+ *descrs = w25rq256_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
+ }
+ break;
+ default:
+ msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
+ ", aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case EON_ID_NOPREFIX:
+ switch (flash->chip->model_id) {
+ case EON_EN25F40:
+ *descrs = en25f40_ranges;
+ *num_entries = ARRAY_SIZE(en25f40_ranges);
+ break;
+ case EON_EN25Q40:
+ *descrs = en25q40_ranges;
+ *num_entries = ARRAY_SIZE(en25q40_ranges);
+ break;
+ case EON_EN25Q80:
+ *descrs = en25q80_ranges;
+ *num_entries = ARRAY_SIZE(en25q80_ranges);
+ break;
+ case EON_EN25Q32:
+ *descrs = en25q32_ranges;
+ *num_entries = ARRAY_SIZE(en25q32_ranges);
+ break;
+ case EON_EN25Q64:
+ *descrs = en25q64_ranges;
+ *num_entries = ARRAY_SIZE(en25q64_ranges);
+ break;
+ case EON_EN25Q128:
+ *descrs = en25q128_ranges;
+ *num_entries = ARRAY_SIZE(en25q128_ranges);
+ break;
+ case EON_EN25S64:
+ *descrs = en25s64_ranges;
+ *num_entries = ARRAY_SIZE(en25s64_ranges);
+ break;
+ default:
+ msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
+ ", aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case MACRONIX_ID:
+ switch (flash->chip->model_id) {
+ case MACRONIX_MX25L1005:
+ *descrs = mx25l1005_ranges;
+ *num_entries = ARRAY_SIZE(mx25l1005_ranges);
+ break;
+ case MACRONIX_MX25L2005:
+ *descrs = mx25l2005_ranges;
+ *num_entries = ARRAY_SIZE(mx25l2005_ranges);
+ break;
+ case MACRONIX_MX25L4005:
+ *descrs = mx25l4005_ranges;
+ *num_entries = ARRAY_SIZE(mx25l4005_ranges);
+ break;
+ case MACRONIX_MX25L8005:
+ *descrs = mx25l8005_ranges;
+ *num_entries = ARRAY_SIZE(mx25l8005_ranges);
+ break;
+ case MACRONIX_MX25L1605:
+ /* FIXME: MX25L1605 and MX25L1605D have different write
+ * protection capabilities, but share IDs */
+ *descrs = mx25l1605d_ranges;
+ *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
+ break;
+ case MACRONIX_MX25L3205:
+ *descrs = mx25l3205d_ranges;
+ *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
+ break;
+ case MACRONIX_MX25U3235E:
+ *descrs = mx25u3235e_ranges;
+ *num_entries = ARRAY_SIZE(mx25u3235e_ranges);
+ break;
+ case MACRONIX_MX25U6435E:
+ *descrs = mx25u6435e_ranges;
+ *num_entries = ARRAY_SIZE(mx25u6435e_ranges);
+ break;
+ case MACRONIX_MX25U12835E:
+ cr = mx25l_read_config_register(flash);
+ if (cr & MX25U12835E_TB) { /* T/B == 1 */
+ *descrs = mx25u12835e_tb1_ranges;
+ *num_entries = ARRAY_SIZE(mx25u12835e_tb1_ranges);
+ } else { /* T/B == 0 */
+ *descrs = mx25u12835e_tb0_ranges;
+ *num_entries = ARRAY_SIZE(mx25u12835e_tb0_ranges);
+ }
+ break;
+ default:
+ msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
+ ", aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case ST_ID:
+ switch(flash->chip->model_id) {
+ case ST_N25Q064__1E:
+ case ST_N25Q064__3E:
+ *descrs = n25q064_ranges;
+ *num_entries = ARRAY_SIZE(n25q064_ranges);
+ break;
+ default:
+ msg_cerr("%s() %d: Micron flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case GIGADEVICE_ID:
+ switch(flash->chip->model_id) {
+ case GIGADEVICE_GD25LQ32:
+ *descrs = w25q32_ranges;
+ *num_entries = ARRAY_SIZE(w25q32_ranges);
+ break;
+ case GIGADEVICE_GD25Q40:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = gd25q40_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(gd25q40_cmp1_ranges);
+ } else {
+ *descrs = gd25q40_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(gd25q40_cmp0_ranges);
+ }
+ break;
+ case GIGADEVICE_GD25Q64:
+ case GIGADEVICE_GD25LQ64:
+ *descrs = gd25q64_ranges;
+ *num_entries = ARRAY_SIZE(gd25q64_ranges);
+ break;
+ case GIGADEVICE_GD25Q128:
+ case GIGADEVICE_GD25LQ128CD:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = w25rq128_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
+ } else {
+ /* CMP == 0 */
+ *descrs = w25rq128_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
+ }
+ break;
+ case GIGADEVICE_GD25Q256D:
+ *descrs = w25rq256_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
+ break;
+ default:
+ msg_cerr("%s() %d: GigaDevice flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case AMIC_ID_NOPREFIX:
+ switch(flash->chip->model_id) {
+ case AMIC_A25L040:
+ *descrs = a25l040_ranges;
+ *num_entries = ARRAY_SIZE(a25l040_ranges);
+ break;
+ default:
+ msg_cerr("%s() %d: AMIC flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case ATMEL_ID:
+ switch(flash->chip->model_id) {
+ //case ATMEL_AT25SF128A:
+ case ATMEL_AT25SL128A:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = w25rq128_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
+ } else {
+ /* CMP == 0 */
+ *descrs = w25rq128_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
+ }
+ break;
+ default:
+ msg_cerr("%s() %d: Atmel flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ default:
+ msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
+ __func__, flash->chip->manufacture_id);
+ return -1;
+ }
+
+ return 0;
+}
+
+int w25_range_to_status(const struct flashctx *flash,
+ unsigned int start, unsigned int len,
+ struct w25q_status *status)
+{
+ struct wp_range_descriptor *descrs;
+ int i, range_found = 0;
+ int num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ struct wp_range *r = &descrs[i].range;
+
+ msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
+ start, len, r->start, r->len);
+ if ((start == r->start) && (len == r->len)) {
+ status->bp0 = descrs[i].bp & 1;
+ status->bp1 = descrs[i].bp >> 1;
+ status->bp2 = descrs[i].bp >> 2;
+ status->tb = descrs[i].m.tb;
+ status->sec = descrs[i].m.sec;
+
+ range_found = 1;
+ break;
+ }
+ }
+
+ if (!range_found) {
+ msg_cerr("%s: matching range not found\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int w25_status_to_range(const struct flashctx *flash,
+ const struct w25q_status *status,
+ unsigned int *start, unsigned int *len)
+{
+ struct wp_range_descriptor *descrs;
+ int i, status_found = 0;
+ int num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ int bp;
+ int table_bp, table_tb, table_sec;
+
+ bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
+ msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
+ bp, descrs[i].bp,
+ status->tb, descrs[i].m.tb,
+ status->sec, descrs[i].m.sec);
+ table_bp = descrs[i].bp;
+ table_tb = descrs[i].m.tb;
+ table_sec = descrs[i].m.sec;
+ if ((bp == table_bp || table_bp == X) &&
+ (status->tb == table_tb || table_tb == X) &&
+ (status->sec == table_sec || table_sec == X)) {
+ *start = descrs[i].range.start;
+ *len = descrs[i].range.len;
+
+ status_found = 1;
+ break;
+ }
+ }
+
+ if (!status_found) {
+ msg_cerr("matching status not found\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Given a [start, len], this function calls w25_range_to_status() to convert
+ * it to flash-chip-specific range bits, then sets into status register.
+ */
+static int w25_set_range(const struct flashctx *flash,
+ unsigned int start, unsigned int len)
+{
+ struct w25q_status status;
+ int tmp = 0;
+ int expected = 0;
+
+ memset(&status, 0, sizeof(status));
+ tmp = do_read_status(flash);
+ memcpy(&status, &tmp, 1);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
+
+ if (w25_range_to_status(flash, start, len, &status))
+ return -1;
+
+ msg_cdbg("status.busy: %x\n", status.busy);
+ msg_cdbg("status.wel: %x\n", status.wel);
+ msg_cdbg("status.bp0: %x\n", status.bp0);
+ msg_cdbg("status.bp1: %x\n", status.bp1);
+ msg_cdbg("status.bp2: %x\n", status.bp2);
+ msg_cdbg("status.tb: %x\n", status.tb);
+ msg_cdbg("status.sec: %x\n", status.sec);
+ msg_cdbg("status.srp0: %x\n", status.srp0);
+
+ memcpy(&expected, &status, sizeof(status));
+ do_write_status(flash, expected);
+
+ tmp = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
+ if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) {
+ msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
+ expected, tmp);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Print out the current status register value with human-readable text. */
+static int w25_wp_status(const struct flashctx *flash)
+{
+ struct w25q_status status;
+ int tmp;
+ unsigned int start, len;
+ int ret = 0;
+
+ memset(&status, 0, sizeof(status));
+ tmp = do_read_status(flash);
+ memcpy(&status, &tmp, 1);
+ msg_cinfo("WP: status: 0x%02x\n", tmp);
+ msg_cinfo("WP: status.srp0: %x\n", status.srp0);
+ msg_cinfo("WP: write protect is %s.\n",
+ status.srp0 ? "enabled" : "disabled");
+
+ msg_cinfo("WP: write protect range: ");
+ if (w25_status_to_range(flash, &status, &start, &len)) {
+ msg_cinfo("(cannot resolve the range)\n");
+ ret = -1;
+ } else {
+ msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
+ }
+
+ return ret;
+}
+
+static int w25q_large_range_to_status(const struct flashctx *flash,
+ unsigned int start, unsigned int len,
+ struct w25q_status_large *status)
+{
+ struct wp_range_descriptor *descrs;
+ int i, range_found = 0;
+ int num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ struct wp_range *r = &descrs[i].range;
+
+ msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
+ start, len, r->start, r->len);
+ if ((start == r->start) && (len == r->len)) {
+ status->bp0 = descrs[i].bp & 1;
+ status->bp1 = descrs[i].bp >> 1;
+ status->bp2 = descrs[i].bp >> 2;
+ status->bp3 = descrs[i].bp >> 3;
+ /*
+ * For MX25U12835E chip, Top/Bottom (T/B) bit is not
+ * part of status register and in that bit position is
+ * Quad Enable (QE)
+ */
+ if (flash->chip->manufacture_id != MACRONIX_ID ||
+ flash->chip->model_id != MACRONIX_MX25U12835E)
+ status->tb = descrs[i].m.tb;
+
+ range_found = 1;
+ break;
+ }
+ }
+
+ if (!range_found) {
+ msg_cerr("%s: matching range not found\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int w25_large_status_to_range(const struct flashctx *flash,
+ const struct w25q_status_large *status,
+ unsigned int *start, unsigned int *len)
+{
+ struct wp_range_descriptor *descrs;
+ int i, status_found = 0;
+ int num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ int bp;
+ int table_bp, table_tb;
+
+ bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) |
+ (status->bp3 << 3);
+ msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n",
+ bp, descrs[i].bp,
+ status->tb, descrs[i].m.tb);
+ table_bp = descrs[i].bp;
+ table_tb = descrs[i].m.tb;
+ if ((bp == table_bp || table_bp == X) &&
+ (status->tb == table_tb || table_tb == X)) {
+ *start = descrs[i].range.start;
+ *len = descrs[i].range.len;
+
+ status_found = 1;
+ break;
+ }
+ }
+
+ if (!status_found) {
+ msg_cerr("matching status not found\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Given a [start, len], this function calls w25_range_to_status() to convert
+ * it to flash-chip-specific range bits, then sets into status register.
+ * Returns 0 if successful, -1 on error, and 1 if reading back was different.
+ */
+static int w25q_large_set_range(const struct flashctx *flash,
+ unsigned int start, unsigned int len)
+{
+ struct w25q_status_large status;
+ int tmp;
+ int expected = 0;
+
+ memset(&status, 0, sizeof(status));
+ tmp = do_read_status(flash);
+ memcpy(&status, &tmp, 1);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
+
+ if (w25q_large_range_to_status(flash, start, len, &status))
+ return -1;
+
+ msg_cdbg("status.busy: %x\n", status.busy);
+ msg_cdbg("status.wel: %x\n", status.wel);
+ msg_cdbg("status.bp0: %x\n", status.bp0);
+ msg_cdbg("status.bp1: %x\n", status.bp1);
+ msg_cdbg("status.bp2: %x\n", status.bp2);
+ msg_cdbg("status.bp3: %x\n", status.bp3);
+ msg_cdbg("status.tb: %x\n", status.tb);
+ msg_cdbg("status.srp0: %x\n", status.srp0);
+
+ memcpy(&expected, &status, sizeof(status));
+ do_write_status(flash, expected);
+
+ tmp = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
+ if ((tmp & MASK_WP_AREA_LARGE) != (expected & MASK_WP_AREA_LARGE)) {
+ msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
+ expected, tmp);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int w25q_large_wp_status(const struct flashctx *flash)
+{
+ struct w25q_status_large sr1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp[2];
+ unsigned int start, len;
+ int ret = 0;
+
+ memset(&sr1, 0, sizeof(sr1));
+ tmp[0] = do_read_status(flash);
+ memcpy(&sr1, &tmp[0], 1);
+
+ memset(&sr2, 0, sizeof(sr2));
+ tmp[1] = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp[1], 1);
+
+ msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
+ msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
+ msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
+ msg_cinfo("WP: write protect is %s.\n",
+ (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
+
+ msg_cinfo("WP: write protect range: ");
+ if (w25_large_status_to_range(flash, &sr1, &start, &len)) {
+ msg_cinfo("(cannot resolve the range)\n");
+ ret = -1;
+ } else {
+ msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
+ }
+
+ return ret;
+}
+
+/* Set/clear the SRP0 bit in the status register. */
+static int w25_set_srp0(const struct flashctx *flash, int enable)
+{
+ struct w25q_status status;
+ int tmp = 0;
+ int expected = 0;
+
+ memset(&status, 0, sizeof(status));
+ tmp = do_read_status(flash);
+ /* FIXME: this is NOT endian-free copy. */
+ memcpy(&status, &tmp, 1);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
+
+ status.srp0 = enable ? 1 : 0;
+ memcpy(&expected, &status, sizeof(status));
+ do_write_status(flash, expected);
+
+ tmp = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
+ if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
+ return 1;
+
+ return 0;
+}
+
+static int w25_enable_writeprotect(const struct flashctx *flash,
+ enum wp_mode wp_mode)
+{
+ int ret;
+
+ if (wp_mode != WP_MODE_HARDWARE) {
+ msg_cerr("%s(): unsupported write-protect mode\n", __func__);
+ return 1;
+ }
+
+ ret = w25_set_srp0(flash, 1);
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+ return ret;
+}
+
+static int w25_disable_writeprotect(const struct flashctx *flash)
+{
+ int ret;
+
+ ret = w25_set_srp0(flash, 0);
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+
+ return ret;
+}
+
+static int w25_list_ranges(const struct flashctx *flash)
+{
+ struct wp_range_descriptor *descrs;
+ int i, num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ msg_cinfo("start: 0x%06x, length: 0x%06x\n",
+ descrs[i].range.start,
+ descrs[i].range.len);
+ }
+
+ return 0;
+}
+
+static int w25q_wp_status(const struct flashctx *flash)
+{
+ struct w25q_status sr1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp[2];
+ unsigned int start, len;
+ int ret = 0;
+
+ memset(&sr1, 0, sizeof(sr1));
+ tmp[0] = do_read_status(flash);
+ memcpy(&sr1, &tmp[0], 1);
+
+ memset(&sr2, 0, sizeof(sr2));
+ tmp[1] = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp[1], 1);
+
+ msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
+ msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
+ msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
+ msg_cinfo("WP: write protect is %s.\n",
+ (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
+
+ msg_cinfo("WP: write protect range: ");
+ if (w25_status_to_range(flash, &sr1, &start, &len)) {
+ msg_cinfo("(cannot resolve the range)\n");
+ ret = -1;
+ } else {
+ msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
+ }
+
+ return ret;
+}
+
+/*
+ * W25Q adds an optional byte to the standard WRSR opcode. If /CS is
+ * de-asserted after the first byte, then it acts like a JEDEC-standard
+ * WRSR command. if /CS is asserted, then the next data byte is written
+ * into status register 2.
+ */
+#define W25Q_WRSR_OUTSIZE 0x03
+static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = W25Q_WRSR_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution\n",
+ __func__);
+ }
+
+ /* WRSR performs a self-timed erase before the changes take effect. */
+ programmer_delay(100 * 1000);
+
+ return result;
+}
+
+/*
+ * Set/clear the SRP1 bit in status register 2.
+ * FIXME: make this more generic if other chips use the same SR2 layout
+ */
+static int w25q_set_srp1(const struct flashctx *flash, int enable)
+{
+ struct w25q_status sr1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp, expected;
+
+ tmp = do_read_status(flash);
+ memcpy(&sr1, &tmp, 1);
+ tmp = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp, 1);
+
+ msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp);
+
+ sr2.srp1 = enable ? 1 : 0;
+
+ memcpy(&expected, &sr2, 1);
+ w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2));
+
+ tmp = w25q_read_status_register_2(flash);
+ msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp);
+ if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA))
+ return 1;
+
+ return 0;
+}
+
+enum wp_mode get_wp_mode(const char *mode_str)
+{
+ enum wp_mode wp_mode = WP_MODE_UNKNOWN;
+
+ if (!strcasecmp(mode_str, "hardware"))
+ wp_mode = WP_MODE_HARDWARE;
+ else if (!strcasecmp(mode_str, "power_cycle"))
+ wp_mode = WP_MODE_POWER_CYCLE;
+ else if (!strcasecmp(mode_str, "permanent"))
+ wp_mode = WP_MODE_PERMANENT;
+
+ return wp_mode;
+}
+
+static int w25q_disable_writeprotect(const struct flashctx *flash,
+ enum wp_mode wp_mode)
+{
+ int ret = 1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp;
+
+ switch (wp_mode) {
+ case WP_MODE_HARDWARE:
+ ret = w25_set_srp0(flash, 0);
+ break;
+ case WP_MODE_POWER_CYCLE:
+ tmp = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp, 1);
+ if (sr2.srp1) {
+ msg_cerr("%s(): must disconnect power to disable "
+ "write-protection\n", __func__);
+ } else {
+ ret = 0;
+ }
+ break;
+ case WP_MODE_PERMANENT:
+ msg_cerr("%s(): cannot disable permanent write-protection\n",
+ __func__);
+ break;
+ default:
+ msg_cerr("%s(): invalid mode specified\n", __func__);
+ break;
+ }
+
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+
+ return ret;
+}
+
+static int w25q_disable_writeprotect_default(const struct flashctx *flash)
+{
+ return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE);
+}
+
+static int w25q_enable_writeprotect(const struct flashctx *flash,
+ enum wp_mode wp_mode)
+{
+ int ret = 1;
+ struct w25q_status sr1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp;
+
+ switch (wp_mode) {
+ case WP_MODE_HARDWARE:
+ if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) {
+ msg_cerr("%s(): cannot disable power cycle WP mode\n",
+ __func__);
+ break;
+ }
+
+ tmp = do_read_status(flash);
+ memcpy(&sr1, &tmp, 1);
+ if (sr1.srp0)
+ ret = 0;
+ else
+ ret = w25_set_srp0(flash, 1);
+
+ break;
+ case WP_MODE_POWER_CYCLE:
+ if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) {
+ msg_cerr("%s(): cannot disable hardware WP mode\n",
+ __func__);
+ break;
+ }
+
+ tmp = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp, 1);
+ if (sr2.srp1)
+ ret = 0;
+ else
+ ret = w25q_set_srp1(flash, 1);
+
+ break;
+ case WP_MODE_PERMANENT:
+ tmp = do_read_status(flash);
+ memcpy(&sr1, &tmp, 1);
+ if (sr1.srp0 == 0) {
+ ret = w25_set_srp0(flash, 1);
+ if (ret) {
+ msg_perr("%s(): cannot enable SRP0 for "
+ "permanent WP\n", __func__);
+ break;
+ }
+ }
+
+ tmp = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp, 1);
+ if (sr2.srp1 == 0) {
+ ret = w25q_set_srp1(flash, 1);
+ if (ret) {
+ msg_perr("%s(): cannot enable SRP1 for "
+ "permanent WP\n", __func__);
+ break;
+ }
+ }
+
+ break;
+ default:
+ msg_perr("%s(): invalid mode %d\n", __func__, wp_mode);
+ break;
+ }
+
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+ return ret;
+}
+
+/* W25P, W25X, and many flash chips from various vendors */
+struct wp wp_w25 = {
+ .list_ranges = w25_list_ranges,
+ .set_range = w25_set_range,
+ .enable = w25_enable_writeprotect,
+ .disable = w25_disable_writeprotect,
+ .wp_status = w25_wp_status,
+
+};
+
+/* W25Q series has features such as a second status register and SFDP */
+struct wp wp_w25q = {
+ .list_ranges = w25_list_ranges,
+ .set_range = w25_set_range,
+ .enable = w25q_enable_writeprotect,
+ /*
+ * By default, disable hardware write-protection. We may change
+ * this later if we want to add fine-grained write-protect disable
+ * as a command-line option.
+ */
+ .disable = w25q_disable_writeprotect_default,
+ .wp_status = w25q_wp_status,
+};
+
+/* W25Q large series has 4 block-protect bits */
+struct wp wp_w25q_large = {
+ .list_ranges = w25_list_ranges,
+ .set_range = w25q_large_set_range,
+ .enable = w25q_enable_writeprotect,
+ /*
+ * By default, disable hardware write-protection. We may change
+ * this later if we want to add fine-grained write-protect disable
+ * as a command-line option.
+ */
+ .disable = w25q_disable_writeprotect_default,
+ .wp_status = w25q_large_wp_status,
+};
+
+struct wp_range_descriptor gd25q32_cmp0_ranges[] = {
+ /* none, bp4 and bp3 => don't care */
+ { { }, 0x00, {0, 0} },
+ { { }, 0x08, {0, 0} },
+ { { }, 0x10, {0, 0} },
+ { { }, 0x18, {0, 0} },
+
+ { { }, 0x01, {0x3f0000, 64 * 1024} },
+ { { }, 0x02, {0x3e0000, 128 * 1024} },
+ { { }, 0x03, {0x3c0000, 256 * 1024} },
+ { { }, 0x04, {0x380000, 512 * 1024} },
+ { { }, 0x05, {0x300000, 1024 * 1024} },
+ { { }, 0x06, {0x200000, 2048 * 1024} },
+
+ { { }, 0x09, {0x000000, 64 * 1024} },
+ { { }, 0x0a, {0x000000, 128 * 1024} },
+ { { }, 0x0b, {0x000000, 256 * 1024} },
+ { { }, 0x0c, {0x000000, 512 * 1024} },
+ { { }, 0x0d, {0x000000, 1024 * 1024} },
+ { { }, 0x0e, {0x000000, 2048 * 1024} },
+
+ /* all, bp4 and bp3 => don't care */
+ { { }, 0x07, {0x000000, 4096 * 1024} },
+ { { }, 0x0f, {0x000000, 4096 * 1024} },
+ { { }, 0x17, {0x000000, 4096 * 1024} },
+ { { }, 0x1f, {0x000000, 4096 * 1024} },
+
+ { { }, 0x11, {0x3ff000, 4 * 1024} },
+ { { }, 0x12, {0x3fe000, 8 * 1024} },
+ { { }, 0x13, {0x3fc000, 16 * 1024} },
+ { { }, 0x14, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
+ { { }, 0x15, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
+ { { }, 0x16, {0x3f8000, 32 * 1024} },
+
+ { { }, 0x19, {0x000000, 4 * 1024} },
+ { { }, 0x1a, {0x000000, 8 * 1024} },
+ { { }, 0x1b, {0x000000, 16 * 1024} },
+ { { }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
+ { { }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
+ { { }, 0x1e, {0x000000, 32 * 1024} },
+};
+
+struct wp_range_descriptor gd25q32_cmp1_ranges[] = {
+ /* All, bp4 and bp3 => don't care */
+ { { }, 0x00, {0x000000, 4096 * 1024} }, /* All */
+ { { }, 0x08, {0x000000, 4096 * 1024} },
+ { { }, 0x10, {0x000000, 4096 * 1024} },
+ { { }, 0x18, {0x000000, 4096 * 1024} },
+
+ { { }, 0x01, {0x000000, 4032 * 1024} },
+ { { }, 0x02, {0x000000, 3968 * 1024} },
+ { { }, 0x03, {0x000000, 3840 * 1024} },
+ { { }, 0x04, {0x000000, 3584 * 1024} },
+ { { }, 0x05, {0x000000, 3 * 1024 * 1024} },
+ { { }, 0x06, {0x000000, 2 * 1024 * 1024} },
+
+ { { }, 0x09, {0x010000, 4032 * 1024} },
+ { { }, 0x0a, {0x020000, 3968 * 1024} },
+ { { }, 0x0b, {0x040000, 3840 * 1024} },
+ { { }, 0x0c, {0x080000, 3584 * 1024} },
+ { { }, 0x0d, {0x100000, 3 * 1024 * 1024} },
+ { { }, 0x0e, {0x200000, 2 * 1024 * 1024} },
+
+ /* None, bp4 and bp3 => don't care */
+ { { }, 0x07, {0, 0} }, /* None */
+ { { }, 0x0f, {0, 0} },
+ { { }, 0x17, {0, 0} },
+ { { }, 0x1f, {0, 0} },
+
+ { { }, 0x11, {0x000000, 4092 * 1024} },
+ { { }, 0x12, {0x000000, 4088 * 1024} },
+ { { }, 0x13, {0x000000, 4080 * 1024} },
+ { { }, 0x14, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
+ { { }, 0x15, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
+ { { }, 0x16, {0x000000, 4064 * 1024} },
+
+ { { }, 0x19, {0x001000, 4092 * 1024} },
+ { { }, 0x1a, {0x002000, 4088 * 1024} },
+ { { }, 0x1b, {0x040000, 4080 * 1024} },
+ { { }, 0x1c, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
+ { { }, 0x1d, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
+ { { }, 0x1e, {0x080000, 4064 * 1024} },
+};
+
+static struct wp_context gd25q32_wp = {
+ /* TODO: map second status register */
+ .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
+};
+
+struct wp_range_descriptor gd25q128_cmp0_ranges[] = {
+ /* none, bp4 and bp3 => don't care, others = 0 */
+ { { .tb = 0 }, 0x00, {0, 0} },
+ { { .tb = 0 }, 0x08, {0, 0} },
+ { { .tb = 0 }, 0x10, {0, 0} },
+ { { .tb = 0 }, 0x18, {0, 0} },
+
+ { { .tb = 0 }, 0x01, {0xfc0000, 256 * 1024} },
+ { { .tb = 0 }, 0x02, {0xf80000, 512 * 1024} },
+ { { .tb = 0 }, 0x03, {0xf00000, 1024 * 1024} },
+ { { .tb = 0 }, 0x04, {0xe00000, 2048 * 1024} },
+ { { .tb = 0 }, 0x05, {0xc00000, 4096 * 1024} },
+ { { .tb = 0 }, 0x06, {0x800000, 8192 * 1024} },
+
+ { { .tb = 0 }, 0x09, {0x000000, 256 * 1024} },
+ { { .tb = 0 }, 0x0a, {0x000000, 512 * 1024} },
+ { { .tb = 0 }, 0x0b, {0x000000, 1024 * 1024} },
+ { { .tb = 0 }, 0x0c, {0x000000, 2048 * 1024} },
+ { { .tb = 0 }, 0x0d, {0x000000, 4096 * 1024} },
+ { { .tb = 0 }, 0x0e, {0x000000, 8192 * 1024} },
+
+ /* all, bp4 and bp3 => don't care, others = 1 */
+ { { .tb = 0 }, 0x07, {0x000000, 16384 * 1024} },
+ { { .tb = 0 }, 0x0f, {0x000000, 16384 * 1024} },
+ { { .tb = 0 }, 0x17, {0x000000, 16384 * 1024} },
+ { { .tb = 0 }, 0x1f, {0x000000, 16384 * 1024} },
+
+ { { .tb = 0 }, 0x11, {0xfff000, 4 * 1024} },
+ { { .tb = 0 }, 0x12, {0xffe000, 8 * 1024} },
+ { { .tb = 0 }, 0x13, {0xffc000, 16 * 1024} },
+ { { .tb = 0 }, 0x14, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
+ { { .tb = 0 }, 0x15, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
+
+ { { .tb = 0 }, 0x19, {0x000000, 4 * 1024} },
+ { { .tb = 0 }, 0x1a, {0x000000, 8 * 1024} },
+ { { .tb = 0 }, 0x1b, {0x000000, 16 * 1024} },
+ { { .tb = 0 }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
+ { { .tb = 0 }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
+ { { .tb = 0 }, 0x1e, {0x000000, 32 * 1024} },
+};
+
+struct wp_range_descriptor gd25q128_cmp1_ranges[] = {
+ /* none, bp4 and bp3 => don't care, others = 0 */
+ { { .tb = 1 }, 0x00, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x10, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x18, {0x000000, 16384 * 1024} },
+
+ { { .tb = 1 }, 0x01, {0x000000, 16128 * 1024} },
+ { { .tb = 1 }, 0x02, {0x000000, 15872 * 1024} },
+ { { .tb = 1 }, 0x03, {0x000000, 15360 * 1024} },
+ { { .tb = 1 }, 0x04, {0x000000, 14336 * 1024} },
+ { { .tb = 1 }, 0x05, {0x000000, 12288 * 1024} },
+ { { .tb = 1 }, 0x06, {0x000000, 8192 * 1024} },
+
+ { { .tb = 1 }, 0x09, {0x000000, 16128 * 1024} },
+ { { .tb = 1 }, 0x0a, {0x000000, 15872 * 1024} },
+ { { .tb = 1 }, 0x0b, {0x000000, 15360 * 1024} },
+ { { .tb = 1 }, 0x0c, {0x000000, 14336 * 1024} },
+ { { .tb = 1 }, 0x0d, {0x000000, 12288 * 1024} },
+ { { .tb = 1 }, 0x0e, {0x000000, 8192 * 1024} },
+
+ /* none, bp4 and bp3 => don't care, others = 1 */
+ { { .tb = 1 }, 0x07, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x0f, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x17, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x1f, {0x000000, 16384 * 1024} },
+
+ { { .tb = 1 }, 0x11, {0x000000, 16380 * 1024} },
+ { { .tb = 1 }, 0x12, {0x000000, 16376 * 1024} },
+ { { .tb = 1 }, 0x13, {0x000000, 16368 * 1024} },
+ { { .tb = 1 }, 0x14, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
+ { { .tb = 1 }, 0x15, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
+
+ { { .tb = 1 }, 0x19, {0x001000, 16380 * 1024} },
+ { { .tb = 1 }, 0x1a, {0x002000, 16376 * 1024} },
+ { { .tb = 1 }, 0x1b, {0x004000, 16368 * 1024} },
+ { { .tb = 1 }, 0x1c, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
+ { { .tb = 1 }, 0x1d, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
+ { { .tb = 1 }, 0x1e, {0x008000, 16352 * 1024} },
+};
+
+static struct wp_context gd25q128_wp = {
+ /* TODO: map second and third status registers */
+ .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
+};
+
+/* FIXME: MX25L6406 has same ID as MX25L6405D */
+struct wp_range_descriptor mx25l6406e_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
+ { { }, 0x2, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
+ { { }, 0x3, {0x7a0000, 64 * 8 * 1024} }, /* blocks 120-127 */
+ { { }, 0x4, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
+ { { }, 0x5, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
+ { { }, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
+
+ { { }, 0x7, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0x9, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { { }, 0xa, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
+ { { }, 0xb, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
+ { { }, 0xc, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
+ { { }, 0xd, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
+ { { }, 0xe, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
+ { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
+};
+
+static struct wp_context mx25l6406e_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
+ .descrs = &mx25l6406e_ranges[0],
+};
+
+struct wp_range_descriptor mx25l6495f_tb0_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
+ { { }, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
+ { { }, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
+
+ { { }, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
+ { { }, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
+ { { }, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
+ { { }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
+ { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
+};
+
+struct wp_range_descriptor mx25l6495f_tb1_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
+ { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
+ { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
+ { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
+ { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
+ { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+ { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
+};
+
+static struct wp_context mx25l6495f_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
+};
+
+struct wp_range_descriptor mx25l25635f_tb0_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* block 511 */
+ { { }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* blocks 510-511 */
+ { { }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* blocks 508-511 */
+ { { }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* blocks 504-511 */
+ { { }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* blocks 496-511 */
+ { { }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* blocks 480-511 */
+ { { }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* blocks 448-511 */
+ { { }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* blocks 384-511 */
+ { { }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* blocks 256-511 */
+ { { }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* all */
+};
+
+struct wp_range_descriptor mx25l25635f_tb1_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
+ { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
+ { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
+ { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
+ { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
+ { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+ { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
+ { { }, 0x9, {0x000000, 64 * 256 * 1024} }, /* blocks 0-255 */
+ { { }, 0xa, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xb, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xc, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xd, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xe, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xf, {0x000000, 64 * 512 * 1024} }, /* all */
+};
+
+static struct wp_context mx25l25635f_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
+};
+
+#if 0
+struct wp_range_descriptor s25fs128s_ranges[] = {
+ { { .tb = 1 }, 0, {0, 0} }, /* none */
+ { { .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* lower 64th */
+ { { .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* lower 32nd */
+ { { .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* lower 16th */
+ { { .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* lower 8th */
+ { { .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* lower 4th */
+ { { .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* lower half */
+ { { .tb = 1 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
+
+ { { .tb = 0 }, 0, {0, 0} }, /* none */
+ { { .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* upper 64th */
+ { { .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* upper 32nd */
+ { { .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* upper 16th */
+ { { .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* upper 8th */
+ { { .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* upper 4th */
+ { { .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* upper half */
+ { { .tb = 0 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
+};
+
+static struct wp_context s25fs128s_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
+ .get_modifier_bits = s25f_get_modifier_bits,
+ .set_modifier_bits = s25f_set_modifier_bits,
+};
+
+
+struct wp_range_descriptor s25fl256s_ranges[] = {
+ { { .tb = 1 }, 0, {0, 0} }, /* none */
+ { { .tb = 1 }, 0x1, {0x000000, 512 * 1024} }, /* lower 64th */
+ { { .tb = 1 }, 0x2, {0x000000, 1024 * 1024} }, /* lower 32nd */
+ { { .tb = 1 }, 0x3, {0x000000, 2048 * 1024} }, /* lower 16th */
+ { { .tb = 1 }, 0x4, {0x000000, 4096 * 1024} }, /* lower 8th */
+ { { .tb = 1 }, 0x5, {0x000000, 8192 * 1024} }, /* lower 4th */
+ { { .tb = 1 }, 0x6, {0x000000, 16384 * 1024} }, /* lower half */
+ { { .tb = 1 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
+
+ { { .tb = 0 }, 0, {0, 0} }, /* none */
+ { { .tb = 0 }, 0x1, {0x1f80000, 512 * 1024} }, /* upper 64th */
+ { { .tb = 0 }, 0x2, {0x1f00000, 1024 * 1024} }, /* upper 32nd */
+ { { .tb = 0 }, 0x3, {0x1e00000, 2048 * 1024} }, /* upper 16th */
+ { { .tb = 0 }, 0x4, {0x1c00000, 4096 * 1024} }, /* upper 8th */
+ { { .tb = 0 }, 0x5, {0x1800000, 8192 * 1024} }, /* upper 4th */
+ { { .tb = 0 }, 0x6, {0x1000000, 16384 * 1024} }, /* upper half */
+ { { .tb = 0 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
+};
+
+static struct wp_context s25fl256s_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
+ .get_modifier_bits = s25f_get_modifier_bits,
+ .set_modifier_bits = s25f_set_modifier_bits,
+};
+#endif
+
+/* Given a flash chip, this function returns its writeprotect info. */
+static int generic_range_table(const struct flashctx *flash,
+ struct wp_context **wp,
+ int *num_entries)
+{
+ *wp = NULL;
+ *num_entries = 0;
+
+ switch (flash->chip->manufacture_id) {
+ case GIGADEVICE_ID:
+ switch(flash->chip->model_id) {
+
+ case GIGADEVICE_GD25LQ32:
+ case GIGADEVICE_GD25Q32: {
+ uint8_t sr1 = w25q_read_status_register_2(flash);
+ *wp = &gd25q32_wp;
+
+ if (!(sr1 & (1 << 6))) { /* CMP == 0 */
+ (*wp)->descrs = &gd25q32_cmp0_ranges[0];
+ *num_entries = ARRAY_SIZE(gd25q32_cmp0_ranges);
+ } else { /* CMP == 1 */
+ (*wp)->descrs = &gd25q32_cmp1_ranges[0];
+ *num_entries = ARRAY_SIZE(gd25q32_cmp1_ranges);
+ }
+
+ break;
+ }
+ case GIGADEVICE_GD25Q128:
+ case GIGADEVICE_GD25LQ128CD: {
+ uint8_t sr1 = w25q_read_status_register_2(flash);
+ *wp = &gd25q128_wp;
+
+ if (!(sr1 & (1 << 6))) { /* CMP == 0 */
+ (*wp)->descrs = &gd25q128_cmp0_ranges[0];
+ *num_entries = ARRAY_SIZE(gd25q128_cmp0_ranges);
+ } else { /* CMP == 1 */
+ (*wp)->descrs = &gd25q128_cmp1_ranges[0];
+ *num_entries = ARRAY_SIZE(gd25q128_cmp1_ranges);
+ }
+
+ break;
+ }
+ default:
+ msg_cerr("%s() %d: GigaDevice flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case MACRONIX_ID:
+ switch (flash->chip->model_id) {
+ case MACRONIX_MX25L6405:
+ /* FIXME: MX25L64* chips have mixed capabilities and
+ share IDs */
+ *wp = &mx25l6406e_wp;
+ *num_entries = ARRAY_SIZE(mx25l6406e_ranges);
+ break;
+ case MACRONIX_MX25L6495F: {
+ uint8_t cr = mx25l_read_config_register(flash);
+
+ *wp = &mx25l6495f_wp;
+ if (!(cr & (1 << 3))) { /* T/B == 0 */
+ (*wp)->descrs = &mx25l6495f_tb0_ranges[0];
+ *num_entries = ARRAY_SIZE(mx25l6495f_tb0_ranges);
+ } else { /* T/B == 1 */
+ (*wp)->descrs = &mx25l6495f_tb1_ranges[0];
+ *num_entries = ARRAY_SIZE(mx25l6495f_tb1_ranges);
+ }
+ break;
+ }
+ case MACRONIX_MX25L25635F: {
+ uint8_t cr = mx25l_read_config_register(flash);
+
+ *wp = &mx25l25635f_wp;
+ if (!(cr & (1 << 3))) { /* T/B == 0 */
+ (*wp)->descrs = &mx25l25635f_tb0_ranges[0];
+ *num_entries = ARRAY_SIZE(mx25l25635f_tb0_ranges);
+ } else { /* T/B == 1 */
+ (*wp)->descrs = &mx25l25635f_tb1_ranges[0];
+ *num_entries = ARRAY_SIZE(mx25l25635f_tb1_ranges);
+ }
+ break;
+ }
+ default:
+ msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
+ ", aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ //case SPANSION_ID:
+ // switch (flash->chip->model_id) {
+ // case SPANSION_S25FS128S_L:
+ // case SPANSION_S25FS128S_S: {
+ // *wp = &s25fs128s_wp;
+ // (*wp)->descrs = s25fs128s_ranges;
+ // *num_entries = ARRAY_SIZE(s25fs128s_ranges);
+ // break;
+ // }
+ // case SPANSION_S25FL256S_UL:
+ // case SPANSION_S25FL256S_US: {
+ // *wp = &s25fl256s_wp;
+ // (*wp)->descrs = s25fl256s_ranges;
+ // *num_entries = ARRAY_SIZE(s25fl256s_ranges);
+ // break;
+ // }
+ // default:
+ // msg_cerr("%s():%d Spansion flash chip mismatch (0x%04x)"
+ // ", aborting\n", __func__, __LINE__,
+ // flash->chip->model_id);
+ // return -1;
+ // }
+ // break;
+ default:
+ msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
+ __func__, flash->chip->manufacture_id);
+ return -1;
+ }
+
+ return 0;
+}
+
+static uint8_t generic_get_bp_mask(struct wp_context *wp)
+{
+ return ((1 << (wp->sr1.bp0_pos + wp->sr1.bp_bits)) - 1) ^ \
+ ((1 << wp->sr1.bp0_pos) - 1);
+}
+
+static uint8_t generic_get_status_check_mask(struct wp_context *wp)
+{
+ return generic_get_bp_mask(wp) | 1 << wp->sr1.srp_pos;
+}
+
+/* Given a [start, len], this function finds a block protect bit combination
+ * (if possible) and sets the corresponding bits in "status". Remaining bits
+ * are preserved. */
+static int generic_range_to_status(const struct flashctx *flash,
+ unsigned int start, unsigned int len,
+ uint8_t *status, uint8_t *check_mask)
+{
+ struct wp_context *wp;
+ struct wp_range_descriptor *r;
+ int i, range_found = 0, num_entries;
+ uint8_t bp_mask;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ bp_mask = generic_get_bp_mask(wp);
+
+ for (i = 0, r = &wp->descrs[0]; i < num_entries; i++, r++) {
+ msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
+ start, len, r->range.start, r->range.len);
+ if ((start == r->range.start) && (len == r->range.len)) {
+ *status &= ~(bp_mask);
+ *status |= r->bp << (wp->sr1.bp0_pos);
+
+ if (wp->set_modifier_bits) {
+ if (wp->set_modifier_bits(flash, &r->m) < 0) {
+ msg_cerr("error setting modifier "
+ "bits for range.\n");
+ return -1;
+ }
+ }
+
+ range_found = 1;
+ break;
+ }
+ }
+
+ if (!range_found) {
+ msg_cerr("%s: matching range not found\n", __func__);
+ return -1;
+ }
+
+ *check_mask = generic_get_status_check_mask(wp);
+ return 0;
+}
+
+static int generic_status_to_range(const struct flashctx *flash,
+ const uint8_t sr1, unsigned int *start, unsigned int *len)
+{
+ struct wp_context *wp;
+ struct wp_range_descriptor *r;
+ int num_entries, i, status_found = 0;
+ uint8_t sr1_bp;
+ struct modifier_bits m;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ /* modifier bits may be compared more than once, so get them here */
+ if (wp->get_modifier_bits && wp->get_modifier_bits(flash, &m) < 0)
+ return -1;
+
+ sr1_bp = (sr1 >> wp->sr1.bp0_pos) & ((1 << wp->sr1.bp_bits) - 1);
+
+ for (i = 0, r = &wp->descrs[0]; i < num_entries; i++, r++) {
+ if (wp->get_modifier_bits) {
+ if (memcmp(&m, &r->m, sizeof(m)))
+ continue;
+ }
+ msg_cspew("comparing 0x%02x 0x%02x\n", sr1_bp, r->bp);
+ if (sr1_bp == r->bp) {
+ *start = r->range.start;
+ *len = r->range.len;
+ status_found = 1;
+ break;
+ }
+ }
+
+ if (!status_found) {
+ msg_cerr("matching status not found\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Given a [start, len], this function calls generic_range_to_status() to
+ * convert it to flash-chip-specific range bits, then sets into status register.
+ */
+static int generic_set_range(const struct flashctx *flash,
+ unsigned int start, unsigned int len)
+{
+ uint8_t status, expected, check_mask;
+
+ status = do_read_status(flash);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, status);
+
+ expected = status; /* preserve non-bp bits */
+ if (generic_range_to_status(flash, start, len, &expected, &check_mask))
+ return -1;
+
+ do_write_status(flash, expected);
+
+ status = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
+ if ((status & check_mask) != (expected & check_mask)) {
+ msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
+ expected, status, check_mask);
+ return 1;
+ }
+ return 0;
+}
+
+/* Set/clear the status regsiter write protect bit in SR1. */
+static int generic_set_srp0(const struct flashctx *flash, int enable)
+{
+ uint8_t status, expected, check_mask;
+ struct wp_context *wp;
+ int num_entries;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ expected = do_read_status(flash);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, expected);
+
+ if (enable)
+ expected |= 1 << wp->sr1.srp_pos;
+ else
+ expected &= ~(1 << wp->sr1.srp_pos);
+
+ do_write_status(flash, expected);
+
+ status = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
+
+ check_mask = generic_get_status_check_mask(wp);
+ msg_cdbg("%s: check mask: 0x%02x\n", __func__, check_mask);
+ if ((status & check_mask) != (expected & check_mask)) {
+ msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
+ expected, status, check_mask);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int generic_enable_writeprotect(const struct flashctx *flash,
+ enum wp_mode wp_mode)
+{
+ int ret;
+
+ if (wp_mode != WP_MODE_HARDWARE) {
+ msg_cerr("%s(): unsupported write-protect mode\n", __func__);
+ return 1;
+ }
+
+ ret = generic_set_srp0(flash, 1);
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+
+ return ret;
+}
+
+static int generic_disable_writeprotect(const struct flashctx *flash)
+{
+ int ret;
+
+ ret = generic_set_srp0(flash, 0);
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+
+ return ret;
+}
+
+static int generic_list_ranges(const struct flashctx *flash)
+{
+ struct wp_context *wp;
+ struct wp_range_descriptor *r;
+ int i, num_entries;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ r = &wp->descrs[0];
+ for (i = 0; i < num_entries; i++) {
+ msg_cinfo("start: 0x%06x, length: 0x%06x\n",
+ r->range.start, r->range.len);
+ r++;
+ }
+
+ return 0;
+}
+
+static int wp_context_status(const struct flashctx *flash)
+{
+ uint8_t sr1;
+ unsigned int start, len;
+ int ret = 0;
+ struct wp_context *wp;
+ int num_entries, wp_en;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ sr1 = do_read_status(flash);
+ wp_en = (sr1 >> wp->sr1.srp_pos) & 1;
+
+ msg_cinfo("WP: status: 0x%04x\n", sr1);
+ msg_cinfo("WP: status.srp0: %x\n", wp_en);
+ /* FIXME: SRP1 is not really generic, but we probably should print
+ * it anyway to have consistent output. #legacycruft */
+ msg_cinfo("WP: status.srp1: %x\n", 0);
+ msg_cinfo("WP: write protect is %s.\n",
+ wp_en ? "enabled" : "disabled");
+
+ msg_cinfo("WP: write protect range: ");
+ if (generic_status_to_range(flash, sr1, &start, &len)) {
+ msg_cinfo("(cannot resolve the range)\n");
+ ret = -1;
+ } else {
+ msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
+ }
+
+ return ret;
+}
+
+struct wp wp_generic = {
+ .list_ranges = generic_list_ranges,
+ .set_range = generic_set_range,
+ .enable = generic_enable_writeprotect,
+ .disable = generic_disable_writeprotect,
+ .wp_status = wp_context_status,
+};
diff --git a/writeprotect.h b/writeprotect.h
new file mode 100644
index 0000000..d39b993
--- /dev/null
+++ b/writeprotect.h
@@ -0,0 +1,57 @@
+/*
+ * 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
+
+enum wp_mode {
+ WP_MODE_UNKNOWN = -1,
+ WP_MODE_HARDWARE, /* hardware WP pin determines status */
+ WP_MODE_POWER_CYCLE, /* WP active until power off/on cycle */
+ WP_MODE_PERMANENT, /* status register permanently locked,
+ WP permanently enabled */
+};
+
+struct wp {
+ int (*list_ranges)(const struct flashctx *flash);
+ int (*set_range)(const struct flashctx *flash,
+ unsigned int start, unsigned int len);
+ int (*enable)(const struct flashctx *flash, enum wp_mode mode);
+ int (*disable)(const struct flashctx *flash);
+ int (*wp_status)(const struct flashctx *flash);
+};
+
+/* winbond w25-series */
+extern struct wp wp_w25; /* older winbond chips (w25p, w25x, etc) */
+extern struct wp wp_w25q;
+extern struct wp wp_w25q_large; /* large winbond chips (>= 32MB) */
+
+extern struct wp wp_generic;
+extern struct wp wp_wpce775x;
+
+enum wp_mode get_wp_mode(const char *mode_str);
+
+/*
+ * Generic write-protect stuff
+ */
+
+struct modifier_bits {
+ int sec; /* if 1, bp bits describe sectors */
+ int tb; /* value of top/bottom select bit */
+};
+
+#endif /* !__WRITEPROTECT_H__ */
--
To view, visit https://review.coreboot.org/c/flashrom/+/40325
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: Id93b5a1cb2da476fa8a7dde41d7b963024117474
Gerrit-Change-Number: 40325
Gerrit-PatchSet: 1
Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org>
Gerrit-MessageType: newchange
6
26
Change in flashrom[master]: cbtable.c: don't assume high addresses can fully map 1 MiB
by Edward O'Callaghan (Code Review) Jan. 25, 2021
by Edward O'Callaghan (Code Review) Jan. 25, 2021
Jan. 25, 2021
Edward O'Callaghan has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/37240 )
Change subject: cbtable.c: don't assume high addresses can fully map 1 MiB
......................................................................
cbtable.c: don't assume high addresses can fully map 1 MiB
Forward port the downstream `commit b17e9e41838`.
When using a forwarding table entry for finding the coreboot table
don't assume one has access to a full 1 MiB where the forwarding
table entry points to. The reason is that the 1 MiB may cover address
regions that have differing cacheability type. As such the kernel will
complain and the mapping will fail. Instead, check the header first then
map in the bytes that it indicates after sanity validation. That way
there is no attempt at requesting an invalid mapping that spans different
memory cacheability attributes.
BUG=b:66681446
BRANCH=None
TEST=Can successfully run 'flashrom -p host --wp-status' on kahlee
without generating PAT errors.
Original-Change-Id: Ic6c5832b069300cced66e11f4ca4a0bbc6e496de
Original-Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/685608
Original-Reviewed-by: Martin Roth <martinroth(a)chromium.org>
Original-Reviewed-by: Justin TerAvest <teravest(a)chromium.org>
Change-Id: I43705c19dd7c816098d03f528bde6f180c4c8f24
Signed-off-by: Edward O'Callaghan <quasisec(a)chromium.org>
---
M cbtable.c
1 file changed, 61 insertions(+), 8 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/40/37240/1
diff --git a/cbtable.c b/cbtable.c
index e566840..38a9f11 100644
--- a/cbtable.c
+++ b/cbtable.c
@@ -210,6 +210,66 @@
return NULL;
}
+static struct lb_header *find_lb_table_remap(unsigned long start_addr,
+ uint8_t **table_area)
+{
+ size_t offset;
+ unsigned long addr, end;
+ size_t mapping_size;
+ void *base;
+
+ mapping_size = getpagesize();
+ offset = start_addr % getpagesize();
+ start_addr -= offset;
+
+ base = physmap_ro("high tables", start_addr, mapping_size);
+ if (ERROR_PTR == base) {
+ msg_perr("Failed getting access to coreboot high tables.\n");
+ return NULL;
+ }
+
+ for (addr = offset, end = getpagesize(); addr < end; addr += 16) {
+ struct lb_record *recs;
+ struct lb_header *head;
+
+ /* No more headers to check. */
+ if (end - addr < sizeof(*head))
+ return NULL;
+
+ head = (struct lb_header *)(((char *)base) + addr);
+
+ if (!lb_header_valid(head, addr))
+ continue;
+
+ if (mapping_size - addr < head->table_bytes + sizeof(*head)) {
+ size_t prev_mapping_size = mapping_size;
+ mapping_size = head->table_bytes + sizeof(*head);
+ mapping_size += addr;
+ mapping_size += getpagesize() -
+ (mapping_size % getpagesize());
+ physunmap(base, prev_mapping_size);
+ base = physmap_ro("high tables", start_addr,
+ mapping_size);
+ if (ERROR_PTR == base) {
+ msg_perr("Failed getting access to coreboot high tables.\n");
+ return NULL;
+ }
+ }
+
+ head = (struct lb_header *)(((char *)base) + addr);
+ recs =
+ (struct lb_record *)(((char *)base) + addr + sizeof(*head));
+ if (!lb_table_valid(head, recs))
+ continue;
+ msg_pdbg("Found coreboot table at 0x%08lx.\n", addr);
+ *table_area = base;
+ return head;
+ }
+
+ physunmap(base, mapping_size);
+ return NULL;
+}
+
static void find_mainboard(struct lb_record *ptr, unsigned long addr)
{
struct lb_mainboard *rec;
@@ -283,15 +343,8 @@
(((char *)lb_table) + lb_table->header_bytes);
if (forward->tag == LB_TAG_FORWARD) {
start = forward->forward;
- start &= ~(getpagesize() - 1);
physunmap_unaligned(table_area, BYTES_TO_MAP);
- // FIXME: table_area is never unmapped below, nor is it unmapped above in the no-forward case
- table_area = physmap_ro_unaligned("high tables", start, BYTES_TO_MAP);
- if (ERROR_PTR == table_area) {
- msg_perr("Failed getting access to coreboot high tables.\n");
- return -1;
- }
- lb_table = find_lb_table(table_area, 0x00000, 0x1000);
+ lb_table = find_lb_table_remap(start, &table_area);
}
}
--
To view, visit https://review.coreboot.org/c/flashrom/+/37240
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I43705c19dd7c816098d03f528bde6f180c4c8f24
Gerrit-Change-Number: 37240
Gerrit-PatchSet: 1
Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org>
Gerrit-MessageType: newchange
5
13
Change in flashrom[master]: flashchips: upstream AT25SF128A from chromiumos
by Alan Green (Code Review) Jan. 5, 2021
by Alan Green (Code Review) Jan. 5, 2021
Jan. 5, 2021
Alan Green has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/37543 )
Change subject: flashchips: upstream AT25SF128A from chromiumos
......................................................................
flashchips: upstream AT25SF128A from chromiumos
Upstream the AT25SF128A chip from chromiumos repository.
Change-Id: I6349aa41eee0c997aaa507383aff7ce26441d24f
---
M flashchips.c
M flashchips.h
2 files changed, 39 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/43/37543/1
diff --git a/flashchips.c b/flashchips.c
index 4324c11..fc4cdff 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -2293,6 +2293,44 @@
{
.vendor = "Atmel",
+ .name = "AT25SF128A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25SF128A,
+ .total_size = 16384,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PR,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 512} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1700, 2000},
+ },
+
+ {
+ .vendor = "Atmel",
.name = "AT25SF161",
.bustype = BUS_SPI,
.manufacture_id = ATMEL_ID,
diff --git a/flashchips.h b/flashchips.h
index 2c2da6c..5609527 100644
--- a/flashchips.h
+++ b/flashchips.h
@@ -151,6 +151,7 @@
#define ATMEL_AT25SF081 0x8501
#define ATMEL_AT25SF161 0x8601
#define ATMEL_AT25SL128A 0x4218
+#define ATMEL_AT25SF128A 0x8901 /* Adesto AT25SF128A */
#define ATMEL_AT26DF041 0x4400
#define ATMEL_AT26DF081 0x4500 /* guessed, no datasheet available */
#define ATMEL_AT26DF081A 0x4501
--
To view, visit https://review.coreboot.org/c/flashrom/+/37543
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I6349aa41eee0c997aaa507383aff7ce26441d24f
Gerrit-Change-Number: 37543
Gerrit-PatchSet: 1
Gerrit-Owner: Alan Green <avg(a)google.com>
Gerrit-MessageType: newchange
3
4
Change in flashrom[master]: flashchips.c: Take GD25LQ128C/D from downstream
by Alan Green (Code Review) Jan. 5, 2021
by Alan Green (Code Review) Jan. 5, 2021
Jan. 5, 2021
Alan Green has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/35478 )
Change subject: flashchips.c: Take GD25LQ128C/D from downstream
......................................................................
flashchips.c: Take GD25LQ128C/D from downstream
Take the diffs for GD25LQ128C/D from the ChromiumOS flashrom repo.
This chips was added in Chromium OS `commit 62cd8106` by
furquan(a)google.com on 2016-07-18, and has been marked tested.
Signed-off-by: Alan Green <avg(a)google.com>
Change-Id: I4358392520507ddbfc654feb49fa982d49db2f28
---
M flashchips.c
1 file changed, 2 insertions(+), 3 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/78/35478/1
diff --git a/flashchips.c b/flashchips.c
index 3994c93..5c797fd 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -5894,7 +5894,7 @@
.page_size = 256,
/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
.feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
- .tested = TEST_UNTESTED,
+ .tested = TEST_OK_PREW,
.probe = probe_spi_rdid,
.probe_timing = TIMING_ZERO,
.block_erasers =
@@ -5916,8 +5916,7 @@
.block_erase = spi_block_erase_c7,
}
},
- .printlock = spi_prettyprint_status_register_bp4_srwd,
- .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .unlock = spi_disable_blockprotect,
.write = spi_chip_write_256,
.read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
.voltage = {1695, 1950},
--
To view, visit https://review.coreboot.org/c/flashrom/+/35478
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I4358392520507ddbfc654feb49fa982d49db2f28
Gerrit-Change-Number: 35478
Gerrit-PatchSet: 1
Gerrit-Owner: Alan Green <avg(a)google.com>
Gerrit-MessageType: newchange
4
10
Change in flashrom[master]: flashchips.c: Take GD25LQ40 from downstream
by Alan Green (Code Review) Jan. 5, 2021
by Alan Green (Code Review) Jan. 5, 2021
Jan. 5, 2021
Alan Green has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/35479 )
Change subject: flashchips.c: Take GD25LQ40 from downstream
......................................................................
flashchips.c: Take GD25LQ40 from downstream
Take definition of GD25LQ40 from ChromiumOS repository. This chip was
added in `commit 59543cd1` by dnschneid(a)chromium.org on 2016-04-27. The
commit message notes that some testing had been done, even thought the
.tested attribute was left as UNTESTED.
Signed-off-by: Alan Green <avg(a)google.com>
Change-Id: I978745b38536cda807adf07af4e4d093d0d93ad1
---
M flashchips.c
1 file changed, 2 insertions(+), 3 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/79/35479/1
diff --git a/flashchips.c b/flashchips.c
index 5c797fd..ac07fa8 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -6009,7 +6009,7 @@
.total_size = 512,
.page_size = 256,
/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
- .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .feature_bits = FEATURE_WRSR_WREN,
.tested = TEST_UNTESTED,
.probe = probe_spi_rdid,
.probe_timing = TIMING_ZERO,
@@ -6032,8 +6032,7 @@
.block_erase = spi_block_erase_c7,
}
},
- .printlock = spi_prettyprint_status_register_bp4_srwd,
- .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .unlock = spi_disable_blockprotect,
.write = spi_chip_write_256,
.read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
.voltage = {1695, 1950},
--
To view, visit https://review.coreboot.org/c/flashrom/+/35479
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I978745b38536cda807adf07af4e4d093d0d93ad1
Gerrit-Change-Number: 35479
Gerrit-PatchSet: 1
Gerrit-Owner: Alan Green <avg(a)google.com>
Gerrit-MessageType: newchange
2
7
Change in flashrom[master]: sb600spi.c: Fix for 0x790b rev 0x61
by Edward O'Callaghan (Code Review) Dec. 23, 2020
by Edward O'Callaghan (Code Review) Dec. 23, 2020
Dec. 23, 2020
Edward O'Callaghan has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/44073 )
Change subject: sb600spi.c: Fix for 0x790b rev 0x61
......................................................................
sb600spi.c: Fix for 0x790b rev 0x61
Adds support for rev 0x61 of pid 0x790b. Note that the
read callback requires a memory mapping.
Change-Id: I5ce63b5de863aed0442cb4ffeff981e9b2fa445b
Signed-off-by: Edward O'Callaghan <quasisec(a)google.com>
---
M sb600spi.c
1 file changed, 2 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/73/44073/1
diff --git a/sb600spi.c b/sb600spi.c
index 5892d57..d879281 100644
--- a/sb600spi.c
+++ b/sb600spi.c
@@ -76,7 +76,7 @@
.max_data_write = FIFO_SIZE_YANGTZE - 3,
.command = spi100_spi_send_command,
.multicommand = default_spi_send_multicommand,
- .read = default_spi_read,
+ .read = read_memmapped,
.write_256 = default_spi_write_256,
.write_aai = default_spi_write_aai,
};
@@ -143,7 +143,7 @@
if (rev == 0x4a) {
amd_gen = CHIPSET_YANGTZE;
msg_pdbg("Yangtze detected.\n");
- } else if (rev == 0x4b) {
+ } else if (rev == 0x4b || rev == 0x61) {
amd_gen = CHIPSET_PROMONTORY;
msg_pdbg("Promontory detected.\n");
} else {
--
To view, visit https://review.coreboot.org/c/flashrom/+/44073
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I5ce63b5de863aed0442cb4ffeff981e9b2fa445b
Gerrit-Change-Number: 44073
Gerrit-PatchSet: 1
Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org>
Gerrit-MessageType: newchange
5
25
Change in flashrom[master]: sb600spi.c: Don't access spibar directly
by Edward O'Callaghan (Code Review) Nov. 25, 2020
by Edward O'Callaghan (Code Review) Nov. 25, 2020
Nov. 25, 2020
Edward O'Callaghan has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/36433 )
Change subject: sb600spi.c: Don't access spibar directly
......................................................................
sb600spi.c: Don't access spibar directly
Constrain the manner in which the global state of spibar is
set and then subsequently accessed. This makes it easier to
write unit-tests and instrument the code.
Change-Id: Ic26372b9c1baebb20716eea1db1e942239ed3e48
Signed-off-by: Edward O'Callaghan <quasisec(a)chromium.org>
---
M sb600spi.c
1 file changed, 53 insertions(+), 38 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/33/36433/1
diff --git a/sb600spi.c b/sb600spi.c
index 23b36ee..7bdd11f 100644
--- a/sb600spi.c
+++ b/sb600spi.c
@@ -40,7 +40,13 @@
*};
*/
-static uint8_t *sb600_spibar = NULL;
+static uint8_t *g_sb600_spibar = NULL;
+
+static inline uint8_t * get_spibar(void)
+{
+ return g_sb600_spibar;
+}
+
enum amd_chipset {
CHIPSET_AMD_UNKNOWN,
CHIPSET_SB6XX,
@@ -180,16 +186,18 @@
static void reset_internal_fifo_pointer(void)
{
- mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2);
+ uint8_t *spibar = get_spibar();
+ mmio_writeb(mmio_readb(spibar + 2) | 0x10, spibar + 2);
/* FIXME: This loop needs a timeout and a clearer message. */
- while (mmio_readb(sb600_spibar + 0xD) & 0x7)
+ while (mmio_readb(spibar + 0xD) & 0x7)
msg_pspew("reset\n");
}
static int compare_internal_fifo_pointer(uint8_t want)
{
- uint8_t have = mmio_readb(sb600_spibar + 0xd) & 0x07;
+ uint8_t *spibar = get_spibar();
+ uint8_t have = mmio_readb(spibar + 0xd) & 0x07;
want %= FIFO_SIZE_OLD;
if (have != want) {
msg_perr("AMD SPI FIFO pointer corruption! Pointer is %d, wanted %d\n", have, want);
@@ -224,8 +232,9 @@
static void execute_command(void)
{
msg_pspew("Executing... ");
- mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2);
- while (mmio_readb(sb600_spibar + 2) & 1)
+ uint8_t *spibar = get_spibar();
+ mmio_writeb(mmio_readb(spibar + 2) | 1, spibar + 2);
+ while (mmio_readb(spibar + 2) & 1)
;
msg_pspew("done\n");
}
@@ -239,7 +248,8 @@
unsigned char cmd = *writearr++;
writecnt--;
msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
- mmio_writeb(cmd, sb600_spibar + 0);
+ uint8_t *spibar = get_spibar();
+ mmio_writeb(cmd, spibar + 0);
int ret = check_readwritecnt(flash, writecnt, readcnt);
if (ret != 0)
@@ -253,14 +263,14 @@
*/
unsigned int readoffby1 = (writecnt > 0) ? 0 : 1;
uint8_t readwrite = (readcnt + readoffby1) << 4 | (writecnt);
- mmio_writeb(readwrite, sb600_spibar + 1);
+ mmio_writeb(readwrite, spibar + 1);
reset_internal_fifo_pointer();
msg_pspew("Filling FIFO: ");
unsigned int count;
for (count = 0; count < writecnt; count++) {
msg_pspew("[%02x]", writearr[count]);
- mmio_writeb(writearr[count], sb600_spibar + 0xC);
+ mmio_writeb(writearr[count], spibar + 0xC);
}
msg_pspew("\n");
if (compare_internal_fifo_pointer(writecnt))
@@ -291,7 +301,7 @@
/* Skip the bytes we sent. */
msg_pspew("Skipping: ");
for (count = 0; count < writecnt; count++) {
- msg_pspew("[%02x]", mmio_readb(sb600_spibar + 0xC));
+ msg_pspew("[%02x]", mmio_readb(spibar + 0xC));
}
msg_pspew("\n");
if (compare_internal_fifo_pointer(writecnt))
@@ -299,14 +309,14 @@
msg_pspew("Reading FIFO: ");
for (count = 0; count < readcnt; count++) {
- readarr[count] = mmio_readb(sb600_spibar + 0xC);
+ readarr[count] = mmio_readb(spibar + 0xC);
msg_pspew("[%02x]", readarr[count]);
}
msg_pspew("\n");
if (compare_internal_fifo_pointer(writecnt+readcnt))
return SPI_PROGRAMMER_ERROR;
- if (mmio_readb(sb600_spibar + 1) != readwrite) {
+ if (mmio_readb(spibar + 1) != readwrite) {
msg_perr("Unexpected change in AMD SPI read/write count!\n");
msg_perr("Something else is accessing the flash chip and causes random corruption.\n"
"Please stop all applications and drivers and IPMI which access the flash chip.\n");
@@ -325,21 +335,22 @@
unsigned char cmd = *writearr++;
writecnt--;
msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
- mmio_writeb(cmd, sb600_spibar + 0);
+ uint8_t *spibar = get_spibar();
+ mmio_writeb(cmd, spibar + 0);
int ret = check_readwritecnt(flash, writecnt, readcnt);
if (ret != 0)
return ret;
/* Use the extended TxByteCount and RxByteCount registers. */
- mmio_writeb(writecnt, sb600_spibar + 0x48);
- mmio_writeb(readcnt, sb600_spibar + 0x4b);
+ mmio_writeb(writecnt, spibar + 0x48);
+ mmio_writeb(readcnt, spibar + 0x4b);
msg_pspew("Filling buffer: ");
unsigned int count;
for (count = 0; count < writecnt; count++) {
msg_pspew("[%02x]", writearr[count]);
- mmio_writeb(writearr[count], sb600_spibar + 0x80 + count);
+ mmio_writeb(writearr[count], spibar + 0x80 + count);
}
msg_pspew("\n");
@@ -347,7 +358,7 @@
msg_pspew("Reading buffer: ");
for (count = 0; count < readcnt; count++) {
- readarr[count] = mmio_readb(sb600_spibar + 0x80 + (writecnt + count) % FIFO_SIZE_YANGTZE);
+ readarr[count] = mmio_readb(spibar + 0x80 + (writecnt + count) % FIFO_SIZE_YANGTZE);
msg_pspew("[%02x]", readarr[count]);
}
msg_pspew("\n");
@@ -375,16 +386,17 @@
{
bool success = false;
uint8_t speed = spispeed->speed;
+ uint8_t *spibar = get_spibar();
msg_pdbg("Setting SPI clock to %s (0x%x).\n", spispeed->name, speed);
if (amd_gen >= CHIPSET_YANGTZE) {
- rmmio_writew((speed << 12) | (speed << 8) | (speed << 4) | speed, sb600_spibar + 0x22);
- uint16_t tmp = mmio_readw(sb600_spibar + 0x22);
+ rmmio_writew((speed << 12) | (speed << 8) | (speed << 4) | speed, spibar + 0x22);
+ uint16_t tmp = mmio_readw(spibar + 0x22);
success = (((tmp >> 12) & 0xf) == speed && ((tmp >> 8) & 0xf) == speed &&
((tmp >> 4) & 0xf) == speed && ((tmp >> 0) & 0xf) == speed);
} else {
- rmmio_writeb((mmio_readb(sb600_spibar + 0xd) & ~(0x3 << 4)) | (speed << 4), sb600_spibar + 0xd);
- success = (speed == ((mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3));
+ rmmio_writeb((mmio_readb(spibar + 0xd) & ~(0x3 << 4)) | (speed << 4), spibar + 0xd);
+ success = (speed == ((mmio_readb(spibar + 0xd) >> 4) & 0x3));
}
if (!success) {
@@ -396,11 +408,12 @@
static int set_mode(struct pci_dev *dev, uint8_t read_mode)
{
- uint32_t tmp = mmio_readl(sb600_spibar + 0x00);
+ uint8_t *spibar = get_spibar();
+ uint32_t tmp = mmio_readl(spibar + 0x00);
tmp &= ~(0x6 << 28 | 0x1 << 18); /* Clear mode bits */
tmp |= ((read_mode & 0x6) << 28) | ((read_mode & 0x1) << 18);
- rmmio_writel(tmp, sb600_spibar + 0x00);
- if (tmp != mmio_readl(sb600_spibar + 0x00))
+ rmmio_writel(tmp, spibar + 0x00);
+ if (tmp != mmio_readl(spibar + 0x00))
return 1;
return 0;
}
@@ -409,6 +422,7 @@
{
uint32_t tmp;
uint8_t spispeed_idx = 3; /* Default to 16.5 MHz */
+ uint8_t *spibar = get_spibar();
char *spispeed = extract_programmer_param("spispeed");
if (spispeed != NULL) {
@@ -450,7 +464,7 @@
"Normal (up to 66 MHz)", /* 6 */
"Fast Read", /* 7 (Not defined in the Bolton datasheet.) */
};
- tmp = mmio_readl(sb600_spibar + 0x00);
+ tmp = mmio_readl(spibar + 0x00);
uint8_t read_mode = ((tmp >> 28) & 0x6) | ((tmp >> 18) & 0x1);
msg_pdbg("SpiReadMode=%s (%i)\n", spireadmodes[read_mode], read_mode);
if (read_mode != 6) {
@@ -463,11 +477,11 @@
}
if (amd_gen >= CHIPSET_YANGTZE) {
- tmp = mmio_readb(sb600_spibar + 0x20);
+ tmp = mmio_readb(spibar + 0x20);
msg_pdbg("UseSpi100 is %sabled\n", (tmp & 0x1) ? "en" : "dis");
if ((tmp & 0x1) == 0) {
- rmmio_writeb(tmp | 0x1, sb600_spibar + 0x20);
- tmp = mmio_readb(sb600_spibar + 0x20) & 0x1;
+ rmmio_writeb(tmp | 0x1, spibar + 0x20);
+ tmp = mmio_readb(spibar + 0x20) & 0x1;
if (tmp == 0) {
msg_perr("Enabling Spi100 failed.\n");
return 1;
@@ -475,7 +489,7 @@
msg_pdbg("Enabling Spi100 succeeded.\n");
}
- tmp = mmio_readw(sb600_spibar + 0x22); /* SPI 100 Speed Config */
+ tmp = mmio_readw(spibar + 0x22); /* SPI 100 Speed Config */
msg_pdbg("NormSpeedNew is %s\n", spispeeds[(tmp >> 12) & 0xf].name);
msg_pdbg("FastSpeedNew is %s\n", spispeeds[(tmp >> 8) & 0xf].name);
msg_pdbg("AltSpeedNew is %s\n", spispeeds[(tmp >> 4) & 0xf].name);
@@ -483,15 +497,15 @@
}
} else {
if (amd_gen >= CHIPSET_SB89XX && amd_gen <= CHIPSET_HUDSON234) {
- bool fast_read = (mmio_readl(sb600_spibar + 0x00) >> 18) & 0x1;
+ bool fast_read = (mmio_readl(spibar + 0x00) >> 18) & 0x1;
msg_pdbg("Fast Reads are %sabled\n", fast_read ? "en" : "dis");
if (fast_read) {
msg_pdbg("Disabling them temporarily.\n");
- rmmio_writel(mmio_readl(sb600_spibar + 0x00) & ~(0x1 << 18),
- sb600_spibar + 0x00);
+ rmmio_writel(mmio_readl(spibar + 0x00) & ~(0x1 << 18),
+ spibar + 0x00);
}
}
- tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3;
+ tmp = (mmio_readb(spibar + 0xd) >> 4) & 0x3;
msg_pdbg("NormSpeed is %s\n", spispeeds[tmp].name);
}
return set_speed(dev, amd_gen, &spispeeds[spispeed_idx]);
@@ -581,14 +595,14 @@
return 0;
/* Physical memory has to be mapped at page (4k) boundaries. */
- sb600_spibar = rphysmap("SB600 SPI registers", tmp & 0xfffff000, 0x1000);
- if (sb600_spibar == ERROR_PTR)
+ g_sb600_spibar = rphysmap("SB600 SPI registers", tmp & 0xfffff000, 0x1000);
+ if (g_sb600_spibar == ERROR_PTR)
return ERROR_FATAL;
/* The low bits of the SPI base address are used as offset into
* the mapped page.
*/
- sb600_spibar += tmp & 0xfff;
+ g_sb600_spibar += tmp & 0xfff;
int amd_gen = determine_generation(dev);
if (amd_gen < 0)
@@ -649,7 +663,8 @@
*
* <1> see handle_speed
*/
- tmp = mmio_readl(sb600_spibar + 0x00);
+ uint8_t *spibar = get_spibar();
+ tmp = mmio_readl(spibar + 0x00);
msg_pdbg("(0x%08" PRIx32 ") SpiArbEnable=%i", tmp, (tmp >> 19) & 0x1);
if (amd_gen >= CHIPSET_YANGTZE)
msg_pdbg(", IllegalAccess=%i", (tmp >> 21) & 0x1);
@@ -679,7 +694,7 @@
}
if (amd_gen >= CHIPSET_SB89XX) {
- tmp = mmio_readb(sb600_spibar + 0x1D);
+ tmp = mmio_readb(spibar + 0x1D);
msg_pdbg("Using SPI_CS%d\n", tmp & 0x3);
/* FIXME: Handle SpiProtect* configuration on Yangtze. */
}
--
To view, visit https://review.coreboot.org/c/flashrom/+/36433
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: Ic26372b9c1baebb20716eea1db1e942239ed3e48
Gerrit-Change-Number: 36433
Gerrit-PatchSet: 1
Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org>
Gerrit-MessageType: newchange
2
2