blob: 5ec4ad3d3e0a5f676b35d83f575cc18c100d22b2 [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 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
David Hendricksf7924d12010-06-10 21:26:44 -070021#include <stdlib.h>
22#include <string.h>
23
24#include "flash.h"
25#include "flashchips.h"
26#include "chipdrivers.h"
Louis Yung-Chieh Lo52aa9302010-09-06 10:45:02 +080027#include "spi.h"
David Hendricks23cd7782010-08-25 12:42:38 -070028#include "writeprotect.h"
David Hendricksf7924d12010-06-10 21:26:44 -070029
David Hendricks1c09f802012-10-03 11:03:48 -070030/*
David Hendricksf7924d12010-06-10 21:26:44 -070031 * The following procedures rely on look-up tables to match the user-specified
32 * range with the chip's supported ranges. This turned out to be the most
33 * elegant approach since diferent flash chips use different levels of
34 * granularity and methods to determine protected ranges. In other words,
David Hendrickse0512a72014-07-15 20:30:47 -070035 * be stupid and simple since clever arithmetic will not work for many chips.
David Hendricksf7924d12010-06-10 21:26:44 -070036 */
37
38struct wp_range {
39 unsigned int start; /* starting address */
40 unsigned int len; /* len */
41};
42
43enum bit_state {
44 OFF = 0,
45 ON = 1,
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +080046 X = -1 /* don't care. Must be bigger than max # of bp. */
David Hendricksf7924d12010-06-10 21:26:44 -070047};
48
David Hendrickse0512a72014-07-15 20:30:47 -070049/*
50 * Generic write-protection schema for 25-series SPI flash chips. This assumes
51 * there is a status register that contains one or more consecutive bits which
52 * determine which address range is protected.
53 */
54
55struct status_register_layout {
56 int bp0_pos; /* position of BP0 */
57 int bp_bits; /* number of block protect bits */
58 int srp_pos; /* position of status register protect enable bit */
59};
60
61struct generic_range {
David Hendricks148a4bf2015-03-13 21:02:42 -070062 struct generic_modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -070063 unsigned int bp; /* block protect bitfield */
64 struct wp_range range;
65};
66
67struct generic_wp {
68 struct status_register_layout sr1; /* status register 1 */
69 struct generic_range *ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -070070
71 /*
72 * Some chips store modifier bits in one or more special control
73 * registers instead of the status register like many older SPI NOR
74 * flash chips did. get_modifier_bits() and set_modifier_bits() will do
75 * any chip-specific operations necessary to get/set these bit values.
76 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -070077 int (*get_modifier_bits)(const struct flashctx *flash,
David Hendricks148a4bf2015-03-13 21:02:42 -070078 struct generic_modifier_bits *m);
Souvik Ghoshd75cd672016-06-17 14:21:39 -070079 int (*set_modifier_bits)(const struct flashctx *flash,
David Hendricks148a4bf2015-03-13 21:02:42 -070080 struct generic_modifier_bits *m);
David Hendrickse0512a72014-07-15 20:30:47 -070081};
82
83/*
84 * The following ranges and functions are useful for representing Winbond-
85 * style writeprotect schema in which there are typically 5 bits of
86 * relevant information stored in status register 1:
87 * sec: This bit indicates the units (sectors vs. blocks)
88 * tb: The top-bottom bit indicates if the affected range is at the top of
89 * the flash memory's address space or at the bottom.
Duncan Laurie1801f7c2019-01-09 18:02:51 -080090 * bp: Bitmask representing the number of affected sectors/blocks.
David Hendrickse0512a72014-07-15 20:30:47 -070091 */
David Hendricksf7924d12010-06-10 21:26:44 -070092struct w25q_range {
Duncan Laurie1801f7c2019-01-09 18:02:51 -080093 enum bit_state sec; /* if 1, bp bits describe sectors */
David Hendricksf7924d12010-06-10 21:26:44 -070094 enum bit_state tb; /* top/bottom select */
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +080095 int bp; /* block protect bitfield */
David Hendricksf7924d12010-06-10 21:26:44 -070096 struct wp_range range;
97};
98
David Hendrickse0512a72014-07-15 20:30:47 -070099/*
100 * Mask to extract write-protect enable and range bits
101 * Status register 1:
102 * SRP0: bit 7
103 * range(BP2-BP0): bit 4-2
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800104 * range(BP3-BP0): bit 5-2 (large chips)
David Hendrickse0512a72014-07-15 20:30:47 -0700105 * Status register 2:
106 * SRP1: bit 1
107 */
108#define MASK_WP_AREA (0x9C)
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800109#define MASK_WP_AREA_LARGE (0x9C)
David Hendrickse0512a72014-07-15 20:30:47 -0700110#define MASK_WP2_AREA (0x01)
111
David Hendricks57566ed2010-08-16 18:24:45 -0700112struct w25q_range en25f40_ranges[] = {
113 { X, X, 0, {0, 0} }, /* none */
114 { 0, 0, 0x1, {0x000000, 504 * 1024} },
115 { 0, 0, 0x2, {0x000000, 496 * 1024} },
116 { 0, 0, 0x3, {0x000000, 480 * 1024} },
117 { 0, 0, 0x4, {0x000000, 448 * 1024} },
118 { 0, 0, 0x5, {0x000000, 384 * 1024} },
119 { 0, 0, 0x6, {0x000000, 256 * 1024} },
120 { 0, 0, 0x7, {0x000000, 512 * 1024} },
121};
122
David Hendrickse185bf22011-05-24 15:34:18 -0700123struct w25q_range en25q40_ranges[] = {
124 { 0, 0, 0, {0, 0} }, /* none */
125 { 0, 0, 0x1, {0x000000, 504 * 1024} },
126 { 0, 0, 0x2, {0x000000, 496 * 1024} },
127 { 0, 0, 0x3, {0x000000, 480 * 1024} },
128
129 { 0, 1, 0x0, {0x000000, 448 * 1024} },
130 { 0, 1, 0x1, {0x000000, 384 * 1024} },
131 { 0, 1, 0x2, {0x000000, 256 * 1024} },
132 { 0, 1, 0x3, {0x000000, 512 * 1024} },
133};
134
135struct w25q_range en25q80_ranges[] = {
136 { 0, 0, 0, {0, 0} }, /* none */
137 { 0, 0, 0x1, {0x000000, 1016 * 1024} },
138 { 0, 0, 0x2, {0x000000, 1008 * 1024} },
139 { 0, 0, 0x3, {0x000000, 992 * 1024} },
140 { 0, 0, 0x4, {0x000000, 960 * 1024} },
141 { 0, 0, 0x5, {0x000000, 896 * 1024} },
142 { 0, 0, 0x6, {0x000000, 768 * 1024} },
143 { 0, 0, 0x7, {0x000000, 1024 * 1024} },
144};
145
146struct w25q_range en25q32_ranges[] = {
147 { 0, 0, 0, {0, 0} }, /* none */
148 { 0, 0, 0x1, {0x000000, 4032 * 1024} },
149 { 0, 0, 0x2, {0x000000, 3968 * 1024} },
150 { 0, 0, 0x3, {0x000000, 3840 * 1024} },
151 { 0, 0, 0x4, {0x000000, 3584 * 1024} },
152 { 0, 0, 0x5, {0x000000, 3072 * 1024} },
153 { 0, 0, 0x6, {0x000000, 2048 * 1024} },
154 { 0, 0, 0x7, {0x000000, 4096 * 1024} },
155
156 { 0, 1, 0, {0, 0} }, /* none */
157 { 0, 1, 0x1, {0x010000, 4032 * 1024} },
158 { 0, 1, 0x2, {0x020000, 3968 * 1024} },
159 { 0, 1, 0x3, {0x040000, 3840 * 1024} },
160 { 0, 1, 0x4, {0x080000, 3584 * 1024} },
161 { 0, 1, 0x5, {0x100000, 3072 * 1024} },
162 { 0, 1, 0x6, {0x200000, 2048 * 1024} },
163 { 0, 1, 0x7, {0x000000, 4096 * 1024} },
164};
165
166struct w25q_range en25q64_ranges[] = {
167 { 0, 0, 0, {0, 0} }, /* none */
168 { 0, 0, 0x1, {0x000000, 8128 * 1024} },
169 { 0, 0, 0x2, {0x000000, 8064 * 1024} },
170 { 0, 0, 0x3, {0x000000, 7936 * 1024} },
171 { 0, 0, 0x4, {0x000000, 7680 * 1024} },
172 { 0, 0, 0x5, {0x000000, 7168 * 1024} },
173 { 0, 0, 0x6, {0x000000, 6144 * 1024} },
174 { 0, 0, 0x7, {0x000000, 8192 * 1024} },
175
176 { 0, 1, 0, {0, 0} }, /* none */
177 { 0, 1, 0x1, {0x010000, 8128 * 1024} },
178 { 0, 1, 0x2, {0x020000, 8064 * 1024} },
179 { 0, 1, 0x3, {0x040000, 7936 * 1024} },
180 { 0, 1, 0x4, {0x080000, 7680 * 1024} },
181 { 0, 1, 0x5, {0x100000, 7168 * 1024} },
182 { 0, 1, 0x6, {0x200000, 6144 * 1024} },
183 { 0, 1, 0x7, {0x000000, 8192 * 1024} },
184};
185
186struct w25q_range en25q128_ranges[] = {
187 { 0, 0, 0, {0, 0} }, /* none */
188 { 0, 0, 0x1, {0x000000, 16320 * 1024} },
189 { 0, 0, 0x2, {0x000000, 16256 * 1024} },
190 { 0, 0, 0x3, {0x000000, 16128 * 1024} },
191 { 0, 0, 0x4, {0x000000, 15872 * 1024} },
192 { 0, 0, 0x5, {0x000000, 15360 * 1024} },
193 { 0, 0, 0x6, {0x000000, 14336 * 1024} },
194 { 0, 0, 0x7, {0x000000, 16384 * 1024} },
195
196 { 0, 1, 0, {0, 0} }, /* none */
197 { 0, 1, 0x1, {0x010000, 16320 * 1024} },
198 { 0, 1, 0x2, {0x020000, 16256 * 1024} },
199 { 0, 1, 0x3, {0x040000, 16128 * 1024} },
200 { 0, 1, 0x4, {0x080000, 15872 * 1024} },
201 { 0, 1, 0x5, {0x100000, 15360 * 1024} },
202 { 0, 1, 0x6, {0x200000, 14336 * 1024} },
203 { 0, 1, 0x7, {0x000000, 16384 * 1024} },
204};
205
Marc Jonesb2f90022014-04-29 17:37:23 -0600206struct w25q_range en25s64_ranges[] = {
207 { 0, 0, 0, {0, 0} }, /* none */
208 { 0, 0, 0x1, {0x000000, 8064 * 1024} },
209 { 0, 0, 0x2, {0x000000, 7936 * 1024} },
210 { 0, 0, 0x3, {0x000000, 7680 * 1024} },
211 { 0, 0, 0x4, {0x000000, 7168 * 1024} },
212 { 0, 0, 0x5, {0x000000, 6144 * 1024} },
213 { 0, 0, 0x6, {0x000000, 4096 * 1024} },
214 { 0, 0, 0x7, {0x000000, 8192 * 1024} },
215
216 { 0, 1, 0, {0, 0} }, /* none */
217 { 0, 1, 0x1, {0x7e0000, 128 * 1024} },
218 { 0, 1, 0x2, {0x7c0000, 256 * 1024} },
219 { 0, 1, 0x3, {0x780000, 512 * 1024} },
220 { 0, 1, 0x4, {0x700000, 1024 * 1024} },
221 { 0, 1, 0x5, {0x600000, 2048 * 1024} },
222 { 0, 1, 0x6, {0x400000, 4096 * 1024} },
223 { 0, 1, 0x7, {0x000000, 8192 * 1024} },
224};
225
David Hendricksf8f00c72011-02-01 12:39:46 -0800226/* mx25l1005 ranges also work for the mx25l1005c */
227static struct w25q_range mx25l1005_ranges[] = {
228 { X, X, 0, {0, 0} }, /* none */
229 { X, X, 0x1, {0x010000, 64 * 1024} },
230 { X, X, 0x2, {0x000000, 128 * 1024} },
231 { X, X, 0x3, {0x000000, 128 * 1024} },
232};
233
234static struct w25q_range mx25l2005_ranges[] = {
235 { X, X, 0, {0, 0} }, /* none */
236 { X, X, 0x1, {0x030000, 64 * 1024} },
237 { X, X, 0x2, {0x020000, 128 * 1024} },
238 { X, X, 0x3, {0x000000, 256 * 1024} },
239};
240
241static struct w25q_range mx25l4005_ranges[] = {
242 { X, X, 0, {0, 0} }, /* none */
243 { X, X, 0x1, {0x070000, 64 * 1 * 1024} }, /* block 7 */
244 { X, X, 0x2, {0x060000, 64 * 2 * 1024} }, /* blocks 6-7 */
245 { X, X, 0x3, {0x040000, 64 * 4 * 1024} }, /* blocks 4-7 */
246 { X, X, 0x4, {0x000000, 512 * 1024} },
247 { X, X, 0x5, {0x000000, 512 * 1024} },
248 { X, X, 0x6, {0x000000, 512 * 1024} },
249 { X, X, 0x7, {0x000000, 512 * 1024} },
250};
251
252static struct w25q_range mx25l8005_ranges[] = {
253 { X, X, 0, {0, 0} }, /* none */
254 { X, X, 0x1, {0x0f0000, 64 * 1 * 1024} }, /* block 15 */
255 { X, X, 0x2, {0x0e0000, 64 * 2 * 1024} }, /* blocks 14-15 */
256 { X, X, 0x3, {0x0c0000, 64 * 4 * 1024} }, /* blocks 12-15 */
257 { X, X, 0x4, {0x080000, 64 * 8 * 1024} }, /* blocks 8-15 */
258 { X, X, 0x5, {0x000000, 1024 * 1024} },
259 { X, X, 0x6, {0x000000, 1024 * 1024} },
260 { X, X, 0x7, {0x000000, 1024 * 1024} },
261};
262
263#if 0
264/* FIXME: mx25l1605 has the same IDs as the mx25l1605d */
265static struct w25q_range mx25l1605_ranges[] = {
266 { X, X, 0, {0, 0} }, /* none */
267 { X, X, 0x1, {0x1f0000, 64 * 1024} }, /* block 31 */
268 { X, X, 0x2, {0x1e0000, 128 * 1024} }, /* blocks 30-31 */
269 { X, X, 0x3, {0x1c0000, 256 * 1024} }, /* blocks 28-31 */
270 { X, X, 0x4, {0x180000, 512 * 1024} }, /* blocks 24-31 */
271 { X, X, 0x4, {0x100000, 1024 * 1024} }, /* blocks 16-31 */
272 { X, X, 0x6, {0x000000, 2048 * 1024} },
273 { X, X, 0x7, {0x000000, 2048 * 1024} },
274};
275#endif
276
277#if 0
278/* FIXME: mx25l6405 has the same IDs as the mx25l6405d */
279static struct w25q_range mx25l6405_ranges[] = {
280 { X, 0, 0, {0, 0} }, /* none */
281 { X, 0, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
282 { X, 0, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
283 { X, 0, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
284 { X, 0, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
285 { X, 0, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
286 { X, 0, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
287 { X, 0, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
288
289 { X, 1, 0x0, {0x000000, 8192 * 1024} },
290 { X, 1, 0x1, {0x000000, 8192 * 1024} },
291 { X, 1, 0x2, {0x000000, 8192 * 1024} },
292 { X, 1, 0x3, {0x000000, 8192 * 1024} },
293 { X, 1, 0x4, {0x000000, 8192 * 1024} },
294 { X, 1, 0x5, {0x000000, 8192 * 1024} },
295 { X, 1, 0x6, {0x000000, 8192 * 1024} },
296 { X, 1, 0x7, {0x000000, 8192 * 1024} },
297};
298#endif
299
300static struct w25q_range mx25l1605d_ranges[] = {
301 { X, 0, 0, {0, 0} }, /* none */
302 { X, 0, 0x1, {0x1f0000, 64 * 1 * 1024} }, /* block 31 */
303 { X, 0, 0x2, {0x1e0000, 64 * 2 * 1024} }, /* blocks 30-31 */
304 { X, 0, 0x3, {0x1c0000, 64 * 4 * 1024} }, /* blocks 28-31 */
305 { X, 0, 0x4, {0x180000, 64 * 8 * 1024} }, /* blocks 24-31 */
306 { X, 0, 0x5, {0x100000, 64 * 16 * 1024} }, /* blocks 16-31 */
307 { X, 0, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
308 { X, 0, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
309
310 { X, 1, 0x0, {0x000000, 2048 * 1024} },
311 { X, 1, 0x1, {0x000000, 2048 * 1024} },
312 { X, 1, 0x2, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
313 { X, 1, 0x3, {0x000000, 64 * 24 * 1024} }, /* blocks 0-23 */
314 { X, 1, 0x4, {0x000000, 64 * 28 * 1024} }, /* blocks 0-27 */
315 { X, 1, 0x5, {0x000000, 64 * 30 * 1024} }, /* blocks 0-29 */
316 { X, 1, 0x6, {0x000000, 64 * 31 * 1024} }, /* blocks 0-30 */
317 { X, 1, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
318};
319
320/* FIXME: Is there an mx25l3205 (without a trailing letter)? */
David Hendricksac72e362010-08-16 18:20:03 -0700321static struct w25q_range mx25l3205d_ranges[] = {
322 { X, 0, 0, {0, 0} }, /* none */
323 { X, 0, 0x1, {0x3f0000, 64 * 1024} },
324 { X, 0, 0x2, {0x3e0000, 128 * 1024} },
325 { X, 0, 0x3, {0x3c0000, 256 * 1024} },
326 { X, 0, 0x4, {0x380000, 512 * 1024} },
327 { X, 0, 0x5, {0x300000, 1024 * 1024} },
328 { X, 0, 0x6, {0x200000, 2048 * 1024} },
329 { X, 0, 0x7, {0x000000, 4096 * 1024} },
330
331 { X, 1, 0x0, {0x000000, 4096 * 1024} },
332 { X, 1, 0x1, {0x000000, 2048 * 1024} },
333 { X, 1, 0x2, {0x000000, 3072 * 1024} },
334 { X, 1, 0x3, {0x000000, 3584 * 1024} },
335 { X, 1, 0x4, {0x000000, 3840 * 1024} },
336 { X, 1, 0x5, {0x000000, 3968 * 1024} },
337 { X, 1, 0x6, {0x000000, 4032 * 1024} },
338 { X, 1, 0x7, {0x000000, 4096 * 1024} },
339};
340
Vincent Palatin87e092a2013-02-28 15:46:14 -0800341static struct w25q_range mx25u3235e_ranges[] = {
342 { X, 0, 0, {0, 0} }, /* none */
343 { 0, 0, 0x1, {0x3f0000, 64 * 1024} },
344 { 0, 0, 0x2, {0x3e0000, 128 * 1024} },
345 { 0, 0, 0x3, {0x3c0000, 256 * 1024} },
346 { 0, 0, 0x4, {0x380000, 512 * 1024} },
347 { 0, 0, 0x5, {0x300000, 1024 * 1024} },
348 { 0, 0, 0x6, {0x200000, 2048 * 1024} },
349 { 0, 0, 0x7, {0x000000, 4096 * 1024} },
350
351 { 0, 1, 0x0, {0x000000, 4096 * 1024} },
352 { 0, 1, 0x1, {0x000000, 2048 * 1024} },
353 { 0, 1, 0x2, {0x000000, 3072 * 1024} },
354 { 0, 1, 0x3, {0x000000, 3584 * 1024} },
355 { 0, 1, 0x4, {0x000000, 3840 * 1024} },
356 { 0, 1, 0x5, {0x000000, 3968 * 1024} },
357 { 0, 1, 0x6, {0x000000, 4032 * 1024} },
358 { 0, 1, 0x7, {0x000000, 4096 * 1024} },
359};
360
Jongpil66a96492014-08-14 17:59:06 +0900361static struct w25q_range mx25u6435e_ranges[] = {
362 { X, 0, 0, {0, 0} }, /* none */
363 { 0, 0, 0x1, {0x7f0000, 1 * 64 * 1024} }, /* block 127 */
364 { 0, 0, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
365 { 0, 0, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
366 { 0, 0, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
367 { 0, 0, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
368 { 0, 0, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
369 { 0, 0, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
370
371 { 0, 1, 0x0, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
372 { 0, 1, 0x1, {0x000000, 96 * 64 * 1024} }, /* blocks 0-95 */
373 { 0, 1, 0x2, {0x000000, 112 * 64 * 1024} }, /* blocks 0-111 */
374 { 0, 1, 0x3, {0x000000, 120 * 64 * 1024} }, /* blocks 0-119 */
375 { 0, 1, 0x4, {0x000000, 124 * 64 * 1024} }, /* blocks 0-123 */
376 { 0, 1, 0x5, {0x000000, 126 * 64 * 1024} }, /* blocks 0-125 */
377 { 0, 1, 0x6, {0x000000, 127 * 64 * 1024} }, /* blocks 0-126 */
378 { 0, 1, 0x7, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
379};
380
Alex Lu831c6092017-11-02 23:19:34 -0700381static struct w25q_range mx25u12835f_ranges[] = {
382 { X, 0, 0, {0, 0} }, /* none */
383 { 0, 0, 0x1, {0xff0000, 1 * 64 * 1024} }, /* block 255 */
384 { 0, 0, 0x2, {0xfe0000, 2 * 64 * 1024} }, /* blocks 254-255 */
385 { 0, 0, 0x3, {0xfc0000, 4 * 64 * 1024} }, /* blocks 252-255 */
386 { 0, 0, 0x4, {0xf80000, 8 * 64 * 1024} }, /* blocks 248-255 */
387 { 0, 0, 0x5, {0xf00000, 16 * 64 * 1024} }, /* blocks 240-255 */
388 { 0, 0, 0x6, {0xe00000, 32 * 64 * 1024} }, /* blocks 224-255 */
389 { 0, 0, 0x7, {0xc00000, 64 * 64 * 1024} }, /* blocks 192-255 */
390
391 { 0, 1, 0x0, {0x800000, 128 * 64 * 1024} }, /* blocks 128-255 */
392 { 0, 1, 0x1, {0x000000, 256 * 64 * 1024} }, /* blocks all */
393 { 0, 1, 0x2, {0x000000, 256 * 64 * 1024} }, /* blocks all */
394 { 0, 1, 0x3, {0x000000, 256 * 64 * 1024} }, /* blocks all */
395 { 0, 1, 0x4, {0x000000, 256 * 64 * 1024} }, /* blocks all */
396 { 0, 1, 0x5, {0x000000, 256 * 64 * 1024} }, /* blocks all */
397 { 0, 1, 0x6, {0x000000, 256 * 64 * 1024} }, /* blocks all */
398 { 0, 1, 0x7, {0x000000, 256 * 64 * 1024} }, /* blocks all */
399};
400
David Hendricksbfa624b2012-07-24 12:47:59 -0700401static struct w25q_range n25q064_ranges[] = {
David Hendricksfe9123b2015-04-21 13:18:31 -0700402 /*
403 * Note: For N25Q064, sec (usually in bit position 6) is called BP3
404 * (block protect bit 3). It is only useful when all blocks are to
405 * be write-protected.
406 */
David Hendricks42a549a2015-04-22 11:25:07 -0700407 { 0, 0, 0, {0, 0} }, /* none */
David Hendricksbfa624b2012-07-24 12:47:59 -0700408
409 { 0, 0, 0x1, {0x7f0000, 64 * 1024} }, /* block 127 */
410 { 0, 0, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
411 { 0, 0, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
412 { 0, 0, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
413 { 0, 0, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
414 { 0, 0, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
415 { 0, 0, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
416
David Hendricksfe9123b2015-04-21 13:18:31 -0700417 { 0, 1, 0x1, {0x000000, 64 * 1024} }, /* block 0 */
418 { 0, 1, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
419 { 0, 1, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
420 { 0, 1, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
421 { 0, 1, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
422 { 0, 1, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
423 { 0, 1, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700424
425 { X, 1, 0x0, {0x000000, 128 * 64 * 1024} }, /* all */
426 { X, 1, 0x1, {0x000000, 128 * 64 * 1024} }, /* all */
427 { X, 1, 0x2, {0x000000, 128 * 64 * 1024} }, /* all */
428 { X, 1, 0x3, {0x000000, 128 * 64 * 1024} }, /* all */
429 { X, 1, 0x4, {0x000000, 128 * 64 * 1024} }, /* all */
430 { X, 1, 0x5, {0x000000, 128 * 64 * 1024} }, /* all */
431 { X, 1, 0x6, {0x000000, 128 * 64 * 1024} }, /* all */
432 { X, 1, 0x7, {0x000000, 128 * 64 * 1024} }, /* all */
433};
434
David Hendricksf7924d12010-06-10 21:26:44 -0700435static struct w25q_range w25q16_ranges[] = {
436 { X, X, 0, {0, 0} }, /* none */
437 { 0, 0, 0x1, {0x1f0000, 64 * 1024} },
438 { 0, 0, 0x2, {0x1e0000, 128 * 1024} },
439 { 0, 0, 0x3, {0x1c0000, 256 * 1024} },
440 { 0, 0, 0x4, {0x180000, 512 * 1024} },
441 { 0, 0, 0x5, {0x100000, 1024 * 1024} },
442
443 { 0, 1, 0x1, {0x000000, 64 * 1024} },
444 { 0, 1, 0x2, {0x000000, 128 * 1024} },
445 { 0, 1, 0x3, {0x000000, 256 * 1024} },
446 { 0, 1, 0x4, {0x000000, 512 * 1024} },
447 { 0, 1, 0x5, {0x000000, 1024 * 1024} },
448 { X, X, 0x6, {0x000000, 2048 * 1024} },
449 { X, X, 0x7, {0x000000, 2048 * 1024} },
450
451 { 1, 0, 0x1, {0x1ff000, 4 * 1024} },
452 { 1, 0, 0x2, {0x1fe000, 8 * 1024} },
453 { 1, 0, 0x3, {0x1fc000, 16 * 1024} },
454 { 1, 0, 0x4, {0x1f8000, 32 * 1024} },
455 { 1, 0, 0x5, {0x1f8000, 32 * 1024} },
456
457 { 1, 1, 0x1, {0x000000, 4 * 1024} },
458 { 1, 1, 0x2, {0x000000, 8 * 1024} },
459 { 1, 1, 0x3, {0x000000, 16 * 1024} },
460 { 1, 1, 0x4, {0x000000, 32 * 1024} },
461 { 1, 1, 0x5, {0x000000, 32 * 1024} },
462};
463
464static struct w25q_range w25q32_ranges[] = {
465 { X, X, 0, {0, 0} }, /* none */
466 { 0, 0, 0x1, {0x3f0000, 64 * 1024} },
467 { 0, 0, 0x2, {0x3e0000, 128 * 1024} },
468 { 0, 0, 0x3, {0x3c0000, 256 * 1024} },
469 { 0, 0, 0x4, {0x380000, 512 * 1024} },
470 { 0, 0, 0x5, {0x300000, 1024 * 1024} },
David Hendricks05653ff2010-06-15 16:05:12 -0700471 { 0, 0, 0x6, {0x200000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700472
473 { 0, 1, 0x1, {0x000000, 64 * 1024} },
474 { 0, 1, 0x2, {0x000000, 128 * 1024} },
475 { 0, 1, 0x3, {0x000000, 256 * 1024} },
476 { 0, 1, 0x4, {0x000000, 512 * 1024} },
477 { 0, 1, 0x5, {0x000000, 1024 * 1024} },
478 { 0, 1, 0x6, {0x000000, 2048 * 1024} },
479 { X, X, 0x7, {0x000000, 4096 * 1024} },
480
481 { 1, 0, 0x1, {0x3ff000, 4 * 1024} },
482 { 1, 0, 0x2, {0x3fe000, 8 * 1024} },
483 { 1, 0, 0x3, {0x3fc000, 16 * 1024} },
484 { 1, 0, 0x4, {0x3f8000, 32 * 1024} },
485 { 1, 0, 0x5, {0x3f8000, 32 * 1024} },
486
487 { 1, 1, 0x1, {0x000000, 4 * 1024} },
488 { 1, 1, 0x2, {0x000000, 8 * 1024} },
489 { 1, 1, 0x3, {0x000000, 16 * 1024} },
490 { 1, 1, 0x4, {0x000000, 32 * 1024} },
491 { 1, 1, 0x5, {0x000000, 32 * 1024} },
492};
493
494static struct w25q_range w25q80_ranges[] = {
495 { X, X, 0, {0, 0} }, /* none */
496 { 0, 0, 0x1, {0x0f0000, 64 * 1024} },
497 { 0, 0, 0x2, {0x0e0000, 128 * 1024} },
498 { 0, 0, 0x3, {0x0c0000, 256 * 1024} },
499 { 0, 0, 0x4, {0x080000, 512 * 1024} },
500
501 { 0, 1, 0x1, {0x000000, 64 * 1024} },
502 { 0, 1, 0x2, {0x000000, 128 * 1024} },
503 { 0, 1, 0x3, {0x000000, 256 * 1024} },
504 { 0, 1, 0x4, {0x000000, 512 * 1024} },
David Hendricks05653ff2010-06-15 16:05:12 -0700505 { X, X, 0x6, {0x000000, 1024 * 1024} },
506 { X, X, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700507
508 { 1, 0, 0x1, {0x1ff000, 4 * 1024} },
509 { 1, 0, 0x2, {0x1fe000, 8 * 1024} },
510 { 1, 0, 0x3, {0x1fc000, 16 * 1024} },
511 { 1, 0, 0x4, {0x1f8000, 32 * 1024} },
512 { 1, 0, 0x5, {0x1f8000, 32 * 1024} },
513
514 { 1, 1, 0x1, {0x000000, 4 * 1024} },
515 { 1, 1, 0x2, {0x000000, 8 * 1024} },
516 { 1, 1, 0x3, {0x000000, 16 * 1024} },
517 { 1, 1, 0x4, {0x000000, 32 * 1024} },
518 { 1, 1, 0x5, {0x000000, 32 * 1024} },
519};
520
David Hendricks2c4a76c2010-06-28 14:00:43 -0700521static struct w25q_range w25q64_ranges[] = {
522 { X, X, 0, {0, 0} }, /* none */
523
524 { 0, 0, 0x1, {0x7e0000, 128 * 1024} },
525 { 0, 0, 0x2, {0x7c0000, 256 * 1024} },
526 { 0, 0, 0x3, {0x780000, 512 * 1024} },
527 { 0, 0, 0x4, {0x700000, 1024 * 1024} },
528 { 0, 0, 0x5, {0x600000, 2048 * 1024} },
529 { 0, 0, 0x6, {0x400000, 4096 * 1024} },
530
531 { 0, 1, 0x1, {0x000000, 128 * 1024} },
532 { 0, 1, 0x2, {0x000000, 256 * 1024} },
533 { 0, 1, 0x3, {0x000000, 512 * 1024} },
534 { 0, 1, 0x4, {0x000000, 1024 * 1024} },
535 { 0, 1, 0x5, {0x000000, 2048 * 1024} },
536 { 0, 1, 0x6, {0x000000, 4096 * 1024} },
537 { X, X, 0x7, {0x000000, 8192 * 1024} },
538
539 { 1, 0, 0x1, {0x7ff000, 4 * 1024} },
540 { 1, 0, 0x2, {0x7fe000, 8 * 1024} },
541 { 1, 0, 0x3, {0x7fc000, 16 * 1024} },
542 { 1, 0, 0x4, {0x7f8000, 32 * 1024} },
543 { 1, 0, 0x5, {0x7f8000, 32 * 1024} },
544
545 { 1, 1, 0x1, {0x000000, 4 * 1024} },
546 { 1, 1, 0x2, {0x000000, 8 * 1024} },
547 { 1, 1, 0x3, {0x000000, 16 * 1024} },
548 { 1, 1, 0x4, {0x000000, 32 * 1024} },
549 { 1, 1, 0x5, {0x000000, 32 * 1024} },
550};
551
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700552static struct w25q_range w25rq128_cmp0_ranges[] = {
553 { X, X, 0, {0, 0} }, /* NONE */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530554
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700555 { 0, 0, 0x1, {0xfc0000, 256 * 1024} }, /* Upper 1/64 */
556 { 0, 0, 0x2, {0xf80000, 512 * 1024} }, /* Upper 1/32 */
557 { 0, 0, 0x3, {0xf00000, 1024 * 1024} }, /* Upper 1/16 */
558 { 0, 0, 0x4, {0xe00000, 2048 * 1024} }, /* Upper 1/8 */
559 { 0, 0, 0x5, {0xc00000, 4096 * 1024} }, /* Upper 1/4 */
560 { 0, 0, 0x6, {0x800000, 8192 * 1024} }, /* Upper 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530561
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700562 { 0, 1, 0x1, {0x000000, 256 * 1024} }, /* Lower 1/64 */
563 { 0, 1, 0x2, {0x000000, 512 * 1024} }, /* Lower 1/32 */
564 { 0, 1, 0x3, {0x000000, 1024 * 1024} }, /* Lower 1/16 */
565 { 0, 1, 0x4, {0x000000, 2048 * 1024} }, /* Lower 1/8 */
566 { 0, 1, 0x5, {0x000000, 4096 * 1024} }, /* Lower 1/4 */
567 { 0, 1, 0x6, {0x000000, 8192 * 1024} }, /* Lower 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530568
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700569 { X, X, 0x7, {0x000000, 16384 * 1024} }, /* ALL */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530570
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700571 { 1, 0, 0x1, {0xfff000, 4 * 1024} }, /* Upper 1/4096 */
572 { 1, 0, 0x2, {0xffe000, 8 * 1024} }, /* Upper 1/2048 */
573 { 1, 0, 0x3, {0xffc000, 16 * 1024} }, /* Upper 1/1024 */
574 { 1, 0, 0x4, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
575 { 1, 0, 0x5, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
576
577 { 1, 1, 0x1, {0x000000, 4 * 1024} }, /* Lower 1/4096 */
578 { 1, 1, 0x2, {0x000000, 8 * 1024} }, /* Lower 1/2048 */
579 { 1, 1, 0x3, {0x000000, 16 * 1024} }, /* Lower 1/1024 */
580 { 1, 1, 0x4, {0x000000, 32 * 1024} }, /* Lower 1/512 */
581 { 1, 1, 0x5, {0x000000, 32 * 1024} }, /* Lower 1/512 */
582};
583
584static struct w25q_range w25rq128_cmp1_ranges[] = {
585 { X, X, 0x0, {0x000000, 16 * 1024 * 1024} }, /* ALL */
586
587 { 0, 0, 0x1, {0x000000, 16128 * 1024} }, /* Lower 63/64 */
588 { 0, 0, 0x2, {0x000000, 15872 * 1024} }, /* Lower 31/32 */
589 { 0, 0, 0x3, {0x000000, 15 * 1024 * 1024} }, /* Lower 15/16 */
590 { 0, 0, 0x4, {0x000000, 14 * 1024 * 1024} }, /* Lower 7/8 */
591 { 0, 0, 0x5, {0x000000, 12 * 1024 * 1024} }, /* Lower 3/4 */
592 { 0, 0, 0x6, {0x000000, 8 * 1024 * 1024} }, /* Lower 1/2 */
593
594 { 0, 1, 0x1, {0x040000, 16128 * 1024} }, /* Upper 63/64 */
595 { 0, 1, 0x2, {0x080000, 15872 * 1024} }, /* Upper 31/32 */
596 { 0, 1, 0x3, {0x100000, 15 * 1024 * 1024} }, /* Upper 15/16 */
597 { 0, 1, 0x4, {0x200000, 14 * 1024 * 1024} }, /* Upper 7/8 */
598 { 0, 1, 0x5, {0x400000, 12 * 1024 * 1024} }, /* Upper 3/4 */
599 { 0, 1, 0x6, {0x800000, 8 * 1024 * 1024} }, /* Upper 1/2 */
600
601 { X, X, 0x7, {0x000000, 0} }, /* NONE */
602
603 { 1, 0, 0x1, {0x000000, 16380 * 1024} }, /* Lower 4095/4096 */
604 { 1, 0, 0x2, {0x000000, 16376 * 1024} }, /* Lower 2048/2048 */
605 { 1, 0, 0x3, {0x000000, 16368 * 1024} }, /* Lower 1023/1024 */
606 { 1, 0, 0x4, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
607 { 1, 0, 0x5, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
608
609 { 1, 1, 0x1, {0x001000, 16380 * 1024} }, /* Upper 4095/4096 */
610 { 1, 1, 0x2, {0x002000, 16376 * 1024} }, /* Upper 2047/2048 */
611 { 1, 1, 0x3, {0x004000, 16368 * 1024} }, /* Upper 1023/1024 */
612 { 1, 1, 0x4, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
613 { 1, 1, 0x5, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530614};
615
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800616static struct w25q_range w25rq256_cmp0_ranges[] = {
617 { X, X, 0x0, {0x0000000, 0x0000000} }, /* NONE */
618
619 { X, 0, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* Upper 1/512 */
620 { X, 0, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* Upper 1/256 */
621 { X, 0, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* Upper 1/128 */
622 { X, 0, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* Upper 1/64 */
623 { X, 0, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* Upper 1/32 */
624 { X, 0, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* Upper 1/16 */
625 { X, 0, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* Upper 1/8 */
626 { X, 0, 0x8, {0x1800000, 64 * 128 * 1024} }, /* Upper 1/4 */
627 { X, 0, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
628
629 { X, 1, 0x1, {0x0000000, 64 * 1 * 1024} }, /* Lower 1/512 */
630 { X, 1, 0x2, {0x0000000, 64 * 2 * 1024} }, /* Lower 1/256 */
631 { X, 1, 0x3, {0x0000000, 64 * 4 * 1024} }, /* Lower 1/128 */
632 { X, 1, 0x4, {0x0000000, 64 * 8 * 1024} }, /* Lower 1/64 */
633 { X, 1, 0x5, {0x0000000, 64 * 16 * 1024} }, /* Lower 1/32 */
634 { X, 1, 0x6, {0x0000000, 64 * 32 * 1024} }, /* Lower 1/16 */
635 { X, 1, 0x7, {0x0000000, 64 * 64 * 1024} }, /* Lower 1/8 */
636 { X, 1, 0x8, {0x0000000, 64 * 128 * 1024} }, /* Lower 1/4 */
637 { X, 1, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
638
639 { X, X, 0xa, {0x0000000, 64 * 512 * 1024} }, /* ALL */
640 { X, X, 0xb, {0x0000000, 64 * 512 * 1024} }, /* ALL */
641 { X, X, 0xc, {0x0000000, 64 * 512 * 1024} }, /* ALL */
642 { X, X, 0xd, {0x0000000, 64 * 512 * 1024} }, /* ALL */
643 { X, X, 0xe, {0x0000000, 64 * 512 * 1024} }, /* ALL */
644 { X, X, 0xf, {0x0000000, 64 * 512 * 1024} }, /* ALL */
645};
646
647static struct w25q_range w25rq256_cmp1_ranges[] = {
648 { X, X, 0x0, {0x0000000, 64 * 512 * 1024} }, /* ALL */
649
650 { X, 0, 0x1, {0x0000000, 64 * 511 * 1024} }, /* Lower 511/512 */
651 { X, 0, 0x2, {0x0000000, 64 * 510 * 1024} }, /* Lower 255/256 */
652 { X, 0, 0x3, {0x0000000, 64 * 508 * 1024} }, /* Lower 127/128 */
653 { X, 0, 0x4, {0x0000000, 64 * 504 * 1024} }, /* Lower 63/64 */
654 { X, 0, 0x5, {0x0000000, 64 * 496 * 1024} }, /* Lower 31/32 */
655 { X, 0, 0x6, {0x0000000, 64 * 480 * 1024} }, /* Lower 15/16 */
656 { X, 0, 0x7, {0x0000000, 64 * 448 * 1024} }, /* Lower 7/8 */
657 { X, 0, 0x8, {0x0000000, 64 * 384 * 1024} }, /* Lower 3/4 */
658 { X, 0, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
659
660 { X, 1, 0x1, {0x0010000, 64 * 511 * 1024} }, /* Upper 511/512 */
661 { X, 1, 0x2, {0x0020000, 64 * 510 * 1024} }, /* Upper 255/256 */
662 { X, 1, 0x3, {0x0040000, 64 * 508 * 1024} }, /* Upper 127/128 */
663 { X, 1, 0x4, {0x0080000, 64 * 504 * 1024} }, /* Upper 63/64 */
664 { X, 1, 0x5, {0x0100000, 64 * 496 * 1024} }, /* Upper 31/32 */
665 { X, 1, 0x6, {0x0200000, 64 * 480 * 1024} }, /* Upper 15/16 */
666 { X, 1, 0x7, {0x0400000, 64 * 448 * 1024} }, /* Upper 7/8 */
667 { X, 1, 0x8, {0x0800000, 64 * 384 * 1024} }, /* Upper 3/4 */
668 { X, 1, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
669
670 { X, X, 0xa, {0x0000000, 0x0000000} }, /* NONE */
671 { X, X, 0xb, {0x0000000, 0x0000000} }, /* NONE */
672 { X, X, 0xc, {0x0000000, 0x0000000} }, /* NONE */
673 { X, X, 0xd, {0x0000000, 0x0000000} }, /* NONE */
674 { X, X, 0xe, {0x0000000, 0x0000000} }, /* NONE */
675 { X, X, 0xf, {0x0000000, 0x0000000} }, /* NONE */
676};
677
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800678struct w25q_range w25x10_ranges[] = {
679 { X, X, 0, {0, 0} }, /* none */
680 { 0, 0, 0x1, {0x010000, 64 * 1024} },
681 { 0, 1, 0x1, {0x000000, 64 * 1024} },
682 { X, X, 0x2, {0x000000, 128 * 1024} },
683 { X, X, 0x3, {0x000000, 128 * 1024} },
684};
685
686struct w25q_range w25x20_ranges[] = {
687 { X, X, 0, {0, 0} }, /* none */
688 { 0, 0, 0x1, {0x030000, 64 * 1024} },
689 { 0, 0, 0x2, {0x020000, 128 * 1024} },
690 { 0, 1, 0x1, {0x000000, 64 * 1024} },
691 { 0, 1, 0x2, {0x000000, 128 * 1024} },
692 { 0, X, 0x3, {0x000000, 256 * 1024} },
693};
694
David Hendricks470ca952010-08-13 14:01:53 -0700695struct w25q_range w25x40_ranges[] = {
696 { X, X, 0, {0, 0} }, /* none */
697 { 0, 0, 0x1, {0x070000, 64 * 1024} },
698 { 0, 0, 0x2, {0x060000, 128 * 1024} },
699 { 0, 0, 0x3, {0x040000, 256 * 1024} },
700 { 0, 1, 0x1, {0x000000, 64 * 1024} },
701 { 0, 1, 0x2, {0x000000, 128 * 1024} },
702 { 0, 1, 0x3, {0x000000, 256 * 1024} },
703 { 0, X, 0x4, {0x000000, 512 * 1024} },
David Hendricksb389abb2016-06-17 16:47:00 -0700704 { 0, X, 0x5, {0x000000, 512 * 1024} },
705 { 0, X, 0x6, {0x000000, 512 * 1024} },
706 { 0, X, 0x7, {0x000000, 512 * 1024} },
David Hendricks470ca952010-08-13 14:01:53 -0700707};
708
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800709struct w25q_range w25x80_ranges[] = {
710 { X, X, 0, {0, 0} }, /* none */
711 { 0, 0, 0x1, {0x0F0000, 64 * 1024} },
712 { 0, 0, 0x2, {0x0E0000, 128 * 1024} },
713 { 0, 0, 0x3, {0x0C0000, 256 * 1024} },
714 { 0, 0, 0x4, {0x080000, 512 * 1024} },
715 { 0, 1, 0x1, {0x000000, 64 * 1024} },
716 { 0, 1, 0x2, {0x000000, 128 * 1024} },
717 { 0, 1, 0x3, {0x000000, 256 * 1024} },
718 { 0, 1, 0x4, {0x000000, 512 * 1024} },
719 { 0, X, 0x5, {0x000000, 1024 * 1024} },
720 { 0, X, 0x6, {0x000000, 1024 * 1024} },
721 { 0, X, 0x7, {0x000000, 1024 * 1024} },
722};
723
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600724static struct w25q_range gd25q40_cmp0_ranges[] = {
725 { X, X, 0, {0, 0} }, /* None */
726 { 0, 0, 0x1, {0x070000, 64 * 1024} },
727 { 0, 0, 0x2, {0x060000, 128 * 1024} },
728 { 0, 0, 0x3, {0x040000, 256 * 1024} },
729 { 0, 1, 0x1, {0x000000, 64 * 1024} },
730 { 0, 1, 0x2, {0x000000, 128 * 1024} },
731 { 0, 1, 0x3, {0x000000, 256 * 1024} },
732 { 0, X, 0x4, {0x000000, 512 * 1024} }, /* All */
733 { 0, X, 0x5, {0x000000, 512 * 1024} }, /* All */
734 { 0, X, 0x6, {0x000000, 512 * 1024} }, /* All */
735 { 0, X, 0x7, {0x000000, 512 * 1024} }, /* All */
736 { 1, 0, 0x1, {0x07F000, 4 * 1024} },
737 { 1, 0, 0x2, {0x07E000, 8 * 1024} },
738 { 1, 0, 0x3, {0x07C000, 16 * 1024} },
739 { 1, 0, 0x4, {0x078000, 32 * 1024} },
740 { 1, 0, 0x5, {0x078000, 32 * 1024} },
741 { 1, 0, 0x6, {0x078000, 32 * 1024} },
742 { 1, 1, 0x1, {0x000000, 4 * 1024} },
743 { 1, 1, 0x2, {0x000000, 8 * 1024} },
744 { 1, 1, 0x3, {0x000000, 16 * 1024} },
745 { 1, 1, 0x4, {0x000000, 32 * 1024} },
746 { 1, 1, 0x5, {0x000000, 32 * 1024} },
747 { 1, 1, 0x6, {0x000000, 32 * 1024} },
748 { 1, X, 0x7, {0x000000, 512 * 1024} }, /* All */
749};
750
751static struct w25q_range gd25q40_cmp1_ranges[] = {
752 { X, X, 0x0, {0x000000, 512 * 1024} }, /* ALL */
753 { 0, 0, 0x1, {0x000000, 448 * 1024} },
754 { 0, 0, 0x2, {0x000000, 384 * 1024} },
755 { 0, 0, 0x3, {0x000000, 256 * 1024} },
756
757 { 0, 1, 0x1, {0x010000, 448 * 1024} },
758 { 0, 1, 0x2, {0x020000, 384 * 1024} },
759 { 0, 1, 0x3, {0x040000, 256 * 1024} },
760
761 { 0, X, 0x4, {0x000000, 0} }, /* None */
762 { 0, X, 0x5, {0x000000, 0} }, /* None */
763 { 0, X, 0x6, {0x000000, 0} }, /* None */
764 { 0, X, 0x7, {0x000000, 0} }, /* None */
765
766 { 1, 0, 0x1, {0x000000, 508 * 1024} },
767 { 1, 0, 0x2, {0x000000, 504 * 1024} },
768 { 1, 0, 0x3, {0x000000, 496 * 1024} },
769 { 1, 0, 0x4, {0x000000, 480 * 1024} },
770 { 1, 0, 0x5, {0x000000, 480 * 1024} },
771 { 1, 0, 0x6, {0x000000, 480 * 1024} },
772
773 { 1, 1, 0x1, {0x001000, 508 * 1024} },
774 { 1, 1, 0x2, {0x002000, 504 * 1024} },
775 { 1, 1, 0x3, {0x004000, 496 * 1024} },
776 { 1, 1, 0x4, {0x008000, 480 * 1024} },
777 { 1, 1, 0x5, {0x008000, 480 * 1024} },
778 { 1, 1, 0x6, {0x008000, 480 * 1024} },
779
780 { 1, X, 0x7, {0x000000, 0} }, /* None */
781};
782
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700783static struct w25q_range gd25q64_ranges[] = {
784 { X, X, 0, {0, 0} }, /* none */
785 { 0, 0, 0x1, {0x7e0000, 128 * 1024} },
786 { 0, 0, 0x2, {0x7c0000, 256 * 1024} },
787 { 0, 0, 0x3, {0x780000, 512 * 1024} },
788 { 0, 0, 0x4, {0x700000, 1024 * 1024} },
789 { 0, 0, 0x5, {0x600000, 2048 * 1024} },
790 { 0, 0, 0x6, {0x400000, 4096 * 1024} },
791
792 { 0, 1, 0x1, {0x000000, 128 * 1024} },
793 { 0, 1, 0x2, {0x000000, 256 * 1024} },
794 { 0, 1, 0x3, {0x000000, 512 * 1024} },
795 { 0, 1, 0x4, {0x000000, 1024 * 1024} },
796 { 0, 1, 0x5, {0x000000, 2048 * 1024} },
797 { 0, 1, 0x6, {0x000000, 4096 * 1024} },
798 { X, X, 0x7, {0x000000, 8192 * 1024} },
799
800 { 1, 0, 0x1, {0x7ff000, 4 * 1024} },
801 { 1, 0, 0x2, {0x7fe000, 8 * 1024} },
802 { 1, 0, 0x3, {0x7fc000, 16 * 1024} },
803 { 1, 0, 0x4, {0x7f8000, 32 * 1024} },
804 { 1, 0, 0x5, {0x7f8000, 32 * 1024} },
805 { 1, 0, 0x6, {0x7f8000, 32 * 1024} },
806
807 { 1, 1, 0x1, {0x000000, 4 * 1024} },
808 { 1, 1, 0x2, {0x000000, 8 * 1024} },
809 { 1, 1, 0x3, {0x000000, 16 * 1024} },
810 { 1, 1, 0x4, {0x000000, 32 * 1024} },
811 { 1, 1, 0x5, {0x000000, 32 * 1024} },
812 { 1, 1, 0x6, {0x000000, 32 * 1024} },
813};
814
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +0800815static struct w25q_range a25l040_ranges[] = {
816 { X, X, 0x0, {0, 0} }, /* none */
817 { X, X, 0x1, {0x70000, 64 * 1024} },
818 { X, X, 0x2, {0x60000, 128 * 1024} },
819 { X, X, 0x3, {0x40000, 256 * 1024} },
820 { X, X, 0x4, {0x00000, 512 * 1024} },
821 { X, X, 0x5, {0x00000, 512 * 1024} },
822 { X, X, 0x6, {0x00000, 512 * 1024} },
823 { X, X, 0x7, {0x00000, 512 * 1024} },
824};
825
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700826static uint8_t do_read_status(const struct flashctx *flash)
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530827{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100828 if (flash->chip->read_status)
829 return flash->chip->read_status(flash);
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530830 else
831 return spi_read_status_register(flash);
832}
833
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700834static int do_write_status(const struct flashctx *flash, int status)
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530835{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100836 if (flash->chip->write_status)
837 return flash->chip->write_status(flash, status);
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530838 else
839 return spi_write_status_register(flash, status);
840}
841
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700842/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700843static uint8_t w25q_read_status_register_2(const struct flashctx *flash)
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700844{
845 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 };
846 unsigned char readarr[2];
847 int ret;
848
849 /* Read Status Register */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700850 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700851 if (ret) {
852 /*
853 * FIXME: make this a benign failure for now in case we are
854 * unable to execute the opcode
855 */
856 msg_cdbg("RDSR2 failed!\n");
857 readarr[0] = 0x00;
858 }
859
860 return readarr[0];
861}
862
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800863/* Given a flash chip, this function returns its range table. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700864static int w25_range_table(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800865 struct w25q_range **w25q_ranges,
866 int *num_entries)
David Hendricksf7924d12010-06-10 21:26:44 -0700867{
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800868 *w25q_ranges = 0;
869 *num_entries = 0;
David Hendricksf7924d12010-06-10 21:26:44 -0700870
Patrick Georgif3fa2992017-02-02 16:24:44 +0100871 switch (flash->chip->manufacture_id) {
David Hendricksd494b0a2010-08-16 16:28:50 -0700872 case WINBOND_NEX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +0100873 switch(flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -0800874 case WINBOND_NEX_W25X10:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800875 *w25q_ranges = w25x10_ranges;
876 *num_entries = ARRAY_SIZE(w25x10_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800877 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800878 case WINBOND_NEX_W25X20:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800879 *w25q_ranges = w25x20_ranges;
880 *num_entries = ARRAY_SIZE(w25x20_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800881 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800882 case WINBOND_NEX_W25X40:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800883 *w25q_ranges = w25x40_ranges;
884 *num_entries = ARRAY_SIZE(w25x40_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700885 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800886 case WINBOND_NEX_W25X80:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800887 *w25q_ranges = w25x80_ranges;
888 *num_entries = ARRAY_SIZE(w25x80_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800889 break;
Patrick Georgicc04a452017-02-06 12:14:43 +0100890 case WINBOND_NEX_W25Q80_V:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800891 *w25q_ranges = w25q80_ranges;
892 *num_entries = ARRAY_SIZE(w25q80_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700893 break;
Patrick Georgicc04a452017-02-06 12:14:43 +0100894 case WINBOND_NEX_W25Q16_V:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800895 *w25q_ranges = w25q16_ranges;
896 *num_entries = ARRAY_SIZE(w25q16_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700897 break;
Patrick Georgicc04a452017-02-06 12:14:43 +0100898 case WINBOND_NEX_W25Q32_V:
899 case WINBOND_NEX_W25Q32_W:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800900 *w25q_ranges = w25q32_ranges;
901 *num_entries = ARRAY_SIZE(w25q32_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700902 break;
Patrick Georgicc04a452017-02-06 12:14:43 +0100903 case WINBOND_NEX_W25Q64_V:
904 case WINBOND_NEX_W25Q64_W:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800905 *w25q_ranges = w25q64_ranges;
906 *num_entries = ARRAY_SIZE(w25q64_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700907 break;
Martin Rothee8dcf92017-05-10 19:16:19 -0600908 case WINBOND_NEX_W25Q128J:
Patrick Georgicc04a452017-02-06 12:14:43 +0100909 case WINBOND_NEX_W25Q128_V:
910 case WINBOND_NEX_W25Q128_W:
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700911 if (w25q_read_status_register_2(flash) & (1 << 6)) {
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700912 /* CMP == 1 */
913 *w25q_ranges = w25rq128_cmp1_ranges;
914 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
915 } else {
916 /* CMP == 0 */
917 *w25q_ranges = w25rq128_cmp0_ranges;
918 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
919 }
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530920 break;
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800921 case WINBOND_NEX_W25Q256JV:
922 if (w25q_read_status_register_2(flash) & (1 << 6)) {
923 /* CMP == 1 */
924 *w25q_ranges = w25rq256_cmp1_ranges;
925 *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges);
926 } else {
927 /* CMP == 0 */
928 *w25q_ranges = w25rq256_cmp0_ranges;
929 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
930 }
931 break;
David Hendricksd494b0a2010-08-16 16:28:50 -0700932 default:
933 msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
934 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +0100935 flash->chip->model_id);
David Hendricksd494b0a2010-08-16 16:28:50 -0700936 return -1;
937 }
David Hendricks2c4a76c2010-06-28 14:00:43 -0700938 break;
David Hendricks57566ed2010-08-16 18:24:45 -0700939 case EON_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +0100940 switch (flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -0800941 case EON_EN25F40:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800942 *w25q_ranges = en25f40_ranges;
943 *num_entries = ARRAY_SIZE(en25f40_ranges);
David Hendricks57566ed2010-08-16 18:24:45 -0700944 break;
David Hendrickse185bf22011-05-24 15:34:18 -0700945 case EON_EN25Q40:
946 *w25q_ranges = en25q40_ranges;
947 *num_entries = ARRAY_SIZE(en25q40_ranges);
948 break;
949 case EON_EN25Q80:
950 *w25q_ranges = en25q80_ranges;
951 *num_entries = ARRAY_SIZE(en25q80_ranges);
952 break;
953 case EON_EN25Q32:
954 *w25q_ranges = en25q32_ranges;
955 *num_entries = ARRAY_SIZE(en25q32_ranges);
956 break;
957 case EON_EN25Q64:
958 *w25q_ranges = en25q64_ranges;
959 *num_entries = ARRAY_SIZE(en25q64_ranges);
960 break;
961 case EON_EN25Q128:
962 *w25q_ranges = en25q128_ranges;
963 *num_entries = ARRAY_SIZE(en25q128_ranges);
964 break;
Marc Jonesb2f90022014-04-29 17:37:23 -0600965 case EON_EN25S64:
966 *w25q_ranges = en25s64_ranges;
967 *num_entries = ARRAY_SIZE(en25s64_ranges);
968 break;
David Hendricks57566ed2010-08-16 18:24:45 -0700969 default:
970 msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
971 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +0100972 flash->chip->model_id);
David Hendricks57566ed2010-08-16 18:24:45 -0700973 return -1;
974 }
975 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800976 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +0100977 switch (flash->chip->model_id) {
David Hendricksf8f00c72011-02-01 12:39:46 -0800978 case MACRONIX_MX25L1005:
979 *w25q_ranges = mx25l1005_ranges;
980 *num_entries = ARRAY_SIZE(mx25l1005_ranges);
981 break;
982 case MACRONIX_MX25L2005:
983 *w25q_ranges = mx25l2005_ranges;
984 *num_entries = ARRAY_SIZE(mx25l2005_ranges);
985 break;
986 case MACRONIX_MX25L4005:
987 *w25q_ranges = mx25l4005_ranges;
988 *num_entries = ARRAY_SIZE(mx25l4005_ranges);
989 break;
990 case MACRONIX_MX25L8005:
991 *w25q_ranges = mx25l8005_ranges;
992 *num_entries = ARRAY_SIZE(mx25l8005_ranges);
993 break;
994 case MACRONIX_MX25L1605:
995 /* FIXME: MX25L1605 and MX25L1605D have different write
996 * protection capabilities, but share IDs */
997 *w25q_ranges = mx25l1605d_ranges;
998 *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
999 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001000 case MACRONIX_MX25L3205:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001001 *w25q_ranges = mx25l3205d_ranges;
1002 *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
David Hendricksac72e362010-08-16 18:20:03 -07001003 break;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001004 case MACRONIX_MX25U3235E:
1005 *w25q_ranges = mx25u3235e_ranges;
1006 *num_entries = ARRAY_SIZE(mx25u3235e_ranges);
1007 break;
Jongpil66a96492014-08-14 17:59:06 +09001008 case MACRONIX_MX25U6435E:
1009 *w25q_ranges = mx25u6435e_ranges;
1010 *num_entries = ARRAY_SIZE(mx25u6435e_ranges);
1011 break;
Alex Lu831c6092017-11-02 23:19:34 -07001012 case MACRONIX_MX25U12835F:
1013 *w25q_ranges = mx25u12835f_ranges;
1014 *num_entries = ARRAY_SIZE(mx25u12835f_ranges);
1015 break;
David Hendricksac72e362010-08-16 18:20:03 -07001016 default:
1017 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
1018 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001019 flash->chip->model_id);
David Hendricksac72e362010-08-16 18:20:03 -07001020 return -1;
1021 }
1022 break;
David Hendricksbfa624b2012-07-24 12:47:59 -07001023 case ST_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001024 switch(flash->chip->model_id) {
David Hendricksbfa624b2012-07-24 12:47:59 -07001025 case ST_N25Q064__1E:
1026 case ST_N25Q064__3E:
1027 *w25q_ranges = n25q064_ranges;
1028 *num_entries = ARRAY_SIZE(n25q064_ranges);
1029 break;
1030 default:
1031 msg_cerr("%s() %d: Micron flash chip mismatch"
1032 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001033 flash->chip->model_id);
David Hendricksbfa624b2012-07-24 12:47:59 -07001034 return -1;
1035 }
1036 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001037 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001038 switch(flash->chip->model_id) {
Bryan Freed9a0051f2012-05-22 16:06:09 -07001039 case GIGADEVICE_GD25LQ32:
1040 *w25q_ranges = w25q32_ranges;
1041 *num_entries = ARRAY_SIZE(w25q32_ranges);
1042 break;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001043 case GIGADEVICE_GD25Q40:
1044 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1045 /* CMP == 1 */
1046 *w25q_ranges = gd25q40_cmp1_ranges;
1047 *num_entries = ARRAY_SIZE(gd25q40_cmp1_ranges);
1048 } else {
1049 *w25q_ranges = gd25q40_cmp0_ranges;
1050 *num_entries = ARRAY_SIZE(gd25q40_cmp0_ranges);
1051 }
1052 break;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001053 case GIGADEVICE_GD25Q64:
Marc Jonesb18734f2014-04-03 16:19:47 -06001054 case GIGADEVICE_GD25LQ64:
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001055 *w25q_ranges = gd25q64_ranges;
1056 *num_entries = ARRAY_SIZE(gd25q64_ranges);
1057 break;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001058 case GIGADEVICE_GD25Q128:
Aaron Durbin6c957d72018-08-20 09:31:01 -06001059 case GIGADEVICE_GD25LQ128CD:
Martin Roth1fd87ed2017-02-27 20:50:50 -07001060 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1061 /* CMP == 1 */
1062 *w25q_ranges = w25rq128_cmp1_ranges;
1063 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1064 } else {
1065 /* CMP == 0 */
1066 *w25q_ranges = w25rq128_cmp0_ranges;
1067 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1068 }
1069 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001070 default:
1071 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
1072 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001073 flash->chip->model_id);
Bryan Freed9a0051f2012-05-22 16:06:09 -07001074 return -1;
1075 }
1076 break;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001077 case AMIC_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001078 switch(flash->chip->model_id) {
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001079 case AMIC_A25L040:
1080 *w25q_ranges = a25l040_ranges;
1081 *num_entries = ARRAY_SIZE(a25l040_ranges);
1082 break;
1083 default:
1084 msg_cerr("%s() %d: AMIC flash chip mismatch"
1085 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001086 flash->chip->model_id);
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001087 return -1;
1088 }
1089 break;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001090 case ATMEL_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001091 switch(flash->chip->model_id) {
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001092 case ATMEL_AT25SL128A:
1093 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1094 /* CMP == 1 */
1095 *w25q_ranges = w25rq128_cmp1_ranges;
1096 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1097 } else {
1098 /* CMP == 0 */
1099 *w25q_ranges = w25rq128_cmp0_ranges;
1100 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1101 }
1102 break;
1103 default:
1104 msg_cerr("%s() %d: Atmel flash chip mismatch"
1105 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001106 flash->chip->model_id);
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001107 return -1;
1108 }
1109 break;
David Hendricksf7924d12010-06-10 21:26:44 -07001110 default:
David Hendricksd494b0a2010-08-16 16:28:50 -07001111 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01001112 __func__, flash->chip->manufacture_id);
David Hendricksf7924d12010-06-10 21:26:44 -07001113 return -1;
1114 }
1115
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001116 return 0;
1117}
1118
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001119int w25_range_to_status(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001120 unsigned int start, unsigned int len,
1121 struct w25q_status *status)
1122{
1123 struct w25q_range *w25q_ranges;
1124 int i, range_found = 0;
1125 int num_entries;
1126
1127 if (w25_range_table(flash, &w25q_ranges, &num_entries)) return -1;
David Hendricksf7924d12010-06-10 21:26:44 -07001128 for (i = 0; i < num_entries; i++) {
1129 struct wp_range *r = &w25q_ranges[i].range;
1130
1131 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1132 start, len, r->start, r->len);
1133 if ((start == r->start) && (len == r->len)) {
David Hendricksd494b0a2010-08-16 16:28:50 -07001134 status->bp0 = w25q_ranges[i].bp & 1;
1135 status->bp1 = w25q_ranges[i].bp >> 1;
1136 status->bp2 = w25q_ranges[i].bp >> 2;
1137 status->tb = w25q_ranges[i].tb;
1138 status->sec = w25q_ranges[i].sec;
David Hendricksf7924d12010-06-10 21:26:44 -07001139
1140 range_found = 1;
1141 break;
1142 }
1143 }
1144
1145 if (!range_found) {
1146 msg_cerr("matching range not found\n");
1147 return -1;
1148 }
David Hendricksd494b0a2010-08-16 16:28:50 -07001149 return 0;
1150}
1151
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001152int w25_status_to_range(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001153 const struct w25q_status *status,
1154 unsigned int *start, unsigned int *len)
1155{
1156 struct w25q_range *w25q_ranges;
1157 int i, status_found = 0;
1158 int num_entries;
1159
1160 if (w25_range_table(flash, &w25q_ranges, &num_entries)) return -1;
1161 for (i = 0; i < num_entries; i++) {
1162 int bp;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001163 int table_bp, table_tb, table_sec;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001164
1165 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
1166 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
1167 bp, w25q_ranges[i].bp,
1168 status->tb, w25q_ranges[i].tb,
1169 status->sec, w25q_ranges[i].sec);
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001170 table_bp = w25q_ranges[i].bp;
1171 table_tb = w25q_ranges[i].tb;
1172 table_sec = w25q_ranges[i].sec;
1173 if ((bp == table_bp || table_bp == X) &&
1174 (status->tb == table_tb || table_tb == X) &&
1175 (status->sec == table_sec || table_sec == X)) {
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001176 *start = w25q_ranges[i].range.start;
1177 *len = w25q_ranges[i].range.len;
1178
1179 status_found = 1;
1180 break;
1181 }
1182 }
1183
1184 if (!status_found) {
1185 msg_cerr("matching status not found\n");
1186 return -1;
1187 }
1188 return 0;
1189}
1190
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001191/* Given a [start, len], this function calls w25_range_to_status() to convert
1192 * it to flash-chip-specific range bits, then sets into status register.
1193 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001194static int w25_set_range(const struct flashctx *flash,
David Hendricksd494b0a2010-08-16 16:28:50 -07001195 unsigned int start, unsigned int len)
1196{
1197 struct w25q_status status;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001198 int tmp = 0;
1199 int expected = 0;
David Hendricksd494b0a2010-08-16 16:28:50 -07001200
1201 memset(&status, 0, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301202 tmp = do_read_status(flash);
David Hendricksd494b0a2010-08-16 16:28:50 -07001203 memcpy(&status, &tmp, 1);
1204 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1205
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001206 if (w25_range_to_status(flash, start, len, &status)) return -1;
David Hendricksf7924d12010-06-10 21:26:44 -07001207
1208 msg_cdbg("status.busy: %x\n", status.busy);
1209 msg_cdbg("status.wel: %x\n", status.wel);
1210 msg_cdbg("status.bp0: %x\n", status.bp0);
1211 msg_cdbg("status.bp1: %x\n", status.bp1);
1212 msg_cdbg("status.bp2: %x\n", status.bp2);
1213 msg_cdbg("status.tb: %x\n", status.tb);
1214 msg_cdbg("status.sec: %x\n", status.sec);
1215 msg_cdbg("status.srp0: %x\n", status.srp0);
1216
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001217 memcpy(&expected, &status, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301218 do_write_status(flash, expected);
David Hendricksf7924d12010-06-10 21:26:44 -07001219
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301220 tmp = do_read_status(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001221 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
1222 if ((tmp & MASK_WP_AREA) == (expected & MASK_WP_AREA)) {
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001223 return 0;
1224 } else {
David Hendricksc801adb2010-12-09 16:58:56 -08001225 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001226 expected, tmp);
1227 return 1;
1228 }
David Hendricksf7924d12010-06-10 21:26:44 -07001229}
1230
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001231/* Print out the current status register value with human-readable text. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001232static int w25_wp_status(const struct flashctx *flash)
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001233{
1234 struct w25q_status status;
1235 int tmp;
David Hendricksce8ded32010-10-08 11:23:38 -07001236 unsigned int start, len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001237 int ret = 0;
1238
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001239 memset(&status, 0, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301240 tmp = do_read_status(flash);
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001241 memcpy(&status, &tmp, 1);
1242 msg_cinfo("WP: status: 0x%02x\n", tmp);
1243 msg_cinfo("WP: status.srp0: %x\n", status.srp0);
1244 msg_cinfo("WP: write protect is %s.\n",
1245 status.srp0 ? "enabled" : "disabled");
1246
1247 msg_cinfo("WP: write protect range: ");
1248 if (w25_status_to_range(flash, &status, &start, &len)) {
1249 msg_cinfo("(cannot resolve the range)\n");
1250 ret = -1;
1251 } else {
1252 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1253 }
1254
1255 return ret;
1256}
1257
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001258static int w25q_large_range_to_status(const struct flashctx *flash,
1259 unsigned int start, unsigned int len,
1260 struct w25q_status_large *status)
1261{
1262 struct w25q_range *w25q_ranges;
1263 int i, range_found = 0;
1264 int num_entries;
1265
1266 if (w25_range_table(flash, &w25q_ranges, &num_entries))
1267 return -1;
1268 for (i = 0; i < num_entries; i++) {
1269 struct wp_range *r = &w25q_ranges[i].range;
1270
1271 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1272 start, len, r->start, r->len);
1273 if ((start == r->start) && (len == r->len)) {
1274 status->bp0 = w25q_ranges[i].bp & 1;
1275 status->bp1 = w25q_ranges[i].bp >> 1;
1276 status->bp2 = w25q_ranges[i].bp >> 2;
1277 status->bp3 = w25q_ranges[i].bp >> 3;
1278 status->tb = w25q_ranges[i].tb;
1279
1280 range_found = 1;
1281 break;
1282 }
1283 }
1284
1285 if (!range_found) {
1286 msg_cerr("matching range not found\n");
1287 return -1;
1288 }
1289 return 0;
1290}
1291
1292static int w25_large_status_to_range(const struct flashctx *flash,
1293 const struct w25q_status_large *status,
1294 unsigned int *start, unsigned int *len)
1295{
1296 struct w25q_range *w25q_ranges;
1297 int i, status_found = 0;
1298 int num_entries;
1299
1300 if (w25_range_table(flash, &w25q_ranges, &num_entries))
1301 return -1;
1302 for (i = 0; i < num_entries; i++) {
1303 int bp;
1304 int table_bp, table_tb;
1305
1306 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) |
1307 (status->bp3 << 3);
1308 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n",
1309 bp, w25q_ranges[i].bp,
1310 status->tb, w25q_ranges[i].tb);
1311 table_bp = w25q_ranges[i].bp;
1312 table_tb = w25q_ranges[i].tb;
1313 if ((bp == table_bp || table_bp == X) &&
1314 (status->tb == table_tb || table_tb == X)) {
1315 *start = w25q_ranges[i].range.start;
1316 *len = w25q_ranges[i].range.len;
1317
1318 status_found = 1;
1319 break;
1320 }
1321 }
1322
1323 if (!status_found) {
1324 msg_cerr("matching status not found\n");
1325 return -1;
1326 }
1327 return 0;
1328}
1329
1330/* Given a [start, len], this function calls w25_range_to_status() to convert
1331 * it to flash-chip-specific range bits, then sets into status register.
1332 * Returns 0 if successful, -1 on error, and 1 if reading back was different.
1333 */
1334static int w25q_large_set_range(const struct flashctx *flash,
1335 unsigned int start, unsigned int len)
1336{
1337 struct w25q_status_large status;
1338 int tmp;
1339 int expected = 0;
1340
1341 memset(&status, 0, sizeof(status));
1342 tmp = do_read_status(flash);
1343 memcpy(&status, &tmp, 1);
1344 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1345
1346 if (w25q_large_range_to_status(flash, start, len, &status))
1347 return -1;
1348
1349 msg_cdbg("status.busy: %x\n", status.busy);
1350 msg_cdbg("status.wel: %x\n", status.wel);
1351 msg_cdbg("status.bp0: %x\n", status.bp0);
1352 msg_cdbg("status.bp1: %x\n", status.bp1);
1353 msg_cdbg("status.bp2: %x\n", status.bp2);
1354 msg_cdbg("status.bp3: %x\n", status.bp3);
1355 msg_cdbg("status.tb: %x\n", status.tb);
1356 msg_cdbg("status.srp0: %x\n", status.srp0);
1357
1358 memcpy(&expected, &status, sizeof(status));
1359 do_write_status(flash, expected);
1360
1361 tmp = do_read_status(flash);
1362 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
1363 if ((tmp & MASK_WP_AREA_LARGE) == (expected & MASK_WP_AREA_LARGE)) {
1364 return 0;
1365 } else {
1366 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
1367 expected, tmp);
1368 return 1;
1369 }
1370}
1371
1372static int w25q_large_wp_status(const struct flashctx *flash)
1373{
1374 struct w25q_status_large sr1;
1375 struct w25q_status_2 sr2;
1376 uint8_t tmp[2];
1377 unsigned int start, len;
1378 int ret = 0;
1379
1380 memset(&sr1, 0, sizeof(sr1));
1381 tmp[0] = do_read_status(flash);
1382 memcpy(&sr1, &tmp[0], 1);
1383
1384 memset(&sr2, 0, sizeof(sr2));
1385 tmp[1] = w25q_read_status_register_2(flash);
1386 memcpy(&sr2, &tmp[1], 1);
1387
1388 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
1389 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1390 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1391 msg_cinfo("WP: write protect is %s.\n",
1392 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1393
1394 msg_cinfo("WP: write protect range: ");
1395 if (w25_large_status_to_range(flash, &sr1, &start, &len)) {
1396 msg_cinfo("(cannot resolve the range)\n");
1397 ret = -1;
1398 } else {
1399 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1400 }
1401
1402 return ret;
1403}
1404
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001405/* Set/clear the SRP0 bit in the status register. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001406static int w25_set_srp0(const struct flashctx *flash, int enable)
David Hendricksf7924d12010-06-10 21:26:44 -07001407{
1408 struct w25q_status status;
1409 int tmp = 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001410 int expected = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001411
1412 memset(&status, 0, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301413 tmp = do_read_status(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001414 /* FIXME: this is NOT endian-free copy. */
David Hendricksf7924d12010-06-10 21:26:44 -07001415 memcpy(&status, &tmp, 1);
1416 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1417
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001418 status.srp0 = enable ? 1 : 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001419 memcpy(&expected, &status, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301420 do_write_status(flash, expected);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001421
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301422 tmp = do_read_status(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001423 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
1424 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
1425 return 1;
David Hendricksf7924d12010-06-10 21:26:44 -07001426
1427 return 0;
1428}
1429
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001430static int w25_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001431 enum wp_mode wp_mode)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001432{
1433 int ret;
1434
David Hendricks1c09f802012-10-03 11:03:48 -07001435 switch (wp_mode) {
1436 case WP_MODE_HARDWARE:
1437 ret = w25_set_srp0(flash, 1);
1438 break;
1439 default:
1440 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
1441 return 1;
1442 }
1443
David Hendricksc801adb2010-12-09 16:58:56 -08001444 if (ret)
1445 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001446 return ret;
1447}
1448
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001449static int w25_disable_writeprotect(const struct flashctx *flash)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001450{
1451 int ret;
1452
1453 ret = w25_set_srp0(flash, 0);
David Hendricksc801adb2010-12-09 16:58:56 -08001454 if (ret)
1455 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001456 return ret;
1457}
1458
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001459static int w25_list_ranges(const struct flashctx *flash)
David Hendricks0f7f5382011-02-11 18:12:31 -08001460{
1461 struct w25q_range *w25q_ranges;
1462 int i, num_entries;
1463
1464 if (w25_range_table(flash, &w25q_ranges, &num_entries)) return -1;
1465 for (i = 0; i < num_entries; i++) {
1466 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
1467 w25q_ranges[i].range.start,
1468 w25q_ranges[i].range.len);
1469 }
1470
1471 return 0;
1472}
1473
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001474static int w25q_wp_status(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001475{
1476 struct w25q_status sr1;
1477 struct w25q_status_2 sr2;
David Hendricksf1bd8802012-10-30 11:37:57 -07001478 uint8_t tmp[2];
David Hendricks1c09f802012-10-03 11:03:48 -07001479 unsigned int start, len;
1480 int ret = 0;
1481
1482 memset(&sr1, 0, sizeof(sr1));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301483 tmp[0] = do_read_status(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001484 memcpy(&sr1, &tmp[0], 1);
David Hendricks1c09f802012-10-03 11:03:48 -07001485
David Hendricksf1bd8802012-10-30 11:37:57 -07001486 memset(&sr2, 0, sizeof(sr2));
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001487 tmp[1] = w25q_read_status_register_2(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001488 memcpy(&sr2, &tmp[1], 1);
1489
1490 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
David Hendricks1c09f802012-10-03 11:03:48 -07001491 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1492 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1493 msg_cinfo("WP: write protect is %s.\n",
1494 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1495
1496 msg_cinfo("WP: write protect range: ");
1497 if (w25_status_to_range(flash, &sr1, &start, &len)) {
1498 msg_cinfo("(cannot resolve the range)\n");
1499 ret = -1;
1500 } else {
1501 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1502 }
1503
1504 return ret;
1505}
1506
1507/*
1508 * W25Q adds an optional byte to the standard WRSR opcode. If /CS is
1509 * de-asserted after the first byte, then it acts like a JEDEC-standard
1510 * WRSR command. if /CS is asserted, then the next data byte is written
1511 * into status register 2.
1512 */
1513#define W25Q_WRSR_OUTSIZE 0x03
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001514static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2)
David Hendricks1c09f802012-10-03 11:03:48 -07001515{
1516 int result;
1517 struct spi_command cmds[] = {
1518 {
1519 /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
1520 .writecnt = JEDEC_WREN_OUTSIZE,
1521 .writearr = (const unsigned char[]){ JEDEC_WREN },
1522 .readcnt = 0,
1523 .readarr = NULL,
1524 }, {
1525 .writecnt = W25Q_WRSR_OUTSIZE,
1526 .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 },
1527 .readcnt = 0,
1528 .readarr = NULL,
1529 }, {
1530 .writecnt = 0,
1531 .writearr = NULL,
1532 .readcnt = 0,
1533 .readarr = NULL,
1534 }};
1535
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001536 result = spi_send_multicommand(flash, cmds);
David Hendricks1c09f802012-10-03 11:03:48 -07001537 if (result) {
1538 msg_cerr("%s failed during command execution\n",
1539 __func__);
1540 }
1541
1542 /* WRSR performs a self-timed erase before the changes take effect. */
David Hendricks60824042014-12-11 17:22:06 -08001543 programmer_delay(100 * 1000);
David Hendricks1c09f802012-10-03 11:03:48 -07001544
1545 return result;
1546}
1547
1548/*
1549 * Set/clear the SRP1 bit in status register 2.
1550 * FIXME: make this more generic if other chips use the same SR2 layout
1551 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001552static int w25q_set_srp1(const struct flashctx *flash, int enable)
David Hendricks1c09f802012-10-03 11:03:48 -07001553{
1554 struct w25q_status sr1;
1555 struct w25q_status_2 sr2;
1556 uint8_t tmp, expected;
1557
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301558 tmp = do_read_status(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001559 memcpy(&sr1, &tmp, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001560 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001561 memcpy(&sr2, &tmp, 1);
1562
1563 msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp);
1564
1565 sr2.srp1 = enable ? 1 : 0;
1566
1567 memcpy(&expected, &sr2, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001568 w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2));
David Hendricks1c09f802012-10-03 11:03:48 -07001569
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001570 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001571 msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp);
1572 if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA))
1573 return 1;
1574
1575 return 0;
1576}
1577
1578enum wp_mode get_wp_mode(const char *mode_str)
1579{
1580 enum wp_mode wp_mode = WP_MODE_UNKNOWN;
1581
1582 if (!strcasecmp(mode_str, "hardware"))
1583 wp_mode = WP_MODE_HARDWARE;
1584 else if (!strcasecmp(mode_str, "power_cycle"))
1585 wp_mode = WP_MODE_POWER_CYCLE;
1586 else if (!strcasecmp(mode_str, "permanent"))
1587 wp_mode = WP_MODE_PERMANENT;
1588
1589 return wp_mode;
1590}
1591
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001592static int w25q_disable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001593 enum wp_mode wp_mode)
1594{
1595 int ret = 1;
David Hendricks1c09f802012-10-03 11:03:48 -07001596 struct w25q_status_2 sr2;
1597 uint8_t tmp;
1598
1599 switch (wp_mode) {
1600 case WP_MODE_HARDWARE:
1601 ret = w25_set_srp0(flash, 0);
1602 break;
1603 case WP_MODE_POWER_CYCLE:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001604 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001605 memcpy(&sr2, &tmp, 1);
1606 if (sr2.srp1) {
1607 msg_cerr("%s(): must disconnect power to disable "
1608 "write-protection\n", __func__);
1609 } else {
1610 ret = 0;
1611 }
1612 break;
1613 case WP_MODE_PERMANENT:
1614 msg_cerr("%s(): cannot disable permanent write-protection\n",
1615 __func__);
1616 break;
1617 default:
1618 msg_cerr("%s(): invalid mode specified\n", __func__);
1619 break;
1620 }
1621
1622 if (ret)
1623 msg_cerr("%s(): error=%d.\n", __func__, ret);
1624 return ret;
1625}
1626
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001627static int w25q_disable_writeprotect_default(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001628{
1629 return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE);
1630}
1631
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001632static int w25q_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001633 enum wp_mode wp_mode)
1634{
1635 int ret = 1;
1636 struct w25q_status sr1;
1637 struct w25q_status_2 sr2;
1638 uint8_t tmp;
1639
1640 switch (wp_mode) {
1641 case WP_MODE_HARDWARE:
1642 if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) {
1643 msg_cerr("%s(): cannot disable power cycle WP mode\n",
1644 __func__);
1645 break;
1646 }
1647
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301648 tmp = do_read_status(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001649 memcpy(&sr1, &tmp, 1);
1650 if (sr1.srp0)
1651 ret = 0;
1652 else
1653 ret = w25_set_srp0(flash, 1);
1654
1655 break;
1656 case WP_MODE_POWER_CYCLE:
1657 if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) {
1658 msg_cerr("%s(): cannot disable hardware WP mode\n",
1659 __func__);
1660 break;
1661 }
1662
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001663 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001664 memcpy(&sr2, &tmp, 1);
1665 if (sr2.srp1)
1666 ret = 0;
1667 else
1668 ret = w25q_set_srp1(flash, 1);
1669
1670 break;
1671 case WP_MODE_PERMANENT:
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301672 tmp = do_read_status(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001673 memcpy(&sr1, &tmp, 1);
1674 if (sr1.srp0 == 0) {
1675 ret = w25_set_srp0(flash, 1);
1676 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001677 msg_perr("%s(): cannot enable SRP0 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001678 "permanent WP\n", __func__);
1679 break;
1680 }
1681 }
1682
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001683 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001684 memcpy(&sr2, &tmp, 1);
1685 if (sr2.srp1 == 0) {
1686 ret = w25q_set_srp1(flash, 1);
1687 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001688 msg_perr("%s(): cannot enable SRP1 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001689 "permanent WP\n", __func__);
1690 break;
1691 }
1692 }
1693
1694 break;
David Hendricksf1bd8802012-10-30 11:37:57 -07001695 default:
1696 msg_perr("%s(): invalid mode %d\n", __func__, wp_mode);
1697 break;
David Hendricks1c09f802012-10-03 11:03:48 -07001698 }
1699
1700 if (ret)
1701 msg_cerr("%s(): error=%d.\n", __func__, ret);
1702 return ret;
1703}
1704
David Hendricksc3496092014-11-13 17:20:55 -08001705/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001706uint8_t mx25l_read_config_register(const struct flashctx *flash)
David Hendricksc3496092014-11-13 17:20:55 -08001707{
1708 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x15 };
1709 unsigned char readarr[2]; /* leave room for dummy byte */
1710 int ret;
1711
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001712 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
David Hendricksc3496092014-11-13 17:20:55 -08001713 if (ret) {
Duncan Laurie870d8af2019-01-09 18:05:23 -08001714 msg_cdbg("RDCR failed!\n");
David Hendricksc3496092014-11-13 17:20:55 -08001715 readarr[0] = 0x00;
1716 }
1717
1718 return readarr[0];
1719}
David Hendricks1c09f802012-10-03 11:03:48 -07001720/* W25P, W25X, and many flash chips from various vendors */
David Hendricksf7924d12010-06-10 21:26:44 -07001721struct wp wp_w25 = {
David Hendricks0f7f5382011-02-11 18:12:31 -08001722 .list_ranges = w25_list_ranges,
David Hendricksf7924d12010-06-10 21:26:44 -07001723 .set_range = w25_set_range,
1724 .enable = w25_enable_writeprotect,
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001725 .disable = w25_disable_writeprotect,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001726 .wp_status = w25_wp_status,
David Hendricks1c09f802012-10-03 11:03:48 -07001727
1728};
1729
1730/* W25Q series has features such as a second status register and SFDP */
1731struct wp wp_w25q = {
1732 .list_ranges = w25_list_ranges,
1733 .set_range = w25_set_range,
1734 .enable = w25q_enable_writeprotect,
1735 /*
1736 * By default, disable hardware write-protection. We may change
1737 * this later if we want to add fine-grained write-protect disable
1738 * as a command-line option.
1739 */
1740 .disable = w25q_disable_writeprotect_default,
1741 .wp_status = w25q_wp_status,
David Hendricksf7924d12010-06-10 21:26:44 -07001742};
David Hendrickse0512a72014-07-15 20:30:47 -07001743
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001744/* W25Q large series has 4 block-protect bits */
1745struct wp wp_w25q_large = {
1746 .list_ranges = w25_list_ranges,
1747 .set_range = w25q_large_set_range,
1748 .enable = w25q_enable_writeprotect,
1749 /*
1750 * By default, disable hardware write-protection. We may change
1751 * this later if we want to add fine-grained write-protect disable
1752 * as a command-line option.
1753 */
1754 .disable = w25q_disable_writeprotect_default,
1755 .wp_status = w25q_large_wp_status,
1756};
1757
David Hendricksaf3944a2014-07-28 18:37:40 -07001758struct generic_range gd25q32_cmp0_ranges[] = {
1759 /* none, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001760 { { }, 0x00, {0, 0} },
1761 { { }, 0x08, {0, 0} },
1762 { { }, 0x10, {0, 0} },
1763 { { }, 0x18, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001764
David Hendricks148a4bf2015-03-13 21:02:42 -07001765 { { }, 0x01, {0x3f0000, 64 * 1024} },
1766 { { }, 0x02, {0x3e0000, 128 * 1024} },
1767 { { }, 0x03, {0x3c0000, 256 * 1024} },
1768 { { }, 0x04, {0x380000, 512 * 1024} },
1769 { { }, 0x05, {0x300000, 1024 * 1024} },
1770 { { }, 0x06, {0x200000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001771
David Hendricks148a4bf2015-03-13 21:02:42 -07001772 { { }, 0x09, {0x000000, 64 * 1024} },
1773 { { }, 0x0a, {0x000000, 128 * 1024} },
1774 { { }, 0x0b, {0x000000, 256 * 1024} },
1775 { { }, 0x0c, {0x000000, 512 * 1024} },
1776 { { }, 0x0d, {0x000000, 1024 * 1024} },
1777 { { }, 0x0e, {0x000000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001778
1779 /* all, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001780 { { }, 0x07, {0x000000, 4096 * 1024} },
1781 { { }, 0x0f, {0x000000, 4096 * 1024} },
1782 { { }, 0x17, {0x000000, 4096 * 1024} },
1783 { { }, 0x1f, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001784
David Hendricks148a4bf2015-03-13 21:02:42 -07001785 { { }, 0x11, {0x3ff000, 4 * 1024} },
1786 { { }, 0x12, {0x3fe000, 8 * 1024} },
1787 { { }, 0x13, {0x3fc000, 16 * 1024} },
1788 { { }, 0x14, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1789 { { }, 0x15, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1790 { { }, 0x16, {0x3f8000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001791
David Hendricks148a4bf2015-03-13 21:02:42 -07001792 { { }, 0x19, {0x000000, 4 * 1024} },
1793 { { }, 0x1a, {0x000000, 8 * 1024} },
1794 { { }, 0x1b, {0x000000, 16 * 1024} },
1795 { { }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1796 { { }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1797 { { }, 0x1e, {0x000000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001798};
1799
1800struct generic_range gd25q32_cmp1_ranges[] = {
Martin Roth563a1fe2017-04-18 14:26:27 -06001801 /* All, bp4 and bp3 => don't care */
1802 { { }, 0x00, {0x000000, 4096 * 1024} }, /* All */
1803 { { }, 0x08, {0x000000, 4096 * 1024} },
1804 { { }, 0x10, {0x000000, 4096 * 1024} },
1805 { { }, 0x18, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001806
David Hendricks148a4bf2015-03-13 21:02:42 -07001807 { { }, 0x01, {0x000000, 4032 * 1024} },
1808 { { }, 0x02, {0x000000, 3968 * 1024} },
1809 { { }, 0x03, {0x000000, 3840 * 1024} },
1810 { { }, 0x04, {0x000000, 3584 * 1024} },
1811 { { }, 0x05, {0x000000, 3 * 1024 * 1024} },
1812 { { }, 0x06, {0x000000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001813
David Hendricks148a4bf2015-03-13 21:02:42 -07001814 { { }, 0x09, {0x010000, 4032 * 1024} },
1815 { { }, 0x0a, {0x020000, 3968 * 1024} },
1816 { { }, 0x0b, {0x040000, 3840 * 1024} },
1817 { { }, 0x0c, {0x080000, 3584 * 1024} },
1818 { { }, 0x0d, {0x100000, 3 * 1024 * 1024} },
1819 { { }, 0x0e, {0x200000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001820
Martin Roth563a1fe2017-04-18 14:26:27 -06001821 /* None, bp4 and bp3 => don't care */
1822 { { }, 0x07, {0, 0} }, /* None */
1823 { { }, 0x0f, {0, 0} },
1824 { { }, 0x17, {0, 0} },
1825 { { }, 0x1f, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001826
David Hendricks148a4bf2015-03-13 21:02:42 -07001827 { { }, 0x11, {0x000000, 4092 * 1024} },
1828 { { }, 0x12, {0x000000, 4088 * 1024} },
1829 { { }, 0x13, {0x000000, 4080 * 1024} },
1830 { { }, 0x14, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
1831 { { }, 0x15, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
1832 { { }, 0x16, {0x000000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001833
David Hendricks148a4bf2015-03-13 21:02:42 -07001834 { { }, 0x19, {0x001000, 4092 * 1024} },
1835 { { }, 0x1a, {0x002000, 4088 * 1024} },
1836 { { }, 0x1b, {0x040000, 4080 * 1024} },
1837 { { }, 0x1c, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
1838 { { }, 0x1d, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
1839 { { }, 0x1e, {0x080000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001840};
1841
1842static struct generic_wp gd25q32_wp = {
1843 /* TODO: map second status register */
1844 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
1845};
1846
David Hendricks1e9d7ca2016-03-14 15:50:34 -07001847struct generic_range gd25q128_cmp0_ranges[] = {
1848 /* none, bp4 and bp3 => don't care, others = 0 */
1849 { { .tb = 0 }, 0x00, {0, 0} },
1850 { { .tb = 0 }, 0x08, {0, 0} },
1851 { { .tb = 0 }, 0x10, {0, 0} },
1852 { { .tb = 0 }, 0x18, {0, 0} },
1853
1854 { { .tb = 0 }, 0x01, {0xfc0000, 256 * 1024} },
1855 { { .tb = 0 }, 0x02, {0xf80000, 512 * 1024} },
1856 { { .tb = 0 }, 0x03, {0xf00000, 1024 * 1024} },
1857 { { .tb = 0 }, 0x04, {0xe00000, 2048 * 1024} },
1858 { { .tb = 0 }, 0x05, {0xc00000, 4096 * 1024} },
1859 { { .tb = 0 }, 0x06, {0x800000, 8192 * 1024} },
1860
1861 { { .tb = 0 }, 0x09, {0x000000, 256 * 1024} },
1862 { { .tb = 0 }, 0x0a, {0x000000, 512 * 1024} },
1863 { { .tb = 0 }, 0x0b, {0x000000, 1024 * 1024} },
1864 { { .tb = 0 }, 0x0c, {0x000000, 2048 * 1024} },
1865 { { .tb = 0 }, 0x0d, {0x000000, 4096 * 1024} },
1866 { { .tb = 0 }, 0x0e, {0x000000, 8192 * 1024} },
1867
1868 /* all, bp4 and bp3 => don't care, others = 1 */
1869 { { .tb = 0 }, 0x07, {0x000000, 16384 * 1024} },
1870 { { .tb = 0 }, 0x0f, {0x000000, 16384 * 1024} },
1871 { { .tb = 0 }, 0x17, {0x000000, 16384 * 1024} },
1872 { { .tb = 0 }, 0x1f, {0x000000, 16384 * 1024} },
1873
1874 { { .tb = 0 }, 0x11, {0xfff000, 4 * 1024} },
1875 { { .tb = 0 }, 0x12, {0xffe000, 8 * 1024} },
1876 { { .tb = 0 }, 0x13, {0xffc000, 16 * 1024} },
1877 { { .tb = 0 }, 0x14, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
1878 { { .tb = 0 }, 0x15, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
1879
1880 { { .tb = 0 }, 0x19, {0x000000, 4 * 1024} },
1881 { { .tb = 0 }, 0x1a, {0x000000, 8 * 1024} },
1882 { { .tb = 0 }, 0x1b, {0x000000, 16 * 1024} },
1883 { { .tb = 0 }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1884 { { .tb = 0 }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1885 { { .tb = 0 }, 0x1e, {0x000000, 32 * 1024} },
1886};
1887
1888struct generic_range gd25q128_cmp1_ranges[] = {
1889 /* none, bp4 and bp3 => don't care, others = 0 */
1890 { { .tb = 1 }, 0x00, {0x000000, 16384 * 1024} },
1891 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
1892 { { .tb = 1 }, 0x10, {0x000000, 16384 * 1024} },
1893 { { .tb = 1 }, 0x18, {0x000000, 16384 * 1024} },
1894
1895 { { .tb = 1 }, 0x01, {0x000000, 16128 * 1024} },
1896 { { .tb = 1 }, 0x02, {0x000000, 15872 * 1024} },
1897 { { .tb = 1 }, 0x03, {0x000000, 15360 * 1024} },
1898 { { .tb = 1 }, 0x04, {0x000000, 14336 * 1024} },
1899 { { .tb = 1 }, 0x05, {0x000000, 12288 * 1024} },
1900 { { .tb = 1 }, 0x06, {0x000000, 8192 * 1024} },
1901
1902 { { .tb = 1 }, 0x09, {0x000000, 16128 * 1024} },
1903 { { .tb = 1 }, 0x0a, {0x000000, 15872 * 1024} },
1904 { { .tb = 1 }, 0x0b, {0x000000, 15360 * 1024} },
1905 { { .tb = 1 }, 0x0c, {0x000000, 14336 * 1024} },
1906 { { .tb = 1 }, 0x0d, {0x000000, 12288 * 1024} },
1907 { { .tb = 1 }, 0x0e, {0x000000, 8192 * 1024} },
1908
1909 /* none, bp4 and bp3 => don't care, others = 1 */
1910 { { .tb = 1 }, 0x07, {0x000000, 16384 * 1024} },
1911 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
1912 { { .tb = 1 }, 0x0f, {0x000000, 16384 * 1024} },
1913 { { .tb = 1 }, 0x17, {0x000000, 16384 * 1024} },
1914 { { .tb = 1 }, 0x1f, {0x000000, 16384 * 1024} },
1915
1916 { { .tb = 1 }, 0x11, {0x000000, 16380 * 1024} },
1917 { { .tb = 1 }, 0x12, {0x000000, 16376 * 1024} },
1918 { { .tb = 1 }, 0x13, {0x000000, 16368 * 1024} },
1919 { { .tb = 1 }, 0x14, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
1920 { { .tb = 1 }, 0x15, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
1921
1922 { { .tb = 1 }, 0x19, {0x001000, 16380 * 1024} },
1923 { { .tb = 1 }, 0x1a, {0x002000, 16376 * 1024} },
1924 { { .tb = 1 }, 0x1b, {0x004000, 16368 * 1024} },
1925 { { .tb = 1 }, 0x1c, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
1926 { { .tb = 1 }, 0x1d, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
1927 { { .tb = 1 }, 0x1e, {0x008000, 16352 * 1024} },
1928};
1929
1930static struct generic_wp gd25q128_wp = {
1931 /* TODO: map second and third status registers */
1932 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
1933};
1934
David Hendricks83541d32014-07-15 20:58:21 -07001935#if 0
1936/* FIXME: MX25L6405D has same ID as MX25L6406 */
1937static struct w25q_range mx25l6405d_ranges[] = {
1938 { X, 0, 0, {0, 0} }, /* none */
1939 { X, 0, 0x1, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
1940 { X, 0, 0x2, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
1941 { X, 0, 0x3, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
1942 { X, 0, 0x4, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
1943 { X, 0, 0x5, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
1944 { X, 0, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
1945 { X, 0, 0x7, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
1946
1947 { X, 1, 0x0, {0x000000, 8192 * 1024} },
1948 { X, 1, 0x1, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
1949 { X, 1, 0x2, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
1950 { X, 1, 0x3, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
1951 { X, 1, 0x4, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
1952 { X, 1, 0x5, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
1953 { X, 1, 0x6, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
1954 { X, 1, 0x7, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
1955};
1956#endif
1957
1958/* FIXME: MX25L6406 has same ID as MX25L6405D */
1959struct generic_range mx25l6406e_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07001960 { { }, 0, {0, 0} }, /* none */
1961 { { }, 0x1, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
1962 { { }, 0x2, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
1963 { { }, 0x3, {0x7a0000, 64 * 8 * 1024} }, /* blocks 120-127 */
1964 { { }, 0x4, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
1965 { { }, 0x5, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
1966 { { }, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricks83541d32014-07-15 20:58:21 -07001967
David Hendricks148a4bf2015-03-13 21:02:42 -07001968 { { }, 0x7, {0x000000, 64 * 128 * 1024} }, /* all */
1969 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
1970 { { }, 0x9, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
1971 { { }, 0xa, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
1972 { { }, 0xb, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
1973 { { }, 0xc, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
1974 { { }, 0xd, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
1975 { { }, 0xe, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
1976 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricks83541d32014-07-15 20:58:21 -07001977};
1978
1979static struct generic_wp mx25l6406e_wp = {
1980 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
1981 .ranges = &mx25l6406e_ranges[0],
1982};
David Hendrickse0512a72014-07-15 20:30:47 -07001983
David Hendricksc3496092014-11-13 17:20:55 -08001984struct generic_range mx25l6495f_tb0_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07001985 { { }, 0, {0, 0} }, /* none */
1986 { { }, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
1987 { { }, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
1988 { { }, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
David Hendricksc3496092014-11-13 17:20:55 -08001989
David Hendricks148a4bf2015-03-13 21:02:42 -07001990 { { }, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
1991 { { }, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
1992 { { }, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
1993 { { }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
1994 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
1995 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
1996 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
1997 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
1998 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
1999 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2000 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2001 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002002};
2003
2004struct generic_range mx25l6495f_tb1_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002005 { { }, 0, {0, 0} }, /* none */
2006 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2007 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2008 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2009 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2010 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2011 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2012 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2013 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2014 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2015 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2016 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2017 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2018 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2019 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2020 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002021};
2022
2023static struct generic_wp mx25l6495f_wp = {
2024 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2025};
2026
Vic Yang848bfd12018-03-23 10:24:07 -07002027struct generic_range mx25l25635f_tb0_ranges[] = {
2028 { { }, 0, {0, 0} }, /* none */
2029 { { }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* block 511 */
2030 { { }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* blocks 510-511 */
2031 { { }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* blocks 508-511 */
2032 { { }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* blocks 504-511 */
2033 { { }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* blocks 496-511 */
2034 { { }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* blocks 480-511 */
2035 { { }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* blocks 448-511 */
2036 { { }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* blocks 384-511 */
2037 { { }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* blocks 256-511 */
2038 { { }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* all */
2039 { { }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* all */
2040 { { }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* all */
2041 { { }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* all */
2042 { { }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* all */
2043 { { }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* all */
2044};
2045
2046struct generic_range mx25l25635f_tb1_ranges[] = {
2047 { { }, 0, {0, 0} }, /* none */
2048 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2049 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2050 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2051 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2052 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2053 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2054 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2055 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
2056 { { }, 0x9, {0x000000, 64 * 256 * 1024} }, /* blocks 0-255 */
2057 { { }, 0xa, {0x000000, 64 * 512 * 1024} }, /* all */
2058 { { }, 0xb, {0x000000, 64 * 512 * 1024} }, /* all */
2059 { { }, 0xc, {0x000000, 64 * 512 * 1024} }, /* all */
2060 { { }, 0xd, {0x000000, 64 * 512 * 1024} }, /* all */
2061 { { }, 0xe, {0x000000, 64 * 512 * 1024} }, /* all */
2062 { { }, 0xf, {0x000000, 64 * 512 * 1024} }, /* all */
2063};
2064
2065static struct generic_wp mx25l25635f_wp = {
2066 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2067};
2068
David Hendricks148a4bf2015-03-13 21:02:42 -07002069struct generic_range s25fs128s_ranges[] = {
2070 { { .tb = 1 }, 0, {0, 0} }, /* none */
2071 { { .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* lower 64th */
2072 { { .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* lower 32nd */
2073 { { .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* lower 16th */
2074 { { .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* lower 8th */
2075 { { .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* lower 4th */
2076 { { .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* lower half */
2077 { { .tb = 1 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002078
David Hendricks148a4bf2015-03-13 21:02:42 -07002079 { { .tb = 0 }, 0, {0, 0} }, /* none */
2080 { { .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* upper 64th */
2081 { { .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* upper 32nd */
2082 { { .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* upper 16th */
2083 { { .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* upper 8th */
2084 { { .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* upper 4th */
2085 { { .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* upper half */
2086 { { .tb = 0 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002087};
2088
2089static struct generic_wp s25fs128s_wp = {
2090 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
David Hendricks148a4bf2015-03-13 21:02:42 -07002091 .get_modifier_bits = s25f_get_modifier_bits,
2092 .set_modifier_bits = s25f_set_modifier_bits,
David Hendricksa9884852014-12-11 15:31:12 -08002093};
2094
David Hendricksc694bb82015-02-25 14:52:17 -08002095
David Hendricks148a4bf2015-03-13 21:02:42 -07002096struct generic_range s25fl256s_ranges[] = {
2097 { { .tb = 1 }, 0, {0, 0} }, /* none */
2098 { { .tb = 1 }, 0x1, {0x000000, 512 * 1024} }, /* lower 64th */
2099 { { .tb = 1 }, 0x2, {0x000000, 1024 * 1024} }, /* lower 32nd */
2100 { { .tb = 1 }, 0x3, {0x000000, 2048 * 1024} }, /* lower 16th */
2101 { { .tb = 1 }, 0x4, {0x000000, 4096 * 1024} }, /* lower 8th */
2102 { { .tb = 1 }, 0x5, {0x000000, 8192 * 1024} }, /* lower 4th */
2103 { { .tb = 1 }, 0x6, {0x000000, 16384 * 1024} }, /* lower half */
2104 { { .tb = 1 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
2105
2106 { { .tb = 0 }, 0, {0, 0} }, /* none */
2107 { { .tb = 0 }, 0x1, {0x1f80000, 512 * 1024} }, /* upper 64th */
2108 { { .tb = 0 }, 0x2, {0x1f00000, 1024 * 1024} }, /* upper 32nd */
2109 { { .tb = 0 }, 0x3, {0x1e00000, 2048 * 1024} }, /* upper 16th */
2110 { { .tb = 0 }, 0x4, {0x1c00000, 4096 * 1024} }, /* upper 8th */
2111 { { .tb = 0 }, 0x5, {0x1800000, 8192 * 1024} }, /* upper 4th */
2112 { { .tb = 0 }, 0x6, {0x1000000, 16384 * 1024} }, /* upper half */
2113 { { .tb = 0 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
David Hendricksc694bb82015-02-25 14:52:17 -08002114};
2115
2116static struct generic_wp s25fl256s_wp = {
2117 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
David Hendricks148a4bf2015-03-13 21:02:42 -07002118 .get_modifier_bits = s25f_get_modifier_bits,
2119 .set_modifier_bits = s25f_set_modifier_bits,
David Hendricksc694bb82015-02-25 14:52:17 -08002120};
2121
David Hendrickse0512a72014-07-15 20:30:47 -07002122/* Given a flash chip, this function returns its writeprotect info. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002123static int generic_range_table(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002124 struct generic_wp **wp,
2125 int *num_entries)
2126{
2127 *wp = NULL;
2128 *num_entries = 0;
2129
Patrick Georgif3fa2992017-02-02 16:24:44 +01002130 switch (flash->chip->manufacture_id) {
David Hendricksaf3944a2014-07-28 18:37:40 -07002131 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002132 switch(flash->chip->model_id) {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002133
Martin Roth563a1fe2017-04-18 14:26:27 -06002134 case GIGADEVICE_GD25LQ32:
David Hendricksaf3944a2014-07-28 18:37:40 -07002135 case GIGADEVICE_GD25Q32: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002136 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricksaf3944a2014-07-28 18:37:40 -07002137 *wp = &gd25q32_wp;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002138
David Hendricksaf3944a2014-07-28 18:37:40 -07002139 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
2140 (*wp)->ranges = &gd25q32_cmp0_ranges[0];
2141 *num_entries = ARRAY_SIZE(gd25q32_cmp0_ranges);
2142 } else { /* CMP == 1 */
2143 (*wp)->ranges = &gd25q32_cmp1_ranges[0];
2144 *num_entries = ARRAY_SIZE(gd25q32_cmp1_ranges);
2145 }
2146
2147 break;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002148 }
Furquan Shaikh62cd8102016-07-17 23:04:06 -07002149 case GIGADEVICE_GD25Q128:
Aaron Durbin6c957d72018-08-20 09:31:01 -06002150 case GIGADEVICE_GD25LQ128CD: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002151 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002152 *wp = &gd25q128_wp;
2153
2154 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
2155 (*wp)->ranges = &gd25q128_cmp0_ranges[0];
2156 *num_entries = ARRAY_SIZE(gd25q128_cmp0_ranges);
2157 } else { /* CMP == 1 */
2158 (*wp)->ranges = &gd25q128_cmp1_ranges[0];
2159 *num_entries = ARRAY_SIZE(gd25q128_cmp1_ranges);
2160 }
2161
2162 break;
David Hendricksaf3944a2014-07-28 18:37:40 -07002163 }
2164 default:
2165 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
2166 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002167 flash->chip->model_id);
David Hendricksaf3944a2014-07-28 18:37:40 -07002168 return -1;
2169 }
2170 break;
David Hendricks83541d32014-07-15 20:58:21 -07002171 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002172 switch (flash->chip->model_id) {
David Hendricks83541d32014-07-15 20:58:21 -07002173 case MACRONIX_MX25L6405:
2174 /* FIXME: MX25L64* chips have mixed capabilities and
2175 share IDs */
2176 *wp = &mx25l6406e_wp;
2177 *num_entries = ARRAY_SIZE(mx25l6406e_ranges);
2178 break;
David Hendricksc3496092014-11-13 17:20:55 -08002179 case MACRONIX_MX25L6495F: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002180 uint8_t cr = mx25l_read_config_register(flash);
David Hendricksc3496092014-11-13 17:20:55 -08002181
2182 *wp = &mx25l6495f_wp;
2183 if (!(cr & (1 << 3))) { /* T/B == 0 */
2184 (*wp)->ranges = &mx25l6495f_tb0_ranges[0];
2185 *num_entries = ARRAY_SIZE(mx25l6495f_tb0_ranges);
2186 } else { /* T/B == 1 */
2187 (*wp)->ranges = &mx25l6495f_tb1_ranges[0];
2188 *num_entries = ARRAY_SIZE(mx25l6495f_tb1_ranges);
2189 }
2190 break;
2191 }
Vic Yang848bfd12018-03-23 10:24:07 -07002192 case MACRONIX_MX25L25635F: {
2193 uint8_t cr = mx25l_read_config_register(flash);
2194
2195 *wp = &mx25l25635f_wp;
2196 if (!(cr & (1 << 3))) { /* T/B == 0 */
2197 (*wp)->ranges = &mx25l25635f_tb0_ranges[0];
2198 *num_entries = ARRAY_SIZE(mx25l25635f_tb0_ranges);
2199 } else { /* T/B == 1 */
2200 (*wp)->ranges = &mx25l25635f_tb1_ranges[0];
2201 *num_entries = ARRAY_SIZE(mx25l25635f_tb1_ranges);
2202 }
2203 break;
2204 }
David Hendricks83541d32014-07-15 20:58:21 -07002205 default:
2206 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
2207 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002208 flash->chip->model_id);
David Hendricks83541d32014-07-15 20:58:21 -07002209 return -1;
2210 }
2211 break;
David Hendricksa9884852014-12-11 15:31:12 -08002212 case SPANSION_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002213 switch (flash->chip->model_id) {
David Hendricksa9884852014-12-11 15:31:12 -08002214 case SPANSION_S25FS128S_L:
2215 case SPANSION_S25FS128S_S: {
David Hendricksa9884852014-12-11 15:31:12 -08002216 *wp = &s25fs128s_wp;
David Hendricks148a4bf2015-03-13 21:02:42 -07002217 (*wp)->ranges = s25fs128s_ranges;
2218 *num_entries = ARRAY_SIZE(s25fs128s_ranges);
David Hendricksa9884852014-12-11 15:31:12 -08002219 break;
2220 }
David Hendricksc694bb82015-02-25 14:52:17 -08002221 case SPANSION_S25FL256S_UL:
2222 case SPANSION_S25FL256S_US: {
David Hendricksc694bb82015-02-25 14:52:17 -08002223 *wp = &s25fl256s_wp;
David Hendricks148a4bf2015-03-13 21:02:42 -07002224 (*wp)->ranges = s25fl256s_ranges;
2225 *num_entries = ARRAY_SIZE(s25fl256s_ranges);
David Hendricksc694bb82015-02-25 14:52:17 -08002226 break;
2227 }
David Hendricksa9884852014-12-11 15:31:12 -08002228 default:
2229 msg_cerr("%s():%d Spansion flash chip mismatch (0x%04x)"
Patrick Georgif3fa2992017-02-02 16:24:44 +01002230 ", aborting\n", __func__, __LINE__,
2231 flash->chip->model_id);
David Hendricksa9884852014-12-11 15:31:12 -08002232 return -1;
2233 }
2234 break;
David Hendrickse0512a72014-07-15 20:30:47 -07002235 default:
2236 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01002237 __func__, flash->chip->manufacture_id);
David Hendrickse0512a72014-07-15 20:30:47 -07002238 return -1;
2239 }
2240
2241 return 0;
2242}
2243
2244/* Given a [start, len], this function finds a block protect bit combination
2245 * (if possible) and sets the corresponding bits in "status". Remaining bits
2246 * are preserved. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002247static int generic_range_to_status(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002248 unsigned int start, unsigned int len,
2249 uint8_t *status)
2250{
2251 struct generic_wp *wp;
2252 struct generic_range *r;
2253 int i, range_found = 0, num_entries;
2254 uint8_t bp_mask;
2255
2256 if (generic_range_table(flash, &wp, &num_entries))
2257 return -1;
2258
2259 bp_mask = ((1 << (wp->sr1.bp0_pos + wp->sr1.bp_bits)) - 1) - \
2260 ((1 << wp->sr1.bp0_pos) - 1);
2261
2262 for (i = 0, r = &wp->ranges[0]; i < num_entries; i++, r++) {
2263 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
2264 start, len, r->range.start, r->range.len);
2265 if ((start == r->range.start) && (len == r->range.len)) {
2266 *status &= ~(bp_mask);
2267 *status |= r->bp << (wp->sr1.bp0_pos);
David Hendricks148a4bf2015-03-13 21:02:42 -07002268
2269 if (wp->set_modifier_bits) {
2270 if (wp->set_modifier_bits(flash, &r->m) < 0) {
2271 msg_cerr("error setting modifier "
2272 "bits for range.\n");
2273 return -1;
2274 }
2275 }
2276
David Hendrickse0512a72014-07-15 20:30:47 -07002277 range_found = 1;
2278 break;
2279 }
2280 }
2281
2282 if (!range_found) {
2283 msg_cerr("matching range not found\n");
2284 return -1;
2285 }
2286 return 0;
2287}
2288
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002289static int generic_status_to_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002290 const uint8_t sr1, unsigned int *start, unsigned int *len)
2291{
2292 struct generic_wp *wp;
2293 struct generic_range *r;
Duncan Laurie04ca1172015-03-12 09:25:34 -07002294 int num_entries, i, status_found = 0;
David Hendrickse0512a72014-07-15 20:30:47 -07002295 uint8_t sr1_bp;
David Hendricks148a4bf2015-03-13 21:02:42 -07002296 struct generic_modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -07002297
2298 if (generic_range_table(flash, &wp, &num_entries))
2299 return -1;
2300
David Hendricks148a4bf2015-03-13 21:02:42 -07002301 /* modifier bits may be compared more than once, so get them here */
2302 if (wp->get_modifier_bits) {
2303 if (wp->get_modifier_bits(flash, &m) < 0)
2304 return -1;
2305 }
2306
David Hendrickse0512a72014-07-15 20:30:47 -07002307 sr1_bp = (sr1 >> wp->sr1.bp0_pos) & ((1 << wp->sr1.bp_bits) - 1);
2308
2309 for (i = 0, r = &wp->ranges[0]; i < num_entries; i++, r++) {
David Hendricks148a4bf2015-03-13 21:02:42 -07002310 if (wp->get_modifier_bits) {
2311 if (memcmp(&m, &r->m, sizeof(m)))
2312 continue;
2313 }
David Hendrickse0512a72014-07-15 20:30:47 -07002314 msg_cspew("comparing 0x%02x 0x%02x\n", sr1_bp, r->bp);
2315 if (sr1_bp == r->bp) {
2316 *start = r->range.start;
2317 *len = r->range.len;
2318 status_found = 1;
2319 break;
2320 }
2321 }
2322
2323 if (!status_found) {
2324 msg_cerr("matching status not found\n");
2325 return -1;
2326 }
2327 return 0;
2328}
2329
2330/* Given a [start, len], this function calls generic_range_to_status() to
2331 * convert it to flash-chip-specific range bits, then sets into status register.
2332 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002333static int generic_set_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002334 unsigned int start, unsigned int len)
2335{
2336 uint8_t status, expected;
2337
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302338 status = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002339 msg_cdbg("%s: old status: 0x%02x\n", __func__, status);
2340
2341 expected = status; /* preserve non-bp bits */
2342 if (generic_range_to_status(flash, start, len, &expected))
2343 return -1;
2344
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302345 do_write_status(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002346
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302347 status = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002348 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
2349 if (status != expected) {
2350 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
2351 expected, status);
2352 return 1;
2353 }
2354
2355 return 0;
2356}
2357
2358/* Set/clear the status regsiter write protect bit in SR1. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002359static int generic_set_srp0(const struct flashctx *flash, int enable)
David Hendrickse0512a72014-07-15 20:30:47 -07002360{
2361 uint8_t status, expected;
2362 struct generic_wp *wp;
2363 int num_entries;
2364
2365 if (generic_range_table(flash, &wp, &num_entries))
2366 return -1;
2367
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302368 expected = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002369 msg_cdbg("%s: old status: 0x%02x\n", __func__, expected);
2370
2371 if (enable)
2372 expected |= 1 << wp->sr1.srp_pos;
2373 else
2374 expected &= ~(1 << wp->sr1.srp_pos);
2375
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302376 do_write_status(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002377
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302378 status = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002379 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
2380 if (status != expected)
2381 return -1;
2382
2383 return 0;
2384}
2385
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002386static int generic_enable_writeprotect(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002387 enum wp_mode wp_mode)
2388{
2389 int ret;
2390
2391 switch (wp_mode) {
2392 case WP_MODE_HARDWARE:
2393 ret = generic_set_srp0(flash, 1);
2394 break;
2395 default:
2396 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
2397 return 1;
2398 }
2399
2400 if (ret)
2401 msg_cerr("%s(): error=%d.\n", __func__, ret);
2402 return ret;
2403}
2404
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002405static int generic_disable_writeprotect(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002406{
2407 int ret;
2408
2409 ret = generic_set_srp0(flash, 0);
2410 if (ret)
2411 msg_cerr("%s(): error=%d.\n", __func__, ret);
2412 return ret;
2413}
2414
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002415static int generic_list_ranges(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002416{
2417 struct generic_wp *wp;
2418 struct generic_range *r;
2419 int i, num_entries;
2420
2421 if (generic_range_table(flash, &wp, &num_entries))
2422 return -1;
2423
2424 r = &wp->ranges[0];
2425 for (i = 0; i < num_entries; i++) {
2426 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
2427 r->range.start, r->range.len);
2428 r++;
2429 }
2430
2431 return 0;
2432}
2433
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002434static int generic_wp_status(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002435{
2436 uint8_t sr1;
2437 unsigned int start, len;
2438 int ret = 0;
2439 struct generic_wp *wp;
David Hendrickse0512a72014-07-15 20:30:47 -07002440 int num_entries, wp_en;
2441
2442 if (generic_range_table(flash, &wp, &num_entries))
2443 return -1;
2444
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302445 sr1 = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002446 wp_en = (sr1 >> wp->sr1.srp_pos) & 1;
2447
2448 msg_cinfo("WP: status: 0x%04x\n", sr1);
2449 msg_cinfo("WP: status.srp0: %x\n", wp_en);
2450 /* FIXME: SRP1 is not really generic, but we probably should print
2451 * it anyway to have consistent output. #legacycruft */
2452 msg_cinfo("WP: status.srp1: %x\n", 0);
2453 msg_cinfo("WP: write protect is %s.\n",
2454 wp_en ? "enabled" : "disabled");
2455
2456 msg_cinfo("WP: write protect range: ");
2457 if (generic_status_to_range(flash, sr1, &start, &len)) {
2458 msg_cinfo("(cannot resolve the range)\n");
2459 ret = -1;
2460 } else {
2461 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
2462 }
2463
2464 return ret;
2465}
2466
2467struct wp wp_generic = {
2468 .list_ranges = generic_list_ranges,
2469 .set_range = generic_set_range,
2470 .enable = generic_enable_writeprotect,
2471 .disable = generic_disable_writeprotect,
2472 .wp_status = generic_wp_status,
2473};