mail.coreboot.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2024
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
List overview
Download
flashrom-gerrit
August 2020
----- 2024 -----
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
flashrom-gerrit@flashrom.org
1 participants
76 discussions
Start a n
N
ew thread
Change in ...flashrom[master]: manibuilder: Add newer targets for Alpine, CentOS, Fedora
by Nico Huber (Code Review)
16 Mar '21
16 Mar '21
Nico Huber has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/33342
Change subject: manibuilder: Add newer targets for Alpine, CentOS, Fedora ...................................................................... manibuilder: Add newer targets for Alpine, CentOS, Fedora Change-Id: I6c9939601abd3bd67424b8fa9a5ec800e50e3a51 Signed-off-by: Nico Huber <nico.h(a)gmx.de> --- M util/manibuilder/Makefile.targets 1 file changed, 8 insertions(+), 2 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/42/33342/1 diff --git a/util/manibuilder/Makefile.targets b/util/manibuilder/Makefile.targets index 044b0a4..f5a142c 100644 --- a/util/manibuilder/Makefile.targets +++ b/util/manibuilder/Makefile.targets @@ -1,8 +1,12 @@ ANITA_TAGS := anita\:amd64 anita\:i386 MULTIARCH_TAGS := \ + centos\:7.6-armhfp-clean centos\:7.6-amd64-clean \ centos\:7.3-aarch64-clean centos\:7.3-amd64-clean \ centos\:7.2-amd64-clean \ + $(foreach a,x86_64 s390x aarch64, fedora\:30-$(a)) \ + $(foreach a,x86_64 s390x ppc64le aarch64, fedora\:29-$(a)) \ + fedora\:28-armhfp \ $(foreach a,x86_64 ppc64le aarch64, \ $(foreach v,25 24, \ fedora\:$(v)-$(a))) \ @@ -14,7 +18,7 @@ ubuntu-debootstrap\:$(a)-$(v))) \ ubuntu-debootstrap\:powerpc-xenial \ $(foreach a,aarch64 armhf amd64 i386, \ - $(foreach v,v3.8 v3.7 v3.6, \ + $(foreach v,v3.9 v3.8 v3.7 v3.6, \ alpine\:$(a)-$(v))) \ OTHER_TAGS := djgpp\:6.1.0 @@ -41,6 +45,7 @@ DEFAULT_TAGS := \ anita\:amd64 \ djgpp\:6.1.0 \ + fedora\:30-aarch64 \ fedora\:25-x86_64 \ fedora\:25-ppc64le \ fedora\:25-aarch64 \ @@ -59,8 +64,9 @@ ubuntu-debootstrap\:amd64-xenial \ ubuntu-debootstrap\:powerpc-xenial \ ubuntu-debootstrap\:amd64-bionic \ - alpine\:amd64-v3.7 \ + alpine\:aarch64-v3.9 \ alpine\:amd64-v3.8 \ + alpine\:amd64-v3.7 \ # also run all native tests by default DEFAULT_TAGS += $(filter-out $(DEFAULT_TAGS),$(NATIVE_TAGS)) -- To view, visit
https://review.coreboot.org/c/flashrom/+/33342
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I6c9939601abd3bd67424b8fa9a5ec800e50e3a51 Gerrit-Change-Number: 33342 Gerrit-PatchSet: 1 Gerrit-Owner: Nico Huber <nico.h(a)gmx.de> Gerrit-MessageType: newchange
3
3
0
0
Change in ...flashrom[master]: manibuilder: Allow warnings in NetBSD and CentOS builds
by Nico Huber (Code Review)
16 Mar '21
16 Mar '21
Nico Huber has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/33339
Change subject: manibuilder: Allow warnings in NetBSD and CentOS builds ...................................................................... manibuilder: Allow warnings in NetBSD and CentOS builds Their old compilers stumble because of `-Wmissing-braces`. Change-Id: Ia9ee17fd1f0c8b191091f89ffbf44329c6521d7d Signed-off-by: Nico Huber <nico.h(a)gmx.de> --- M util/manibuilder/Makefile 1 file changed, 2 insertions(+), 1 deletion(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/39/33339/1 diff --git a/util/manibuilder/Makefile b/util/manibuilder/Makefile index d93abec..7f76c33 100644 --- a/util/manibuilder/Makefile +++ b/util/manibuilder/Makefile @@ -53,9 +53,10 @@ djgpp\:6.1.0: LIBS_BASE=../ djgpp\:6.1.0: MAKEARGS+=strip CONFIG_JLINK_SPI=no $(ANITA_TAGS): MAKECMD=gmake -$(ANITA_TAGS): MAKEARGS+=CONFIG_JLINK_SPI=no +$(ANITA_TAGS): MAKEARGS+=CONFIG_JLINK_SPI=no WARNERROR=no $(filter alpine% centos%,$(MULTIARCH_TAGS)): MAKEARGS+=CONFIG_JLINK_SPI=no $(filter %-xenial %-stretch,$(MULTIARCH_TAGS)): MAKEARGS+=CONFIG_JLINK_SPI=no +$(filter centos%,$(MULTIARCH_TAGS)): MAKEARGS+=WARNERROR=no $(ALL_TAGS): export QUIET_SETUP=$(QUIET_TEST) $(ALL_TAGS): %: %-check-build $(QUIET_TEST)docker rm -f mani_$(call ident,$*) >/dev/null 2>&1 || true -- To view, visit
https://review.coreboot.org/c/flashrom/+/33339
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: Ia9ee17fd1f0c8b191091f89ffbf44329c6521d7d Gerrit-Change-Number: 33339 Gerrit-PatchSet: 1 Gerrit-Owner: Nico Huber <nico.h(a)gmx.de> Gerrit-MessageType: newchange
3
3
0
0
Change in ...flashrom[master]: manibuilder: Add libjaylink where possible, disable where not
by Nico Huber (Code Review)
16 Mar '21
16 Mar '21
Nico Huber has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/33338
Change subject: manibuilder: Add libjaylink where possible, disable where not ...................................................................... manibuilder: Add libjaylink where possible, disable where not Change-Id: I2f7aebe602ebdb0a4748640e281b9a92146f0ca8 Signed-off-by: Nico Huber <nico.h(a)gmx.de> --- M util/manibuilder/Dockerfile.debian-debootstrap M util/manibuilder/Dockerfile.fedora M util/manibuilder/Dockerfile.ubuntu-debootstrap M util/manibuilder/Makefile 4 files changed, 10 insertions(+), 4 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/38/33338/1 diff --git a/util/manibuilder/Dockerfile.debian-debootstrap b/util/manibuilder/Dockerfile.debian-debootstrap index c6648e8..ce201f8 100644 --- a/util/manibuilder/Dockerfile.debian-debootstrap +++ b/util/manibuilder/Dockerfile.debian-debootstrap @@ -5,8 +5,9 @@ apt-get -qq update && \ apt-get -qq upgrade && \ apt-get -qq dist-upgrade && \ - apt-get -qqy install gcc make git doxygen ccache \ + apt-get -qqy install gcc make git doxygen ccache pkg-config \ libpci-dev libusb-dev libftdi-dev libusb-1.0-0-dev && \ + { apt-get -qqy install libjaylink-dev || true; } && \ apt-get clean USER mani diff --git a/util/manibuilder/Dockerfile.fedora b/util/manibuilder/Dockerfile.fedora index 927e487..319039b 100644 --- a/util/manibuilder/Dockerfile.fedora +++ b/util/manibuilder/Dockerfile.fedora @@ -3,7 +3,8 @@ RUN \ useradd -p locked -m mani && \ dnf install -q -y git gcc ccache make \ - pciutils-devel libusb-devel libusbx-devel libftdi-devel && \ + pciutils-devel libusb-devel libusbx-devel libftdi-devel \ + libjaylink-devel && \ dnf clean -q -y all USER mani diff --git a/util/manibuilder/Dockerfile.ubuntu-debootstrap b/util/manibuilder/Dockerfile.ubuntu-debootstrap index f50047c..fe3ad1e 100644 --- a/util/manibuilder/Dockerfile.ubuntu-debootstrap +++ b/util/manibuilder/Dockerfile.ubuntu-debootstrap @@ -18,8 +18,9 @@ apt-get -qq update && \ apt-get -qq upgrade && \ apt-get -qq dist-upgrade && \ - apt-get -qqy install gcc make git doxygen ccache \ + apt-get -qqy install gcc make git doxygen ccache pkg-config \ libpci-dev libusb-dev libftdi-dev libusb-1.0-0-dev && \ + { apt-get -qqy install libjaylink-dev || true; } && \ apt-get clean USER mani diff --git a/util/manibuilder/Makefile b/util/manibuilder/Makefile index 9b87f45..d93abec 100644 --- a/util/manibuilder/Makefile +++ b/util/manibuilder/Makefile @@ -51,8 +51,11 @@ 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: MAKEARGS+=strip +djgpp\:6.1.0: MAKEARGS+=strip CONFIG_JLINK_SPI=no $(ANITA_TAGS): MAKECMD=gmake +$(ANITA_TAGS): MAKEARGS+=CONFIG_JLINK_SPI=no +$(filter alpine% centos%,$(MULTIARCH_TAGS)): MAKEARGS+=CONFIG_JLINK_SPI=no +$(filter %-xenial %-stretch,$(MULTIARCH_TAGS)): MAKEARGS+=CONFIG_JLINK_SPI=no $(ALL_TAGS): export QUIET_SETUP=$(QUIET_TEST) $(ALL_TAGS): %: %-check-build $(QUIET_TEST)docker rm -f mani_$(call ident,$*) >/dev/null 2>&1 || true -- To view, visit
https://review.coreboot.org/c/flashrom/+/33338
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I2f7aebe602ebdb0a4748640e281b9a92146f0ca8 Gerrit-Change-Number: 33338 Gerrit-PatchSet: 1 Gerrit-Owner: Nico Huber <nico.h(a)gmx.de> Gerrit-MessageType: newchange
3
3
0
0
Change in ...flashrom[master]: manibuilder: Enable CONFIG_EVERYTHING=yes
by Nico Huber (Code Review)
16 Mar '21
16 Mar '21
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
0
0
Change in flashrom[master]: libflashrom: add querying functions with meson integration
by Michał Żygowski (Code Review)
13 Mar '21
13 Mar '21
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
0
0
Change in flashrom[master]: pcidev: Fix typo
by Martijn Berger (Code Review)
07 Mar '21
07 Mar '21
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
0
0
Change in flashrom[master]: Add writeprotect support
by Edward O'Callaghan (Code Review)
28 Feb '21
28 Feb '21
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
0
0
Change in flashrom[master]: cbtable.c: don't assume high addresses can fully map 1 MiB
by Edward O'Callaghan (Code Review)
25 Jan '21
25 Jan '21
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
0
0
Change in flashrom[master]: flashchips: upstream AT25SF128A from chromiumos
by Alan Green (Code Review)
05 Jan '21
05 Jan '21
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
0
0
Change in flashrom[master]: flashchips.c: Take GD25LQ128C/D from downstream
by Alan Green (Code Review)
05 Jan '21
05 Jan '21
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
0
0
← Newer
1
2
3
4
5
6
7
8
Older →
Jump to page:
1
2
3
4
5
6
7
8
Results per page:
10
25
50
100
200