blob: 357dd122cf87093cb1c8d1bfb281a545ed8c4588 [file] [log] [blame]
David Hendricksd1c55d72010-08-24 15:14:19 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2010 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
David Hendricksd1c55d72010-08-24 15:14:19 -070016 */
17
David Hendricksf7924d12010-06-10 21:26:44 -070018#include <stdlib.h>
19#include <string.h>
Edward O'Callaghanb4300ca2019-09-03 16:15:21 +100020#include <strings.h>
David Hendricksf7924d12010-06-10 21:26:44 -070021
22#include "flash.h"
23#include "flashchips.h"
24#include "chipdrivers.h"
Louis Yung-Chieh Lo52aa9302010-09-06 10:45:02 +080025#include "spi.h"
David Hendricks23cd7782010-08-25 12:42:38 -070026#include "writeprotect.h"
David Hendricksf7924d12010-06-10 21:26:44 -070027
David Hendricks1c09f802012-10-03 11:03:48 -070028/*
David Hendricksf7924d12010-06-10 21:26:44 -070029 * The following procedures rely on look-up tables to match the user-specified
30 * range with the chip's supported ranges. This turned out to be the most
31 * elegant approach since diferent flash chips use different levels of
32 * granularity and methods to determine protected ranges. In other words,
David Hendrickse0512a72014-07-15 20:30:47 -070033 * be stupid and simple since clever arithmetic will not work for many chips.
David Hendricksf7924d12010-06-10 21:26:44 -070034 */
35
36struct wp_range {
37 unsigned int start; /* starting address */
38 unsigned int len; /* len */
39};
40
41enum bit_state {
42 OFF = 0,
43 ON = 1,
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +080044 X = -1 /* don't care. Must be bigger than max # of bp. */
David Hendricksf7924d12010-06-10 21:26:44 -070045};
46
David Hendrickse0512a72014-07-15 20:30:47 -070047/*
48 * Generic write-protection schema for 25-series SPI flash chips. This assumes
49 * there is a status register that contains one or more consecutive bits which
50 * determine which address range is protected.
51 */
52
53struct status_register_layout {
54 int bp0_pos; /* position of BP0 */
55 int bp_bits; /* number of block protect bits */
56 int srp_pos; /* position of status register protect enable bit */
57};
58
59struct generic_range {
David Hendricks148a4bf2015-03-13 21:02:42 -070060 struct generic_modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -070061 unsigned int bp; /* block protect bitfield */
62 struct wp_range range;
63};
64
65struct generic_wp {
66 struct status_register_layout sr1; /* status register 1 */
67 struct generic_range *ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -070068
69 /*
70 * Some chips store modifier bits in one or more special control
71 * registers instead of the status register like many older SPI NOR
72 * flash chips did. get_modifier_bits() and set_modifier_bits() will do
73 * any chip-specific operations necessary to get/set these bit values.
74 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -070075 int (*get_modifier_bits)(const struct flashctx *flash,
David Hendricks148a4bf2015-03-13 21:02:42 -070076 struct generic_modifier_bits *m);
Souvik Ghoshd75cd672016-06-17 14:21:39 -070077 int (*set_modifier_bits)(const struct flashctx *flash,
David Hendricks148a4bf2015-03-13 21:02:42 -070078 struct generic_modifier_bits *m);
David Hendrickse0512a72014-07-15 20:30:47 -070079};
80
81/*
82 * The following ranges and functions are useful for representing Winbond-
83 * style writeprotect schema in which there are typically 5 bits of
84 * relevant information stored in status register 1:
85 * sec: This bit indicates the units (sectors vs. blocks)
86 * tb: The top-bottom bit indicates if the affected range is at the top of
87 * the flash memory's address space or at the bottom.
Duncan Laurie1801f7c2019-01-09 18:02:51 -080088 * bp: Bitmask representing the number of affected sectors/blocks.
David Hendrickse0512a72014-07-15 20:30:47 -070089 */
David Hendricksf7924d12010-06-10 21:26:44 -070090struct w25q_range {
Duncan Laurie1801f7c2019-01-09 18:02:51 -080091 enum bit_state sec; /* if 1, bp bits describe sectors */
David Hendricksf7924d12010-06-10 21:26:44 -070092 enum bit_state tb; /* top/bottom select */
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +080093 int bp; /* block protect bitfield */
David Hendricksf7924d12010-06-10 21:26:44 -070094 struct wp_range range;
95};
96
David Hendrickse0512a72014-07-15 20:30:47 -070097/*
98 * Mask to extract write-protect enable and range bits
99 * Status register 1:
100 * SRP0: bit 7
101 * range(BP2-BP0): bit 4-2
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800102 * range(BP3-BP0): bit 5-2 (large chips)
David Hendrickse0512a72014-07-15 20:30:47 -0700103 * Status register 2:
104 * SRP1: bit 1
105 */
106#define MASK_WP_AREA (0x9C)
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800107#define MASK_WP_AREA_LARGE (0x9C)
David Hendrickse0512a72014-07-15 20:30:47 -0700108#define MASK_WP2_AREA (0x01)
109
David Hendricks57566ed2010-08-16 18:24:45 -0700110struct w25q_range en25f40_ranges[] = {
111 { X, X, 0, {0, 0} }, /* none */
112 { 0, 0, 0x1, {0x000000, 504 * 1024} },
113 { 0, 0, 0x2, {0x000000, 496 * 1024} },
114 { 0, 0, 0x3, {0x000000, 480 * 1024} },
115 { 0, 0, 0x4, {0x000000, 448 * 1024} },
116 { 0, 0, 0x5, {0x000000, 384 * 1024} },
117 { 0, 0, 0x6, {0x000000, 256 * 1024} },
118 { 0, 0, 0x7, {0x000000, 512 * 1024} },
119};
120
David Hendrickse185bf22011-05-24 15:34:18 -0700121struct w25q_range en25q40_ranges[] = {
122 { 0, 0, 0, {0, 0} }, /* none */
123 { 0, 0, 0x1, {0x000000, 504 * 1024} },
124 { 0, 0, 0x2, {0x000000, 496 * 1024} },
125 { 0, 0, 0x3, {0x000000, 480 * 1024} },
126
127 { 0, 1, 0x0, {0x000000, 448 * 1024} },
128 { 0, 1, 0x1, {0x000000, 384 * 1024} },
129 { 0, 1, 0x2, {0x000000, 256 * 1024} },
130 { 0, 1, 0x3, {0x000000, 512 * 1024} },
131};
132
133struct w25q_range en25q80_ranges[] = {
134 { 0, 0, 0, {0, 0} }, /* none */
135 { 0, 0, 0x1, {0x000000, 1016 * 1024} },
136 { 0, 0, 0x2, {0x000000, 1008 * 1024} },
137 { 0, 0, 0x3, {0x000000, 992 * 1024} },
138 { 0, 0, 0x4, {0x000000, 960 * 1024} },
139 { 0, 0, 0x5, {0x000000, 896 * 1024} },
140 { 0, 0, 0x6, {0x000000, 768 * 1024} },
141 { 0, 0, 0x7, {0x000000, 1024 * 1024} },
142};
143
144struct w25q_range en25q32_ranges[] = {
145 { 0, 0, 0, {0, 0} }, /* none */
146 { 0, 0, 0x1, {0x000000, 4032 * 1024} },
147 { 0, 0, 0x2, {0x000000, 3968 * 1024} },
148 { 0, 0, 0x3, {0x000000, 3840 * 1024} },
149 { 0, 0, 0x4, {0x000000, 3584 * 1024} },
150 { 0, 0, 0x5, {0x000000, 3072 * 1024} },
151 { 0, 0, 0x6, {0x000000, 2048 * 1024} },
152 { 0, 0, 0x7, {0x000000, 4096 * 1024} },
153
154 { 0, 1, 0, {0, 0} }, /* none */
155 { 0, 1, 0x1, {0x010000, 4032 * 1024} },
156 { 0, 1, 0x2, {0x020000, 3968 * 1024} },
157 { 0, 1, 0x3, {0x040000, 3840 * 1024} },
158 { 0, 1, 0x4, {0x080000, 3584 * 1024} },
159 { 0, 1, 0x5, {0x100000, 3072 * 1024} },
160 { 0, 1, 0x6, {0x200000, 2048 * 1024} },
161 { 0, 1, 0x7, {0x000000, 4096 * 1024} },
162};
163
164struct w25q_range en25q64_ranges[] = {
165 { 0, 0, 0, {0, 0} }, /* none */
166 { 0, 0, 0x1, {0x000000, 8128 * 1024} },
167 { 0, 0, 0x2, {0x000000, 8064 * 1024} },
168 { 0, 0, 0x3, {0x000000, 7936 * 1024} },
169 { 0, 0, 0x4, {0x000000, 7680 * 1024} },
170 { 0, 0, 0x5, {0x000000, 7168 * 1024} },
171 { 0, 0, 0x6, {0x000000, 6144 * 1024} },
172 { 0, 0, 0x7, {0x000000, 8192 * 1024} },
173
174 { 0, 1, 0, {0, 0} }, /* none */
175 { 0, 1, 0x1, {0x010000, 8128 * 1024} },
176 { 0, 1, 0x2, {0x020000, 8064 * 1024} },
177 { 0, 1, 0x3, {0x040000, 7936 * 1024} },
178 { 0, 1, 0x4, {0x080000, 7680 * 1024} },
179 { 0, 1, 0x5, {0x100000, 7168 * 1024} },
180 { 0, 1, 0x6, {0x200000, 6144 * 1024} },
181 { 0, 1, 0x7, {0x000000, 8192 * 1024} },
182};
183
184struct w25q_range en25q128_ranges[] = {
185 { 0, 0, 0, {0, 0} }, /* none */
186 { 0, 0, 0x1, {0x000000, 16320 * 1024} },
187 { 0, 0, 0x2, {0x000000, 16256 * 1024} },
188 { 0, 0, 0x3, {0x000000, 16128 * 1024} },
189 { 0, 0, 0x4, {0x000000, 15872 * 1024} },
190 { 0, 0, 0x5, {0x000000, 15360 * 1024} },
191 { 0, 0, 0x6, {0x000000, 14336 * 1024} },
192 { 0, 0, 0x7, {0x000000, 16384 * 1024} },
193
194 { 0, 1, 0, {0, 0} }, /* none */
195 { 0, 1, 0x1, {0x010000, 16320 * 1024} },
196 { 0, 1, 0x2, {0x020000, 16256 * 1024} },
197 { 0, 1, 0x3, {0x040000, 16128 * 1024} },
198 { 0, 1, 0x4, {0x080000, 15872 * 1024} },
199 { 0, 1, 0x5, {0x100000, 15360 * 1024} },
200 { 0, 1, 0x6, {0x200000, 14336 * 1024} },
201 { 0, 1, 0x7, {0x000000, 16384 * 1024} },
202};
203
Marc Jonesb2f90022014-04-29 17:37:23 -0600204struct w25q_range en25s64_ranges[] = {
205 { 0, 0, 0, {0, 0} }, /* none */
206 { 0, 0, 0x1, {0x000000, 8064 * 1024} },
207 { 0, 0, 0x2, {0x000000, 7936 * 1024} },
208 { 0, 0, 0x3, {0x000000, 7680 * 1024} },
209 { 0, 0, 0x4, {0x000000, 7168 * 1024} },
210 { 0, 0, 0x5, {0x000000, 6144 * 1024} },
211 { 0, 0, 0x6, {0x000000, 4096 * 1024} },
212 { 0, 0, 0x7, {0x000000, 8192 * 1024} },
213
214 { 0, 1, 0, {0, 0} }, /* none */
215 { 0, 1, 0x1, {0x7e0000, 128 * 1024} },
216 { 0, 1, 0x2, {0x7c0000, 256 * 1024} },
217 { 0, 1, 0x3, {0x780000, 512 * 1024} },
218 { 0, 1, 0x4, {0x700000, 1024 * 1024} },
219 { 0, 1, 0x5, {0x600000, 2048 * 1024} },
220 { 0, 1, 0x6, {0x400000, 4096 * 1024} },
221 { 0, 1, 0x7, {0x000000, 8192 * 1024} },
222};
223
David Hendricksf8f00c72011-02-01 12:39:46 -0800224/* mx25l1005 ranges also work for the mx25l1005c */
225static struct w25q_range mx25l1005_ranges[] = {
226 { X, X, 0, {0, 0} }, /* none */
227 { X, X, 0x1, {0x010000, 64 * 1024} },
228 { X, X, 0x2, {0x000000, 128 * 1024} },
229 { X, X, 0x3, {0x000000, 128 * 1024} },
230};
231
232static struct w25q_range mx25l2005_ranges[] = {
233 { X, X, 0, {0, 0} }, /* none */
234 { X, X, 0x1, {0x030000, 64 * 1024} },
235 { X, X, 0x2, {0x020000, 128 * 1024} },
236 { X, X, 0x3, {0x000000, 256 * 1024} },
237};
238
239static struct w25q_range mx25l4005_ranges[] = {
240 { X, X, 0, {0, 0} }, /* none */
241 { X, X, 0x1, {0x070000, 64 * 1 * 1024} }, /* block 7 */
242 { X, X, 0x2, {0x060000, 64 * 2 * 1024} }, /* blocks 6-7 */
243 { X, X, 0x3, {0x040000, 64 * 4 * 1024} }, /* blocks 4-7 */
244 { X, X, 0x4, {0x000000, 512 * 1024} },
245 { X, X, 0x5, {0x000000, 512 * 1024} },
246 { X, X, 0x6, {0x000000, 512 * 1024} },
247 { X, X, 0x7, {0x000000, 512 * 1024} },
248};
249
250static struct w25q_range mx25l8005_ranges[] = {
251 { X, X, 0, {0, 0} }, /* none */
252 { X, X, 0x1, {0x0f0000, 64 * 1 * 1024} }, /* block 15 */
253 { X, X, 0x2, {0x0e0000, 64 * 2 * 1024} }, /* blocks 14-15 */
254 { X, X, 0x3, {0x0c0000, 64 * 4 * 1024} }, /* blocks 12-15 */
255 { X, X, 0x4, {0x080000, 64 * 8 * 1024} }, /* blocks 8-15 */
256 { X, X, 0x5, {0x000000, 1024 * 1024} },
257 { X, X, 0x6, {0x000000, 1024 * 1024} },
258 { X, X, 0x7, {0x000000, 1024 * 1024} },
259};
260
David Hendricksf8f00c72011-02-01 12:39:46 -0800261static struct w25q_range mx25l1605d_ranges[] = {
262 { X, 0, 0, {0, 0} }, /* none */
263 { X, 0, 0x1, {0x1f0000, 64 * 1 * 1024} }, /* block 31 */
264 { X, 0, 0x2, {0x1e0000, 64 * 2 * 1024} }, /* blocks 30-31 */
265 { X, 0, 0x3, {0x1c0000, 64 * 4 * 1024} }, /* blocks 28-31 */
266 { X, 0, 0x4, {0x180000, 64 * 8 * 1024} }, /* blocks 24-31 */
267 { X, 0, 0x5, {0x100000, 64 * 16 * 1024} }, /* blocks 16-31 */
268 { X, 0, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
269 { X, 0, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
270
271 { X, 1, 0x0, {0x000000, 2048 * 1024} },
272 { X, 1, 0x1, {0x000000, 2048 * 1024} },
273 { X, 1, 0x2, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
274 { X, 1, 0x3, {0x000000, 64 * 24 * 1024} }, /* blocks 0-23 */
275 { X, 1, 0x4, {0x000000, 64 * 28 * 1024} }, /* blocks 0-27 */
276 { X, 1, 0x5, {0x000000, 64 * 30 * 1024} }, /* blocks 0-29 */
277 { X, 1, 0x6, {0x000000, 64 * 31 * 1024} }, /* blocks 0-30 */
278 { X, 1, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
279};
280
281/* FIXME: Is there an mx25l3205 (without a trailing letter)? */
David Hendricksac72e362010-08-16 18:20:03 -0700282static struct w25q_range mx25l3205d_ranges[] = {
283 { X, 0, 0, {0, 0} }, /* none */
284 { X, 0, 0x1, {0x3f0000, 64 * 1024} },
285 { X, 0, 0x2, {0x3e0000, 128 * 1024} },
286 { X, 0, 0x3, {0x3c0000, 256 * 1024} },
287 { X, 0, 0x4, {0x380000, 512 * 1024} },
288 { X, 0, 0x5, {0x300000, 1024 * 1024} },
289 { X, 0, 0x6, {0x200000, 2048 * 1024} },
290 { X, 0, 0x7, {0x000000, 4096 * 1024} },
291
292 { X, 1, 0x0, {0x000000, 4096 * 1024} },
293 { X, 1, 0x1, {0x000000, 2048 * 1024} },
294 { X, 1, 0x2, {0x000000, 3072 * 1024} },
295 { X, 1, 0x3, {0x000000, 3584 * 1024} },
296 { X, 1, 0x4, {0x000000, 3840 * 1024} },
297 { X, 1, 0x5, {0x000000, 3968 * 1024} },
298 { X, 1, 0x6, {0x000000, 4032 * 1024} },
299 { X, 1, 0x7, {0x000000, 4096 * 1024} },
300};
301
Vincent Palatin87e092a2013-02-28 15:46:14 -0800302static struct w25q_range mx25u3235e_ranges[] = {
303 { X, 0, 0, {0, 0} }, /* none */
304 { 0, 0, 0x1, {0x3f0000, 64 * 1024} },
305 { 0, 0, 0x2, {0x3e0000, 128 * 1024} },
306 { 0, 0, 0x3, {0x3c0000, 256 * 1024} },
307 { 0, 0, 0x4, {0x380000, 512 * 1024} },
308 { 0, 0, 0x5, {0x300000, 1024 * 1024} },
309 { 0, 0, 0x6, {0x200000, 2048 * 1024} },
310 { 0, 0, 0x7, {0x000000, 4096 * 1024} },
311
312 { 0, 1, 0x0, {0x000000, 4096 * 1024} },
313 { 0, 1, 0x1, {0x000000, 2048 * 1024} },
314 { 0, 1, 0x2, {0x000000, 3072 * 1024} },
315 { 0, 1, 0x3, {0x000000, 3584 * 1024} },
316 { 0, 1, 0x4, {0x000000, 3840 * 1024} },
317 { 0, 1, 0x5, {0x000000, 3968 * 1024} },
318 { 0, 1, 0x6, {0x000000, 4032 * 1024} },
319 { 0, 1, 0x7, {0x000000, 4096 * 1024} },
320};
321
Jongpil66a96492014-08-14 17:59:06 +0900322static struct w25q_range mx25u6435e_ranges[] = {
323 { X, 0, 0, {0, 0} }, /* none */
324 { 0, 0, 0x1, {0x7f0000, 1 * 64 * 1024} }, /* block 127 */
325 { 0, 0, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
326 { 0, 0, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
327 { 0, 0, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
328 { 0, 0, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
329 { 0, 0, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
330 { 0, 0, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
331
332 { 0, 1, 0x0, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
333 { 0, 1, 0x1, {0x000000, 96 * 64 * 1024} }, /* blocks 0-95 */
334 { 0, 1, 0x2, {0x000000, 112 * 64 * 1024} }, /* blocks 0-111 */
335 { 0, 1, 0x3, {0x000000, 120 * 64 * 1024} }, /* blocks 0-119 */
336 { 0, 1, 0x4, {0x000000, 124 * 64 * 1024} }, /* blocks 0-123 */
337 { 0, 1, 0x5, {0x000000, 126 * 64 * 1024} }, /* blocks 0-125 */
338 { 0, 1, 0x6, {0x000000, 127 * 64 * 1024} }, /* blocks 0-126 */
339 { 0, 1, 0x7, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
340};
341
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600342#define MX25U12835E_TB (1 << 3)
343static struct w25q_range mx25u12835e_tb0_ranges[] = {
Paul Fagerburg90571582019-03-15 11:32:57 -0600344 { X, X, 0, {0, 0} }, /* none */
Alex Lu831c6092017-11-02 23:19:34 -0700345 { 0, 0, 0x1, {0xff0000, 1 * 64 * 1024} }, /* block 255 */
346 { 0, 0, 0x2, {0xfe0000, 2 * 64 * 1024} }, /* blocks 254-255 */
347 { 0, 0, 0x3, {0xfc0000, 4 * 64 * 1024} }, /* blocks 252-255 */
348 { 0, 0, 0x4, {0xf80000, 8 * 64 * 1024} }, /* blocks 248-255 */
349 { 0, 0, 0x5, {0xf00000, 16 * 64 * 1024} }, /* blocks 240-255 */
350 { 0, 0, 0x6, {0xe00000, 32 * 64 * 1024} }, /* blocks 224-255 */
351 { 0, 0, 0x7, {0xc00000, 64 * 64 * 1024} }, /* blocks 192-255 */
Paul Fagerburg90571582019-03-15 11:32:57 -0600352 { 0, 0, 0x8, {0x800000, 128 * 64 * 1024} }, /* blocks 128-255 */
353 { 0, 0, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
354 { 0, 0, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
355 { 0, 0, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
356 { 0, 0, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
357 { 0, 0, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
358 { 0, 0, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
359 { 0, 0, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600360};
Alex Lu831c6092017-11-02 23:19:34 -0700361
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600362static struct w25q_range mx25u12835e_tb1_ranges[] = {
Paul Fagerburg90571582019-03-15 11:32:57 -0600363 { 0, 1, 0x1, {0x000000, 1 * 64 * 1024} }, /* block 0 */
364 { 0, 1, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
365 { 0, 1, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
366 { 0, 1, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
367 { 0, 1, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
368 { 0, 1, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
369 { 0, 1, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
370 { 0, 1, 0x8, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
371 { 0, 1, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
372 { 0, 1, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
373 { 0, 1, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
374 { 0, 1, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
375 { 0, 1, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
376 { 0, 1, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
377 { 0, 1, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Alex Lu831c6092017-11-02 23:19:34 -0700378};
379
David Hendricksbfa624b2012-07-24 12:47:59 -0700380static struct w25q_range n25q064_ranges[] = {
David Hendricksfe9123b2015-04-21 13:18:31 -0700381 /*
382 * Note: For N25Q064, sec (usually in bit position 6) is called BP3
383 * (block protect bit 3). It is only useful when all blocks are to
384 * be write-protected.
385 */
David Hendricks42a549a2015-04-22 11:25:07 -0700386 { 0, 0, 0, {0, 0} }, /* none */
David Hendricksbfa624b2012-07-24 12:47:59 -0700387
388 { 0, 0, 0x1, {0x7f0000, 64 * 1024} }, /* block 127 */
389 { 0, 0, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
390 { 0, 0, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
391 { 0, 0, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
392 { 0, 0, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
393 { 0, 0, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
394 { 0, 0, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
395
David Hendricksfe9123b2015-04-21 13:18:31 -0700396 { 0, 1, 0x1, {0x000000, 64 * 1024} }, /* block 0 */
397 { 0, 1, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
398 { 0, 1, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
399 { 0, 1, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
400 { 0, 1, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
401 { 0, 1, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
402 { 0, 1, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700403
404 { X, 1, 0x0, {0x000000, 128 * 64 * 1024} }, /* all */
405 { X, 1, 0x1, {0x000000, 128 * 64 * 1024} }, /* all */
406 { X, 1, 0x2, {0x000000, 128 * 64 * 1024} }, /* all */
407 { X, 1, 0x3, {0x000000, 128 * 64 * 1024} }, /* all */
408 { X, 1, 0x4, {0x000000, 128 * 64 * 1024} }, /* all */
409 { X, 1, 0x5, {0x000000, 128 * 64 * 1024} }, /* all */
410 { X, 1, 0x6, {0x000000, 128 * 64 * 1024} }, /* all */
411 { X, 1, 0x7, {0x000000, 128 * 64 * 1024} }, /* all */
412};
413
David Hendricksf7924d12010-06-10 21:26:44 -0700414static struct w25q_range w25q16_ranges[] = {
415 { X, X, 0, {0, 0} }, /* none */
416 { 0, 0, 0x1, {0x1f0000, 64 * 1024} },
417 { 0, 0, 0x2, {0x1e0000, 128 * 1024} },
418 { 0, 0, 0x3, {0x1c0000, 256 * 1024} },
419 { 0, 0, 0x4, {0x180000, 512 * 1024} },
420 { 0, 0, 0x5, {0x100000, 1024 * 1024} },
421
422 { 0, 1, 0x1, {0x000000, 64 * 1024} },
423 { 0, 1, 0x2, {0x000000, 128 * 1024} },
424 { 0, 1, 0x3, {0x000000, 256 * 1024} },
425 { 0, 1, 0x4, {0x000000, 512 * 1024} },
426 { 0, 1, 0x5, {0x000000, 1024 * 1024} },
427 { X, X, 0x6, {0x000000, 2048 * 1024} },
428 { X, X, 0x7, {0x000000, 2048 * 1024} },
429
430 { 1, 0, 0x1, {0x1ff000, 4 * 1024} },
431 { 1, 0, 0x2, {0x1fe000, 8 * 1024} },
432 { 1, 0, 0x3, {0x1fc000, 16 * 1024} },
433 { 1, 0, 0x4, {0x1f8000, 32 * 1024} },
Paul Fagerburg90571582019-03-15 11:32:57 -0600434 { 1, 0, 0x5, {0x1f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700435
436 { 1, 1, 0x1, {0x000000, 4 * 1024} },
437 { 1, 1, 0x2, {0x000000, 8 * 1024} },
438 { 1, 1, 0x3, {0x000000, 16 * 1024} },
Paul Fagerburg90571582019-03-15 11:32:57 -0600439 { 1, 1, 0x4, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700440 { 1, 1, 0x5, {0x000000, 32 * 1024} },
441};
442
443static struct w25q_range w25q32_ranges[] = {
444 { X, X, 0, {0, 0} }, /* none */
445 { 0, 0, 0x1, {0x3f0000, 64 * 1024} },
446 { 0, 0, 0x2, {0x3e0000, 128 * 1024} },
447 { 0, 0, 0x3, {0x3c0000, 256 * 1024} },
448 { 0, 0, 0x4, {0x380000, 512 * 1024} },
449 { 0, 0, 0x5, {0x300000, 1024 * 1024} },
David Hendricks05653ff2010-06-15 16:05:12 -0700450 { 0, 0, 0x6, {0x200000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700451
452 { 0, 1, 0x1, {0x000000, 64 * 1024} },
453 { 0, 1, 0x2, {0x000000, 128 * 1024} },
454 { 0, 1, 0x3, {0x000000, 256 * 1024} },
455 { 0, 1, 0x4, {0x000000, 512 * 1024} },
456 { 0, 1, 0x5, {0x000000, 1024 * 1024} },
457 { 0, 1, 0x6, {0x000000, 2048 * 1024} },
458 { X, X, 0x7, {0x000000, 4096 * 1024} },
459
460 { 1, 0, 0x1, {0x3ff000, 4 * 1024} },
461 { 1, 0, 0x2, {0x3fe000, 8 * 1024} },
462 { 1, 0, 0x3, {0x3fc000, 16 * 1024} },
463 { 1, 0, 0x4, {0x3f8000, 32 * 1024} },
Paul Fagerburg90571582019-03-15 11:32:57 -0600464 { 1, 0, 0x5, {0x3f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700465
466 { 1, 1, 0x1, {0x000000, 4 * 1024} },
467 { 1, 1, 0x2, {0x000000, 8 * 1024} },
468 { 1, 1, 0x3, {0x000000, 16 * 1024} },
469 { 1, 1, 0x4, {0x000000, 32 * 1024} },
470 { 1, 1, 0x5, {0x000000, 32 * 1024} },
471};
472
473static struct w25q_range w25q80_ranges[] = {
474 { X, X, 0, {0, 0} }, /* none */
475 { 0, 0, 0x1, {0x0f0000, 64 * 1024} },
476 { 0, 0, 0x2, {0x0e0000, 128 * 1024} },
477 { 0, 0, 0x3, {0x0c0000, 256 * 1024} },
478 { 0, 0, 0x4, {0x080000, 512 * 1024} },
479
480 { 0, 1, 0x1, {0x000000, 64 * 1024} },
481 { 0, 1, 0x2, {0x000000, 128 * 1024} },
482 { 0, 1, 0x3, {0x000000, 256 * 1024} },
483 { 0, 1, 0x4, {0x000000, 512 * 1024} },
David Hendricks05653ff2010-06-15 16:05:12 -0700484 { X, X, 0x6, {0x000000, 1024 * 1024} },
485 { X, X, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700486
487 { 1, 0, 0x1, {0x1ff000, 4 * 1024} },
488 { 1, 0, 0x2, {0x1fe000, 8 * 1024} },
489 { 1, 0, 0x3, {0x1fc000, 16 * 1024} },
490 { 1, 0, 0x4, {0x1f8000, 32 * 1024} },
491 { 1, 0, 0x5, {0x1f8000, 32 * 1024} },
492
493 { 1, 1, 0x1, {0x000000, 4 * 1024} },
494 { 1, 1, 0x2, {0x000000, 8 * 1024} },
495 { 1, 1, 0x3, {0x000000, 16 * 1024} },
496 { 1, 1, 0x4, {0x000000, 32 * 1024} },
497 { 1, 1, 0x5, {0x000000, 32 * 1024} },
498};
499
David Hendricks2c4a76c2010-06-28 14:00:43 -0700500static struct w25q_range w25q64_ranges[] = {
501 { X, X, 0, {0, 0} }, /* none */
502
503 { 0, 0, 0x1, {0x7e0000, 128 * 1024} },
504 { 0, 0, 0x2, {0x7c0000, 256 * 1024} },
505 { 0, 0, 0x3, {0x780000, 512 * 1024} },
506 { 0, 0, 0x4, {0x700000, 1024 * 1024} },
507 { 0, 0, 0x5, {0x600000, 2048 * 1024} },
508 { 0, 0, 0x6, {0x400000, 4096 * 1024} },
509
510 { 0, 1, 0x1, {0x000000, 128 * 1024} },
511 { 0, 1, 0x2, {0x000000, 256 * 1024} },
512 { 0, 1, 0x3, {0x000000, 512 * 1024} },
513 { 0, 1, 0x4, {0x000000, 1024 * 1024} },
514 { 0, 1, 0x5, {0x000000, 2048 * 1024} },
515 { 0, 1, 0x6, {0x000000, 4096 * 1024} },
516 { X, X, 0x7, {0x000000, 8192 * 1024} },
517
518 { 1, 0, 0x1, {0x7ff000, 4 * 1024} },
519 { 1, 0, 0x2, {0x7fe000, 8 * 1024} },
520 { 1, 0, 0x3, {0x7fc000, 16 * 1024} },
521 { 1, 0, 0x4, {0x7f8000, 32 * 1024} },
522 { 1, 0, 0x5, {0x7f8000, 32 * 1024} },
523
524 { 1, 1, 0x1, {0x000000, 4 * 1024} },
525 { 1, 1, 0x2, {0x000000, 8 * 1024} },
526 { 1, 1, 0x3, {0x000000, 16 * 1024} },
527 { 1, 1, 0x4, {0x000000, 32 * 1024} },
528 { 1, 1, 0x5, {0x000000, 32 * 1024} },
529};
530
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700531static struct w25q_range w25rq128_cmp0_ranges[] = {
532 { X, X, 0, {0, 0} }, /* NONE */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530533
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700534 { 0, 0, 0x1, {0xfc0000, 256 * 1024} }, /* Upper 1/64 */
535 { 0, 0, 0x2, {0xf80000, 512 * 1024} }, /* Upper 1/32 */
536 { 0, 0, 0x3, {0xf00000, 1024 * 1024} }, /* Upper 1/16 */
537 { 0, 0, 0x4, {0xe00000, 2048 * 1024} }, /* Upper 1/8 */
538 { 0, 0, 0x5, {0xc00000, 4096 * 1024} }, /* Upper 1/4 */
539 { 0, 0, 0x6, {0x800000, 8192 * 1024} }, /* Upper 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530540
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700541 { 0, 1, 0x1, {0x000000, 256 * 1024} }, /* Lower 1/64 */
542 { 0, 1, 0x2, {0x000000, 512 * 1024} }, /* Lower 1/32 */
543 { 0, 1, 0x3, {0x000000, 1024 * 1024} }, /* Lower 1/16 */
544 { 0, 1, 0x4, {0x000000, 2048 * 1024} }, /* Lower 1/8 */
545 { 0, 1, 0x5, {0x000000, 4096 * 1024} }, /* Lower 1/4 */
546 { 0, 1, 0x6, {0x000000, 8192 * 1024} }, /* Lower 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530547
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700548 { X, X, 0x7, {0x000000, 16384 * 1024} }, /* ALL */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530549
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700550 { 1, 0, 0x1, {0xfff000, 4 * 1024} }, /* Upper 1/4096 */
551 { 1, 0, 0x2, {0xffe000, 8 * 1024} }, /* Upper 1/2048 */
552 { 1, 0, 0x3, {0xffc000, 16 * 1024} }, /* Upper 1/1024 */
553 { 1, 0, 0x4, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
554 { 1, 0, 0x5, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
555
556 { 1, 1, 0x1, {0x000000, 4 * 1024} }, /* Lower 1/4096 */
557 { 1, 1, 0x2, {0x000000, 8 * 1024} }, /* Lower 1/2048 */
558 { 1, 1, 0x3, {0x000000, 16 * 1024} }, /* Lower 1/1024 */
559 { 1, 1, 0x4, {0x000000, 32 * 1024} }, /* Lower 1/512 */
560 { 1, 1, 0x5, {0x000000, 32 * 1024} }, /* Lower 1/512 */
561};
562
563static struct w25q_range w25rq128_cmp1_ranges[] = {
564 { X, X, 0x0, {0x000000, 16 * 1024 * 1024} }, /* ALL */
565
566 { 0, 0, 0x1, {0x000000, 16128 * 1024} }, /* Lower 63/64 */
567 { 0, 0, 0x2, {0x000000, 15872 * 1024} }, /* Lower 31/32 */
568 { 0, 0, 0x3, {0x000000, 15 * 1024 * 1024} }, /* Lower 15/16 */
569 { 0, 0, 0x4, {0x000000, 14 * 1024 * 1024} }, /* Lower 7/8 */
570 { 0, 0, 0x5, {0x000000, 12 * 1024 * 1024} }, /* Lower 3/4 */
571 { 0, 0, 0x6, {0x000000, 8 * 1024 * 1024} }, /* Lower 1/2 */
572
573 { 0, 1, 0x1, {0x040000, 16128 * 1024} }, /* Upper 63/64 */
574 { 0, 1, 0x2, {0x080000, 15872 * 1024} }, /* Upper 31/32 */
575 { 0, 1, 0x3, {0x100000, 15 * 1024 * 1024} }, /* Upper 15/16 */
576 { 0, 1, 0x4, {0x200000, 14 * 1024 * 1024} }, /* Upper 7/8 */
577 { 0, 1, 0x5, {0x400000, 12 * 1024 * 1024} }, /* Upper 3/4 */
578 { 0, 1, 0x6, {0x800000, 8 * 1024 * 1024} }, /* Upper 1/2 */
579
580 { X, X, 0x7, {0x000000, 0} }, /* NONE */
581
582 { 1, 0, 0x1, {0x000000, 16380 * 1024} }, /* Lower 4095/4096 */
583 { 1, 0, 0x2, {0x000000, 16376 * 1024} }, /* Lower 2048/2048 */
584 { 1, 0, 0x3, {0x000000, 16368 * 1024} }, /* Lower 1023/1024 */
585 { 1, 0, 0x4, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
586 { 1, 0, 0x5, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
587
588 { 1, 1, 0x1, {0x001000, 16380 * 1024} }, /* Upper 4095/4096 */
589 { 1, 1, 0x2, {0x002000, 16376 * 1024} }, /* Upper 2047/2048 */
590 { 1, 1, 0x3, {0x004000, 16368 * 1024} }, /* Upper 1023/1024 */
591 { 1, 1, 0x4, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
592 { 1, 1, 0x5, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530593};
594
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800595static struct w25q_range w25rq256_cmp0_ranges[] = {
596 { X, X, 0x0, {0x0000000, 0x0000000} }, /* NONE */
597
598 { X, 0, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* Upper 1/512 */
599 { X, 0, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* Upper 1/256 */
600 { X, 0, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* Upper 1/128 */
601 { X, 0, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* Upper 1/64 */
602 { X, 0, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* Upper 1/32 */
603 { X, 0, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* Upper 1/16 */
604 { X, 0, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* Upper 1/8 */
605 { X, 0, 0x8, {0x1800000, 64 * 128 * 1024} }, /* Upper 1/4 */
606 { X, 0, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
607
608 { X, 1, 0x1, {0x0000000, 64 * 1 * 1024} }, /* Lower 1/512 */
609 { X, 1, 0x2, {0x0000000, 64 * 2 * 1024} }, /* Lower 1/256 */
610 { X, 1, 0x3, {0x0000000, 64 * 4 * 1024} }, /* Lower 1/128 */
611 { X, 1, 0x4, {0x0000000, 64 * 8 * 1024} }, /* Lower 1/64 */
612 { X, 1, 0x5, {0x0000000, 64 * 16 * 1024} }, /* Lower 1/32 */
613 { X, 1, 0x6, {0x0000000, 64 * 32 * 1024} }, /* Lower 1/16 */
614 { X, 1, 0x7, {0x0000000, 64 * 64 * 1024} }, /* Lower 1/8 */
615 { X, 1, 0x8, {0x0000000, 64 * 128 * 1024} }, /* Lower 1/4 */
616 { X, 1, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
617
618 { X, X, 0xa, {0x0000000, 64 * 512 * 1024} }, /* ALL */
619 { X, X, 0xb, {0x0000000, 64 * 512 * 1024} }, /* ALL */
620 { X, X, 0xc, {0x0000000, 64 * 512 * 1024} }, /* ALL */
621 { X, X, 0xd, {0x0000000, 64 * 512 * 1024} }, /* ALL */
622 { X, X, 0xe, {0x0000000, 64 * 512 * 1024} }, /* ALL */
623 { X, X, 0xf, {0x0000000, 64 * 512 * 1024} }, /* ALL */
624};
625
626static struct w25q_range w25rq256_cmp1_ranges[] = {
627 { X, X, 0x0, {0x0000000, 64 * 512 * 1024} }, /* ALL */
628
629 { X, 0, 0x1, {0x0000000, 64 * 511 * 1024} }, /* Lower 511/512 */
630 { X, 0, 0x2, {0x0000000, 64 * 510 * 1024} }, /* Lower 255/256 */
631 { X, 0, 0x3, {0x0000000, 64 * 508 * 1024} }, /* Lower 127/128 */
632 { X, 0, 0x4, {0x0000000, 64 * 504 * 1024} }, /* Lower 63/64 */
633 { X, 0, 0x5, {0x0000000, 64 * 496 * 1024} }, /* Lower 31/32 */
634 { X, 0, 0x6, {0x0000000, 64 * 480 * 1024} }, /* Lower 15/16 */
635 { X, 0, 0x7, {0x0000000, 64 * 448 * 1024} }, /* Lower 7/8 */
636 { X, 0, 0x8, {0x0000000, 64 * 384 * 1024} }, /* Lower 3/4 */
637 { X, 0, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
638
639 { X, 1, 0x1, {0x0010000, 64 * 511 * 1024} }, /* Upper 511/512 */
640 { X, 1, 0x2, {0x0020000, 64 * 510 * 1024} }, /* Upper 255/256 */
641 { X, 1, 0x3, {0x0040000, 64 * 508 * 1024} }, /* Upper 127/128 */
642 { X, 1, 0x4, {0x0080000, 64 * 504 * 1024} }, /* Upper 63/64 */
643 { X, 1, 0x5, {0x0100000, 64 * 496 * 1024} }, /* Upper 31/32 */
644 { X, 1, 0x6, {0x0200000, 64 * 480 * 1024} }, /* Upper 15/16 */
645 { X, 1, 0x7, {0x0400000, 64 * 448 * 1024} }, /* Upper 7/8 */
646 { X, 1, 0x8, {0x0800000, 64 * 384 * 1024} }, /* Upper 3/4 */
647 { X, 1, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
648
649 { X, X, 0xa, {0x0000000, 0x0000000} }, /* NONE */
650 { X, X, 0xb, {0x0000000, 0x0000000} }, /* NONE */
651 { X, X, 0xc, {0x0000000, 0x0000000} }, /* NONE */
652 { X, X, 0xd, {0x0000000, 0x0000000} }, /* NONE */
653 { X, X, 0xe, {0x0000000, 0x0000000} }, /* NONE */
654 { X, X, 0xf, {0x0000000, 0x0000000} }, /* NONE */
655};
656
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800657struct w25q_range w25x10_ranges[] = {
658 { X, X, 0, {0, 0} }, /* none */
659 { 0, 0, 0x1, {0x010000, 64 * 1024} },
660 { 0, 1, 0x1, {0x000000, 64 * 1024} },
661 { X, X, 0x2, {0x000000, 128 * 1024} },
662 { X, X, 0x3, {0x000000, 128 * 1024} },
663};
664
665struct w25q_range w25x20_ranges[] = {
666 { X, X, 0, {0, 0} }, /* none */
667 { 0, 0, 0x1, {0x030000, 64 * 1024} },
668 { 0, 0, 0x2, {0x020000, 128 * 1024} },
669 { 0, 1, 0x1, {0x000000, 64 * 1024} },
670 { 0, 1, 0x2, {0x000000, 128 * 1024} },
671 { 0, X, 0x3, {0x000000, 256 * 1024} },
672};
673
David Hendricks470ca952010-08-13 14:01:53 -0700674struct w25q_range w25x40_ranges[] = {
675 { X, X, 0, {0, 0} }, /* none */
676 { 0, 0, 0x1, {0x070000, 64 * 1024} },
677 { 0, 0, 0x2, {0x060000, 128 * 1024} },
678 { 0, 0, 0x3, {0x040000, 256 * 1024} },
679 { 0, 1, 0x1, {0x000000, 64 * 1024} },
680 { 0, 1, 0x2, {0x000000, 128 * 1024} },
681 { 0, 1, 0x3, {0x000000, 256 * 1024} },
682 { 0, X, 0x4, {0x000000, 512 * 1024} },
David Hendricksb389abb2016-06-17 16:47:00 -0700683 { 0, X, 0x5, {0x000000, 512 * 1024} },
684 { 0, X, 0x6, {0x000000, 512 * 1024} },
685 { 0, X, 0x7, {0x000000, 512 * 1024} },
David Hendricks470ca952010-08-13 14:01:53 -0700686};
687
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800688struct w25q_range w25x80_ranges[] = {
689 { X, X, 0, {0, 0} }, /* none */
690 { 0, 0, 0x1, {0x0F0000, 64 * 1024} },
691 { 0, 0, 0x2, {0x0E0000, 128 * 1024} },
692 { 0, 0, 0x3, {0x0C0000, 256 * 1024} },
693 { 0, 0, 0x4, {0x080000, 512 * 1024} },
694 { 0, 1, 0x1, {0x000000, 64 * 1024} },
695 { 0, 1, 0x2, {0x000000, 128 * 1024} },
696 { 0, 1, 0x3, {0x000000, 256 * 1024} },
697 { 0, 1, 0x4, {0x000000, 512 * 1024} },
698 { 0, X, 0x5, {0x000000, 1024 * 1024} },
699 { 0, X, 0x6, {0x000000, 1024 * 1024} },
700 { 0, X, 0x7, {0x000000, 1024 * 1024} },
701};
702
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600703static struct w25q_range gd25q40_cmp0_ranges[] = {
704 { X, X, 0, {0, 0} }, /* None */
705 { 0, 0, 0x1, {0x070000, 64 * 1024} },
706 { 0, 0, 0x2, {0x060000, 128 * 1024} },
707 { 0, 0, 0x3, {0x040000, 256 * 1024} },
708 { 0, 1, 0x1, {0x000000, 64 * 1024} },
709 { 0, 1, 0x2, {0x000000, 128 * 1024} },
710 { 0, 1, 0x3, {0x000000, 256 * 1024} },
711 { 0, X, 0x4, {0x000000, 512 * 1024} }, /* All */
712 { 0, X, 0x5, {0x000000, 512 * 1024} }, /* All */
713 { 0, X, 0x6, {0x000000, 512 * 1024} }, /* All */
714 { 0, X, 0x7, {0x000000, 512 * 1024} }, /* All */
715 { 1, 0, 0x1, {0x07F000, 4 * 1024} },
716 { 1, 0, 0x2, {0x07E000, 8 * 1024} },
717 { 1, 0, 0x3, {0x07C000, 16 * 1024} },
718 { 1, 0, 0x4, {0x078000, 32 * 1024} },
719 { 1, 0, 0x5, {0x078000, 32 * 1024} },
720 { 1, 0, 0x6, {0x078000, 32 * 1024} },
721 { 1, 1, 0x1, {0x000000, 4 * 1024} },
722 { 1, 1, 0x2, {0x000000, 8 * 1024} },
723 { 1, 1, 0x3, {0x000000, 16 * 1024} },
724 { 1, 1, 0x4, {0x000000, 32 * 1024} },
725 { 1, 1, 0x5, {0x000000, 32 * 1024} },
726 { 1, 1, 0x6, {0x000000, 32 * 1024} },
727 { 1, X, 0x7, {0x000000, 512 * 1024} }, /* All */
728};
729
730static struct w25q_range gd25q40_cmp1_ranges[] = {
731 { X, X, 0x0, {0x000000, 512 * 1024} }, /* ALL */
732 { 0, 0, 0x1, {0x000000, 448 * 1024} },
733 { 0, 0, 0x2, {0x000000, 384 * 1024} },
734 { 0, 0, 0x3, {0x000000, 256 * 1024} },
735
736 { 0, 1, 0x1, {0x010000, 448 * 1024} },
737 { 0, 1, 0x2, {0x020000, 384 * 1024} },
738 { 0, 1, 0x3, {0x040000, 256 * 1024} },
739
740 { 0, X, 0x4, {0x000000, 0} }, /* None */
741 { 0, X, 0x5, {0x000000, 0} }, /* None */
742 { 0, X, 0x6, {0x000000, 0} }, /* None */
743 { 0, X, 0x7, {0x000000, 0} }, /* None */
744
745 { 1, 0, 0x1, {0x000000, 508 * 1024} },
746 { 1, 0, 0x2, {0x000000, 504 * 1024} },
747 { 1, 0, 0x3, {0x000000, 496 * 1024} },
748 { 1, 0, 0x4, {0x000000, 480 * 1024} },
749 { 1, 0, 0x5, {0x000000, 480 * 1024} },
750 { 1, 0, 0x6, {0x000000, 480 * 1024} },
751
752 { 1, 1, 0x1, {0x001000, 508 * 1024} },
753 { 1, 1, 0x2, {0x002000, 504 * 1024} },
754 { 1, 1, 0x3, {0x004000, 496 * 1024} },
755 { 1, 1, 0x4, {0x008000, 480 * 1024} },
756 { 1, 1, 0x5, {0x008000, 480 * 1024} },
757 { 1, 1, 0x6, {0x008000, 480 * 1024} },
758
759 { 1, X, 0x7, {0x000000, 0} }, /* None */
760};
761
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700762static struct w25q_range gd25q64_ranges[] = {
763 { X, X, 0, {0, 0} }, /* none */
764 { 0, 0, 0x1, {0x7e0000, 128 * 1024} },
765 { 0, 0, 0x2, {0x7c0000, 256 * 1024} },
766 { 0, 0, 0x3, {0x780000, 512 * 1024} },
767 { 0, 0, 0x4, {0x700000, 1024 * 1024} },
768 { 0, 0, 0x5, {0x600000, 2048 * 1024} },
769 { 0, 0, 0x6, {0x400000, 4096 * 1024} },
770
771 { 0, 1, 0x1, {0x000000, 128 * 1024} },
772 { 0, 1, 0x2, {0x000000, 256 * 1024} },
773 { 0, 1, 0x3, {0x000000, 512 * 1024} },
774 { 0, 1, 0x4, {0x000000, 1024 * 1024} },
775 { 0, 1, 0x5, {0x000000, 2048 * 1024} },
776 { 0, 1, 0x6, {0x000000, 4096 * 1024} },
777 { X, X, 0x7, {0x000000, 8192 * 1024} },
778
779 { 1, 0, 0x1, {0x7ff000, 4 * 1024} },
780 { 1, 0, 0x2, {0x7fe000, 8 * 1024} },
781 { 1, 0, 0x3, {0x7fc000, 16 * 1024} },
782 { 1, 0, 0x4, {0x7f8000, 32 * 1024} },
783 { 1, 0, 0x5, {0x7f8000, 32 * 1024} },
784 { 1, 0, 0x6, {0x7f8000, 32 * 1024} },
785
786 { 1, 1, 0x1, {0x000000, 4 * 1024} },
787 { 1, 1, 0x2, {0x000000, 8 * 1024} },
788 { 1, 1, 0x3, {0x000000, 16 * 1024} },
789 { 1, 1, 0x4, {0x000000, 32 * 1024} },
790 { 1, 1, 0x5, {0x000000, 32 * 1024} },
791 { 1, 1, 0x6, {0x000000, 32 * 1024} },
792};
793
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +0800794static struct w25q_range a25l040_ranges[] = {
795 { X, X, 0x0, {0, 0} }, /* none */
796 { X, X, 0x1, {0x70000, 64 * 1024} },
797 { X, X, 0x2, {0x60000, 128 * 1024} },
798 { X, X, 0x3, {0x40000, 256 * 1024} },
799 { X, X, 0x4, {0x00000, 512 * 1024} },
800 { X, X, 0x5, {0x00000, 512 * 1024} },
801 { X, X, 0x6, {0x00000, 512 * 1024} },
802 { X, X, 0x7, {0x00000, 512 * 1024} },
803};
804
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700805static uint8_t do_read_status(const struct flashctx *flash)
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530806{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100807 if (flash->chip->read_status)
808 return flash->chip->read_status(flash);
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530809 else
810 return spi_read_status_register(flash);
811}
812
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700813static int do_write_status(const struct flashctx *flash, int status)
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530814{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100815 if (flash->chip->write_status)
816 return flash->chip->write_status(flash, status);
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530817 else
818 return spi_write_status_register(flash, status);
819}
820
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700821/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700822static uint8_t w25q_read_status_register_2(const struct flashctx *flash)
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700823{
824 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 };
825 unsigned char readarr[2];
826 int ret;
827
828 /* Read Status Register */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700829 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700830 if (ret) {
831 /*
832 * FIXME: make this a benign failure for now in case we are
833 * unable to execute the opcode
834 */
835 msg_cdbg("RDSR2 failed!\n");
836 readarr[0] = 0x00;
837 }
838
839 return readarr[0];
840}
841
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600842/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
843uint8_t mx25l_read_config_register(const struct flashctx *flash)
844{
845 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x15 };
846 unsigned char readarr[2]; /* leave room for dummy byte */
847 int ret;
848
849 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
850 if (ret) {
851 msg_cdbg("RDCR failed!\n");
852 readarr[0] = 0x00;
853 }
854
855 return readarr[0];
856}
857
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800858/* Given a flash chip, this function returns its range table. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700859static int w25_range_table(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800860 struct w25q_range **w25q_ranges,
861 int *num_entries)
David Hendricksf7924d12010-06-10 21:26:44 -0700862{
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600863 uint8_t cr;
864
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800865 *w25q_ranges = 0;
866 *num_entries = 0;
David Hendricksf7924d12010-06-10 21:26:44 -0700867
Patrick Georgif3fa2992017-02-02 16:24:44 +0100868 switch (flash->chip->manufacture_id) {
David Hendricksd494b0a2010-08-16 16:28:50 -0700869 case WINBOND_NEX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +0100870 switch(flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -0800871 case WINBOND_NEX_W25X10:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800872 *w25q_ranges = w25x10_ranges;
873 *num_entries = ARRAY_SIZE(w25x10_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800874 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800875 case WINBOND_NEX_W25X20:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800876 *w25q_ranges = w25x20_ranges;
877 *num_entries = ARRAY_SIZE(w25x20_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800878 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800879 case WINBOND_NEX_W25X40:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800880 *w25q_ranges = w25x40_ranges;
881 *num_entries = ARRAY_SIZE(w25x40_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700882 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800883 case WINBOND_NEX_W25X80:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800884 *w25q_ranges = w25x80_ranges;
885 *num_entries = ARRAY_SIZE(w25x80_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800886 break;
Patrick Georgicc04a452017-02-06 12:14:43 +0100887 case WINBOND_NEX_W25Q80_V:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800888 *w25q_ranges = w25q80_ranges;
889 *num_entries = ARRAY_SIZE(w25q80_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700890 break;
Patrick Georgicc04a452017-02-06 12:14:43 +0100891 case WINBOND_NEX_W25Q16_V:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800892 *w25q_ranges = w25q16_ranges;
893 *num_entries = ARRAY_SIZE(w25q16_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700894 break;
Patrick Georgicc04a452017-02-06 12:14:43 +0100895 case WINBOND_NEX_W25Q32_V:
896 case WINBOND_NEX_W25Q32_W:
Edward O'Callaghand80cf712019-05-24 22:06:36 +1000897 case WINBOND_NEX_W25Q32JW:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800898 *w25q_ranges = w25q32_ranges;
899 *num_entries = ARRAY_SIZE(w25q32_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700900 break;
Patrick Georgicc04a452017-02-06 12:14:43 +0100901 case WINBOND_NEX_W25Q64_V:
902 case WINBOND_NEX_W25Q64_W:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800903 *w25q_ranges = w25q64_ranges;
904 *num_entries = ARRAY_SIZE(w25q64_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700905 break;
Edward O'Callaghan517cb822019-11-21 14:08:32 +1100906 case WINBOND_NEX_W25Q128_DTR:
Alan Green77a95de2019-07-01 16:40:39 +1000907 case WINBOND_NEX_W25Q128_V_M:
Patrick Georgicc04a452017-02-06 12:14:43 +0100908 case WINBOND_NEX_W25Q128_V:
909 case WINBOND_NEX_W25Q128_W:
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700910 if (w25q_read_status_register_2(flash) & (1 << 6)) {
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700911 /* CMP == 1 */
912 *w25q_ranges = w25rq128_cmp1_ranges;
913 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
914 } else {
915 /* CMP == 0 */
916 *w25q_ranges = w25rq128_cmp0_ranges;
917 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
918 }
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530919 break;
Alan Green77a95de2019-07-01 16:40:39 +1000920 case WINBOND_NEX_W25Q256JV_M:
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800921 if (w25q_read_status_register_2(flash) & (1 << 6)) {
922 /* CMP == 1 */
923 *w25q_ranges = w25rq256_cmp1_ranges;
924 *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges);
925 } else {
926 /* CMP == 0 */
927 *w25q_ranges = w25rq256_cmp0_ranges;
928 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
929 }
930 break;
David Hendricksd494b0a2010-08-16 16:28:50 -0700931 default:
932 msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
933 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +0100934 flash->chip->model_id);
David Hendricksd494b0a2010-08-16 16:28:50 -0700935 return -1;
936 }
David Hendricks2c4a76c2010-06-28 14:00:43 -0700937 break;
David Hendricks57566ed2010-08-16 18:24:45 -0700938 case EON_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +0100939 switch (flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -0800940 case EON_EN25F40:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800941 *w25q_ranges = en25f40_ranges;
942 *num_entries = ARRAY_SIZE(en25f40_ranges);
David Hendricks57566ed2010-08-16 18:24:45 -0700943 break;
David Hendrickse185bf22011-05-24 15:34:18 -0700944 case EON_EN25Q40:
945 *w25q_ranges = en25q40_ranges;
946 *num_entries = ARRAY_SIZE(en25q40_ranges);
947 break;
948 case EON_EN25Q80:
949 *w25q_ranges = en25q80_ranges;
950 *num_entries = ARRAY_SIZE(en25q80_ranges);
951 break;
952 case EON_EN25Q32:
953 *w25q_ranges = en25q32_ranges;
954 *num_entries = ARRAY_SIZE(en25q32_ranges);
955 break;
956 case EON_EN25Q64:
957 *w25q_ranges = en25q64_ranges;
958 *num_entries = ARRAY_SIZE(en25q64_ranges);
959 break;
960 case EON_EN25Q128:
961 *w25q_ranges = en25q128_ranges;
962 *num_entries = ARRAY_SIZE(en25q128_ranges);
963 break;
Marc Jonesb2f90022014-04-29 17:37:23 -0600964 case EON_EN25S64:
965 *w25q_ranges = en25s64_ranges;
966 *num_entries = ARRAY_SIZE(en25s64_ranges);
967 break;
David Hendricks57566ed2010-08-16 18:24:45 -0700968 default:
969 msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
970 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +0100971 flash->chip->model_id);
David Hendricks57566ed2010-08-16 18:24:45 -0700972 return -1;
973 }
974 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800975 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +0100976 switch (flash->chip->model_id) {
David Hendricksf8f00c72011-02-01 12:39:46 -0800977 case MACRONIX_MX25L1005:
978 *w25q_ranges = mx25l1005_ranges;
979 *num_entries = ARRAY_SIZE(mx25l1005_ranges);
980 break;
981 case MACRONIX_MX25L2005:
982 *w25q_ranges = mx25l2005_ranges;
983 *num_entries = ARRAY_SIZE(mx25l2005_ranges);
984 break;
985 case MACRONIX_MX25L4005:
986 *w25q_ranges = mx25l4005_ranges;
987 *num_entries = ARRAY_SIZE(mx25l4005_ranges);
988 break;
989 case MACRONIX_MX25L8005:
990 *w25q_ranges = mx25l8005_ranges;
991 *num_entries = ARRAY_SIZE(mx25l8005_ranges);
992 break;
993 case MACRONIX_MX25L1605:
994 /* FIXME: MX25L1605 and MX25L1605D have different write
995 * protection capabilities, but share IDs */
996 *w25q_ranges = mx25l1605d_ranges;
997 *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
998 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800999 case MACRONIX_MX25L3205:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001000 *w25q_ranges = mx25l3205d_ranges;
1001 *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
David Hendricksac72e362010-08-16 18:20:03 -07001002 break;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001003 case MACRONIX_MX25U3235E:
1004 *w25q_ranges = mx25u3235e_ranges;
1005 *num_entries = ARRAY_SIZE(mx25u3235e_ranges);
1006 break;
Jongpil66a96492014-08-14 17:59:06 +09001007 case MACRONIX_MX25U6435E:
1008 *w25q_ranges = mx25u6435e_ranges;
1009 *num_entries = ARRAY_SIZE(mx25u6435e_ranges);
1010 break;
Alan Greendc0792e2019-07-01 15:01:34 +10001011 case MACRONIX_MX25U12835E:
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001012 cr = mx25l_read_config_register(flash);
1013 if (cr & MX25U12835E_TB) { /* T/B == 1 */
1014 *w25q_ranges = mx25u12835e_tb1_ranges;
1015 *num_entries = ARRAY_SIZE(mx25u12835e_tb1_ranges);
1016 } else { /* T/B == 0 */
1017 *w25q_ranges = mx25u12835e_tb0_ranges;
1018 *num_entries = ARRAY_SIZE(mx25u12835e_tb0_ranges);
1019 }
Alex Lu831c6092017-11-02 23:19:34 -07001020 break;
David Hendricksac72e362010-08-16 18:20:03 -07001021 default:
1022 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
1023 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001024 flash->chip->model_id);
David Hendricksac72e362010-08-16 18:20:03 -07001025 return -1;
1026 }
1027 break;
David Hendricksbfa624b2012-07-24 12:47:59 -07001028 case ST_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001029 switch(flash->chip->model_id) {
David Hendricksbfa624b2012-07-24 12:47:59 -07001030 case ST_N25Q064__1E:
1031 case ST_N25Q064__3E:
1032 *w25q_ranges = n25q064_ranges;
1033 *num_entries = ARRAY_SIZE(n25q064_ranges);
1034 break;
1035 default:
1036 msg_cerr("%s() %d: Micron flash chip mismatch"
1037 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001038 flash->chip->model_id);
David Hendricksbfa624b2012-07-24 12:47:59 -07001039 return -1;
1040 }
1041 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001042 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001043 switch(flash->chip->model_id) {
Bryan Freed9a0051f2012-05-22 16:06:09 -07001044 case GIGADEVICE_GD25LQ32:
1045 *w25q_ranges = w25q32_ranges;
1046 *num_entries = ARRAY_SIZE(w25q32_ranges);
1047 break;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001048 case GIGADEVICE_GD25Q40:
1049 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1050 /* CMP == 1 */
1051 *w25q_ranges = gd25q40_cmp1_ranges;
1052 *num_entries = ARRAY_SIZE(gd25q40_cmp1_ranges);
1053 } else {
1054 *w25q_ranges = gd25q40_cmp0_ranges;
1055 *num_entries = ARRAY_SIZE(gd25q40_cmp0_ranges);
1056 }
1057 break;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001058 case GIGADEVICE_GD25Q64:
Marc Jonesb18734f2014-04-03 16:19:47 -06001059 case GIGADEVICE_GD25LQ64:
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001060 *w25q_ranges = gd25q64_ranges;
1061 *num_entries = ARRAY_SIZE(gd25q64_ranges);
1062 break;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001063 case GIGADEVICE_GD25Q128:
Aaron Durbin6c957d72018-08-20 09:31:01 -06001064 case GIGADEVICE_GD25LQ128CD:
Martin Roth1fd87ed2017-02-27 20:50:50 -07001065 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1066 /* CMP == 1 */
1067 *w25q_ranges = w25rq128_cmp1_ranges;
1068 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1069 } else {
1070 /* CMP == 0 */
1071 *w25q_ranges = w25rq128_cmp0_ranges;
1072 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1073 }
1074 break;
Duncan Laurie0c383552019-03-16 12:35:16 -07001075 case GIGADEVICE_GD25Q256D:
1076 *w25q_ranges = w25rq256_cmp0_ranges;
1077 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
1078 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001079 default:
1080 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
1081 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001082 flash->chip->model_id);
Bryan Freed9a0051f2012-05-22 16:06:09 -07001083 return -1;
1084 }
1085 break;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001086 case AMIC_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001087 switch(flash->chip->model_id) {
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001088 case AMIC_A25L040:
1089 *w25q_ranges = a25l040_ranges;
1090 *num_entries = ARRAY_SIZE(a25l040_ranges);
1091 break;
1092 default:
1093 msg_cerr("%s() %d: AMIC flash chip mismatch"
1094 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001095 flash->chip->model_id);
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001096 return -1;
1097 }
1098 break;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001099 case ATMEL_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001100 switch(flash->chip->model_id) {
Edward O'Callaghan1fa87e02019-05-03 02:27:24 -04001101 case ATMEL_AT25SF128A:
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001102 case ATMEL_AT25SL128A:
1103 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1104 /* CMP == 1 */
1105 *w25q_ranges = w25rq128_cmp1_ranges;
1106 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1107 } else {
1108 /* CMP == 0 */
1109 *w25q_ranges = w25rq128_cmp0_ranges;
1110 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1111 }
1112 break;
1113 default:
1114 msg_cerr("%s() %d: Atmel flash chip mismatch"
1115 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001116 flash->chip->model_id);
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001117 return -1;
1118 }
1119 break;
David Hendricksf7924d12010-06-10 21:26:44 -07001120 default:
David Hendricksd494b0a2010-08-16 16:28:50 -07001121 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01001122 __func__, flash->chip->manufacture_id);
David Hendricksf7924d12010-06-10 21:26:44 -07001123 return -1;
1124 }
1125
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001126 return 0;
1127}
1128
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001129int w25_range_to_status(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001130 unsigned int start, unsigned int len,
1131 struct w25q_status *status)
1132{
1133 struct w25q_range *w25q_ranges;
1134 int i, range_found = 0;
1135 int num_entries;
1136
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001137 if (w25_range_table(flash, &w25q_ranges, &num_entries))
1138 return -1;
1139
David Hendricksf7924d12010-06-10 21:26:44 -07001140 for (i = 0; i < num_entries; i++) {
1141 struct wp_range *r = &w25q_ranges[i].range;
1142
1143 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1144 start, len, r->start, r->len);
1145 if ((start == r->start) && (len == r->len)) {
David Hendricksd494b0a2010-08-16 16:28:50 -07001146 status->bp0 = w25q_ranges[i].bp & 1;
1147 status->bp1 = w25q_ranges[i].bp >> 1;
1148 status->bp2 = w25q_ranges[i].bp >> 2;
1149 status->tb = w25q_ranges[i].tb;
1150 status->sec = w25q_ranges[i].sec;
David Hendricksf7924d12010-06-10 21:26:44 -07001151
1152 range_found = 1;
1153 break;
1154 }
1155 }
1156
1157 if (!range_found) {
1158 msg_cerr("matching range not found\n");
1159 return -1;
1160 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001161
David Hendricksd494b0a2010-08-16 16:28:50 -07001162 return 0;
1163}
1164
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001165int w25_status_to_range(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001166 const struct w25q_status *status,
1167 unsigned int *start, unsigned int *len)
1168{
1169 struct w25q_range *w25q_ranges;
1170 int i, status_found = 0;
1171 int num_entries;
1172
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001173 if (w25_range_table(flash, &w25q_ranges, &num_entries))
1174 return -1;
1175
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001176 for (i = 0; i < num_entries; i++) {
1177 int bp;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001178 int table_bp, table_tb, table_sec;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001179
1180 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
1181 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
1182 bp, w25q_ranges[i].bp,
1183 status->tb, w25q_ranges[i].tb,
1184 status->sec, w25q_ranges[i].sec);
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001185 table_bp = w25q_ranges[i].bp;
1186 table_tb = w25q_ranges[i].tb;
1187 table_sec = w25q_ranges[i].sec;
1188 if ((bp == table_bp || table_bp == X) &&
1189 (status->tb == table_tb || table_tb == X) &&
1190 (status->sec == table_sec || table_sec == X)) {
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001191 *start = w25q_ranges[i].range.start;
1192 *len = w25q_ranges[i].range.len;
1193
1194 status_found = 1;
1195 break;
1196 }
1197 }
1198
1199 if (!status_found) {
1200 msg_cerr("matching status not found\n");
1201 return -1;
1202 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001203
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001204 return 0;
1205}
1206
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001207/* Given a [start, len], this function calls w25_range_to_status() to convert
1208 * it to flash-chip-specific range bits, then sets into status register.
1209 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001210static int w25_set_range(const struct flashctx *flash,
David Hendricksd494b0a2010-08-16 16:28:50 -07001211 unsigned int start, unsigned int len)
1212{
1213 struct w25q_status status;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001214 int tmp = 0;
1215 int expected = 0;
David Hendricksd494b0a2010-08-16 16:28:50 -07001216
1217 memset(&status, 0, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301218 tmp = do_read_status(flash);
David Hendricksd494b0a2010-08-16 16:28:50 -07001219 memcpy(&status, &tmp, 1);
1220 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1221
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001222 if (w25_range_to_status(flash, start, len, &status))
1223 return -1;
David Hendricksf7924d12010-06-10 21:26:44 -07001224
1225 msg_cdbg("status.busy: %x\n", status.busy);
1226 msg_cdbg("status.wel: %x\n", status.wel);
1227 msg_cdbg("status.bp0: %x\n", status.bp0);
1228 msg_cdbg("status.bp1: %x\n", status.bp1);
1229 msg_cdbg("status.bp2: %x\n", status.bp2);
1230 msg_cdbg("status.tb: %x\n", status.tb);
1231 msg_cdbg("status.sec: %x\n", status.sec);
1232 msg_cdbg("status.srp0: %x\n", status.srp0);
1233
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001234 memcpy(&expected, &status, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301235 do_write_status(flash, expected);
David Hendricksf7924d12010-06-10 21:26:44 -07001236
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301237 tmp = do_read_status(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001238 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001239 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) {
David Hendricksc801adb2010-12-09 16:58:56 -08001240 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001241 expected, tmp);
1242 return 1;
1243 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001244
1245 return 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001246}
1247
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001248/* Print out the current status register value with human-readable text. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001249static int w25_wp_status(const struct flashctx *flash)
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001250{
1251 struct w25q_status status;
1252 int tmp;
David Hendricksce8ded32010-10-08 11:23:38 -07001253 unsigned int start, len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001254 int ret = 0;
1255
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001256 memset(&status, 0, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301257 tmp = do_read_status(flash);
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001258 memcpy(&status, &tmp, 1);
1259 msg_cinfo("WP: status: 0x%02x\n", tmp);
1260 msg_cinfo("WP: status.srp0: %x\n", status.srp0);
1261 msg_cinfo("WP: write protect is %s.\n",
1262 status.srp0 ? "enabled" : "disabled");
1263
1264 msg_cinfo("WP: write protect range: ");
1265 if (w25_status_to_range(flash, &status, &start, &len)) {
1266 msg_cinfo("(cannot resolve the range)\n");
1267 ret = -1;
1268 } else {
1269 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1270 }
1271
1272 return ret;
1273}
1274
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001275static int w25q_large_range_to_status(const struct flashctx *flash,
1276 unsigned int start, unsigned int len,
1277 struct w25q_status_large *status)
1278{
1279 struct w25q_range *w25q_ranges;
1280 int i, range_found = 0;
1281 int num_entries;
1282
1283 if (w25_range_table(flash, &w25q_ranges, &num_entries))
1284 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001285
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001286 for (i = 0; i < num_entries; i++) {
1287 struct wp_range *r = &w25q_ranges[i].range;
1288
1289 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1290 start, len, r->start, r->len);
1291 if ((start == r->start) && (len == r->len)) {
1292 status->bp0 = w25q_ranges[i].bp & 1;
1293 status->bp1 = w25q_ranges[i].bp >> 1;
1294 status->bp2 = w25q_ranges[i].bp >> 2;
1295 status->bp3 = w25q_ranges[i].bp >> 3;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001296 /*
1297 * For MX25U12835E chip, Top/Bottom (T/B) bit is not
1298 * part of status register and in that bit position is
1299 * Quad Enable (QE)
1300 */
1301 if (flash->chip->manufacture_id != MACRONIX_ID ||
1302 flash->chip->model_id != MACRONIX_MX25U12835E)
1303 status->tb = w25q_ranges[i].tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001304
1305 range_found = 1;
1306 break;
1307 }
1308 }
1309
1310 if (!range_found) {
1311 msg_cerr("matching range not found\n");
1312 return -1;
1313 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001314
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001315 return 0;
1316}
1317
1318static int w25_large_status_to_range(const struct flashctx *flash,
1319 const struct w25q_status_large *status,
1320 unsigned int *start, unsigned int *len)
1321{
1322 struct w25q_range *w25q_ranges;
1323 int i, status_found = 0;
1324 int num_entries;
1325
1326 if (w25_range_table(flash, &w25q_ranges, &num_entries))
1327 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001328
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001329 for (i = 0; i < num_entries; i++) {
1330 int bp;
1331 int table_bp, table_tb;
1332
1333 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) |
1334 (status->bp3 << 3);
1335 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n",
1336 bp, w25q_ranges[i].bp,
1337 status->tb, w25q_ranges[i].tb);
1338 table_bp = w25q_ranges[i].bp;
1339 table_tb = w25q_ranges[i].tb;
1340 if ((bp == table_bp || table_bp == X) &&
1341 (status->tb == table_tb || table_tb == X)) {
1342 *start = w25q_ranges[i].range.start;
1343 *len = w25q_ranges[i].range.len;
1344
1345 status_found = 1;
1346 break;
1347 }
1348 }
1349
1350 if (!status_found) {
1351 msg_cerr("matching status not found\n");
1352 return -1;
1353 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001354
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001355 return 0;
1356}
1357
1358/* Given a [start, len], this function calls w25_range_to_status() to convert
1359 * it to flash-chip-specific range bits, then sets into status register.
1360 * Returns 0 if successful, -1 on error, and 1 if reading back was different.
1361 */
1362static int w25q_large_set_range(const struct flashctx *flash,
1363 unsigned int start, unsigned int len)
1364{
1365 struct w25q_status_large status;
1366 int tmp;
1367 int expected = 0;
1368
1369 memset(&status, 0, sizeof(status));
1370 tmp = do_read_status(flash);
1371 memcpy(&status, &tmp, 1);
1372 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1373
1374 if (w25q_large_range_to_status(flash, start, len, &status))
1375 return -1;
1376
1377 msg_cdbg("status.busy: %x\n", status.busy);
1378 msg_cdbg("status.wel: %x\n", status.wel);
1379 msg_cdbg("status.bp0: %x\n", status.bp0);
1380 msg_cdbg("status.bp1: %x\n", status.bp1);
1381 msg_cdbg("status.bp2: %x\n", status.bp2);
1382 msg_cdbg("status.bp3: %x\n", status.bp3);
1383 msg_cdbg("status.tb: %x\n", status.tb);
1384 msg_cdbg("status.srp0: %x\n", status.srp0);
1385
1386 memcpy(&expected, &status, sizeof(status));
1387 do_write_status(flash, expected);
1388
1389 tmp = do_read_status(flash);
1390 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001391 if ((tmp & MASK_WP_AREA_LARGE) != (expected & MASK_WP_AREA_LARGE)) {
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001392 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
1393 expected, tmp);
1394 return 1;
1395 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001396
1397 return 0;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001398}
1399
1400static int w25q_large_wp_status(const struct flashctx *flash)
1401{
1402 struct w25q_status_large sr1;
1403 struct w25q_status_2 sr2;
1404 uint8_t tmp[2];
1405 unsigned int start, len;
1406 int ret = 0;
1407
1408 memset(&sr1, 0, sizeof(sr1));
1409 tmp[0] = do_read_status(flash);
1410 memcpy(&sr1, &tmp[0], 1);
1411
1412 memset(&sr2, 0, sizeof(sr2));
1413 tmp[1] = w25q_read_status_register_2(flash);
1414 memcpy(&sr2, &tmp[1], 1);
1415
1416 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
1417 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1418 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1419 msg_cinfo("WP: write protect is %s.\n",
1420 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1421
1422 msg_cinfo("WP: write protect range: ");
1423 if (w25_large_status_to_range(flash, &sr1, &start, &len)) {
1424 msg_cinfo("(cannot resolve the range)\n");
1425 ret = -1;
1426 } else {
1427 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1428 }
1429
1430 return ret;
1431}
1432
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001433/* Set/clear the SRP0 bit in the status register. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001434static int w25_set_srp0(const struct flashctx *flash, int enable)
David Hendricksf7924d12010-06-10 21:26:44 -07001435{
1436 struct w25q_status status;
1437 int tmp = 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001438 int expected = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001439
1440 memset(&status, 0, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301441 tmp = do_read_status(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001442 /* FIXME: this is NOT endian-free copy. */
David Hendricksf7924d12010-06-10 21:26:44 -07001443 memcpy(&status, &tmp, 1);
1444 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1445
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001446 status.srp0 = enable ? 1 : 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001447 memcpy(&expected, &status, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301448 do_write_status(flash, expected);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001449
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301450 tmp = do_read_status(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001451 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
1452 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
1453 return 1;
David Hendricksf7924d12010-06-10 21:26:44 -07001454
1455 return 0;
1456}
1457
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001458static int w25_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001459 enum wp_mode wp_mode)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001460{
1461 int ret;
1462
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001463 if (wp_mode != WP_MODE_HARDWARE) {
David Hendricks1c09f802012-10-03 11:03:48 -07001464 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
1465 return 1;
1466 }
1467
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001468 ret = w25_set_srp0(flash, 1);
David Hendricksc801adb2010-12-09 16:58:56 -08001469 if (ret)
1470 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001471 return ret;
1472}
1473
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001474static int w25_disable_writeprotect(const struct flashctx *flash)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001475{
1476 int ret;
1477
1478 ret = w25_set_srp0(flash, 0);
David Hendricksc801adb2010-12-09 16:58:56 -08001479 if (ret)
1480 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001481
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001482 return ret;
1483}
1484
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001485static int w25_list_ranges(const struct flashctx *flash)
David Hendricks0f7f5382011-02-11 18:12:31 -08001486{
1487 struct w25q_range *w25q_ranges;
1488 int i, num_entries;
1489
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001490 if (w25_range_table(flash, &w25q_ranges, &num_entries))
1491 return -1;
1492
David Hendricks0f7f5382011-02-11 18:12:31 -08001493 for (i = 0; i < num_entries; i++) {
1494 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
1495 w25q_ranges[i].range.start,
1496 w25q_ranges[i].range.len);
1497 }
1498
1499 return 0;
1500}
1501
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001502static int w25q_wp_status(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001503{
1504 struct w25q_status sr1;
1505 struct w25q_status_2 sr2;
David Hendricksf1bd8802012-10-30 11:37:57 -07001506 uint8_t tmp[2];
David Hendricks1c09f802012-10-03 11:03:48 -07001507 unsigned int start, len;
1508 int ret = 0;
1509
1510 memset(&sr1, 0, sizeof(sr1));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301511 tmp[0] = do_read_status(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001512 memcpy(&sr1, &tmp[0], 1);
David Hendricks1c09f802012-10-03 11:03:48 -07001513
David Hendricksf1bd8802012-10-30 11:37:57 -07001514 memset(&sr2, 0, sizeof(sr2));
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001515 tmp[1] = w25q_read_status_register_2(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001516 memcpy(&sr2, &tmp[1], 1);
1517
1518 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
David Hendricks1c09f802012-10-03 11:03:48 -07001519 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1520 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1521 msg_cinfo("WP: write protect is %s.\n",
1522 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1523
1524 msg_cinfo("WP: write protect range: ");
1525 if (w25_status_to_range(flash, &sr1, &start, &len)) {
1526 msg_cinfo("(cannot resolve the range)\n");
1527 ret = -1;
1528 } else {
1529 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1530 }
1531
1532 return ret;
1533}
1534
1535/*
1536 * W25Q adds an optional byte to the standard WRSR opcode. If /CS is
1537 * de-asserted after the first byte, then it acts like a JEDEC-standard
1538 * WRSR command. if /CS is asserted, then the next data byte is written
1539 * into status register 2.
1540 */
1541#define W25Q_WRSR_OUTSIZE 0x03
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001542static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2)
David Hendricks1c09f802012-10-03 11:03:48 -07001543{
1544 int result;
1545 struct spi_command cmds[] = {
1546 {
1547 /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
1548 .writecnt = JEDEC_WREN_OUTSIZE,
1549 .writearr = (const unsigned char[]){ JEDEC_WREN },
1550 .readcnt = 0,
1551 .readarr = NULL,
1552 }, {
1553 .writecnt = W25Q_WRSR_OUTSIZE,
1554 .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 },
1555 .readcnt = 0,
1556 .readarr = NULL,
1557 }, {
1558 .writecnt = 0,
1559 .writearr = NULL,
1560 .readcnt = 0,
1561 .readarr = NULL,
1562 }};
1563
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001564 result = spi_send_multicommand(flash, cmds);
David Hendricks1c09f802012-10-03 11:03:48 -07001565 if (result) {
1566 msg_cerr("%s failed during command execution\n",
1567 __func__);
1568 }
1569
1570 /* WRSR performs a self-timed erase before the changes take effect. */
David Hendricks60824042014-12-11 17:22:06 -08001571 programmer_delay(100 * 1000);
David Hendricks1c09f802012-10-03 11:03:48 -07001572
1573 return result;
1574}
1575
1576/*
1577 * Set/clear the SRP1 bit in status register 2.
1578 * FIXME: make this more generic if other chips use the same SR2 layout
1579 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001580static int w25q_set_srp1(const struct flashctx *flash, int enable)
David Hendricks1c09f802012-10-03 11:03:48 -07001581{
1582 struct w25q_status sr1;
1583 struct w25q_status_2 sr2;
1584 uint8_t tmp, expected;
1585
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301586 tmp = do_read_status(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001587 memcpy(&sr1, &tmp, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001588 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001589 memcpy(&sr2, &tmp, 1);
1590
1591 msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp);
1592
1593 sr2.srp1 = enable ? 1 : 0;
1594
1595 memcpy(&expected, &sr2, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001596 w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2));
David Hendricks1c09f802012-10-03 11:03:48 -07001597
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001598 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001599 msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp);
1600 if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA))
1601 return 1;
1602
1603 return 0;
1604}
1605
1606enum wp_mode get_wp_mode(const char *mode_str)
1607{
1608 enum wp_mode wp_mode = WP_MODE_UNKNOWN;
1609
1610 if (!strcasecmp(mode_str, "hardware"))
1611 wp_mode = WP_MODE_HARDWARE;
1612 else if (!strcasecmp(mode_str, "power_cycle"))
1613 wp_mode = WP_MODE_POWER_CYCLE;
1614 else if (!strcasecmp(mode_str, "permanent"))
1615 wp_mode = WP_MODE_PERMANENT;
1616
1617 return wp_mode;
1618}
1619
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001620static int w25q_disable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001621 enum wp_mode wp_mode)
1622{
1623 int ret = 1;
David Hendricks1c09f802012-10-03 11:03:48 -07001624 struct w25q_status_2 sr2;
1625 uint8_t tmp;
1626
1627 switch (wp_mode) {
1628 case WP_MODE_HARDWARE:
1629 ret = w25_set_srp0(flash, 0);
1630 break;
1631 case WP_MODE_POWER_CYCLE:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001632 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001633 memcpy(&sr2, &tmp, 1);
1634 if (sr2.srp1) {
1635 msg_cerr("%s(): must disconnect power to disable "
1636 "write-protection\n", __func__);
1637 } else {
1638 ret = 0;
1639 }
1640 break;
1641 case WP_MODE_PERMANENT:
1642 msg_cerr("%s(): cannot disable permanent write-protection\n",
1643 __func__);
1644 break;
1645 default:
1646 msg_cerr("%s(): invalid mode specified\n", __func__);
1647 break;
1648 }
1649
1650 if (ret)
1651 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001652
David Hendricks1c09f802012-10-03 11:03:48 -07001653 return ret;
1654}
1655
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001656static int w25q_disable_writeprotect_default(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001657{
1658 return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE);
1659}
1660
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001661static int w25q_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001662 enum wp_mode wp_mode)
1663{
1664 int ret = 1;
1665 struct w25q_status sr1;
1666 struct w25q_status_2 sr2;
1667 uint8_t tmp;
1668
1669 switch (wp_mode) {
1670 case WP_MODE_HARDWARE:
1671 if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) {
1672 msg_cerr("%s(): cannot disable power cycle WP mode\n",
1673 __func__);
1674 break;
1675 }
1676
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301677 tmp = do_read_status(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001678 memcpy(&sr1, &tmp, 1);
1679 if (sr1.srp0)
1680 ret = 0;
1681 else
1682 ret = w25_set_srp0(flash, 1);
1683
1684 break;
1685 case WP_MODE_POWER_CYCLE:
1686 if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) {
1687 msg_cerr("%s(): cannot disable hardware WP mode\n",
1688 __func__);
1689 break;
1690 }
1691
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001692 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001693 memcpy(&sr2, &tmp, 1);
1694 if (sr2.srp1)
1695 ret = 0;
1696 else
1697 ret = w25q_set_srp1(flash, 1);
1698
1699 break;
1700 case WP_MODE_PERMANENT:
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301701 tmp = do_read_status(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001702 memcpy(&sr1, &tmp, 1);
1703 if (sr1.srp0 == 0) {
1704 ret = w25_set_srp0(flash, 1);
1705 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001706 msg_perr("%s(): cannot enable SRP0 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001707 "permanent WP\n", __func__);
1708 break;
1709 }
1710 }
1711
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001712 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001713 memcpy(&sr2, &tmp, 1);
1714 if (sr2.srp1 == 0) {
1715 ret = w25q_set_srp1(flash, 1);
1716 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001717 msg_perr("%s(): cannot enable SRP1 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001718 "permanent WP\n", __func__);
1719 break;
1720 }
1721 }
1722
1723 break;
David Hendricksf1bd8802012-10-30 11:37:57 -07001724 default:
1725 msg_perr("%s(): invalid mode %d\n", __func__, wp_mode);
1726 break;
David Hendricks1c09f802012-10-03 11:03:48 -07001727 }
1728
1729 if (ret)
1730 msg_cerr("%s(): error=%d.\n", __func__, ret);
1731 return ret;
1732}
1733
1734/* W25P, W25X, and many flash chips from various vendors */
David Hendricksf7924d12010-06-10 21:26:44 -07001735struct wp wp_w25 = {
David Hendricks0f7f5382011-02-11 18:12:31 -08001736 .list_ranges = w25_list_ranges,
David Hendricksf7924d12010-06-10 21:26:44 -07001737 .set_range = w25_set_range,
1738 .enable = w25_enable_writeprotect,
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001739 .disable = w25_disable_writeprotect,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001740 .wp_status = w25_wp_status,
David Hendricks1c09f802012-10-03 11:03:48 -07001741
1742};
1743
1744/* W25Q series has features such as a second status register and SFDP */
1745struct wp wp_w25q = {
1746 .list_ranges = w25_list_ranges,
1747 .set_range = w25_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_wp_status,
David Hendricksf7924d12010-06-10 21:26:44 -07001756};
David Hendrickse0512a72014-07-15 20:30:47 -07001757
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001758/* W25Q large series has 4 block-protect bits */
1759struct wp wp_w25q_large = {
1760 .list_ranges = w25_list_ranges,
1761 .set_range = w25q_large_set_range,
1762 .enable = w25q_enable_writeprotect,
1763 /*
1764 * By default, disable hardware write-protection. We may change
1765 * this later if we want to add fine-grained write-protect disable
1766 * as a command-line option.
1767 */
1768 .disable = w25q_disable_writeprotect_default,
1769 .wp_status = w25q_large_wp_status,
1770};
1771
David Hendricksaf3944a2014-07-28 18:37:40 -07001772struct generic_range gd25q32_cmp0_ranges[] = {
1773 /* none, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001774 { { }, 0x00, {0, 0} },
1775 { { }, 0x08, {0, 0} },
1776 { { }, 0x10, {0, 0} },
1777 { { }, 0x18, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001778
David Hendricks148a4bf2015-03-13 21:02:42 -07001779 { { }, 0x01, {0x3f0000, 64 * 1024} },
1780 { { }, 0x02, {0x3e0000, 128 * 1024} },
1781 { { }, 0x03, {0x3c0000, 256 * 1024} },
1782 { { }, 0x04, {0x380000, 512 * 1024} },
1783 { { }, 0x05, {0x300000, 1024 * 1024} },
1784 { { }, 0x06, {0x200000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001785
David Hendricks148a4bf2015-03-13 21:02:42 -07001786 { { }, 0x09, {0x000000, 64 * 1024} },
1787 { { }, 0x0a, {0x000000, 128 * 1024} },
1788 { { }, 0x0b, {0x000000, 256 * 1024} },
1789 { { }, 0x0c, {0x000000, 512 * 1024} },
1790 { { }, 0x0d, {0x000000, 1024 * 1024} },
1791 { { }, 0x0e, {0x000000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001792
1793 /* all, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001794 { { }, 0x07, {0x000000, 4096 * 1024} },
1795 { { }, 0x0f, {0x000000, 4096 * 1024} },
1796 { { }, 0x17, {0x000000, 4096 * 1024} },
1797 { { }, 0x1f, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001798
David Hendricks148a4bf2015-03-13 21:02:42 -07001799 { { }, 0x11, {0x3ff000, 4 * 1024} },
1800 { { }, 0x12, {0x3fe000, 8 * 1024} },
1801 { { }, 0x13, {0x3fc000, 16 * 1024} },
1802 { { }, 0x14, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1803 { { }, 0x15, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1804 { { }, 0x16, {0x3f8000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001805
David Hendricks148a4bf2015-03-13 21:02:42 -07001806 { { }, 0x19, {0x000000, 4 * 1024} },
1807 { { }, 0x1a, {0x000000, 8 * 1024} },
1808 { { }, 0x1b, {0x000000, 16 * 1024} },
1809 { { }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1810 { { }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1811 { { }, 0x1e, {0x000000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001812};
1813
1814struct generic_range gd25q32_cmp1_ranges[] = {
Martin Roth563a1fe2017-04-18 14:26:27 -06001815 /* All, bp4 and bp3 => don't care */
1816 { { }, 0x00, {0x000000, 4096 * 1024} }, /* All */
1817 { { }, 0x08, {0x000000, 4096 * 1024} },
1818 { { }, 0x10, {0x000000, 4096 * 1024} },
1819 { { }, 0x18, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001820
David Hendricks148a4bf2015-03-13 21:02:42 -07001821 { { }, 0x01, {0x000000, 4032 * 1024} },
1822 { { }, 0x02, {0x000000, 3968 * 1024} },
1823 { { }, 0x03, {0x000000, 3840 * 1024} },
1824 { { }, 0x04, {0x000000, 3584 * 1024} },
1825 { { }, 0x05, {0x000000, 3 * 1024 * 1024} },
1826 { { }, 0x06, {0x000000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001827
David Hendricks148a4bf2015-03-13 21:02:42 -07001828 { { }, 0x09, {0x010000, 4032 * 1024} },
1829 { { }, 0x0a, {0x020000, 3968 * 1024} },
1830 { { }, 0x0b, {0x040000, 3840 * 1024} },
1831 { { }, 0x0c, {0x080000, 3584 * 1024} },
1832 { { }, 0x0d, {0x100000, 3 * 1024 * 1024} },
1833 { { }, 0x0e, {0x200000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001834
Martin Roth563a1fe2017-04-18 14:26:27 -06001835 /* None, bp4 and bp3 => don't care */
1836 { { }, 0x07, {0, 0} }, /* None */
1837 { { }, 0x0f, {0, 0} },
1838 { { }, 0x17, {0, 0} },
1839 { { }, 0x1f, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001840
David Hendricks148a4bf2015-03-13 21:02:42 -07001841 { { }, 0x11, {0x000000, 4092 * 1024} },
1842 { { }, 0x12, {0x000000, 4088 * 1024} },
1843 { { }, 0x13, {0x000000, 4080 * 1024} },
1844 { { }, 0x14, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
1845 { { }, 0x15, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
1846 { { }, 0x16, {0x000000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001847
David Hendricks148a4bf2015-03-13 21:02:42 -07001848 { { }, 0x19, {0x001000, 4092 * 1024} },
1849 { { }, 0x1a, {0x002000, 4088 * 1024} },
1850 { { }, 0x1b, {0x040000, 4080 * 1024} },
1851 { { }, 0x1c, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
1852 { { }, 0x1d, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
1853 { { }, 0x1e, {0x080000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001854};
1855
1856static struct generic_wp gd25q32_wp = {
1857 /* TODO: map second status register */
1858 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
1859};
1860
David Hendricks1e9d7ca2016-03-14 15:50:34 -07001861struct generic_range gd25q128_cmp0_ranges[] = {
1862 /* none, bp4 and bp3 => don't care, others = 0 */
1863 { { .tb = 0 }, 0x00, {0, 0} },
1864 { { .tb = 0 }, 0x08, {0, 0} },
1865 { { .tb = 0 }, 0x10, {0, 0} },
1866 { { .tb = 0 }, 0x18, {0, 0} },
1867
1868 { { .tb = 0 }, 0x01, {0xfc0000, 256 * 1024} },
1869 { { .tb = 0 }, 0x02, {0xf80000, 512 * 1024} },
1870 { { .tb = 0 }, 0x03, {0xf00000, 1024 * 1024} },
1871 { { .tb = 0 }, 0x04, {0xe00000, 2048 * 1024} },
1872 { { .tb = 0 }, 0x05, {0xc00000, 4096 * 1024} },
1873 { { .tb = 0 }, 0x06, {0x800000, 8192 * 1024} },
1874
1875 { { .tb = 0 }, 0x09, {0x000000, 256 * 1024} },
1876 { { .tb = 0 }, 0x0a, {0x000000, 512 * 1024} },
1877 { { .tb = 0 }, 0x0b, {0x000000, 1024 * 1024} },
1878 { { .tb = 0 }, 0x0c, {0x000000, 2048 * 1024} },
1879 { { .tb = 0 }, 0x0d, {0x000000, 4096 * 1024} },
1880 { { .tb = 0 }, 0x0e, {0x000000, 8192 * 1024} },
1881
1882 /* all, bp4 and bp3 => don't care, others = 1 */
1883 { { .tb = 0 }, 0x07, {0x000000, 16384 * 1024} },
1884 { { .tb = 0 }, 0x0f, {0x000000, 16384 * 1024} },
1885 { { .tb = 0 }, 0x17, {0x000000, 16384 * 1024} },
1886 { { .tb = 0 }, 0x1f, {0x000000, 16384 * 1024} },
1887
1888 { { .tb = 0 }, 0x11, {0xfff000, 4 * 1024} },
1889 { { .tb = 0 }, 0x12, {0xffe000, 8 * 1024} },
1890 { { .tb = 0 }, 0x13, {0xffc000, 16 * 1024} },
1891 { { .tb = 0 }, 0x14, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
1892 { { .tb = 0 }, 0x15, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
1893
1894 { { .tb = 0 }, 0x19, {0x000000, 4 * 1024} },
1895 { { .tb = 0 }, 0x1a, {0x000000, 8 * 1024} },
1896 { { .tb = 0 }, 0x1b, {0x000000, 16 * 1024} },
1897 { { .tb = 0 }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1898 { { .tb = 0 }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1899 { { .tb = 0 }, 0x1e, {0x000000, 32 * 1024} },
1900};
1901
1902struct generic_range gd25q128_cmp1_ranges[] = {
1903 /* none, bp4 and bp3 => don't care, others = 0 */
1904 { { .tb = 1 }, 0x00, {0x000000, 16384 * 1024} },
1905 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
1906 { { .tb = 1 }, 0x10, {0x000000, 16384 * 1024} },
1907 { { .tb = 1 }, 0x18, {0x000000, 16384 * 1024} },
1908
1909 { { .tb = 1 }, 0x01, {0x000000, 16128 * 1024} },
1910 { { .tb = 1 }, 0x02, {0x000000, 15872 * 1024} },
1911 { { .tb = 1 }, 0x03, {0x000000, 15360 * 1024} },
1912 { { .tb = 1 }, 0x04, {0x000000, 14336 * 1024} },
1913 { { .tb = 1 }, 0x05, {0x000000, 12288 * 1024} },
1914 { { .tb = 1 }, 0x06, {0x000000, 8192 * 1024} },
1915
1916 { { .tb = 1 }, 0x09, {0x000000, 16128 * 1024} },
1917 { { .tb = 1 }, 0x0a, {0x000000, 15872 * 1024} },
1918 { { .tb = 1 }, 0x0b, {0x000000, 15360 * 1024} },
1919 { { .tb = 1 }, 0x0c, {0x000000, 14336 * 1024} },
1920 { { .tb = 1 }, 0x0d, {0x000000, 12288 * 1024} },
1921 { { .tb = 1 }, 0x0e, {0x000000, 8192 * 1024} },
1922
1923 /* none, bp4 and bp3 => don't care, others = 1 */
1924 { { .tb = 1 }, 0x07, {0x000000, 16384 * 1024} },
1925 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
1926 { { .tb = 1 }, 0x0f, {0x000000, 16384 * 1024} },
1927 { { .tb = 1 }, 0x17, {0x000000, 16384 * 1024} },
1928 { { .tb = 1 }, 0x1f, {0x000000, 16384 * 1024} },
1929
1930 { { .tb = 1 }, 0x11, {0x000000, 16380 * 1024} },
1931 { { .tb = 1 }, 0x12, {0x000000, 16376 * 1024} },
1932 { { .tb = 1 }, 0x13, {0x000000, 16368 * 1024} },
1933 { { .tb = 1 }, 0x14, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
1934 { { .tb = 1 }, 0x15, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
1935
1936 { { .tb = 1 }, 0x19, {0x001000, 16380 * 1024} },
1937 { { .tb = 1 }, 0x1a, {0x002000, 16376 * 1024} },
1938 { { .tb = 1 }, 0x1b, {0x004000, 16368 * 1024} },
1939 { { .tb = 1 }, 0x1c, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
1940 { { .tb = 1 }, 0x1d, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
1941 { { .tb = 1 }, 0x1e, {0x008000, 16352 * 1024} },
1942};
1943
1944static struct generic_wp gd25q128_wp = {
1945 /* TODO: map second and third status registers */
1946 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
1947};
1948
David Hendricks83541d32014-07-15 20:58:21 -07001949/* FIXME: MX25L6406 has same ID as MX25L6405D */
1950struct generic_range mx25l6406e_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07001951 { { }, 0, {0, 0} }, /* none */
1952 { { }, 0x1, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
1953 { { }, 0x2, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
1954 { { }, 0x3, {0x7a0000, 64 * 8 * 1024} }, /* blocks 120-127 */
1955 { { }, 0x4, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
1956 { { }, 0x5, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
1957 { { }, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricks83541d32014-07-15 20:58:21 -07001958
David Hendricks148a4bf2015-03-13 21:02:42 -07001959 { { }, 0x7, {0x000000, 64 * 128 * 1024} }, /* all */
1960 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
1961 { { }, 0x9, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
1962 { { }, 0xa, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
1963 { { }, 0xb, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
1964 { { }, 0xc, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
1965 { { }, 0xd, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
1966 { { }, 0xe, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
1967 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricks83541d32014-07-15 20:58:21 -07001968};
1969
1970static struct generic_wp mx25l6406e_wp = {
1971 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
1972 .ranges = &mx25l6406e_ranges[0],
1973};
David Hendrickse0512a72014-07-15 20:30:47 -07001974
David Hendricksc3496092014-11-13 17:20:55 -08001975struct generic_range mx25l6495f_tb0_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07001976 { { }, 0, {0, 0} }, /* none */
1977 { { }, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
1978 { { }, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
1979 { { }, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
David Hendricksc3496092014-11-13 17:20:55 -08001980
David Hendricks148a4bf2015-03-13 21:02:42 -07001981 { { }, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
1982 { { }, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
1983 { { }, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
1984 { { }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
1985 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
1986 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
1987 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
1988 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
1989 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
1990 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
1991 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
1992 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08001993};
1994
1995struct generic_range mx25l6495f_tb1_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07001996 { { }, 0, {0, 0} }, /* none */
1997 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
1998 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
1999 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2000 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2001 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2002 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2003 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2004 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2005 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2006 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2007 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2008 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2009 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2010 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2011 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002012};
2013
2014static struct generic_wp mx25l6495f_wp = {
2015 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2016};
2017
Vic Yang848bfd12018-03-23 10:24:07 -07002018struct generic_range mx25l25635f_tb0_ranges[] = {
2019 { { }, 0, {0, 0} }, /* none */
2020 { { }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* block 511 */
2021 { { }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* blocks 510-511 */
2022 { { }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* blocks 508-511 */
2023 { { }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* blocks 504-511 */
2024 { { }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* blocks 496-511 */
2025 { { }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* blocks 480-511 */
2026 { { }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* blocks 448-511 */
2027 { { }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* blocks 384-511 */
2028 { { }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* blocks 256-511 */
2029 { { }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* all */
2030 { { }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* all */
2031 { { }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* all */
2032 { { }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* all */
2033 { { }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* all */
2034 { { }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* all */
2035};
2036
2037struct generic_range mx25l25635f_tb1_ranges[] = {
2038 { { }, 0, {0, 0} }, /* none */
2039 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2040 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2041 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2042 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2043 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2044 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2045 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2046 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
2047 { { }, 0x9, {0x000000, 64 * 256 * 1024} }, /* blocks 0-255 */
2048 { { }, 0xa, {0x000000, 64 * 512 * 1024} }, /* all */
2049 { { }, 0xb, {0x000000, 64 * 512 * 1024} }, /* all */
2050 { { }, 0xc, {0x000000, 64 * 512 * 1024} }, /* all */
2051 { { }, 0xd, {0x000000, 64 * 512 * 1024} }, /* all */
2052 { { }, 0xe, {0x000000, 64 * 512 * 1024} }, /* all */
2053 { { }, 0xf, {0x000000, 64 * 512 * 1024} }, /* all */
2054};
2055
2056static struct generic_wp mx25l25635f_wp = {
2057 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2058};
2059
David Hendricks148a4bf2015-03-13 21:02:42 -07002060struct generic_range s25fs128s_ranges[] = {
2061 { { .tb = 1 }, 0, {0, 0} }, /* none */
2062 { { .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* lower 64th */
2063 { { .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* lower 32nd */
2064 { { .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* lower 16th */
2065 { { .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* lower 8th */
2066 { { .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* lower 4th */
2067 { { .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* lower half */
2068 { { .tb = 1 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002069
David Hendricks148a4bf2015-03-13 21:02:42 -07002070 { { .tb = 0 }, 0, {0, 0} }, /* none */
2071 { { .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* upper 64th */
2072 { { .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* upper 32nd */
2073 { { .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* upper 16th */
2074 { { .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* upper 8th */
2075 { { .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* upper 4th */
2076 { { .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* upper half */
2077 { { .tb = 0 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002078};
2079
2080static struct generic_wp s25fs128s_wp = {
2081 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
David Hendricks148a4bf2015-03-13 21:02:42 -07002082 .get_modifier_bits = s25f_get_modifier_bits,
2083 .set_modifier_bits = s25f_set_modifier_bits,
David Hendricksa9884852014-12-11 15:31:12 -08002084};
2085
David Hendricksc694bb82015-02-25 14:52:17 -08002086
David Hendricks148a4bf2015-03-13 21:02:42 -07002087struct generic_range s25fl256s_ranges[] = {
2088 { { .tb = 1 }, 0, {0, 0} }, /* none */
2089 { { .tb = 1 }, 0x1, {0x000000, 512 * 1024} }, /* lower 64th */
2090 { { .tb = 1 }, 0x2, {0x000000, 1024 * 1024} }, /* lower 32nd */
2091 { { .tb = 1 }, 0x3, {0x000000, 2048 * 1024} }, /* lower 16th */
2092 { { .tb = 1 }, 0x4, {0x000000, 4096 * 1024} }, /* lower 8th */
2093 { { .tb = 1 }, 0x5, {0x000000, 8192 * 1024} }, /* lower 4th */
2094 { { .tb = 1 }, 0x6, {0x000000, 16384 * 1024} }, /* lower half */
2095 { { .tb = 1 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
2096
2097 { { .tb = 0 }, 0, {0, 0} }, /* none */
2098 { { .tb = 0 }, 0x1, {0x1f80000, 512 * 1024} }, /* upper 64th */
2099 { { .tb = 0 }, 0x2, {0x1f00000, 1024 * 1024} }, /* upper 32nd */
2100 { { .tb = 0 }, 0x3, {0x1e00000, 2048 * 1024} }, /* upper 16th */
2101 { { .tb = 0 }, 0x4, {0x1c00000, 4096 * 1024} }, /* upper 8th */
2102 { { .tb = 0 }, 0x5, {0x1800000, 8192 * 1024} }, /* upper 4th */
2103 { { .tb = 0 }, 0x6, {0x1000000, 16384 * 1024} }, /* upper half */
2104 { { .tb = 0 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
David Hendricksc694bb82015-02-25 14:52:17 -08002105};
2106
2107static struct generic_wp s25fl256s_wp = {
2108 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
David Hendricks148a4bf2015-03-13 21:02:42 -07002109 .get_modifier_bits = s25f_get_modifier_bits,
2110 .set_modifier_bits = s25f_set_modifier_bits,
David Hendricksc694bb82015-02-25 14:52:17 -08002111};
2112
David Hendrickse0512a72014-07-15 20:30:47 -07002113/* Given a flash chip, this function returns its writeprotect info. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002114static int generic_range_table(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002115 struct generic_wp **wp,
2116 int *num_entries)
2117{
2118 *wp = NULL;
2119 *num_entries = 0;
2120
Patrick Georgif3fa2992017-02-02 16:24:44 +01002121 switch (flash->chip->manufacture_id) {
David Hendricksaf3944a2014-07-28 18:37:40 -07002122 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002123 switch(flash->chip->model_id) {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002124
Martin Roth563a1fe2017-04-18 14:26:27 -06002125 case GIGADEVICE_GD25LQ32:
David Hendricksaf3944a2014-07-28 18:37:40 -07002126 case GIGADEVICE_GD25Q32: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002127 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricksaf3944a2014-07-28 18:37:40 -07002128 *wp = &gd25q32_wp;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002129
David Hendricksaf3944a2014-07-28 18:37:40 -07002130 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
2131 (*wp)->ranges = &gd25q32_cmp0_ranges[0];
2132 *num_entries = ARRAY_SIZE(gd25q32_cmp0_ranges);
2133 } else { /* CMP == 1 */
2134 (*wp)->ranges = &gd25q32_cmp1_ranges[0];
2135 *num_entries = ARRAY_SIZE(gd25q32_cmp1_ranges);
2136 }
2137
2138 break;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002139 }
Furquan Shaikh62cd8102016-07-17 23:04:06 -07002140 case GIGADEVICE_GD25Q128:
Aaron Durbin6c957d72018-08-20 09:31:01 -06002141 case GIGADEVICE_GD25LQ128CD: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002142 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002143 *wp = &gd25q128_wp;
2144
2145 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
2146 (*wp)->ranges = &gd25q128_cmp0_ranges[0];
2147 *num_entries = ARRAY_SIZE(gd25q128_cmp0_ranges);
2148 } else { /* CMP == 1 */
2149 (*wp)->ranges = &gd25q128_cmp1_ranges[0];
2150 *num_entries = ARRAY_SIZE(gd25q128_cmp1_ranges);
2151 }
2152
2153 break;
David Hendricksaf3944a2014-07-28 18:37:40 -07002154 }
2155 default:
2156 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
2157 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002158 flash->chip->model_id);
David Hendricksaf3944a2014-07-28 18:37:40 -07002159 return -1;
2160 }
2161 break;
David Hendricks83541d32014-07-15 20:58:21 -07002162 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002163 switch (flash->chip->model_id) {
David Hendricks83541d32014-07-15 20:58:21 -07002164 case MACRONIX_MX25L6405:
2165 /* FIXME: MX25L64* chips have mixed capabilities and
2166 share IDs */
2167 *wp = &mx25l6406e_wp;
2168 *num_entries = ARRAY_SIZE(mx25l6406e_ranges);
2169 break;
David Hendricksc3496092014-11-13 17:20:55 -08002170 case MACRONIX_MX25L6495F: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002171 uint8_t cr = mx25l_read_config_register(flash);
David Hendricksc3496092014-11-13 17:20:55 -08002172
2173 *wp = &mx25l6495f_wp;
2174 if (!(cr & (1 << 3))) { /* T/B == 0 */
2175 (*wp)->ranges = &mx25l6495f_tb0_ranges[0];
2176 *num_entries = ARRAY_SIZE(mx25l6495f_tb0_ranges);
2177 } else { /* T/B == 1 */
2178 (*wp)->ranges = &mx25l6495f_tb1_ranges[0];
2179 *num_entries = ARRAY_SIZE(mx25l6495f_tb1_ranges);
2180 }
2181 break;
2182 }
Vic Yang848bfd12018-03-23 10:24:07 -07002183 case MACRONIX_MX25L25635F: {
2184 uint8_t cr = mx25l_read_config_register(flash);
2185
2186 *wp = &mx25l25635f_wp;
2187 if (!(cr & (1 << 3))) { /* T/B == 0 */
2188 (*wp)->ranges = &mx25l25635f_tb0_ranges[0];
2189 *num_entries = ARRAY_SIZE(mx25l25635f_tb0_ranges);
2190 } else { /* T/B == 1 */
2191 (*wp)->ranges = &mx25l25635f_tb1_ranges[0];
2192 *num_entries = ARRAY_SIZE(mx25l25635f_tb1_ranges);
2193 }
2194 break;
2195 }
David Hendricks83541d32014-07-15 20:58:21 -07002196 default:
2197 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
2198 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002199 flash->chip->model_id);
David Hendricks83541d32014-07-15 20:58:21 -07002200 return -1;
2201 }
2202 break;
David Hendricksa9884852014-12-11 15:31:12 -08002203 case SPANSION_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002204 switch (flash->chip->model_id) {
David Hendricksa9884852014-12-11 15:31:12 -08002205 case SPANSION_S25FS128S_L:
2206 case SPANSION_S25FS128S_S: {
David Hendricksa9884852014-12-11 15:31:12 -08002207 *wp = &s25fs128s_wp;
David Hendricks148a4bf2015-03-13 21:02:42 -07002208 (*wp)->ranges = s25fs128s_ranges;
2209 *num_entries = ARRAY_SIZE(s25fs128s_ranges);
David Hendricksa9884852014-12-11 15:31:12 -08002210 break;
2211 }
David Hendricksc694bb82015-02-25 14:52:17 -08002212 case SPANSION_S25FL256S_UL:
2213 case SPANSION_S25FL256S_US: {
David Hendricksc694bb82015-02-25 14:52:17 -08002214 *wp = &s25fl256s_wp;
David Hendricks148a4bf2015-03-13 21:02:42 -07002215 (*wp)->ranges = s25fl256s_ranges;
2216 *num_entries = ARRAY_SIZE(s25fl256s_ranges);
David Hendricksc694bb82015-02-25 14:52:17 -08002217 break;
2218 }
David Hendricksa9884852014-12-11 15:31:12 -08002219 default:
2220 msg_cerr("%s():%d Spansion flash chip mismatch (0x%04x)"
Patrick Georgif3fa2992017-02-02 16:24:44 +01002221 ", aborting\n", __func__, __LINE__,
2222 flash->chip->model_id);
David Hendricksa9884852014-12-11 15:31:12 -08002223 return -1;
2224 }
2225 break;
David Hendrickse0512a72014-07-15 20:30:47 -07002226 default:
2227 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01002228 __func__, flash->chip->manufacture_id);
David Hendrickse0512a72014-07-15 20:30:47 -07002229 return -1;
2230 }
2231
2232 return 0;
2233}
2234
Marco Chen9d5bddb2020-02-11 17:12:56 +08002235static uint8_t generic_get_bp_mask(struct generic_wp *wp)
2236{
2237 return ((1 << (wp->sr1.bp0_pos + wp->sr1.bp_bits)) - 1) ^ \
2238 ((1 << wp->sr1.bp0_pos) - 1);
2239}
2240
2241static uint8_t generic_get_status_check_mask(struct generic_wp *wp)
2242{
2243 return generic_get_bp_mask(wp) | 1 << wp->sr1.srp_pos;
2244}
2245
David Hendrickse0512a72014-07-15 20:30:47 -07002246/* Given a [start, len], this function finds a block protect bit combination
2247 * (if possible) and sets the corresponding bits in "status". Remaining bits
2248 * are preserved. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002249static int generic_range_to_status(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002250 unsigned int start, unsigned int len,
Marco Chen9d5bddb2020-02-11 17:12:56 +08002251 uint8_t *status, uint8_t *check_mask)
David Hendrickse0512a72014-07-15 20:30:47 -07002252{
2253 struct generic_wp *wp;
2254 struct generic_range *r;
2255 int i, range_found = 0, num_entries;
2256 uint8_t bp_mask;
2257
2258 if (generic_range_table(flash, &wp, &num_entries))
2259 return -1;
2260
Marco Chen9d5bddb2020-02-11 17:12:56 +08002261 bp_mask = generic_get_bp_mask(wp);
David Hendrickse0512a72014-07-15 20:30:47 -07002262
2263 for (i = 0, r = &wp->ranges[0]; i < num_entries; i++, r++) {
2264 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
2265 start, len, r->range.start, r->range.len);
2266 if ((start == r->range.start) && (len == r->range.len)) {
2267 *status &= ~(bp_mask);
2268 *status |= r->bp << (wp->sr1.bp0_pos);
David Hendricks148a4bf2015-03-13 21:02:42 -07002269
2270 if (wp->set_modifier_bits) {
2271 if (wp->set_modifier_bits(flash, &r->m) < 0) {
2272 msg_cerr("error setting modifier "
2273 "bits for range.\n");
2274 return -1;
2275 }
2276 }
2277
David Hendrickse0512a72014-07-15 20:30:47 -07002278 range_found = 1;
2279 break;
2280 }
2281 }
2282
2283 if (!range_found) {
2284 msg_cerr("matching range not found\n");
2285 return -1;
2286 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002287
Marco Chen9d5bddb2020-02-11 17:12:56 +08002288 *check_mask = generic_get_status_check_mask(wp);
David Hendrickse0512a72014-07-15 20:30:47 -07002289 return 0;
2290}
2291
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002292static int generic_status_to_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002293 const uint8_t sr1, unsigned int *start, unsigned int *len)
2294{
2295 struct generic_wp *wp;
2296 struct generic_range *r;
Duncan Laurie04ca1172015-03-12 09:25:34 -07002297 int num_entries, i, status_found = 0;
David Hendrickse0512a72014-07-15 20:30:47 -07002298 uint8_t sr1_bp;
David Hendricks148a4bf2015-03-13 21:02:42 -07002299 struct generic_modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -07002300
2301 if (generic_range_table(flash, &wp, &num_entries))
2302 return -1;
2303
David Hendricks148a4bf2015-03-13 21:02:42 -07002304 /* modifier bits may be compared more than once, so get them here */
Edward O'Callaghanadcc7782019-12-04 14:50:14 +11002305 if (wp->get_modifier_bits && wp->get_modifier_bits(flash, &m) < 0)
David Hendricks148a4bf2015-03-13 21:02:42 -07002306 return -1;
David Hendricks148a4bf2015-03-13 21:02:42 -07002307
David Hendrickse0512a72014-07-15 20:30:47 -07002308 sr1_bp = (sr1 >> wp->sr1.bp0_pos) & ((1 << wp->sr1.bp_bits) - 1);
2309
2310 for (i = 0, r = &wp->ranges[0]; i < num_entries; i++, r++) {
David Hendricks148a4bf2015-03-13 21:02:42 -07002311 if (wp->get_modifier_bits) {
2312 if (memcmp(&m, &r->m, sizeof(m)))
2313 continue;
2314 }
David Hendrickse0512a72014-07-15 20:30:47 -07002315 msg_cspew("comparing 0x%02x 0x%02x\n", sr1_bp, r->bp);
2316 if (sr1_bp == r->bp) {
2317 *start = r->range.start;
2318 *len = r->range.len;
2319 status_found = 1;
2320 break;
2321 }
2322 }
2323
2324 if (!status_found) {
2325 msg_cerr("matching status not found\n");
2326 return -1;
2327 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002328
David Hendrickse0512a72014-07-15 20:30:47 -07002329 return 0;
2330}
2331
2332/* Given a [start, len], this function calls generic_range_to_status() to
2333 * convert it to flash-chip-specific range bits, then sets into status register.
2334 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002335static int generic_set_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002336 unsigned int start, unsigned int len)
2337{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002338 uint8_t status, expected, check_mask;
David Hendrickse0512a72014-07-15 20:30:47 -07002339
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302340 status = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002341 msg_cdbg("%s: old status: 0x%02x\n", __func__, status);
2342
2343 expected = status; /* preserve non-bp bits */
Marco Chen9d5bddb2020-02-11 17:12:56 +08002344 if (generic_range_to_status(flash, start, len, &expected, &check_mask))
David Hendrickse0512a72014-07-15 20:30:47 -07002345 return -1;
2346
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302347 do_write_status(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002348
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302349 status = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002350 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002351 if ((status & check_mask) != (expected & check_mask)) {
2352 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2353 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002354 return 1;
2355 }
David Hendrickse0512a72014-07-15 20:30:47 -07002356 return 0;
2357}
2358
2359/* Set/clear the status regsiter write protect bit in SR1. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002360static int generic_set_srp0(const struct flashctx *flash, int enable)
David Hendrickse0512a72014-07-15 20:30:47 -07002361{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002362 uint8_t status, expected, check_mask;
David Hendrickse0512a72014-07-15 20:30:47 -07002363 struct generic_wp *wp;
2364 int num_entries;
2365
2366 if (generic_range_table(flash, &wp, &num_entries))
2367 return -1;
2368
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302369 expected = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002370 msg_cdbg("%s: old status: 0x%02x\n", __func__, expected);
2371
2372 if (enable)
2373 expected |= 1 << wp->sr1.srp_pos;
2374 else
2375 expected &= ~(1 << wp->sr1.srp_pos);
2376
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302377 do_write_status(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002378
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302379 status = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002380 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002381
2382 check_mask = generic_get_status_check_mask(wp);
2383 msg_cdbg("%s: check mask: 0x%02x\n", __func__, check_mask);
2384 if ((status & check_mask) != (expected & check_mask)) {
2385 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2386 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002387 return -1;
Marco Chen9d5bddb2020-02-11 17:12:56 +08002388 }
David Hendrickse0512a72014-07-15 20:30:47 -07002389
2390 return 0;
2391}
2392
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002393static int generic_enable_writeprotect(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002394 enum wp_mode wp_mode)
2395{
2396 int ret;
2397
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002398 if (wp_mode != WP_MODE_HARDWARE) {
David Hendrickse0512a72014-07-15 20:30:47 -07002399 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
2400 return 1;
2401 }
2402
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002403 ret = generic_set_srp0(flash, 1);
David Hendrickse0512a72014-07-15 20:30:47 -07002404 if (ret)
2405 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002406
David Hendrickse0512a72014-07-15 20:30:47 -07002407 return ret;
2408}
2409
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002410static int generic_disable_writeprotect(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002411{
2412 int ret;
2413
2414 ret = generic_set_srp0(flash, 0);
2415 if (ret)
2416 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002417
David Hendrickse0512a72014-07-15 20:30:47 -07002418 return ret;
2419}
2420
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002421static int generic_list_ranges(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002422{
2423 struct generic_wp *wp;
2424 struct generic_range *r;
2425 int i, num_entries;
2426
2427 if (generic_range_table(flash, &wp, &num_entries))
2428 return -1;
2429
2430 r = &wp->ranges[0];
2431 for (i = 0; i < num_entries; i++) {
2432 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
2433 r->range.start, r->range.len);
2434 r++;
2435 }
2436
2437 return 0;
2438}
2439
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002440static int generic_wp_status(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002441{
2442 uint8_t sr1;
2443 unsigned int start, len;
2444 int ret = 0;
2445 struct generic_wp *wp;
David Hendrickse0512a72014-07-15 20:30:47 -07002446 int num_entries, wp_en;
2447
2448 if (generic_range_table(flash, &wp, &num_entries))
2449 return -1;
2450
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302451 sr1 = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002452 wp_en = (sr1 >> wp->sr1.srp_pos) & 1;
2453
2454 msg_cinfo("WP: status: 0x%04x\n", sr1);
2455 msg_cinfo("WP: status.srp0: %x\n", wp_en);
2456 /* FIXME: SRP1 is not really generic, but we probably should print
2457 * it anyway to have consistent output. #legacycruft */
2458 msg_cinfo("WP: status.srp1: %x\n", 0);
2459 msg_cinfo("WP: write protect is %s.\n",
2460 wp_en ? "enabled" : "disabled");
2461
2462 msg_cinfo("WP: write protect range: ");
2463 if (generic_status_to_range(flash, sr1, &start, &len)) {
2464 msg_cinfo("(cannot resolve the range)\n");
2465 ret = -1;
2466 } else {
2467 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
2468 }
2469
2470 return ret;
2471}
2472
2473struct wp wp_generic = {
2474 .list_ranges = generic_list_ranges,
2475 .set_range = generic_set_range,
2476 .enable = generic_enable_writeprotect,
2477 .disable = generic_disable_writeprotect,
2478 .wp_status = generic_wp_status,
2479};