blob: c44ea338cef70f39f3f64c4d8f485ce4e6d5aaaf [file] [log] [blame]
David Hendricksd1c55d72010-08-24 15:14:19 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2010 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
David Hendricksd1c55d72010-08-24 15:14:19 -070016 */
17
David Hendricksf7924d12010-06-10 21:26:44 -070018#include <stdlib.h>
19#include <string.h>
Edward O'Callaghanb4300ca2019-09-03 16:15:21 +100020#include <strings.h>
David Hendricksf7924d12010-06-10 21:26:44 -070021
22#include "flash.h"
23#include "flashchips.h"
24#include "chipdrivers.h"
Louis Yung-Chieh Lo52aa9302010-09-06 10:45:02 +080025#include "spi.h"
David Hendricks23cd7782010-08-25 12:42:38 -070026#include "writeprotect.h"
David Hendricksf7924d12010-06-10 21:26:44 -070027
David Hendricks1c09f802012-10-03 11:03:48 -070028/*
David Hendricksf7924d12010-06-10 21:26:44 -070029 * The following procedures rely on look-up tables to match the user-specified
30 * range with the chip's supported ranges. This turned out to be the most
31 * elegant approach since diferent flash chips use different levels of
32 * granularity and methods to determine protected ranges. In other words,
David Hendrickse0512a72014-07-15 20:30:47 -070033 * be stupid and simple since clever arithmetic will not work for many chips.
David Hendricksf7924d12010-06-10 21:26:44 -070034 */
35
36struct wp_range {
37 unsigned int start; /* starting address */
38 unsigned int len; /* len */
39};
40
41enum bit_state {
42 OFF = 0,
43 ON = 1,
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +080044 X = -1 /* don't care. Must be bigger than max # of bp. */
David Hendricksf7924d12010-06-10 21:26:44 -070045};
46
David Hendrickse0512a72014-07-15 20:30:47 -070047/*
48 * Generic write-protection schema for 25-series SPI flash chips. This assumes
49 * there is a status register that contains one or more consecutive bits which
50 * determine which address range is protected.
51 */
52
53struct status_register_layout {
54 int bp0_pos; /* position of BP0 */
55 int bp_bits; /* number of block protect bits */
56 int srp_pos; /* position of status register protect enable bit */
57};
58
Edward O'Callaghan91b38272019-12-04 17:12:43 +110059/*
60 * The following ranges and functions are useful for representing the
61 * writeprotect schema in which there are typically 5 bits of
62 * relevant information stored in status register 1:
63 * m.sec: This bit indicates the units (sectors vs. blocks)
64 * m.tb: The top-bottom bit indicates if the affected range is at the top of
65 * the flash memory's address space or at the bottom.
66 * bp: Bitmask representing the number of affected sectors/blocks.
67 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +110068struct wp_range_descriptor {
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +110069 struct modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -070070 unsigned int bp; /* block protect bitfield */
71 struct wp_range range;
72};
73
Edward O'Callaghana3edcb22019-12-05 14:30:50 +110074struct wp_context {
David Hendrickse0512a72014-07-15 20:30:47 -070075 struct status_register_layout sr1; /* status register 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +110076 struct wp_range_descriptor *descrs;
David Hendrickse0512a72014-07-15 20:30:47 -070077};
78
Edward O'Callaghanc69f6b82019-12-05 16:49:21 +110079struct w25q_status {
80 /* this maps to register layout -- do not change ordering */
81 unsigned char busy : 1;
82 unsigned char wel : 1;
83 unsigned char bp0 : 1;
84 unsigned char bp1 : 1;
85 unsigned char bp2 : 1;
86 unsigned char tb : 1;
87 unsigned char sec : 1;
88 unsigned char srp0 : 1;
89} __attribute__ ((packed));
90
91/* Status register for large flash layouts with 4 BP bits */
92struct w25q_status_large {
93 unsigned char busy : 1;
94 unsigned char wel : 1;
95 unsigned char bp0 : 1;
96 unsigned char bp1 : 1;
97 unsigned char bp2 : 1;
98 unsigned char bp3 : 1;
99 unsigned char tb : 1;
100 unsigned char srp0 : 1;
101} __attribute__ ((packed));
102
103struct w25q_status_2 {
104 unsigned char srp1 : 1;
105 unsigned char qe : 1;
106 unsigned char rsvd : 6;
107} __attribute__ ((packed));
108
109int w25_range_to_status(const struct flashctx *flash,
110 unsigned int start, unsigned int len,
111 struct w25q_status *status);
112int w25_status_to_range(const struct flashctx *flash,
113 const struct w25q_status *status,
114 unsigned int *start, unsigned int *len);
115
David Hendrickse0512a72014-07-15 20:30:47 -0700116/*
David Hendrickse0512a72014-07-15 20:30:47 -0700117 * Mask to extract write-protect enable and range bits
118 * Status register 1:
119 * SRP0: bit 7
120 * range(BP2-BP0): bit 4-2
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800121 * range(BP3-BP0): bit 5-2 (large chips)
David Hendrickse0512a72014-07-15 20:30:47 -0700122 * Status register 2:
123 * SRP1: bit 1
124 */
125#define MASK_WP_AREA (0x9C)
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800126#define MASK_WP_AREA_LARGE (0x9C)
David Hendrickse0512a72014-07-15 20:30:47 -0700127#define MASK_WP2_AREA (0x01)
128
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000129static struct wp_range_descriptor en25f40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100130 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
131 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
132 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
133 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
134 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 448 * 1024} },
135 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 384 * 1024} },
136 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 256 * 1024} },
137 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 512 * 1024} },
David Hendricks57566ed2010-08-16 18:24:45 -0700138};
139
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000140static struct wp_range_descriptor en25q40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100141 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
142 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
143 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
144 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700145
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100146 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 448 * 1024} },
147 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 384 * 1024} },
148 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
149 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700150};
151
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000152static struct wp_range_descriptor en25q80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100153 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
154 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 1016 * 1024} },
155 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 1008 * 1024} },
156 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 992 * 1024} },
157 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 960 * 1024} },
158 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 896 * 1024} },
159 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 768 * 1024} },
160 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 1024 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700161};
162
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000163static struct wp_range_descriptor en25q32_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100164 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
165 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 4032 * 1024} },
166 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 3968 * 1024} },
167 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 3840 * 1024} },
168 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 3584 * 1024} },
169 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 3072 * 1024} },
170 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 2048 * 1024} },
171 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700172
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100173 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
174 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 4032 * 1024} },
175 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 3968 * 1024} },
176 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 3840 * 1024} },
177 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 3584 * 1024} },
178 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 3072 * 1024} },
179 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 2048 * 1024} },
180 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700181};
182
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000183static struct wp_range_descriptor en25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100184 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
185 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8128 * 1024} },
186 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 8064 * 1024} },
187 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7936 * 1024} },
188 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7680 * 1024} },
189 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 7168 * 1024} },
190 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 6144 * 1024} },
191 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700192
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100193 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
194 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 8128 * 1024} },
195 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 8064 * 1024} },
196 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 7936 * 1024} },
197 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 7680 * 1024} },
198 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 7168 * 1024} },
199 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 6144 * 1024} },
200 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700201};
202
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000203static struct wp_range_descriptor en25q128_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100204 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
205 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16320 * 1024} },
206 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 16256 * 1024} },
207 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 16128 * 1024} },
208 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 15872 * 1024} },
209 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 15360 * 1024} },
210 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 14336 * 1024} },
211 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 16384 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700212
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100213 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
214 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 16320 * 1024} },
215 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 16256 * 1024} },
216 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 16128 * 1024} },
217 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 15872 * 1024} },
218 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 15360 * 1024} },
219 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 14336 * 1024} },
220 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 16384 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700221};
222
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000223static struct wp_range_descriptor en25s64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100224 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
225 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8064 * 1024} },
226 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 7936 * 1024} },
227 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7680 * 1024} },
228 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7168 * 1024} },
229 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 6144 * 1024} },
230 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 4096 * 1024} },
231 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
Marc Jonesb2f90022014-04-29 17:37:23 -0600232
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100233 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
234 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x7e0000, 128 * 1024} },
235 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x7c0000, 256 * 1024} },
236 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x780000, 512 * 1024} },
237 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x700000, 1024 * 1024} },
238 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x600000, 2048 * 1024} },
239 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x400000, 4096 * 1024} },
240 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
Marc Jonesb2f90022014-04-29 17:37:23 -0600241};
242
David Hendricksf8f00c72011-02-01 12:39:46 -0800243/* mx25l1005 ranges also work for the mx25l1005c */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100244static struct wp_range_descriptor mx25l1005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100245 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
246 { .m = { .sec = X, .tb = X }, 0x1, {0x010000, 64 * 1024} },
247 { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
248 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800249};
250
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100251static struct wp_range_descriptor mx25l2005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100252 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
253 { .m = { .sec = X, .tb = X }, 0x1, {0x030000, 64 * 1024} },
254 { .m = { .sec = X, .tb = X }, 0x2, {0x020000, 128 * 1024} },
255 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 256 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800256};
257
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100258static struct wp_range_descriptor mx25l4005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100259 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
260 { .m = { .sec = X, .tb = X }, 0x1, {0x070000, 64 * 1 * 1024} }, /* block 7 */
261 { .m = { .sec = X, .tb = X }, 0x2, {0x060000, 64 * 2 * 1024} }, /* blocks 6-7 */
262 { .m = { .sec = X, .tb = X }, 0x3, {0x040000, 64 * 4 * 1024} }, /* blocks 4-7 */
263 { .m = { .sec = X, .tb = X }, 0x4, {0x000000, 512 * 1024} },
264 { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 512 * 1024} },
265 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 512 * 1024} },
266 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 512 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800267};
268
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100269static struct wp_range_descriptor mx25l8005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100270 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
271 { .m = { .sec = X, .tb = X }, 0x1, {0x0f0000, 64 * 1 * 1024} }, /* block 15 */
272 { .m = { .sec = X, .tb = X }, 0x2, {0x0e0000, 64 * 2 * 1024} }, /* blocks 14-15 */
273 { .m = { .sec = X, .tb = X }, 0x3, {0x0c0000, 64 * 4 * 1024} }, /* blocks 12-15 */
274 { .m = { .sec = X, .tb = X }, 0x4, {0x080000, 64 * 8 * 1024} }, /* blocks 8-15 */
275 { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
276 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
277 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800278};
279
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100280static struct wp_range_descriptor mx25l1605d_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100281 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
282 { .m = { .sec = X, .tb = 0 }, 0x1, {0x1f0000, 64 * 1 * 1024} }, /* block 31 */
283 { .m = { .sec = X, .tb = 0 }, 0x2, {0x1e0000, 64 * 2 * 1024} }, /* blocks 30-31 */
284 { .m = { .sec = X, .tb = 0 }, 0x3, {0x1c0000, 64 * 4 * 1024} }, /* blocks 28-31 */
285 { .m = { .sec = X, .tb = 0 }, 0x4, {0x180000, 64 * 8 * 1024} }, /* blocks 24-31 */
286 { .m = { .sec = X, .tb = 0 }, 0x5, {0x100000, 64 * 16 * 1024} }, /* blocks 16-31 */
287 { .m = { .sec = X, .tb = 0 }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
288 { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
David Hendricksf8f00c72011-02-01 12:39:46 -0800289
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100290 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 2048 * 1024} },
291 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
292 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
293 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 64 * 24 * 1024} }, /* blocks 0-23 */
294 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 64 * 28 * 1024} }, /* blocks 0-27 */
295 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 64 * 30 * 1024} }, /* blocks 0-29 */
296 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 64 * 31 * 1024} }, /* blocks 0-30 */
297 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
David Hendricksf8f00c72011-02-01 12:39:46 -0800298};
299
300/* FIXME: Is there an mx25l3205 (without a trailing letter)? */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100301static struct wp_range_descriptor mx25l3205d_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100302 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
303 { .m = { .sec = X, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
304 { .m = { .sec = X, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
305 { .m = { .sec = X, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
306 { .m = { .sec = X, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
307 { .m = { .sec = X, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
308 { .m = { .sec = X, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
309 { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksac72e362010-08-16 18:20:03 -0700310
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100311 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
312 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
313 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
314 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
315 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
316 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
317 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
318 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksac72e362010-08-16 18:20:03 -0700319};
320
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100321static struct wp_range_descriptor mx25u3235e_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100322 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
323 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
324 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
325 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
326 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
327 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
328 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
329 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
Vincent Palatin87e092a2013-02-28 15:46:14 -0800330
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100331 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
332 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
333 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
334 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
335 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
336 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
337 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
338 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
Vincent Palatin87e092a2013-02-28 15:46:14 -0800339};
340
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100341static struct wp_range_descriptor mx25u6435e_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100342 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
343 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 1 * 64 * 1024} }, /* block 127 */
344 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
345 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
346 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
347 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
348 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
349 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
Jongpil66a96492014-08-14 17:59:06 +0900350
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100351 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
352 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 96 * 64 * 1024} }, /* blocks 0-95 */
353 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 112 * 64 * 1024} }, /* blocks 0-111 */
354 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 120 * 64 * 1024} }, /* blocks 0-119 */
355 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 124 * 64 * 1024} }, /* blocks 0-123 */
356 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 126 * 64 * 1024} }, /* blocks 0-125 */
357 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 127 * 64 * 1024} }, /* blocks 0-126 */
358 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
Jongpil66a96492014-08-14 17:59:06 +0900359};
360
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600361#define MX25U12835E_TB (1 << 3)
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100362static struct wp_range_descriptor mx25u12835e_tb0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100363 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
364 { .m = { .sec = 0, .tb = 0 }, 0x1, {0xff0000, 1 * 64 * 1024} }, /* block 255 */
365 { .m = { .sec = 0, .tb = 0 }, 0x2, {0xfe0000, 2 * 64 * 1024} }, /* blocks 254-255 */
366 { .m = { .sec = 0, .tb = 0 }, 0x3, {0xfc0000, 4 * 64 * 1024} }, /* blocks 252-255 */
367 { .m = { .sec = 0, .tb = 0 }, 0x4, {0xf80000, 8 * 64 * 1024} }, /* blocks 248-255 */
368 { .m = { .sec = 0, .tb = 0 }, 0x5, {0xf00000, 16 * 64 * 1024} }, /* blocks 240-255 */
369 { .m = { .sec = 0, .tb = 0 }, 0x6, {0xe00000, 32 * 64 * 1024} }, /* blocks 224-255 */
370 { .m = { .sec = 0, .tb = 0 }, 0x7, {0xc00000, 64 * 64 * 1024} }, /* blocks 192-255 */
371 { .m = { .sec = 0, .tb = 0 }, 0x8, {0x800000, 128 * 64 * 1024} }, /* blocks 128-255 */
372 { .m = { .sec = 0, .tb = 0 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
373 { .m = { .sec = 0, .tb = 0 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
374 { .m = { .sec = 0, .tb = 0 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
375 { .m = { .sec = 0, .tb = 0 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
376 { .m = { .sec = 0, .tb = 0 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
377 { .m = { .sec = 0, .tb = 0 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
378 { .m = { .sec = 0, .tb = 0 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600379};
Alex Lu831c6092017-11-02 23:19:34 -0700380
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100381static struct wp_range_descriptor mx25u12835e_tb1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100382 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 1 * 64 * 1024} }, /* block 0 */
383 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
384 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
385 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
386 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
387 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
388 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
389 { .m = { .sec = 0, .tb = 1 }, 0x8, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
390 { .m = { .sec = 0, .tb = 1 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
391 { .m = { .sec = 0, .tb = 1 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
392 { .m = { .sec = 0, .tb = 1 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
393 { .m = { .sec = 0, .tb = 1 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
394 { .m = { .sec = 0, .tb = 1 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
395 { .m = { .sec = 0, .tb = 1 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
396 { .m = { .sec = 0, .tb = 1 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Alex Lu831c6092017-11-02 23:19:34 -0700397};
398
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100399static struct wp_range_descriptor n25q064_ranges[] = {
David Hendricksfe9123b2015-04-21 13:18:31 -0700400 /*
401 * Note: For N25Q064, sec (usually in bit position 6) is called BP3
402 * (block protect bit 3). It is only useful when all blocks are to
403 * be write-protected.
404 */
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100405 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
David Hendricksbfa624b2012-07-24 12:47:59 -0700406
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100407 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 64 * 1024} }, /* block 127 */
408 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
409 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
410 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
411 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
412 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
413 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700414
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100415 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, /* block 0 */
416 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
417 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
418 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
419 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
420 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
421 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700422
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100423 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 128 * 64 * 1024} }, /* all */
424 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 128 * 64 * 1024} }, /* all */
425 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 128 * 64 * 1024} }, /* all */
426 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 128 * 64 * 1024} }, /* all */
427 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 128 * 64 * 1024} }, /* all */
428 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 128 * 64 * 1024} }, /* all */
429 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 128 * 64 * 1024} }, /* all */
430 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* all */
David Hendricksbfa624b2012-07-24 12:47:59 -0700431};
432
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100433static struct wp_range_descriptor w25q16_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100434 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
435 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x1f0000, 64 * 1024} },
436 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x1e0000, 128 * 1024} },
437 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x1c0000, 256 * 1024} },
438 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x180000, 512 * 1024} },
439 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x100000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700440
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100441 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
442 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
443 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
444 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
445 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
446 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 2048 * 1024} },
447 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700448
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100449 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
450 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
451 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
452 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
453 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700454
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100455 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
456 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
457 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
458 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
459 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700460};
461
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100462static struct wp_range_descriptor w25q32_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100463 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
464 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
465 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
466 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
467 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
468 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
469 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700470
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100471 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
472 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
473 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
474 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
475 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
476 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 2048 * 1024} },
477 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700478
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100479 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x3ff000, 4 * 1024} },
480 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x3fe000, 8 * 1024} },
481 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x3fc000, 16 * 1024} },
482 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x3f8000, 32 * 1024} },
483 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x3f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700484
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100485 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
486 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
487 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
488 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
489 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700490};
491
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100492static struct wp_range_descriptor w25q80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100493 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
494 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0f0000, 64 * 1024} },
495 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0e0000, 128 * 1024} },
496 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0c0000, 256 * 1024} },
497 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700498
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100499 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
500 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
501 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
502 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
503 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
504 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700505
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100506 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
507 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
508 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
509 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
510 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700511
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100512 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
513 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
514 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
515 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
516 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700517};
518
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100519static struct wp_range_descriptor w25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100520 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
David Hendricks2c4a76c2010-06-28 14:00:43 -0700521
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100522 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
523 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
524 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
525 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
526 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
527 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700528
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100529 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
530 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
531 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
532 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
533 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
534 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
535 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700536
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100537 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
538 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
539 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
540 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
541 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700542
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100543 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
544 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
545 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
546 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
547 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700548};
549
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100550static struct wp_range_descriptor w25rq128_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100551 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* NONE */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530552
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100553 { .m = { .sec = 0, .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* Upper 1/64 */
554 { .m = { .sec = 0, .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* Upper 1/32 */
555 { .m = { .sec = 0, .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* Upper 1/16 */
556 { .m = { .sec = 0, .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* Upper 1/8 */
557 { .m = { .sec = 0, .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* Upper 1/4 */
558 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* Upper 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530559
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100560 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* Lower 1/64 */
561 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* Lower 1/32 */
562 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* Lower 1/16 */
563 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* Lower 1/8 */
564 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* Lower 1/4 */
565 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* Lower 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530566
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100567 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 16384 * 1024} }, /* ALL */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530568
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100569 { .m = { .sec = 1, .tb = 0 }, 0x1, {0xfff000, 4 * 1024} }, /* Upper 1/4096 */
570 { .m = { .sec = 1, .tb = 0 }, 0x2, {0xffe000, 8 * 1024} }, /* Upper 1/2048 */
571 { .m = { .sec = 1, .tb = 0 }, 0x3, {0xffc000, 16 * 1024} }, /* Upper 1/1024 */
572 { .m = { .sec = 1, .tb = 0 }, 0x4, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
573 { .m = { .sec = 1, .tb = 0 }, 0x5, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700574
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100575 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, /* Lower 1/4096 */
576 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, /* Lower 1/2048 */
577 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, /* Lower 1/1024 */
578 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, /* Lower 1/512 */
579 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, /* Lower 1/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700580};
581
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100582static struct wp_range_descriptor w25rq128_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100583 { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 16 * 1024 * 1024} }, /* ALL */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700584
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100585 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16128 * 1024} }, /* Lower 63/64 */
586 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 15872 * 1024} }, /* Lower 31/32 */
587 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 15 * 1024 * 1024} }, /* Lower 15/16 */
588 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 14 * 1024 * 1024} }, /* Lower 7/8 */
589 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 12 * 1024 * 1024} }, /* Lower 3/4 */
590 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 8 * 1024 * 1024} }, /* Lower 1/2 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700591
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100592 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x040000, 16128 * 1024} }, /* Upper 63/64 */
593 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x080000, 15872 * 1024} }, /* Upper 31/32 */
594 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x100000, 15 * 1024 * 1024} }, /* Upper 15/16 */
595 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x200000, 14 * 1024 * 1024} }, /* Upper 7/8 */
596 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x400000, 12 * 1024 * 1024} }, /* Upper 3/4 */
597 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x800000, 8 * 1024 * 1024} }, /* Upper 1/2 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700598
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100599 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 0} }, /* NONE */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700600
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100601 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 16380 * 1024} }, /* Lower 4095/4096 */
602 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 16376 * 1024} }, /* Lower 2048/2048 */
603 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 16368 * 1024} }, /* Lower 1023/1024 */
604 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
605 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700606
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100607 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 16380 * 1024} }, /* Upper 4095/4096 */
608 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 16376 * 1024} }, /* Upper 2047/2048 */
609 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 16368 * 1024} }, /* Upper 1023/1024 */
610 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
611 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530612};
613
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100614static struct wp_range_descriptor w25rq256_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100615 { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 0x0000000} }, /* NONE */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800616
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100617 { .m = { .sec = X, .tb = 0 }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* Upper 1/512 */
618 { .m = { .sec = X, .tb = 0 }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* Upper 1/256 */
619 { .m = { .sec = X, .tb = 0 }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* Upper 1/128 */
620 { .m = { .sec = X, .tb = 0 }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* Upper 1/64 */
621 { .m = { .sec = X, .tb = 0 }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* Upper 1/32 */
622 { .m = { .sec = X, .tb = 0 }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* Upper 1/16 */
623 { .m = { .sec = X, .tb = 0 }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* Upper 1/8 */
624 { .m = { .sec = X, .tb = 0 }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* Upper 1/4 */
625 { .m = { .sec = X, .tb = 0 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800626
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100627 { .m = { .sec = X, .tb = 1 }, 0x1, {0x0000000, 64 * 1 * 1024} }, /* Lower 1/512 */
628 { .m = { .sec = X, .tb = 1 }, 0x2, {0x0000000, 64 * 2 * 1024} }, /* Lower 1/256 */
629 { .m = { .sec = X, .tb = 1 }, 0x3, {0x0000000, 64 * 4 * 1024} }, /* Lower 1/128 */
630 { .m = { .sec = X, .tb = 1 }, 0x4, {0x0000000, 64 * 8 * 1024} }, /* Lower 1/64 */
631 { .m = { .sec = X, .tb = 1 }, 0x5, {0x0000000, 64 * 16 * 1024} }, /* Lower 1/32 */
632 { .m = { .sec = X, .tb = 1 }, 0x6, {0x0000000, 64 * 32 * 1024} }, /* Lower 1/16 */
633 { .m = { .sec = X, .tb = 1 }, 0x7, {0x0000000, 64 * 64 * 1024} }, /* Lower 1/8 */
634 { .m = { .sec = X, .tb = 1 }, 0x8, {0x0000000, 64 * 128 * 1024} }, /* Lower 1/4 */
635 { .m = { .sec = X, .tb = 1 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800636
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100637 { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* ALL */
638 { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* ALL */
639 { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* ALL */
640 { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* ALL */
641 { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* ALL */
642 { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* ALL */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800643};
644
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100645static struct wp_range_descriptor w25rq256_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100646 { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 64 * 512 * 1024} }, /* ALL */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800647
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100648 { .m = { .sec = X, .tb = 0 }, 0x1, {0x0000000, 64 * 511 * 1024} }, /* Lower 511/512 */
649 { .m = { .sec = X, .tb = 0 }, 0x2, {0x0000000, 64 * 510 * 1024} }, /* Lower 255/256 */
650 { .m = { .sec = X, .tb = 0 }, 0x3, {0x0000000, 64 * 508 * 1024} }, /* Lower 127/128 */
651 { .m = { .sec = X, .tb = 0 }, 0x4, {0x0000000, 64 * 504 * 1024} }, /* Lower 63/64 */
652 { .m = { .sec = X, .tb = 0 }, 0x5, {0x0000000, 64 * 496 * 1024} }, /* Lower 31/32 */
653 { .m = { .sec = X, .tb = 0 }, 0x6, {0x0000000, 64 * 480 * 1024} }, /* Lower 15/16 */
654 { .m = { .sec = X, .tb = 0 }, 0x7, {0x0000000, 64 * 448 * 1024} }, /* Lower 7/8 */
655 { .m = { .sec = X, .tb = 0 }, 0x8, {0x0000000, 64 * 384 * 1024} }, /* Lower 3/4 */
656 { .m = { .sec = X, .tb = 0 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800657
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100658 { .m = { .sec = X, .tb = 1 }, 0x1, {0x0010000, 64 * 511 * 1024} }, /* Upper 511/512 */
659 { .m = { .sec = X, .tb = 1 }, 0x2, {0x0020000, 64 * 510 * 1024} }, /* Upper 255/256 */
660 { .m = { .sec = X, .tb = 1 }, 0x3, {0x0040000, 64 * 508 * 1024} }, /* Upper 127/128 */
661 { .m = { .sec = X, .tb = 1 }, 0x4, {0x0080000, 64 * 504 * 1024} }, /* Upper 63/64 */
662 { .m = { .sec = X, .tb = 1 }, 0x5, {0x0100000, 64 * 496 * 1024} }, /* Upper 31/32 */
663 { .m = { .sec = X, .tb = 1 }, 0x6, {0x0200000, 64 * 480 * 1024} }, /* Upper 15/16 */
664 { .m = { .sec = X, .tb = 1 }, 0x7, {0x0400000, 64 * 448 * 1024} }, /* Upper 7/8 */
665 { .m = { .sec = X, .tb = 1 }, 0x8, {0x0800000, 64 * 384 * 1024} }, /* Upper 3/4 */
666 { .m = { .sec = X, .tb = 1 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800667
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100668 { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 0x0000000} }, /* NONE */
669 { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 0x0000000} }, /* NONE */
670 { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 0x0000000} }, /* NONE */
671 { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 0x0000000} }, /* NONE */
672 { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 0x0000000} }, /* NONE */
673 { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 0x0000000} }, /* NONE */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800674};
675
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000676static struct wp_range_descriptor w25x10_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100677 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
678 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x010000, 64 * 1024} },
679 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
680 { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
681 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800682};
683
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000684static struct wp_range_descriptor w25x20_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100685 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
686 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x030000, 64 * 1024} },
687 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x020000, 128 * 1024} },
688 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
689 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
690 { .m = { .sec = 0, .tb = X }, 0x3, {0x000000, 256 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800691};
692
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000693static struct wp_range_descriptor w25x40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100694 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
695 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
696 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
697 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
698 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
699 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
700 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
701 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} },
702 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} },
703 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} },
704 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} },
David Hendricks470ca952010-08-13 14:01:53 -0700705};
706
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000707static struct wp_range_descriptor w25x80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100708 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
709 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0F0000, 64 * 1024} },
710 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0E0000, 128 * 1024} },
711 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0C0000, 256 * 1024} },
712 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
713 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
714 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
715 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
716 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
717 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
718 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
719 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800720};
721
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100722static struct wp_range_descriptor gd25q40_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100723 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* None */
724 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
725 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
726 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
727 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
728 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
729 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
730 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} }, /* All */
731 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} }, /* All */
732 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} }, /* All */
733 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
734 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x07F000, 4 * 1024} },
735 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x07E000, 8 * 1024} },
736 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x07C000, 16 * 1024} },
737 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x078000, 32 * 1024} },
738 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x078000, 32 * 1024} },
739 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x078000, 32 * 1024} },
740 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
741 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
742 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
743 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
744 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
745 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
746 { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600747};
748
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100749static struct wp_range_descriptor gd25q40_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100750 { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 512 * 1024} }, /* ALL */
751 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 448 * 1024} },
752 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 384 * 1024} },
753 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 256 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600754
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100755 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 448 * 1024} },
756 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 384 * 1024} },
757 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 256 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600758
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100759 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 0} }, /* None */
760 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 0} }, /* None */
761 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 0} }, /* None */
762 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 0} }, /* None */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600763
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100764 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 508 * 1024} },
765 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 504 * 1024} },
766 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 496 * 1024} },
767 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 480 * 1024} },
768 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 480 * 1024} },
769 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x000000, 480 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600770
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100771 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 508 * 1024} },
772 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 504 * 1024} },
773 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 496 * 1024} },
774 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 480 * 1024} },
775 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 480 * 1024} },
776 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x008000, 480 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600777
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100778 { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 0} }, /* None */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600779};
780
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100781static struct wp_range_descriptor gd25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100782 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
783 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
784 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
785 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
786 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
787 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
788 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700789
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100790 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
791 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
792 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
793 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
794 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
795 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
796 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700797
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100798 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
799 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
800 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
801 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
802 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
803 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x7f8000, 32 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700804
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100805 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
806 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
807 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
808 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
809 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
810 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700811};
812
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100813static struct wp_range_descriptor a25l040_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100814 { .m = { .sec = X, .tb = X }, 0x0, {0, 0} }, /* none */
815 { .m = { .sec = X, .tb = X }, 0x1, {0x70000, 64 * 1024} },
816 { .m = { .sec = X, .tb = X }, 0x2, {0x60000, 128 * 1024} },
817 { .m = { .sec = X, .tb = X }, 0x3, {0x40000, 256 * 1024} },
818 { .m = { .sec = X, .tb = X }, 0x4, {0x00000, 512 * 1024} },
819 { .m = { .sec = X, .tb = X }, 0x5, {0x00000, 512 * 1024} },
820 { .m = { .sec = X, .tb = X }, 0x6, {0x00000, 512 * 1024} },
821 { .m = { .sec = X, .tb = X }, 0x7, {0x00000, 512 * 1024} },
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +0800822};
823
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100824struct wp *get_wp_for_flashchip(const struct flashchip *chip) {
825 // FIXME: The .wp field should be deleted from from struct flashchip
826 // completly, but linux_mtd and cros_ec still assign their own values
827 // to it. When they are cleaned up we can delete this.
828 if(chip->wp) return chip->wp;
829
830 switch (chip->manufacture_id) {
831 case WINBOND_NEX_ID:
832 switch(chip->model_id) {
833 case WINBOND_NEX_W25X10:
834 case WINBOND_NEX_W25X20:
835 case WINBOND_NEX_W25X40:
836 case WINBOND_NEX_W25X80:
837 case WINBOND_NEX_W25Q128_V_M:
838 return &wp_w25;
839 case WINBOND_NEX_W25Q80_V:
840 case WINBOND_NEX_W25Q16_V:
841 case WINBOND_NEX_W25Q32_V:
842 case WINBOND_NEX_W25Q32_W:
843 case WINBOND_NEX_W25Q32JW:
844 case WINBOND_NEX_W25Q64_V:
845 case WINBOND_NEX_W25Q64_W:
846 // W25Q64JW does not have a range table entry, but the flashchip
847 // set .wp to wp_25q, so keep it here until the issue is resolved
848 case WINBOND_NEX_W25Q64JW:
849 case WINBOND_NEX_W25Q128_DTR:
850 case WINBOND_NEX_W25Q128_V:
851 case WINBOND_NEX_W25Q128_W:
852 return &wp_w25q;
853 case WINBOND_NEX_W25Q256_V:
854 case WINBOND_NEX_W25Q256JV_M:
855 return &wp_w25q_large;
856 }
857 break;
858 case EON_ID_NOPREFIX:
859 switch (chip->model_id) {
860 case EON_EN25F40:
861 case EON_EN25Q40:
862 case EON_EN25Q80:
863 case EON_EN25Q32:
864 case EON_EN25Q64:
865 case EON_EN25Q128:
866 case EON_EN25QH128:
867 case EON_EN25S64:
868 return &wp_w25;
869 }
870 break;
871 case MACRONIX_ID:
872 switch (chip->model_id) {
873 case MACRONIX_MX25L1005:
874 case MACRONIX_MX25L2005:
875 case MACRONIX_MX25L4005:
876 case MACRONIX_MX25L8005:
877 case MACRONIX_MX25L1605:
878 case MACRONIX_MX25L3205:
879 case MACRONIX_MX25U3235E:
880 case MACRONIX_MX25U6435E:
881 return &wp_w25;
882 case MACRONIX_MX25U12835E:
883 return &wp_w25q_large;
884 case MACRONIX_MX25L6405:
885 case MACRONIX_MX25L6495F:
886 case MACRONIX_MX25L25635F:
887 return &wp_generic;
888 }
889 break;
890 case ST_ID:
891 switch(chip->model_id) {
892 case ST_N25Q064__1E:
893 case ST_N25Q064__3E:
894 return &wp_w25;
895 }
896 break;
897 case GIGADEVICE_ID:
898 switch(chip->model_id) {
899 case GIGADEVICE_GD25LQ32:
900 // GD25Q40 does not have a .wp field in flashchips.c, but
901 // it is in the w25 range table function, so note it here
902 // until the issue is resolved:
903 // case GIGADEVICE_GD25Q40:
904 case GIGADEVICE_GD25Q64:
905 case GIGADEVICE_GD25LQ64:
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100906 case GIGADEVICE_GD25Q128:
907 return &wp_w25;
908 case GIGADEVICE_GD25Q256D:
909 return &wp_w25q_large;
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100910 case GIGADEVICE_GD25LQ128CD:
911 case GIGADEVICE_GD25Q32:
912 return &wp_generic;
913 }
914 break;
915 case AMIC_ID_NOPREFIX:
916 switch(chip->model_id) {
917 case AMIC_A25L040:
918 return &wp_w25;
919 }
920 break;
921 case ATMEL_ID:
922 switch(chip->model_id) {
923 case ATMEL_AT25SF128A:
924 case ATMEL_AT25SL128A:
925 return &wp_w25q;
926 }
927 break;
928 case PROGMANUF_ID:
929 switch(chip->model_id) {
930 case PROGDEV_ID:
931 return &wp_w25;
932 }
933 break;
934 case SPANSION_ID:
935 switch (chip->model_id) {
936 case SPANSION_S25FS128S_L:
937 case SPANSION_S25FS128S_S:
938 case SPANSION_S25FL256S_UL:
939 case SPANSION_S25FL256S_US:
940 // SPANSION_S25FL128S_UL does not have a range table entry,
941 // but its flashchip set .wp to wp_generic, so keep it here
942 // until the issue resolved
943 case SPANSION_S25FL128S_UL:
944 // SPANSION_S25FL128S_US does not have a range table entry,
945 // but its flashchip set .wp to wp_generic, so keep it here
946 // until the issue resolved
947 case SPANSION_S25FL128S_US:
948 return &wp_generic;
949 }
950 break;
951 }
952
953
954 return NULL;
955}
956
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700957/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700958static uint8_t w25q_read_status_register_2(const struct flashctx *flash)
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700959{
960 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 };
961 unsigned char readarr[2];
962 int ret;
963
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +1100964 if (flash->chip->read_status) {
965 msg_cdbg("RDSR2 failed! cmd=0x35 unimpl for opaque chips\n");
966 return 0;
967 }
968
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700969 /* Read Status Register */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700970 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700971 if (ret) {
972 /*
973 * FIXME: make this a benign failure for now in case we are
974 * unable to execute the opcode
975 */
976 msg_cdbg("RDSR2 failed!\n");
977 readarr[0] = 0x00;
978 }
979
980 return readarr[0];
981}
982
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600983/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Edward O'Callaghandf43e902020-11-13 23:08:26 +1100984static uint8_t mx25l_read_config_register(const struct flashctx *flash)
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600985{
986 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x15 };
987 unsigned char readarr[2]; /* leave room for dummy byte */
988 int ret;
989
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +1100990 if (flash->chip->read_status) {
991 msg_cdbg("RDCR failed! cmd=0x15 unimpl for opaque chips\n");
992 return 0;
993 }
994
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600995 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
996 if (ret) {
997 msg_cdbg("RDCR failed!\n");
998 readarr[0] = 0x00;
999 }
1000
1001 return readarr[0];
1002}
1003
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001004/* Given a flash chip, this function returns its range table. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001005static int w25_range_table(const struct flashctx *flash,
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001006 struct wp_range_descriptor **descrs,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001007 int *num_entries)
David Hendricksf7924d12010-06-10 21:26:44 -07001008{
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001009 uint8_t cr;
1010
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001011 *descrs = 0;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001012 *num_entries = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001013
Patrick Georgif3fa2992017-02-02 16:24:44 +01001014 switch (flash->chip->manufacture_id) {
David Hendricksd494b0a2010-08-16 16:28:50 -07001015 case WINBOND_NEX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001016 switch(flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -08001017 case WINBOND_NEX_W25X10:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001018 *descrs = w25x10_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001019 *num_entries = ARRAY_SIZE(w25x10_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001020 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001021 case WINBOND_NEX_W25X20:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001022 *descrs = w25x20_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001023 *num_entries = ARRAY_SIZE(w25x20_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001024 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001025 case WINBOND_NEX_W25X40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001026 *descrs = w25x40_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001027 *num_entries = ARRAY_SIZE(w25x40_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001028 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001029 case WINBOND_NEX_W25X80:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001030 *descrs = w25x80_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001031 *num_entries = ARRAY_SIZE(w25x80_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001032 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001033 case WINBOND_NEX_W25Q80_V:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001034 *descrs = w25q80_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001035 *num_entries = ARRAY_SIZE(w25q80_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001036 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001037 case WINBOND_NEX_W25Q16_V:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001038 *descrs = w25q16_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001039 *num_entries = ARRAY_SIZE(w25q16_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001040 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001041 case WINBOND_NEX_W25Q32_V:
1042 case WINBOND_NEX_W25Q32_W:
Edward O'Callaghand80cf712019-05-24 22:06:36 +10001043 case WINBOND_NEX_W25Q32JW:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001044 *descrs = w25q32_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001045 *num_entries = ARRAY_SIZE(w25q32_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001046 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001047 case WINBOND_NEX_W25Q64_V:
1048 case WINBOND_NEX_W25Q64_W:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001049 *descrs = w25q64_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001050 *num_entries = ARRAY_SIZE(w25q64_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001051 break;
Edward O'Callaghan517cb822019-11-21 14:08:32 +11001052 case WINBOND_NEX_W25Q128_DTR:
Alan Green77a95de2019-07-01 16:40:39 +10001053 case WINBOND_NEX_W25Q128_V_M:
Patrick Georgicc04a452017-02-06 12:14:43 +01001054 case WINBOND_NEX_W25Q128_V:
1055 case WINBOND_NEX_W25Q128_W:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001056 if (w25q_read_status_register_2(flash) & (1 << 6)) {
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001057 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001058 *descrs = w25rq128_cmp1_ranges;
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001059 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1060 } else {
1061 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001062 *descrs = w25rq128_cmp0_ranges;
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001063 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1064 }
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +05301065 break;
Justin TerAvest40083232020-08-17 16:34:46 -06001066 case WINBOND_NEX_W25Q256_V:
Alan Green77a95de2019-07-01 16:40:39 +10001067 case WINBOND_NEX_W25Q256JV_M:
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001068 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1069 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001070 *descrs = w25rq256_cmp1_ranges;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001071 *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges);
1072 } else {
1073 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001074 *descrs = w25rq256_cmp0_ranges;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001075 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
1076 }
1077 break;
David Hendricksd494b0a2010-08-16 16:28:50 -07001078 default:
1079 msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
1080 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001081 flash->chip->model_id);
David Hendricksd494b0a2010-08-16 16:28:50 -07001082 return -1;
1083 }
David Hendricks2c4a76c2010-06-28 14:00:43 -07001084 break;
David Hendricks57566ed2010-08-16 18:24:45 -07001085 case EON_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001086 switch (flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -08001087 case EON_EN25F40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001088 *descrs = en25f40_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001089 *num_entries = ARRAY_SIZE(en25f40_ranges);
David Hendricks57566ed2010-08-16 18:24:45 -07001090 break;
David Hendrickse185bf22011-05-24 15:34:18 -07001091 case EON_EN25Q40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001092 *descrs = en25q40_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001093 *num_entries = ARRAY_SIZE(en25q40_ranges);
1094 break;
1095 case EON_EN25Q80:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001096 *descrs = en25q80_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001097 *num_entries = ARRAY_SIZE(en25q80_ranges);
1098 break;
1099 case EON_EN25Q32:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001100 *descrs = en25q32_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001101 *num_entries = ARRAY_SIZE(en25q32_ranges);
1102 break;
1103 case EON_EN25Q64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001104 *descrs = en25q64_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001105 *num_entries = ARRAY_SIZE(en25q64_ranges);
1106 break;
1107 case EON_EN25Q128:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001108 *descrs = en25q128_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001109 *num_entries = ARRAY_SIZE(en25q128_ranges);
1110 break;
Tim Chen136fd0a2020-06-30 19:12:50 +08001111 case EON_EN25QH128:
1112 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1113 /* CMP == 1 */
1114 *descrs = w25rq128_cmp1_ranges;
1115 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1116 } else {
1117 /* CMP == 0 */
1118 *descrs = w25rq128_cmp0_ranges;
1119 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1120 }
1121 break;
Marc Jonesb2f90022014-04-29 17:37:23 -06001122 case EON_EN25S64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001123 *descrs = en25s64_ranges;
Marc Jonesb2f90022014-04-29 17:37:23 -06001124 *num_entries = ARRAY_SIZE(en25s64_ranges);
1125 break;
David Hendricks57566ed2010-08-16 18:24:45 -07001126 default:
1127 msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
1128 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001129 flash->chip->model_id);
David Hendricks57566ed2010-08-16 18:24:45 -07001130 return -1;
1131 }
1132 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001133 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001134 switch (flash->chip->model_id) {
David Hendricksf8f00c72011-02-01 12:39:46 -08001135 case MACRONIX_MX25L1005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001136 *descrs = mx25l1005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001137 *num_entries = ARRAY_SIZE(mx25l1005_ranges);
1138 break;
1139 case MACRONIX_MX25L2005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001140 *descrs = mx25l2005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001141 *num_entries = ARRAY_SIZE(mx25l2005_ranges);
1142 break;
1143 case MACRONIX_MX25L4005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001144 *descrs = mx25l4005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001145 *num_entries = ARRAY_SIZE(mx25l4005_ranges);
1146 break;
1147 case MACRONIX_MX25L8005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001148 *descrs = mx25l8005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001149 *num_entries = ARRAY_SIZE(mx25l8005_ranges);
1150 break;
1151 case MACRONIX_MX25L1605:
1152 /* FIXME: MX25L1605 and MX25L1605D have different write
1153 * protection capabilities, but share IDs */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001154 *descrs = mx25l1605d_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001155 *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
1156 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001157 case MACRONIX_MX25L3205:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001158 *descrs = mx25l3205d_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001159 *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
David Hendricksac72e362010-08-16 18:20:03 -07001160 break;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001161 case MACRONIX_MX25U3235E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001162 *descrs = mx25u3235e_ranges;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001163 *num_entries = ARRAY_SIZE(mx25u3235e_ranges);
1164 break;
Jongpil66a96492014-08-14 17:59:06 +09001165 case MACRONIX_MX25U6435E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001166 *descrs = mx25u6435e_ranges;
Jongpil66a96492014-08-14 17:59:06 +09001167 *num_entries = ARRAY_SIZE(mx25u6435e_ranges);
1168 break;
Alan Greendc0792e2019-07-01 15:01:34 +10001169 case MACRONIX_MX25U12835E:
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001170 cr = mx25l_read_config_register(flash);
1171 if (cr & MX25U12835E_TB) { /* T/B == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001172 *descrs = mx25u12835e_tb1_ranges;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001173 *num_entries = ARRAY_SIZE(mx25u12835e_tb1_ranges);
1174 } else { /* T/B == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001175 *descrs = mx25u12835e_tb0_ranges;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001176 *num_entries = ARRAY_SIZE(mx25u12835e_tb0_ranges);
1177 }
Alex Lu831c6092017-11-02 23:19:34 -07001178 break;
David Hendricksac72e362010-08-16 18:20:03 -07001179 default:
1180 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
1181 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001182 flash->chip->model_id);
David Hendricksac72e362010-08-16 18:20:03 -07001183 return -1;
1184 }
1185 break;
David Hendricksbfa624b2012-07-24 12:47:59 -07001186 case ST_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001187 switch(flash->chip->model_id) {
David Hendricksbfa624b2012-07-24 12:47:59 -07001188 case ST_N25Q064__1E:
1189 case ST_N25Q064__3E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001190 *descrs = n25q064_ranges;
David Hendricksbfa624b2012-07-24 12:47:59 -07001191 *num_entries = ARRAY_SIZE(n25q064_ranges);
1192 break;
1193 default:
1194 msg_cerr("%s() %d: Micron flash chip mismatch"
1195 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001196 flash->chip->model_id);
David Hendricksbfa624b2012-07-24 12:47:59 -07001197 return -1;
1198 }
1199 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001200 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001201 switch(flash->chip->model_id) {
Bryan Freed9a0051f2012-05-22 16:06:09 -07001202 case GIGADEVICE_GD25LQ32:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001203 *descrs = w25q32_ranges;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001204 *num_entries = ARRAY_SIZE(w25q32_ranges);
1205 break;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001206 case GIGADEVICE_GD25Q40:
1207 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1208 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001209 *descrs = gd25q40_cmp1_ranges;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001210 *num_entries = ARRAY_SIZE(gd25q40_cmp1_ranges);
1211 } else {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001212 *descrs = gd25q40_cmp0_ranges;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001213 *num_entries = ARRAY_SIZE(gd25q40_cmp0_ranges);
1214 }
1215 break;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001216 case GIGADEVICE_GD25Q64:
Marc Jonesb18734f2014-04-03 16:19:47 -06001217 case GIGADEVICE_GD25LQ64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001218 *descrs = gd25q64_ranges;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001219 *num_entries = ARRAY_SIZE(gd25q64_ranges);
1220 break;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001221 case GIGADEVICE_GD25Q128:
1222 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1223 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001224 *descrs = w25rq128_cmp1_ranges;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001225 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1226 } else {
1227 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001228 *descrs = w25rq128_cmp0_ranges;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001229 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1230 }
1231 break;
Duncan Laurie0c383552019-03-16 12:35:16 -07001232 case GIGADEVICE_GD25Q256D:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001233 *descrs = w25rq256_cmp0_ranges;
Duncan Laurie0c383552019-03-16 12:35:16 -07001234 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
1235 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001236 default:
1237 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
1238 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001239 flash->chip->model_id);
Bryan Freed9a0051f2012-05-22 16:06:09 -07001240 return -1;
1241 }
1242 break;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001243 case AMIC_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001244 switch(flash->chip->model_id) {
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001245 case AMIC_A25L040:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001246 *descrs = a25l040_ranges;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001247 *num_entries = ARRAY_SIZE(a25l040_ranges);
1248 break;
1249 default:
1250 msg_cerr("%s() %d: AMIC flash chip mismatch"
1251 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001252 flash->chip->model_id);
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001253 return -1;
1254 }
1255 break;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001256 case ATMEL_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001257 switch(flash->chip->model_id) {
Edward O'Callaghan1fa87e02019-05-03 02:27:24 -04001258 case ATMEL_AT25SF128A:
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001259 case ATMEL_AT25SL128A:
1260 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1261 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001262 *descrs = w25rq128_cmp1_ranges;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001263 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1264 } else {
1265 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001266 *descrs = w25rq128_cmp0_ranges;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001267 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1268 }
1269 break;
1270 default:
1271 msg_cerr("%s() %d: Atmel flash chip mismatch"
1272 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001273 flash->chip->model_id);
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001274 return -1;
1275 }
1276 break;
David Hendricksf7924d12010-06-10 21:26:44 -07001277 default:
David Hendricksd494b0a2010-08-16 16:28:50 -07001278 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01001279 __func__, flash->chip->manufacture_id);
David Hendricksf7924d12010-06-10 21:26:44 -07001280 return -1;
1281 }
1282
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001283 return 0;
1284}
1285
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001286int w25_range_to_status(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001287 unsigned int start, unsigned int len,
1288 struct w25q_status *status)
1289{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001290 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001291 int i, range_found = 0;
1292 int num_entries;
1293
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001294 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001295 return -1;
1296
David Hendricksf7924d12010-06-10 21:26:44 -07001297 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001298 struct wp_range *r = &descrs[i].range;
David Hendricksf7924d12010-06-10 21:26:44 -07001299
1300 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1301 start, len, r->start, r->len);
1302 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001303 status->bp0 = descrs[i].bp & 1;
1304 status->bp1 = descrs[i].bp >> 1;
1305 status->bp2 = descrs[i].bp >> 2;
1306 status->tb = descrs[i].m.tb;
1307 status->sec = descrs[i].m.sec;
David Hendricksf7924d12010-06-10 21:26:44 -07001308
1309 range_found = 1;
1310 break;
1311 }
1312 }
1313
1314 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001315 msg_cerr("%s: matching range not found\n", __func__);
David Hendricksf7924d12010-06-10 21:26:44 -07001316 return -1;
1317 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001318
David Hendricksd494b0a2010-08-16 16:28:50 -07001319 return 0;
1320}
1321
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001322int w25_status_to_range(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001323 const struct w25q_status *status,
1324 unsigned int *start, unsigned int *len)
1325{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001326 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001327 int i, status_found = 0;
1328 int num_entries;
1329
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001330 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001331 return -1;
1332
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001333 for (i = 0; i < num_entries; i++) {
1334 int bp;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001335 int table_bp, table_tb, table_sec;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001336
1337 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
1338 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001339 bp, descrs[i].bp,
1340 status->tb, descrs[i].m.tb,
1341 status->sec, descrs[i].m.sec);
1342 table_bp = descrs[i].bp;
1343 table_tb = descrs[i].m.tb;
1344 table_sec = descrs[i].m.sec;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001345 if ((bp == table_bp || table_bp == X) &&
1346 (status->tb == table_tb || table_tb == X) &&
1347 (status->sec == table_sec || table_sec == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001348 *start = descrs[i].range.start;
1349 *len = descrs[i].range.len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001350
1351 status_found = 1;
1352 break;
1353 }
1354 }
1355
1356 if (!status_found) {
1357 msg_cerr("matching status not found\n");
1358 return -1;
1359 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001360
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001361 return 0;
1362}
1363
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001364/* Given a [start, len], this function calls w25_range_to_status() to convert
1365 * it to flash-chip-specific range bits, then sets into status register.
1366 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001367static int w25_set_range(const struct flashctx *flash,
David Hendricksd494b0a2010-08-16 16:28:50 -07001368 unsigned int start, unsigned int len)
1369{
1370 struct w25q_status status;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001371 int tmp = 0;
1372 int expected = 0;
David Hendricksd494b0a2010-08-16 16:28:50 -07001373
1374 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001375 tmp = spi_read_status_register(flash);
David Hendricksd494b0a2010-08-16 16:28:50 -07001376 memcpy(&status, &tmp, 1);
1377 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1378
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001379 if (w25_range_to_status(flash, start, len, &status))
1380 return -1;
David Hendricksf7924d12010-06-10 21:26:44 -07001381
1382 msg_cdbg("status.busy: %x\n", status.busy);
1383 msg_cdbg("status.wel: %x\n", status.wel);
1384 msg_cdbg("status.bp0: %x\n", status.bp0);
1385 msg_cdbg("status.bp1: %x\n", status.bp1);
1386 msg_cdbg("status.bp2: %x\n", status.bp2);
1387 msg_cdbg("status.tb: %x\n", status.tb);
1388 msg_cdbg("status.sec: %x\n", status.sec);
1389 msg_cdbg("status.srp0: %x\n", status.srp0);
1390
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001391 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001392 spi_write_status_register(flash, expected);
David Hendricksf7924d12010-06-10 21:26:44 -07001393
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001394 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001395 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001396 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) {
David Hendricksc801adb2010-12-09 16:58:56 -08001397 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001398 expected, tmp);
1399 return 1;
1400 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001401
1402 return 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001403}
1404
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001405/* Print out the current status register value with human-readable text. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001406static int w25_wp_status(const struct flashctx *flash)
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001407{
1408 struct w25q_status status;
1409 int tmp;
David Hendricksce8ded32010-10-08 11:23:38 -07001410 unsigned int start, len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001411 int ret = 0;
1412
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001413 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001414 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001415 memcpy(&status, &tmp, 1);
1416 msg_cinfo("WP: status: 0x%02x\n", tmp);
1417 msg_cinfo("WP: status.srp0: %x\n", status.srp0);
1418 msg_cinfo("WP: write protect is %s.\n",
1419 status.srp0 ? "enabled" : "disabled");
1420
1421 msg_cinfo("WP: write protect range: ");
1422 if (w25_status_to_range(flash, &status, &start, &len)) {
1423 msg_cinfo("(cannot resolve the range)\n");
1424 ret = -1;
1425 } else {
1426 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1427 }
1428
1429 return ret;
1430}
1431
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001432static int w25q_large_range_to_status(const struct flashctx *flash,
1433 unsigned int start, unsigned int len,
1434 struct w25q_status_large *status)
1435{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001436 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001437 int i, range_found = 0;
1438 int num_entries;
1439
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001440 if (w25_range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001441 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001442
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001443 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001444 struct wp_range *r = &descrs[i].range;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001445
1446 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1447 start, len, r->start, r->len);
1448 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001449 status->bp0 = descrs[i].bp & 1;
1450 status->bp1 = descrs[i].bp >> 1;
1451 status->bp2 = descrs[i].bp >> 2;
1452 status->bp3 = descrs[i].bp >> 3;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001453 /*
1454 * For MX25U12835E chip, Top/Bottom (T/B) bit is not
1455 * part of status register and in that bit position is
1456 * Quad Enable (QE)
1457 */
1458 if (flash->chip->manufacture_id != MACRONIX_ID ||
1459 flash->chip->model_id != MACRONIX_MX25U12835E)
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001460 status->tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001461
1462 range_found = 1;
1463 break;
1464 }
1465 }
1466
1467 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001468 msg_cerr("%s: matching range not found\n", __func__);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001469 return -1;
1470 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001471
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001472 return 0;
1473}
1474
1475static int w25_large_status_to_range(const struct flashctx *flash,
1476 const struct w25q_status_large *status,
1477 unsigned int *start, unsigned int *len)
1478{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001479 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001480 int i, status_found = 0;
1481 int num_entries;
1482
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001483 if (w25_range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001484 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001485
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001486 for (i = 0; i < num_entries; i++) {
1487 int bp;
1488 int table_bp, table_tb;
1489
1490 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) |
1491 (status->bp3 << 3);
1492 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001493 bp, descrs[i].bp,
1494 status->tb, descrs[i].m.tb);
1495 table_bp = descrs[i].bp;
1496 table_tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001497 if ((bp == table_bp || table_bp == X) &&
1498 (status->tb == table_tb || table_tb == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001499 *start = descrs[i].range.start;
1500 *len = descrs[i].range.len;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001501
1502 status_found = 1;
1503 break;
1504 }
1505 }
1506
1507 if (!status_found) {
1508 msg_cerr("matching status not found\n");
1509 return -1;
1510 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001511
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001512 return 0;
1513}
1514
1515/* Given a [start, len], this function calls w25_range_to_status() to convert
1516 * it to flash-chip-specific range bits, then sets into status register.
1517 * Returns 0 if successful, -1 on error, and 1 if reading back was different.
1518 */
1519static int w25q_large_set_range(const struct flashctx *flash,
1520 unsigned int start, unsigned int len)
1521{
1522 struct w25q_status_large status;
1523 int tmp;
1524 int expected = 0;
1525
1526 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001527 tmp = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001528 memcpy(&status, &tmp, 1);
1529 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1530
1531 if (w25q_large_range_to_status(flash, start, len, &status))
1532 return -1;
1533
1534 msg_cdbg("status.busy: %x\n", status.busy);
1535 msg_cdbg("status.wel: %x\n", status.wel);
1536 msg_cdbg("status.bp0: %x\n", status.bp0);
1537 msg_cdbg("status.bp1: %x\n", status.bp1);
1538 msg_cdbg("status.bp2: %x\n", status.bp2);
1539 msg_cdbg("status.bp3: %x\n", status.bp3);
1540 msg_cdbg("status.tb: %x\n", status.tb);
1541 msg_cdbg("status.srp0: %x\n", status.srp0);
1542
1543 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001544 spi_write_status_register(flash, expected);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001545
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001546 tmp = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001547 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001548 if ((tmp & MASK_WP_AREA_LARGE) != (expected & MASK_WP_AREA_LARGE)) {
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001549 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
1550 expected, tmp);
1551 return 1;
1552 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001553
1554 return 0;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001555}
1556
1557static int w25q_large_wp_status(const struct flashctx *flash)
1558{
1559 struct w25q_status_large sr1;
1560 struct w25q_status_2 sr2;
1561 uint8_t tmp[2];
1562 unsigned int start, len;
1563 int ret = 0;
1564
1565 memset(&sr1, 0, sizeof(sr1));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001566 tmp[0] = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001567 memcpy(&sr1, &tmp[0], 1);
1568
1569 memset(&sr2, 0, sizeof(sr2));
1570 tmp[1] = w25q_read_status_register_2(flash);
1571 memcpy(&sr2, &tmp[1], 1);
1572
1573 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
1574 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1575 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1576 msg_cinfo("WP: write protect is %s.\n",
1577 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1578
1579 msg_cinfo("WP: write protect range: ");
1580 if (w25_large_status_to_range(flash, &sr1, &start, &len)) {
1581 msg_cinfo("(cannot resolve the range)\n");
1582 ret = -1;
1583 } else {
1584 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1585 }
1586
1587 return ret;
1588}
1589
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001590/* Set/clear the SRP0 bit in the status register. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001591static int w25_set_srp0(const struct flashctx *flash, int enable)
David Hendricksf7924d12010-06-10 21:26:44 -07001592{
1593 struct w25q_status status;
1594 int tmp = 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001595 int expected = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001596
1597 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001598 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001599 /* FIXME: this is NOT endian-free copy. */
David Hendricksf7924d12010-06-10 21:26:44 -07001600 memcpy(&status, &tmp, 1);
1601 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1602
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001603 status.srp0 = enable ? 1 : 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001604 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001605 spi_write_status_register(flash, expected);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001606
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001607 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001608 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
1609 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
1610 return 1;
David Hendricksf7924d12010-06-10 21:26:44 -07001611
1612 return 0;
1613}
1614
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001615static int w25_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001616 enum wp_mode wp_mode)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001617{
1618 int ret;
1619
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001620 if (wp_mode != WP_MODE_HARDWARE) {
David Hendricks1c09f802012-10-03 11:03:48 -07001621 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
1622 return 1;
1623 }
1624
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001625 ret = w25_set_srp0(flash, 1);
David Hendricksc801adb2010-12-09 16:58:56 -08001626 if (ret)
1627 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001628 return ret;
1629}
1630
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001631static int w25_disable_writeprotect(const struct flashctx *flash)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001632{
1633 int ret;
1634
1635 ret = w25_set_srp0(flash, 0);
David Hendricksc801adb2010-12-09 16:58:56 -08001636 if (ret)
1637 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001638
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001639 return ret;
1640}
1641
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001642static int w25_list_ranges(const struct flashctx *flash)
David Hendricks0f7f5382011-02-11 18:12:31 -08001643{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001644 struct wp_range_descriptor *descrs;
David Hendricks0f7f5382011-02-11 18:12:31 -08001645 int i, num_entries;
1646
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001647 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001648 return -1;
1649
David Hendricks0f7f5382011-02-11 18:12:31 -08001650 for (i = 0; i < num_entries; i++) {
1651 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001652 descrs[i].range.start,
1653 descrs[i].range.len);
David Hendricks0f7f5382011-02-11 18:12:31 -08001654 }
1655
1656 return 0;
1657}
1658
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001659static int w25q_wp_status(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001660{
1661 struct w25q_status sr1;
1662 struct w25q_status_2 sr2;
David Hendricksf1bd8802012-10-30 11:37:57 -07001663 uint8_t tmp[2];
David Hendricks1c09f802012-10-03 11:03:48 -07001664 unsigned int start, len;
1665 int ret = 0;
1666
1667 memset(&sr1, 0, sizeof(sr1));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001668 tmp[0] = spi_read_status_register(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001669 memcpy(&sr1, &tmp[0], 1);
David Hendricks1c09f802012-10-03 11:03:48 -07001670
David Hendricksf1bd8802012-10-30 11:37:57 -07001671 memset(&sr2, 0, sizeof(sr2));
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001672 tmp[1] = w25q_read_status_register_2(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001673 memcpy(&sr2, &tmp[1], 1);
1674
1675 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
David Hendricks1c09f802012-10-03 11:03:48 -07001676 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1677 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1678 msg_cinfo("WP: write protect is %s.\n",
1679 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1680
1681 msg_cinfo("WP: write protect range: ");
1682 if (w25_status_to_range(flash, &sr1, &start, &len)) {
1683 msg_cinfo("(cannot resolve the range)\n");
1684 ret = -1;
1685 } else {
1686 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1687 }
1688
1689 return ret;
1690}
1691
1692/*
1693 * W25Q adds an optional byte to the standard WRSR opcode. If /CS is
1694 * de-asserted after the first byte, then it acts like a JEDEC-standard
1695 * WRSR command. if /CS is asserted, then the next data byte is written
1696 * into status register 2.
1697 */
1698#define W25Q_WRSR_OUTSIZE 0x03
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001699static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2)
David Hendricks1c09f802012-10-03 11:03:48 -07001700{
1701 int result;
1702 struct spi_command cmds[] = {
1703 {
1704 /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
1705 .writecnt = JEDEC_WREN_OUTSIZE,
1706 .writearr = (const unsigned char[]){ JEDEC_WREN },
1707 .readcnt = 0,
1708 .readarr = NULL,
1709 }, {
1710 .writecnt = W25Q_WRSR_OUTSIZE,
1711 .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 },
1712 .readcnt = 0,
1713 .readarr = NULL,
1714 }, {
1715 .writecnt = 0,
1716 .writearr = NULL,
1717 .readcnt = 0,
1718 .readarr = NULL,
1719 }};
1720
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001721 result = spi_send_multicommand(flash, cmds);
David Hendricks1c09f802012-10-03 11:03:48 -07001722 if (result) {
1723 msg_cerr("%s failed during command execution\n",
1724 __func__);
1725 }
1726
1727 /* WRSR performs a self-timed erase before the changes take effect. */
David Hendricks60824042014-12-11 17:22:06 -08001728 programmer_delay(100 * 1000);
David Hendricks1c09f802012-10-03 11:03:48 -07001729
1730 return result;
1731}
1732
1733/*
1734 * Set/clear the SRP1 bit in status register 2.
1735 * FIXME: make this more generic if other chips use the same SR2 layout
1736 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001737static int w25q_set_srp1(const struct flashctx *flash, int enable)
David Hendricks1c09f802012-10-03 11:03:48 -07001738{
1739 struct w25q_status sr1;
1740 struct w25q_status_2 sr2;
1741 uint8_t tmp, expected;
1742
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001743 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001744 memcpy(&sr1, &tmp, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001745 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001746 memcpy(&sr2, &tmp, 1);
1747
1748 msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp);
1749
1750 sr2.srp1 = enable ? 1 : 0;
1751
1752 memcpy(&expected, &sr2, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001753 w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2));
David Hendricks1c09f802012-10-03 11:03:48 -07001754
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001755 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001756 msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp);
1757 if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA))
1758 return 1;
1759
1760 return 0;
1761}
1762
1763enum wp_mode get_wp_mode(const char *mode_str)
1764{
1765 enum wp_mode wp_mode = WP_MODE_UNKNOWN;
1766
1767 if (!strcasecmp(mode_str, "hardware"))
1768 wp_mode = WP_MODE_HARDWARE;
1769 else if (!strcasecmp(mode_str, "power_cycle"))
1770 wp_mode = WP_MODE_POWER_CYCLE;
1771 else if (!strcasecmp(mode_str, "permanent"))
1772 wp_mode = WP_MODE_PERMANENT;
1773
1774 return wp_mode;
1775}
1776
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001777static int w25q_disable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001778 enum wp_mode wp_mode)
1779{
1780 int ret = 1;
David Hendricks1c09f802012-10-03 11:03:48 -07001781 struct w25q_status_2 sr2;
1782 uint8_t tmp;
1783
1784 switch (wp_mode) {
1785 case WP_MODE_HARDWARE:
1786 ret = w25_set_srp0(flash, 0);
1787 break;
1788 case WP_MODE_POWER_CYCLE:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001789 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001790 memcpy(&sr2, &tmp, 1);
1791 if (sr2.srp1) {
1792 msg_cerr("%s(): must disconnect power to disable "
1793 "write-protection\n", __func__);
1794 } else {
1795 ret = 0;
1796 }
1797 break;
1798 case WP_MODE_PERMANENT:
1799 msg_cerr("%s(): cannot disable permanent write-protection\n",
1800 __func__);
1801 break;
1802 default:
1803 msg_cerr("%s(): invalid mode specified\n", __func__);
1804 break;
1805 }
1806
1807 if (ret)
1808 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001809
David Hendricks1c09f802012-10-03 11:03:48 -07001810 return ret;
1811}
1812
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001813static int w25q_disable_writeprotect_default(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001814{
1815 return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE);
1816}
1817
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001818static int w25q_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001819 enum wp_mode wp_mode)
1820{
1821 int ret = 1;
1822 struct w25q_status sr1;
1823 struct w25q_status_2 sr2;
1824 uint8_t tmp;
1825
1826 switch (wp_mode) {
1827 case WP_MODE_HARDWARE:
1828 if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) {
1829 msg_cerr("%s(): cannot disable power cycle WP mode\n",
1830 __func__);
1831 break;
1832 }
1833
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001834 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001835 memcpy(&sr1, &tmp, 1);
1836 if (sr1.srp0)
1837 ret = 0;
1838 else
1839 ret = w25_set_srp0(flash, 1);
1840
1841 break;
1842 case WP_MODE_POWER_CYCLE:
1843 if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) {
1844 msg_cerr("%s(): cannot disable hardware WP mode\n",
1845 __func__);
1846 break;
1847 }
1848
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001849 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001850 memcpy(&sr2, &tmp, 1);
1851 if (sr2.srp1)
1852 ret = 0;
1853 else
1854 ret = w25q_set_srp1(flash, 1);
1855
1856 break;
1857 case WP_MODE_PERMANENT:
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001858 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001859 memcpy(&sr1, &tmp, 1);
1860 if (sr1.srp0 == 0) {
1861 ret = w25_set_srp0(flash, 1);
1862 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001863 msg_perr("%s(): cannot enable SRP0 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001864 "permanent WP\n", __func__);
1865 break;
1866 }
1867 }
1868
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001869 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001870 memcpy(&sr2, &tmp, 1);
1871 if (sr2.srp1 == 0) {
1872 ret = w25q_set_srp1(flash, 1);
1873 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001874 msg_perr("%s(): cannot enable SRP1 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001875 "permanent WP\n", __func__);
1876 break;
1877 }
1878 }
1879
1880 break;
David Hendricksf1bd8802012-10-30 11:37:57 -07001881 default:
1882 msg_perr("%s(): invalid mode %d\n", __func__, wp_mode);
1883 break;
David Hendricks1c09f802012-10-03 11:03:48 -07001884 }
1885
1886 if (ret)
1887 msg_cerr("%s(): error=%d.\n", __func__, ret);
1888 return ret;
1889}
1890
1891/* W25P, W25X, and many flash chips from various vendors */
David Hendricksf7924d12010-06-10 21:26:44 -07001892struct wp wp_w25 = {
David Hendricks0f7f5382011-02-11 18:12:31 -08001893 .list_ranges = w25_list_ranges,
David Hendricksf7924d12010-06-10 21:26:44 -07001894 .set_range = w25_set_range,
1895 .enable = w25_enable_writeprotect,
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001896 .disable = w25_disable_writeprotect,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001897 .wp_status = w25_wp_status,
David Hendricks1c09f802012-10-03 11:03:48 -07001898
1899};
1900
1901/* W25Q series has features such as a second status register and SFDP */
1902struct wp wp_w25q = {
1903 .list_ranges = w25_list_ranges,
1904 .set_range = w25_set_range,
1905 .enable = w25q_enable_writeprotect,
1906 /*
1907 * By default, disable hardware write-protection. We may change
1908 * this later if we want to add fine-grained write-protect disable
1909 * as a command-line option.
1910 */
1911 .disable = w25q_disable_writeprotect_default,
1912 .wp_status = w25q_wp_status,
David Hendricksf7924d12010-06-10 21:26:44 -07001913};
David Hendrickse0512a72014-07-15 20:30:47 -07001914
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001915/* W25Q large series has 4 block-protect bits */
1916struct wp wp_w25q_large = {
1917 .list_ranges = w25_list_ranges,
1918 .set_range = w25q_large_set_range,
1919 .enable = w25q_enable_writeprotect,
1920 /*
1921 * By default, disable hardware write-protection. We may change
1922 * this later if we want to add fine-grained write-protect disable
1923 * as a command-line option.
1924 */
1925 .disable = w25q_disable_writeprotect_default,
1926 .wp_status = w25q_large_wp_status,
1927};
1928
Edward O'Callaghan3b996502020-04-12 20:46:51 +10001929static struct wp_range_descriptor gd25q32_cmp0_ranges[] = {
David Hendricksaf3944a2014-07-28 18:37:40 -07001930 /* none, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001931 { { }, 0x00, {0, 0} },
1932 { { }, 0x08, {0, 0} },
1933 { { }, 0x10, {0, 0} },
1934 { { }, 0x18, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001935
David Hendricks148a4bf2015-03-13 21:02:42 -07001936 { { }, 0x01, {0x3f0000, 64 * 1024} },
1937 { { }, 0x02, {0x3e0000, 128 * 1024} },
1938 { { }, 0x03, {0x3c0000, 256 * 1024} },
1939 { { }, 0x04, {0x380000, 512 * 1024} },
1940 { { }, 0x05, {0x300000, 1024 * 1024} },
1941 { { }, 0x06, {0x200000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001942
David Hendricks148a4bf2015-03-13 21:02:42 -07001943 { { }, 0x09, {0x000000, 64 * 1024} },
1944 { { }, 0x0a, {0x000000, 128 * 1024} },
1945 { { }, 0x0b, {0x000000, 256 * 1024} },
1946 { { }, 0x0c, {0x000000, 512 * 1024} },
1947 { { }, 0x0d, {0x000000, 1024 * 1024} },
1948 { { }, 0x0e, {0x000000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001949
1950 /* all, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001951 { { }, 0x07, {0x000000, 4096 * 1024} },
1952 { { }, 0x0f, {0x000000, 4096 * 1024} },
1953 { { }, 0x17, {0x000000, 4096 * 1024} },
1954 { { }, 0x1f, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001955
David Hendricks148a4bf2015-03-13 21:02:42 -07001956 { { }, 0x11, {0x3ff000, 4 * 1024} },
1957 { { }, 0x12, {0x3fe000, 8 * 1024} },
1958 { { }, 0x13, {0x3fc000, 16 * 1024} },
1959 { { }, 0x14, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1960 { { }, 0x15, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1961 { { }, 0x16, {0x3f8000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001962
David Hendricks148a4bf2015-03-13 21:02:42 -07001963 { { }, 0x19, {0x000000, 4 * 1024} },
1964 { { }, 0x1a, {0x000000, 8 * 1024} },
1965 { { }, 0x1b, {0x000000, 16 * 1024} },
1966 { { }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1967 { { }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1968 { { }, 0x1e, {0x000000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001969};
1970
Edward O'Callaghan3b996502020-04-12 20:46:51 +10001971static struct wp_range_descriptor gd25q32_cmp1_ranges[] = {
Martin Roth563a1fe2017-04-18 14:26:27 -06001972 /* All, bp4 and bp3 => don't care */
1973 { { }, 0x00, {0x000000, 4096 * 1024} }, /* All */
1974 { { }, 0x08, {0x000000, 4096 * 1024} },
1975 { { }, 0x10, {0x000000, 4096 * 1024} },
1976 { { }, 0x18, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001977
David Hendricks148a4bf2015-03-13 21:02:42 -07001978 { { }, 0x01, {0x000000, 4032 * 1024} },
1979 { { }, 0x02, {0x000000, 3968 * 1024} },
1980 { { }, 0x03, {0x000000, 3840 * 1024} },
1981 { { }, 0x04, {0x000000, 3584 * 1024} },
1982 { { }, 0x05, {0x000000, 3 * 1024 * 1024} },
1983 { { }, 0x06, {0x000000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001984
David Hendricks148a4bf2015-03-13 21:02:42 -07001985 { { }, 0x09, {0x010000, 4032 * 1024} },
1986 { { }, 0x0a, {0x020000, 3968 * 1024} },
1987 { { }, 0x0b, {0x040000, 3840 * 1024} },
1988 { { }, 0x0c, {0x080000, 3584 * 1024} },
1989 { { }, 0x0d, {0x100000, 3 * 1024 * 1024} },
1990 { { }, 0x0e, {0x200000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001991
Martin Roth563a1fe2017-04-18 14:26:27 -06001992 /* None, bp4 and bp3 => don't care */
1993 { { }, 0x07, {0, 0} }, /* None */
1994 { { }, 0x0f, {0, 0} },
1995 { { }, 0x17, {0, 0} },
1996 { { }, 0x1f, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001997
David Hendricks148a4bf2015-03-13 21:02:42 -07001998 { { }, 0x11, {0x000000, 4092 * 1024} },
1999 { { }, 0x12, {0x000000, 4088 * 1024} },
2000 { { }, 0x13, {0x000000, 4080 * 1024} },
2001 { { }, 0x14, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
2002 { { }, 0x15, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
2003 { { }, 0x16, {0x000000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002004
David Hendricks148a4bf2015-03-13 21:02:42 -07002005 { { }, 0x19, {0x001000, 4092 * 1024} },
2006 { { }, 0x1a, {0x002000, 4088 * 1024} },
2007 { { }, 0x1b, {0x040000, 4080 * 1024} },
2008 { { }, 0x1c, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
2009 { { }, 0x1d, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
2010 { { }, 0x1e, {0x080000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002011};
2012
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002013static struct wp_context gd25q32_wp = {
David Hendricksaf3944a2014-07-28 18:37:40 -07002014 /* TODO: map second status register */
2015 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
2016};
2017
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002018static struct wp_range_descriptor gd25q128_cmp0_ranges[] = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002019 /* none, bp4 and bp3 => don't care, others = 0 */
2020 { { .tb = 0 }, 0x00, {0, 0} },
2021 { { .tb = 0 }, 0x08, {0, 0} },
2022 { { .tb = 0 }, 0x10, {0, 0} },
2023 { { .tb = 0 }, 0x18, {0, 0} },
2024
2025 { { .tb = 0 }, 0x01, {0xfc0000, 256 * 1024} },
2026 { { .tb = 0 }, 0x02, {0xf80000, 512 * 1024} },
2027 { { .tb = 0 }, 0x03, {0xf00000, 1024 * 1024} },
2028 { { .tb = 0 }, 0x04, {0xe00000, 2048 * 1024} },
2029 { { .tb = 0 }, 0x05, {0xc00000, 4096 * 1024} },
2030 { { .tb = 0 }, 0x06, {0x800000, 8192 * 1024} },
2031
2032 { { .tb = 0 }, 0x09, {0x000000, 256 * 1024} },
2033 { { .tb = 0 }, 0x0a, {0x000000, 512 * 1024} },
2034 { { .tb = 0 }, 0x0b, {0x000000, 1024 * 1024} },
2035 { { .tb = 0 }, 0x0c, {0x000000, 2048 * 1024} },
2036 { { .tb = 0 }, 0x0d, {0x000000, 4096 * 1024} },
2037 { { .tb = 0 }, 0x0e, {0x000000, 8192 * 1024} },
2038
2039 /* all, bp4 and bp3 => don't care, others = 1 */
2040 { { .tb = 0 }, 0x07, {0x000000, 16384 * 1024} },
2041 { { .tb = 0 }, 0x0f, {0x000000, 16384 * 1024} },
2042 { { .tb = 0 }, 0x17, {0x000000, 16384 * 1024} },
2043 { { .tb = 0 }, 0x1f, {0x000000, 16384 * 1024} },
2044
2045 { { .tb = 0 }, 0x11, {0xfff000, 4 * 1024} },
2046 { { .tb = 0 }, 0x12, {0xffe000, 8 * 1024} },
2047 { { .tb = 0 }, 0x13, {0xffc000, 16 * 1024} },
2048 { { .tb = 0 }, 0x14, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
2049 { { .tb = 0 }, 0x15, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
2050
2051 { { .tb = 0 }, 0x19, {0x000000, 4 * 1024} },
2052 { { .tb = 0 }, 0x1a, {0x000000, 8 * 1024} },
2053 { { .tb = 0 }, 0x1b, {0x000000, 16 * 1024} },
2054 { { .tb = 0 }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2055 { { .tb = 0 }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2056 { { .tb = 0 }, 0x1e, {0x000000, 32 * 1024} },
2057};
2058
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002059static struct wp_range_descriptor gd25q128_cmp1_ranges[] = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002060 /* none, bp4 and bp3 => don't care, others = 0 */
2061 { { .tb = 1 }, 0x00, {0x000000, 16384 * 1024} },
2062 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
2063 { { .tb = 1 }, 0x10, {0x000000, 16384 * 1024} },
2064 { { .tb = 1 }, 0x18, {0x000000, 16384 * 1024} },
2065
2066 { { .tb = 1 }, 0x01, {0x000000, 16128 * 1024} },
2067 { { .tb = 1 }, 0x02, {0x000000, 15872 * 1024} },
2068 { { .tb = 1 }, 0x03, {0x000000, 15360 * 1024} },
2069 { { .tb = 1 }, 0x04, {0x000000, 14336 * 1024} },
2070 { { .tb = 1 }, 0x05, {0x000000, 12288 * 1024} },
2071 { { .tb = 1 }, 0x06, {0x000000, 8192 * 1024} },
2072
2073 { { .tb = 1 }, 0x09, {0x000000, 16128 * 1024} },
2074 { { .tb = 1 }, 0x0a, {0x000000, 15872 * 1024} },
2075 { { .tb = 1 }, 0x0b, {0x000000, 15360 * 1024} },
2076 { { .tb = 1 }, 0x0c, {0x000000, 14336 * 1024} },
2077 { { .tb = 1 }, 0x0d, {0x000000, 12288 * 1024} },
2078 { { .tb = 1 }, 0x0e, {0x000000, 8192 * 1024} },
2079
2080 /* none, bp4 and bp3 => don't care, others = 1 */
2081 { { .tb = 1 }, 0x07, {0x000000, 16384 * 1024} },
2082 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
2083 { { .tb = 1 }, 0x0f, {0x000000, 16384 * 1024} },
2084 { { .tb = 1 }, 0x17, {0x000000, 16384 * 1024} },
2085 { { .tb = 1 }, 0x1f, {0x000000, 16384 * 1024} },
2086
2087 { { .tb = 1 }, 0x11, {0x000000, 16380 * 1024} },
2088 { { .tb = 1 }, 0x12, {0x000000, 16376 * 1024} },
2089 { { .tb = 1 }, 0x13, {0x000000, 16368 * 1024} },
2090 { { .tb = 1 }, 0x14, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
2091 { { .tb = 1 }, 0x15, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
2092
2093 { { .tb = 1 }, 0x19, {0x001000, 16380 * 1024} },
2094 { { .tb = 1 }, 0x1a, {0x002000, 16376 * 1024} },
2095 { { .tb = 1 }, 0x1b, {0x004000, 16368 * 1024} },
2096 { { .tb = 1 }, 0x1c, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
2097 { { .tb = 1 }, 0x1d, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
2098 { { .tb = 1 }, 0x1e, {0x008000, 16352 * 1024} },
2099};
2100
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002101static struct wp_context gd25q128_wp = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002102 /* TODO: map second and third status registers */
2103 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
2104};
2105
David Hendricks83541d32014-07-15 20:58:21 -07002106/* FIXME: MX25L6406 has same ID as MX25L6405D */
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002107static struct wp_range_descriptor mx25l6406e_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002108 { { }, 0, {0, 0} }, /* none */
2109 { { }, 0x1, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
2110 { { }, 0x2, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
2111 { { }, 0x3, {0x7a0000, 64 * 8 * 1024} }, /* blocks 120-127 */
2112 { { }, 0x4, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
2113 { { }, 0x5, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
2114 { { }, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricks83541d32014-07-15 20:58:21 -07002115
David Hendricks148a4bf2015-03-13 21:02:42 -07002116 { { }, 0x7, {0x000000, 64 * 128 * 1024} }, /* all */
2117 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2118 { { }, 0x9, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2119 { { }, 0xa, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
2120 { { }, 0xb, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
2121 { { }, 0xc, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
2122 { { }, 0xd, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
2123 { { }, 0xe, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
2124 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricks83541d32014-07-15 20:58:21 -07002125};
2126
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002127static struct wp_context mx25l6406e_wp = {
David Hendricks83541d32014-07-15 20:58:21 -07002128 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002129 .descrs = &mx25l6406e_ranges[0],
David Hendricks83541d32014-07-15 20:58:21 -07002130};
David Hendrickse0512a72014-07-15 20:30:47 -07002131
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002132static struct wp_range_descriptor mx25l6495f_tb0_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002133 { { }, 0, {0, 0} }, /* none */
2134 { { }, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
2135 { { }, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
2136 { { }, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
David Hendricksc3496092014-11-13 17:20:55 -08002137
David Hendricks148a4bf2015-03-13 21:02:42 -07002138 { { }, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
2139 { { }, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
2140 { { }, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
2141 { { }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
2142 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2143 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2144 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2145 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2146 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2147 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2148 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2149 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002150};
2151
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002152static struct wp_range_descriptor mx25l6495f_tb1_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002153 { { }, 0, {0, 0} }, /* none */
2154 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2155 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2156 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2157 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2158 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2159 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2160 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2161 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2162 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2163 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2164 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2165 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2166 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2167 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2168 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002169};
2170
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002171static struct wp_context mx25l6495f_wp = {
David Hendricksc3496092014-11-13 17:20:55 -08002172 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2173};
2174
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002175static struct wp_range_descriptor mx25l25635f_tb0_ranges[] = {
Vic Yang848bfd12018-03-23 10:24:07 -07002176 { { }, 0, {0, 0} }, /* none */
2177 { { }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* block 511 */
2178 { { }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* blocks 510-511 */
2179 { { }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* blocks 508-511 */
2180 { { }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* blocks 504-511 */
2181 { { }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* blocks 496-511 */
2182 { { }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* blocks 480-511 */
2183 { { }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* blocks 448-511 */
2184 { { }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* blocks 384-511 */
2185 { { }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* blocks 256-511 */
2186 { { }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* all */
2187 { { }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* all */
2188 { { }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* all */
2189 { { }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* all */
2190 { { }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* all */
2191 { { }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* all */
2192};
2193
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002194static struct wp_range_descriptor mx25l25635f_tb1_ranges[] = {
Vic Yang848bfd12018-03-23 10:24:07 -07002195 { { }, 0, {0, 0} }, /* none */
2196 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2197 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2198 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2199 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2200 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2201 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2202 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2203 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
2204 { { }, 0x9, {0x000000, 64 * 256 * 1024} }, /* blocks 0-255 */
2205 { { }, 0xa, {0x000000, 64 * 512 * 1024} }, /* all */
2206 { { }, 0xb, {0x000000, 64 * 512 * 1024} }, /* all */
2207 { { }, 0xc, {0x000000, 64 * 512 * 1024} }, /* all */
2208 { { }, 0xd, {0x000000, 64 * 512 * 1024} }, /* all */
2209 { { }, 0xe, {0x000000, 64 * 512 * 1024} }, /* all */
2210 { { }, 0xf, {0x000000, 64 * 512 * 1024} }, /* all */
2211};
2212
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002213static struct wp_context mx25l25635f_wp = {
Vic Yang848bfd12018-03-23 10:24:07 -07002214 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2215};
2216
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002217static struct wp_range_descriptor s25fs128s_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002218 { { .tb = 1 }, 0, {0, 0} }, /* none */
2219 { { .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* lower 64th */
2220 { { .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* lower 32nd */
2221 { { .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* lower 16th */
2222 { { .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* lower 8th */
2223 { { .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* lower 4th */
2224 { { .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* lower half */
2225 { { .tb = 1 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002226
David Hendricks148a4bf2015-03-13 21:02:42 -07002227 { { .tb = 0 }, 0, {0, 0} }, /* none */
2228 { { .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* upper 64th */
2229 { { .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* upper 32nd */
2230 { { .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* upper 16th */
2231 { { .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* upper 8th */
2232 { { .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* upper 4th */
2233 { { .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* upper half */
2234 { { .tb = 0 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002235};
2236
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002237static struct wp_context s25fs128s_wp = {
David Hendricksa9884852014-12-11 15:31:12 -08002238 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
2239};
2240
David Hendricksc694bb82015-02-25 14:52:17 -08002241
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002242static struct wp_range_descriptor s25fl256s_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002243 { { .tb = 1 }, 0, {0, 0} }, /* none */
2244 { { .tb = 1 }, 0x1, {0x000000, 512 * 1024} }, /* lower 64th */
2245 { { .tb = 1 }, 0x2, {0x000000, 1024 * 1024} }, /* lower 32nd */
2246 { { .tb = 1 }, 0x3, {0x000000, 2048 * 1024} }, /* lower 16th */
2247 { { .tb = 1 }, 0x4, {0x000000, 4096 * 1024} }, /* lower 8th */
2248 { { .tb = 1 }, 0x5, {0x000000, 8192 * 1024} }, /* lower 4th */
2249 { { .tb = 1 }, 0x6, {0x000000, 16384 * 1024} }, /* lower half */
2250 { { .tb = 1 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
2251
2252 { { .tb = 0 }, 0, {0, 0} }, /* none */
2253 { { .tb = 0 }, 0x1, {0x1f80000, 512 * 1024} }, /* upper 64th */
2254 { { .tb = 0 }, 0x2, {0x1f00000, 1024 * 1024} }, /* upper 32nd */
2255 { { .tb = 0 }, 0x3, {0x1e00000, 2048 * 1024} }, /* upper 16th */
2256 { { .tb = 0 }, 0x4, {0x1c00000, 4096 * 1024} }, /* upper 8th */
2257 { { .tb = 0 }, 0x5, {0x1800000, 8192 * 1024} }, /* upper 4th */
2258 { { .tb = 0 }, 0x6, {0x1000000, 16384 * 1024} }, /* upper half */
2259 { { .tb = 0 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
David Hendricksc694bb82015-02-25 14:52:17 -08002260};
2261
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002262static struct wp_context s25fl256s_wp = {
David Hendricksc694bb82015-02-25 14:52:17 -08002263 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
2264};
2265
David Hendrickse0512a72014-07-15 20:30:47 -07002266/* Given a flash chip, this function returns its writeprotect info. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002267static int generic_range_table(const struct flashctx *flash,
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002268 struct wp_context **wp,
David Hendrickse0512a72014-07-15 20:30:47 -07002269 int *num_entries)
2270{
2271 *wp = NULL;
2272 *num_entries = 0;
2273
Patrick Georgif3fa2992017-02-02 16:24:44 +01002274 switch (flash->chip->manufacture_id) {
David Hendricksaf3944a2014-07-28 18:37:40 -07002275 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002276 switch(flash->chip->model_id) {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002277
David Hendricksaf3944a2014-07-28 18:37:40 -07002278 case GIGADEVICE_GD25Q32: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002279 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricksaf3944a2014-07-28 18:37:40 -07002280 *wp = &gd25q32_wp;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002281
David Hendricksaf3944a2014-07-28 18:37:40 -07002282 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002283 (*wp)->descrs = &gd25q32_cmp0_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002284 *num_entries = ARRAY_SIZE(gd25q32_cmp0_ranges);
2285 } else { /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002286 (*wp)->descrs = &gd25q32_cmp1_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002287 *num_entries = ARRAY_SIZE(gd25q32_cmp1_ranges);
2288 }
2289
2290 break;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002291 }
Aaron Durbin6c957d72018-08-20 09:31:01 -06002292 case GIGADEVICE_GD25LQ128CD: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002293 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002294 *wp = &gd25q128_wp;
2295
2296 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002297 (*wp)->descrs = &gd25q128_cmp0_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002298 *num_entries = ARRAY_SIZE(gd25q128_cmp0_ranges);
2299 } else { /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002300 (*wp)->descrs = &gd25q128_cmp1_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002301 *num_entries = ARRAY_SIZE(gd25q128_cmp1_ranges);
2302 }
2303
2304 break;
David Hendricksaf3944a2014-07-28 18:37:40 -07002305 }
2306 default:
2307 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
2308 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002309 flash->chip->model_id);
David Hendricksaf3944a2014-07-28 18:37:40 -07002310 return -1;
2311 }
2312 break;
David Hendricks83541d32014-07-15 20:58:21 -07002313 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002314 switch (flash->chip->model_id) {
David Hendricks83541d32014-07-15 20:58:21 -07002315 case MACRONIX_MX25L6405:
2316 /* FIXME: MX25L64* chips have mixed capabilities and
2317 share IDs */
2318 *wp = &mx25l6406e_wp;
2319 *num_entries = ARRAY_SIZE(mx25l6406e_ranges);
2320 break;
David Hendricksc3496092014-11-13 17:20:55 -08002321 case MACRONIX_MX25L6495F: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002322 uint8_t cr = mx25l_read_config_register(flash);
David Hendricksc3496092014-11-13 17:20:55 -08002323
2324 *wp = &mx25l6495f_wp;
2325 if (!(cr & (1 << 3))) { /* T/B == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002326 (*wp)->descrs = &mx25l6495f_tb0_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002327 *num_entries = ARRAY_SIZE(mx25l6495f_tb0_ranges);
2328 } else { /* T/B == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002329 (*wp)->descrs = &mx25l6495f_tb1_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002330 *num_entries = ARRAY_SIZE(mx25l6495f_tb1_ranges);
2331 }
2332 break;
2333 }
Vic Yang848bfd12018-03-23 10:24:07 -07002334 case MACRONIX_MX25L25635F: {
2335 uint8_t cr = mx25l_read_config_register(flash);
2336
2337 *wp = &mx25l25635f_wp;
2338 if (!(cr & (1 << 3))) { /* T/B == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002339 (*wp)->descrs = &mx25l25635f_tb0_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002340 *num_entries = ARRAY_SIZE(mx25l25635f_tb0_ranges);
2341 } else { /* T/B == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002342 (*wp)->descrs = &mx25l25635f_tb1_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002343 *num_entries = ARRAY_SIZE(mx25l25635f_tb1_ranges);
2344 }
2345 break;
2346 }
David Hendricks83541d32014-07-15 20:58:21 -07002347 default:
2348 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
2349 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002350 flash->chip->model_id);
David Hendricks83541d32014-07-15 20:58:21 -07002351 return -1;
2352 }
2353 break;
David Hendricksa9884852014-12-11 15:31:12 -08002354 case SPANSION_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002355 switch (flash->chip->model_id) {
David Hendricksa9884852014-12-11 15:31:12 -08002356 case SPANSION_S25FS128S_L:
2357 case SPANSION_S25FS128S_S: {
David Hendricksa9884852014-12-11 15:31:12 -08002358 *wp = &s25fs128s_wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002359 (*wp)->descrs = s25fs128s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002360 *num_entries = ARRAY_SIZE(s25fs128s_ranges);
David Hendricksa9884852014-12-11 15:31:12 -08002361 break;
2362 }
David Hendricksc694bb82015-02-25 14:52:17 -08002363 case SPANSION_S25FL256S_UL:
2364 case SPANSION_S25FL256S_US: {
David Hendricksc694bb82015-02-25 14:52:17 -08002365 *wp = &s25fl256s_wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002366 (*wp)->descrs = s25fl256s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002367 *num_entries = ARRAY_SIZE(s25fl256s_ranges);
David Hendricksc694bb82015-02-25 14:52:17 -08002368 break;
2369 }
David Hendricksa9884852014-12-11 15:31:12 -08002370 default:
2371 msg_cerr("%s():%d Spansion flash chip mismatch (0x%04x)"
Patrick Georgif3fa2992017-02-02 16:24:44 +01002372 ", aborting\n", __func__, __LINE__,
2373 flash->chip->model_id);
David Hendricksa9884852014-12-11 15:31:12 -08002374 return -1;
2375 }
2376 break;
David Hendrickse0512a72014-07-15 20:30:47 -07002377 default:
2378 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01002379 __func__, flash->chip->manufacture_id);
David Hendrickse0512a72014-07-15 20:30:47 -07002380 return -1;
2381 }
2382
2383 return 0;
2384}
2385
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002386/* Determines if special s25f-specific functions need to be used to access a
2387 * given chip's modifier bits. Very much a hard-coded special case hack, but it
2388 * is also very easy to replace once a proper abstraction for accessing
2389 * specific modifier bits is added. */
2390static int use_s25f_modifier_bits(const struct flashctx *flash)
2391{
2392 bool model_match =
2393 flash->chip->model_id == SPANSION_S25FS128S_L ||
2394 flash->chip->model_id == SPANSION_S25FS128S_S ||
2395 flash->chip->model_id == SPANSION_S25FL256S_UL ||
2396 flash->chip->model_id == SPANSION_S25FL256S_US;
2397 return (flash->chip->manufacture_id == SPANSION_ID) && model_match;
2398}
2399
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002400static uint8_t generic_get_bp_mask(struct wp_context *wp)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002401{
2402 return ((1 << (wp->sr1.bp0_pos + wp->sr1.bp_bits)) - 1) ^ \
2403 ((1 << wp->sr1.bp0_pos) - 1);
2404}
2405
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002406static uint8_t generic_get_status_check_mask(struct wp_context *wp)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002407{
2408 return generic_get_bp_mask(wp) | 1 << wp->sr1.srp_pos;
2409}
2410
David Hendrickse0512a72014-07-15 20:30:47 -07002411/* Given a [start, len], this function finds a block protect bit combination
2412 * (if possible) and sets the corresponding bits in "status". Remaining bits
2413 * are preserved. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002414static int generic_range_to_status(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002415 unsigned int start, unsigned int len,
Marco Chen9d5bddb2020-02-11 17:12:56 +08002416 uint8_t *status, uint8_t *check_mask)
David Hendrickse0512a72014-07-15 20:30:47 -07002417{
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002418 struct wp_context *wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002419 struct wp_range_descriptor *r;
David Hendrickse0512a72014-07-15 20:30:47 -07002420 int i, range_found = 0, num_entries;
2421 uint8_t bp_mask;
2422
2423 if (generic_range_table(flash, &wp, &num_entries))
2424 return -1;
2425
Marco Chen9d5bddb2020-02-11 17:12:56 +08002426 bp_mask = generic_get_bp_mask(wp);
David Hendrickse0512a72014-07-15 20:30:47 -07002427
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002428 for (i = 0, r = &wp->descrs[0]; i < num_entries; i++, r++) {
David Hendrickse0512a72014-07-15 20:30:47 -07002429 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
2430 start, len, r->range.start, r->range.len);
2431 if ((start == r->range.start) && (len == r->range.len)) {
2432 *status &= ~(bp_mask);
2433 *status |= r->bp << (wp->sr1.bp0_pos);
David Hendricks148a4bf2015-03-13 21:02:42 -07002434
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002435 if (use_s25f_modifier_bits(flash)) {
2436 if (s25f_set_modifier_bits(flash, &r->m) < 0) {
Edward O'Callaghan0b662c12021-01-22 00:30:24 +11002437 msg_cerr("error setting modifier bits for range.\n");
David Hendricks148a4bf2015-03-13 21:02:42 -07002438 return -1;
2439 }
2440 }
2441
David Hendrickse0512a72014-07-15 20:30:47 -07002442 range_found = 1;
2443 break;
2444 }
2445 }
2446
2447 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11002448 msg_cerr("%s: matching range not found\n", __func__);
David Hendrickse0512a72014-07-15 20:30:47 -07002449 return -1;
2450 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002451
Marco Chen9d5bddb2020-02-11 17:12:56 +08002452 *check_mask = generic_get_status_check_mask(wp);
David Hendrickse0512a72014-07-15 20:30:47 -07002453 return 0;
2454}
2455
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002456static int generic_status_to_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002457 const uint8_t sr1, unsigned int *start, unsigned int *len)
2458{
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002459 struct wp_context *wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002460 struct wp_range_descriptor *r;
Duncan Laurie04ca1172015-03-12 09:25:34 -07002461 int num_entries, i, status_found = 0;
David Hendrickse0512a72014-07-15 20:30:47 -07002462 uint8_t sr1_bp;
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +11002463 struct modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -07002464
2465 if (generic_range_table(flash, &wp, &num_entries))
2466 return -1;
2467
David Hendricks148a4bf2015-03-13 21:02:42 -07002468 /* modifier bits may be compared more than once, so get them here */
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002469 if (use_s25f_modifier_bits(flash) && s25f_get_modifier_bits(flash, &m) < 0)
2470 return -1;
David Hendricks148a4bf2015-03-13 21:02:42 -07002471
David Hendrickse0512a72014-07-15 20:30:47 -07002472 sr1_bp = (sr1 >> wp->sr1.bp0_pos) & ((1 << wp->sr1.bp_bits) - 1);
2473
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002474 for (i = 0, r = &wp->descrs[0]; i < num_entries; i++, r++) {
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002475 if (use_s25f_modifier_bits(flash)) {
David Hendricks148a4bf2015-03-13 21:02:42 -07002476 if (memcmp(&m, &r->m, sizeof(m)))
2477 continue;
2478 }
David Hendrickse0512a72014-07-15 20:30:47 -07002479 msg_cspew("comparing 0x%02x 0x%02x\n", sr1_bp, r->bp);
2480 if (sr1_bp == r->bp) {
2481 *start = r->range.start;
2482 *len = r->range.len;
2483 status_found = 1;
2484 break;
2485 }
2486 }
2487
2488 if (!status_found) {
2489 msg_cerr("matching status not found\n");
2490 return -1;
2491 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002492
David Hendrickse0512a72014-07-15 20:30:47 -07002493 return 0;
2494}
2495
2496/* Given a [start, len], this function calls generic_range_to_status() to
2497 * convert it to flash-chip-specific range bits, then sets into status register.
2498 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002499static int generic_set_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002500 unsigned int start, unsigned int len)
2501{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002502 uint8_t status, expected, check_mask;
David Hendrickse0512a72014-07-15 20:30:47 -07002503
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002504 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002505 msg_cdbg("%s: old status: 0x%02x\n", __func__, status);
2506
2507 expected = status; /* preserve non-bp bits */
Marco Chen9d5bddb2020-02-11 17:12:56 +08002508 if (generic_range_to_status(flash, start, len, &expected, &check_mask))
David Hendrickse0512a72014-07-15 20:30:47 -07002509 return -1;
2510
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002511 spi_write_status_register(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002512
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002513 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002514 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002515 if ((status & check_mask) != (expected & check_mask)) {
2516 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2517 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002518 return 1;
2519 }
David Hendrickse0512a72014-07-15 20:30:47 -07002520 return 0;
2521}
2522
2523/* Set/clear the status regsiter write protect bit in SR1. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002524static int generic_set_srp0(const struct flashctx *flash, int enable)
David Hendrickse0512a72014-07-15 20:30:47 -07002525{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002526 uint8_t status, expected, check_mask;
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002527 struct wp_context *wp;
David Hendrickse0512a72014-07-15 20:30:47 -07002528 int num_entries;
2529
2530 if (generic_range_table(flash, &wp, &num_entries))
2531 return -1;
2532
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002533 expected = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002534 msg_cdbg("%s: old status: 0x%02x\n", __func__, expected);
2535
2536 if (enable)
2537 expected |= 1 << wp->sr1.srp_pos;
2538 else
2539 expected &= ~(1 << wp->sr1.srp_pos);
2540
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002541 spi_write_status_register(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002542
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002543 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002544 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002545
2546 check_mask = generic_get_status_check_mask(wp);
2547 msg_cdbg("%s: check mask: 0x%02x\n", __func__, check_mask);
2548 if ((status & check_mask) != (expected & check_mask)) {
2549 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2550 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002551 return -1;
Marco Chen9d5bddb2020-02-11 17:12:56 +08002552 }
David Hendrickse0512a72014-07-15 20:30:47 -07002553
2554 return 0;
2555}
2556
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002557static int generic_enable_writeprotect(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002558 enum wp_mode wp_mode)
2559{
2560 int ret;
2561
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002562 if (wp_mode != WP_MODE_HARDWARE) {
David Hendrickse0512a72014-07-15 20:30:47 -07002563 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
2564 return 1;
2565 }
2566
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002567 ret = generic_set_srp0(flash, 1);
David Hendrickse0512a72014-07-15 20:30:47 -07002568 if (ret)
2569 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002570
David Hendrickse0512a72014-07-15 20:30:47 -07002571 return ret;
2572}
2573
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002574static int generic_disable_writeprotect(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002575{
2576 int ret;
2577
2578 ret = generic_set_srp0(flash, 0);
2579 if (ret)
2580 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002581
David Hendrickse0512a72014-07-15 20:30:47 -07002582 return ret;
2583}
2584
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002585static int generic_list_ranges(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002586{
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002587 struct wp_context *wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002588 struct wp_range_descriptor *r;
David Hendrickse0512a72014-07-15 20:30:47 -07002589 int i, num_entries;
2590
2591 if (generic_range_table(flash, &wp, &num_entries))
2592 return -1;
2593
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002594 r = &wp->descrs[0];
David Hendrickse0512a72014-07-15 20:30:47 -07002595 for (i = 0; i < num_entries; i++) {
2596 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
2597 r->range.start, r->range.len);
2598 r++;
2599 }
2600
2601 return 0;
2602}
2603
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002604static int wp_context_status(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002605{
2606 uint8_t sr1;
2607 unsigned int start, len;
2608 int ret = 0;
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002609 struct wp_context *wp;
David Hendrickse0512a72014-07-15 20:30:47 -07002610 int num_entries, wp_en;
2611
2612 if (generic_range_table(flash, &wp, &num_entries))
2613 return -1;
2614
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002615 sr1 = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002616 wp_en = (sr1 >> wp->sr1.srp_pos) & 1;
2617
2618 msg_cinfo("WP: status: 0x%04x\n", sr1);
2619 msg_cinfo("WP: status.srp0: %x\n", wp_en);
2620 /* FIXME: SRP1 is not really generic, but we probably should print
2621 * it anyway to have consistent output. #legacycruft */
2622 msg_cinfo("WP: status.srp1: %x\n", 0);
2623 msg_cinfo("WP: write protect is %s.\n",
2624 wp_en ? "enabled" : "disabled");
2625
2626 msg_cinfo("WP: write protect range: ");
2627 if (generic_status_to_range(flash, sr1, &start, &len)) {
2628 msg_cinfo("(cannot resolve the range)\n");
2629 ret = -1;
2630 } else {
2631 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
2632 }
2633
2634 return ret;
2635}
2636
2637struct wp wp_generic = {
2638 .list_ranges = generic_list_ranges,
2639 .set_range = generic_set_range,
2640 .enable = generic_enable_writeprotect,
2641 .disable = generic_disable_writeprotect,
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002642 .wp_status = wp_context_status,
David Hendrickse0512a72014-07-15 20:30:47 -07002643};