flashrom-gerrit
Threads by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
July 2020
- 1 participants
- 110 discussions
Change in flashrom[master]: chipset_enable.c: Add CMP-H IDs
by Gaggery Tsai (Code Review) April 26, 2021
by Gaggery Tsai (Code Review) April 26, 2021
April 26, 2021
Gaggery Tsai has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/37677 )
Change subject: chipset_enable.c: Add CMP-H IDs
......................................................................
chipset_enable.c: Add CMP-H IDs
This patch adds CMP-H support. They are HM470, WM490, QM480,
W480, H470, Z490 and Q470.
TEST=build flashrom and run on CML-S with CMP-H
flashrom -p internal -w ./coreboot.rom
reboot and check the code is flashed correctly
Signed-off-by: Gaggery Tsai <gaggery.tsai(a)intel.com>
Change-Id: Ic7f04fc5cbe3422cbd219c46586c32fc847c921f
---
M chipset_enable.c
1 file changed, 7 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/77/37677/1
diff --git a/chipset_enable.c b/chipset_enable.c
index b55852c..62a19af 100644
--- a/chipset_enable.c
+++ b/chipset_enable.c
@@ -2046,6 +2046,13 @@
{0x8086, 0xa30c, B_S, NT, "Intel", "QM370", enable_flash_pch300},
{0x8086, 0xa30d, B_S, NT, "Intel", "HM370", enable_flash_pch300},
{0x8086, 0xa30e, B_S, DEP, "Intel", "CM246", enable_flash_pch300},
+ {0x8086, 0x068d, B_S, NT, "Intel", "HM470", enable_flash_pch300},
+ {0x8086, 0x068e, B_S, NT, "Intel", "WM490", enable_flash_pch300},
+ {0x8086, 0x068c, B_S, NT, "Intel", "QM480", enable_flash_pch300},
+ {0x8086, 0x0697, B_S, NT, "Intel", "W480", enable_flash_pch300},
+ {0x8086, 0x0684, B_S, NT, "Intel", "H470", enable_flash_pch300},
+ {0x8086, 0x0685, B_S, NT, "Intel", "Z490", enable_flash_pch300},
+ {0x8086, 0x0687, B_S, NT, "Intel", "Q470", enable_flash_pch300},
#endif
{0},
};
--
To view, visit https://review.coreboot.org/c/flashrom/+/37677
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: Ic7f04fc5cbe3422cbd219c46586c32fc847c921f
Gerrit-Change-Number: 37677
Gerrit-PatchSet: 1
Gerrit-Owner: Gaggery Tsai <gaggery.tsai(a)intel.com>
Gerrit-MessageType: newchange
7
25
Change in flashrom[master]: ft2232_spi.c - Pack read cmd and nCS in one ftdi_write_data() call
by Simon Buhrow (Code Review) April 23, 2021
by Simon Buhrow (Code Review) April 23, 2021
April 23, 2021
Simon Buhrow has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/40748 )
Change subject: ft2232_spi.c - Pack read cmd and nCS in one ftdi_write_data() call
......................................................................
ft2232_spi.c - Pack read cmd and nCS in one ftdi_write_data() call
Every ftdi_write_data() call is quite time consuming as the ftdi-chip takes 2-3ms to respond.
As the comment already says: Minimize USB transfers by packing as many commands as possible together.
As I cannot see any reason to put read cmd and deassertion of nCS into seperated ftdi calls,
I packed them together to save programming time.
Signed-off-by: Simon Buhrow <simon.buhrow(a)posteo.de>
Change-Id: I3f76da3dd0124a0deec7f937dbc4b7dc488c8004
---
M ft2232_spi.c
1 file changed, 38 insertions(+), 40 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/48/40748/1
diff --git a/ft2232_spi.c b/ft2232_spi.c
index 9f4c7f0..b2f5e40 100644
--- a/ft2232_spi.c
+++ b/ft2232_spi.c
@@ -398,7 +398,10 @@
msg_perr("Unable to set latency timer (%s).\n", ftdi_get_error_string(ftdic));
}
- if (ftdi_write_data_set_chunksize(ftdic, 270)) {
+ if (ftdi_write_data_set_chunksize(ftdic, 280)) {
+ /* 280 Byte = (9 Byte CMD + 1 Byte WREN) + (9 Byte CMD + 1 Byte op) + 4 Byte Addr
+ * + 256 Byte PageWrite-data
+ with op: PageProgram or Erase; CMD: FTDI-Chip commands*/
msg_perr("Unable to set chunk size (%s).\n", ftdi_get_error_string(ftdic));
}
@@ -467,31 +470,26 @@
struct ftdi_context *ftdic = &ftdic_context;
static unsigned char *buf = NULL;
/* failed is special. We use bitwise ops, but it is essentially bool. */
- int i = 0, ret = 0, failed = 0;
- size_t bufsize;
- static size_t oldbufsize = 0;
+ static int i = 0;
+ int ret = 0, failed = 0;
if (writecnt > 65536 || readcnt > 65536)
return SPI_INVALID_LENGTH;
- /* buf is not used for the response from the chip. */
- bufsize = max(writecnt + 9, 260 + 9);
- /* Never shrink. realloc() calls are expensive. */
- if (!buf || bufsize > oldbufsize) {
- buf = realloc(buf, bufsize);
+ if (!buf) {
+ /* set buf size to hardware buffer size of ftdi*/
+ buf = realloc(buf, 4096);
if (!buf) {
msg_perr("Out of memory!\n");
/* TODO: What to do with buf? */
return SPI_GENERIC_ERROR;
}
- oldbufsize = bufsize;
}
/*
* Minimize USB transfers by packing as many commands as possible
- * together. If we're not expecting to read, we can assert CS#, write,
- * and deassert CS# all in one shot. If reading, we do three separate
- * operations.
+ * together. We can assert CS#, write, (read,)
+ * and deassert CS# all in one shot.
*/
msg_pspew("Assert CS#\n");
buf[i++] = SET_BITS_LOW;
@@ -506,44 +504,44 @@
i += writecnt;
}
- /*
- * Optionally terminate this batch of commands with a
- * read command, then do the fetch of the results.
- */
+ /* A optionally read command */
if (readcnt) {
buf[i++] = MPSSE_DO_READ;
buf[i++] = (readcnt - 1) & 0xff;
buf[i++] = ((readcnt - 1) >> 8) & 0xff;
- ret = send_buf(ftdic, buf, i);
- failed = ret;
- /* We can't abort here, we still have to deassert CS#. */
- if (ret)
- msg_perr("send_buf failed before read: %i\n", ret);
- i = 0;
- if (ret == 0) {
- /*
- * FIXME: This is unreliable. There's no guarantee that
- * we read the response directly after sending the read
- * command. We may be scheduled out etc.
- */
- ret = get_buf(ftdic, readarr, readcnt);
- failed |= ret;
- /* We can't abort here either. */
- if (ret)
- msg_perr("get_buf failed: %i\n", ret);
- }
}
msg_pspew("De-assert CS#\n");
buf[i++] = SET_BITS_LOW;
buf[i++] = cs_bits;
buf[i++] = pindir;
- ret = send_buf(ftdic, buf, i);
- failed |= ret;
- if (ret)
- msg_perr("send_buf failed at end: %i\n", ret);
- return failed ? -1 : 0;
+ if (writearr[0] == JEDEC_WREN) {
+ /* Return to get second op (Program or Erase) without resetting buf nor i*/
+ return 0;
+ } else {
+ ret = send_buf(ftdic, buf, i);
+ failed |= ret;
+ if (ret)
+ msg_perr("send_buf failed: %i\n", ret);
+ i = 0;
+
+ if (readcnt) {
+ if (ret == 0) {
+ /*
+ * FIXME: This is unreliable. There's no guarantee that
+ * we read the response directly after sending the read
+ * command. We may be scheduled out etc.
+ */
+ ret = get_buf(ftdic, readarr, readcnt);
+ failed |= ret;
+ /* We can't abort here either. */
+ if (ret)
+ msg_perr("get_buf failed: %i\n", ret);
+ }
+ }
+ return failed ? -1 : 0;
+ }
}
#endif
--
To view, visit https://review.coreboot.org/c/flashrom/+/40748
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I3f76da3dd0124a0deec7f937dbc4b7dc488c8004
Gerrit-Change-Number: 40748
Gerrit-PatchSet: 1
Gerrit-Owner: Simon Buhrow
Gerrit-MessageType: newchange
3
10
Change in flashrom[master]: raiden_debug: Upstream ChromiumOS servo debug board prog
by Edward O'Callaghan (Code Review) March 29, 2021
by Edward O'Callaghan (Code Review) March 29, 2021
March 29, 2021
Edward O'Callaghan has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/38209 )
Change subject: raiden_debug: Upstream ChromiumOS servo debug board prog
......................................................................
raiden_debug: Upstream ChromiumOS servo debug board prog
Initial check-in of the Raiden debugger programmer.
Squash in,
usb_device: needed for raiden_debug prog
BUG=b:143389556
BRANCH=none
TEST=builds
Change-Id: Ifad273a708acea4de797a0808be58960635a8864
Signed-off-by: Edward O'Callaghan <quasisec(a)google.com>
---
M Makefile
M flashrom.c
M meson.build
M meson_options.txt
M programmer.h
A raiden_debug_spi.c
A usb_device.c
A usb_device.h
8 files changed, 977 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/09/38209/1
diff --git a/Makefile b/Makefile
index 7242b09..e8ca7a8 100644
--- a/Makefile
+++ b/Makefile
@@ -226,6 +226,11 @@
else
override CONFIG_RAYER_SPI = no
endif
+ifeq ($(CONFIG_RAIDEN), yes)
+UNSUPPORTED_FEATURES += CONFIG_RAIDEN=yes
+else
+override CONFIG_RAIDEN = no
+endif
ifeq ($(CONFIG_NIC3COM), yes)
UNSUPPORTED_FEATURES += CONFIG_NIC3COM=yes
else
@@ -607,6 +612,9 @@
# RayeR SPIPGM hardware support
CONFIG_RAYER_SPI ?= yes
+# ChromiumOS servo DUT debug board hardware support
+CONFIG_RAIDEN ?= yes
+
# PonyProg2000 SPI hardware support
CONFIG_PONY_SPI ?= yes
@@ -817,6 +825,11 @@
NEED_RAW_ACCESS += CONFIG_RAYER_SPI
endif
+ifeq ($(CONFIG_RAIDEN), yes)
+FEATURE_CFLAGS += -D'CONFIG_RAIDEN=1'
+PROGRAMMER_OBJS += raiden_debug_spi.o usb_device.o
+endif
+
ifeq ($(CONFIG_PONY_SPI), yes)
FEATURE_CFLAGS += -D'CONFIG_PONY_SPI=1'
PROGRAMMER_OBJS += pony_spi.o
diff --git a/flashrom.c b/flashrom.c
index e540027..e541b68 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -133,6 +133,18 @@
},
#endif
+#if CONFIG_RAIDEN == 1
+ {
+ .name = "raiden_debug",
+ .type = USB,
+ .devs.note = "All programmer devices speaking the raiden protocol\n",
+ .init = raiden_debug_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
#if CONFIG_DRKAISER == 1
{
.name = "drkaiser",
diff --git a/meson.build b/meson.build
index 375089c..88b14cf 100644
--- a/meson.build
+++ b/meson.build
@@ -42,6 +42,7 @@
config_dummy = get_option('config_dummy')
config_ft2232_spi = get_option('config_ft2232_spi')
config_gfxnvidia = get_option('config_gfxnvidia')
+config_raiden = get_option('config_raiden')
config_internal = get_option('config_internal')
config_it8212 = get_option('config_it8212')
config_linux_mtd = get_option('config_linux_mtd')
@@ -104,6 +105,7 @@
config_atavia = false
config_drkaiser = false
config_gfxnvidia = false
+ config_raiden = false
config_internal = false
config_it8212 = false
config_nic3com = false
@@ -170,6 +172,11 @@
srcs += 'gfxnvidia.c'
cargs += '-DCONFIG_GFXNVIDIA=1'
endif
+if config_raiden
+ srcs += 'raiden_debug_spi.c'
+ srcs += 'usb_device.c'
+ cargs += '-DCONFIG_RAIDEN=1'
+endif
if config_internal
srcs += 'board_enable.c'
srcs += 'cbtable.c'
diff --git a/meson_options.txt b/meson_options.txt
index b7633d0..4334814 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -13,6 +13,7 @@
option('config_dummy', type : 'boolean', value : true, description : 'dummy tracing')
option('config_ft2232_spi', type : 'boolean', value : true, description : 'FT2232 SPI dongles')
option('config_gfxnvidia', type : 'boolean', value : true, description : 'NVIDIA graphics cards')
+option('config_raiden', type : 'boolean', value : true, description : 'ChromiumOS Servo DUT debug board')
option('config_internal', type : 'boolean', value : true, description : 'internal/onboard')
option('config_internal_dmi', type : 'boolean', value : true, description : 'Use internal DMI parser')
option('config_it8212', type : 'boolean', value : true, description : 'ITE IT8212F PATA')
diff --git a/programmer.h b/programmer.h
index 3cf53b9..08500c6 100644
--- a/programmer.h
+++ b/programmer.h
@@ -43,6 +43,9 @@
#if CONFIG_GFXNVIDIA == 1
PROGRAMMER_GFXNVIDIA,
#endif
+#if CONFIG_RAIDEN == 1
+ PROGRAMMER_RAIDEN,
+#endif
#if CONFIG_DRKAISER == 1
PROGRAMMER_DRKAISER,
#endif
@@ -401,6 +404,11 @@
extern const struct dev_entry gfx_nvidia[];
#endif
+/* raiden_debug_spi.c */
+#if CONFIG_RAIDEN == 1
+int raiden_debug_spi_init(void);
+#endif
+
/* drkaiser.c */
#if CONFIG_DRKAISER == 1
int drkaiser_init(void);
diff --git a/raiden_debug_spi.c b/raiden_debug_spi.c
new file mode 100644
index 0000000..9af0f7d
--- /dev/null
+++ b/raiden_debug_spi.c
@@ -0,0 +1,369 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ */
+
+/*
+ * This SPI flash programming interface is designed to talk to a Chromium OS
+ * device over a Raiden USB connection. The USB connection is routed to a
+ * microcontroller running an image compiled from:
+ *
+ * https://chromium.googlesource.com/chromiumos/platform/ec
+ *
+ * The protocol for the USB-SPI bridge is documented in the following file in
+ * that respository:
+ *
+ * chip/stm32/usb_spi.c
+ */
+
+#include "programmer.h"
+#include "spi.h"
+#include "usb_device.h"
+
+#include <libusb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define GOOGLE_VID 0x18D1
+#define GOOGLE_RAIDEN_SPI_SUBCLASS 0x51
+#define GOOGLE_RAIDEN_SPI_PROTOCOL 0x01
+
+enum raiden_debug_spi_request {
+ RAIDEN_DEBUG_SPI_REQ_ENABLE = 0x0000,
+ RAIDEN_DEBUG_SPI_REQ_DISABLE = 0x0001,
+ RAIDEN_DEBUG_SPI_REQ_ENABLE_AP = 0x0002,
+ RAIDEN_DEBUG_SPI_REQ_ENABLE_EC = 0x0003,
+};
+
+#define PACKET_HEADER_SIZE 2
+#define MAX_PACKET_SIZE 64
+
+/*
+ * This timeout is so large because the Raiden SPI timeout is 800ms.
+ */
+#define TRANSFER_TIMEOUT_MS 1000
+
+struct usb_device *device = NULL;
+uint8_t in_endpoint = 0;
+uint8_t out_endpoint = 0;
+
+static int send_command(struct flashctx *flash,
+ unsigned int write_count,
+ unsigned int read_count,
+ const unsigned char *write_buffer,
+ unsigned char *read_buffer)
+{
+ uint8_t buffer[MAX_PACKET_SIZE];
+ int transferred;
+
+ if (write_count > MAX_PACKET_SIZE - PACKET_HEADER_SIZE) {
+ msg_perr("Raiden: invalid write_count of %d\n", write_count);
+ return SPI_INVALID_LENGTH;
+ }
+
+ if (read_count > MAX_PACKET_SIZE - PACKET_HEADER_SIZE) {
+ msg_perr("Raiden: invalid read_count of %d\n", read_count);
+ return SPI_INVALID_LENGTH;
+ }
+
+ buffer[0] = write_count;
+ buffer[1] = read_count;
+
+ memcpy(buffer + PACKET_HEADER_SIZE, write_buffer, write_count);
+
+ CHECK(LIBUSB(libusb_bulk_transfer(device->handle,
+ out_endpoint,
+ buffer,
+ write_count + PACKET_HEADER_SIZE,
+ &transferred,
+ TRANSFER_TIMEOUT_MS)),
+ "Raiden: OUT transfer failed\n"
+ " write_count = %d\n"
+ " read_count = %d\n",
+ write_count,
+ read_count);
+
+ if ((unsigned) transferred != write_count + PACKET_HEADER_SIZE) {
+ msg_perr("Raiden: Write failure (wrote %d, expected %d)\n",
+ transferred, write_count + PACKET_HEADER_SIZE);
+ return 0x10001;
+ }
+
+ CHECK(LIBUSB(libusb_bulk_transfer(device->handle,
+ in_endpoint,
+ buffer,
+ read_count + PACKET_HEADER_SIZE,
+ &transferred,
+ TRANSFER_TIMEOUT_MS)),
+ "Raiden: IN transfer failed\n"
+ " write_count = %d\n"
+ " read_count = %d\n",
+ write_count,
+ read_count);
+
+ if ((unsigned) transferred != read_count + PACKET_HEADER_SIZE) {
+ msg_perr("Raiden: Read failure (read %d, expected %d)\n",
+ transferred, read_count + PACKET_HEADER_SIZE);
+ return 0x10002;
+ }
+
+ memcpy(read_buffer, buffer + PACKET_HEADER_SIZE, read_count);
+
+ return buffer[0] | (buffer[1] << 8);
+}
+
+/*
+ * Unfortunately there doesn't seem to be a way to specify the maximum number
+ * of bytes that your SPI device can read/write, these values are the maximum
+ * data chunk size that flashrom will package up with an additional five bytes
+ * of command for the flash device, resulting in a 62 byte packet, that we then
+ * add two bytes to in either direction, making our way up to the 64 byte
+ * maximum USB packet size for the device.
+ *
+ * The largest command that flashrom generates is the byte program command, so
+ * we use that command header maximum size here.
+ */
+#define MAX_DATA_SIZE (MAX_PACKET_SIZE - \
+ PACKET_HEADER_SIZE - \
+ JEDEC_BYTE_PROGRAM_OUTSIZE)
+
+static const struct spi_master spi_master_raiden_debug = {
+ .features = SPI_MASTER_4BA,
+ .max_data_read = MAX_DATA_SIZE,
+ .max_data_write = MAX_DATA_SIZE,
+ .command = send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+};
+
+static int match_endpoint(struct libusb_endpoint_descriptor const *descriptor,
+ enum libusb_endpoint_direction direction)
+{
+ return (((descriptor->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) ==
+ direction) &&
+ ((descriptor->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) ==
+ LIBUSB_TRANSFER_TYPE_BULK));
+}
+
+static int find_endpoints(struct usb_device *dev, uint8_t *in_ep, uint8_t *out_ep)
+{
+ int i;
+ int in_count = 0;
+ int out_count = 0;
+
+ for (i = 0; i < dev->interface_descriptor->bNumEndpoints; i++) {
+ struct libusb_endpoint_descriptor const *endpoint =
+ &dev->interface_descriptor->endpoint[i];
+
+ if (match_endpoint(endpoint, LIBUSB_ENDPOINT_IN)) {
+ in_count++;
+ *in_ep = endpoint->bEndpointAddress;
+ } else if (match_endpoint(endpoint, LIBUSB_ENDPOINT_OUT)) {
+ out_count++;
+ *out_ep = endpoint->bEndpointAddress;
+ }
+ }
+
+ if (in_count != 1 || out_count != 1) {
+ msg_perr("Raiden: Failed to find one IN and one OUT endpoint\n"
+ " found %d IN and %d OUT endpoints\n",
+ in_count,
+ out_count);
+ return 1;
+ }
+
+ msg_pdbg("Raiden: Found IN endpoint = 0x%02x\n", *in_ep);
+ msg_pdbg("Raiden: Found OUT endpoint = 0x%02x\n", *out_ep);
+
+ return 0;
+}
+
+static int shutdown(void * data)
+{
+ CHECK(LIBUSB(libusb_control_transfer(
+ device->handle,
+ LIBUSB_ENDPOINT_OUT |
+ LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_RECIPIENT_INTERFACE,
+ RAIDEN_DEBUG_SPI_REQ_DISABLE,
+ 0,
+ device->interface_descriptor->bInterfaceNumber,
+ NULL,
+ 0,
+ TRANSFER_TIMEOUT_MS)),
+ "Raiden: Failed to disable SPI bridge\n");
+
+ usb_device_free(device);
+
+ device = NULL;
+ libusb_exit(NULL);
+
+ return 0;
+}
+
+static int get_target()
+{
+ int request_enable = RAIDEN_DEBUG_SPI_REQ_ENABLE;
+
+ char *target_str = extract_programmer_param("target");
+ if (target_str) {
+ if (!strcasecmp(target_str, "ap"))
+ request_enable = RAIDEN_DEBUG_SPI_REQ_ENABLE_AP;
+ else if (!strcasecmp(target_str, "ec"))
+ request_enable = RAIDEN_DEBUG_SPI_REQ_ENABLE_EC;
+ else {
+ msg_perr("Invalid target: %s\n", target_str);
+ request_enable = -1;
+ }
+ }
+ free(target_str);
+
+ return request_enable;
+}
+
+static void free_dev_list(struct usb_device **dev_lst)
+{
+ struct usb_device *dev = *dev_lst;
+ /* free devices we don't care about */
+ dev = dev->next;
+ while (dev)
+ dev = usb_device_free(dev);
+}
+
+int raiden_debug_spi_init(void)
+{
+ struct usb_match match;
+ char *serial = extract_programmer_param("serial");
+ struct usb_device *current;
+ int found = 0;
+
+ int request_enable = get_target();
+ if (request_enable < 0)
+ return 1;
+
+ usb_match_init(&match);
+
+ usb_match_value_default(&match.vid, GOOGLE_VID);
+ usb_match_value_default(&match.class, LIBUSB_CLASS_VENDOR_SPEC);
+ usb_match_value_default(&match.subclass, GOOGLE_RAIDEN_SPI_SUBCLASS);
+ usb_match_value_default(&match.protocol, GOOGLE_RAIDEN_SPI_PROTOCOL);
+
+ CHECK(LIBUSB(libusb_init(NULL)), "Raiden: libusb_init failed\n");
+
+ CHECK(usb_device_find(&match, ¤t),
+ "Raiden: Failed to find devices\n");
+
+ while (current) {
+ device = current;
+
+ if (find_endpoints(device, &in_endpoint, &out_endpoint)) {
+ msg_pdbg("Raiden: Failed to find valid endpoints on device");
+ usb_device_show(" ", current);
+ goto loop_end;
+ }
+
+ if (usb_device_claim(device)) {
+ msg_pdbg("Raiden: Failed to claim USB device");
+ usb_device_show(" ", current);
+ goto loop_end;
+ }
+
+ if (!serial) {
+ found = 1;
+ goto loop_end;
+ } else {
+ unsigned char dev_serial[32];
+ struct libusb_device_descriptor descriptor;
+ int rc;
+
+ memset(dev_serial, 0, sizeof(dev_serial));
+
+ if (libusb_get_device_descriptor(device->device, &descriptor)) {
+ msg_pdbg("USB: Failed to get device descriptor.\n");
+ goto loop_end;
+ }
+
+ rc = libusb_get_string_descriptor_ascii(device->handle,
+ descriptor.iSerialNumber,
+ dev_serial,
+ sizeof(dev_serial));
+ if (rc < 0) {
+ LIBUSB(rc);
+ } else {
+ if (strcmp(serial, (char *)dev_serial)) {
+ msg_pdbg("Raiden: Serial number %s did not match device", serial);
+ usb_device_show(" ", current);
+ } else {
+ msg_pinfo("Raiden: Serial number %s matched device", serial);
+ usb_device_show(" ", current);
+ found = 1;
+ }
+ }
+ }
+
+loop_end:
+ if (found)
+ break;
+ else
+ current = usb_device_free(current);
+ }
+
+ if (!device || !found) {
+ msg_perr("Raiden: No usable device found.\n");
+ return 1;
+ }
+
+ free_dev_list(¤t);
+
+ CHECK(LIBUSB(libusb_control_transfer(
+ device->handle,
+ LIBUSB_ENDPOINT_OUT |
+ LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_RECIPIENT_INTERFACE,
+ request_enable,
+ 0,
+ device->interface_descriptor->bInterfaceNumber,
+ NULL,
+ 0,
+ TRANSFER_TIMEOUT_MS)),
+ "Raiden: Failed to enable SPI bridge\n");
+
+ register_spi_master(&spi_master_raiden_debug);
+ register_shutdown(shutdown, NULL);
+
+ return 0;
+}
diff --git a/usb_device.c b/usb_device.c
new file mode 100644
index 0000000..0909694
--- /dev/null
+++ b/usb_device.c
@@ -0,0 +1,366 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ */
+
+#include "programmer.h"
+#include "spi.h"
+#include "usb_device.h"
+
+#include <assert.h>
+#include <libusb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Possibly extract a programmer parameter and use it to initialize the given
+ * match value structure.
+ */
+static void usb_match_value_init(struct usb_match_value *match,
+ char const *parameter)
+{
+ char *string = extract_programmer_param(parameter);
+
+ match->name = parameter;
+
+ if (string) {
+ match->set = 1;
+ match->value = strtol(string, NULL, 0);
+ } else {
+ match->set = 0;
+ }
+
+ free(string);
+}
+
+#define USB_MATCH_VALUE_INIT(NAME) \
+ usb_match_value_init(&match->NAME, #NAME)
+
+void usb_match_init(struct usb_match *match)
+{
+ USB_MATCH_VALUE_INIT(vid);
+ USB_MATCH_VALUE_INIT(pid);
+ USB_MATCH_VALUE_INIT(bus);
+ USB_MATCH_VALUE_INIT(address);
+ USB_MATCH_VALUE_INIT(config);
+ USB_MATCH_VALUE_INIT(interface);
+ USB_MATCH_VALUE_INIT(altsetting);
+ USB_MATCH_VALUE_INIT(class);
+ USB_MATCH_VALUE_INIT(subclass);
+ USB_MATCH_VALUE_INIT(protocol);
+}
+
+void usb_match_value_default(struct usb_match_value *value,
+ long int default_value)
+{
+ if (value->set)
+ return;
+
+ value->set = 1;
+ value->value = default_value;
+}
+
+/*
+ * Match the value against a possible user supplied parameter.
+ *
+ * Return:
+ * 0: The user supplied the given parameter and it did not match the value.
+ * 1: Either the user didn't supply the parameter, or they did and it
+ * matches the given value.
+ */
+static int check_match(struct usb_match_value const *match_value, int value)
+{
+ int reject = match_value->set && (match_value->value != value);
+
+ if (reject)
+ msg_pdbg("USB: Rejecting device because %s = %d != %d\n",
+ match_value->name,
+ value,
+ match_value->value);
+
+ return !reject;
+}
+
+/*
+ * Allocate a copy of device and add it to the head of the devices list.
+ */
+static void add_device(struct usb_device *device,
+ struct usb_device **devices)
+{
+ struct usb_device *copy = malloc(sizeof(struct usb_device));
+
+ assert(copy != NULL);
+
+ *copy = *device;
+
+ copy->next = *devices;
+ *devices = copy;
+
+ libusb_ref_device(copy->device);
+}
+
+/*
+ * Look through the interfaces of the current device config for a match. Stop
+ * looking after the first valid match is found.
+ *
+ * Return:
+ * 0: No matching interface was found.
+ * 1: A complete match was found and added to the devices list.
+ */
+static int find_interface(struct usb_match const *match,
+ struct usb_device *current,
+ struct usb_device **devices)
+{
+ int i, j;
+
+ for (i = 0; i < current->config_descriptor->bNumInterfaces; ++i) {
+ struct libusb_interface const *interface;
+
+ interface = ¤t->config_descriptor->interface[i];
+
+ for (j = 0; j < interface->num_altsetting; ++j) {
+ struct libusb_interface_descriptor const *descriptor;
+
+ descriptor = &interface->altsetting[j];
+
+ if (check_match(&match->interface,
+ descriptor->bInterfaceNumber) &&
+ check_match(&match->altsetting,
+ descriptor->bAlternateSetting) &&
+ check_match(&match->class,
+ descriptor->bInterfaceClass) &&
+ check_match(&match->subclass,
+ descriptor->bInterfaceSubClass) &&
+ check_match(&match->protocol,
+ descriptor->bInterfaceProtocol)) {
+ current->interface_descriptor = descriptor;
+ add_device(current, devices);
+ msg_pdbg("USB: Found matching device\n");
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Look through the configs of the current device for a match. Stop looking
+ * after the first valid match is found.
+ *
+ * Return:
+ * 0: All configurations successfully checked, one may have been added to
+ * the list.
+ * non-zero: There was a failure while checking for a match.
+ */
+static int find_config(struct usb_match const *match,
+ struct usb_device *current,
+ struct libusb_device_descriptor const *device_descriptor,
+ struct usb_device **devices)
+{
+ int i;
+
+ for (i = 0; i < device_descriptor->bNumConfigurations; ++i) {
+ CHECK(LIBUSB(libusb_get_config_descriptor(
+ current->device,
+ i,
+ ¤t->config_descriptor)),
+ "USB: Failed to get config descriptor");
+
+ if (check_match(&match->config,
+ current->config_descriptor->
+ bConfigurationValue) &&
+ find_interface(match, current, devices))
+ break;
+
+ libusb_free_config_descriptor(current->config_descriptor);
+ }
+
+ return 0;
+}
+
+int usb_device_find(struct usb_match const *match, struct usb_device **devices)
+{
+ libusb_device **list;
+ size_t count;
+ size_t i;
+
+ *devices = NULL;
+
+ CHECK(LIBUSB(count = libusb_get_device_list(NULL, &list)),
+ "USB: Failed to get device list");
+
+ for (i = 0; i < count; ++i) {
+ struct libusb_device_descriptor descriptor;
+ struct usb_device current = {
+ .device = list[i],
+ .handle = NULL,
+ .next = NULL,
+ };
+
+ uint8_t bus = libusb_get_bus_number(list[i]);
+ uint8_t address = libusb_get_device_address(list[i]);
+
+ msg_pdbg("USB: Inspecting device (Bus %d, Address %d)\n",
+ bus,
+ address);
+
+ CHECK(LIBUSB(libusb_get_device_descriptor(list[i],
+ &descriptor)),
+ "USB: Failed to get device descriptor");
+
+ if (check_match(&match->vid, descriptor.idVendor) &&
+ check_match(&match->pid, descriptor.idProduct) &&
+ check_match(&match->bus, bus) &&
+ check_match(&match->address, address))
+ CHECK(find_config(match,
+ ¤t,
+ &descriptor,
+ devices),
+ "USB: Failed to find config");
+ }
+
+ libusb_free_device_list(list, 1);
+
+ return (*devices == NULL);
+}
+
+/*
+ * If the underlying libusb device is not open, open it.
+ *
+ * Return:
+ * 0: The device was already open or was successfully opened.
+ * non-zero: There was a failure while opening the device.
+ */
+static int usb_device_open(struct usb_device *device)
+{
+ if (device->handle == NULL)
+ CHECK(LIBUSB(libusb_open(device->device, &device->handle)),
+ "USB: Failed to open device\n");
+
+ return 0;
+}
+
+int usb_device_show(char const *prefix, struct usb_device *device)
+{
+ struct libusb_device_descriptor descriptor;
+ unsigned char product[256];
+
+ CHECK(usb_device_open(device), "USB: Failed to open device\n");
+
+ CHECK(LIBUSB(libusb_get_device_descriptor(device->device, &descriptor)),
+ "USB: Failed to get device descriptor\n");
+
+ CHECK(LIBUSB(libusb_get_string_descriptor_ascii(
+ device->handle,
+ descriptor.iProduct,
+ product,
+ sizeof(product))),
+ "USB: Failed to get device product string\n");
+
+ product[255] = '\0';
+
+ msg_perr("%sbus=0x%02x,address=0x%02x | %s\n",
+ prefix,
+ libusb_get_bus_number(device->device),
+ libusb_get_device_address(device->device),
+ product);
+
+ return 0;
+}
+
+int usb_device_claim(struct usb_device *device)
+{
+ int current_config;
+
+ CHECK(usb_device_open(device), "USB: Failed to open device\n");
+
+ CHECK(LIBUSB(libusb_get_configuration(device->handle,
+ ¤t_config)),
+ "USB: Failed to get current device configuration\n");
+
+ if (current_config != device->config_descriptor->bConfigurationValue)
+ CHECK(LIBUSB(libusb_set_configuration(
+ device->handle,
+ device->
+ config_descriptor->
+ bConfigurationValue)),
+ "USB: Failed to set new configuration from %d to %d\n",
+ current_config,
+ device->config_descriptor->bConfigurationValue);
+
+ CHECK(LIBUSB(libusb_set_auto_detach_kernel_driver(device->handle, 1)),
+ "USB: Failed to enable auto kernel driver detach\n");
+
+ CHECK(LIBUSB(libusb_claim_interface(device->handle,
+ device->
+ interface_descriptor->
+ bInterfaceNumber)),
+ "USB: Could not claim device interface %d\n",
+ device->interface_descriptor->bInterfaceNumber);
+
+ if (device->interface_descriptor->bAlternateSetting != 0)
+ CHECK(LIBUSB(libusb_set_interface_alt_setting(
+ device->handle,
+ device->
+ interface_descriptor->
+ bInterfaceNumber,
+ device->
+ interface_descriptor->
+ bAlternateSetting)),
+ "USB: Failed to set alternate setting %d\n",
+ device->interface_descriptor->bAlternateSetting);
+
+ return 0;
+}
+
+struct usb_device *usb_device_free(struct usb_device *device)
+{
+ struct usb_device *next = device->next;
+
+ if (device->handle != NULL)
+ libusb_close(device->handle);
+
+ /*
+ * This unref balances the ref added in the add_device function.
+ */
+ libusb_unref_device(device->device);
+ libusb_free_config_descriptor(device->config_descriptor);
+
+ free(device);
+
+ return next;
+}
diff --git a/usb_device.h b/usb_device.h
new file mode 100644
index 0000000..46d2458
--- /dev/null
+++ b/usb_device.h
@@ -0,0 +1,201 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ */
+
+/*
+ * USB device matching framework
+ *
+ * This can be used to match a USB device by a number of different parameters.
+ * The parameters can be passed on the command line and defaults can be set
+ * by the programmer.
+ */
+
+#include <libusb.h>
+#include <stdint.h>
+
+/*
+ * Check Macro
+ *
+ * The check macro simplifies common return error code checking logic. If the
+ * expression does not evaluate to zero then the string error is printed and
+ * the expressions result is imediately returned, else the result is available
+ * as the value of the CHECK expression statement.
+ */
+
+#define CHECK(expression, string...) \
+ ({ \
+ int error__ = (expression); \
+ \
+ if (error__ != 0) { \
+ msg_perr(string); \
+ return error__; \
+ } \
+ \
+ error__; \
+ })
+
+/*
+ * The LIBUSB macro converts a libusb failure code into an error code that
+ * flashrom recognizes. It also displays additional libusb specific
+ * information about the failure.
+ */
+#define LIBUSB(expression) \
+ ({ \
+ int libusb_error__ = (expression); \
+ \
+ if (libusb_error__ < 0) { \
+ msg_perr("libusb error: %s:%d %s\n", \
+ __FILE__, \
+ __LINE__, \
+ libusb_error_name(libusb_error__)); \
+ libusb_error__ = 0x20000 | -libusb_error__; \
+ } else { \
+ libusb_error__ = 0; \
+ } \
+ \
+ libusb_error__; \
+ })
+
+/*
+ * A USB match and associated value struct are used to encode the information
+ * about a device against which we wish to match. If the value of a
+ * usb_match_value has been set then a device must match that value. The name
+ * of the usb_match_value is used to fetch the programmer parameter from the
+ * flashrom command line and is the same as the name of the corresponding
+ * field in usb_match.
+ */
+struct usb_match_value {
+ char const *name;
+ int value;
+ int set;
+};
+
+struct usb_match {
+ struct usb_match_value bus;
+ struct usb_match_value address;
+ struct usb_match_value vid;
+ struct usb_match_value pid;
+ struct usb_match_value serial;
+ struct usb_match_value config;
+ struct usb_match_value interface;
+ struct usb_match_value altsetting;
+ struct usb_match_value class;
+ struct usb_match_value subclass;
+ struct usb_match_value protocol;
+};
+
+/*
+ * Initialize a usb_match structure so that each value's name matches the
+ * values name in the usb_match structure (so bus.name == "bus"...), and
+ * look for each value in the flashrom command line via
+ * extract_programmer_param. If the value is found convert it to an integer
+ * using strtol, accepting hex, decimal and octal encoding.
+ */
+void usb_match_init(struct usb_match *match);
+
+/*
+ * Add a default value to a usb_match_value. This must be done after calling
+ * usb_match_init. If usb_match_init already set the value of a usb_match_value
+ * we do nothing, otherwise set the value to default_value. This ensures that
+ * parameters passed on the command line override defaults.
+ */
+void usb_match_value_default(struct usb_match_value *match,
+ long int default_value);
+
+/*
+ * The usb_device structure is an entry in a linked list of devices that were
+ * matched by usb_device_find.
+ */
+struct usb_device {
+ struct libusb_device *device;
+ struct libusb_config_descriptor *config_descriptor;
+ struct libusb_interface_descriptor const *interface_descriptor;
+
+ /*
+ * Initially NULL, the libusb_device_handle is only valid once the
+ * usb_device has been successfully passed to usb_device_show or
+ * usb_device_claim.
+ */
+ struct libusb_device_handle *handle;
+
+ /*
+ * Link to next device, or NULL
+ */
+ struct usb_device *next;
+};
+
+/*
+ * Find and return a list of all compatible devices. Each device is added to
+ * the list with its first valid configuration and interface. If an alternate
+ * configuration (config, interface, altsetting...) is desired the specifics
+ * can be supplied as programmer parameters.
+ *
+ * Return:
+ * 0: At least one matching device was found.
+ * 1: No matching devices were found.
+ */
+int usb_device_find(struct usb_match const *match, struct usb_device **devices);
+
+/*
+ * Display the devices bus and address as well as its product string. The
+ * underlying libusb device is opened if it is not already open.
+ *
+ * Return:
+ * 0: The device information was displayed.
+ * non-zero: There was a failure while displaying the device information.
+ */
+int usb_device_show(char const *prefix, struct usb_device *device);
+
+/*
+ * Open the underlying libusb device, set its config, claim the interface and
+ * select the correct alternate interface.
+ *
+ * Return:
+ * 0: The device was successfully claimed.
+ * non-zero: There was a failure while trying to claim the device.
+ */
+int usb_device_claim(struct usb_device *device);
+
+/*
+ * Free a usb_device structure.
+ *
+ * This ensures that the libusb device is closed and that all allocated
+ * handles and descriptors are freed.
+ *
+ * Return:
+ * The next device in the device list.
+ */
+struct usb_device *usb_device_free(struct usb_device *device);
--
To view, visit https://review.coreboot.org/c/flashrom/+/38209
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: Ifad273a708acea4de797a0808be58960635a8864
Gerrit-Change-Number: 38209
Gerrit-PatchSet: 1
Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org>
Gerrit-MessageType: newchange
7
29
Change in ...flashrom[master]: manibuilder: Add list of tags used for the 1.1.x branch
by Nico Huber (Code Review) March 16, 2021
by Nico Huber (Code Review) March 16, 2021
March 16, 2021
Nico Huber has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/33340
Change subject: manibuilder: Add list of tags used for the 1.1.x branch
......................................................................
manibuilder: Add list of tags used for the 1.1.x branch
Change-Id: I243aa11f4a017a4209efcc227cec56eb12020e28
Signed-off-by: Nico Huber <nico.h(a)gmx.de>
---
M util/manibuilder/Makefile.targets
1 file changed, 48 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/40/33340/1
diff --git a/util/manibuilder/Makefile.targets b/util/manibuilder/Makefile.targets
index 044b0a4..079c181 100644
--- a/util/manibuilder/Makefile.targets
+++ b/util/manibuilder/Makefile.targets
@@ -112,6 +112,46 @@
# can only run what is still maintained
10X_TAGS := $(filter $(10X_TAGS),$(ALL_TAGS))
+# original 1.1.x tags
+11X_TAGS := \
+ anita\:amd64 \
+ djgpp\:6.1.0 \
+ fedora\:25-x86_64 \
+ fedora\:25-ppc64le \
+ fedora\:25-aarch64 \
+ fedora\:24-x86_64 \
+ centos\:7.3-aarch64-clean \
+ centos\:7.3-amd64-clean \
+ centos\:7.2-amd64-clean \
+ debian-debootstrap\:ppc64el-stretch \
+ debian-debootstrap\:armhf-stretch \
+ debian-debootstrap\:mips-stretch \
+ debian-debootstrap\:mipsel-stretch \
+ debian-debootstrap\:amd64-stretch \
+ debian-debootstrap\:i386-stretch \
+ debian-debootstrap\:amd64-sid \
+ ubuntu-debootstrap\:arm64-xenial \
+ ubuntu-debootstrap\:amd64-xenial \
+ ubuntu-debootstrap\:powerpc-xenial \
+ ubuntu-debootstrap\:amd64-bionic \
+ alpine\:amd64-v3.7 \
+ alpine\:amd64-v3.8 \
+ alpine\:amd64-v3.6 \
+ alpine\:armhf-v3.8 \
+ alpine\:i386-v3.6 \
+ alpine\:i386-v3.7 \
+ alpine\:i386-v3.8 \
+ debian-debootstrap\:amd64-buster \
+ debian-debootstrap\:i386-buster \
+ debian-debootstrap\:i386-sid \
+ ubuntu-debootstrap\:armhf-xenial \
+ ubuntu-debootstrap\:i386-bionic \
+ ubuntu-debootstrap\:i386-xenial \
+ ubuntu-debootstrap\:ppc64el-xenial \
+
+# can only run what is still maintained
+11X_TAGS := $(filter $(11X_TAGS),$(ALL_TAGS))
+
default: $(DEFAULT_TAGS)
native: $(NATIVE_TAGS)
@@ -121,6 +161,9 @@
1.0.x: export TEST_REVISION=refs/heads/1.0.x
1.0.x: $(10X_TAGS)
+1.1.x: export TEST_REVISION=refs/heads/1.1.x
+1.1.x: $(11X_TAGS)
+
show-default:
@printf "%s\n" $(DEFAULT_TAGS)
@@ -133,6 +176,9 @@
show-1.0.x:
@printf "%s\n" $(10X_TAGS)
-.PHONY: default native all 1.0.x
-.PHONY: show-default show-native show-all show-1.0.x
+show-1.1.x:
+ @printf "%s\n" $(11X_TAGS)
+
+.PHONY: default native all 1.0.x 1.1.x
+.PHONY: show-default show-native show-all show-1.0.x show-1.1.x
.PHONY: $(ALL_TAGS)
--
To view, visit https://review.coreboot.org/c/flashrom/+/33340
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I243aa11f4a017a4209efcc227cec56eb12020e28
Gerrit-Change-Number: 33340
Gerrit-PatchSet: 1
Gerrit-Owner: Nico Huber <nico.h(a)gmx.de>
Gerrit-MessageType: newchange
3
4
Change in ...flashrom[master]: manibuilder: Add newer targets for Alpine, CentOS, Fedora
by Nico Huber (Code Review) March 16, 2021
by Nico Huber (Code Review) March 16, 2021
March 16, 2021
Nico Huber has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/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
Change in ...flashrom[master]: manibuilder: Allow warnings in NetBSD and CentOS builds
by Nico Huber (Code Review) March 16, 2021
by Nico Huber (Code Review) March 16, 2021
March 16, 2021
Nico Huber has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/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
Change in ...flashrom[master]: manibuilder: Add libjaylink where possible, disable where not
by Nico Huber (Code Review) March 16, 2021
by Nico Huber (Code Review) March 16, 2021
March 16, 2021
Nico Huber has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/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
Change in ...flashrom[master]: manibuilder: Enable CONFIG_EVERYTHING=yes
by Nico Huber (Code Review) March 16, 2021
by Nico Huber (Code Review) March 16, 2021
March 16, 2021
Nico Huber has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/33337
Change subject: manibuilder: Enable CONFIG_EVERYTHING=yes
......................................................................
manibuilder: Enable CONFIG_EVERYTHING=yes
Change-Id: I4651b55744d730956aa8fda8fdfccbbd68cdda19
Signed-off-by: Nico Huber <nico.h(a)gmx.de>
---
M util/manibuilder/Makefile
1 file changed, 3 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/37/33337/1
diff --git a/util/manibuilder/Makefile b/util/manibuilder/Makefile
index b1e161c..9b87f45 100644
--- a/util/manibuilder/Makefile
+++ b/util/manibuilder/Makefile
@@ -4,6 +4,7 @@
CC := ccache cc
MAKECMD := make
+MAKEARGS := CONFIG_EVERYTHING=yes
spc :=
spc +=
@@ -50,7 +51,7 @@
djgpp\:6.1.0: CC=ccache i586-pc-msdosdjgpp-gcc
djgpp\:6.1.0: STRIP=i586-pc-msdosdjgpp-strip
djgpp\:6.1.0: LIBS_BASE=../
-djgpp\:6.1.0: TARGET=strip
+djgpp\:6.1.0: MAKEARGS+=strip
$(ANITA_TAGS): MAKECMD=gmake
$(ALL_TAGS): export QUIET_SETUP=$(QUIET_TEST)
$(ALL_TAGS): %: %-check-build
@@ -65,7 +66,7 @@
$(MAKECMD) clean && $(MAKECMD) -j$${CPUS:-1} CC='$(CC)' \
$(if $(STRIP),STRIP='$(STRIP)') \
$(if $(LIBS_BASE),LIBS_BASE='$(LIBS_BASE)') \
- $(TARGET)" \
+ $(MAKEARGS)" \
$(if $(QUIET_TEST),>/dev/null 2>&1) || echo $*: $$?
$(addsuffix -shell,$(ALL_TAGS)): %-shell: %-check-build
--
To view, visit https://review.coreboot.org/c/flashrom/+/33337
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I4651b55744d730956aa8fda8fdfccbbd68cdda19
Gerrit-Change-Number: 33337
Gerrit-PatchSet: 1
Gerrit-Owner: Nico Huber <nico.h(a)gmx.de>
Gerrit-MessageType: newchange
3
4
Change in flashrom[master]: libflashrom: add querying functions with meson integration
by Michał Żygowski (Code Review) March 13, 2021
by Michał Żygowski (Code Review) March 13, 2021
March 13, 2021
Hello ?ukasz Dmitrowski,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/flashrom/+/34363
to review the following change.
Change subject: libflashrom: add querying functions with meson integration
......................................................................
libflashrom: add querying functions with meson integration
Work based on lukasz.dmitrowski(a)gmail.com code
Change-Id: I49041b8fa5700dabe59fef0d2337339d34cd6c6f
Signed-off-by: Artur Raglis <artur.raglis(a)3mdeb.com>
Signed-off-by: Lukasz Dmitrowski <lukasz.dmitrowski(a)gmail.com>
---
M libflashrom.c
M libflashrom.h
M libflashrom.map
M meson.build
4 files changed, 187 insertions(+), 3 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/63/34363/1
diff --git a/libflashrom.c b/libflashrom.c
index f90a22c..cc26835 100644
--- a/libflashrom.c
+++ b/libflashrom.c
@@ -103,7 +103,142 @@
* @{
*/
-/* TBD */
+/**
+ * @brief Returns flashrom version
+ * @return Flashrom version
+ */
+const char *flashrom_version_info(void)
+{
+ return flashrom_version;
+}
+
+/**
+ * @brief Returns list of supported programmers
+ * @return List of supported programmers
+ */
+const char **flashrom_supported_programmers(void)
+{
+ enum programmer p = 0;
+ const char **supported_programmers = NULL;
+ supported_programmers = malloc((PROGRAMMER_INVALID + 1) * sizeof(char*));
+
+ if (supported_programmers != NULL) {
+ for (; p < PROGRAMMER_INVALID; ++p) {
+ supported_programmers[p] = programmer_table[p].name;
+ }
+ } else {
+ msg_gerr("Memory allocation error!\n");
+ }
+
+ return supported_programmers;
+}
+
+/**
+ * @brief Returns list of supported flash chips
+ * @return List of supported flash chips
+ */
+flashrom_flashchip_info *flashrom_supported_flash_chips(void)
+{
+ int i = 0;
+ flashrom_flashchip_info *supported_flashchips = malloc(flashchips_size * sizeof(flashrom_flashchip_info));
+
+ if (supported_flashchips != NULL) {
+ for (; i < flashchips_size; ++i) {
+ supported_flashchips[i].vendor = flashchips[i].vendor;
+ supported_flashchips[i].name = flashchips[i].name;
+ supported_flashchips[i].tested.erase = (flashrom_test_state) flashchips[i].tested.erase;
+ supported_flashchips[i].tested.probe = (flashrom_test_state) flashchips[i].tested.probe;
+ supported_flashchips[i].tested.read = (flashrom_test_state) flashchips[i].tested.read;
+ supported_flashchips[i].tested.write = (flashrom_test_state) flashchips[i].tested.write;
+ supported_flashchips[i].total_size = flashchips[i].total_size;
+ }
+ } else {
+ msg_gerr("Memory allocation error!\n");
+ }
+
+ return supported_flashchips;
+}
+
+/**
+ * @brief Returns list of supported mainboards
+ * @return List of supported mainboards
+ */
+flashrom_board_info *flashrom_supported_boards(void)
+{
+ int boards_known_size = 0;
+ int i = 0;
+ const struct board_info *binfo = boards_known;
+
+ while ((binfo++)->vendor)
+ ++boards_known_size;
+ binfo = boards_known;
+ /* add place for {0} */
+ ++boards_known_size;
+
+ flashrom_board_info *supported_boards = malloc(boards_known_size * sizeof(flashrom_board_info));
+
+ if (supported_boards != NULL) {
+ for (; i < boards_known_size; ++i) {
+ supported_boards[i].vendor = binfo[i].vendor;
+ supported_boards[i].name = binfo[i].name;
+ supported_boards[i].working = binfo[i].working;
+ }
+ } else {
+ msg_gerr("Memory allocation error!\n");
+ }
+
+ return supported_boards;
+}
+
+/**
+ * @brief Returns list of supported chipsets
+ * @return List of supported chipsets
+ */
+flashrom_chipset_info *flashrom_supported_chipsets(void)
+{
+ int chipset_enables_size = 0;
+ int i = 0;
+ const struct penable *chipset = chipset_enables;
+
+ while ((chipset++)->vendor_name)
+ ++chipset_enables_size;
+ chipset = chipset_enables;
+ /* add place for {0}*/
+ ++chipset_enables_size;
+
+ flashrom_chipset_info *supported_chipsets = malloc(chipset_enables_size * sizeof(flashrom_chipset_info));
+
+ if (supported_chipsets != NULL) {
+ for (; i < chipset_enables_size; ++i) {
+ supported_chipsets[i].vendor = chipset[i].vendor_name;
+ supported_chipsets[i].chipset = chipset[i].device_name;
+ supported_chipsets[i].vendor_id = chipset[i].vendor_id;
+ supported_chipsets[i].chipset_id = chipset[i].device_id;
+ supported_chipsets[i].status = chipset[i].status;
+ }
+ } else {
+ msg_gerr("Memory allocation error!\n");
+ }
+
+ return supported_chipsets;
+}
+
+/**
+ * @brief Frees memory allocated by libflashrom API
+ * @param Pointer to block of memory which should be freed
+ * @return 0 on success
+ * 1 on null pointer error
+ */
+int flashrom_data_free(void *const p)
+{
+ if (!p) {
+ msg_gerr("flashrom_data_free - Null pointer!\n");
+ return 1;
+ } else {
+ free(p);
+ return 0;
+ }
+}
/** @} */ /* end flashrom-query */
diff --git a/libflashrom.h b/libflashrom.h
index 38c95d2..ebba16b 100644
--- a/libflashrom.h
+++ b/libflashrom.h
@@ -37,6 +37,50 @@
typedef int(flashrom_log_callback)(enum flashrom_log_level, const char *format, va_list);
void flashrom_set_log_callback(flashrom_log_callback *);
+/** @ingroup flashrom-query */
+typedef enum {
+ FLASHROM_TESTED_OK = 0,
+ FLASHROM_TESTED_NT = 1,
+ FLASHROM_TESTED_BAD = 2,
+ FLASHROM_TESTED_DEP = 3,
+ FLASHROM_TESTED_NA = 4,
+} flashrom_test_state;
+
+typedef struct flashrom_flashchip_info {
+ const char *vendor;
+ const char *name;
+ unsigned int total_size;
+ struct flashrom_tested {
+ flashrom_test_state probe;
+ flashrom_test_state read;
+ flashrom_test_state erase;
+ flashrom_test_state write;
+ } tested;
+} flashrom_flashchip_info;
+
+typedef struct flashrom_board_info {
+ const char *vendor;
+ const char *name;
+ flashrom_test_state working;
+} flashrom_board_info;
+
+typedef struct flashrom_chipset_info {
+ const char *vendor;
+ const char *chipset;
+ uint16_t vendor_id;
+ uint16_t chipset_id;
+ flashrom_test_state status;
+} flashrom_chipset_info;
+
+const char *flashrom_version_info(void);
+void flashrom_system_info(void);
+const char **flashrom_supported_programmers(void);
+flashrom_flashchip_info *flashrom_supported_flash_chips(void);
+flashrom_board_info *flashrom_supported_boards(void);
+flashrom_chipset_info *flashrom_supported_chipsets(void);
+int flashrom_data_free(void *const p);
+
+/** @ingroup flashrom-prog */
struct flashrom_programmer;
int flashrom_programmer_init(struct flashrom_programmer **, const char *prog_name, const char *prog_params);
int flashrom_programmer_shutdown(struct flashrom_programmer *);
diff --git a/libflashrom.map b/libflashrom.map
index 3c287ff..d6dd24d 100644
--- a/libflashrom.map
+++ b/libflashrom.map
@@ -1,7 +1,11 @@
LIBFLASHROM_1.0 {
global:
+ flashrom_board_info;
+ flashrom_chipset_info;
+ flashrom_data_free;
flashrom_flag_get;
flashrom_flag_set;
+ flashrom_flashchip_info;
flashrom_flash_erase;
flashrom_flash_getsize;
flashrom_flash_probe;
@@ -20,5 +24,8 @@
flashrom_programmer_shutdown;
flashrom_set_log_callback;
flashrom_shutdown;
+ flashrom_supported_programmers;
+ flashrom_system_info;
+ flashrom_version_info;
local: *;
};
diff --git a/meson.build b/meson.build
index 1923fdf..00f4a2f 100644
--- a/meson.build
+++ b/meson.build
@@ -68,8 +68,6 @@
srcs = []
need_libusb0 = false
-need_raw_access = false
-need_serial = false
# check for required symbols
if cc.has_function('clock_gettime')
--
To view, visit https://review.coreboot.org/c/flashrom/+/34363
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I49041b8fa5700dabe59fef0d2337339d34cd6c6f
Gerrit-Change-Number: 34363
Gerrit-PatchSet: 1
Gerrit-Owner: Michał Żygowski <michal.zygowski(a)3mdeb.com>
Gerrit-Reviewer: ?ukasz Dmitrowski <lukasz.dmitrowski(a)gmail.com>
Gerrit-MessageType: newchange
5
23
Change in flashrom[master]: Add writeprotect support
by Edward O'Callaghan (Code Review) Feb. 28, 2021
by Edward O'Callaghan (Code Review) Feb. 28, 2021
Feb. 28, 2021
Edward O'Callaghan has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/40325 )
Change subject: Add writeprotect support
......................................................................
Add writeprotect support
[NOT-FOR-MERGE-(yet)]: PoC WIP.
BUG=b:153800563
BRANCH=none
TEST=builds
Change-Id: Id93b5a1cb2da476fa8a7dde41d7b963024117474
Signed-off-by: Edward O'Callaghan <quasisec(a)google.com>
---
M Makefile
M cli_classic.c
M flash.h
M meson.build
A writeprotect.c
A writeprotect.h
6 files changed, 2,722 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/25/40325/1
diff --git a/Makefile b/Makefile
index a533acf..a1bf898 100644
--- a/Makefile
+++ b/Makefile
@@ -589,7 +589,7 @@
CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \
sst28sf040.o 82802ab.o \
sst49lfxxxc.o sst_fwhub.o edi.o flashchips.o spi.o spi25.o spi25_statusreg.o \
- spi95.o opaque.o sfdp.o en29lv640b.o at45db.o
+ spi95.o opaque.o sfdp.o en29lv640b.o at45db.o writeprotect.o
###############################################################################
# Library code.
diff --git a/cli_classic.c b/cli_classic.c
index 73cc417..9e2d8e8 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -17,6 +17,7 @@
* GNU General Public License for more details.
*/
+#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -27,6 +28,7 @@
#include "flashchips.h"
#include "fmap.h"
#include "programmer.h"
+#include "writeprotect.h"
#include "libflashrom.h"
static void cli_classic_usage(const char *name)
@@ -116,6 +118,8 @@
int list_supported_wiki = 0;
#endif
int flash_name = 0, flash_size = 0;
+ int set_wp_range = 0, set_wp_region = 0, set_wp_enable = 0,
+ set_wp_disable = 0, wp_status = 0, wp_list = 0;
int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
int dont_verify_it = 0, dont_verify_all = 0, list_supported = 0, operation_specified = 0;
struct flashrom_layout *layout = NULL;
@@ -127,6 +131,12 @@
OPTION_FLASH_CONTENTS,
OPTION_FLASH_NAME,
OPTION_FLASH_SIZE,
+ OPTION_WP_STATUS,
+ OPTION_WP_SET_RANGE,
+ OPTION_WP_SET_REGION,
+ OPTION_WP_ENABLE,
+ OPTION_WP_DISABLE,
+ OPTION_WP_LIST,
};
int ret = 0;
@@ -150,6 +160,12 @@
{"flash-name", 0, NULL, OPTION_FLASH_NAME},
{"flash-size", 0, NULL, OPTION_FLASH_SIZE},
{"get-size", 0, NULL, OPTION_FLASH_SIZE}, // (deprecated): back compatibility.
+ {"wp-status", 0, 0, OPTION_WP_STATUS},
+ {"wp-range", 0, 0, OPTION_WP_SET_RANGE},
+ {"wp-region", 1, 0, OPTION_WP_SET_REGION},
+ {"wp-enable", optional_argument, 0, OPTION_WP_ENABLE},
+ {"wp-disable", 0, 0, OPTION_WP_DISABLE},
+ {"wp-list", 0, 0, OPTION_WP_LIST},
{"list-supported", 0, NULL, 'L'},
{"list-supported-wiki", 0, NULL, 'z'},
{"programmer", 1, NULL, 'p'},
@@ -169,6 +185,7 @@
char *tempstr = NULL;
char *pparam = NULL;
struct layout_include_args *include_args = NULL;
+ char *wp_mode_opt = NULL;
flashrom_set_log_callback((flashrom_log_callback *)&flashrom_print_cb);
@@ -283,6 +300,23 @@
cli_classic_validate_singleop(&operation_specified);
flash_size = 1;
break;
+ case OPTION_WP_STATUS:
+ wp_status = 1;
+ break;
+ case OPTION_WP_LIST:
+ wp_list = 1;
+ break;
+ case OPTION_WP_SET_RANGE:
+ set_wp_range = 1;
+ break;
+ case OPTION_WP_ENABLE:
+ set_wp_enable = 1;
+ if (optarg)
+ wp_mode_opt = strdup(optarg);
+ break;
+ case OPTION_WP_DISABLE:
+ set_wp_disable = 1;
+ break;
case 'L':
cli_classic_validate_singleop(&operation_specified);
list_supported = 1;
@@ -562,11 +596,19 @@
goto out_shutdown;
}
- if (!(read_it | write_it | verify_it | erase_it | flash_name | flash_size)) {
+ if (!(read_it | write_it | verify_it | erase_it | flash_name | flash_size
+ | set_wp_range | set_wp_region | set_wp_enable |
+ set_wp_disable | wp_status | wp_list )) {
msg_ginfo("No operations were specified.\n");
goto out_shutdown;
}
+ if (set_wp_enable && set_wp_disable) {
+ msg_ginfo("Error: --wp-enable and --wp-disable are mutually exclusive\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+
if (flash_name) {
if (fill_flash->chip->vendor && fill_flash->chip->name) {
printf("vendor=\"%s\" name=\"%s\"\n",
@@ -583,6 +625,110 @@
goto out_shutdown;
}
+ if (wp_status) {
+ if (fill_flash->chip->wp && fill_flash->chip->wp->wp_status) {
+ ret |= fill_flash->chip->wp->wp_status(fill_flash);
+ } else {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ }
+ goto out_shutdown;
+ }
+
+ /* Note: set_wp_disable should be done before setting the range */
+ if (set_wp_disable) {
+ if (fill_flash->chip->wp && fill_flash->chip->wp->disable) {
+ ret |= fill_flash->chip->wp->disable(fill_flash);
+ } else {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+ }
+
+ if (!ret && set_wp_enable) {
+ enum wp_mode wp_mode;
+
+ if (wp_mode_opt)
+ wp_mode = get_wp_mode(wp_mode_opt);
+ else
+ wp_mode = WP_MODE_HARDWARE; /* default */
+
+ if (wp_mode == WP_MODE_UNKNOWN) {
+ msg_gerr("Error: Invalid WP mode: \"%s\"\n", wp_mode_opt);
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ if (fill_flash->chip->wp && fill_flash->chip->wp->enable) {
+ ret |= fill_flash->chip->wp->enable(fill_flash, wp_mode);
+ } else {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+ }
+
+ if (wp_list) {
+ msg_ginfo("Valid write protection ranges:\n");
+ if (fill_flash->chip->wp && fill_flash->chip->wp->list_ranges) {
+ ret |= fill_flash->chip->wp->list_ranges(fill_flash);
+ } else {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ }
+ goto out_shutdown;
+ }
+
+ if (set_wp_range || set_wp_region) {
+ if (set_wp_range && set_wp_region) {
+ msg_gerr("Error: Cannot use both --wp-range and "
+ "--wp-region simultaneously.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ if (!fill_flash->chip->wp || !fill_flash->chip->wp->set_range) {
+ msg_gerr("Error: write protect is not supported "
+ "on this flash chip.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+ }
+
+ /* Note: set_wp_range must happen before set_wp_enable */
+ if (set_wp_range) {
+ unsigned int start, len;
+ char *endptr = NULL;
+
+ if ((argc - optind) != 2) {
+ msg_gerr("Error: invalid number of arguments\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ /* FIXME: add some error checking */
+ start = strtoul(argv[optind], &endptr, 0);
+ if (errno == ERANGE || errno == EINVAL || *endptr != '\0') {
+ msg_gerr("Error: value \"%s\" invalid\n", argv[optind]);
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ len = strtoul(argv[optind + 1], &endptr, 0);
+ if (errno == ERANGE || errno == EINVAL || *endptr != '\0') {
+ msg_gerr("Error: value \"%s\" invalid\n", argv[optind + 1]);
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ ret |= fill_flash->chip->wp->set_range(fill_flash, start, len);
+ }
+
if (layoutfile) {
layout = get_global_layout();
} else if (ifd && (flashrom_layout_read_from_ifd(&layout, fill_flash, NULL, 0) ||
diff --git a/flash.h b/flash.h
index 2f0143b..fefca9d 100644
--- a/flash.h
+++ b/flash.h
@@ -235,6 +235,8 @@
int (*unlock) (struct flashctx *flash);
int (*write) (struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
int (*read) (struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+ uint8_t (*read_status) (const struct flashctx *flash);
+ int (*write_status) (const struct flashctx *flash, int status);
struct voltage {
uint16_t min;
uint16_t max;
@@ -243,6 +245,8 @@
/* SPI specific options (TODO: Make it a union in case other bustypes get specific options.) */
uint8_t wrea_override; /**< override opcode for write extended address register */
+
+ struct wp *wp;
};
struct flashrom_flashctx {
diff --git a/meson.build b/meson.build
index 699370a..38bf671 100644
--- a/meson.build
+++ b/meson.build
@@ -350,6 +350,7 @@
srcs += 'udelay.c'
srcs += 'w29ee011.c'
srcs += 'w39.c'
+srcs += 'writeprotect.c'
mapfile = 'libflashrom.map'
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
diff --git a/writeprotect.c b/writeprotect.c
new file mode 100644
index 0000000..f54ccad
--- /dev/null
+++ b/writeprotect.c
@@ -0,0 +1,2512 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#include "flash.h"
+#include "flashchips.h"
+#include "chipdrivers.h"
+#include "spi.h"
+#include "writeprotect.h"
+
+/*
+ * The following procedures rely on look-up tables to match the user-specified
+ * range with the chip's supported ranges. This turned out to be the most
+ * elegant approach since diferent flash chips use different levels of
+ * granularity and methods to determine protected ranges. In other words,
+ * be stupid and simple since clever arithmetic will not work for many chips.
+ */
+
+struct wp_range {
+ unsigned int start; /* starting address */
+ unsigned int len; /* len */
+};
+
+enum bit_state {
+ OFF = 0,
+ ON = 1,
+ X = -1 /* don't care. Must be bigger than max # of bp. */
+};
+
+/*
+ * Generic write-protection schema for 25-series SPI flash chips. This assumes
+ * there is a status register that contains one or more consecutive bits which
+ * determine which address range is protected.
+ */
+
+struct status_register_layout {
+ int bp0_pos; /* position of BP0 */
+ int bp_bits; /* number of block protect bits */
+ int srp_pos; /* position of status register protect enable bit */
+};
+
+/*
+ * The following ranges and functions are useful for representing the
+ * writeprotect schema in which there are typically 5 bits of
+ * relevant information stored in status register 1:
+ * m.sec: This bit indicates the units (sectors vs. blocks)
+ * m.tb: The top-bottom bit indicates if the affected range is at the top of
+ * the flash memory's address space or at the bottom.
+ * bp: Bitmask representing the number of affected sectors/blocks.
+ */
+struct wp_range_descriptor {
+ struct modifier_bits m;
+ unsigned int bp; /* block protect bitfield */
+ struct wp_range range;
+};
+
+struct wp_context {
+ struct status_register_layout sr1; /* status register 1 */
+ struct wp_range_descriptor *descrs;
+
+ /*
+ * Some chips store modifier bits in one or more special control
+ * registers instead of the status register like many older SPI NOR
+ * flash chips did. get_modifier_bits() and set_modifier_bits() will do
+ * any chip-specific operations necessary to get/set these bit values.
+ */
+ int (*get_modifier_bits)(const struct flashctx *flash,
+ struct modifier_bits *m);
+ int (*set_modifier_bits)(const struct flashctx *flash,
+ struct modifier_bits *m);
+};
+
+struct w25q_status {
+ /* this maps to register layout -- do not change ordering */
+ unsigned char busy : 1;
+ unsigned char wel : 1;
+ unsigned char bp0 : 1;
+ unsigned char bp1 : 1;
+ unsigned char bp2 : 1;
+ unsigned char tb : 1;
+ unsigned char sec : 1;
+ unsigned char srp0 : 1;
+} __attribute__ ((packed));
+
+/* Status register for large flash layouts with 4 BP bits */
+struct w25q_status_large {
+ unsigned char busy : 1;
+ unsigned char wel : 1;
+ unsigned char bp0 : 1;
+ unsigned char bp1 : 1;
+ unsigned char bp2 : 1;
+ unsigned char bp3 : 1;
+ unsigned char tb : 1;
+ unsigned char srp0 : 1;
+} __attribute__ ((packed));
+
+struct w25q_status_2 {
+ unsigned char srp1 : 1;
+ unsigned char qe : 1;
+ unsigned char rsvd : 6;
+} __attribute__ ((packed));
+
+int w25_range_to_status(const struct flashctx *flash,
+ unsigned int start, unsigned int len,
+ struct w25q_status *status);
+int w25_status_to_range(const struct flashctx *flash,
+ const struct w25q_status *status,
+ unsigned int *start, unsigned int *len);
+
+/*
+ * Mask to extract write-protect enable and range bits
+ * Status register 1:
+ * SRP0: bit 7
+ * range(BP2-BP0): bit 4-2
+ * range(BP3-BP0): bit 5-2 (large chips)
+ * Status register 2:
+ * SRP1: bit 1
+ */
+#define MASK_WP_AREA (0x9C)
+#define MASK_WP_AREA_LARGE (0x9C)
+#define MASK_WP2_AREA (0x01)
+
+struct wp_range_descriptor en25f40_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 448 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 384 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 512 * 1024} },
+};
+
+struct wp_range_descriptor en25q40_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 448 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 384 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
+};
+
+struct wp_range_descriptor en25q80_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 1016 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 1008 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 992 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 960 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 896 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 768 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 1024 * 1024} },
+};
+
+struct wp_range_descriptor en25q32_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 4032 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 3968 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 3840 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 3584 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 3072 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 4032 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 3968 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 3840 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 3584 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 3072 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
+};
+
+struct wp_range_descriptor en25q64_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 8064 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7936 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7680 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 7168 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 6144 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 8128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 8064 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 7936 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 7680 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 7168 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 6144 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
+};
+
+struct wp_range_descriptor en25q128_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16320 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 16256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 16128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 15872 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 15360 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 14336 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 16384 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 16320 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 16256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 16128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 15872 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 15360 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 14336 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 16384 * 1024} },
+};
+
+struct wp_range_descriptor en25s64_ranges[] = {
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8064 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 7936 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7680 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7168 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 6144 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 4096 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x7e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x7c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x780000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x700000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x600000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x400000, 4096 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
+};
+
+/* mx25l1005 ranges also work for the mx25l1005c */
+static struct wp_range_descriptor mx25l1005_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x010000, 64 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
+};
+
+static struct wp_range_descriptor mx25l2005_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x030000, 64 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x2, {0x020000, 128 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 256 * 1024} },
+};
+
+static struct wp_range_descriptor mx25l4005_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x070000, 64 * 1 * 1024} }, /* block 7 */
+ { .m = { .sec = X, .tb = X }, 0x2, {0x060000, 64 * 2 * 1024} }, /* blocks 6-7 */
+ { .m = { .sec = X, .tb = X }, 0x3, {0x040000, 64 * 4 * 1024} }, /* blocks 4-7 */
+ { .m = { .sec = X, .tb = X }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 512 * 1024} },
+};
+
+static struct wp_range_descriptor mx25l8005_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x0f0000, 64 * 1 * 1024} }, /* block 15 */
+ { .m = { .sec = X, .tb = X }, 0x2, {0x0e0000, 64 * 2 * 1024} }, /* blocks 14-15 */
+ { .m = { .sec = X, .tb = X }, 0x3, {0x0c0000, 64 * 4 * 1024} }, /* blocks 12-15 */
+ { .m = { .sec = X, .tb = X }, 0x4, {0x080000, 64 * 8 * 1024} }, /* blocks 8-15 */
+ { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
+};
+
+static struct wp_range_descriptor mx25l1605d_ranges[] = {
+ { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = 0 }, 0x1, {0x1f0000, 64 * 1 * 1024} }, /* block 31 */
+ { .m = { .sec = X, .tb = 0 }, 0x2, {0x1e0000, 64 * 2 * 1024} }, /* blocks 30-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x3, {0x1c0000, 64 * 4 * 1024} }, /* blocks 28-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x4, {0x180000, 64 * 8 * 1024} }, /* blocks 24-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x5, {0x100000, 64 * 16 * 1024} }, /* blocks 16-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+ { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+
+ { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 64 * 24 * 1024} }, /* blocks 0-23 */
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 64 * 28 * 1024} }, /* blocks 0-27 */
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 64 * 30 * 1024} }, /* blocks 0-29 */
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 64 * 31 * 1024} }, /* blocks 0-30 */
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+};
+
+/* FIXME: Is there an mx25l3205 (without a trailing letter)? */
+static struct wp_range_descriptor mx25l3205d_ranges[] = {
+ { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
+
+ { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
+};
+
+static struct wp_range_descriptor mx25u3235e_ranges[] = {
+ { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
+};
+
+static struct wp_range_descriptor mx25u6435e_ranges[] = {
+ { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 1 * 64 * 1024} }, /* block 127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
+
+ { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 96 * 64 * 1024} }, /* blocks 0-95 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 112 * 64 * 1024} }, /* blocks 0-111 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 120 * 64 * 1024} }, /* blocks 0-119 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 124 * 64 * 1024} }, /* blocks 0-123 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 126 * 64 * 1024} }, /* blocks 0-125 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 127 * 64 * 1024} }, /* blocks 0-126 */
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
+};
+
+#define MX25U12835E_TB (1 << 3)
+static struct wp_range_descriptor mx25u12835e_tb0_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0xff0000, 1 * 64 * 1024} }, /* block 255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0xfe0000, 2 * 64 * 1024} }, /* blocks 254-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0xfc0000, 4 * 64 * 1024} }, /* blocks 252-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0xf80000, 8 * 64 * 1024} }, /* blocks 248-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0xf00000, 16 * 64 * 1024} }, /* blocks 240-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0xe00000, 32 * 64 * 1024} }, /* blocks 224-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0xc00000, 64 * 64 * 1024} }, /* blocks 192-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x8, {0x800000, 128 * 64 * 1024} }, /* blocks 128-255 */
+ { .m = { .sec = 0, .tb = 0 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 0 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+};
+
+static struct wp_range_descriptor mx25u12835e_tb1_ranges[] = {
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 1 * 64 * 1024} }, /* block 0 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { .m = { .sec = 0, .tb = 1 }, 0x8, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
+ { .m = { .sec = 0, .tb = 1 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+ { .m = { .sec = 0, .tb = 1 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
+};
+
+static struct wp_range_descriptor n25q064_ranges[] = {
+ /*
+ * Note: For N25Q064, sec (usually in bit position 6) is called BP3
+ * (block protect bit 3). It is only useful when all blocks are to
+ * be write-protected.
+ */
+ { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
+
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 64 * 1024} }, /* block 127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
+ { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, /* block 0 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
+ { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+
+ { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 128 * 64 * 1024} }, /* all */
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* all */
+};
+
+static struct wp_range_descriptor w25q16_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x1f0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x1e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x1c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x180000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x100000, 1024 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 2048 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor w25q32_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 2048 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 4096 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x3ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x3fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x3fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x3f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x3f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor w25q80_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0f0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor w25q64_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor w25rq128_cmp0_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* NONE */
+
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* Upper 1/64 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* Upper 1/32 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* Upper 1/16 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* Upper 1/8 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* Upper 1/4 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* Upper 1/2 */
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* Lower 1/64 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* Lower 1/32 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* Lower 1/16 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* Lower 1/8 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* Lower 1/4 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* Lower 1/2 */
+
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 16384 * 1024} }, /* ALL */
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0xfff000, 4 * 1024} }, /* Upper 1/4096 */
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0xffe000, 8 * 1024} }, /* Upper 1/2048 */
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0xffc000, 16 * 1024} }, /* Upper 1/1024 */
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, /* Lower 1/4096 */
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, /* Lower 1/2048 */
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, /* Lower 1/1024 */
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, /* Lower 1/512 */
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, /* Lower 1/512 */
+};
+
+static struct wp_range_descriptor w25rq128_cmp1_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 16 * 1024 * 1024} }, /* ALL */
+
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16128 * 1024} }, /* Lower 63/64 */
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 15872 * 1024} }, /* Lower 31/32 */
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 15 * 1024 * 1024} }, /* Lower 15/16 */
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 14 * 1024 * 1024} }, /* Lower 7/8 */
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 12 * 1024 * 1024} }, /* Lower 3/4 */
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 8 * 1024 * 1024} }, /* Lower 1/2 */
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x040000, 16128 * 1024} }, /* Upper 63/64 */
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x080000, 15872 * 1024} }, /* Upper 31/32 */
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x100000, 15 * 1024 * 1024} }, /* Upper 15/16 */
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x200000, 14 * 1024 * 1024} }, /* Upper 7/8 */
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x400000, 12 * 1024 * 1024} }, /* Upper 3/4 */
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x800000, 8 * 1024 * 1024} }, /* Upper 1/2 */
+
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 0} }, /* NONE */
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 16380 * 1024} }, /* Lower 4095/4096 */
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 16376 * 1024} }, /* Lower 2048/2048 */
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 16368 * 1024} }, /* Lower 1023/1024 */
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 16380 * 1024} }, /* Upper 4095/4096 */
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 16376 * 1024} }, /* Upper 2047/2048 */
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 16368 * 1024} }, /* Upper 1023/1024 */
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
+};
+
+static struct wp_range_descriptor w25rq256_cmp0_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 0x0000000} }, /* NONE */
+
+ { .m = { .sec = X, .tb = 0 }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* Upper 1/512 */
+ { .m = { .sec = X, .tb = 0 }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* Upper 1/256 */
+ { .m = { .sec = X, .tb = 0 }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* Upper 1/128 */
+ { .m = { .sec = X, .tb = 0 }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* Upper 1/64 */
+ { .m = { .sec = X, .tb = 0 }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* Upper 1/32 */
+ { .m = { .sec = X, .tb = 0 }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* Upper 1/16 */
+ { .m = { .sec = X, .tb = 0 }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* Upper 1/8 */
+ { .m = { .sec = X, .tb = 0 }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* Upper 1/4 */
+ { .m = { .sec = X, .tb = 0 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
+
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x0000000, 64 * 1 * 1024} }, /* Lower 1/512 */
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x0000000, 64 * 2 * 1024} }, /* Lower 1/256 */
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x0000000, 64 * 4 * 1024} }, /* Lower 1/128 */
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x0000000, 64 * 8 * 1024} }, /* Lower 1/64 */
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x0000000, 64 * 16 * 1024} }, /* Lower 1/32 */
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x0000000, 64 * 32 * 1024} }, /* Lower 1/16 */
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x0000000, 64 * 64 * 1024} }, /* Lower 1/8 */
+ { .m = { .sec = X, .tb = 1 }, 0x8, {0x0000000, 64 * 128 * 1024} }, /* Lower 1/4 */
+ { .m = { .sec = X, .tb = 1 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
+
+ { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+ { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+};
+
+static struct wp_range_descriptor w25rq256_cmp1_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 64 * 512 * 1024} }, /* ALL */
+
+ { .m = { .sec = X, .tb = 0 }, 0x1, {0x0000000, 64 * 511 * 1024} }, /* Lower 511/512 */
+ { .m = { .sec = X, .tb = 0 }, 0x2, {0x0000000, 64 * 510 * 1024} }, /* Lower 255/256 */
+ { .m = { .sec = X, .tb = 0 }, 0x3, {0x0000000, 64 * 508 * 1024} }, /* Lower 127/128 */
+ { .m = { .sec = X, .tb = 0 }, 0x4, {0x0000000, 64 * 504 * 1024} }, /* Lower 63/64 */
+ { .m = { .sec = X, .tb = 0 }, 0x5, {0x0000000, 64 * 496 * 1024} }, /* Lower 31/32 */
+ { .m = { .sec = X, .tb = 0 }, 0x6, {0x0000000, 64 * 480 * 1024} }, /* Lower 15/16 */
+ { .m = { .sec = X, .tb = 0 }, 0x7, {0x0000000, 64 * 448 * 1024} }, /* Lower 7/8 */
+ { .m = { .sec = X, .tb = 0 }, 0x8, {0x0000000, 64 * 384 * 1024} }, /* Lower 3/4 */
+ { .m = { .sec = X, .tb = 0 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
+
+ { .m = { .sec = X, .tb = 1 }, 0x1, {0x0010000, 64 * 511 * 1024} }, /* Upper 511/512 */
+ { .m = { .sec = X, .tb = 1 }, 0x2, {0x0020000, 64 * 510 * 1024} }, /* Upper 255/256 */
+ { .m = { .sec = X, .tb = 1 }, 0x3, {0x0040000, 64 * 508 * 1024} }, /* Upper 127/128 */
+ { .m = { .sec = X, .tb = 1 }, 0x4, {0x0080000, 64 * 504 * 1024} }, /* Upper 63/64 */
+ { .m = { .sec = X, .tb = 1 }, 0x5, {0x0100000, 64 * 496 * 1024} }, /* Upper 31/32 */
+ { .m = { .sec = X, .tb = 1 }, 0x6, {0x0200000, 64 * 480 * 1024} }, /* Upper 15/16 */
+ { .m = { .sec = X, .tb = 1 }, 0x7, {0x0400000, 64 * 448 * 1024} }, /* Upper 7/8 */
+ { .m = { .sec = X, .tb = 1 }, 0x8, {0x0800000, 64 * 384 * 1024} }, /* Upper 3/4 */
+ { .m = { .sec = X, .tb = 1 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
+
+ { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 0x0000000} }, /* NONE */
+ { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 0x0000000} }, /* NONE */
+};
+
+struct wp_range_descriptor w25x10_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x010000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
+};
+
+struct wp_range_descriptor w25x20_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x030000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x020000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x3, {0x000000, 256 * 1024} },
+};
+
+struct wp_range_descriptor w25x40_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} },
+};
+
+struct wp_range_descriptor w25x80_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0F0000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0E0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0C0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
+};
+
+static struct wp_range_descriptor gd25q40_cmp0_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* None */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} }, /* All */
+ { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} }, /* All */
+ { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} }, /* All */
+ { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x07F000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x07E000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x07C000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x078000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x078000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x6, {0x078000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
+};
+
+static struct wp_range_descriptor gd25q40_cmp1_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 512 * 1024} }, /* ALL */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 448 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 384 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 256 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 448 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 384 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 256 * 1024} },
+
+ { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 0} }, /* None */
+ { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 0} }, /* None */
+ { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 0} }, /* None */
+ { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 0} }, /* None */
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 508 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 504 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 496 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 480 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 480 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x6, {0x000000, 480 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 508 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 504 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 496 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 480 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 480 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x6, {0x008000, 480 * 1024} },
+
+ { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 0} }, /* None */
+};
+
+static struct wp_range_descriptor gd25q64_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
+ { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
+
+ { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
+ { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
+
+ { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 0 }, 0x6, {0x7f8000, 32 * 1024} },
+
+ { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
+ { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
+};
+
+static struct wp_range_descriptor a25l040_ranges[] = {
+ { .m = { .sec = X, .tb = X }, 0x0, {0, 0} }, /* none */
+ { .m = { .sec = X, .tb = X }, 0x1, {0x70000, 64 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x2, {0x60000, 128 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x3, {0x40000, 256 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x4, {0x00000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x5, {0x00000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x6, {0x00000, 512 * 1024} },
+ { .m = { .sec = X, .tb = X }, 0x7, {0x00000, 512 * 1024} },
+};
+
+static uint8_t do_read_status(const struct flashctx *flash)
+{
+ if (flash->chip->read_status)
+ return flash->chip->read_status(flash);
+ else
+ return spi_read_status_register(flash);
+}
+
+static int do_write_status(const struct flashctx *flash, int status)
+{
+ if (flash->chip->write_status)
+ return flash->chip->write_status(flash, status);
+ else
+ return spi_write_status_register(flash, status);
+}
+
+/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
+static uint8_t w25q_read_status_register_2(const struct flashctx *flash)
+{
+ static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 };
+ unsigned char readarr[2];
+ int ret;
+
+ /* Read Status Register */
+ ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
+ if (ret) {
+ /*
+ * FIXME: make this a benign failure for now in case we are
+ * unable to execute the opcode
+ */
+ msg_cdbg("RDSR2 failed!\n");
+ readarr[0] = 0x00;
+ }
+
+ return readarr[0];
+}
+
+/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
+uint8_t mx25l_read_config_register(const struct flashctx *flash);//XXX
+uint8_t mx25l_read_config_register(const struct flashctx *flash)
+{
+ static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x15 };
+ unsigned char readarr[2]; /* leave room for dummy byte */
+ int ret;
+
+ ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
+ if (ret) {
+ msg_cdbg("RDCR failed!\n");
+ readarr[0] = 0x00;
+ }
+
+ return readarr[0];
+}
+
+/* Given a flash chip, this function returns its range table. */
+static int w25_range_table(const struct flashctx *flash,
+ struct wp_range_descriptor **descrs,
+ int *num_entries)
+{
+ uint8_t cr;
+
+ *descrs = 0;
+ *num_entries = 0;
+
+ switch (flash->chip->manufacture_id) {
+ case WINBOND_NEX_ID:
+ switch(flash->chip->model_id) {
+ case WINBOND_NEX_W25X10:
+ *descrs = w25x10_ranges;
+ *num_entries = ARRAY_SIZE(w25x10_ranges);
+ break;
+ case WINBOND_NEX_W25X20:
+ *descrs = w25x20_ranges;
+ *num_entries = ARRAY_SIZE(w25x20_ranges);
+ break;
+ case WINBOND_NEX_W25X40:
+ *descrs = w25x40_ranges;
+ *num_entries = ARRAY_SIZE(w25x40_ranges);
+ break;
+ case WINBOND_NEX_W25X80:
+ *descrs = w25x80_ranges;
+ *num_entries = ARRAY_SIZE(w25x80_ranges);
+ break;
+ case WINBOND_NEX_W25Q80_V:
+ *descrs = w25q80_ranges;
+ *num_entries = ARRAY_SIZE(w25q80_ranges);
+ break;
+ case WINBOND_NEX_W25Q16_V:
+ *descrs = w25q16_ranges;
+ *num_entries = ARRAY_SIZE(w25q16_ranges);
+ break;
+ case WINBOND_NEX_W25Q32_V:
+ case WINBOND_NEX_W25Q32_W:
+ //case WINBOND_NEX_W25Q32JW:
+ *descrs = w25q32_ranges;
+ *num_entries = ARRAY_SIZE(w25q32_ranges);
+ break;
+ case WINBOND_NEX_W25Q64_V:
+ case WINBOND_NEX_W25Q64_W:
+ *descrs = w25q64_ranges;
+ *num_entries = ARRAY_SIZE(w25q64_ranges);
+ break;
+ case WINBOND_NEX_W25Q128_DTR:
+ case WINBOND_NEX_W25Q128_V_M:
+ case WINBOND_NEX_W25Q128_V:
+ case WINBOND_NEX_W25Q128_W:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = w25rq128_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
+ } else {
+ /* CMP == 0 */
+ *descrs = w25rq128_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
+ }
+ break;
+ case WINBOND_NEX_W25Q256JV_M:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = w25rq256_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges);
+ } else {
+ /* CMP == 0 */
+ *descrs = w25rq256_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
+ }
+ break;
+ default:
+ msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
+ ", aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case EON_ID_NOPREFIX:
+ switch (flash->chip->model_id) {
+ case EON_EN25F40:
+ *descrs = en25f40_ranges;
+ *num_entries = ARRAY_SIZE(en25f40_ranges);
+ break;
+ case EON_EN25Q40:
+ *descrs = en25q40_ranges;
+ *num_entries = ARRAY_SIZE(en25q40_ranges);
+ break;
+ case EON_EN25Q80:
+ *descrs = en25q80_ranges;
+ *num_entries = ARRAY_SIZE(en25q80_ranges);
+ break;
+ case EON_EN25Q32:
+ *descrs = en25q32_ranges;
+ *num_entries = ARRAY_SIZE(en25q32_ranges);
+ break;
+ case EON_EN25Q64:
+ *descrs = en25q64_ranges;
+ *num_entries = ARRAY_SIZE(en25q64_ranges);
+ break;
+ case EON_EN25Q128:
+ *descrs = en25q128_ranges;
+ *num_entries = ARRAY_SIZE(en25q128_ranges);
+ break;
+ case EON_EN25S64:
+ *descrs = en25s64_ranges;
+ *num_entries = ARRAY_SIZE(en25s64_ranges);
+ break;
+ default:
+ msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
+ ", aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case MACRONIX_ID:
+ switch (flash->chip->model_id) {
+ case MACRONIX_MX25L1005:
+ *descrs = mx25l1005_ranges;
+ *num_entries = ARRAY_SIZE(mx25l1005_ranges);
+ break;
+ case MACRONIX_MX25L2005:
+ *descrs = mx25l2005_ranges;
+ *num_entries = ARRAY_SIZE(mx25l2005_ranges);
+ break;
+ case MACRONIX_MX25L4005:
+ *descrs = mx25l4005_ranges;
+ *num_entries = ARRAY_SIZE(mx25l4005_ranges);
+ break;
+ case MACRONIX_MX25L8005:
+ *descrs = mx25l8005_ranges;
+ *num_entries = ARRAY_SIZE(mx25l8005_ranges);
+ break;
+ case MACRONIX_MX25L1605:
+ /* FIXME: MX25L1605 and MX25L1605D have different write
+ * protection capabilities, but share IDs */
+ *descrs = mx25l1605d_ranges;
+ *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
+ break;
+ case MACRONIX_MX25L3205:
+ *descrs = mx25l3205d_ranges;
+ *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
+ break;
+ case MACRONIX_MX25U3235E:
+ *descrs = mx25u3235e_ranges;
+ *num_entries = ARRAY_SIZE(mx25u3235e_ranges);
+ break;
+ case MACRONIX_MX25U6435E:
+ *descrs = mx25u6435e_ranges;
+ *num_entries = ARRAY_SIZE(mx25u6435e_ranges);
+ break;
+ case MACRONIX_MX25U12835E:
+ cr = mx25l_read_config_register(flash);
+ if (cr & MX25U12835E_TB) { /* T/B == 1 */
+ *descrs = mx25u12835e_tb1_ranges;
+ *num_entries = ARRAY_SIZE(mx25u12835e_tb1_ranges);
+ } else { /* T/B == 0 */
+ *descrs = mx25u12835e_tb0_ranges;
+ *num_entries = ARRAY_SIZE(mx25u12835e_tb0_ranges);
+ }
+ break;
+ default:
+ msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
+ ", aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case ST_ID:
+ switch(flash->chip->model_id) {
+ case ST_N25Q064__1E:
+ case ST_N25Q064__3E:
+ *descrs = n25q064_ranges;
+ *num_entries = ARRAY_SIZE(n25q064_ranges);
+ break;
+ default:
+ msg_cerr("%s() %d: Micron flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case GIGADEVICE_ID:
+ switch(flash->chip->model_id) {
+ case GIGADEVICE_GD25LQ32:
+ *descrs = w25q32_ranges;
+ *num_entries = ARRAY_SIZE(w25q32_ranges);
+ break;
+ case GIGADEVICE_GD25Q40:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = gd25q40_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(gd25q40_cmp1_ranges);
+ } else {
+ *descrs = gd25q40_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(gd25q40_cmp0_ranges);
+ }
+ break;
+ case GIGADEVICE_GD25Q64:
+ case GIGADEVICE_GD25LQ64:
+ *descrs = gd25q64_ranges;
+ *num_entries = ARRAY_SIZE(gd25q64_ranges);
+ break;
+ case GIGADEVICE_GD25Q128:
+ case GIGADEVICE_GD25LQ128CD:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = w25rq128_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
+ } else {
+ /* CMP == 0 */
+ *descrs = w25rq128_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
+ }
+ break;
+ case GIGADEVICE_GD25Q256D:
+ *descrs = w25rq256_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
+ break;
+ default:
+ msg_cerr("%s() %d: GigaDevice flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case AMIC_ID_NOPREFIX:
+ switch(flash->chip->model_id) {
+ case AMIC_A25L040:
+ *descrs = a25l040_ranges;
+ *num_entries = ARRAY_SIZE(a25l040_ranges);
+ break;
+ default:
+ msg_cerr("%s() %d: AMIC flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case ATMEL_ID:
+ switch(flash->chip->model_id) {
+ //case ATMEL_AT25SF128A:
+ case ATMEL_AT25SL128A:
+ if (w25q_read_status_register_2(flash) & (1 << 6)) {
+ /* CMP == 1 */
+ *descrs = w25rq128_cmp1_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
+ } else {
+ /* CMP == 0 */
+ *descrs = w25rq128_cmp0_ranges;
+ *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
+ }
+ break;
+ default:
+ msg_cerr("%s() %d: Atmel flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ default:
+ msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
+ __func__, flash->chip->manufacture_id);
+ return -1;
+ }
+
+ return 0;
+}
+
+int w25_range_to_status(const struct flashctx *flash,
+ unsigned int start, unsigned int len,
+ struct w25q_status *status)
+{
+ struct wp_range_descriptor *descrs;
+ int i, range_found = 0;
+ int num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ struct wp_range *r = &descrs[i].range;
+
+ msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
+ start, len, r->start, r->len);
+ if ((start == r->start) && (len == r->len)) {
+ status->bp0 = descrs[i].bp & 1;
+ status->bp1 = descrs[i].bp >> 1;
+ status->bp2 = descrs[i].bp >> 2;
+ status->tb = descrs[i].m.tb;
+ status->sec = descrs[i].m.sec;
+
+ range_found = 1;
+ break;
+ }
+ }
+
+ if (!range_found) {
+ msg_cerr("%s: matching range not found\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int w25_status_to_range(const struct flashctx *flash,
+ const struct w25q_status *status,
+ unsigned int *start, unsigned int *len)
+{
+ struct wp_range_descriptor *descrs;
+ int i, status_found = 0;
+ int num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ int bp;
+ int table_bp, table_tb, table_sec;
+
+ bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
+ msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
+ bp, descrs[i].bp,
+ status->tb, descrs[i].m.tb,
+ status->sec, descrs[i].m.sec);
+ table_bp = descrs[i].bp;
+ table_tb = descrs[i].m.tb;
+ table_sec = descrs[i].m.sec;
+ if ((bp == table_bp || table_bp == X) &&
+ (status->tb == table_tb || table_tb == X) &&
+ (status->sec == table_sec || table_sec == X)) {
+ *start = descrs[i].range.start;
+ *len = descrs[i].range.len;
+
+ status_found = 1;
+ break;
+ }
+ }
+
+ if (!status_found) {
+ msg_cerr("matching status not found\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Given a [start, len], this function calls w25_range_to_status() to convert
+ * it to flash-chip-specific range bits, then sets into status register.
+ */
+static int w25_set_range(const struct flashctx *flash,
+ unsigned int start, unsigned int len)
+{
+ struct w25q_status status;
+ int tmp = 0;
+ int expected = 0;
+
+ memset(&status, 0, sizeof(status));
+ tmp = do_read_status(flash);
+ memcpy(&status, &tmp, 1);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
+
+ if (w25_range_to_status(flash, start, len, &status))
+ return -1;
+
+ msg_cdbg("status.busy: %x\n", status.busy);
+ msg_cdbg("status.wel: %x\n", status.wel);
+ msg_cdbg("status.bp0: %x\n", status.bp0);
+ msg_cdbg("status.bp1: %x\n", status.bp1);
+ msg_cdbg("status.bp2: %x\n", status.bp2);
+ msg_cdbg("status.tb: %x\n", status.tb);
+ msg_cdbg("status.sec: %x\n", status.sec);
+ msg_cdbg("status.srp0: %x\n", status.srp0);
+
+ memcpy(&expected, &status, sizeof(status));
+ do_write_status(flash, expected);
+
+ tmp = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
+ if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) {
+ msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
+ expected, tmp);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Print out the current status register value with human-readable text. */
+static int w25_wp_status(const struct flashctx *flash)
+{
+ struct w25q_status status;
+ int tmp;
+ unsigned int start, len;
+ int ret = 0;
+
+ memset(&status, 0, sizeof(status));
+ tmp = do_read_status(flash);
+ memcpy(&status, &tmp, 1);
+ msg_cinfo("WP: status: 0x%02x\n", tmp);
+ msg_cinfo("WP: status.srp0: %x\n", status.srp0);
+ msg_cinfo("WP: write protect is %s.\n",
+ status.srp0 ? "enabled" : "disabled");
+
+ msg_cinfo("WP: write protect range: ");
+ if (w25_status_to_range(flash, &status, &start, &len)) {
+ msg_cinfo("(cannot resolve the range)\n");
+ ret = -1;
+ } else {
+ msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
+ }
+
+ return ret;
+}
+
+static int w25q_large_range_to_status(const struct flashctx *flash,
+ unsigned int start, unsigned int len,
+ struct w25q_status_large *status)
+{
+ struct wp_range_descriptor *descrs;
+ int i, range_found = 0;
+ int num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ struct wp_range *r = &descrs[i].range;
+
+ msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
+ start, len, r->start, r->len);
+ if ((start == r->start) && (len == r->len)) {
+ status->bp0 = descrs[i].bp & 1;
+ status->bp1 = descrs[i].bp >> 1;
+ status->bp2 = descrs[i].bp >> 2;
+ status->bp3 = descrs[i].bp >> 3;
+ /*
+ * For MX25U12835E chip, Top/Bottom (T/B) bit is not
+ * part of status register and in that bit position is
+ * Quad Enable (QE)
+ */
+ if (flash->chip->manufacture_id != MACRONIX_ID ||
+ flash->chip->model_id != MACRONIX_MX25U12835E)
+ status->tb = descrs[i].m.tb;
+
+ range_found = 1;
+ break;
+ }
+ }
+
+ if (!range_found) {
+ msg_cerr("%s: matching range not found\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int w25_large_status_to_range(const struct flashctx *flash,
+ const struct w25q_status_large *status,
+ unsigned int *start, unsigned int *len)
+{
+ struct wp_range_descriptor *descrs;
+ int i, status_found = 0;
+ int num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ int bp;
+ int table_bp, table_tb;
+
+ bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) |
+ (status->bp3 << 3);
+ msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n",
+ bp, descrs[i].bp,
+ status->tb, descrs[i].m.tb);
+ table_bp = descrs[i].bp;
+ table_tb = descrs[i].m.tb;
+ if ((bp == table_bp || table_bp == X) &&
+ (status->tb == table_tb || table_tb == X)) {
+ *start = descrs[i].range.start;
+ *len = descrs[i].range.len;
+
+ status_found = 1;
+ break;
+ }
+ }
+
+ if (!status_found) {
+ msg_cerr("matching status not found\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Given a [start, len], this function calls w25_range_to_status() to convert
+ * it to flash-chip-specific range bits, then sets into status register.
+ * Returns 0 if successful, -1 on error, and 1 if reading back was different.
+ */
+static int w25q_large_set_range(const struct flashctx *flash,
+ unsigned int start, unsigned int len)
+{
+ struct w25q_status_large status;
+ int tmp;
+ int expected = 0;
+
+ memset(&status, 0, sizeof(status));
+ tmp = do_read_status(flash);
+ memcpy(&status, &tmp, 1);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
+
+ if (w25q_large_range_to_status(flash, start, len, &status))
+ return -1;
+
+ msg_cdbg("status.busy: %x\n", status.busy);
+ msg_cdbg("status.wel: %x\n", status.wel);
+ msg_cdbg("status.bp0: %x\n", status.bp0);
+ msg_cdbg("status.bp1: %x\n", status.bp1);
+ msg_cdbg("status.bp2: %x\n", status.bp2);
+ msg_cdbg("status.bp3: %x\n", status.bp3);
+ msg_cdbg("status.tb: %x\n", status.tb);
+ msg_cdbg("status.srp0: %x\n", status.srp0);
+
+ memcpy(&expected, &status, sizeof(status));
+ do_write_status(flash, expected);
+
+ tmp = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
+ if ((tmp & MASK_WP_AREA_LARGE) != (expected & MASK_WP_AREA_LARGE)) {
+ msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
+ expected, tmp);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int w25q_large_wp_status(const struct flashctx *flash)
+{
+ struct w25q_status_large sr1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp[2];
+ unsigned int start, len;
+ int ret = 0;
+
+ memset(&sr1, 0, sizeof(sr1));
+ tmp[0] = do_read_status(flash);
+ memcpy(&sr1, &tmp[0], 1);
+
+ memset(&sr2, 0, sizeof(sr2));
+ tmp[1] = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp[1], 1);
+
+ msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
+ msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
+ msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
+ msg_cinfo("WP: write protect is %s.\n",
+ (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
+
+ msg_cinfo("WP: write protect range: ");
+ if (w25_large_status_to_range(flash, &sr1, &start, &len)) {
+ msg_cinfo("(cannot resolve the range)\n");
+ ret = -1;
+ } else {
+ msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
+ }
+
+ return ret;
+}
+
+/* Set/clear the SRP0 bit in the status register. */
+static int w25_set_srp0(const struct flashctx *flash, int enable)
+{
+ struct w25q_status status;
+ int tmp = 0;
+ int expected = 0;
+
+ memset(&status, 0, sizeof(status));
+ tmp = do_read_status(flash);
+ /* FIXME: this is NOT endian-free copy. */
+ memcpy(&status, &tmp, 1);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
+
+ status.srp0 = enable ? 1 : 0;
+ memcpy(&expected, &status, sizeof(status));
+ do_write_status(flash, expected);
+
+ tmp = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
+ if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
+ return 1;
+
+ return 0;
+}
+
+static int w25_enable_writeprotect(const struct flashctx *flash,
+ enum wp_mode wp_mode)
+{
+ int ret;
+
+ if (wp_mode != WP_MODE_HARDWARE) {
+ msg_cerr("%s(): unsupported write-protect mode\n", __func__);
+ return 1;
+ }
+
+ ret = w25_set_srp0(flash, 1);
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+ return ret;
+}
+
+static int w25_disable_writeprotect(const struct flashctx *flash)
+{
+ int ret;
+
+ ret = w25_set_srp0(flash, 0);
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+
+ return ret;
+}
+
+static int w25_list_ranges(const struct flashctx *flash)
+{
+ struct wp_range_descriptor *descrs;
+ int i, num_entries;
+
+ if (w25_range_table(flash, &descrs, &num_entries))
+ return -1;
+
+ for (i = 0; i < num_entries; i++) {
+ msg_cinfo("start: 0x%06x, length: 0x%06x\n",
+ descrs[i].range.start,
+ descrs[i].range.len);
+ }
+
+ return 0;
+}
+
+static int w25q_wp_status(const struct flashctx *flash)
+{
+ struct w25q_status sr1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp[2];
+ unsigned int start, len;
+ int ret = 0;
+
+ memset(&sr1, 0, sizeof(sr1));
+ tmp[0] = do_read_status(flash);
+ memcpy(&sr1, &tmp[0], 1);
+
+ memset(&sr2, 0, sizeof(sr2));
+ tmp[1] = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp[1], 1);
+
+ msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
+ msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
+ msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
+ msg_cinfo("WP: write protect is %s.\n",
+ (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
+
+ msg_cinfo("WP: write protect range: ");
+ if (w25_status_to_range(flash, &sr1, &start, &len)) {
+ msg_cinfo("(cannot resolve the range)\n");
+ ret = -1;
+ } else {
+ msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
+ }
+
+ return ret;
+}
+
+/*
+ * W25Q adds an optional byte to the standard WRSR opcode. If /CS is
+ * de-asserted after the first byte, then it acts like a JEDEC-standard
+ * WRSR command. if /CS is asserted, then the next data byte is written
+ * into status register 2.
+ */
+#define W25Q_WRSR_OUTSIZE 0x03
+static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = W25Q_WRSR_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution\n",
+ __func__);
+ }
+
+ /* WRSR performs a self-timed erase before the changes take effect. */
+ programmer_delay(100 * 1000);
+
+ return result;
+}
+
+/*
+ * Set/clear the SRP1 bit in status register 2.
+ * FIXME: make this more generic if other chips use the same SR2 layout
+ */
+static int w25q_set_srp1(const struct flashctx *flash, int enable)
+{
+ struct w25q_status sr1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp, expected;
+
+ tmp = do_read_status(flash);
+ memcpy(&sr1, &tmp, 1);
+ tmp = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp, 1);
+
+ msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp);
+
+ sr2.srp1 = enable ? 1 : 0;
+
+ memcpy(&expected, &sr2, 1);
+ w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2));
+
+ tmp = w25q_read_status_register_2(flash);
+ msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp);
+ if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA))
+ return 1;
+
+ return 0;
+}
+
+enum wp_mode get_wp_mode(const char *mode_str)
+{
+ enum wp_mode wp_mode = WP_MODE_UNKNOWN;
+
+ if (!strcasecmp(mode_str, "hardware"))
+ wp_mode = WP_MODE_HARDWARE;
+ else if (!strcasecmp(mode_str, "power_cycle"))
+ wp_mode = WP_MODE_POWER_CYCLE;
+ else if (!strcasecmp(mode_str, "permanent"))
+ wp_mode = WP_MODE_PERMANENT;
+
+ return wp_mode;
+}
+
+static int w25q_disable_writeprotect(const struct flashctx *flash,
+ enum wp_mode wp_mode)
+{
+ int ret = 1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp;
+
+ switch (wp_mode) {
+ case WP_MODE_HARDWARE:
+ ret = w25_set_srp0(flash, 0);
+ break;
+ case WP_MODE_POWER_CYCLE:
+ tmp = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp, 1);
+ if (sr2.srp1) {
+ msg_cerr("%s(): must disconnect power to disable "
+ "write-protection\n", __func__);
+ } else {
+ ret = 0;
+ }
+ break;
+ case WP_MODE_PERMANENT:
+ msg_cerr("%s(): cannot disable permanent write-protection\n",
+ __func__);
+ break;
+ default:
+ msg_cerr("%s(): invalid mode specified\n", __func__);
+ break;
+ }
+
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+
+ return ret;
+}
+
+static int w25q_disable_writeprotect_default(const struct flashctx *flash)
+{
+ return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE);
+}
+
+static int w25q_enable_writeprotect(const struct flashctx *flash,
+ enum wp_mode wp_mode)
+{
+ int ret = 1;
+ struct w25q_status sr1;
+ struct w25q_status_2 sr2;
+ uint8_t tmp;
+
+ switch (wp_mode) {
+ case WP_MODE_HARDWARE:
+ if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) {
+ msg_cerr("%s(): cannot disable power cycle WP mode\n",
+ __func__);
+ break;
+ }
+
+ tmp = do_read_status(flash);
+ memcpy(&sr1, &tmp, 1);
+ if (sr1.srp0)
+ ret = 0;
+ else
+ ret = w25_set_srp0(flash, 1);
+
+ break;
+ case WP_MODE_POWER_CYCLE:
+ if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) {
+ msg_cerr("%s(): cannot disable hardware WP mode\n",
+ __func__);
+ break;
+ }
+
+ tmp = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp, 1);
+ if (sr2.srp1)
+ ret = 0;
+ else
+ ret = w25q_set_srp1(flash, 1);
+
+ break;
+ case WP_MODE_PERMANENT:
+ tmp = do_read_status(flash);
+ memcpy(&sr1, &tmp, 1);
+ if (sr1.srp0 == 0) {
+ ret = w25_set_srp0(flash, 1);
+ if (ret) {
+ msg_perr("%s(): cannot enable SRP0 for "
+ "permanent WP\n", __func__);
+ break;
+ }
+ }
+
+ tmp = w25q_read_status_register_2(flash);
+ memcpy(&sr2, &tmp, 1);
+ if (sr2.srp1 == 0) {
+ ret = w25q_set_srp1(flash, 1);
+ if (ret) {
+ msg_perr("%s(): cannot enable SRP1 for "
+ "permanent WP\n", __func__);
+ break;
+ }
+ }
+
+ break;
+ default:
+ msg_perr("%s(): invalid mode %d\n", __func__, wp_mode);
+ break;
+ }
+
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+ return ret;
+}
+
+/* W25P, W25X, and many flash chips from various vendors */
+struct wp wp_w25 = {
+ .list_ranges = w25_list_ranges,
+ .set_range = w25_set_range,
+ .enable = w25_enable_writeprotect,
+ .disable = w25_disable_writeprotect,
+ .wp_status = w25_wp_status,
+
+};
+
+/* W25Q series has features such as a second status register and SFDP */
+struct wp wp_w25q = {
+ .list_ranges = w25_list_ranges,
+ .set_range = w25_set_range,
+ .enable = w25q_enable_writeprotect,
+ /*
+ * By default, disable hardware write-protection. We may change
+ * this later if we want to add fine-grained write-protect disable
+ * as a command-line option.
+ */
+ .disable = w25q_disable_writeprotect_default,
+ .wp_status = w25q_wp_status,
+};
+
+/* W25Q large series has 4 block-protect bits */
+struct wp wp_w25q_large = {
+ .list_ranges = w25_list_ranges,
+ .set_range = w25q_large_set_range,
+ .enable = w25q_enable_writeprotect,
+ /*
+ * By default, disable hardware write-protection. We may change
+ * this later if we want to add fine-grained write-protect disable
+ * as a command-line option.
+ */
+ .disable = w25q_disable_writeprotect_default,
+ .wp_status = w25q_large_wp_status,
+};
+
+struct wp_range_descriptor gd25q32_cmp0_ranges[] = {
+ /* none, bp4 and bp3 => don't care */
+ { { }, 0x00, {0, 0} },
+ { { }, 0x08, {0, 0} },
+ { { }, 0x10, {0, 0} },
+ { { }, 0x18, {0, 0} },
+
+ { { }, 0x01, {0x3f0000, 64 * 1024} },
+ { { }, 0x02, {0x3e0000, 128 * 1024} },
+ { { }, 0x03, {0x3c0000, 256 * 1024} },
+ { { }, 0x04, {0x380000, 512 * 1024} },
+ { { }, 0x05, {0x300000, 1024 * 1024} },
+ { { }, 0x06, {0x200000, 2048 * 1024} },
+
+ { { }, 0x09, {0x000000, 64 * 1024} },
+ { { }, 0x0a, {0x000000, 128 * 1024} },
+ { { }, 0x0b, {0x000000, 256 * 1024} },
+ { { }, 0x0c, {0x000000, 512 * 1024} },
+ { { }, 0x0d, {0x000000, 1024 * 1024} },
+ { { }, 0x0e, {0x000000, 2048 * 1024} },
+
+ /* all, bp4 and bp3 => don't care */
+ { { }, 0x07, {0x000000, 4096 * 1024} },
+ { { }, 0x0f, {0x000000, 4096 * 1024} },
+ { { }, 0x17, {0x000000, 4096 * 1024} },
+ { { }, 0x1f, {0x000000, 4096 * 1024} },
+
+ { { }, 0x11, {0x3ff000, 4 * 1024} },
+ { { }, 0x12, {0x3fe000, 8 * 1024} },
+ { { }, 0x13, {0x3fc000, 16 * 1024} },
+ { { }, 0x14, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
+ { { }, 0x15, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
+ { { }, 0x16, {0x3f8000, 32 * 1024} },
+
+ { { }, 0x19, {0x000000, 4 * 1024} },
+ { { }, 0x1a, {0x000000, 8 * 1024} },
+ { { }, 0x1b, {0x000000, 16 * 1024} },
+ { { }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
+ { { }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
+ { { }, 0x1e, {0x000000, 32 * 1024} },
+};
+
+struct wp_range_descriptor gd25q32_cmp1_ranges[] = {
+ /* All, bp4 and bp3 => don't care */
+ { { }, 0x00, {0x000000, 4096 * 1024} }, /* All */
+ { { }, 0x08, {0x000000, 4096 * 1024} },
+ { { }, 0x10, {0x000000, 4096 * 1024} },
+ { { }, 0x18, {0x000000, 4096 * 1024} },
+
+ { { }, 0x01, {0x000000, 4032 * 1024} },
+ { { }, 0x02, {0x000000, 3968 * 1024} },
+ { { }, 0x03, {0x000000, 3840 * 1024} },
+ { { }, 0x04, {0x000000, 3584 * 1024} },
+ { { }, 0x05, {0x000000, 3 * 1024 * 1024} },
+ { { }, 0x06, {0x000000, 2 * 1024 * 1024} },
+
+ { { }, 0x09, {0x010000, 4032 * 1024} },
+ { { }, 0x0a, {0x020000, 3968 * 1024} },
+ { { }, 0x0b, {0x040000, 3840 * 1024} },
+ { { }, 0x0c, {0x080000, 3584 * 1024} },
+ { { }, 0x0d, {0x100000, 3 * 1024 * 1024} },
+ { { }, 0x0e, {0x200000, 2 * 1024 * 1024} },
+
+ /* None, bp4 and bp3 => don't care */
+ { { }, 0x07, {0, 0} }, /* None */
+ { { }, 0x0f, {0, 0} },
+ { { }, 0x17, {0, 0} },
+ { { }, 0x1f, {0, 0} },
+
+ { { }, 0x11, {0x000000, 4092 * 1024} },
+ { { }, 0x12, {0x000000, 4088 * 1024} },
+ { { }, 0x13, {0x000000, 4080 * 1024} },
+ { { }, 0x14, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
+ { { }, 0x15, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
+ { { }, 0x16, {0x000000, 4064 * 1024} },
+
+ { { }, 0x19, {0x001000, 4092 * 1024} },
+ { { }, 0x1a, {0x002000, 4088 * 1024} },
+ { { }, 0x1b, {0x040000, 4080 * 1024} },
+ { { }, 0x1c, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
+ { { }, 0x1d, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
+ { { }, 0x1e, {0x080000, 4064 * 1024} },
+};
+
+static struct wp_context gd25q32_wp = {
+ /* TODO: map second status register */
+ .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
+};
+
+struct wp_range_descriptor gd25q128_cmp0_ranges[] = {
+ /* none, bp4 and bp3 => don't care, others = 0 */
+ { { .tb = 0 }, 0x00, {0, 0} },
+ { { .tb = 0 }, 0x08, {0, 0} },
+ { { .tb = 0 }, 0x10, {0, 0} },
+ { { .tb = 0 }, 0x18, {0, 0} },
+
+ { { .tb = 0 }, 0x01, {0xfc0000, 256 * 1024} },
+ { { .tb = 0 }, 0x02, {0xf80000, 512 * 1024} },
+ { { .tb = 0 }, 0x03, {0xf00000, 1024 * 1024} },
+ { { .tb = 0 }, 0x04, {0xe00000, 2048 * 1024} },
+ { { .tb = 0 }, 0x05, {0xc00000, 4096 * 1024} },
+ { { .tb = 0 }, 0x06, {0x800000, 8192 * 1024} },
+
+ { { .tb = 0 }, 0x09, {0x000000, 256 * 1024} },
+ { { .tb = 0 }, 0x0a, {0x000000, 512 * 1024} },
+ { { .tb = 0 }, 0x0b, {0x000000, 1024 * 1024} },
+ { { .tb = 0 }, 0x0c, {0x000000, 2048 * 1024} },
+ { { .tb = 0 }, 0x0d, {0x000000, 4096 * 1024} },
+ { { .tb = 0 }, 0x0e, {0x000000, 8192 * 1024} },
+
+ /* all, bp4 and bp3 => don't care, others = 1 */
+ { { .tb = 0 }, 0x07, {0x000000, 16384 * 1024} },
+ { { .tb = 0 }, 0x0f, {0x000000, 16384 * 1024} },
+ { { .tb = 0 }, 0x17, {0x000000, 16384 * 1024} },
+ { { .tb = 0 }, 0x1f, {0x000000, 16384 * 1024} },
+
+ { { .tb = 0 }, 0x11, {0xfff000, 4 * 1024} },
+ { { .tb = 0 }, 0x12, {0xffe000, 8 * 1024} },
+ { { .tb = 0 }, 0x13, {0xffc000, 16 * 1024} },
+ { { .tb = 0 }, 0x14, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
+ { { .tb = 0 }, 0x15, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
+
+ { { .tb = 0 }, 0x19, {0x000000, 4 * 1024} },
+ { { .tb = 0 }, 0x1a, {0x000000, 8 * 1024} },
+ { { .tb = 0 }, 0x1b, {0x000000, 16 * 1024} },
+ { { .tb = 0 }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
+ { { .tb = 0 }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
+ { { .tb = 0 }, 0x1e, {0x000000, 32 * 1024} },
+};
+
+struct wp_range_descriptor gd25q128_cmp1_ranges[] = {
+ /* none, bp4 and bp3 => don't care, others = 0 */
+ { { .tb = 1 }, 0x00, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x10, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x18, {0x000000, 16384 * 1024} },
+
+ { { .tb = 1 }, 0x01, {0x000000, 16128 * 1024} },
+ { { .tb = 1 }, 0x02, {0x000000, 15872 * 1024} },
+ { { .tb = 1 }, 0x03, {0x000000, 15360 * 1024} },
+ { { .tb = 1 }, 0x04, {0x000000, 14336 * 1024} },
+ { { .tb = 1 }, 0x05, {0x000000, 12288 * 1024} },
+ { { .tb = 1 }, 0x06, {0x000000, 8192 * 1024} },
+
+ { { .tb = 1 }, 0x09, {0x000000, 16128 * 1024} },
+ { { .tb = 1 }, 0x0a, {0x000000, 15872 * 1024} },
+ { { .tb = 1 }, 0x0b, {0x000000, 15360 * 1024} },
+ { { .tb = 1 }, 0x0c, {0x000000, 14336 * 1024} },
+ { { .tb = 1 }, 0x0d, {0x000000, 12288 * 1024} },
+ { { .tb = 1 }, 0x0e, {0x000000, 8192 * 1024} },
+
+ /* none, bp4 and bp3 => don't care, others = 1 */
+ { { .tb = 1 }, 0x07, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x0f, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x17, {0x000000, 16384 * 1024} },
+ { { .tb = 1 }, 0x1f, {0x000000, 16384 * 1024} },
+
+ { { .tb = 1 }, 0x11, {0x000000, 16380 * 1024} },
+ { { .tb = 1 }, 0x12, {0x000000, 16376 * 1024} },
+ { { .tb = 1 }, 0x13, {0x000000, 16368 * 1024} },
+ { { .tb = 1 }, 0x14, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
+ { { .tb = 1 }, 0x15, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
+
+ { { .tb = 1 }, 0x19, {0x001000, 16380 * 1024} },
+ { { .tb = 1 }, 0x1a, {0x002000, 16376 * 1024} },
+ { { .tb = 1 }, 0x1b, {0x004000, 16368 * 1024} },
+ { { .tb = 1 }, 0x1c, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
+ { { .tb = 1 }, 0x1d, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
+ { { .tb = 1 }, 0x1e, {0x008000, 16352 * 1024} },
+};
+
+static struct wp_context gd25q128_wp = {
+ /* TODO: map second and third status registers */
+ .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
+};
+
+/* FIXME: MX25L6406 has same ID as MX25L6405D */
+struct wp_range_descriptor mx25l6406e_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
+ { { }, 0x2, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
+ { { }, 0x3, {0x7a0000, 64 * 8 * 1024} }, /* blocks 120-127 */
+ { { }, 0x4, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
+ { { }, 0x5, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
+ { { }, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
+
+ { { }, 0x7, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0x9, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { { }, 0xa, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
+ { { }, 0xb, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
+ { { }, 0xc, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
+ { { }, 0xd, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
+ { { }, 0xe, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
+ { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
+};
+
+static struct wp_context mx25l6406e_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
+ .descrs = &mx25l6406e_ranges[0],
+};
+
+struct wp_range_descriptor mx25l6495f_tb0_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
+ { { }, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
+ { { }, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
+
+ { { }, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
+ { { }, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
+ { { }, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
+ { { }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
+ { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
+};
+
+struct wp_range_descriptor mx25l6495f_tb1_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
+ { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
+ { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
+ { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
+ { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
+ { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+ { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
+ { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
+};
+
+static struct wp_context mx25l6495f_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
+};
+
+struct wp_range_descriptor mx25l25635f_tb0_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* block 511 */
+ { { }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* blocks 510-511 */
+ { { }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* blocks 508-511 */
+ { { }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* blocks 504-511 */
+ { { }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* blocks 496-511 */
+ { { }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* blocks 480-511 */
+ { { }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* blocks 448-511 */
+ { { }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* blocks 384-511 */
+ { { }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* blocks 256-511 */
+ { { }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* all */
+};
+
+struct wp_range_descriptor mx25l25635f_tb1_ranges[] = {
+ { { }, 0, {0, 0} }, /* none */
+ { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
+ { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
+ { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
+ { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
+ { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
+ { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
+ { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
+ { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
+ { { }, 0x9, {0x000000, 64 * 256 * 1024} }, /* blocks 0-255 */
+ { { }, 0xa, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xb, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xc, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xd, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xe, {0x000000, 64 * 512 * 1024} }, /* all */
+ { { }, 0xf, {0x000000, 64 * 512 * 1024} }, /* all */
+};
+
+static struct wp_context mx25l25635f_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
+};
+
+#if 0
+struct wp_range_descriptor s25fs128s_ranges[] = {
+ { { .tb = 1 }, 0, {0, 0} }, /* none */
+ { { .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* lower 64th */
+ { { .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* lower 32nd */
+ { { .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* lower 16th */
+ { { .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* lower 8th */
+ { { .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* lower 4th */
+ { { .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* lower half */
+ { { .tb = 1 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
+
+ { { .tb = 0 }, 0, {0, 0} }, /* none */
+ { { .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* upper 64th */
+ { { .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* upper 32nd */
+ { { .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* upper 16th */
+ { { .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* upper 8th */
+ { { .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* upper 4th */
+ { { .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* upper half */
+ { { .tb = 0 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
+};
+
+static struct wp_context s25fs128s_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
+ .get_modifier_bits = s25f_get_modifier_bits,
+ .set_modifier_bits = s25f_set_modifier_bits,
+};
+
+
+struct wp_range_descriptor s25fl256s_ranges[] = {
+ { { .tb = 1 }, 0, {0, 0} }, /* none */
+ { { .tb = 1 }, 0x1, {0x000000, 512 * 1024} }, /* lower 64th */
+ { { .tb = 1 }, 0x2, {0x000000, 1024 * 1024} }, /* lower 32nd */
+ { { .tb = 1 }, 0x3, {0x000000, 2048 * 1024} }, /* lower 16th */
+ { { .tb = 1 }, 0x4, {0x000000, 4096 * 1024} }, /* lower 8th */
+ { { .tb = 1 }, 0x5, {0x000000, 8192 * 1024} }, /* lower 4th */
+ { { .tb = 1 }, 0x6, {0x000000, 16384 * 1024} }, /* lower half */
+ { { .tb = 1 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
+
+ { { .tb = 0 }, 0, {0, 0} }, /* none */
+ { { .tb = 0 }, 0x1, {0x1f80000, 512 * 1024} }, /* upper 64th */
+ { { .tb = 0 }, 0x2, {0x1f00000, 1024 * 1024} }, /* upper 32nd */
+ { { .tb = 0 }, 0x3, {0x1e00000, 2048 * 1024} }, /* upper 16th */
+ { { .tb = 0 }, 0x4, {0x1c00000, 4096 * 1024} }, /* upper 8th */
+ { { .tb = 0 }, 0x5, {0x1800000, 8192 * 1024} }, /* upper 4th */
+ { { .tb = 0 }, 0x6, {0x1000000, 16384 * 1024} }, /* upper half */
+ { { .tb = 0 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
+};
+
+static struct wp_context s25fl256s_wp = {
+ .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
+ .get_modifier_bits = s25f_get_modifier_bits,
+ .set_modifier_bits = s25f_set_modifier_bits,
+};
+#endif
+
+/* Given a flash chip, this function returns its writeprotect info. */
+static int generic_range_table(const struct flashctx *flash,
+ struct wp_context **wp,
+ int *num_entries)
+{
+ *wp = NULL;
+ *num_entries = 0;
+
+ switch (flash->chip->manufacture_id) {
+ case GIGADEVICE_ID:
+ switch(flash->chip->model_id) {
+
+ case GIGADEVICE_GD25LQ32:
+ case GIGADEVICE_GD25Q32: {
+ uint8_t sr1 = w25q_read_status_register_2(flash);
+ *wp = &gd25q32_wp;
+
+ if (!(sr1 & (1 << 6))) { /* CMP == 0 */
+ (*wp)->descrs = &gd25q32_cmp0_ranges[0];
+ *num_entries = ARRAY_SIZE(gd25q32_cmp0_ranges);
+ } else { /* CMP == 1 */
+ (*wp)->descrs = &gd25q32_cmp1_ranges[0];
+ *num_entries = ARRAY_SIZE(gd25q32_cmp1_ranges);
+ }
+
+ break;
+ }
+ case GIGADEVICE_GD25Q128:
+ case GIGADEVICE_GD25LQ128CD: {
+ uint8_t sr1 = w25q_read_status_register_2(flash);
+ *wp = &gd25q128_wp;
+
+ if (!(sr1 & (1 << 6))) { /* CMP == 0 */
+ (*wp)->descrs = &gd25q128_cmp0_ranges[0];
+ *num_entries = ARRAY_SIZE(gd25q128_cmp0_ranges);
+ } else { /* CMP == 1 */
+ (*wp)->descrs = &gd25q128_cmp1_ranges[0];
+ *num_entries = ARRAY_SIZE(gd25q128_cmp1_ranges);
+ }
+
+ break;
+ }
+ default:
+ msg_cerr("%s() %d: GigaDevice flash chip mismatch"
+ " (0x%04x), aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ case MACRONIX_ID:
+ switch (flash->chip->model_id) {
+ case MACRONIX_MX25L6405:
+ /* FIXME: MX25L64* chips have mixed capabilities and
+ share IDs */
+ *wp = &mx25l6406e_wp;
+ *num_entries = ARRAY_SIZE(mx25l6406e_ranges);
+ break;
+ case MACRONIX_MX25L6495F: {
+ uint8_t cr = mx25l_read_config_register(flash);
+
+ *wp = &mx25l6495f_wp;
+ if (!(cr & (1 << 3))) { /* T/B == 0 */
+ (*wp)->descrs = &mx25l6495f_tb0_ranges[0];
+ *num_entries = ARRAY_SIZE(mx25l6495f_tb0_ranges);
+ } else { /* T/B == 1 */
+ (*wp)->descrs = &mx25l6495f_tb1_ranges[0];
+ *num_entries = ARRAY_SIZE(mx25l6495f_tb1_ranges);
+ }
+ break;
+ }
+ case MACRONIX_MX25L25635F: {
+ uint8_t cr = mx25l_read_config_register(flash);
+
+ *wp = &mx25l25635f_wp;
+ if (!(cr & (1 << 3))) { /* T/B == 0 */
+ (*wp)->descrs = &mx25l25635f_tb0_ranges[0];
+ *num_entries = ARRAY_SIZE(mx25l25635f_tb0_ranges);
+ } else { /* T/B == 1 */
+ (*wp)->descrs = &mx25l25635f_tb1_ranges[0];
+ *num_entries = ARRAY_SIZE(mx25l25635f_tb1_ranges);
+ }
+ break;
+ }
+ default:
+ msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
+ ", aborting\n", __func__, __LINE__,
+ flash->chip->model_id);
+ return -1;
+ }
+ break;
+ //case SPANSION_ID:
+ // switch (flash->chip->model_id) {
+ // case SPANSION_S25FS128S_L:
+ // case SPANSION_S25FS128S_S: {
+ // *wp = &s25fs128s_wp;
+ // (*wp)->descrs = s25fs128s_ranges;
+ // *num_entries = ARRAY_SIZE(s25fs128s_ranges);
+ // break;
+ // }
+ // case SPANSION_S25FL256S_UL:
+ // case SPANSION_S25FL256S_US: {
+ // *wp = &s25fl256s_wp;
+ // (*wp)->descrs = s25fl256s_ranges;
+ // *num_entries = ARRAY_SIZE(s25fl256s_ranges);
+ // break;
+ // }
+ // default:
+ // msg_cerr("%s():%d Spansion flash chip mismatch (0x%04x)"
+ // ", aborting\n", __func__, __LINE__,
+ // flash->chip->model_id);
+ // return -1;
+ // }
+ // break;
+ default:
+ msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
+ __func__, flash->chip->manufacture_id);
+ return -1;
+ }
+
+ return 0;
+}
+
+static uint8_t generic_get_bp_mask(struct wp_context *wp)
+{
+ return ((1 << (wp->sr1.bp0_pos + wp->sr1.bp_bits)) - 1) ^ \
+ ((1 << wp->sr1.bp0_pos) - 1);
+}
+
+static uint8_t generic_get_status_check_mask(struct wp_context *wp)
+{
+ return generic_get_bp_mask(wp) | 1 << wp->sr1.srp_pos;
+}
+
+/* Given a [start, len], this function finds a block protect bit combination
+ * (if possible) and sets the corresponding bits in "status". Remaining bits
+ * are preserved. */
+static int generic_range_to_status(const struct flashctx *flash,
+ unsigned int start, unsigned int len,
+ uint8_t *status, uint8_t *check_mask)
+{
+ struct wp_context *wp;
+ struct wp_range_descriptor *r;
+ int i, range_found = 0, num_entries;
+ uint8_t bp_mask;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ bp_mask = generic_get_bp_mask(wp);
+
+ for (i = 0, r = &wp->descrs[0]; i < num_entries; i++, r++) {
+ msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
+ start, len, r->range.start, r->range.len);
+ if ((start == r->range.start) && (len == r->range.len)) {
+ *status &= ~(bp_mask);
+ *status |= r->bp << (wp->sr1.bp0_pos);
+
+ if (wp->set_modifier_bits) {
+ if (wp->set_modifier_bits(flash, &r->m) < 0) {
+ msg_cerr("error setting modifier "
+ "bits for range.\n");
+ return -1;
+ }
+ }
+
+ range_found = 1;
+ break;
+ }
+ }
+
+ if (!range_found) {
+ msg_cerr("%s: matching range not found\n", __func__);
+ return -1;
+ }
+
+ *check_mask = generic_get_status_check_mask(wp);
+ return 0;
+}
+
+static int generic_status_to_range(const struct flashctx *flash,
+ const uint8_t sr1, unsigned int *start, unsigned int *len)
+{
+ struct wp_context *wp;
+ struct wp_range_descriptor *r;
+ int num_entries, i, status_found = 0;
+ uint8_t sr1_bp;
+ struct modifier_bits m;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ /* modifier bits may be compared more than once, so get them here */
+ if (wp->get_modifier_bits && wp->get_modifier_bits(flash, &m) < 0)
+ return -1;
+
+ sr1_bp = (sr1 >> wp->sr1.bp0_pos) & ((1 << wp->sr1.bp_bits) - 1);
+
+ for (i = 0, r = &wp->descrs[0]; i < num_entries; i++, r++) {
+ if (wp->get_modifier_bits) {
+ if (memcmp(&m, &r->m, sizeof(m)))
+ continue;
+ }
+ msg_cspew("comparing 0x%02x 0x%02x\n", sr1_bp, r->bp);
+ if (sr1_bp == r->bp) {
+ *start = r->range.start;
+ *len = r->range.len;
+ status_found = 1;
+ break;
+ }
+ }
+
+ if (!status_found) {
+ msg_cerr("matching status not found\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Given a [start, len], this function calls generic_range_to_status() to
+ * convert it to flash-chip-specific range bits, then sets into status register.
+ */
+static int generic_set_range(const struct flashctx *flash,
+ unsigned int start, unsigned int len)
+{
+ uint8_t status, expected, check_mask;
+
+ status = do_read_status(flash);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, status);
+
+ expected = status; /* preserve non-bp bits */
+ if (generic_range_to_status(flash, start, len, &expected, &check_mask))
+ return -1;
+
+ do_write_status(flash, expected);
+
+ status = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
+ if ((status & check_mask) != (expected & check_mask)) {
+ msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
+ expected, status, check_mask);
+ return 1;
+ }
+ return 0;
+}
+
+/* Set/clear the status regsiter write protect bit in SR1. */
+static int generic_set_srp0(const struct flashctx *flash, int enable)
+{
+ uint8_t status, expected, check_mask;
+ struct wp_context *wp;
+ int num_entries;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ expected = do_read_status(flash);
+ msg_cdbg("%s: old status: 0x%02x\n", __func__, expected);
+
+ if (enable)
+ expected |= 1 << wp->sr1.srp_pos;
+ else
+ expected &= ~(1 << wp->sr1.srp_pos);
+
+ do_write_status(flash, expected);
+
+ status = do_read_status(flash);
+ msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
+
+ check_mask = generic_get_status_check_mask(wp);
+ msg_cdbg("%s: check mask: 0x%02x\n", __func__, check_mask);
+ if ((status & check_mask) != (expected & check_mask)) {
+ msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
+ expected, status, check_mask);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int generic_enable_writeprotect(const struct flashctx *flash,
+ enum wp_mode wp_mode)
+{
+ int ret;
+
+ if (wp_mode != WP_MODE_HARDWARE) {
+ msg_cerr("%s(): unsupported write-protect mode\n", __func__);
+ return 1;
+ }
+
+ ret = generic_set_srp0(flash, 1);
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+
+ return ret;
+}
+
+static int generic_disable_writeprotect(const struct flashctx *flash)
+{
+ int ret;
+
+ ret = generic_set_srp0(flash, 0);
+ if (ret)
+ msg_cerr("%s(): error=%d.\n", __func__, ret);
+
+ return ret;
+}
+
+static int generic_list_ranges(const struct flashctx *flash)
+{
+ struct wp_context *wp;
+ struct wp_range_descriptor *r;
+ int i, num_entries;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ r = &wp->descrs[0];
+ for (i = 0; i < num_entries; i++) {
+ msg_cinfo("start: 0x%06x, length: 0x%06x\n",
+ r->range.start, r->range.len);
+ r++;
+ }
+
+ return 0;
+}
+
+static int wp_context_status(const struct flashctx *flash)
+{
+ uint8_t sr1;
+ unsigned int start, len;
+ int ret = 0;
+ struct wp_context *wp;
+ int num_entries, wp_en;
+
+ if (generic_range_table(flash, &wp, &num_entries))
+ return -1;
+
+ sr1 = do_read_status(flash);
+ wp_en = (sr1 >> wp->sr1.srp_pos) & 1;
+
+ msg_cinfo("WP: status: 0x%04x\n", sr1);
+ msg_cinfo("WP: status.srp0: %x\n", wp_en);
+ /* FIXME: SRP1 is not really generic, but we probably should print
+ * it anyway to have consistent output. #legacycruft */
+ msg_cinfo("WP: status.srp1: %x\n", 0);
+ msg_cinfo("WP: write protect is %s.\n",
+ wp_en ? "enabled" : "disabled");
+
+ msg_cinfo("WP: write protect range: ");
+ if (generic_status_to_range(flash, sr1, &start, &len)) {
+ msg_cinfo("(cannot resolve the range)\n");
+ ret = -1;
+ } else {
+ msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
+ }
+
+ return ret;
+}
+
+struct wp wp_generic = {
+ .list_ranges = generic_list_ranges,
+ .set_range = generic_set_range,
+ .enable = generic_enable_writeprotect,
+ .disable = generic_disable_writeprotect,
+ .wp_status = wp_context_status,
+};
diff --git a/writeprotect.h b/writeprotect.h
new file mode 100644
index 0000000..d39b993
--- /dev/null
+++ b/writeprotect.h
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __WRITEPROTECT_H__
+#define __WRITEPROTECT_H__ 1
+
+enum wp_mode {
+ WP_MODE_UNKNOWN = -1,
+ WP_MODE_HARDWARE, /* hardware WP pin determines status */
+ WP_MODE_POWER_CYCLE, /* WP active until power off/on cycle */
+ WP_MODE_PERMANENT, /* status register permanently locked,
+ WP permanently enabled */
+};
+
+struct wp {
+ int (*list_ranges)(const struct flashctx *flash);
+ int (*set_range)(const struct flashctx *flash,
+ unsigned int start, unsigned int len);
+ int (*enable)(const struct flashctx *flash, enum wp_mode mode);
+ int (*disable)(const struct flashctx *flash);
+ int (*wp_status)(const struct flashctx *flash);
+};
+
+/* winbond w25-series */
+extern struct wp wp_w25; /* older winbond chips (w25p, w25x, etc) */
+extern struct wp wp_w25q;
+extern struct wp wp_w25q_large; /* large winbond chips (>= 32MB) */
+
+extern struct wp wp_generic;
+extern struct wp wp_wpce775x;
+
+enum wp_mode get_wp_mode(const char *mode_str);
+
+/*
+ * Generic write-protect stuff
+ */
+
+struct modifier_bits {
+ int sec; /* if 1, bp bits describe sectors */
+ int tb; /* value of top/bottom select bit */
+};
+
+#endif /* !__WRITEPROTECT_H__ */
--
To view, visit https://review.coreboot.org/c/flashrom/+/40325
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: Id93b5a1cb2da476fa8a7dde41d7b963024117474
Gerrit-Change-Number: 40325
Gerrit-PatchSet: 1
Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org>
Gerrit-MessageType: newchange
6
26