blob: 81927b49c04fc090fd588ce87e9d7b49d852ca3f [file] [log] [blame]
David Hendricksd1c55d72010-08-24 15:14:19 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2010 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
David Hendricksd1c55d72010-08-24 15:14:19 -070016 */
17
David Hendricksf7924d12010-06-10 21:26:44 -070018#include <stdlib.h>
19#include <string.h>
Edward O'Callaghanb4300ca2019-09-03 16:15:21 +100020#include <strings.h>
David Hendricksf7924d12010-06-10 21:26:44 -070021
22#include "flash.h"
23#include "flashchips.h"
24#include "chipdrivers.h"
Louis Yung-Chieh Lo52aa9302010-09-06 10:45:02 +080025#include "spi.h"
David Hendricks23cd7782010-08-25 12:42:38 -070026#include "writeprotect.h"
David Hendricksf7924d12010-06-10 21:26:44 -070027
David Hendricks1c09f802012-10-03 11:03:48 -070028/*
David Hendricksf7924d12010-06-10 21:26:44 -070029 * The following procedures rely on look-up tables to match the user-specified
30 * range with the chip's supported ranges. This turned out to be the most
31 * elegant approach since diferent flash chips use different levels of
32 * granularity and methods to determine protected ranges. In other words,
David Hendrickse0512a72014-07-15 20:30:47 -070033 * be stupid and simple since clever arithmetic will not work for many chips.
David Hendricksf7924d12010-06-10 21:26:44 -070034 */
35
36struct wp_range {
37 unsigned int start; /* starting address */
38 unsigned int len; /* len */
39};
40
41enum bit_state {
42 OFF = 0,
43 ON = 1,
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +080044 X = -1 /* don't care. Must be bigger than max # of bp. */
David Hendricksf7924d12010-06-10 21:26:44 -070045};
46
David Hendrickse0512a72014-07-15 20:30:47 -070047/*
48 * Generic write-protection schema for 25-series SPI flash chips. This assumes
49 * there is a status register that contains one or more consecutive bits which
50 * determine which address range is protected.
51 */
52
53struct status_register_layout {
54 int bp0_pos; /* position of BP0 */
55 int bp_bits; /* number of block protect bits */
56 int srp_pos; /* position of status register protect enable bit */
57};
58
Edward O'Callaghan91b38272019-12-04 17:12:43 +110059/*
60 * The following ranges and functions are useful for representing the
61 * writeprotect schema in which there are typically 5 bits of
62 * relevant information stored in status register 1:
63 * m.sec: This bit indicates the units (sectors vs. blocks)
64 * m.tb: The top-bottom bit indicates if the affected range is at the top of
65 * the flash memory's address space or at the bottom.
66 * bp: Bitmask representing the number of affected sectors/blocks.
67 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +110068struct wp_range_descriptor {
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +110069 struct modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -070070 unsigned int bp; /* block protect bitfield */
71 struct wp_range range;
72};
73
Edward O'Callaghana3edcb22019-12-05 14:30:50 +110074struct wp_context {
David Hendrickse0512a72014-07-15 20:30:47 -070075 struct status_register_layout sr1; /* status register 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +110076 struct wp_range_descriptor *descrs;
David Hendricks148a4bf2015-03-13 21:02:42 -070077
78 /*
79 * Some chips store modifier bits in one or more special control
80 * registers instead of the status register like many older SPI NOR
81 * flash chips did. get_modifier_bits() and set_modifier_bits() will do
82 * any chip-specific operations necessary to get/set these bit values.
83 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -070084 int (*get_modifier_bits)(const struct flashctx *flash,
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +110085 struct modifier_bits *m);
Souvik Ghoshd75cd672016-06-17 14:21:39 -070086 int (*set_modifier_bits)(const struct flashctx *flash,
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +110087 struct modifier_bits *m);
David Hendrickse0512a72014-07-15 20:30:47 -070088};
89
Edward O'Callaghanc69f6b82019-12-05 16:49:21 +110090struct w25q_status {
91 /* this maps to register layout -- do not change ordering */
92 unsigned char busy : 1;
93 unsigned char wel : 1;
94 unsigned char bp0 : 1;
95 unsigned char bp1 : 1;
96 unsigned char bp2 : 1;
97 unsigned char tb : 1;
98 unsigned char sec : 1;
99 unsigned char srp0 : 1;
100} __attribute__ ((packed));
101
102/* Status register for large flash layouts with 4 BP bits */
103struct w25q_status_large {
104 unsigned char busy : 1;
105 unsigned char wel : 1;
106 unsigned char bp0 : 1;
107 unsigned char bp1 : 1;
108 unsigned char bp2 : 1;
109 unsigned char bp3 : 1;
110 unsigned char tb : 1;
111 unsigned char srp0 : 1;
112} __attribute__ ((packed));
113
114struct w25q_status_2 {
115 unsigned char srp1 : 1;
116 unsigned char qe : 1;
117 unsigned char rsvd : 6;
118} __attribute__ ((packed));
119
120int w25_range_to_status(const struct flashctx *flash,
121 unsigned int start, unsigned int len,
122 struct w25q_status *status);
123int w25_status_to_range(const struct flashctx *flash,
124 const struct w25q_status *status,
125 unsigned int *start, unsigned int *len);
126
David Hendrickse0512a72014-07-15 20:30:47 -0700127/*
David Hendrickse0512a72014-07-15 20:30:47 -0700128 * Mask to extract write-protect enable and range bits
129 * Status register 1:
130 * SRP0: bit 7
131 * range(BP2-BP0): bit 4-2
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800132 * range(BP3-BP0): bit 5-2 (large chips)
David Hendrickse0512a72014-07-15 20:30:47 -0700133 * Status register 2:
134 * SRP1: bit 1
135 */
136#define MASK_WP_AREA (0x9C)
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800137#define MASK_WP_AREA_LARGE (0x9C)
David Hendrickse0512a72014-07-15 20:30:47 -0700138#define MASK_WP2_AREA (0x01)
139
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000140static struct wp_range_descriptor en25f40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100141 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
142 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
143 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
144 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
145 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 448 * 1024} },
146 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 384 * 1024} },
147 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 256 * 1024} },
148 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 512 * 1024} },
David Hendricks57566ed2010-08-16 18:24:45 -0700149};
150
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000151static struct wp_range_descriptor en25q40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100152 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
153 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
154 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
155 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700156
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100157 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 448 * 1024} },
158 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 384 * 1024} },
159 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
160 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700161};
162
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000163static struct wp_range_descriptor en25q80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100164 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
165 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 1016 * 1024} },
166 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 1008 * 1024} },
167 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 992 * 1024} },
168 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 960 * 1024} },
169 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 896 * 1024} },
170 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 768 * 1024} },
171 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 1024 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700172};
173
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000174static struct wp_range_descriptor en25q32_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100175 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
176 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 4032 * 1024} },
177 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 3968 * 1024} },
178 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 3840 * 1024} },
179 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 3584 * 1024} },
180 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 3072 * 1024} },
181 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 2048 * 1024} },
182 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700183
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100184 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
185 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 4032 * 1024} },
186 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 3968 * 1024} },
187 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 3840 * 1024} },
188 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 3584 * 1024} },
189 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 3072 * 1024} },
190 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 2048 * 1024} },
191 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700192};
193
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000194static struct wp_range_descriptor en25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100195 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
196 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8128 * 1024} },
197 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 8064 * 1024} },
198 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7936 * 1024} },
199 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7680 * 1024} },
200 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 7168 * 1024} },
201 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 6144 * 1024} },
202 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700203
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100204 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
205 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 8128 * 1024} },
206 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 8064 * 1024} },
207 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 7936 * 1024} },
208 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 7680 * 1024} },
209 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 7168 * 1024} },
210 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 6144 * 1024} },
211 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700212};
213
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000214static struct wp_range_descriptor en25q128_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100215 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
216 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16320 * 1024} },
217 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 16256 * 1024} },
218 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 16128 * 1024} },
219 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 15872 * 1024} },
220 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 15360 * 1024} },
221 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 14336 * 1024} },
222 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 16384 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700223
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100224 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
225 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 16320 * 1024} },
226 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 16256 * 1024} },
227 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 16128 * 1024} },
228 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 15872 * 1024} },
229 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 15360 * 1024} },
230 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 14336 * 1024} },
231 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 16384 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700232};
233
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000234static struct wp_range_descriptor en25s64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100235 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
236 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8064 * 1024} },
237 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 7936 * 1024} },
238 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7680 * 1024} },
239 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7168 * 1024} },
240 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 6144 * 1024} },
241 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 4096 * 1024} },
242 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
Marc Jonesb2f90022014-04-29 17:37:23 -0600243
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100244 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
245 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x7e0000, 128 * 1024} },
246 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x7c0000, 256 * 1024} },
247 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x780000, 512 * 1024} },
248 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x700000, 1024 * 1024} },
249 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x600000, 2048 * 1024} },
250 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x400000, 4096 * 1024} },
251 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
Marc Jonesb2f90022014-04-29 17:37:23 -0600252};
253
David Hendricksf8f00c72011-02-01 12:39:46 -0800254/* mx25l1005 ranges also work for the mx25l1005c */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100255static struct wp_range_descriptor mx25l1005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100256 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
257 { .m = { .sec = X, .tb = X }, 0x1, {0x010000, 64 * 1024} },
258 { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
259 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800260};
261
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100262static struct wp_range_descriptor mx25l2005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100263 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
264 { .m = { .sec = X, .tb = X }, 0x1, {0x030000, 64 * 1024} },
265 { .m = { .sec = X, .tb = X }, 0x2, {0x020000, 128 * 1024} },
266 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 256 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800267};
268
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100269static struct wp_range_descriptor mx25l4005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100270 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
271 { .m = { .sec = X, .tb = X }, 0x1, {0x070000, 64 * 1 * 1024} }, /* block 7 */
272 { .m = { .sec = X, .tb = X }, 0x2, {0x060000, 64 * 2 * 1024} }, /* blocks 6-7 */
273 { .m = { .sec = X, .tb = X }, 0x3, {0x040000, 64 * 4 * 1024} }, /* blocks 4-7 */
274 { .m = { .sec = X, .tb = X }, 0x4, {0x000000, 512 * 1024} },
275 { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 512 * 1024} },
276 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 512 * 1024} },
277 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 512 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800278};
279
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100280static struct wp_range_descriptor mx25l8005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100281 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
282 { .m = { .sec = X, .tb = X }, 0x1, {0x0f0000, 64 * 1 * 1024} }, /* block 15 */
283 { .m = { .sec = X, .tb = X }, 0x2, {0x0e0000, 64 * 2 * 1024} }, /* blocks 14-15 */
284 { .m = { .sec = X, .tb = X }, 0x3, {0x0c0000, 64 * 4 * 1024} }, /* blocks 12-15 */
285 { .m = { .sec = X, .tb = X }, 0x4, {0x080000, 64 * 8 * 1024} }, /* blocks 8-15 */
286 { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
287 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
288 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800289};
290
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100291static struct wp_range_descriptor mx25l1605d_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100292 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
293 { .m = { .sec = X, .tb = 0 }, 0x1, {0x1f0000, 64 * 1 * 1024} }, /* block 31 */
294 { .m = { .sec = X, .tb = 0 }, 0x2, {0x1e0000, 64 * 2 * 1024} }, /* blocks 30-31 */
295 { .m = { .sec = X, .tb = 0 }, 0x3, {0x1c0000, 64 * 4 * 1024} }, /* blocks 28-31 */
296 { .m = { .sec = X, .tb = 0 }, 0x4, {0x180000, 64 * 8 * 1024} }, /* blocks 24-31 */
297 { .m = { .sec = X, .tb = 0 }, 0x5, {0x100000, 64 * 16 * 1024} }, /* blocks 16-31 */
298 { .m = { .sec = X, .tb = 0 }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
299 { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
David Hendricksf8f00c72011-02-01 12:39:46 -0800300
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100301 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 2048 * 1024} },
302 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
303 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
304 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 64 * 24 * 1024} }, /* blocks 0-23 */
305 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 64 * 28 * 1024} }, /* blocks 0-27 */
306 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 64 * 30 * 1024} }, /* blocks 0-29 */
307 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 64 * 31 * 1024} }, /* blocks 0-30 */
308 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
David Hendricksf8f00c72011-02-01 12:39:46 -0800309};
310
311/* FIXME: Is there an mx25l3205 (without a trailing letter)? */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100312static struct wp_range_descriptor mx25l3205d_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100313 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
314 { .m = { .sec = X, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
315 { .m = { .sec = X, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
316 { .m = { .sec = X, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
317 { .m = { .sec = X, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
318 { .m = { .sec = X, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
319 { .m = { .sec = X, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
320 { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksac72e362010-08-16 18:20:03 -0700321
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100322 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
323 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
324 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
325 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
326 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
327 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
328 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
329 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksac72e362010-08-16 18:20:03 -0700330};
331
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100332static struct wp_range_descriptor mx25u3235e_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100333 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
334 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
335 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
336 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
337 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
338 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
339 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
340 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
Vincent Palatin87e092a2013-02-28 15:46:14 -0800341
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100342 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
343 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
344 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
345 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
346 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
347 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
348 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
349 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
Vincent Palatin87e092a2013-02-28 15:46:14 -0800350};
351
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100352static struct wp_range_descriptor mx25u6435e_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100353 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
354 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 1 * 64 * 1024} }, /* block 127 */
355 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
356 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
357 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
358 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
359 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
360 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
Jongpil66a96492014-08-14 17:59:06 +0900361
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100362 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
363 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 96 * 64 * 1024} }, /* blocks 0-95 */
364 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 112 * 64 * 1024} }, /* blocks 0-111 */
365 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 120 * 64 * 1024} }, /* blocks 0-119 */
366 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 124 * 64 * 1024} }, /* blocks 0-123 */
367 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 126 * 64 * 1024} }, /* blocks 0-125 */
368 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 127 * 64 * 1024} }, /* blocks 0-126 */
369 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
Jongpil66a96492014-08-14 17:59:06 +0900370};
371
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600372#define MX25U12835E_TB (1 << 3)
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100373static struct wp_range_descriptor mx25u12835e_tb0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100374 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
375 { .m = { .sec = 0, .tb = 0 }, 0x1, {0xff0000, 1 * 64 * 1024} }, /* block 255 */
376 { .m = { .sec = 0, .tb = 0 }, 0x2, {0xfe0000, 2 * 64 * 1024} }, /* blocks 254-255 */
377 { .m = { .sec = 0, .tb = 0 }, 0x3, {0xfc0000, 4 * 64 * 1024} }, /* blocks 252-255 */
378 { .m = { .sec = 0, .tb = 0 }, 0x4, {0xf80000, 8 * 64 * 1024} }, /* blocks 248-255 */
379 { .m = { .sec = 0, .tb = 0 }, 0x5, {0xf00000, 16 * 64 * 1024} }, /* blocks 240-255 */
380 { .m = { .sec = 0, .tb = 0 }, 0x6, {0xe00000, 32 * 64 * 1024} }, /* blocks 224-255 */
381 { .m = { .sec = 0, .tb = 0 }, 0x7, {0xc00000, 64 * 64 * 1024} }, /* blocks 192-255 */
382 { .m = { .sec = 0, .tb = 0 }, 0x8, {0x800000, 128 * 64 * 1024} }, /* blocks 128-255 */
383 { .m = { .sec = 0, .tb = 0 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
384 { .m = { .sec = 0, .tb = 0 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
385 { .m = { .sec = 0, .tb = 0 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
386 { .m = { .sec = 0, .tb = 0 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
387 { .m = { .sec = 0, .tb = 0 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
388 { .m = { .sec = 0, .tb = 0 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
389 { .m = { .sec = 0, .tb = 0 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600390};
Alex Lu831c6092017-11-02 23:19:34 -0700391
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100392static struct wp_range_descriptor mx25u12835e_tb1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100393 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 1 * 64 * 1024} }, /* block 0 */
394 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
395 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
396 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
397 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
398 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
399 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
400 { .m = { .sec = 0, .tb = 1 }, 0x8, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
401 { .m = { .sec = 0, .tb = 1 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
402 { .m = { .sec = 0, .tb = 1 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
403 { .m = { .sec = 0, .tb = 1 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
404 { .m = { .sec = 0, .tb = 1 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
405 { .m = { .sec = 0, .tb = 1 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
406 { .m = { .sec = 0, .tb = 1 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
407 { .m = { .sec = 0, .tb = 1 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Alex Lu831c6092017-11-02 23:19:34 -0700408};
409
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100410static struct wp_range_descriptor n25q064_ranges[] = {
David Hendricksfe9123b2015-04-21 13:18:31 -0700411 /*
412 * Note: For N25Q064, sec (usually in bit position 6) is called BP3
413 * (block protect bit 3). It is only useful when all blocks are to
414 * be write-protected.
415 */
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100416 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
David Hendricksbfa624b2012-07-24 12:47:59 -0700417
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100418 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 64 * 1024} }, /* block 127 */
419 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
420 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
421 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
422 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
423 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
424 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700425
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100426 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, /* block 0 */
427 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
428 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
429 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
430 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
431 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
432 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700433
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100434 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 128 * 64 * 1024} }, /* all */
435 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 128 * 64 * 1024} }, /* all */
436 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 128 * 64 * 1024} }, /* all */
437 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 128 * 64 * 1024} }, /* all */
438 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 128 * 64 * 1024} }, /* all */
439 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 128 * 64 * 1024} }, /* all */
440 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 128 * 64 * 1024} }, /* all */
441 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* all */
David Hendricksbfa624b2012-07-24 12:47:59 -0700442};
443
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100444static struct wp_range_descriptor w25q16_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100445 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
446 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x1f0000, 64 * 1024} },
447 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x1e0000, 128 * 1024} },
448 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x1c0000, 256 * 1024} },
449 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x180000, 512 * 1024} },
450 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x100000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700451
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100452 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
453 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
454 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
455 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
456 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
457 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 2048 * 1024} },
458 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700459
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100460 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
461 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
462 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
463 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
464 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700465
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100466 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
467 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
468 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
469 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
470 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700471};
472
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100473static struct wp_range_descriptor w25q32_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100474 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
475 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
476 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
477 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
478 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
479 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
480 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700481
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100482 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
483 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
484 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
485 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
486 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
487 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 2048 * 1024} },
488 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700489
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100490 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x3ff000, 4 * 1024} },
491 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x3fe000, 8 * 1024} },
492 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x3fc000, 16 * 1024} },
493 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x3f8000, 32 * 1024} },
494 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x3f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700495
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100496 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
497 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
498 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
499 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
500 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700501};
502
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100503static struct wp_range_descriptor w25q80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100504 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
505 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0f0000, 64 * 1024} },
506 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0e0000, 128 * 1024} },
507 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0c0000, 256 * 1024} },
508 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700509
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100510 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
511 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
512 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
513 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
514 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
515 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700516
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100517 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
518 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
519 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
520 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
521 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700522
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100523 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
524 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
525 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
526 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
527 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700528};
529
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100530static struct wp_range_descriptor w25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100531 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
David Hendricks2c4a76c2010-06-28 14:00:43 -0700532
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100533 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
534 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
535 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
536 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
537 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
538 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700539
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100540 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
541 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
542 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
543 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
544 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
545 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
546 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700547
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100548 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
549 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
550 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
551 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
552 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700553
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100554 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
555 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
556 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
557 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
558 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700559};
560
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100561static struct wp_range_descriptor w25rq128_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100562 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* NONE */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530563
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100564 { .m = { .sec = 0, .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* Upper 1/64 */
565 { .m = { .sec = 0, .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* Upper 1/32 */
566 { .m = { .sec = 0, .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* Upper 1/16 */
567 { .m = { .sec = 0, .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* Upper 1/8 */
568 { .m = { .sec = 0, .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* Upper 1/4 */
569 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* Upper 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530570
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100571 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* Lower 1/64 */
572 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* Lower 1/32 */
573 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* Lower 1/16 */
574 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* Lower 1/8 */
575 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* Lower 1/4 */
576 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* Lower 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530577
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100578 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 16384 * 1024} }, /* ALL */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530579
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100580 { .m = { .sec = 1, .tb = 0 }, 0x1, {0xfff000, 4 * 1024} }, /* Upper 1/4096 */
581 { .m = { .sec = 1, .tb = 0 }, 0x2, {0xffe000, 8 * 1024} }, /* Upper 1/2048 */
582 { .m = { .sec = 1, .tb = 0 }, 0x3, {0xffc000, 16 * 1024} }, /* Upper 1/1024 */
583 { .m = { .sec = 1, .tb = 0 }, 0x4, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
584 { .m = { .sec = 1, .tb = 0 }, 0x5, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700585
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100586 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, /* Lower 1/4096 */
587 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, /* Lower 1/2048 */
588 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, /* Lower 1/1024 */
589 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, /* Lower 1/512 */
590 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, /* Lower 1/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700591};
592
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100593static struct wp_range_descriptor w25rq128_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100594 { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 16 * 1024 * 1024} }, /* ALL */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700595
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100596 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16128 * 1024} }, /* Lower 63/64 */
597 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 15872 * 1024} }, /* Lower 31/32 */
598 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 15 * 1024 * 1024} }, /* Lower 15/16 */
599 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 14 * 1024 * 1024} }, /* Lower 7/8 */
600 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 12 * 1024 * 1024} }, /* Lower 3/4 */
601 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 8 * 1024 * 1024} }, /* Lower 1/2 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700602
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100603 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x040000, 16128 * 1024} }, /* Upper 63/64 */
604 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x080000, 15872 * 1024} }, /* Upper 31/32 */
605 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x100000, 15 * 1024 * 1024} }, /* Upper 15/16 */
606 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x200000, 14 * 1024 * 1024} }, /* Upper 7/8 */
607 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x400000, 12 * 1024 * 1024} }, /* Upper 3/4 */
608 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x800000, 8 * 1024 * 1024} }, /* Upper 1/2 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700609
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100610 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 0} }, /* NONE */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700611
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100612 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 16380 * 1024} }, /* Lower 4095/4096 */
613 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 16376 * 1024} }, /* Lower 2048/2048 */
614 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 16368 * 1024} }, /* Lower 1023/1024 */
615 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
616 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700617
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100618 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 16380 * 1024} }, /* Upper 4095/4096 */
619 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 16376 * 1024} }, /* Upper 2047/2048 */
620 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 16368 * 1024} }, /* Upper 1023/1024 */
621 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
622 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530623};
624
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100625static struct wp_range_descriptor w25rq256_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100626 { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 0x0000000} }, /* NONE */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800627
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100628 { .m = { .sec = X, .tb = 0 }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* Upper 1/512 */
629 { .m = { .sec = X, .tb = 0 }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* Upper 1/256 */
630 { .m = { .sec = X, .tb = 0 }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* Upper 1/128 */
631 { .m = { .sec = X, .tb = 0 }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* Upper 1/64 */
632 { .m = { .sec = X, .tb = 0 }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* Upper 1/32 */
633 { .m = { .sec = X, .tb = 0 }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* Upper 1/16 */
634 { .m = { .sec = X, .tb = 0 }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* Upper 1/8 */
635 { .m = { .sec = X, .tb = 0 }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* Upper 1/4 */
636 { .m = { .sec = X, .tb = 0 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800637
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100638 { .m = { .sec = X, .tb = 1 }, 0x1, {0x0000000, 64 * 1 * 1024} }, /* Lower 1/512 */
639 { .m = { .sec = X, .tb = 1 }, 0x2, {0x0000000, 64 * 2 * 1024} }, /* Lower 1/256 */
640 { .m = { .sec = X, .tb = 1 }, 0x3, {0x0000000, 64 * 4 * 1024} }, /* Lower 1/128 */
641 { .m = { .sec = X, .tb = 1 }, 0x4, {0x0000000, 64 * 8 * 1024} }, /* Lower 1/64 */
642 { .m = { .sec = X, .tb = 1 }, 0x5, {0x0000000, 64 * 16 * 1024} }, /* Lower 1/32 */
643 { .m = { .sec = X, .tb = 1 }, 0x6, {0x0000000, 64 * 32 * 1024} }, /* Lower 1/16 */
644 { .m = { .sec = X, .tb = 1 }, 0x7, {0x0000000, 64 * 64 * 1024} }, /* Lower 1/8 */
645 { .m = { .sec = X, .tb = 1 }, 0x8, {0x0000000, 64 * 128 * 1024} }, /* Lower 1/4 */
646 { .m = { .sec = X, .tb = 1 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800647
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100648 { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* ALL */
649 { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* ALL */
650 { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* ALL */
651 { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* ALL */
652 { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* ALL */
653 { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* ALL */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800654};
655
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100656static struct wp_range_descriptor w25rq256_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100657 { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 64 * 512 * 1024} }, /* ALL */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800658
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100659 { .m = { .sec = X, .tb = 0 }, 0x1, {0x0000000, 64 * 511 * 1024} }, /* Lower 511/512 */
660 { .m = { .sec = X, .tb = 0 }, 0x2, {0x0000000, 64 * 510 * 1024} }, /* Lower 255/256 */
661 { .m = { .sec = X, .tb = 0 }, 0x3, {0x0000000, 64 * 508 * 1024} }, /* Lower 127/128 */
662 { .m = { .sec = X, .tb = 0 }, 0x4, {0x0000000, 64 * 504 * 1024} }, /* Lower 63/64 */
663 { .m = { .sec = X, .tb = 0 }, 0x5, {0x0000000, 64 * 496 * 1024} }, /* Lower 31/32 */
664 { .m = { .sec = X, .tb = 0 }, 0x6, {0x0000000, 64 * 480 * 1024} }, /* Lower 15/16 */
665 { .m = { .sec = X, .tb = 0 }, 0x7, {0x0000000, 64 * 448 * 1024} }, /* Lower 7/8 */
666 { .m = { .sec = X, .tb = 0 }, 0x8, {0x0000000, 64 * 384 * 1024} }, /* Lower 3/4 */
667 { .m = { .sec = X, .tb = 0 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800668
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100669 { .m = { .sec = X, .tb = 1 }, 0x1, {0x0010000, 64 * 511 * 1024} }, /* Upper 511/512 */
670 { .m = { .sec = X, .tb = 1 }, 0x2, {0x0020000, 64 * 510 * 1024} }, /* Upper 255/256 */
671 { .m = { .sec = X, .tb = 1 }, 0x3, {0x0040000, 64 * 508 * 1024} }, /* Upper 127/128 */
672 { .m = { .sec = X, .tb = 1 }, 0x4, {0x0080000, 64 * 504 * 1024} }, /* Upper 63/64 */
673 { .m = { .sec = X, .tb = 1 }, 0x5, {0x0100000, 64 * 496 * 1024} }, /* Upper 31/32 */
674 { .m = { .sec = X, .tb = 1 }, 0x6, {0x0200000, 64 * 480 * 1024} }, /* Upper 15/16 */
675 { .m = { .sec = X, .tb = 1 }, 0x7, {0x0400000, 64 * 448 * 1024} }, /* Upper 7/8 */
676 { .m = { .sec = X, .tb = 1 }, 0x8, {0x0800000, 64 * 384 * 1024} }, /* Upper 3/4 */
677 { .m = { .sec = X, .tb = 1 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800678
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100679 { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 0x0000000} }, /* NONE */
680 { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 0x0000000} }, /* NONE */
681 { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 0x0000000} }, /* NONE */
682 { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 0x0000000} }, /* NONE */
683 { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 0x0000000} }, /* NONE */
684 { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 0x0000000} }, /* NONE */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800685};
686
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000687static struct wp_range_descriptor w25x10_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100688 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
689 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x010000, 64 * 1024} },
690 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
691 { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
692 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800693};
694
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000695static struct wp_range_descriptor w25x20_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100696 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
697 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x030000, 64 * 1024} },
698 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x020000, 128 * 1024} },
699 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
700 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
701 { .m = { .sec = 0, .tb = X }, 0x3, {0x000000, 256 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800702};
703
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000704static struct wp_range_descriptor w25x40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100705 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
706 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
707 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
708 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
709 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
710 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
711 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
712 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} },
713 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} },
714 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} },
715 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} },
David Hendricks470ca952010-08-13 14:01:53 -0700716};
717
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000718static struct wp_range_descriptor w25x80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100719 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
720 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0F0000, 64 * 1024} },
721 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0E0000, 128 * 1024} },
722 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0C0000, 256 * 1024} },
723 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
724 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
725 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
726 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
727 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
728 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
729 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
730 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800731};
732
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100733static struct wp_range_descriptor gd25q40_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100734 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* None */
735 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
736 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
737 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
738 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
739 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
740 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
741 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} }, /* All */
742 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} }, /* All */
743 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} }, /* All */
744 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
745 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x07F000, 4 * 1024} },
746 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x07E000, 8 * 1024} },
747 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x07C000, 16 * 1024} },
748 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x078000, 32 * 1024} },
749 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x078000, 32 * 1024} },
750 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x078000, 32 * 1024} },
751 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
752 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
753 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
754 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
755 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
756 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
757 { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600758};
759
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100760static struct wp_range_descriptor gd25q40_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100761 { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 512 * 1024} }, /* ALL */
762 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 448 * 1024} },
763 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 384 * 1024} },
764 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 256 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600765
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100766 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 448 * 1024} },
767 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 384 * 1024} },
768 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 256 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600769
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100770 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 0} }, /* None */
771 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 0} }, /* None */
772 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 0} }, /* None */
773 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 0} }, /* None */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600774
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100775 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 508 * 1024} },
776 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 504 * 1024} },
777 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 496 * 1024} },
778 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 480 * 1024} },
779 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 480 * 1024} },
780 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x000000, 480 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600781
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100782 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 508 * 1024} },
783 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 504 * 1024} },
784 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 496 * 1024} },
785 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 480 * 1024} },
786 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 480 * 1024} },
787 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x008000, 480 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600788
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100789 { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 0} }, /* None */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600790};
791
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100792static struct wp_range_descriptor gd25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100793 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
794 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
795 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
796 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
797 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
798 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
799 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700800
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100801 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
802 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
803 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
804 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
805 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
806 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
807 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700808
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100809 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
810 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
811 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
812 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
813 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
814 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x7f8000, 32 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700815
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100816 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
817 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
818 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
819 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
820 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
821 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700822};
823
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100824static struct wp_range_descriptor a25l040_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100825 { .m = { .sec = X, .tb = X }, 0x0, {0, 0} }, /* none */
826 { .m = { .sec = X, .tb = X }, 0x1, {0x70000, 64 * 1024} },
827 { .m = { .sec = X, .tb = X }, 0x2, {0x60000, 128 * 1024} },
828 { .m = { .sec = X, .tb = X }, 0x3, {0x40000, 256 * 1024} },
829 { .m = { .sec = X, .tb = X }, 0x4, {0x00000, 512 * 1024} },
830 { .m = { .sec = X, .tb = X }, 0x5, {0x00000, 512 * 1024} },
831 { .m = { .sec = X, .tb = X }, 0x6, {0x00000, 512 * 1024} },
832 { .m = { .sec = X, .tb = X }, 0x7, {0x00000, 512 * 1024} },
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +0800833};
834
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100835struct wp *get_wp_for_flashchip(const struct flashchip *chip) {
836 // FIXME: The .wp field should be deleted from from struct flashchip
837 // completly, but linux_mtd and cros_ec still assign their own values
838 // to it. When they are cleaned up we can delete this.
839 if(chip->wp) return chip->wp;
840
841 switch (chip->manufacture_id) {
842 case WINBOND_NEX_ID:
843 switch(chip->model_id) {
844 case WINBOND_NEX_W25X10:
845 case WINBOND_NEX_W25X20:
846 case WINBOND_NEX_W25X40:
847 case WINBOND_NEX_W25X80:
848 case WINBOND_NEX_W25Q128_V_M:
849 return &wp_w25;
850 case WINBOND_NEX_W25Q80_V:
851 case WINBOND_NEX_W25Q16_V:
852 case WINBOND_NEX_W25Q32_V:
853 case WINBOND_NEX_W25Q32_W:
854 case WINBOND_NEX_W25Q32JW:
855 case WINBOND_NEX_W25Q64_V:
856 case WINBOND_NEX_W25Q64_W:
857 // W25Q64JW does not have a range table entry, but the flashchip
858 // set .wp to wp_25q, so keep it here until the issue is resolved
859 case WINBOND_NEX_W25Q64JW:
860 case WINBOND_NEX_W25Q128_DTR:
861 case WINBOND_NEX_W25Q128_V:
862 case WINBOND_NEX_W25Q128_W:
863 return &wp_w25q;
864 case WINBOND_NEX_W25Q256_V:
865 case WINBOND_NEX_W25Q256JV_M:
866 return &wp_w25q_large;
867 }
868 break;
869 case EON_ID_NOPREFIX:
870 switch (chip->model_id) {
871 case EON_EN25F40:
872 case EON_EN25Q40:
873 case EON_EN25Q80:
874 case EON_EN25Q32:
875 case EON_EN25Q64:
876 case EON_EN25Q128:
877 case EON_EN25QH128:
878 case EON_EN25S64:
879 return &wp_w25;
880 }
881 break;
882 case MACRONIX_ID:
883 switch (chip->model_id) {
884 case MACRONIX_MX25L1005:
885 case MACRONIX_MX25L2005:
886 case MACRONIX_MX25L4005:
887 case MACRONIX_MX25L8005:
888 case MACRONIX_MX25L1605:
889 case MACRONIX_MX25L3205:
890 case MACRONIX_MX25U3235E:
891 case MACRONIX_MX25U6435E:
892 return &wp_w25;
893 case MACRONIX_MX25U12835E:
894 return &wp_w25q_large;
895 case MACRONIX_MX25L6405:
896 case MACRONIX_MX25L6495F:
897 case MACRONIX_MX25L25635F:
898 return &wp_generic;
899 }
900 break;
901 case ST_ID:
902 switch(chip->model_id) {
903 case ST_N25Q064__1E:
904 case ST_N25Q064__3E:
905 return &wp_w25;
906 }
907 break;
908 case GIGADEVICE_ID:
909 switch(chip->model_id) {
910 case GIGADEVICE_GD25LQ32:
911 // GD25Q40 does not have a .wp field in flashchips.c, but
912 // it is in the w25 range table function, so note it here
913 // until the issue is resolved:
914 // case GIGADEVICE_GD25Q40:
915 case GIGADEVICE_GD25Q64:
916 case GIGADEVICE_GD25LQ64:
917 // Ranges for GD25Q128 are defined in both the generic and
918 // w25 range table functions. The .wp field in the flashchip
919 // pointed to wp_w25, so use that here as well.
920 case GIGADEVICE_GD25Q128:
921 return &wp_w25;
922 case GIGADEVICE_GD25Q256D:
923 return &wp_w25q_large;
924 // Ranges for GD25Q128CD are defined in both the generic and
925 // w25 range table functions. The .wp field in the flashchip
926 // pointed to wp_generic, so use that here as well.
927 case GIGADEVICE_GD25LQ128CD:
928 case GIGADEVICE_GD25Q32:
929 return &wp_generic;
930 }
931 break;
932 case AMIC_ID_NOPREFIX:
933 switch(chip->model_id) {
934 case AMIC_A25L040:
935 return &wp_w25;
936 }
937 break;
938 case ATMEL_ID:
939 switch(chip->model_id) {
940 case ATMEL_AT25SF128A:
941 case ATMEL_AT25SL128A:
942 return &wp_w25q;
943 }
944 break;
945 case PROGMANUF_ID:
946 switch(chip->model_id) {
947 case PROGDEV_ID:
948 return &wp_w25;
949 }
950 break;
951 case SPANSION_ID:
952 switch (chip->model_id) {
953 case SPANSION_S25FS128S_L:
954 case SPANSION_S25FS128S_S:
955 case SPANSION_S25FL256S_UL:
956 case SPANSION_S25FL256S_US:
957 // SPANSION_S25FL128S_UL does not have a range table entry,
958 // but its flashchip set .wp to wp_generic, so keep it here
959 // until the issue resolved
960 case SPANSION_S25FL128S_UL:
961 // SPANSION_S25FL128S_US does not have a range table entry,
962 // but its flashchip set .wp to wp_generic, so keep it here
963 // until the issue resolved
964 case SPANSION_S25FL128S_US:
965 return &wp_generic;
966 }
967 break;
968 }
969
970
971 return NULL;
972}
973
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700974static uint8_t do_read_status(const struct flashctx *flash)
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530975{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100976 if (flash->chip->read_status)
977 return flash->chip->read_status(flash);
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530978 else
979 return spi_read_status_register(flash);
980}
981
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700982static int do_write_status(const struct flashctx *flash, int status)
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530983{
Patrick Georgif3fa2992017-02-02 16:24:44 +0100984 if (flash->chip->write_status)
985 return flash->chip->write_status(flash, status);
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530986 else
987 return spi_write_status_register(flash, status);
988}
989
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700990/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700991static uint8_t w25q_read_status_register_2(const struct flashctx *flash)
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700992{
993 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 };
994 unsigned char readarr[2];
995 int ret;
996
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +1100997 if (flash->chip->read_status) {
998 msg_cdbg("RDSR2 failed! cmd=0x35 unimpl for opaque chips\n");
999 return 0;
1000 }
1001
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001002 /* Read Status Register */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001003 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001004 if (ret) {
1005 /*
1006 * FIXME: make this a benign failure for now in case we are
1007 * unable to execute the opcode
1008 */
1009 msg_cdbg("RDSR2 failed!\n");
1010 readarr[0] = 0x00;
1011 }
1012
1013 return readarr[0];
1014}
1015
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001016/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Edward O'Callaghandf43e902020-11-13 23:08:26 +11001017static uint8_t mx25l_read_config_register(const struct flashctx *flash)
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001018{
1019 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x15 };
1020 unsigned char readarr[2]; /* leave room for dummy byte */
1021 int ret;
1022
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +11001023 if (flash->chip->read_status) {
1024 msg_cdbg("RDCR failed! cmd=0x15 unimpl for opaque chips\n");
1025 return 0;
1026 }
1027
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001028 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
1029 if (ret) {
1030 msg_cdbg("RDCR failed!\n");
1031 readarr[0] = 0x00;
1032 }
1033
1034 return readarr[0];
1035}
1036
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001037/* Given a flash chip, this function returns its range table. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001038static int w25_range_table(const struct flashctx *flash,
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001039 struct wp_range_descriptor **descrs,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001040 int *num_entries)
David Hendricksf7924d12010-06-10 21:26:44 -07001041{
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001042 uint8_t cr;
1043
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001044 *descrs = 0;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001045 *num_entries = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001046
Patrick Georgif3fa2992017-02-02 16:24:44 +01001047 switch (flash->chip->manufacture_id) {
David Hendricksd494b0a2010-08-16 16:28:50 -07001048 case WINBOND_NEX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001049 switch(flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -08001050 case WINBOND_NEX_W25X10:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001051 *descrs = w25x10_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001052 *num_entries = ARRAY_SIZE(w25x10_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001053 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001054 case WINBOND_NEX_W25X20:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001055 *descrs = w25x20_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001056 *num_entries = ARRAY_SIZE(w25x20_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001057 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001058 case WINBOND_NEX_W25X40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001059 *descrs = w25x40_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001060 *num_entries = ARRAY_SIZE(w25x40_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001061 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001062 case WINBOND_NEX_W25X80:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001063 *descrs = w25x80_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001064 *num_entries = ARRAY_SIZE(w25x80_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001065 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001066 case WINBOND_NEX_W25Q80_V:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001067 *descrs = w25q80_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001068 *num_entries = ARRAY_SIZE(w25q80_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001069 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001070 case WINBOND_NEX_W25Q16_V:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001071 *descrs = w25q16_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001072 *num_entries = ARRAY_SIZE(w25q16_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001073 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001074 case WINBOND_NEX_W25Q32_V:
1075 case WINBOND_NEX_W25Q32_W:
Edward O'Callaghand80cf712019-05-24 22:06:36 +10001076 case WINBOND_NEX_W25Q32JW:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001077 *descrs = w25q32_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001078 *num_entries = ARRAY_SIZE(w25q32_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001079 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001080 case WINBOND_NEX_W25Q64_V:
1081 case WINBOND_NEX_W25Q64_W:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001082 *descrs = w25q64_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001083 *num_entries = ARRAY_SIZE(w25q64_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001084 break;
Edward O'Callaghan517cb822019-11-21 14:08:32 +11001085 case WINBOND_NEX_W25Q128_DTR:
Alan Green77a95de2019-07-01 16:40:39 +10001086 case WINBOND_NEX_W25Q128_V_M:
Patrick Georgicc04a452017-02-06 12:14:43 +01001087 case WINBOND_NEX_W25Q128_V:
1088 case WINBOND_NEX_W25Q128_W:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001089 if (w25q_read_status_register_2(flash) & (1 << 6)) {
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001090 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001091 *descrs = w25rq128_cmp1_ranges;
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001092 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1093 } else {
1094 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001095 *descrs = w25rq128_cmp0_ranges;
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001096 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1097 }
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +05301098 break;
Justin TerAvest40083232020-08-17 16:34:46 -06001099 case WINBOND_NEX_W25Q256_V:
Alan Green77a95de2019-07-01 16:40:39 +10001100 case WINBOND_NEX_W25Q256JV_M:
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001101 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1102 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001103 *descrs = w25rq256_cmp1_ranges;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001104 *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges);
1105 } else {
1106 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001107 *descrs = w25rq256_cmp0_ranges;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001108 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
1109 }
1110 break;
David Hendricksd494b0a2010-08-16 16:28:50 -07001111 default:
1112 msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
1113 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001114 flash->chip->model_id);
David Hendricksd494b0a2010-08-16 16:28:50 -07001115 return -1;
1116 }
David Hendricks2c4a76c2010-06-28 14:00:43 -07001117 break;
David Hendricks57566ed2010-08-16 18:24:45 -07001118 case EON_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001119 switch (flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -08001120 case EON_EN25F40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001121 *descrs = en25f40_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001122 *num_entries = ARRAY_SIZE(en25f40_ranges);
David Hendricks57566ed2010-08-16 18:24:45 -07001123 break;
David Hendrickse185bf22011-05-24 15:34:18 -07001124 case EON_EN25Q40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001125 *descrs = en25q40_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001126 *num_entries = ARRAY_SIZE(en25q40_ranges);
1127 break;
1128 case EON_EN25Q80:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001129 *descrs = en25q80_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001130 *num_entries = ARRAY_SIZE(en25q80_ranges);
1131 break;
1132 case EON_EN25Q32:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001133 *descrs = en25q32_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001134 *num_entries = ARRAY_SIZE(en25q32_ranges);
1135 break;
1136 case EON_EN25Q64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001137 *descrs = en25q64_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001138 *num_entries = ARRAY_SIZE(en25q64_ranges);
1139 break;
1140 case EON_EN25Q128:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001141 *descrs = en25q128_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001142 *num_entries = ARRAY_SIZE(en25q128_ranges);
1143 break;
Tim Chen136fd0a2020-06-30 19:12:50 +08001144 case EON_EN25QH128:
1145 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1146 /* CMP == 1 */
1147 *descrs = w25rq128_cmp1_ranges;
1148 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1149 } else {
1150 /* CMP == 0 */
1151 *descrs = w25rq128_cmp0_ranges;
1152 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1153 }
1154 break;
Marc Jonesb2f90022014-04-29 17:37:23 -06001155 case EON_EN25S64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001156 *descrs = en25s64_ranges;
Marc Jonesb2f90022014-04-29 17:37:23 -06001157 *num_entries = ARRAY_SIZE(en25s64_ranges);
1158 break;
David Hendricks57566ed2010-08-16 18:24:45 -07001159 default:
1160 msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
1161 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001162 flash->chip->model_id);
David Hendricks57566ed2010-08-16 18:24:45 -07001163 return -1;
1164 }
1165 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001166 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001167 switch (flash->chip->model_id) {
David Hendricksf8f00c72011-02-01 12:39:46 -08001168 case MACRONIX_MX25L1005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001169 *descrs = mx25l1005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001170 *num_entries = ARRAY_SIZE(mx25l1005_ranges);
1171 break;
1172 case MACRONIX_MX25L2005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001173 *descrs = mx25l2005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001174 *num_entries = ARRAY_SIZE(mx25l2005_ranges);
1175 break;
1176 case MACRONIX_MX25L4005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001177 *descrs = mx25l4005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001178 *num_entries = ARRAY_SIZE(mx25l4005_ranges);
1179 break;
1180 case MACRONIX_MX25L8005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001181 *descrs = mx25l8005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001182 *num_entries = ARRAY_SIZE(mx25l8005_ranges);
1183 break;
1184 case MACRONIX_MX25L1605:
1185 /* FIXME: MX25L1605 and MX25L1605D have different write
1186 * protection capabilities, but share IDs */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001187 *descrs = mx25l1605d_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001188 *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
1189 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001190 case MACRONIX_MX25L3205:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001191 *descrs = mx25l3205d_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001192 *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
David Hendricksac72e362010-08-16 18:20:03 -07001193 break;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001194 case MACRONIX_MX25U3235E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001195 *descrs = mx25u3235e_ranges;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001196 *num_entries = ARRAY_SIZE(mx25u3235e_ranges);
1197 break;
Jongpil66a96492014-08-14 17:59:06 +09001198 case MACRONIX_MX25U6435E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001199 *descrs = mx25u6435e_ranges;
Jongpil66a96492014-08-14 17:59:06 +09001200 *num_entries = ARRAY_SIZE(mx25u6435e_ranges);
1201 break;
Alan Greendc0792e2019-07-01 15:01:34 +10001202 case MACRONIX_MX25U12835E:
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001203 cr = mx25l_read_config_register(flash);
1204 if (cr & MX25U12835E_TB) { /* T/B == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001205 *descrs = mx25u12835e_tb1_ranges;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001206 *num_entries = ARRAY_SIZE(mx25u12835e_tb1_ranges);
1207 } else { /* T/B == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001208 *descrs = mx25u12835e_tb0_ranges;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001209 *num_entries = ARRAY_SIZE(mx25u12835e_tb0_ranges);
1210 }
Alex Lu831c6092017-11-02 23:19:34 -07001211 break;
David Hendricksac72e362010-08-16 18:20:03 -07001212 default:
1213 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
1214 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001215 flash->chip->model_id);
David Hendricksac72e362010-08-16 18:20:03 -07001216 return -1;
1217 }
1218 break;
David Hendricksbfa624b2012-07-24 12:47:59 -07001219 case ST_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001220 switch(flash->chip->model_id) {
David Hendricksbfa624b2012-07-24 12:47:59 -07001221 case ST_N25Q064__1E:
1222 case ST_N25Q064__3E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001223 *descrs = n25q064_ranges;
David Hendricksbfa624b2012-07-24 12:47:59 -07001224 *num_entries = ARRAY_SIZE(n25q064_ranges);
1225 break;
1226 default:
1227 msg_cerr("%s() %d: Micron flash chip mismatch"
1228 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001229 flash->chip->model_id);
David Hendricksbfa624b2012-07-24 12:47:59 -07001230 return -1;
1231 }
1232 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001233 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001234 switch(flash->chip->model_id) {
Bryan Freed9a0051f2012-05-22 16:06:09 -07001235 case GIGADEVICE_GD25LQ32:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001236 *descrs = w25q32_ranges;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001237 *num_entries = ARRAY_SIZE(w25q32_ranges);
1238 break;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001239 case GIGADEVICE_GD25Q40:
1240 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1241 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001242 *descrs = gd25q40_cmp1_ranges;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001243 *num_entries = ARRAY_SIZE(gd25q40_cmp1_ranges);
1244 } else {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001245 *descrs = gd25q40_cmp0_ranges;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001246 *num_entries = ARRAY_SIZE(gd25q40_cmp0_ranges);
1247 }
1248 break;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001249 case GIGADEVICE_GD25Q64:
Marc Jonesb18734f2014-04-03 16:19:47 -06001250 case GIGADEVICE_GD25LQ64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001251 *descrs = gd25q64_ranges;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001252 *num_entries = ARRAY_SIZE(gd25q64_ranges);
1253 break;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001254 case GIGADEVICE_GD25Q128:
Aaron Durbin6c957d72018-08-20 09:31:01 -06001255 case GIGADEVICE_GD25LQ128CD:
Martin Roth1fd87ed2017-02-27 20:50:50 -07001256 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1257 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001258 *descrs = w25rq128_cmp1_ranges;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001259 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1260 } else {
1261 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001262 *descrs = w25rq128_cmp0_ranges;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001263 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1264 }
1265 break;
Duncan Laurie0c383552019-03-16 12:35:16 -07001266 case GIGADEVICE_GD25Q256D:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001267 *descrs = w25rq256_cmp0_ranges;
Duncan Laurie0c383552019-03-16 12:35:16 -07001268 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
1269 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001270 default:
1271 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
1272 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001273 flash->chip->model_id);
Bryan Freed9a0051f2012-05-22 16:06:09 -07001274 return -1;
1275 }
1276 break;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001277 case AMIC_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001278 switch(flash->chip->model_id) {
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001279 case AMIC_A25L040:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001280 *descrs = a25l040_ranges;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001281 *num_entries = ARRAY_SIZE(a25l040_ranges);
1282 break;
1283 default:
1284 msg_cerr("%s() %d: AMIC flash chip mismatch"
1285 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001286 flash->chip->model_id);
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001287 return -1;
1288 }
1289 break;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001290 case ATMEL_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001291 switch(flash->chip->model_id) {
Edward O'Callaghan1fa87e02019-05-03 02:27:24 -04001292 case ATMEL_AT25SF128A:
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001293 case ATMEL_AT25SL128A:
1294 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1295 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001296 *descrs = w25rq128_cmp1_ranges;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001297 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1298 } else {
1299 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001300 *descrs = w25rq128_cmp0_ranges;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001301 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1302 }
1303 break;
1304 default:
1305 msg_cerr("%s() %d: Atmel flash chip mismatch"
1306 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001307 flash->chip->model_id);
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001308 return -1;
1309 }
1310 break;
David Hendricksf7924d12010-06-10 21:26:44 -07001311 default:
David Hendricksd494b0a2010-08-16 16:28:50 -07001312 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01001313 __func__, flash->chip->manufacture_id);
David Hendricksf7924d12010-06-10 21:26:44 -07001314 return -1;
1315 }
1316
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001317 return 0;
1318}
1319
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001320int w25_range_to_status(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001321 unsigned int start, unsigned int len,
1322 struct w25q_status *status)
1323{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001324 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001325 int i, range_found = 0;
1326 int num_entries;
1327
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001328 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001329 return -1;
1330
David Hendricksf7924d12010-06-10 21:26:44 -07001331 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001332 struct wp_range *r = &descrs[i].range;
David Hendricksf7924d12010-06-10 21:26:44 -07001333
1334 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1335 start, len, r->start, r->len);
1336 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001337 status->bp0 = descrs[i].bp & 1;
1338 status->bp1 = descrs[i].bp >> 1;
1339 status->bp2 = descrs[i].bp >> 2;
1340 status->tb = descrs[i].m.tb;
1341 status->sec = descrs[i].m.sec;
David Hendricksf7924d12010-06-10 21:26:44 -07001342
1343 range_found = 1;
1344 break;
1345 }
1346 }
1347
1348 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001349 msg_cerr("%s: matching range not found\n", __func__);
David Hendricksf7924d12010-06-10 21:26:44 -07001350 return -1;
1351 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001352
David Hendricksd494b0a2010-08-16 16:28:50 -07001353 return 0;
1354}
1355
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001356int w25_status_to_range(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001357 const struct w25q_status *status,
1358 unsigned int *start, unsigned int *len)
1359{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001360 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001361 int i, status_found = 0;
1362 int num_entries;
1363
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001364 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001365 return -1;
1366
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001367 for (i = 0; i < num_entries; i++) {
1368 int bp;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001369 int table_bp, table_tb, table_sec;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001370
1371 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
1372 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001373 bp, descrs[i].bp,
1374 status->tb, descrs[i].m.tb,
1375 status->sec, descrs[i].m.sec);
1376 table_bp = descrs[i].bp;
1377 table_tb = descrs[i].m.tb;
1378 table_sec = descrs[i].m.sec;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001379 if ((bp == table_bp || table_bp == X) &&
1380 (status->tb == table_tb || table_tb == X) &&
1381 (status->sec == table_sec || table_sec == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001382 *start = descrs[i].range.start;
1383 *len = descrs[i].range.len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001384
1385 status_found = 1;
1386 break;
1387 }
1388 }
1389
1390 if (!status_found) {
1391 msg_cerr("matching status not found\n");
1392 return -1;
1393 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001394
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001395 return 0;
1396}
1397
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001398/* Given a [start, len], this function calls w25_range_to_status() to convert
1399 * it to flash-chip-specific range bits, then sets into status register.
1400 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001401static int w25_set_range(const struct flashctx *flash,
David Hendricksd494b0a2010-08-16 16:28:50 -07001402 unsigned int start, unsigned int len)
1403{
1404 struct w25q_status status;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001405 int tmp = 0;
1406 int expected = 0;
David Hendricksd494b0a2010-08-16 16:28:50 -07001407
1408 memset(&status, 0, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301409 tmp = do_read_status(flash);
David Hendricksd494b0a2010-08-16 16:28:50 -07001410 memcpy(&status, &tmp, 1);
1411 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1412
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001413 if (w25_range_to_status(flash, start, len, &status))
1414 return -1;
David Hendricksf7924d12010-06-10 21:26:44 -07001415
1416 msg_cdbg("status.busy: %x\n", status.busy);
1417 msg_cdbg("status.wel: %x\n", status.wel);
1418 msg_cdbg("status.bp0: %x\n", status.bp0);
1419 msg_cdbg("status.bp1: %x\n", status.bp1);
1420 msg_cdbg("status.bp2: %x\n", status.bp2);
1421 msg_cdbg("status.tb: %x\n", status.tb);
1422 msg_cdbg("status.sec: %x\n", status.sec);
1423 msg_cdbg("status.srp0: %x\n", status.srp0);
1424
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001425 memcpy(&expected, &status, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301426 do_write_status(flash, expected);
David Hendricksf7924d12010-06-10 21:26:44 -07001427
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301428 tmp = do_read_status(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001429 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001430 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) {
David Hendricksc801adb2010-12-09 16:58:56 -08001431 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001432 expected, tmp);
1433 return 1;
1434 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001435
1436 return 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001437}
1438
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001439/* Print out the current status register value with human-readable text. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001440static int w25_wp_status(const struct flashctx *flash)
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001441{
1442 struct w25q_status status;
1443 int tmp;
David Hendricksce8ded32010-10-08 11:23:38 -07001444 unsigned int start, len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001445 int ret = 0;
1446
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001447 memset(&status, 0, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301448 tmp = do_read_status(flash);
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001449 memcpy(&status, &tmp, 1);
1450 msg_cinfo("WP: status: 0x%02x\n", tmp);
1451 msg_cinfo("WP: status.srp0: %x\n", status.srp0);
1452 msg_cinfo("WP: write protect is %s.\n",
1453 status.srp0 ? "enabled" : "disabled");
1454
1455 msg_cinfo("WP: write protect range: ");
1456 if (w25_status_to_range(flash, &status, &start, &len)) {
1457 msg_cinfo("(cannot resolve the range)\n");
1458 ret = -1;
1459 } else {
1460 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1461 }
1462
1463 return ret;
1464}
1465
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001466static int w25q_large_range_to_status(const struct flashctx *flash,
1467 unsigned int start, unsigned int len,
1468 struct w25q_status_large *status)
1469{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001470 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001471 int i, range_found = 0;
1472 int num_entries;
1473
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001474 if (w25_range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001475 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001476
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001477 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001478 struct wp_range *r = &descrs[i].range;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001479
1480 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1481 start, len, r->start, r->len);
1482 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001483 status->bp0 = descrs[i].bp & 1;
1484 status->bp1 = descrs[i].bp >> 1;
1485 status->bp2 = descrs[i].bp >> 2;
1486 status->bp3 = descrs[i].bp >> 3;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001487 /*
1488 * For MX25U12835E chip, Top/Bottom (T/B) bit is not
1489 * part of status register and in that bit position is
1490 * Quad Enable (QE)
1491 */
1492 if (flash->chip->manufacture_id != MACRONIX_ID ||
1493 flash->chip->model_id != MACRONIX_MX25U12835E)
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001494 status->tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001495
1496 range_found = 1;
1497 break;
1498 }
1499 }
1500
1501 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001502 msg_cerr("%s: matching range not found\n", __func__);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001503 return -1;
1504 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001505
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001506 return 0;
1507}
1508
1509static int w25_large_status_to_range(const struct flashctx *flash,
1510 const struct w25q_status_large *status,
1511 unsigned int *start, unsigned int *len)
1512{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001513 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001514 int i, status_found = 0;
1515 int num_entries;
1516
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001517 if (w25_range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001518 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001519
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001520 for (i = 0; i < num_entries; i++) {
1521 int bp;
1522 int table_bp, table_tb;
1523
1524 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) |
1525 (status->bp3 << 3);
1526 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001527 bp, descrs[i].bp,
1528 status->tb, descrs[i].m.tb);
1529 table_bp = descrs[i].bp;
1530 table_tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001531 if ((bp == table_bp || table_bp == X) &&
1532 (status->tb == table_tb || table_tb == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001533 *start = descrs[i].range.start;
1534 *len = descrs[i].range.len;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001535
1536 status_found = 1;
1537 break;
1538 }
1539 }
1540
1541 if (!status_found) {
1542 msg_cerr("matching status not found\n");
1543 return -1;
1544 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001545
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001546 return 0;
1547}
1548
1549/* Given a [start, len], this function calls w25_range_to_status() to convert
1550 * it to flash-chip-specific range bits, then sets into status register.
1551 * Returns 0 if successful, -1 on error, and 1 if reading back was different.
1552 */
1553static int w25q_large_set_range(const struct flashctx *flash,
1554 unsigned int start, unsigned int len)
1555{
1556 struct w25q_status_large status;
1557 int tmp;
1558 int expected = 0;
1559
1560 memset(&status, 0, sizeof(status));
1561 tmp = do_read_status(flash);
1562 memcpy(&status, &tmp, 1);
1563 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1564
1565 if (w25q_large_range_to_status(flash, start, len, &status))
1566 return -1;
1567
1568 msg_cdbg("status.busy: %x\n", status.busy);
1569 msg_cdbg("status.wel: %x\n", status.wel);
1570 msg_cdbg("status.bp0: %x\n", status.bp0);
1571 msg_cdbg("status.bp1: %x\n", status.bp1);
1572 msg_cdbg("status.bp2: %x\n", status.bp2);
1573 msg_cdbg("status.bp3: %x\n", status.bp3);
1574 msg_cdbg("status.tb: %x\n", status.tb);
1575 msg_cdbg("status.srp0: %x\n", status.srp0);
1576
1577 memcpy(&expected, &status, sizeof(status));
1578 do_write_status(flash, expected);
1579
1580 tmp = do_read_status(flash);
1581 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001582 if ((tmp & MASK_WP_AREA_LARGE) != (expected & MASK_WP_AREA_LARGE)) {
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001583 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
1584 expected, tmp);
1585 return 1;
1586 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001587
1588 return 0;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001589}
1590
1591static int w25q_large_wp_status(const struct flashctx *flash)
1592{
1593 struct w25q_status_large sr1;
1594 struct w25q_status_2 sr2;
1595 uint8_t tmp[2];
1596 unsigned int start, len;
1597 int ret = 0;
1598
1599 memset(&sr1, 0, sizeof(sr1));
1600 tmp[0] = do_read_status(flash);
1601 memcpy(&sr1, &tmp[0], 1);
1602
1603 memset(&sr2, 0, sizeof(sr2));
1604 tmp[1] = w25q_read_status_register_2(flash);
1605 memcpy(&sr2, &tmp[1], 1);
1606
1607 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
1608 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1609 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1610 msg_cinfo("WP: write protect is %s.\n",
1611 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1612
1613 msg_cinfo("WP: write protect range: ");
1614 if (w25_large_status_to_range(flash, &sr1, &start, &len)) {
1615 msg_cinfo("(cannot resolve the range)\n");
1616 ret = -1;
1617 } else {
1618 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1619 }
1620
1621 return ret;
1622}
1623
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001624/* Set/clear the SRP0 bit in the status register. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001625static int w25_set_srp0(const struct flashctx *flash, int enable)
David Hendricksf7924d12010-06-10 21:26:44 -07001626{
1627 struct w25q_status status;
1628 int tmp = 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001629 int expected = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001630
1631 memset(&status, 0, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301632 tmp = do_read_status(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001633 /* FIXME: this is NOT endian-free copy. */
David Hendricksf7924d12010-06-10 21:26:44 -07001634 memcpy(&status, &tmp, 1);
1635 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1636
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001637 status.srp0 = enable ? 1 : 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001638 memcpy(&expected, &status, sizeof(status));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301639 do_write_status(flash, expected);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001640
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301641 tmp = do_read_status(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001642 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
1643 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
1644 return 1;
David Hendricksf7924d12010-06-10 21:26:44 -07001645
1646 return 0;
1647}
1648
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001649static int w25_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001650 enum wp_mode wp_mode)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001651{
1652 int ret;
1653
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001654 if (wp_mode != WP_MODE_HARDWARE) {
David Hendricks1c09f802012-10-03 11:03:48 -07001655 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
1656 return 1;
1657 }
1658
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001659 ret = w25_set_srp0(flash, 1);
David Hendricksc801adb2010-12-09 16:58:56 -08001660 if (ret)
1661 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001662 return ret;
1663}
1664
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001665static int w25_disable_writeprotect(const struct flashctx *flash)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001666{
1667 int ret;
1668
1669 ret = w25_set_srp0(flash, 0);
David Hendricksc801adb2010-12-09 16:58:56 -08001670 if (ret)
1671 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001672
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001673 return ret;
1674}
1675
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001676static int w25_list_ranges(const struct flashctx *flash)
David Hendricks0f7f5382011-02-11 18:12:31 -08001677{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001678 struct wp_range_descriptor *descrs;
David Hendricks0f7f5382011-02-11 18:12:31 -08001679 int i, num_entries;
1680
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001681 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001682 return -1;
1683
David Hendricks0f7f5382011-02-11 18:12:31 -08001684 for (i = 0; i < num_entries; i++) {
1685 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001686 descrs[i].range.start,
1687 descrs[i].range.len);
David Hendricks0f7f5382011-02-11 18:12:31 -08001688 }
1689
1690 return 0;
1691}
1692
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001693static int w25q_wp_status(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001694{
1695 struct w25q_status sr1;
1696 struct w25q_status_2 sr2;
David Hendricksf1bd8802012-10-30 11:37:57 -07001697 uint8_t tmp[2];
David Hendricks1c09f802012-10-03 11:03:48 -07001698 unsigned int start, len;
1699 int ret = 0;
1700
1701 memset(&sr1, 0, sizeof(sr1));
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301702 tmp[0] = do_read_status(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001703 memcpy(&sr1, &tmp[0], 1);
David Hendricks1c09f802012-10-03 11:03:48 -07001704
David Hendricksf1bd8802012-10-30 11:37:57 -07001705 memset(&sr2, 0, sizeof(sr2));
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001706 tmp[1] = w25q_read_status_register_2(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001707 memcpy(&sr2, &tmp[1], 1);
1708
1709 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
David Hendricks1c09f802012-10-03 11:03:48 -07001710 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1711 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1712 msg_cinfo("WP: write protect is %s.\n",
1713 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1714
1715 msg_cinfo("WP: write protect range: ");
1716 if (w25_status_to_range(flash, &sr1, &start, &len)) {
1717 msg_cinfo("(cannot resolve the range)\n");
1718 ret = -1;
1719 } else {
1720 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1721 }
1722
1723 return ret;
1724}
1725
1726/*
1727 * W25Q adds an optional byte to the standard WRSR opcode. If /CS is
1728 * de-asserted after the first byte, then it acts like a JEDEC-standard
1729 * WRSR command. if /CS is asserted, then the next data byte is written
1730 * into status register 2.
1731 */
1732#define W25Q_WRSR_OUTSIZE 0x03
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001733static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2)
David Hendricks1c09f802012-10-03 11:03:48 -07001734{
1735 int result;
1736 struct spi_command cmds[] = {
1737 {
1738 /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
1739 .writecnt = JEDEC_WREN_OUTSIZE,
1740 .writearr = (const unsigned char[]){ JEDEC_WREN },
1741 .readcnt = 0,
1742 .readarr = NULL,
1743 }, {
1744 .writecnt = W25Q_WRSR_OUTSIZE,
1745 .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 },
1746 .readcnt = 0,
1747 .readarr = NULL,
1748 }, {
1749 .writecnt = 0,
1750 .writearr = NULL,
1751 .readcnt = 0,
1752 .readarr = NULL,
1753 }};
1754
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001755 result = spi_send_multicommand(flash, cmds);
David Hendricks1c09f802012-10-03 11:03:48 -07001756 if (result) {
1757 msg_cerr("%s failed during command execution\n",
1758 __func__);
1759 }
1760
1761 /* WRSR performs a self-timed erase before the changes take effect. */
David Hendricks60824042014-12-11 17:22:06 -08001762 programmer_delay(100 * 1000);
David Hendricks1c09f802012-10-03 11:03:48 -07001763
1764 return result;
1765}
1766
1767/*
1768 * Set/clear the SRP1 bit in status register 2.
1769 * FIXME: make this more generic if other chips use the same SR2 layout
1770 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001771static int w25q_set_srp1(const struct flashctx *flash, int enable)
David Hendricks1c09f802012-10-03 11:03:48 -07001772{
1773 struct w25q_status sr1;
1774 struct w25q_status_2 sr2;
1775 uint8_t tmp, expected;
1776
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301777 tmp = do_read_status(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001778 memcpy(&sr1, &tmp, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001779 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001780 memcpy(&sr2, &tmp, 1);
1781
1782 msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp);
1783
1784 sr2.srp1 = enable ? 1 : 0;
1785
1786 memcpy(&expected, &sr2, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001787 w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2));
David Hendricks1c09f802012-10-03 11:03:48 -07001788
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001789 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001790 msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp);
1791 if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA))
1792 return 1;
1793
1794 return 0;
1795}
1796
1797enum wp_mode get_wp_mode(const char *mode_str)
1798{
1799 enum wp_mode wp_mode = WP_MODE_UNKNOWN;
1800
1801 if (!strcasecmp(mode_str, "hardware"))
1802 wp_mode = WP_MODE_HARDWARE;
1803 else if (!strcasecmp(mode_str, "power_cycle"))
1804 wp_mode = WP_MODE_POWER_CYCLE;
1805 else if (!strcasecmp(mode_str, "permanent"))
1806 wp_mode = WP_MODE_PERMANENT;
1807
1808 return wp_mode;
1809}
1810
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001811static int w25q_disable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001812 enum wp_mode wp_mode)
1813{
1814 int ret = 1;
David Hendricks1c09f802012-10-03 11:03:48 -07001815 struct w25q_status_2 sr2;
1816 uint8_t tmp;
1817
1818 switch (wp_mode) {
1819 case WP_MODE_HARDWARE:
1820 ret = w25_set_srp0(flash, 0);
1821 break;
1822 case WP_MODE_POWER_CYCLE:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001823 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001824 memcpy(&sr2, &tmp, 1);
1825 if (sr2.srp1) {
1826 msg_cerr("%s(): must disconnect power to disable "
1827 "write-protection\n", __func__);
1828 } else {
1829 ret = 0;
1830 }
1831 break;
1832 case WP_MODE_PERMANENT:
1833 msg_cerr("%s(): cannot disable permanent write-protection\n",
1834 __func__);
1835 break;
1836 default:
1837 msg_cerr("%s(): invalid mode specified\n", __func__);
1838 break;
1839 }
1840
1841 if (ret)
1842 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001843
David Hendricks1c09f802012-10-03 11:03:48 -07001844 return ret;
1845}
1846
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001847static int w25q_disable_writeprotect_default(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001848{
1849 return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE);
1850}
1851
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001852static int w25q_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001853 enum wp_mode wp_mode)
1854{
1855 int ret = 1;
1856 struct w25q_status sr1;
1857 struct w25q_status_2 sr2;
1858 uint8_t tmp;
1859
1860 switch (wp_mode) {
1861 case WP_MODE_HARDWARE:
1862 if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) {
1863 msg_cerr("%s(): cannot disable power cycle WP mode\n",
1864 __func__);
1865 break;
1866 }
1867
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301868 tmp = do_read_status(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001869 memcpy(&sr1, &tmp, 1);
1870 if (sr1.srp0)
1871 ret = 0;
1872 else
1873 ret = w25_set_srp0(flash, 1);
1874
1875 break;
1876 case WP_MODE_POWER_CYCLE:
1877 if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) {
1878 msg_cerr("%s(): cannot disable hardware WP mode\n",
1879 __func__);
1880 break;
1881 }
1882
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001883 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001884 memcpy(&sr2, &tmp, 1);
1885 if (sr2.srp1)
1886 ret = 0;
1887 else
1888 ret = w25q_set_srp1(flash, 1);
1889
1890 break;
1891 case WP_MODE_PERMANENT:
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05301892 tmp = do_read_status(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001893 memcpy(&sr1, &tmp, 1);
1894 if (sr1.srp0 == 0) {
1895 ret = w25_set_srp0(flash, 1);
1896 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001897 msg_perr("%s(): cannot enable SRP0 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001898 "permanent WP\n", __func__);
1899 break;
1900 }
1901 }
1902
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001903 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001904 memcpy(&sr2, &tmp, 1);
1905 if (sr2.srp1 == 0) {
1906 ret = w25q_set_srp1(flash, 1);
1907 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001908 msg_perr("%s(): cannot enable SRP1 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001909 "permanent WP\n", __func__);
1910 break;
1911 }
1912 }
1913
1914 break;
David Hendricksf1bd8802012-10-30 11:37:57 -07001915 default:
1916 msg_perr("%s(): invalid mode %d\n", __func__, wp_mode);
1917 break;
David Hendricks1c09f802012-10-03 11:03:48 -07001918 }
1919
1920 if (ret)
1921 msg_cerr("%s(): error=%d.\n", __func__, ret);
1922 return ret;
1923}
1924
1925/* W25P, W25X, and many flash chips from various vendors */
David Hendricksf7924d12010-06-10 21:26:44 -07001926struct wp wp_w25 = {
David Hendricks0f7f5382011-02-11 18:12:31 -08001927 .list_ranges = w25_list_ranges,
David Hendricksf7924d12010-06-10 21:26:44 -07001928 .set_range = w25_set_range,
1929 .enable = w25_enable_writeprotect,
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001930 .disable = w25_disable_writeprotect,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001931 .wp_status = w25_wp_status,
David Hendricks1c09f802012-10-03 11:03:48 -07001932
1933};
1934
1935/* W25Q series has features such as a second status register and SFDP */
1936struct wp wp_w25q = {
1937 .list_ranges = w25_list_ranges,
1938 .set_range = w25_set_range,
1939 .enable = w25q_enable_writeprotect,
1940 /*
1941 * By default, disable hardware write-protection. We may change
1942 * this later if we want to add fine-grained write-protect disable
1943 * as a command-line option.
1944 */
1945 .disable = w25q_disable_writeprotect_default,
1946 .wp_status = w25q_wp_status,
David Hendricksf7924d12010-06-10 21:26:44 -07001947};
David Hendrickse0512a72014-07-15 20:30:47 -07001948
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001949/* W25Q large series has 4 block-protect bits */
1950struct wp wp_w25q_large = {
1951 .list_ranges = w25_list_ranges,
1952 .set_range = w25q_large_set_range,
1953 .enable = w25q_enable_writeprotect,
1954 /*
1955 * By default, disable hardware write-protection. We may change
1956 * this later if we want to add fine-grained write-protect disable
1957 * as a command-line option.
1958 */
1959 .disable = w25q_disable_writeprotect_default,
1960 .wp_status = w25q_large_wp_status,
1961};
1962
Edward O'Callaghan3b996502020-04-12 20:46:51 +10001963static struct wp_range_descriptor gd25q32_cmp0_ranges[] = {
David Hendricksaf3944a2014-07-28 18:37:40 -07001964 /* none, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001965 { { }, 0x00, {0, 0} },
1966 { { }, 0x08, {0, 0} },
1967 { { }, 0x10, {0, 0} },
1968 { { }, 0x18, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001969
David Hendricks148a4bf2015-03-13 21:02:42 -07001970 { { }, 0x01, {0x3f0000, 64 * 1024} },
1971 { { }, 0x02, {0x3e0000, 128 * 1024} },
1972 { { }, 0x03, {0x3c0000, 256 * 1024} },
1973 { { }, 0x04, {0x380000, 512 * 1024} },
1974 { { }, 0x05, {0x300000, 1024 * 1024} },
1975 { { }, 0x06, {0x200000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001976
David Hendricks148a4bf2015-03-13 21:02:42 -07001977 { { }, 0x09, {0x000000, 64 * 1024} },
1978 { { }, 0x0a, {0x000000, 128 * 1024} },
1979 { { }, 0x0b, {0x000000, 256 * 1024} },
1980 { { }, 0x0c, {0x000000, 512 * 1024} },
1981 { { }, 0x0d, {0x000000, 1024 * 1024} },
1982 { { }, 0x0e, {0x000000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001983
1984 /* all, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001985 { { }, 0x07, {0x000000, 4096 * 1024} },
1986 { { }, 0x0f, {0x000000, 4096 * 1024} },
1987 { { }, 0x17, {0x000000, 4096 * 1024} },
1988 { { }, 0x1f, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001989
David Hendricks148a4bf2015-03-13 21:02:42 -07001990 { { }, 0x11, {0x3ff000, 4 * 1024} },
1991 { { }, 0x12, {0x3fe000, 8 * 1024} },
1992 { { }, 0x13, {0x3fc000, 16 * 1024} },
1993 { { }, 0x14, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1994 { { }, 0x15, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1995 { { }, 0x16, {0x3f8000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001996
David Hendricks148a4bf2015-03-13 21:02:42 -07001997 { { }, 0x19, {0x000000, 4 * 1024} },
1998 { { }, 0x1a, {0x000000, 8 * 1024} },
1999 { { }, 0x1b, {0x000000, 16 * 1024} },
2000 { { }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2001 { { }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2002 { { }, 0x1e, {0x000000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002003};
2004
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002005static struct wp_range_descriptor gd25q32_cmp1_ranges[] = {
Martin Roth563a1fe2017-04-18 14:26:27 -06002006 /* All, bp4 and bp3 => don't care */
2007 { { }, 0x00, {0x000000, 4096 * 1024} }, /* All */
2008 { { }, 0x08, {0x000000, 4096 * 1024} },
2009 { { }, 0x10, {0x000000, 4096 * 1024} },
2010 { { }, 0x18, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002011
David Hendricks148a4bf2015-03-13 21:02:42 -07002012 { { }, 0x01, {0x000000, 4032 * 1024} },
2013 { { }, 0x02, {0x000000, 3968 * 1024} },
2014 { { }, 0x03, {0x000000, 3840 * 1024} },
2015 { { }, 0x04, {0x000000, 3584 * 1024} },
2016 { { }, 0x05, {0x000000, 3 * 1024 * 1024} },
2017 { { }, 0x06, {0x000000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002018
David Hendricks148a4bf2015-03-13 21:02:42 -07002019 { { }, 0x09, {0x010000, 4032 * 1024} },
2020 { { }, 0x0a, {0x020000, 3968 * 1024} },
2021 { { }, 0x0b, {0x040000, 3840 * 1024} },
2022 { { }, 0x0c, {0x080000, 3584 * 1024} },
2023 { { }, 0x0d, {0x100000, 3 * 1024 * 1024} },
2024 { { }, 0x0e, {0x200000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002025
Martin Roth563a1fe2017-04-18 14:26:27 -06002026 /* None, bp4 and bp3 => don't care */
2027 { { }, 0x07, {0, 0} }, /* None */
2028 { { }, 0x0f, {0, 0} },
2029 { { }, 0x17, {0, 0} },
2030 { { }, 0x1f, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002031
David Hendricks148a4bf2015-03-13 21:02:42 -07002032 { { }, 0x11, {0x000000, 4092 * 1024} },
2033 { { }, 0x12, {0x000000, 4088 * 1024} },
2034 { { }, 0x13, {0x000000, 4080 * 1024} },
2035 { { }, 0x14, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
2036 { { }, 0x15, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
2037 { { }, 0x16, {0x000000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002038
David Hendricks148a4bf2015-03-13 21:02:42 -07002039 { { }, 0x19, {0x001000, 4092 * 1024} },
2040 { { }, 0x1a, {0x002000, 4088 * 1024} },
2041 { { }, 0x1b, {0x040000, 4080 * 1024} },
2042 { { }, 0x1c, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
2043 { { }, 0x1d, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
2044 { { }, 0x1e, {0x080000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002045};
2046
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002047static struct wp_context gd25q32_wp = {
David Hendricksaf3944a2014-07-28 18:37:40 -07002048 /* TODO: map second status register */
2049 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
2050};
2051
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002052static struct wp_range_descriptor gd25q128_cmp0_ranges[] = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002053 /* none, bp4 and bp3 => don't care, others = 0 */
2054 { { .tb = 0 }, 0x00, {0, 0} },
2055 { { .tb = 0 }, 0x08, {0, 0} },
2056 { { .tb = 0 }, 0x10, {0, 0} },
2057 { { .tb = 0 }, 0x18, {0, 0} },
2058
2059 { { .tb = 0 }, 0x01, {0xfc0000, 256 * 1024} },
2060 { { .tb = 0 }, 0x02, {0xf80000, 512 * 1024} },
2061 { { .tb = 0 }, 0x03, {0xf00000, 1024 * 1024} },
2062 { { .tb = 0 }, 0x04, {0xe00000, 2048 * 1024} },
2063 { { .tb = 0 }, 0x05, {0xc00000, 4096 * 1024} },
2064 { { .tb = 0 }, 0x06, {0x800000, 8192 * 1024} },
2065
2066 { { .tb = 0 }, 0x09, {0x000000, 256 * 1024} },
2067 { { .tb = 0 }, 0x0a, {0x000000, 512 * 1024} },
2068 { { .tb = 0 }, 0x0b, {0x000000, 1024 * 1024} },
2069 { { .tb = 0 }, 0x0c, {0x000000, 2048 * 1024} },
2070 { { .tb = 0 }, 0x0d, {0x000000, 4096 * 1024} },
2071 { { .tb = 0 }, 0x0e, {0x000000, 8192 * 1024} },
2072
2073 /* all, bp4 and bp3 => don't care, others = 1 */
2074 { { .tb = 0 }, 0x07, {0x000000, 16384 * 1024} },
2075 { { .tb = 0 }, 0x0f, {0x000000, 16384 * 1024} },
2076 { { .tb = 0 }, 0x17, {0x000000, 16384 * 1024} },
2077 { { .tb = 0 }, 0x1f, {0x000000, 16384 * 1024} },
2078
2079 { { .tb = 0 }, 0x11, {0xfff000, 4 * 1024} },
2080 { { .tb = 0 }, 0x12, {0xffe000, 8 * 1024} },
2081 { { .tb = 0 }, 0x13, {0xffc000, 16 * 1024} },
2082 { { .tb = 0 }, 0x14, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
2083 { { .tb = 0 }, 0x15, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
2084
2085 { { .tb = 0 }, 0x19, {0x000000, 4 * 1024} },
2086 { { .tb = 0 }, 0x1a, {0x000000, 8 * 1024} },
2087 { { .tb = 0 }, 0x1b, {0x000000, 16 * 1024} },
2088 { { .tb = 0 }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2089 { { .tb = 0 }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2090 { { .tb = 0 }, 0x1e, {0x000000, 32 * 1024} },
2091};
2092
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002093static struct wp_range_descriptor gd25q128_cmp1_ranges[] = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002094 /* none, bp4 and bp3 => don't care, others = 0 */
2095 { { .tb = 1 }, 0x00, {0x000000, 16384 * 1024} },
2096 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
2097 { { .tb = 1 }, 0x10, {0x000000, 16384 * 1024} },
2098 { { .tb = 1 }, 0x18, {0x000000, 16384 * 1024} },
2099
2100 { { .tb = 1 }, 0x01, {0x000000, 16128 * 1024} },
2101 { { .tb = 1 }, 0x02, {0x000000, 15872 * 1024} },
2102 { { .tb = 1 }, 0x03, {0x000000, 15360 * 1024} },
2103 { { .tb = 1 }, 0x04, {0x000000, 14336 * 1024} },
2104 { { .tb = 1 }, 0x05, {0x000000, 12288 * 1024} },
2105 { { .tb = 1 }, 0x06, {0x000000, 8192 * 1024} },
2106
2107 { { .tb = 1 }, 0x09, {0x000000, 16128 * 1024} },
2108 { { .tb = 1 }, 0x0a, {0x000000, 15872 * 1024} },
2109 { { .tb = 1 }, 0x0b, {0x000000, 15360 * 1024} },
2110 { { .tb = 1 }, 0x0c, {0x000000, 14336 * 1024} },
2111 { { .tb = 1 }, 0x0d, {0x000000, 12288 * 1024} },
2112 { { .tb = 1 }, 0x0e, {0x000000, 8192 * 1024} },
2113
2114 /* none, bp4 and bp3 => don't care, others = 1 */
2115 { { .tb = 1 }, 0x07, {0x000000, 16384 * 1024} },
2116 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
2117 { { .tb = 1 }, 0x0f, {0x000000, 16384 * 1024} },
2118 { { .tb = 1 }, 0x17, {0x000000, 16384 * 1024} },
2119 { { .tb = 1 }, 0x1f, {0x000000, 16384 * 1024} },
2120
2121 { { .tb = 1 }, 0x11, {0x000000, 16380 * 1024} },
2122 { { .tb = 1 }, 0x12, {0x000000, 16376 * 1024} },
2123 { { .tb = 1 }, 0x13, {0x000000, 16368 * 1024} },
2124 { { .tb = 1 }, 0x14, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
2125 { { .tb = 1 }, 0x15, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
2126
2127 { { .tb = 1 }, 0x19, {0x001000, 16380 * 1024} },
2128 { { .tb = 1 }, 0x1a, {0x002000, 16376 * 1024} },
2129 { { .tb = 1 }, 0x1b, {0x004000, 16368 * 1024} },
2130 { { .tb = 1 }, 0x1c, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
2131 { { .tb = 1 }, 0x1d, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
2132 { { .tb = 1 }, 0x1e, {0x008000, 16352 * 1024} },
2133};
2134
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002135static struct wp_context gd25q128_wp = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002136 /* TODO: map second and third status registers */
2137 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
2138};
2139
David Hendricks83541d32014-07-15 20:58:21 -07002140/* FIXME: MX25L6406 has same ID as MX25L6405D */
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002141static struct wp_range_descriptor mx25l6406e_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002142 { { }, 0, {0, 0} }, /* none */
2143 { { }, 0x1, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
2144 { { }, 0x2, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
2145 { { }, 0x3, {0x7a0000, 64 * 8 * 1024} }, /* blocks 120-127 */
2146 { { }, 0x4, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
2147 { { }, 0x5, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
2148 { { }, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricks83541d32014-07-15 20:58:21 -07002149
David Hendricks148a4bf2015-03-13 21:02:42 -07002150 { { }, 0x7, {0x000000, 64 * 128 * 1024} }, /* all */
2151 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2152 { { }, 0x9, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2153 { { }, 0xa, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
2154 { { }, 0xb, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
2155 { { }, 0xc, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
2156 { { }, 0xd, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
2157 { { }, 0xe, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
2158 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricks83541d32014-07-15 20:58:21 -07002159};
2160
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002161static struct wp_context mx25l6406e_wp = {
David Hendricks83541d32014-07-15 20:58:21 -07002162 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002163 .descrs = &mx25l6406e_ranges[0],
David Hendricks83541d32014-07-15 20:58:21 -07002164};
David Hendrickse0512a72014-07-15 20:30:47 -07002165
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002166static struct wp_range_descriptor mx25l6495f_tb0_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002167 { { }, 0, {0, 0} }, /* none */
2168 { { }, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
2169 { { }, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
2170 { { }, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
David Hendricksc3496092014-11-13 17:20:55 -08002171
David Hendricks148a4bf2015-03-13 21:02:42 -07002172 { { }, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
2173 { { }, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
2174 { { }, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
2175 { { }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
2176 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2177 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2178 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2179 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2180 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2181 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2182 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2183 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002184};
2185
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002186static struct wp_range_descriptor mx25l6495f_tb1_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002187 { { }, 0, {0, 0} }, /* none */
2188 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2189 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2190 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2191 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2192 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2193 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2194 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2195 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2196 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2197 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2198 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2199 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2200 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2201 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2202 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002203};
2204
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002205static struct wp_context mx25l6495f_wp = {
David Hendricksc3496092014-11-13 17:20:55 -08002206 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2207};
2208
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002209static struct wp_range_descriptor mx25l25635f_tb0_ranges[] = {
Vic Yang848bfd12018-03-23 10:24:07 -07002210 { { }, 0, {0, 0} }, /* none */
2211 { { }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* block 511 */
2212 { { }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* blocks 510-511 */
2213 { { }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* blocks 508-511 */
2214 { { }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* blocks 504-511 */
2215 { { }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* blocks 496-511 */
2216 { { }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* blocks 480-511 */
2217 { { }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* blocks 448-511 */
2218 { { }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* blocks 384-511 */
2219 { { }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* blocks 256-511 */
2220 { { }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* all */
2221 { { }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* all */
2222 { { }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* all */
2223 { { }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* all */
2224 { { }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* all */
2225 { { }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* all */
2226};
2227
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002228static struct wp_range_descriptor mx25l25635f_tb1_ranges[] = {
Vic Yang848bfd12018-03-23 10:24:07 -07002229 { { }, 0, {0, 0} }, /* none */
2230 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2231 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2232 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2233 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2234 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2235 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2236 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2237 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
2238 { { }, 0x9, {0x000000, 64 * 256 * 1024} }, /* blocks 0-255 */
2239 { { }, 0xa, {0x000000, 64 * 512 * 1024} }, /* all */
2240 { { }, 0xb, {0x000000, 64 * 512 * 1024} }, /* all */
2241 { { }, 0xc, {0x000000, 64 * 512 * 1024} }, /* all */
2242 { { }, 0xd, {0x000000, 64 * 512 * 1024} }, /* all */
2243 { { }, 0xe, {0x000000, 64 * 512 * 1024} }, /* all */
2244 { { }, 0xf, {0x000000, 64 * 512 * 1024} }, /* all */
2245};
2246
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002247static struct wp_context mx25l25635f_wp = {
Vic Yang848bfd12018-03-23 10:24:07 -07002248 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2249};
2250
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002251static struct wp_range_descriptor s25fs128s_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002252 { { .tb = 1 }, 0, {0, 0} }, /* none */
2253 { { .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* lower 64th */
2254 { { .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* lower 32nd */
2255 { { .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* lower 16th */
2256 { { .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* lower 8th */
2257 { { .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* lower 4th */
2258 { { .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* lower half */
2259 { { .tb = 1 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002260
David Hendricks148a4bf2015-03-13 21:02:42 -07002261 { { .tb = 0 }, 0, {0, 0} }, /* none */
2262 { { .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* upper 64th */
2263 { { .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* upper 32nd */
2264 { { .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* upper 16th */
2265 { { .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* upper 8th */
2266 { { .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* upper 4th */
2267 { { .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* upper half */
2268 { { .tb = 0 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002269};
2270
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002271static struct wp_context s25fs128s_wp = {
David Hendricksa9884852014-12-11 15:31:12 -08002272 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
David Hendricks148a4bf2015-03-13 21:02:42 -07002273 .get_modifier_bits = s25f_get_modifier_bits,
2274 .set_modifier_bits = s25f_set_modifier_bits,
David Hendricksa9884852014-12-11 15:31:12 -08002275};
2276
David Hendricksc694bb82015-02-25 14:52:17 -08002277
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002278static struct wp_range_descriptor s25fl256s_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002279 { { .tb = 1 }, 0, {0, 0} }, /* none */
2280 { { .tb = 1 }, 0x1, {0x000000, 512 * 1024} }, /* lower 64th */
2281 { { .tb = 1 }, 0x2, {0x000000, 1024 * 1024} }, /* lower 32nd */
2282 { { .tb = 1 }, 0x3, {0x000000, 2048 * 1024} }, /* lower 16th */
2283 { { .tb = 1 }, 0x4, {0x000000, 4096 * 1024} }, /* lower 8th */
2284 { { .tb = 1 }, 0x5, {0x000000, 8192 * 1024} }, /* lower 4th */
2285 { { .tb = 1 }, 0x6, {0x000000, 16384 * 1024} }, /* lower half */
2286 { { .tb = 1 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
2287
2288 { { .tb = 0 }, 0, {0, 0} }, /* none */
2289 { { .tb = 0 }, 0x1, {0x1f80000, 512 * 1024} }, /* upper 64th */
2290 { { .tb = 0 }, 0x2, {0x1f00000, 1024 * 1024} }, /* upper 32nd */
2291 { { .tb = 0 }, 0x3, {0x1e00000, 2048 * 1024} }, /* upper 16th */
2292 { { .tb = 0 }, 0x4, {0x1c00000, 4096 * 1024} }, /* upper 8th */
2293 { { .tb = 0 }, 0x5, {0x1800000, 8192 * 1024} }, /* upper 4th */
2294 { { .tb = 0 }, 0x6, {0x1000000, 16384 * 1024} }, /* upper half */
2295 { { .tb = 0 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
David Hendricksc694bb82015-02-25 14:52:17 -08002296};
2297
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002298static struct wp_context s25fl256s_wp = {
David Hendricksc694bb82015-02-25 14:52:17 -08002299 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
David Hendricks148a4bf2015-03-13 21:02:42 -07002300 .get_modifier_bits = s25f_get_modifier_bits,
2301 .set_modifier_bits = s25f_set_modifier_bits,
David Hendricksc694bb82015-02-25 14:52:17 -08002302};
2303
David Hendrickse0512a72014-07-15 20:30:47 -07002304/* Given a flash chip, this function returns its writeprotect info. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002305static int generic_range_table(const struct flashctx *flash,
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002306 struct wp_context **wp,
David Hendrickse0512a72014-07-15 20:30:47 -07002307 int *num_entries)
2308{
2309 *wp = NULL;
2310 *num_entries = 0;
2311
Patrick Georgif3fa2992017-02-02 16:24:44 +01002312 switch (flash->chip->manufacture_id) {
David Hendricksaf3944a2014-07-28 18:37:40 -07002313 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002314 switch(flash->chip->model_id) {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002315
Martin Roth563a1fe2017-04-18 14:26:27 -06002316 case GIGADEVICE_GD25LQ32:
David Hendricksaf3944a2014-07-28 18:37:40 -07002317 case GIGADEVICE_GD25Q32: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002318 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricksaf3944a2014-07-28 18:37:40 -07002319 *wp = &gd25q32_wp;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002320
David Hendricksaf3944a2014-07-28 18:37:40 -07002321 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002322 (*wp)->descrs = &gd25q32_cmp0_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002323 *num_entries = ARRAY_SIZE(gd25q32_cmp0_ranges);
2324 } else { /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002325 (*wp)->descrs = &gd25q32_cmp1_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002326 *num_entries = ARRAY_SIZE(gd25q32_cmp1_ranges);
2327 }
2328
2329 break;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002330 }
Furquan Shaikh62cd8102016-07-17 23:04:06 -07002331 case GIGADEVICE_GD25Q128:
Aaron Durbin6c957d72018-08-20 09:31:01 -06002332 case GIGADEVICE_GD25LQ128CD: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002333 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002334 *wp = &gd25q128_wp;
2335
2336 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002337 (*wp)->descrs = &gd25q128_cmp0_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002338 *num_entries = ARRAY_SIZE(gd25q128_cmp0_ranges);
2339 } else { /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002340 (*wp)->descrs = &gd25q128_cmp1_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002341 *num_entries = ARRAY_SIZE(gd25q128_cmp1_ranges);
2342 }
2343
2344 break;
David Hendricksaf3944a2014-07-28 18:37:40 -07002345 }
2346 default:
2347 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
2348 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002349 flash->chip->model_id);
David Hendricksaf3944a2014-07-28 18:37:40 -07002350 return -1;
2351 }
2352 break;
David Hendricks83541d32014-07-15 20:58:21 -07002353 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002354 switch (flash->chip->model_id) {
David Hendricks83541d32014-07-15 20:58:21 -07002355 case MACRONIX_MX25L6405:
2356 /* FIXME: MX25L64* chips have mixed capabilities and
2357 share IDs */
2358 *wp = &mx25l6406e_wp;
2359 *num_entries = ARRAY_SIZE(mx25l6406e_ranges);
2360 break;
David Hendricksc3496092014-11-13 17:20:55 -08002361 case MACRONIX_MX25L6495F: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002362 uint8_t cr = mx25l_read_config_register(flash);
David Hendricksc3496092014-11-13 17:20:55 -08002363
2364 *wp = &mx25l6495f_wp;
2365 if (!(cr & (1 << 3))) { /* T/B == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002366 (*wp)->descrs = &mx25l6495f_tb0_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002367 *num_entries = ARRAY_SIZE(mx25l6495f_tb0_ranges);
2368 } else { /* T/B == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002369 (*wp)->descrs = &mx25l6495f_tb1_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002370 *num_entries = ARRAY_SIZE(mx25l6495f_tb1_ranges);
2371 }
2372 break;
2373 }
Vic Yang848bfd12018-03-23 10:24:07 -07002374 case MACRONIX_MX25L25635F: {
2375 uint8_t cr = mx25l_read_config_register(flash);
2376
2377 *wp = &mx25l25635f_wp;
2378 if (!(cr & (1 << 3))) { /* T/B == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002379 (*wp)->descrs = &mx25l25635f_tb0_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002380 *num_entries = ARRAY_SIZE(mx25l25635f_tb0_ranges);
2381 } else { /* T/B == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002382 (*wp)->descrs = &mx25l25635f_tb1_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002383 *num_entries = ARRAY_SIZE(mx25l25635f_tb1_ranges);
2384 }
2385 break;
2386 }
David Hendricks83541d32014-07-15 20:58:21 -07002387 default:
2388 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
2389 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002390 flash->chip->model_id);
David Hendricks83541d32014-07-15 20:58:21 -07002391 return -1;
2392 }
2393 break;
David Hendricksa9884852014-12-11 15:31:12 -08002394 case SPANSION_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002395 switch (flash->chip->model_id) {
David Hendricksa9884852014-12-11 15:31:12 -08002396 case SPANSION_S25FS128S_L:
2397 case SPANSION_S25FS128S_S: {
David Hendricksa9884852014-12-11 15:31:12 -08002398 *wp = &s25fs128s_wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002399 (*wp)->descrs = s25fs128s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002400 *num_entries = ARRAY_SIZE(s25fs128s_ranges);
David Hendricksa9884852014-12-11 15:31:12 -08002401 break;
2402 }
David Hendricksc694bb82015-02-25 14:52:17 -08002403 case SPANSION_S25FL256S_UL:
2404 case SPANSION_S25FL256S_US: {
David Hendricksc694bb82015-02-25 14:52:17 -08002405 *wp = &s25fl256s_wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002406 (*wp)->descrs = s25fl256s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002407 *num_entries = ARRAY_SIZE(s25fl256s_ranges);
David Hendricksc694bb82015-02-25 14:52:17 -08002408 break;
2409 }
David Hendricksa9884852014-12-11 15:31:12 -08002410 default:
2411 msg_cerr("%s():%d Spansion flash chip mismatch (0x%04x)"
Patrick Georgif3fa2992017-02-02 16:24:44 +01002412 ", aborting\n", __func__, __LINE__,
2413 flash->chip->model_id);
David Hendricksa9884852014-12-11 15:31:12 -08002414 return -1;
2415 }
2416 break;
David Hendrickse0512a72014-07-15 20:30:47 -07002417 default:
2418 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01002419 __func__, flash->chip->manufacture_id);
David Hendrickse0512a72014-07-15 20:30:47 -07002420 return -1;
2421 }
2422
2423 return 0;
2424}
2425
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002426static uint8_t generic_get_bp_mask(struct wp_context *wp)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002427{
2428 return ((1 << (wp->sr1.bp0_pos + wp->sr1.bp_bits)) - 1) ^ \
2429 ((1 << wp->sr1.bp0_pos) - 1);
2430}
2431
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002432static uint8_t generic_get_status_check_mask(struct wp_context *wp)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002433{
2434 return generic_get_bp_mask(wp) | 1 << wp->sr1.srp_pos;
2435}
2436
David Hendrickse0512a72014-07-15 20:30:47 -07002437/* Given a [start, len], this function finds a block protect bit combination
2438 * (if possible) and sets the corresponding bits in "status". Remaining bits
2439 * are preserved. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002440static int generic_range_to_status(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002441 unsigned int start, unsigned int len,
Marco Chen9d5bddb2020-02-11 17:12:56 +08002442 uint8_t *status, uint8_t *check_mask)
David Hendrickse0512a72014-07-15 20:30:47 -07002443{
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002444 struct wp_context *wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002445 struct wp_range_descriptor *r;
David Hendrickse0512a72014-07-15 20:30:47 -07002446 int i, range_found = 0, num_entries;
2447 uint8_t bp_mask;
2448
2449 if (generic_range_table(flash, &wp, &num_entries))
2450 return -1;
2451
Marco Chen9d5bddb2020-02-11 17:12:56 +08002452 bp_mask = generic_get_bp_mask(wp);
David Hendrickse0512a72014-07-15 20:30:47 -07002453
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002454 for (i = 0, r = &wp->descrs[0]; i < num_entries; i++, r++) {
David Hendrickse0512a72014-07-15 20:30:47 -07002455 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
2456 start, len, r->range.start, r->range.len);
2457 if ((start == r->range.start) && (len == r->range.len)) {
2458 *status &= ~(bp_mask);
2459 *status |= r->bp << (wp->sr1.bp0_pos);
David Hendricks148a4bf2015-03-13 21:02:42 -07002460
2461 if (wp->set_modifier_bits) {
2462 if (wp->set_modifier_bits(flash, &r->m) < 0) {
Edward O'Callaghan0b662c12021-01-22 00:30:24 +11002463 msg_cerr("error setting modifier bits for range.\n");
David Hendricks148a4bf2015-03-13 21:02:42 -07002464 return -1;
2465 }
2466 }
2467
David Hendrickse0512a72014-07-15 20:30:47 -07002468 range_found = 1;
2469 break;
2470 }
2471 }
2472
2473 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11002474 msg_cerr("%s: matching range not found\n", __func__);
David Hendrickse0512a72014-07-15 20:30:47 -07002475 return -1;
2476 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002477
Marco Chen9d5bddb2020-02-11 17:12:56 +08002478 *check_mask = generic_get_status_check_mask(wp);
David Hendrickse0512a72014-07-15 20:30:47 -07002479 return 0;
2480}
2481
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002482static int generic_status_to_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002483 const uint8_t sr1, unsigned int *start, unsigned int *len)
2484{
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002485 struct wp_context *wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002486 struct wp_range_descriptor *r;
Duncan Laurie04ca1172015-03-12 09:25:34 -07002487 int num_entries, i, status_found = 0;
David Hendrickse0512a72014-07-15 20:30:47 -07002488 uint8_t sr1_bp;
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +11002489 struct modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -07002490
2491 if (generic_range_table(flash, &wp, &num_entries))
2492 return -1;
2493
David Hendricks148a4bf2015-03-13 21:02:42 -07002494 /* modifier bits may be compared more than once, so get them here */
Edward O'Callaghanadcc7782019-12-04 14:50:14 +11002495 if (wp->get_modifier_bits && wp->get_modifier_bits(flash, &m) < 0)
David Hendricks148a4bf2015-03-13 21:02:42 -07002496 return -1;
David Hendricks148a4bf2015-03-13 21:02:42 -07002497
David Hendrickse0512a72014-07-15 20:30:47 -07002498 sr1_bp = (sr1 >> wp->sr1.bp0_pos) & ((1 << wp->sr1.bp_bits) - 1);
2499
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002500 for (i = 0, r = &wp->descrs[0]; i < num_entries; i++, r++) {
David Hendricks148a4bf2015-03-13 21:02:42 -07002501 if (wp->get_modifier_bits) {
2502 if (memcmp(&m, &r->m, sizeof(m)))
2503 continue;
2504 }
David Hendrickse0512a72014-07-15 20:30:47 -07002505 msg_cspew("comparing 0x%02x 0x%02x\n", sr1_bp, r->bp);
2506 if (sr1_bp == r->bp) {
2507 *start = r->range.start;
2508 *len = r->range.len;
2509 status_found = 1;
2510 break;
2511 }
2512 }
2513
2514 if (!status_found) {
2515 msg_cerr("matching status not found\n");
2516 return -1;
2517 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002518
David Hendrickse0512a72014-07-15 20:30:47 -07002519 return 0;
2520}
2521
2522/* Given a [start, len], this function calls generic_range_to_status() to
2523 * convert it to flash-chip-specific range bits, then sets into status register.
2524 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002525static int generic_set_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002526 unsigned int start, unsigned int len)
2527{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002528 uint8_t status, expected, check_mask;
David Hendrickse0512a72014-07-15 20:30:47 -07002529
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302530 status = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002531 msg_cdbg("%s: old status: 0x%02x\n", __func__, status);
2532
2533 expected = status; /* preserve non-bp bits */
Marco Chen9d5bddb2020-02-11 17:12:56 +08002534 if (generic_range_to_status(flash, start, len, &expected, &check_mask))
David Hendrickse0512a72014-07-15 20:30:47 -07002535 return -1;
2536
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302537 do_write_status(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002538
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302539 status = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002540 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002541 if ((status & check_mask) != (expected & check_mask)) {
2542 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2543 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002544 return 1;
2545 }
David Hendrickse0512a72014-07-15 20:30:47 -07002546 return 0;
2547}
2548
2549/* Set/clear the status regsiter write protect bit in SR1. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002550static int generic_set_srp0(const struct flashctx *flash, int enable)
David Hendrickse0512a72014-07-15 20:30:47 -07002551{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002552 uint8_t status, expected, check_mask;
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002553 struct wp_context *wp;
David Hendrickse0512a72014-07-15 20:30:47 -07002554 int num_entries;
2555
2556 if (generic_range_table(flash, &wp, &num_entries))
2557 return -1;
2558
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302559 expected = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002560 msg_cdbg("%s: old status: 0x%02x\n", __func__, expected);
2561
2562 if (enable)
2563 expected |= 1 << wp->sr1.srp_pos;
2564 else
2565 expected &= ~(1 << wp->sr1.srp_pos);
2566
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302567 do_write_status(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002568
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302569 status = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002570 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002571
2572 check_mask = generic_get_status_check_mask(wp);
2573 msg_cdbg("%s: check mask: 0x%02x\n", __func__, check_mask);
2574 if ((status & check_mask) != (expected & check_mask)) {
2575 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2576 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002577 return -1;
Marco Chen9d5bddb2020-02-11 17:12:56 +08002578 }
David Hendrickse0512a72014-07-15 20:30:47 -07002579
2580 return 0;
2581}
2582
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002583static int generic_enable_writeprotect(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002584 enum wp_mode wp_mode)
2585{
2586 int ret;
2587
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002588 if (wp_mode != WP_MODE_HARDWARE) {
David Hendrickse0512a72014-07-15 20:30:47 -07002589 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
2590 return 1;
2591 }
2592
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002593 ret = generic_set_srp0(flash, 1);
David Hendrickse0512a72014-07-15 20:30:47 -07002594 if (ret)
2595 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002596
David Hendrickse0512a72014-07-15 20:30:47 -07002597 return ret;
2598}
2599
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002600static int generic_disable_writeprotect(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002601{
2602 int ret;
2603
2604 ret = generic_set_srp0(flash, 0);
2605 if (ret)
2606 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002607
David Hendrickse0512a72014-07-15 20:30:47 -07002608 return ret;
2609}
2610
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002611static int generic_list_ranges(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002612{
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002613 struct wp_context *wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002614 struct wp_range_descriptor *r;
David Hendrickse0512a72014-07-15 20:30:47 -07002615 int i, num_entries;
2616
2617 if (generic_range_table(flash, &wp, &num_entries))
2618 return -1;
2619
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002620 r = &wp->descrs[0];
David Hendrickse0512a72014-07-15 20:30:47 -07002621 for (i = 0; i < num_entries; i++) {
2622 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
2623 r->range.start, r->range.len);
2624 r++;
2625 }
2626
2627 return 0;
2628}
2629
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002630static int wp_context_status(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002631{
2632 uint8_t sr1;
2633 unsigned int start, len;
2634 int ret = 0;
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002635 struct wp_context *wp;
David Hendrickse0512a72014-07-15 20:30:47 -07002636 int num_entries, wp_en;
2637
2638 if (generic_range_table(flash, &wp, &num_entries))
2639 return -1;
2640
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +05302641 sr1 = do_read_status(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002642 wp_en = (sr1 >> wp->sr1.srp_pos) & 1;
2643
2644 msg_cinfo("WP: status: 0x%04x\n", sr1);
2645 msg_cinfo("WP: status.srp0: %x\n", wp_en);
2646 /* FIXME: SRP1 is not really generic, but we probably should print
2647 * it anyway to have consistent output. #legacycruft */
2648 msg_cinfo("WP: status.srp1: %x\n", 0);
2649 msg_cinfo("WP: write protect is %s.\n",
2650 wp_en ? "enabled" : "disabled");
2651
2652 msg_cinfo("WP: write protect range: ");
2653 if (generic_status_to_range(flash, sr1, &start, &len)) {
2654 msg_cinfo("(cannot resolve the range)\n");
2655 ret = -1;
2656 } else {
2657 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
2658 }
2659
2660 return ret;
2661}
2662
2663struct wp wp_generic = {
2664 .list_ranges = generic_list_ranges,
2665 .set_range = generic_set_range,
2666 .enable = generic_enable_writeprotect,
2667 .disable = generic_disable_writeprotect,
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002668 .wp_status = wp_context_status,
David Hendrickse0512a72014-07-15 20:30:47 -07002669};