blob: d85d328fe1fce3a898e5680224b5a1bfa017eb03 [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'Callaghanc69f6b82019-12-05 16:49:21 +110074struct w25q_status {
75 /* this maps to register layout -- do not change ordering */
76 unsigned char busy : 1;
77 unsigned char wel : 1;
78 unsigned char bp0 : 1;
79 unsigned char bp1 : 1;
80 unsigned char bp2 : 1;
81 unsigned char tb : 1;
82 unsigned char sec : 1;
83 unsigned char srp0 : 1;
84} __attribute__ ((packed));
85
86/* Status register for large flash layouts with 4 BP bits */
87struct w25q_status_large {
88 unsigned char busy : 1;
89 unsigned char wel : 1;
90 unsigned char bp0 : 1;
91 unsigned char bp1 : 1;
92 unsigned char bp2 : 1;
93 unsigned char bp3 : 1;
94 unsigned char tb : 1;
95 unsigned char srp0 : 1;
96} __attribute__ ((packed));
97
98struct w25q_status_2 {
99 unsigned char srp1 : 1;
100 unsigned char qe : 1;
101 unsigned char rsvd : 6;
102} __attribute__ ((packed));
103
104int w25_range_to_status(const struct flashctx *flash,
105 unsigned int start, unsigned int len,
106 struct w25q_status *status);
107int w25_status_to_range(const struct flashctx *flash,
108 const struct w25q_status *status,
109 unsigned int *start, unsigned int *len);
110
David Hendrickse0512a72014-07-15 20:30:47 -0700111/*
David Hendrickse0512a72014-07-15 20:30:47 -0700112 * Mask to extract write-protect enable and range bits
113 * Status register 1:
114 * SRP0: bit 7
115 * range(BP2-BP0): bit 4-2
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800116 * range(BP3-BP0): bit 5-2 (large chips)
David Hendrickse0512a72014-07-15 20:30:47 -0700117 * Status register 2:
118 * SRP1: bit 1
119 */
120#define MASK_WP_AREA (0x9C)
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800121#define MASK_WP_AREA_LARGE (0x9C)
David Hendrickse0512a72014-07-15 20:30:47 -0700122#define MASK_WP2_AREA (0x01)
123
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000124static struct wp_range_descriptor en25f40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100125 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
126 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
127 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
128 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
129 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 448 * 1024} },
130 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 384 * 1024} },
131 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 256 * 1024} },
132 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 512 * 1024} },
David Hendricks57566ed2010-08-16 18:24:45 -0700133};
134
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000135static struct wp_range_descriptor en25q40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100136 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
137 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
138 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
139 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700140
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100141 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 448 * 1024} },
142 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 384 * 1024} },
143 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
144 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700145};
146
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000147static struct wp_range_descriptor en25q80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100148 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
149 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 1016 * 1024} },
150 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 1008 * 1024} },
151 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 992 * 1024} },
152 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 960 * 1024} },
153 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 896 * 1024} },
154 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 768 * 1024} },
155 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 1024 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700156};
157
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000158static struct wp_range_descriptor en25q32_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100159 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
160 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 4032 * 1024} },
161 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 3968 * 1024} },
162 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 3840 * 1024} },
163 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 3584 * 1024} },
164 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 3072 * 1024} },
165 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 2048 * 1024} },
166 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700167
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100168 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
169 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 4032 * 1024} },
170 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 3968 * 1024} },
171 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 3840 * 1024} },
172 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 3584 * 1024} },
173 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 3072 * 1024} },
174 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 2048 * 1024} },
175 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700176};
177
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000178static struct wp_range_descriptor en25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100179 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
180 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8128 * 1024} },
181 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 8064 * 1024} },
182 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7936 * 1024} },
183 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7680 * 1024} },
184 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 7168 * 1024} },
185 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 6144 * 1024} },
186 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700187
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100188 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
189 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 8128 * 1024} },
190 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 8064 * 1024} },
191 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 7936 * 1024} },
192 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 7680 * 1024} },
193 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 7168 * 1024} },
194 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 6144 * 1024} },
195 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700196};
197
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000198static struct wp_range_descriptor en25q128_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100199 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
200 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16320 * 1024} },
201 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 16256 * 1024} },
202 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 16128 * 1024} },
203 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 15872 * 1024} },
204 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 15360 * 1024} },
205 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 14336 * 1024} },
206 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 16384 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700207
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100208 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
209 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 16320 * 1024} },
210 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 16256 * 1024} },
211 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 16128 * 1024} },
212 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 15872 * 1024} },
213 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 15360 * 1024} },
214 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 14336 * 1024} },
215 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 16384 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700216};
217
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000218static struct wp_range_descriptor en25s64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100219 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
220 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8064 * 1024} },
221 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 7936 * 1024} },
222 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7680 * 1024} },
223 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7168 * 1024} },
224 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 6144 * 1024} },
225 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 4096 * 1024} },
226 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
Marc Jonesb2f90022014-04-29 17:37:23 -0600227
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100228 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
229 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x7e0000, 128 * 1024} },
230 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x7c0000, 256 * 1024} },
231 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x780000, 512 * 1024} },
232 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x700000, 1024 * 1024} },
233 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x600000, 2048 * 1024} },
234 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x400000, 4096 * 1024} },
235 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
Marc Jonesb2f90022014-04-29 17:37:23 -0600236};
237
David Hendricksf8f00c72011-02-01 12:39:46 -0800238/* mx25l1005 ranges also work for the mx25l1005c */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100239static struct wp_range_descriptor mx25l1005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100240 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
241 { .m = { .sec = X, .tb = X }, 0x1, {0x010000, 64 * 1024} },
242 { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
243 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800244};
245
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100246static struct wp_range_descriptor mx25l2005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100247 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
248 { .m = { .sec = X, .tb = X }, 0x1, {0x030000, 64 * 1024} },
249 { .m = { .sec = X, .tb = X }, 0x2, {0x020000, 128 * 1024} },
250 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 256 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800251};
252
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100253static struct wp_range_descriptor mx25l4005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100254 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
255 { .m = { .sec = X, .tb = X }, 0x1, {0x070000, 64 * 1 * 1024} }, /* block 7 */
256 { .m = { .sec = X, .tb = X }, 0x2, {0x060000, 64 * 2 * 1024} }, /* blocks 6-7 */
257 { .m = { .sec = X, .tb = X }, 0x3, {0x040000, 64 * 4 * 1024} }, /* blocks 4-7 */
258 { .m = { .sec = X, .tb = X }, 0x4, {0x000000, 512 * 1024} },
259 { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 512 * 1024} },
260 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 512 * 1024} },
261 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 512 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800262};
263
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100264static struct wp_range_descriptor mx25l8005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100265 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
266 { .m = { .sec = X, .tb = X }, 0x1, {0x0f0000, 64 * 1 * 1024} }, /* block 15 */
267 { .m = { .sec = X, .tb = X }, 0x2, {0x0e0000, 64 * 2 * 1024} }, /* blocks 14-15 */
268 { .m = { .sec = X, .tb = X }, 0x3, {0x0c0000, 64 * 4 * 1024} }, /* blocks 12-15 */
269 { .m = { .sec = X, .tb = X }, 0x4, {0x080000, 64 * 8 * 1024} }, /* blocks 8-15 */
270 { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
271 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
272 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800273};
274
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100275static struct wp_range_descriptor mx25l1605d_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100276 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
277 { .m = { .sec = X, .tb = 0 }, 0x1, {0x1f0000, 64 * 1 * 1024} }, /* block 31 */
278 { .m = { .sec = X, .tb = 0 }, 0x2, {0x1e0000, 64 * 2 * 1024} }, /* blocks 30-31 */
279 { .m = { .sec = X, .tb = 0 }, 0x3, {0x1c0000, 64 * 4 * 1024} }, /* blocks 28-31 */
280 { .m = { .sec = X, .tb = 0 }, 0x4, {0x180000, 64 * 8 * 1024} }, /* blocks 24-31 */
281 { .m = { .sec = X, .tb = 0 }, 0x5, {0x100000, 64 * 16 * 1024} }, /* blocks 16-31 */
282 { .m = { .sec = X, .tb = 0 }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
283 { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
David Hendricksf8f00c72011-02-01 12:39:46 -0800284
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100285 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 2048 * 1024} },
286 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
287 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
288 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 64 * 24 * 1024} }, /* blocks 0-23 */
289 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 64 * 28 * 1024} }, /* blocks 0-27 */
290 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 64 * 30 * 1024} }, /* blocks 0-29 */
291 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 64 * 31 * 1024} }, /* blocks 0-30 */
292 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
David Hendricksf8f00c72011-02-01 12:39:46 -0800293};
294
295/* FIXME: Is there an mx25l3205 (without a trailing letter)? */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100296static struct wp_range_descriptor mx25l3205d_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100297 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
298 { .m = { .sec = X, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
299 { .m = { .sec = X, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
300 { .m = { .sec = X, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
301 { .m = { .sec = X, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
302 { .m = { .sec = X, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
303 { .m = { .sec = X, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
304 { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksac72e362010-08-16 18:20:03 -0700305
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100306 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
307 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
308 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
309 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
310 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
311 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
312 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
313 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksac72e362010-08-16 18:20:03 -0700314};
315
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100316static struct wp_range_descriptor mx25u3235e_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100317 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
318 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
319 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
320 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
321 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
322 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
323 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
324 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
Vincent Palatin87e092a2013-02-28 15:46:14 -0800325
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100326 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
327 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
328 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
329 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
330 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
331 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
332 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
333 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
Vincent Palatin87e092a2013-02-28 15:46:14 -0800334};
335
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100336static struct wp_range_descriptor mx25u6435e_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100337 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
338 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 1 * 64 * 1024} }, /* block 127 */
339 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
340 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
341 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
342 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
343 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
344 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
Jongpil66a96492014-08-14 17:59:06 +0900345
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100346 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
347 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 96 * 64 * 1024} }, /* blocks 0-95 */
348 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 112 * 64 * 1024} }, /* blocks 0-111 */
349 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 120 * 64 * 1024} }, /* blocks 0-119 */
350 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 124 * 64 * 1024} }, /* blocks 0-123 */
351 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 126 * 64 * 1024} }, /* blocks 0-125 */
352 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 127 * 64 * 1024} }, /* blocks 0-126 */
353 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
Jongpil66a96492014-08-14 17:59:06 +0900354};
355
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600356#define MX25U12835E_TB (1 << 3)
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100357static struct wp_range_descriptor mx25u12835e_tb0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100358 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
359 { .m = { .sec = 0, .tb = 0 }, 0x1, {0xff0000, 1 * 64 * 1024} }, /* block 255 */
360 { .m = { .sec = 0, .tb = 0 }, 0x2, {0xfe0000, 2 * 64 * 1024} }, /* blocks 254-255 */
361 { .m = { .sec = 0, .tb = 0 }, 0x3, {0xfc0000, 4 * 64 * 1024} }, /* blocks 252-255 */
362 { .m = { .sec = 0, .tb = 0 }, 0x4, {0xf80000, 8 * 64 * 1024} }, /* blocks 248-255 */
363 { .m = { .sec = 0, .tb = 0 }, 0x5, {0xf00000, 16 * 64 * 1024} }, /* blocks 240-255 */
364 { .m = { .sec = 0, .tb = 0 }, 0x6, {0xe00000, 32 * 64 * 1024} }, /* blocks 224-255 */
365 { .m = { .sec = 0, .tb = 0 }, 0x7, {0xc00000, 64 * 64 * 1024} }, /* blocks 192-255 */
366 { .m = { .sec = 0, .tb = 0 }, 0x8, {0x800000, 128 * 64 * 1024} }, /* blocks 128-255 */
367 { .m = { .sec = 0, .tb = 0 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
368 { .m = { .sec = 0, .tb = 0 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
369 { .m = { .sec = 0, .tb = 0 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
370 { .m = { .sec = 0, .tb = 0 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
371 { .m = { .sec = 0, .tb = 0 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
372 { .m = { .sec = 0, .tb = 0 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
373 { .m = { .sec = 0, .tb = 0 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600374};
Alex Lu831c6092017-11-02 23:19:34 -0700375
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100376static struct wp_range_descriptor mx25u12835e_tb1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100377 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 1 * 64 * 1024} }, /* block 0 */
378 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
379 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
380 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
381 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
382 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
383 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
384 { .m = { .sec = 0, .tb = 1 }, 0x8, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
385 { .m = { .sec = 0, .tb = 1 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
386 { .m = { .sec = 0, .tb = 1 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
387 { .m = { .sec = 0, .tb = 1 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
388 { .m = { .sec = 0, .tb = 1 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
389 { .m = { .sec = 0, .tb = 1 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
390 { .m = { .sec = 0, .tb = 1 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
391 { .m = { .sec = 0, .tb = 1 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Alex Lu831c6092017-11-02 23:19:34 -0700392};
393
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100394static struct wp_range_descriptor n25q064_ranges[] = {
David Hendricksfe9123b2015-04-21 13:18:31 -0700395 /*
396 * Note: For N25Q064, sec (usually in bit position 6) is called BP3
397 * (block protect bit 3). It is only useful when all blocks are to
398 * be write-protected.
399 */
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100400 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
David Hendricksbfa624b2012-07-24 12:47:59 -0700401
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100402 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 64 * 1024} }, /* block 127 */
403 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
404 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
405 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
406 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
407 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
408 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700409
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100410 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, /* block 0 */
411 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
412 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
413 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
414 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
415 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
416 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700417
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100418 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 128 * 64 * 1024} }, /* all */
419 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 128 * 64 * 1024} }, /* all */
420 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 128 * 64 * 1024} }, /* all */
421 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 128 * 64 * 1024} }, /* all */
422 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 128 * 64 * 1024} }, /* all */
423 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 128 * 64 * 1024} }, /* all */
424 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 128 * 64 * 1024} }, /* all */
425 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* all */
David Hendricksbfa624b2012-07-24 12:47:59 -0700426};
427
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100428static struct wp_range_descriptor w25q16_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100429 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
430 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x1f0000, 64 * 1024} },
431 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x1e0000, 128 * 1024} },
432 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x1c0000, 256 * 1024} },
433 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x180000, 512 * 1024} },
434 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x100000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700435
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100436 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
437 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
438 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
439 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
440 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
441 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 2048 * 1024} },
442 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700443
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100444 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
445 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
446 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
447 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
448 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700449
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100450 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
451 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
452 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
453 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
454 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700455};
456
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100457static struct wp_range_descriptor w25q32_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100458 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
459 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
460 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
461 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
462 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
463 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
464 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700465
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100466 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
467 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
468 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
469 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
470 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
471 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 2048 * 1024} },
472 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700473
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100474 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x3ff000, 4 * 1024} },
475 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x3fe000, 8 * 1024} },
476 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x3fc000, 16 * 1024} },
477 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x3f8000, 32 * 1024} },
478 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x3f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700479
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100480 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
481 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
482 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
483 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
484 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700485};
486
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100487static struct wp_range_descriptor w25q80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100488 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
489 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0f0000, 64 * 1024} },
490 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0e0000, 128 * 1024} },
491 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0c0000, 256 * 1024} },
492 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700493
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100494 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
495 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
496 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
497 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
498 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
499 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700500
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100501 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
502 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
503 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
504 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
505 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700506
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100507 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
508 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
509 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
510 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
511 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700512};
513
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100514static struct wp_range_descriptor w25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100515 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
David Hendricks2c4a76c2010-06-28 14:00:43 -0700516
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100517 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
518 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
519 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
520 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
521 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
522 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700523
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100524 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
525 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
526 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
527 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
528 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
529 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
530 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700531
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100532 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
533 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
534 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
535 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
536 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700537
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100538 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
539 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
540 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
541 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
542 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700543};
544
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100545static struct wp_range_descriptor w25rq128_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100546 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* NONE */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530547
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100548 { .m = { .sec = 0, .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* Upper 1/64 */
549 { .m = { .sec = 0, .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* Upper 1/32 */
550 { .m = { .sec = 0, .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* Upper 1/16 */
551 { .m = { .sec = 0, .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* Upper 1/8 */
552 { .m = { .sec = 0, .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* Upper 1/4 */
553 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* Upper 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530554
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100555 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* Lower 1/64 */
556 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* Lower 1/32 */
557 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* Lower 1/16 */
558 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* Lower 1/8 */
559 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* Lower 1/4 */
560 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* Lower 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530561
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100562 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 16384 * 1024} }, /* ALL */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530563
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100564 { .m = { .sec = 1, .tb = 0 }, 0x1, {0xfff000, 4 * 1024} }, /* Upper 1/4096 */
565 { .m = { .sec = 1, .tb = 0 }, 0x2, {0xffe000, 8 * 1024} }, /* Upper 1/2048 */
566 { .m = { .sec = 1, .tb = 0 }, 0x3, {0xffc000, 16 * 1024} }, /* Upper 1/1024 */
567 { .m = { .sec = 1, .tb = 0 }, 0x4, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
568 { .m = { .sec = 1, .tb = 0 }, 0x5, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700569
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100570 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, /* Lower 1/4096 */
571 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, /* Lower 1/2048 */
572 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, /* Lower 1/1024 */
573 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, /* Lower 1/512 */
574 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, /* Lower 1/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700575};
576
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100577static struct wp_range_descriptor w25rq128_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100578 { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 16 * 1024 * 1024} }, /* ALL */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700579
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100580 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16128 * 1024} }, /* Lower 63/64 */
581 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 15872 * 1024} }, /* Lower 31/32 */
582 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 15 * 1024 * 1024} }, /* Lower 15/16 */
583 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 14 * 1024 * 1024} }, /* Lower 7/8 */
584 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 12 * 1024 * 1024} }, /* Lower 3/4 */
585 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 8 * 1024 * 1024} }, /* Lower 1/2 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700586
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100587 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x040000, 16128 * 1024} }, /* Upper 63/64 */
588 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x080000, 15872 * 1024} }, /* Upper 31/32 */
589 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x100000, 15 * 1024 * 1024} }, /* Upper 15/16 */
590 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x200000, 14 * 1024 * 1024} }, /* Upper 7/8 */
591 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x400000, 12 * 1024 * 1024} }, /* Upper 3/4 */
592 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x800000, 8 * 1024 * 1024} }, /* Upper 1/2 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700593
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100594 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 0} }, /* NONE */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700595
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100596 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 16380 * 1024} }, /* Lower 4095/4096 */
597 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 16376 * 1024} }, /* Lower 2048/2048 */
598 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 16368 * 1024} }, /* Lower 1023/1024 */
599 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
600 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700601
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100602 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 16380 * 1024} }, /* Upper 4095/4096 */
603 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 16376 * 1024} }, /* Upper 2047/2048 */
604 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 16368 * 1024} }, /* Upper 1023/1024 */
605 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
606 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530607};
608
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100609static struct wp_range_descriptor w25rq256_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100610 { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 0x0000000} }, /* NONE */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800611
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100612 { .m = { .sec = X, .tb = 0 }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* Upper 1/512 */
613 { .m = { .sec = X, .tb = 0 }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* Upper 1/256 */
614 { .m = { .sec = X, .tb = 0 }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* Upper 1/128 */
615 { .m = { .sec = X, .tb = 0 }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* Upper 1/64 */
616 { .m = { .sec = X, .tb = 0 }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* Upper 1/32 */
617 { .m = { .sec = X, .tb = 0 }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* Upper 1/16 */
618 { .m = { .sec = X, .tb = 0 }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* Upper 1/8 */
619 { .m = { .sec = X, .tb = 0 }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* Upper 1/4 */
620 { .m = { .sec = X, .tb = 0 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800621
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100622 { .m = { .sec = X, .tb = 1 }, 0x1, {0x0000000, 64 * 1 * 1024} }, /* Lower 1/512 */
623 { .m = { .sec = X, .tb = 1 }, 0x2, {0x0000000, 64 * 2 * 1024} }, /* Lower 1/256 */
624 { .m = { .sec = X, .tb = 1 }, 0x3, {0x0000000, 64 * 4 * 1024} }, /* Lower 1/128 */
625 { .m = { .sec = X, .tb = 1 }, 0x4, {0x0000000, 64 * 8 * 1024} }, /* Lower 1/64 */
626 { .m = { .sec = X, .tb = 1 }, 0x5, {0x0000000, 64 * 16 * 1024} }, /* Lower 1/32 */
627 { .m = { .sec = X, .tb = 1 }, 0x6, {0x0000000, 64 * 32 * 1024} }, /* Lower 1/16 */
628 { .m = { .sec = X, .tb = 1 }, 0x7, {0x0000000, 64 * 64 * 1024} }, /* Lower 1/8 */
629 { .m = { .sec = X, .tb = 1 }, 0x8, {0x0000000, 64 * 128 * 1024} }, /* Lower 1/4 */
630 { .m = { .sec = X, .tb = 1 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800631
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100632 { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* ALL */
633 { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* ALL */
634 { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* ALL */
635 { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* ALL */
636 { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* ALL */
637 { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* ALL */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800638};
639
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100640static struct wp_range_descriptor w25rq256_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100641 { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 64 * 512 * 1024} }, /* ALL */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800642
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100643 { .m = { .sec = X, .tb = 0 }, 0x1, {0x0000000, 64 * 511 * 1024} }, /* Lower 511/512 */
644 { .m = { .sec = X, .tb = 0 }, 0x2, {0x0000000, 64 * 510 * 1024} }, /* Lower 255/256 */
645 { .m = { .sec = X, .tb = 0 }, 0x3, {0x0000000, 64 * 508 * 1024} }, /* Lower 127/128 */
646 { .m = { .sec = X, .tb = 0 }, 0x4, {0x0000000, 64 * 504 * 1024} }, /* Lower 63/64 */
647 { .m = { .sec = X, .tb = 0 }, 0x5, {0x0000000, 64 * 496 * 1024} }, /* Lower 31/32 */
648 { .m = { .sec = X, .tb = 0 }, 0x6, {0x0000000, 64 * 480 * 1024} }, /* Lower 15/16 */
649 { .m = { .sec = X, .tb = 0 }, 0x7, {0x0000000, 64 * 448 * 1024} }, /* Lower 7/8 */
650 { .m = { .sec = X, .tb = 0 }, 0x8, {0x0000000, 64 * 384 * 1024} }, /* Lower 3/4 */
651 { .m = { .sec = X, .tb = 0 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800652
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100653 { .m = { .sec = X, .tb = 1 }, 0x1, {0x0010000, 64 * 511 * 1024} }, /* Upper 511/512 */
654 { .m = { .sec = X, .tb = 1 }, 0x2, {0x0020000, 64 * 510 * 1024} }, /* Upper 255/256 */
655 { .m = { .sec = X, .tb = 1 }, 0x3, {0x0040000, 64 * 508 * 1024} }, /* Upper 127/128 */
656 { .m = { .sec = X, .tb = 1 }, 0x4, {0x0080000, 64 * 504 * 1024} }, /* Upper 63/64 */
657 { .m = { .sec = X, .tb = 1 }, 0x5, {0x0100000, 64 * 496 * 1024} }, /* Upper 31/32 */
658 { .m = { .sec = X, .tb = 1 }, 0x6, {0x0200000, 64 * 480 * 1024} }, /* Upper 15/16 */
659 { .m = { .sec = X, .tb = 1 }, 0x7, {0x0400000, 64 * 448 * 1024} }, /* Upper 7/8 */
660 { .m = { .sec = X, .tb = 1 }, 0x8, {0x0800000, 64 * 384 * 1024} }, /* Upper 3/4 */
661 { .m = { .sec = X, .tb = 1 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800662
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100663 { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 0x0000000} }, /* NONE */
664 { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 0x0000000} }, /* NONE */
665 { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 0x0000000} }, /* NONE */
666 { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 0x0000000} }, /* NONE */
667 { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 0x0000000} }, /* NONE */
668 { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 0x0000000} }, /* NONE */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800669};
670
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000671static struct wp_range_descriptor w25x10_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100672 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
673 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x010000, 64 * 1024} },
674 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
675 { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
676 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800677};
678
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000679static struct wp_range_descriptor w25x20_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100680 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
681 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x030000, 64 * 1024} },
682 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x020000, 128 * 1024} },
683 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
684 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
685 { .m = { .sec = 0, .tb = X }, 0x3, {0x000000, 256 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800686};
687
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000688static struct wp_range_descriptor w25x40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100689 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
690 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
691 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
692 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
693 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
694 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
695 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
696 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} },
697 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} },
698 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} },
699 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} },
David Hendricks470ca952010-08-13 14:01:53 -0700700};
701
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000702static struct wp_range_descriptor w25x80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100703 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
704 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0F0000, 64 * 1024} },
705 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0E0000, 128 * 1024} },
706 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0C0000, 256 * 1024} },
707 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
708 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
709 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
710 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
711 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
712 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
713 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
714 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800715};
716
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100717static struct wp_range_descriptor gd25q40_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100718 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* None */
719 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
720 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
721 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
722 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
723 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
724 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
725 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} }, /* All */
726 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} }, /* All */
727 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} }, /* All */
728 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
729 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x07F000, 4 * 1024} },
730 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x07E000, 8 * 1024} },
731 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x07C000, 16 * 1024} },
732 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x078000, 32 * 1024} },
733 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x078000, 32 * 1024} },
734 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x078000, 32 * 1024} },
735 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
736 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
737 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
738 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
739 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
740 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
741 { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600742};
743
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100744static struct wp_range_descriptor gd25q40_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100745 { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 512 * 1024} }, /* ALL */
746 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 448 * 1024} },
747 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 384 * 1024} },
748 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 256 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600749
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100750 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 448 * 1024} },
751 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 384 * 1024} },
752 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 256 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600753
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100754 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 0} }, /* None */
755 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 0} }, /* None */
756 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 0} }, /* None */
757 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 0} }, /* None */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600758
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100759 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 508 * 1024} },
760 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 504 * 1024} },
761 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 496 * 1024} },
762 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 480 * 1024} },
763 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 480 * 1024} },
764 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x000000, 480 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600765
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100766 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 508 * 1024} },
767 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 504 * 1024} },
768 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 496 * 1024} },
769 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 480 * 1024} },
770 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 480 * 1024} },
771 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x008000, 480 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600772
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100773 { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 0} }, /* None */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600774};
775
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100776static struct wp_range_descriptor gd25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100777 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
778 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
779 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
780 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
781 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
782 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
783 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700784
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100785 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
786 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
787 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
788 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
789 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
790 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
791 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700792
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100793 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
794 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
795 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
796 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
797 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
798 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x7f8000, 32 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700799
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100800 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
801 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
802 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
803 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
804 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
805 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700806};
807
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100808static struct wp_range_descriptor a25l040_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100809 { .m = { .sec = X, .tb = X }, 0x0, {0, 0} }, /* none */
810 { .m = { .sec = X, .tb = X }, 0x1, {0x70000, 64 * 1024} },
811 { .m = { .sec = X, .tb = X }, 0x2, {0x60000, 128 * 1024} },
812 { .m = { .sec = X, .tb = X }, 0x3, {0x40000, 256 * 1024} },
813 { .m = { .sec = X, .tb = X }, 0x4, {0x00000, 512 * 1024} },
814 { .m = { .sec = X, .tb = X }, 0x5, {0x00000, 512 * 1024} },
815 { .m = { .sec = X, .tb = X }, 0x6, {0x00000, 512 * 1024} },
816 { .m = { .sec = X, .tb = X }, 0x7, {0x00000, 512 * 1024} },
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +0800817};
818
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100819struct wp *get_wp_for_flashchip(const struct flashchip *chip) {
820 // FIXME: The .wp field should be deleted from from struct flashchip
821 // completly, but linux_mtd and cros_ec still assign their own values
822 // to it. When they are cleaned up we can delete this.
823 if(chip->wp) return chip->wp;
824
825 switch (chip->manufacture_id) {
826 case WINBOND_NEX_ID:
827 switch(chip->model_id) {
828 case WINBOND_NEX_W25X10:
829 case WINBOND_NEX_W25X20:
830 case WINBOND_NEX_W25X40:
831 case WINBOND_NEX_W25X80:
832 case WINBOND_NEX_W25Q128_V_M:
833 return &wp_w25;
834 case WINBOND_NEX_W25Q80_V:
835 case WINBOND_NEX_W25Q16_V:
836 case WINBOND_NEX_W25Q32_V:
837 case WINBOND_NEX_W25Q32_W:
838 case WINBOND_NEX_W25Q32JW:
839 case WINBOND_NEX_W25Q64_V:
840 case WINBOND_NEX_W25Q64_W:
841 // W25Q64JW does not have a range table entry, but the flashchip
842 // set .wp to wp_25q, so keep it here until the issue is resolved
843 case WINBOND_NEX_W25Q64JW:
844 case WINBOND_NEX_W25Q128_DTR:
845 case WINBOND_NEX_W25Q128_V:
846 case WINBOND_NEX_W25Q128_W:
847 return &wp_w25q;
848 case WINBOND_NEX_W25Q256_V:
849 case WINBOND_NEX_W25Q256JV_M:
850 return &wp_w25q_large;
851 }
852 break;
853 case EON_ID_NOPREFIX:
854 switch (chip->model_id) {
855 case EON_EN25F40:
856 case EON_EN25Q40:
857 case EON_EN25Q80:
858 case EON_EN25Q32:
859 case EON_EN25Q64:
860 case EON_EN25Q128:
861 case EON_EN25QH128:
862 case EON_EN25S64:
863 return &wp_w25;
864 }
865 break;
866 case MACRONIX_ID:
867 switch (chip->model_id) {
868 case MACRONIX_MX25L1005:
869 case MACRONIX_MX25L2005:
870 case MACRONIX_MX25L4005:
871 case MACRONIX_MX25L8005:
872 case MACRONIX_MX25L1605:
873 case MACRONIX_MX25L3205:
874 case MACRONIX_MX25U3235E:
875 case MACRONIX_MX25U6435E:
876 return &wp_w25;
877 case MACRONIX_MX25U12835E:
878 return &wp_w25q_large;
879 case MACRONIX_MX25L6405:
880 case MACRONIX_MX25L6495F:
881 case MACRONIX_MX25L25635F:
882 return &wp_generic;
883 }
884 break;
885 case ST_ID:
886 switch(chip->model_id) {
887 case ST_N25Q064__1E:
888 case ST_N25Q064__3E:
889 return &wp_w25;
890 }
891 break;
892 case GIGADEVICE_ID:
893 switch(chip->model_id) {
894 case GIGADEVICE_GD25LQ32:
895 // GD25Q40 does not have a .wp field in flashchips.c, but
896 // it is in the w25 range table function, so note it here
897 // until the issue is resolved:
898 // case GIGADEVICE_GD25Q40:
899 case GIGADEVICE_GD25Q64:
900 case GIGADEVICE_GD25LQ64:
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100901 case GIGADEVICE_GD25Q128:
902 return &wp_w25;
903 case GIGADEVICE_GD25Q256D:
904 return &wp_w25q_large;
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100905 case GIGADEVICE_GD25LQ128CD:
906 case GIGADEVICE_GD25Q32:
907 return &wp_generic;
908 }
909 break;
910 case AMIC_ID_NOPREFIX:
911 switch(chip->model_id) {
912 case AMIC_A25L040:
913 return &wp_w25;
914 }
915 break;
916 case ATMEL_ID:
917 switch(chip->model_id) {
918 case ATMEL_AT25SF128A:
919 case ATMEL_AT25SL128A:
920 return &wp_w25q;
921 }
922 break;
923 case PROGMANUF_ID:
924 switch(chip->model_id) {
925 case PROGDEV_ID:
926 return &wp_w25;
927 }
928 break;
929 case SPANSION_ID:
930 switch (chip->model_id) {
931 case SPANSION_S25FS128S_L:
932 case SPANSION_S25FS128S_S:
933 case SPANSION_S25FL256S_UL:
934 case SPANSION_S25FL256S_US:
935 // SPANSION_S25FL128S_UL does not have a range table entry,
936 // but its flashchip set .wp to wp_generic, so keep it here
937 // until the issue resolved
938 case SPANSION_S25FL128S_UL:
939 // SPANSION_S25FL128S_US does not have a range table entry,
940 // but its flashchip set .wp to wp_generic, so keep it here
941 // until the issue resolved
942 case SPANSION_S25FL128S_US:
943 return &wp_generic;
944 }
945 break;
946 }
947
948
949 return NULL;
950}
951
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700952/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700953static uint8_t w25q_read_status_register_2(const struct flashctx *flash)
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700954{
955 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 };
956 unsigned char readarr[2];
957 int ret;
958
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +1100959 if (flash->chip->read_status) {
960 msg_cdbg("RDSR2 failed! cmd=0x35 unimpl for opaque chips\n");
961 return 0;
962 }
963
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700964 /* Read Status Register */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700965 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700966 if (ret) {
967 /*
968 * FIXME: make this a benign failure for now in case we are
969 * unable to execute the opcode
970 */
971 msg_cdbg("RDSR2 failed!\n");
972 readarr[0] = 0x00;
973 }
974
975 return readarr[0];
976}
977
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600978/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Edward O'Callaghandf43e902020-11-13 23:08:26 +1100979static uint8_t mx25l_read_config_register(const struct flashctx *flash)
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600980{
981 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x15 };
982 unsigned char readarr[2]; /* leave room for dummy byte */
983 int ret;
984
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +1100985 if (flash->chip->read_status) {
986 msg_cdbg("RDCR failed! cmd=0x15 unimpl for opaque chips\n");
987 return 0;
988 }
989
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600990 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
991 if (ret) {
992 msg_cdbg("RDCR failed!\n");
993 readarr[0] = 0x00;
994 }
995
996 return readarr[0];
997}
998
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800999/* Given a flash chip, this function returns its range table. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001000static int w25_range_table(const struct flashctx *flash,
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001001 struct wp_range_descriptor **descrs,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001002 int *num_entries)
David Hendricksf7924d12010-06-10 21:26:44 -07001003{
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001004 uint8_t cr;
1005
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001006 *descrs = 0;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001007 *num_entries = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001008
Patrick Georgif3fa2992017-02-02 16:24:44 +01001009 switch (flash->chip->manufacture_id) {
David Hendricksd494b0a2010-08-16 16:28:50 -07001010 case WINBOND_NEX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001011 switch(flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -08001012 case WINBOND_NEX_W25X10:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001013 *descrs = w25x10_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001014 *num_entries = ARRAY_SIZE(w25x10_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001015 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001016 case WINBOND_NEX_W25X20:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001017 *descrs = w25x20_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001018 *num_entries = ARRAY_SIZE(w25x20_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001019 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001020 case WINBOND_NEX_W25X40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001021 *descrs = w25x40_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001022 *num_entries = ARRAY_SIZE(w25x40_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001023 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001024 case WINBOND_NEX_W25X80:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001025 *descrs = w25x80_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001026 *num_entries = ARRAY_SIZE(w25x80_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001027 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001028 case WINBOND_NEX_W25Q80_V:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001029 *descrs = w25q80_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001030 *num_entries = ARRAY_SIZE(w25q80_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001031 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001032 case WINBOND_NEX_W25Q16_V:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001033 *descrs = w25q16_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001034 *num_entries = ARRAY_SIZE(w25q16_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001035 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001036 case WINBOND_NEX_W25Q32_V:
1037 case WINBOND_NEX_W25Q32_W:
Edward O'Callaghand80cf712019-05-24 22:06:36 +10001038 case WINBOND_NEX_W25Q32JW:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001039 *descrs = w25q32_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001040 *num_entries = ARRAY_SIZE(w25q32_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001041 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001042 case WINBOND_NEX_W25Q64_V:
1043 case WINBOND_NEX_W25Q64_W:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001044 *descrs = w25q64_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001045 *num_entries = ARRAY_SIZE(w25q64_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001046 break;
Edward O'Callaghan517cb822019-11-21 14:08:32 +11001047 case WINBOND_NEX_W25Q128_DTR:
Alan Green77a95de2019-07-01 16:40:39 +10001048 case WINBOND_NEX_W25Q128_V_M:
Patrick Georgicc04a452017-02-06 12:14:43 +01001049 case WINBOND_NEX_W25Q128_V:
1050 case WINBOND_NEX_W25Q128_W:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001051 if (w25q_read_status_register_2(flash) & (1 << 6)) {
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001052 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001053 *descrs = w25rq128_cmp1_ranges;
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001054 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1055 } else {
1056 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001057 *descrs = w25rq128_cmp0_ranges;
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001058 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1059 }
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +05301060 break;
Justin TerAvest40083232020-08-17 16:34:46 -06001061 case WINBOND_NEX_W25Q256_V:
Alan Green77a95de2019-07-01 16:40:39 +10001062 case WINBOND_NEX_W25Q256JV_M:
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001063 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1064 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001065 *descrs = w25rq256_cmp1_ranges;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001066 *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges);
1067 } else {
1068 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001069 *descrs = w25rq256_cmp0_ranges;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001070 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
1071 }
1072 break;
David Hendricksd494b0a2010-08-16 16:28:50 -07001073 default:
1074 msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
1075 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001076 flash->chip->model_id);
David Hendricksd494b0a2010-08-16 16:28:50 -07001077 return -1;
1078 }
David Hendricks2c4a76c2010-06-28 14:00:43 -07001079 break;
David Hendricks57566ed2010-08-16 18:24:45 -07001080 case EON_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001081 switch (flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -08001082 case EON_EN25F40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001083 *descrs = en25f40_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001084 *num_entries = ARRAY_SIZE(en25f40_ranges);
David Hendricks57566ed2010-08-16 18:24:45 -07001085 break;
David Hendrickse185bf22011-05-24 15:34:18 -07001086 case EON_EN25Q40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001087 *descrs = en25q40_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001088 *num_entries = ARRAY_SIZE(en25q40_ranges);
1089 break;
1090 case EON_EN25Q80:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001091 *descrs = en25q80_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001092 *num_entries = ARRAY_SIZE(en25q80_ranges);
1093 break;
1094 case EON_EN25Q32:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001095 *descrs = en25q32_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001096 *num_entries = ARRAY_SIZE(en25q32_ranges);
1097 break;
1098 case EON_EN25Q64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001099 *descrs = en25q64_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001100 *num_entries = ARRAY_SIZE(en25q64_ranges);
1101 break;
1102 case EON_EN25Q128:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001103 *descrs = en25q128_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001104 *num_entries = ARRAY_SIZE(en25q128_ranges);
1105 break;
Tim Chen136fd0a2020-06-30 19:12:50 +08001106 case EON_EN25QH128:
1107 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1108 /* CMP == 1 */
1109 *descrs = w25rq128_cmp1_ranges;
1110 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1111 } else {
1112 /* CMP == 0 */
1113 *descrs = w25rq128_cmp0_ranges;
1114 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1115 }
1116 break;
Marc Jonesb2f90022014-04-29 17:37:23 -06001117 case EON_EN25S64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001118 *descrs = en25s64_ranges;
Marc Jonesb2f90022014-04-29 17:37:23 -06001119 *num_entries = ARRAY_SIZE(en25s64_ranges);
1120 break;
David Hendricks57566ed2010-08-16 18:24:45 -07001121 default:
1122 msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
1123 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001124 flash->chip->model_id);
David Hendricks57566ed2010-08-16 18:24:45 -07001125 return -1;
1126 }
1127 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001128 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001129 switch (flash->chip->model_id) {
David Hendricksf8f00c72011-02-01 12:39:46 -08001130 case MACRONIX_MX25L1005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001131 *descrs = mx25l1005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001132 *num_entries = ARRAY_SIZE(mx25l1005_ranges);
1133 break;
1134 case MACRONIX_MX25L2005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001135 *descrs = mx25l2005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001136 *num_entries = ARRAY_SIZE(mx25l2005_ranges);
1137 break;
1138 case MACRONIX_MX25L4005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001139 *descrs = mx25l4005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001140 *num_entries = ARRAY_SIZE(mx25l4005_ranges);
1141 break;
1142 case MACRONIX_MX25L8005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001143 *descrs = mx25l8005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001144 *num_entries = ARRAY_SIZE(mx25l8005_ranges);
1145 break;
1146 case MACRONIX_MX25L1605:
1147 /* FIXME: MX25L1605 and MX25L1605D have different write
1148 * protection capabilities, but share IDs */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001149 *descrs = mx25l1605d_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001150 *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
1151 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001152 case MACRONIX_MX25L3205:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001153 *descrs = mx25l3205d_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001154 *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
David Hendricksac72e362010-08-16 18:20:03 -07001155 break;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001156 case MACRONIX_MX25U3235E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001157 *descrs = mx25u3235e_ranges;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001158 *num_entries = ARRAY_SIZE(mx25u3235e_ranges);
1159 break;
Jongpil66a96492014-08-14 17:59:06 +09001160 case MACRONIX_MX25U6435E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001161 *descrs = mx25u6435e_ranges;
Jongpil66a96492014-08-14 17:59:06 +09001162 *num_entries = ARRAY_SIZE(mx25u6435e_ranges);
1163 break;
Alan Greendc0792e2019-07-01 15:01:34 +10001164 case MACRONIX_MX25U12835E:
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001165 cr = mx25l_read_config_register(flash);
1166 if (cr & MX25U12835E_TB) { /* T/B == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001167 *descrs = mx25u12835e_tb1_ranges;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001168 *num_entries = ARRAY_SIZE(mx25u12835e_tb1_ranges);
1169 } else { /* T/B == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001170 *descrs = mx25u12835e_tb0_ranges;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001171 *num_entries = ARRAY_SIZE(mx25u12835e_tb0_ranges);
1172 }
Alex Lu831c6092017-11-02 23:19:34 -07001173 break;
David Hendricksac72e362010-08-16 18:20:03 -07001174 default:
1175 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
1176 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001177 flash->chip->model_id);
David Hendricksac72e362010-08-16 18:20:03 -07001178 return -1;
1179 }
1180 break;
David Hendricksbfa624b2012-07-24 12:47:59 -07001181 case ST_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001182 switch(flash->chip->model_id) {
David Hendricksbfa624b2012-07-24 12:47:59 -07001183 case ST_N25Q064__1E:
1184 case ST_N25Q064__3E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001185 *descrs = n25q064_ranges;
David Hendricksbfa624b2012-07-24 12:47:59 -07001186 *num_entries = ARRAY_SIZE(n25q064_ranges);
1187 break;
1188 default:
1189 msg_cerr("%s() %d: Micron flash chip mismatch"
1190 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001191 flash->chip->model_id);
David Hendricksbfa624b2012-07-24 12:47:59 -07001192 return -1;
1193 }
1194 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001195 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001196 switch(flash->chip->model_id) {
Bryan Freed9a0051f2012-05-22 16:06:09 -07001197 case GIGADEVICE_GD25LQ32:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001198 *descrs = w25q32_ranges;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001199 *num_entries = ARRAY_SIZE(w25q32_ranges);
1200 break;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001201 case GIGADEVICE_GD25Q40:
1202 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1203 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001204 *descrs = gd25q40_cmp1_ranges;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001205 *num_entries = ARRAY_SIZE(gd25q40_cmp1_ranges);
1206 } else {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001207 *descrs = gd25q40_cmp0_ranges;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001208 *num_entries = ARRAY_SIZE(gd25q40_cmp0_ranges);
1209 }
1210 break;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001211 case GIGADEVICE_GD25Q64:
Marc Jonesb18734f2014-04-03 16:19:47 -06001212 case GIGADEVICE_GD25LQ64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001213 *descrs = gd25q64_ranges;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001214 *num_entries = ARRAY_SIZE(gd25q64_ranges);
1215 break;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001216 case GIGADEVICE_GD25Q128:
1217 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1218 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001219 *descrs = w25rq128_cmp1_ranges;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001220 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1221 } else {
1222 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001223 *descrs = w25rq128_cmp0_ranges;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001224 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1225 }
1226 break;
Duncan Laurie0c383552019-03-16 12:35:16 -07001227 case GIGADEVICE_GD25Q256D:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001228 *descrs = w25rq256_cmp0_ranges;
Duncan Laurie0c383552019-03-16 12:35:16 -07001229 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
1230 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001231 default:
1232 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
1233 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001234 flash->chip->model_id);
Bryan Freed9a0051f2012-05-22 16:06:09 -07001235 return -1;
1236 }
1237 break;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001238 case AMIC_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001239 switch(flash->chip->model_id) {
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001240 case AMIC_A25L040:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001241 *descrs = a25l040_ranges;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001242 *num_entries = ARRAY_SIZE(a25l040_ranges);
1243 break;
1244 default:
1245 msg_cerr("%s() %d: AMIC flash chip mismatch"
1246 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001247 flash->chip->model_id);
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001248 return -1;
1249 }
1250 break;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001251 case ATMEL_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001252 switch(flash->chip->model_id) {
Edward O'Callaghan1fa87e02019-05-03 02:27:24 -04001253 case ATMEL_AT25SF128A:
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001254 case ATMEL_AT25SL128A:
1255 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1256 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001257 *descrs = w25rq128_cmp1_ranges;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001258 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1259 } else {
1260 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001261 *descrs = w25rq128_cmp0_ranges;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001262 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1263 }
1264 break;
1265 default:
1266 msg_cerr("%s() %d: Atmel flash chip mismatch"
1267 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001268 flash->chip->model_id);
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001269 return -1;
1270 }
1271 break;
David Hendricksf7924d12010-06-10 21:26:44 -07001272 default:
David Hendricksd494b0a2010-08-16 16:28:50 -07001273 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01001274 __func__, flash->chip->manufacture_id);
David Hendricksf7924d12010-06-10 21:26:44 -07001275 return -1;
1276 }
1277
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001278 return 0;
1279}
1280
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001281int w25_range_to_status(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001282 unsigned int start, unsigned int len,
1283 struct w25q_status *status)
1284{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001285 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001286 int i, range_found = 0;
1287 int num_entries;
1288
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001289 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001290 return -1;
1291
David Hendricksf7924d12010-06-10 21:26:44 -07001292 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001293 struct wp_range *r = &descrs[i].range;
David Hendricksf7924d12010-06-10 21:26:44 -07001294
1295 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1296 start, len, r->start, r->len);
1297 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001298 status->bp0 = descrs[i].bp & 1;
1299 status->bp1 = descrs[i].bp >> 1;
1300 status->bp2 = descrs[i].bp >> 2;
1301 status->tb = descrs[i].m.tb;
1302 status->sec = descrs[i].m.sec;
David Hendricksf7924d12010-06-10 21:26:44 -07001303
1304 range_found = 1;
1305 break;
1306 }
1307 }
1308
1309 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001310 msg_cerr("%s: matching range not found\n", __func__);
David Hendricksf7924d12010-06-10 21:26:44 -07001311 return -1;
1312 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001313
David Hendricksd494b0a2010-08-16 16:28:50 -07001314 return 0;
1315}
1316
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001317int w25_status_to_range(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001318 const struct w25q_status *status,
1319 unsigned int *start, unsigned int *len)
1320{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001321 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001322 int i, status_found = 0;
1323 int num_entries;
1324
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001325 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001326 return -1;
1327
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001328 for (i = 0; i < num_entries; i++) {
1329 int bp;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001330 int table_bp, table_tb, table_sec;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001331
1332 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
1333 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001334 bp, descrs[i].bp,
1335 status->tb, descrs[i].m.tb,
1336 status->sec, descrs[i].m.sec);
1337 table_bp = descrs[i].bp;
1338 table_tb = descrs[i].m.tb;
1339 table_sec = descrs[i].m.sec;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001340 if ((bp == table_bp || table_bp == X) &&
1341 (status->tb == table_tb || table_tb == X) &&
1342 (status->sec == table_sec || table_sec == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001343 *start = descrs[i].range.start;
1344 *len = descrs[i].range.len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001345
1346 status_found = 1;
1347 break;
1348 }
1349 }
1350
1351 if (!status_found) {
1352 msg_cerr("matching status not found\n");
1353 return -1;
1354 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001355
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001356 return 0;
1357}
1358
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001359/* Given a [start, len], this function calls w25_range_to_status() to convert
1360 * it to flash-chip-specific range bits, then sets into status register.
1361 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001362static int w25_set_range(const struct flashctx *flash,
David Hendricksd494b0a2010-08-16 16:28:50 -07001363 unsigned int start, unsigned int len)
1364{
1365 struct w25q_status status;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001366 int tmp = 0;
1367 int expected = 0;
David Hendricksd494b0a2010-08-16 16:28:50 -07001368
1369 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001370 tmp = spi_read_status_register(flash);
David Hendricksd494b0a2010-08-16 16:28:50 -07001371 memcpy(&status, &tmp, 1);
1372 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1373
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001374 if (w25_range_to_status(flash, start, len, &status))
1375 return -1;
David Hendricksf7924d12010-06-10 21:26:44 -07001376
1377 msg_cdbg("status.busy: %x\n", status.busy);
1378 msg_cdbg("status.wel: %x\n", status.wel);
1379 msg_cdbg("status.bp0: %x\n", status.bp0);
1380 msg_cdbg("status.bp1: %x\n", status.bp1);
1381 msg_cdbg("status.bp2: %x\n", status.bp2);
1382 msg_cdbg("status.tb: %x\n", status.tb);
1383 msg_cdbg("status.sec: %x\n", status.sec);
1384 msg_cdbg("status.srp0: %x\n", status.srp0);
1385
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001386 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001387 spi_write_status_register(flash, expected);
David Hendricksf7924d12010-06-10 21:26:44 -07001388
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001389 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001390 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001391 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) {
David Hendricksc801adb2010-12-09 16:58:56 -08001392 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001393 expected, tmp);
1394 return 1;
1395 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001396
1397 return 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001398}
1399
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001400/* Print out the current status register value with human-readable text. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001401static int w25_wp_status(const struct flashctx *flash)
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001402{
1403 struct w25q_status status;
1404 int tmp;
David Hendricksce8ded32010-10-08 11:23:38 -07001405 unsigned int start, len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001406 int ret = 0;
1407
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001408 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001409 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001410 memcpy(&status, &tmp, 1);
1411 msg_cinfo("WP: status: 0x%02x\n", tmp);
1412 msg_cinfo("WP: status.srp0: %x\n", status.srp0);
1413 msg_cinfo("WP: write protect is %s.\n",
1414 status.srp0 ? "enabled" : "disabled");
1415
1416 msg_cinfo("WP: write protect range: ");
1417 if (w25_status_to_range(flash, &status, &start, &len)) {
1418 msg_cinfo("(cannot resolve the range)\n");
1419 ret = -1;
1420 } else {
1421 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1422 }
1423
1424 return ret;
1425}
1426
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001427static int w25q_large_range_to_status(const struct flashctx *flash,
1428 unsigned int start, unsigned int len,
1429 struct w25q_status_large *status)
1430{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001431 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001432 int i, range_found = 0;
1433 int num_entries;
1434
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001435 if (w25_range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001436 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001437
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001438 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001439 struct wp_range *r = &descrs[i].range;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001440
1441 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1442 start, len, r->start, r->len);
1443 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001444 status->bp0 = descrs[i].bp & 1;
1445 status->bp1 = descrs[i].bp >> 1;
1446 status->bp2 = descrs[i].bp >> 2;
1447 status->bp3 = descrs[i].bp >> 3;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001448 /*
1449 * For MX25U12835E chip, Top/Bottom (T/B) bit is not
1450 * part of status register and in that bit position is
1451 * Quad Enable (QE)
1452 */
1453 if (flash->chip->manufacture_id != MACRONIX_ID ||
1454 flash->chip->model_id != MACRONIX_MX25U12835E)
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001455 status->tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001456
1457 range_found = 1;
1458 break;
1459 }
1460 }
1461
1462 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001463 msg_cerr("%s: matching range not found\n", __func__);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001464 return -1;
1465 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001466
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001467 return 0;
1468}
1469
1470static int w25_large_status_to_range(const struct flashctx *flash,
1471 const struct w25q_status_large *status,
1472 unsigned int *start, unsigned int *len)
1473{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001474 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001475 int i, status_found = 0;
1476 int num_entries;
1477
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001478 if (w25_range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001479 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001480
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001481 for (i = 0; i < num_entries; i++) {
1482 int bp;
1483 int table_bp, table_tb;
1484
1485 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) |
1486 (status->bp3 << 3);
1487 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001488 bp, descrs[i].bp,
1489 status->tb, descrs[i].m.tb);
1490 table_bp = descrs[i].bp;
1491 table_tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001492 if ((bp == table_bp || table_bp == X) &&
1493 (status->tb == table_tb || table_tb == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001494 *start = descrs[i].range.start;
1495 *len = descrs[i].range.len;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001496
1497 status_found = 1;
1498 break;
1499 }
1500 }
1501
1502 if (!status_found) {
1503 msg_cerr("matching status not found\n");
1504 return -1;
1505 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001506
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001507 return 0;
1508}
1509
1510/* Given a [start, len], this function calls w25_range_to_status() to convert
1511 * it to flash-chip-specific range bits, then sets into status register.
1512 * Returns 0 if successful, -1 on error, and 1 if reading back was different.
1513 */
1514static int w25q_large_set_range(const struct flashctx *flash,
1515 unsigned int start, unsigned int len)
1516{
1517 struct w25q_status_large status;
1518 int tmp;
1519 int expected = 0;
1520
1521 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001522 tmp = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001523 memcpy(&status, &tmp, 1);
1524 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1525
1526 if (w25q_large_range_to_status(flash, start, len, &status))
1527 return -1;
1528
1529 msg_cdbg("status.busy: %x\n", status.busy);
1530 msg_cdbg("status.wel: %x\n", status.wel);
1531 msg_cdbg("status.bp0: %x\n", status.bp0);
1532 msg_cdbg("status.bp1: %x\n", status.bp1);
1533 msg_cdbg("status.bp2: %x\n", status.bp2);
1534 msg_cdbg("status.bp3: %x\n", status.bp3);
1535 msg_cdbg("status.tb: %x\n", status.tb);
1536 msg_cdbg("status.srp0: %x\n", status.srp0);
1537
1538 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001539 spi_write_status_register(flash, expected);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001540
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001541 tmp = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001542 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001543 if ((tmp & MASK_WP_AREA_LARGE) != (expected & MASK_WP_AREA_LARGE)) {
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001544 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
1545 expected, tmp);
1546 return 1;
1547 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001548
1549 return 0;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001550}
1551
1552static int w25q_large_wp_status(const struct flashctx *flash)
1553{
1554 struct w25q_status_large sr1;
1555 struct w25q_status_2 sr2;
1556 uint8_t tmp[2];
1557 unsigned int start, len;
1558 int ret = 0;
1559
1560 memset(&sr1, 0, sizeof(sr1));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001561 tmp[0] = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001562 memcpy(&sr1, &tmp[0], 1);
1563
1564 memset(&sr2, 0, sizeof(sr2));
1565 tmp[1] = w25q_read_status_register_2(flash);
1566 memcpy(&sr2, &tmp[1], 1);
1567
1568 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
1569 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1570 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1571 msg_cinfo("WP: write protect is %s.\n",
1572 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1573
1574 msg_cinfo("WP: write protect range: ");
1575 if (w25_large_status_to_range(flash, &sr1, &start, &len)) {
1576 msg_cinfo("(cannot resolve the range)\n");
1577 ret = -1;
1578 } else {
1579 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1580 }
1581
1582 return ret;
1583}
1584
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001585/* Set/clear the SRP0 bit in the status register. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001586static int w25_set_srp0(const struct flashctx *flash, int enable)
David Hendricksf7924d12010-06-10 21:26:44 -07001587{
1588 struct w25q_status status;
1589 int tmp = 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001590 int expected = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001591
1592 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001593 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001594 /* FIXME: this is NOT endian-free copy. */
David Hendricksf7924d12010-06-10 21:26:44 -07001595 memcpy(&status, &tmp, 1);
1596 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1597
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001598 status.srp0 = enable ? 1 : 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001599 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001600 spi_write_status_register(flash, expected);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001601
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001602 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001603 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
1604 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
1605 return 1;
David Hendricksf7924d12010-06-10 21:26:44 -07001606
1607 return 0;
1608}
1609
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001610static int w25_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001611 enum wp_mode wp_mode)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001612{
1613 int ret;
1614
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001615 if (wp_mode != WP_MODE_HARDWARE) {
David Hendricks1c09f802012-10-03 11:03:48 -07001616 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
1617 return 1;
1618 }
1619
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001620 ret = w25_set_srp0(flash, 1);
David Hendricksc801adb2010-12-09 16:58:56 -08001621 if (ret)
1622 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001623 return ret;
1624}
1625
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001626static int w25_disable_writeprotect(const struct flashctx *flash)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001627{
1628 int ret;
1629
1630 ret = w25_set_srp0(flash, 0);
David Hendricksc801adb2010-12-09 16:58:56 -08001631 if (ret)
1632 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001633
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001634 return ret;
1635}
1636
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001637static int w25_list_ranges(const struct flashctx *flash)
David Hendricks0f7f5382011-02-11 18:12:31 -08001638{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001639 struct wp_range_descriptor *descrs;
David Hendricks0f7f5382011-02-11 18:12:31 -08001640 int i, num_entries;
1641
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001642 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001643 return -1;
1644
David Hendricks0f7f5382011-02-11 18:12:31 -08001645 for (i = 0; i < num_entries; i++) {
1646 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001647 descrs[i].range.start,
1648 descrs[i].range.len);
David Hendricks0f7f5382011-02-11 18:12:31 -08001649 }
1650
1651 return 0;
1652}
1653
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001654static int w25q_wp_status(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001655{
1656 struct w25q_status sr1;
1657 struct w25q_status_2 sr2;
David Hendricksf1bd8802012-10-30 11:37:57 -07001658 uint8_t tmp[2];
David Hendricks1c09f802012-10-03 11:03:48 -07001659 unsigned int start, len;
1660 int ret = 0;
1661
1662 memset(&sr1, 0, sizeof(sr1));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001663 tmp[0] = spi_read_status_register(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001664 memcpy(&sr1, &tmp[0], 1);
David Hendricks1c09f802012-10-03 11:03:48 -07001665
David Hendricksf1bd8802012-10-30 11:37:57 -07001666 memset(&sr2, 0, sizeof(sr2));
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001667 tmp[1] = w25q_read_status_register_2(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001668 memcpy(&sr2, &tmp[1], 1);
1669
1670 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
David Hendricks1c09f802012-10-03 11:03:48 -07001671 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1672 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1673 msg_cinfo("WP: write protect is %s.\n",
1674 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1675
1676 msg_cinfo("WP: write protect range: ");
1677 if (w25_status_to_range(flash, &sr1, &start, &len)) {
1678 msg_cinfo("(cannot resolve the range)\n");
1679 ret = -1;
1680 } else {
1681 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1682 }
1683
1684 return ret;
1685}
1686
1687/*
1688 * W25Q adds an optional byte to the standard WRSR opcode. If /CS is
1689 * de-asserted after the first byte, then it acts like a JEDEC-standard
1690 * WRSR command. if /CS is asserted, then the next data byte is written
1691 * into status register 2.
1692 */
1693#define W25Q_WRSR_OUTSIZE 0x03
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001694static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2)
David Hendricks1c09f802012-10-03 11:03:48 -07001695{
1696 int result;
1697 struct spi_command cmds[] = {
1698 {
1699 /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
1700 .writecnt = JEDEC_WREN_OUTSIZE,
1701 .writearr = (const unsigned char[]){ JEDEC_WREN },
1702 .readcnt = 0,
1703 .readarr = NULL,
1704 }, {
1705 .writecnt = W25Q_WRSR_OUTSIZE,
1706 .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 },
1707 .readcnt = 0,
1708 .readarr = NULL,
1709 }, {
1710 .writecnt = 0,
1711 .writearr = NULL,
1712 .readcnt = 0,
1713 .readarr = NULL,
1714 }};
1715
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001716 result = spi_send_multicommand(flash, cmds);
David Hendricks1c09f802012-10-03 11:03:48 -07001717 if (result) {
1718 msg_cerr("%s failed during command execution\n",
1719 __func__);
1720 }
1721
1722 /* WRSR performs a self-timed erase before the changes take effect. */
David Hendricks60824042014-12-11 17:22:06 -08001723 programmer_delay(100 * 1000);
David Hendricks1c09f802012-10-03 11:03:48 -07001724
1725 return result;
1726}
1727
1728/*
1729 * Set/clear the SRP1 bit in status register 2.
1730 * FIXME: make this more generic if other chips use the same SR2 layout
1731 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001732static int w25q_set_srp1(const struct flashctx *flash, int enable)
David Hendricks1c09f802012-10-03 11:03:48 -07001733{
1734 struct w25q_status sr1;
1735 struct w25q_status_2 sr2;
1736 uint8_t tmp, expected;
1737
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001738 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001739 memcpy(&sr1, &tmp, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001740 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001741 memcpy(&sr2, &tmp, 1);
1742
1743 msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp);
1744
1745 sr2.srp1 = enable ? 1 : 0;
1746
1747 memcpy(&expected, &sr2, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001748 w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2));
David Hendricks1c09f802012-10-03 11:03:48 -07001749
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001750 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001751 msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp);
1752 if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA))
1753 return 1;
1754
1755 return 0;
1756}
1757
1758enum wp_mode get_wp_mode(const char *mode_str)
1759{
1760 enum wp_mode wp_mode = WP_MODE_UNKNOWN;
1761
1762 if (!strcasecmp(mode_str, "hardware"))
1763 wp_mode = WP_MODE_HARDWARE;
1764 else if (!strcasecmp(mode_str, "power_cycle"))
1765 wp_mode = WP_MODE_POWER_CYCLE;
1766 else if (!strcasecmp(mode_str, "permanent"))
1767 wp_mode = WP_MODE_PERMANENT;
1768
1769 return wp_mode;
1770}
1771
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001772static int w25q_disable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001773 enum wp_mode wp_mode)
1774{
1775 int ret = 1;
David Hendricks1c09f802012-10-03 11:03:48 -07001776 struct w25q_status_2 sr2;
1777 uint8_t tmp;
1778
1779 switch (wp_mode) {
1780 case WP_MODE_HARDWARE:
1781 ret = w25_set_srp0(flash, 0);
1782 break;
1783 case WP_MODE_POWER_CYCLE:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001784 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001785 memcpy(&sr2, &tmp, 1);
1786 if (sr2.srp1) {
1787 msg_cerr("%s(): must disconnect power to disable "
1788 "write-protection\n", __func__);
1789 } else {
1790 ret = 0;
1791 }
1792 break;
1793 case WP_MODE_PERMANENT:
1794 msg_cerr("%s(): cannot disable permanent write-protection\n",
1795 __func__);
1796 break;
1797 default:
1798 msg_cerr("%s(): invalid mode specified\n", __func__);
1799 break;
1800 }
1801
1802 if (ret)
1803 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001804
David Hendricks1c09f802012-10-03 11:03:48 -07001805 return ret;
1806}
1807
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001808static int w25q_disable_writeprotect_default(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001809{
1810 return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE);
1811}
1812
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001813static int w25q_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001814 enum wp_mode wp_mode)
1815{
1816 int ret = 1;
1817 struct w25q_status sr1;
1818 struct w25q_status_2 sr2;
1819 uint8_t tmp;
1820
1821 switch (wp_mode) {
1822 case WP_MODE_HARDWARE:
1823 if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) {
1824 msg_cerr("%s(): cannot disable power cycle WP mode\n",
1825 __func__);
1826 break;
1827 }
1828
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001829 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001830 memcpy(&sr1, &tmp, 1);
1831 if (sr1.srp0)
1832 ret = 0;
1833 else
1834 ret = w25_set_srp0(flash, 1);
1835
1836 break;
1837 case WP_MODE_POWER_CYCLE:
1838 if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) {
1839 msg_cerr("%s(): cannot disable hardware WP mode\n",
1840 __func__);
1841 break;
1842 }
1843
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001844 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001845 memcpy(&sr2, &tmp, 1);
1846 if (sr2.srp1)
1847 ret = 0;
1848 else
1849 ret = w25q_set_srp1(flash, 1);
1850
1851 break;
1852 case WP_MODE_PERMANENT:
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001853 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001854 memcpy(&sr1, &tmp, 1);
1855 if (sr1.srp0 == 0) {
1856 ret = w25_set_srp0(flash, 1);
1857 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001858 msg_perr("%s(): cannot enable SRP0 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001859 "permanent WP\n", __func__);
1860 break;
1861 }
1862 }
1863
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001864 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001865 memcpy(&sr2, &tmp, 1);
1866 if (sr2.srp1 == 0) {
1867 ret = w25q_set_srp1(flash, 1);
1868 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001869 msg_perr("%s(): cannot enable SRP1 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001870 "permanent WP\n", __func__);
1871 break;
1872 }
1873 }
1874
1875 break;
David Hendricksf1bd8802012-10-30 11:37:57 -07001876 default:
1877 msg_perr("%s(): invalid mode %d\n", __func__, wp_mode);
1878 break;
David Hendricks1c09f802012-10-03 11:03:48 -07001879 }
1880
1881 if (ret)
1882 msg_cerr("%s(): error=%d.\n", __func__, ret);
1883 return ret;
1884}
1885
1886/* W25P, W25X, and many flash chips from various vendors */
David Hendricksf7924d12010-06-10 21:26:44 -07001887struct wp wp_w25 = {
David Hendricks0f7f5382011-02-11 18:12:31 -08001888 .list_ranges = w25_list_ranges,
David Hendricksf7924d12010-06-10 21:26:44 -07001889 .set_range = w25_set_range,
1890 .enable = w25_enable_writeprotect,
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001891 .disable = w25_disable_writeprotect,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001892 .wp_status = w25_wp_status,
David Hendricks1c09f802012-10-03 11:03:48 -07001893
1894};
1895
1896/* W25Q series has features such as a second status register and SFDP */
1897struct wp wp_w25q = {
1898 .list_ranges = w25_list_ranges,
1899 .set_range = w25_set_range,
1900 .enable = w25q_enable_writeprotect,
1901 /*
1902 * By default, disable hardware write-protection. We may change
1903 * this later if we want to add fine-grained write-protect disable
1904 * as a command-line option.
1905 */
1906 .disable = w25q_disable_writeprotect_default,
1907 .wp_status = w25q_wp_status,
David Hendricksf7924d12010-06-10 21:26:44 -07001908};
David Hendrickse0512a72014-07-15 20:30:47 -07001909
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001910/* W25Q large series has 4 block-protect bits */
1911struct wp wp_w25q_large = {
1912 .list_ranges = w25_list_ranges,
1913 .set_range = w25q_large_set_range,
1914 .enable = w25q_enable_writeprotect,
1915 /*
1916 * By default, disable hardware write-protection. We may change
1917 * this later if we want to add fine-grained write-protect disable
1918 * as a command-line option.
1919 */
1920 .disable = w25q_disable_writeprotect_default,
1921 .wp_status = w25q_large_wp_status,
1922};
1923
Edward O'Callaghan3b996502020-04-12 20:46:51 +10001924static struct wp_range_descriptor gd25q32_cmp0_ranges[] = {
David Hendricksaf3944a2014-07-28 18:37:40 -07001925 /* none, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001926 { { }, 0x00, {0, 0} },
1927 { { }, 0x08, {0, 0} },
1928 { { }, 0x10, {0, 0} },
1929 { { }, 0x18, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001930
David Hendricks148a4bf2015-03-13 21:02:42 -07001931 { { }, 0x01, {0x3f0000, 64 * 1024} },
1932 { { }, 0x02, {0x3e0000, 128 * 1024} },
1933 { { }, 0x03, {0x3c0000, 256 * 1024} },
1934 { { }, 0x04, {0x380000, 512 * 1024} },
1935 { { }, 0x05, {0x300000, 1024 * 1024} },
1936 { { }, 0x06, {0x200000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001937
David Hendricks148a4bf2015-03-13 21:02:42 -07001938 { { }, 0x09, {0x000000, 64 * 1024} },
1939 { { }, 0x0a, {0x000000, 128 * 1024} },
1940 { { }, 0x0b, {0x000000, 256 * 1024} },
1941 { { }, 0x0c, {0x000000, 512 * 1024} },
1942 { { }, 0x0d, {0x000000, 1024 * 1024} },
1943 { { }, 0x0e, {0x000000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001944
1945 /* all, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001946 { { }, 0x07, {0x000000, 4096 * 1024} },
1947 { { }, 0x0f, {0x000000, 4096 * 1024} },
1948 { { }, 0x17, {0x000000, 4096 * 1024} },
1949 { { }, 0x1f, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001950
David Hendricks148a4bf2015-03-13 21:02:42 -07001951 { { }, 0x11, {0x3ff000, 4 * 1024} },
1952 { { }, 0x12, {0x3fe000, 8 * 1024} },
1953 { { }, 0x13, {0x3fc000, 16 * 1024} },
1954 { { }, 0x14, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1955 { { }, 0x15, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1956 { { }, 0x16, {0x3f8000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001957
David Hendricks148a4bf2015-03-13 21:02:42 -07001958 { { }, 0x19, {0x000000, 4 * 1024} },
1959 { { }, 0x1a, {0x000000, 8 * 1024} },
1960 { { }, 0x1b, {0x000000, 16 * 1024} },
1961 { { }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1962 { { }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1963 { { }, 0x1e, {0x000000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001964};
1965
Edward O'Callaghan3b996502020-04-12 20:46:51 +10001966static struct wp_range_descriptor gd25q32_cmp1_ranges[] = {
Martin Roth563a1fe2017-04-18 14:26:27 -06001967 /* All, bp4 and bp3 => don't care */
1968 { { }, 0x00, {0x000000, 4096 * 1024} }, /* All */
1969 { { }, 0x08, {0x000000, 4096 * 1024} },
1970 { { }, 0x10, {0x000000, 4096 * 1024} },
1971 { { }, 0x18, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001972
David Hendricks148a4bf2015-03-13 21:02:42 -07001973 { { }, 0x01, {0x000000, 4032 * 1024} },
1974 { { }, 0x02, {0x000000, 3968 * 1024} },
1975 { { }, 0x03, {0x000000, 3840 * 1024} },
1976 { { }, 0x04, {0x000000, 3584 * 1024} },
1977 { { }, 0x05, {0x000000, 3 * 1024 * 1024} },
1978 { { }, 0x06, {0x000000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001979
David Hendricks148a4bf2015-03-13 21:02:42 -07001980 { { }, 0x09, {0x010000, 4032 * 1024} },
1981 { { }, 0x0a, {0x020000, 3968 * 1024} },
1982 { { }, 0x0b, {0x040000, 3840 * 1024} },
1983 { { }, 0x0c, {0x080000, 3584 * 1024} },
1984 { { }, 0x0d, {0x100000, 3 * 1024 * 1024} },
1985 { { }, 0x0e, {0x200000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001986
Martin Roth563a1fe2017-04-18 14:26:27 -06001987 /* None, bp4 and bp3 => don't care */
1988 { { }, 0x07, {0, 0} }, /* None */
1989 { { }, 0x0f, {0, 0} },
1990 { { }, 0x17, {0, 0} },
1991 { { }, 0x1f, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001992
David Hendricks148a4bf2015-03-13 21:02:42 -07001993 { { }, 0x11, {0x000000, 4092 * 1024} },
1994 { { }, 0x12, {0x000000, 4088 * 1024} },
1995 { { }, 0x13, {0x000000, 4080 * 1024} },
1996 { { }, 0x14, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
1997 { { }, 0x15, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
1998 { { }, 0x16, {0x000000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001999
David Hendricks148a4bf2015-03-13 21:02:42 -07002000 { { }, 0x19, {0x001000, 4092 * 1024} },
2001 { { }, 0x1a, {0x002000, 4088 * 1024} },
2002 { { }, 0x1b, {0x040000, 4080 * 1024} },
2003 { { }, 0x1c, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
2004 { { }, 0x1d, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
2005 { { }, 0x1e, {0x080000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002006};
2007
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002008static struct status_register_layout gd25q32_sr1 = {
David Hendricksaf3944a2014-07-28 18:37:40 -07002009 /* TODO: map second status register */
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002010 .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7
David Hendricksaf3944a2014-07-28 18:37:40 -07002011};
2012
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002013static struct wp_range_descriptor gd25q128_cmp0_ranges[] = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002014 /* none, bp4 and bp3 => don't care, others = 0 */
2015 { { .tb = 0 }, 0x00, {0, 0} },
2016 { { .tb = 0 }, 0x08, {0, 0} },
2017 { { .tb = 0 }, 0x10, {0, 0} },
2018 { { .tb = 0 }, 0x18, {0, 0} },
2019
2020 { { .tb = 0 }, 0x01, {0xfc0000, 256 * 1024} },
2021 { { .tb = 0 }, 0x02, {0xf80000, 512 * 1024} },
2022 { { .tb = 0 }, 0x03, {0xf00000, 1024 * 1024} },
2023 { { .tb = 0 }, 0x04, {0xe00000, 2048 * 1024} },
2024 { { .tb = 0 }, 0x05, {0xc00000, 4096 * 1024} },
2025 { { .tb = 0 }, 0x06, {0x800000, 8192 * 1024} },
2026
2027 { { .tb = 0 }, 0x09, {0x000000, 256 * 1024} },
2028 { { .tb = 0 }, 0x0a, {0x000000, 512 * 1024} },
2029 { { .tb = 0 }, 0x0b, {0x000000, 1024 * 1024} },
2030 { { .tb = 0 }, 0x0c, {0x000000, 2048 * 1024} },
2031 { { .tb = 0 }, 0x0d, {0x000000, 4096 * 1024} },
2032 { { .tb = 0 }, 0x0e, {0x000000, 8192 * 1024} },
2033
2034 /* all, bp4 and bp3 => don't care, others = 1 */
2035 { { .tb = 0 }, 0x07, {0x000000, 16384 * 1024} },
2036 { { .tb = 0 }, 0x0f, {0x000000, 16384 * 1024} },
2037 { { .tb = 0 }, 0x17, {0x000000, 16384 * 1024} },
2038 { { .tb = 0 }, 0x1f, {0x000000, 16384 * 1024} },
2039
2040 { { .tb = 0 }, 0x11, {0xfff000, 4 * 1024} },
2041 { { .tb = 0 }, 0x12, {0xffe000, 8 * 1024} },
2042 { { .tb = 0 }, 0x13, {0xffc000, 16 * 1024} },
2043 { { .tb = 0 }, 0x14, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
2044 { { .tb = 0 }, 0x15, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
2045
2046 { { .tb = 0 }, 0x19, {0x000000, 4 * 1024} },
2047 { { .tb = 0 }, 0x1a, {0x000000, 8 * 1024} },
2048 { { .tb = 0 }, 0x1b, {0x000000, 16 * 1024} },
2049 { { .tb = 0 }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2050 { { .tb = 0 }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2051 { { .tb = 0 }, 0x1e, {0x000000, 32 * 1024} },
2052};
2053
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002054static struct wp_range_descriptor gd25q128_cmp1_ranges[] = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002055 /* none, bp4 and bp3 => don't care, others = 0 */
2056 { { .tb = 1 }, 0x00, {0x000000, 16384 * 1024} },
2057 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
2058 { { .tb = 1 }, 0x10, {0x000000, 16384 * 1024} },
2059 { { .tb = 1 }, 0x18, {0x000000, 16384 * 1024} },
2060
2061 { { .tb = 1 }, 0x01, {0x000000, 16128 * 1024} },
2062 { { .tb = 1 }, 0x02, {0x000000, 15872 * 1024} },
2063 { { .tb = 1 }, 0x03, {0x000000, 15360 * 1024} },
2064 { { .tb = 1 }, 0x04, {0x000000, 14336 * 1024} },
2065 { { .tb = 1 }, 0x05, {0x000000, 12288 * 1024} },
2066 { { .tb = 1 }, 0x06, {0x000000, 8192 * 1024} },
2067
2068 { { .tb = 1 }, 0x09, {0x000000, 16128 * 1024} },
2069 { { .tb = 1 }, 0x0a, {0x000000, 15872 * 1024} },
2070 { { .tb = 1 }, 0x0b, {0x000000, 15360 * 1024} },
2071 { { .tb = 1 }, 0x0c, {0x000000, 14336 * 1024} },
2072 { { .tb = 1 }, 0x0d, {0x000000, 12288 * 1024} },
2073 { { .tb = 1 }, 0x0e, {0x000000, 8192 * 1024} },
2074
2075 /* none, bp4 and bp3 => don't care, others = 1 */
2076 { { .tb = 1 }, 0x07, {0x000000, 16384 * 1024} },
2077 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
2078 { { .tb = 1 }, 0x0f, {0x000000, 16384 * 1024} },
2079 { { .tb = 1 }, 0x17, {0x000000, 16384 * 1024} },
2080 { { .tb = 1 }, 0x1f, {0x000000, 16384 * 1024} },
2081
2082 { { .tb = 1 }, 0x11, {0x000000, 16380 * 1024} },
2083 { { .tb = 1 }, 0x12, {0x000000, 16376 * 1024} },
2084 { { .tb = 1 }, 0x13, {0x000000, 16368 * 1024} },
2085 { { .tb = 1 }, 0x14, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
2086 { { .tb = 1 }, 0x15, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
2087
2088 { { .tb = 1 }, 0x19, {0x001000, 16380 * 1024} },
2089 { { .tb = 1 }, 0x1a, {0x002000, 16376 * 1024} },
2090 { { .tb = 1 }, 0x1b, {0x004000, 16368 * 1024} },
2091 { { .tb = 1 }, 0x1c, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
2092 { { .tb = 1 }, 0x1d, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
2093 { { .tb = 1 }, 0x1e, {0x008000, 16352 * 1024} },
2094};
2095
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002096static struct status_register_layout gd25q128_sr1 = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002097 /* TODO: map second and third status registers */
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002098 .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002099};
2100
David Hendricks83541d32014-07-15 20:58:21 -07002101/* FIXME: MX25L6406 has same ID as MX25L6405D */
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002102static struct wp_range_descriptor mx25l6406e_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002103 { { }, 0, {0, 0} }, /* none */
2104 { { }, 0x1, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
2105 { { }, 0x2, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
2106 { { }, 0x3, {0x7a0000, 64 * 8 * 1024} }, /* blocks 120-127 */
2107 { { }, 0x4, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
2108 { { }, 0x5, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
2109 { { }, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricks83541d32014-07-15 20:58:21 -07002110
David Hendricks148a4bf2015-03-13 21:02:42 -07002111 { { }, 0x7, {0x000000, 64 * 128 * 1024} }, /* all */
2112 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2113 { { }, 0x9, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2114 { { }, 0xa, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
2115 { { }, 0xb, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
2116 { { }, 0xc, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
2117 { { }, 0xd, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
2118 { { }, 0xe, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
2119 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricks83541d32014-07-15 20:58:21 -07002120};
2121
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002122static struct status_register_layout mx25l6406e_sr1 = {
2123 .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7
David Hendricks83541d32014-07-15 20:58:21 -07002124};
David Hendrickse0512a72014-07-15 20:30:47 -07002125
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002126static struct wp_range_descriptor mx25l6495f_tb0_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002127 { { }, 0, {0, 0} }, /* none */
2128 { { }, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
2129 { { }, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
2130 { { }, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
David Hendricksc3496092014-11-13 17:20:55 -08002131
David Hendricks148a4bf2015-03-13 21:02:42 -07002132 { { }, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
2133 { { }, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
2134 { { }, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
2135 { { }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
2136 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2137 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2138 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2139 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2140 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2141 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2142 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2143 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002144};
2145
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002146static struct wp_range_descriptor mx25l6495f_tb1_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002147 { { }, 0, {0, 0} }, /* none */
2148 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2149 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2150 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2151 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2152 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2153 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2154 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2155 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2156 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2157 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2158 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2159 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2160 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2161 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2162 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002163};
2164
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002165static struct status_register_layout mx25l6495f_sr1 = {
2166 .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7
David Hendricksc3496092014-11-13 17:20:55 -08002167};
2168
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002169static struct wp_range_descriptor mx25l25635f_tb0_ranges[] = {
Vic Yang848bfd12018-03-23 10:24:07 -07002170 { { }, 0, {0, 0} }, /* none */
2171 { { }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* block 511 */
2172 { { }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* blocks 510-511 */
2173 { { }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* blocks 508-511 */
2174 { { }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* blocks 504-511 */
2175 { { }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* blocks 496-511 */
2176 { { }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* blocks 480-511 */
2177 { { }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* blocks 448-511 */
2178 { { }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* blocks 384-511 */
2179 { { }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* blocks 256-511 */
2180 { { }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* all */
2181 { { }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* all */
2182 { { }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* all */
2183 { { }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* all */
2184 { { }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* all */
2185 { { }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* all */
2186};
2187
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002188static struct wp_range_descriptor mx25l25635f_tb1_ranges[] = {
Vic Yang848bfd12018-03-23 10:24:07 -07002189 { { }, 0, {0, 0} }, /* none */
2190 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2191 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2192 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2193 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2194 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2195 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2196 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2197 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
2198 { { }, 0x9, {0x000000, 64 * 256 * 1024} }, /* blocks 0-255 */
2199 { { }, 0xa, {0x000000, 64 * 512 * 1024} }, /* all */
2200 { { }, 0xb, {0x000000, 64 * 512 * 1024} }, /* all */
2201 { { }, 0xc, {0x000000, 64 * 512 * 1024} }, /* all */
2202 { { }, 0xd, {0x000000, 64 * 512 * 1024} }, /* all */
2203 { { }, 0xe, {0x000000, 64 * 512 * 1024} }, /* all */
2204 { { }, 0xf, {0x000000, 64 * 512 * 1024} }, /* all */
2205};
2206
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002207static struct status_register_layout mx25l25635f_sr1 = {
2208 .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7
Vic Yang848bfd12018-03-23 10:24:07 -07002209};
2210
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002211static struct wp_range_descriptor s25fs128s_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002212 { { .tb = 1 }, 0, {0, 0} }, /* none */
2213 { { .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* lower 64th */
2214 { { .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* lower 32nd */
2215 { { .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* lower 16th */
2216 { { .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* lower 8th */
2217 { { .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* lower 4th */
2218 { { .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* lower half */
2219 { { .tb = 1 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002220
David Hendricks148a4bf2015-03-13 21:02:42 -07002221 { { .tb = 0 }, 0, {0, 0} }, /* none */
2222 { { .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* upper 64th */
2223 { { .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* upper 32nd */
2224 { { .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* upper 16th */
2225 { { .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* upper 8th */
2226 { { .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* upper 4th */
2227 { { .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* upper half */
2228 { { .tb = 0 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002229};
2230
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002231static struct status_register_layout s25fs128s_sr1 = {
2232 .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7
David Hendricksa9884852014-12-11 15:31:12 -08002233};
2234
David Hendricksc694bb82015-02-25 14:52:17 -08002235
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002236static struct wp_range_descriptor s25fl256s_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002237 { { .tb = 1 }, 0, {0, 0} }, /* none */
2238 { { .tb = 1 }, 0x1, {0x000000, 512 * 1024} }, /* lower 64th */
2239 { { .tb = 1 }, 0x2, {0x000000, 1024 * 1024} }, /* lower 32nd */
2240 { { .tb = 1 }, 0x3, {0x000000, 2048 * 1024} }, /* lower 16th */
2241 { { .tb = 1 }, 0x4, {0x000000, 4096 * 1024} }, /* lower 8th */
2242 { { .tb = 1 }, 0x5, {0x000000, 8192 * 1024} }, /* lower 4th */
2243 { { .tb = 1 }, 0x6, {0x000000, 16384 * 1024} }, /* lower half */
2244 { { .tb = 1 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
2245
2246 { { .tb = 0 }, 0, {0, 0} }, /* none */
2247 { { .tb = 0 }, 0x1, {0x1f80000, 512 * 1024} }, /* upper 64th */
2248 { { .tb = 0 }, 0x2, {0x1f00000, 1024 * 1024} }, /* upper 32nd */
2249 { { .tb = 0 }, 0x3, {0x1e00000, 2048 * 1024} }, /* upper 16th */
2250 { { .tb = 0 }, 0x4, {0x1c00000, 4096 * 1024} }, /* upper 8th */
2251 { { .tb = 0 }, 0x5, {0x1800000, 8192 * 1024} }, /* upper 4th */
2252 { { .tb = 0 }, 0x6, {0x1000000, 16384 * 1024} }, /* upper half */
2253 { { .tb = 0 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
David Hendricksc694bb82015-02-25 14:52:17 -08002254};
2255
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002256static struct status_register_layout s25fl256s_sr1 = {
2257 .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7
David Hendricksc694bb82015-02-25 14:52:17 -08002258};
2259
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002260static int get_sr1_layout(
2261 const struct flashctx *flash, struct status_register_layout *sr1)
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002262{
2263 switch (flash->chip->manufacture_id) {
2264 case GIGADEVICE_ID:
2265 switch(flash->chip->model_id) {
2266
2267 case GIGADEVICE_GD25Q32:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002268 *sr1 = gd25q32_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002269 return 0;
2270 case GIGADEVICE_GD25LQ128CD:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002271 *sr1 = gd25q128_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002272 return 0;
2273 }
2274 break;
2275 case MACRONIX_ID:
2276 switch (flash->chip->model_id) {
2277 case MACRONIX_MX25L6405:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002278 *sr1 = mx25l6406e_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002279 return 0;
2280 case MACRONIX_MX25L6495F:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002281 *sr1 = mx25l6495f_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002282 return 0;
2283 case MACRONIX_MX25L25635F:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002284 *sr1 = mx25l25635f_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002285 return 0;
2286 }
2287 break;
2288 case SPANSION_ID:
2289 switch (flash->chip->model_id) {
2290 case SPANSION_S25FS128S_L:
2291 case SPANSION_S25FS128S_S:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002292 *sr1 = s25fs128s_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002293 return 0;
2294 case SPANSION_S25FL256S_UL:
2295 case SPANSION_S25FL256S_US:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002296 *sr1 = s25fl256s_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002297 return 0;
2298 }
2299 break;
2300 }
2301
2302 return 1;
2303}
2304
David Hendrickse0512a72014-07-15 20:30:47 -07002305/* Given a flash chip, this function returns its writeprotect info. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002306static int generic_range_table(const struct flashctx *flash,
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002307 struct wp_range_descriptor **descrs,
David Hendrickse0512a72014-07-15 20:30:47 -07002308 int *num_entries)
2309{
David Hendrickse0512a72014-07-15 20:30:47 -07002310 *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
David Hendricksaf3944a2014-07-28 18:37:40 -07002316 case GIGADEVICE_GD25Q32: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002317 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002318
David Hendricksaf3944a2014-07-28 18:37:40 -07002319 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002320 *descrs = &gd25q32_cmp0_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002321 *num_entries = ARRAY_SIZE(gd25q32_cmp0_ranges);
2322 } else { /* CMP == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002323 *descrs = &gd25q32_cmp1_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002324 *num_entries = ARRAY_SIZE(gd25q32_cmp1_ranges);
2325 }
2326
2327 break;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002328 }
Aaron Durbin6c957d72018-08-20 09:31:01 -06002329 case GIGADEVICE_GD25LQ128CD: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002330 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002331
2332 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002333 *descrs = &gd25q128_cmp0_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002334 *num_entries = ARRAY_SIZE(gd25q128_cmp0_ranges);
2335 } else { /* CMP == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002336 *descrs = &gd25q128_cmp1_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002337 *num_entries = ARRAY_SIZE(gd25q128_cmp1_ranges);
2338 }
2339
2340 break;
David Hendricksaf3944a2014-07-28 18:37:40 -07002341 }
2342 default:
2343 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
2344 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002345 flash->chip->model_id);
David Hendricksaf3944a2014-07-28 18:37:40 -07002346 return -1;
2347 }
2348 break;
David Hendricks83541d32014-07-15 20:58:21 -07002349 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002350 switch (flash->chip->model_id) {
David Hendricks83541d32014-07-15 20:58:21 -07002351 case MACRONIX_MX25L6405:
2352 /* FIXME: MX25L64* chips have mixed capabilities and
2353 share IDs */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002354 *descrs = &mx25l6406e_ranges[0];
David Hendricks83541d32014-07-15 20:58:21 -07002355 *num_entries = ARRAY_SIZE(mx25l6406e_ranges);
2356 break;
David Hendricksc3496092014-11-13 17:20:55 -08002357 case MACRONIX_MX25L6495F: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002358 uint8_t cr = mx25l_read_config_register(flash);
David Hendricksc3496092014-11-13 17:20:55 -08002359
David Hendricksc3496092014-11-13 17:20:55 -08002360 if (!(cr & (1 << 3))) { /* T/B == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002361 *descrs = &mx25l6495f_tb0_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002362 *num_entries = ARRAY_SIZE(mx25l6495f_tb0_ranges);
2363 } else { /* T/B == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002364 *descrs = &mx25l6495f_tb1_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002365 *num_entries = ARRAY_SIZE(mx25l6495f_tb1_ranges);
2366 }
2367 break;
2368 }
Vic Yang848bfd12018-03-23 10:24:07 -07002369 case MACRONIX_MX25L25635F: {
2370 uint8_t cr = mx25l_read_config_register(flash);
2371
Vic Yang848bfd12018-03-23 10:24:07 -07002372 if (!(cr & (1 << 3))) { /* T/B == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002373 *descrs = &mx25l25635f_tb0_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002374 *num_entries = ARRAY_SIZE(mx25l25635f_tb0_ranges);
2375 } else { /* T/B == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002376 *descrs = &mx25l25635f_tb1_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002377 *num_entries = ARRAY_SIZE(mx25l25635f_tb1_ranges);
2378 }
2379 break;
2380 }
David Hendricks83541d32014-07-15 20:58:21 -07002381 default:
2382 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
2383 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002384 flash->chip->model_id);
David Hendricks83541d32014-07-15 20:58:21 -07002385 return -1;
2386 }
2387 break;
David Hendricksa9884852014-12-11 15:31:12 -08002388 case SPANSION_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002389 switch (flash->chip->model_id) {
David Hendricksa9884852014-12-11 15:31:12 -08002390 case SPANSION_S25FS128S_L:
2391 case SPANSION_S25FS128S_S: {
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002392 *descrs = s25fs128s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002393 *num_entries = ARRAY_SIZE(s25fs128s_ranges);
David Hendricksa9884852014-12-11 15:31:12 -08002394 break;
2395 }
David Hendricksc694bb82015-02-25 14:52:17 -08002396 case SPANSION_S25FL256S_UL:
2397 case SPANSION_S25FL256S_US: {
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002398 *descrs = s25fl256s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002399 *num_entries = ARRAY_SIZE(s25fl256s_ranges);
David Hendricksc694bb82015-02-25 14:52:17 -08002400 break;
2401 }
David Hendricksa9884852014-12-11 15:31:12 -08002402 default:
2403 msg_cerr("%s():%d Spansion flash chip mismatch (0x%04x)"
Patrick Georgif3fa2992017-02-02 16:24:44 +01002404 ", aborting\n", __func__, __LINE__,
2405 flash->chip->model_id);
David Hendricksa9884852014-12-11 15:31:12 -08002406 return -1;
2407 }
2408 break;
David Hendrickse0512a72014-07-15 20:30:47 -07002409 default:
2410 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01002411 __func__, flash->chip->manufacture_id);
David Hendrickse0512a72014-07-15 20:30:47 -07002412 return -1;
2413 }
2414
2415 return 0;
2416}
2417
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002418/* Determines if special s25f-specific functions need to be used to access a
2419 * given chip's modifier bits. Very much a hard-coded special case hack, but it
2420 * is also very easy to replace once a proper abstraction for accessing
2421 * specific modifier bits is added. */
2422static int use_s25f_modifier_bits(const struct flashctx *flash)
2423{
2424 bool model_match =
2425 flash->chip->model_id == SPANSION_S25FS128S_L ||
2426 flash->chip->model_id == SPANSION_S25FS128S_S ||
2427 flash->chip->model_id == SPANSION_S25FL256S_UL ||
2428 flash->chip->model_id == SPANSION_S25FL256S_US;
2429 return (flash->chip->manufacture_id == SPANSION_ID) && model_match;
2430}
2431
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002432static uint8_t generic_get_bp_mask(struct status_register_layout sr1)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002433{
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002434 return ((1 << (sr1.bp0_pos + sr1.bp_bits)) - 1) ^ \
2435 ((1 << sr1.bp0_pos) - 1);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002436}
2437
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002438static uint8_t generic_get_status_check_mask(struct status_register_layout sr1)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002439{
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002440 return generic_get_bp_mask(sr1) | 1 << sr1.srp_pos;
Marco Chen9d5bddb2020-02-11 17:12:56 +08002441}
2442
David Hendrickse0512a72014-07-15 20:30:47 -07002443/* Given a [start, len], this function finds a block protect bit combination
2444 * (if possible) and sets the corresponding bits in "status". Remaining bits
2445 * are preserved. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002446static int generic_range_to_status(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002447 unsigned int start, unsigned int len,
Marco Chen9d5bddb2020-02-11 17:12:56 +08002448 uint8_t *status, uint8_t *check_mask)
David Hendrickse0512a72014-07-15 20:30:47 -07002449{
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002450 struct status_register_layout sr1;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002451 struct wp_range_descriptor *r;
David Hendrickse0512a72014-07-15 20:30:47 -07002452 int i, range_found = 0, num_entries;
2453 uint8_t bp_mask;
2454
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002455 if (get_sr1_layout(flash, &sr1))
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002456 return -1;
2457
2458 if (generic_range_table(flash, &r, &num_entries))
David Hendrickse0512a72014-07-15 20:30:47 -07002459 return -1;
2460
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002461 bp_mask = generic_get_bp_mask(sr1);
David Hendrickse0512a72014-07-15 20:30:47 -07002462
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002463 for (i = 0; i < num_entries; i++, r++) {
David Hendrickse0512a72014-07-15 20:30:47 -07002464 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
2465 start, len, r->range.start, r->range.len);
2466 if ((start == r->range.start) && (len == r->range.len)) {
2467 *status &= ~(bp_mask);
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002468 *status |= r->bp << (sr1.bp0_pos);
David Hendricks148a4bf2015-03-13 21:02:42 -07002469
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002470 if (use_s25f_modifier_bits(flash)) {
2471 if (s25f_set_modifier_bits(flash, &r->m) < 0) {
Edward O'Callaghan0b662c12021-01-22 00:30:24 +11002472 msg_cerr("error setting modifier bits for range.\n");
David Hendricks148a4bf2015-03-13 21:02:42 -07002473 return -1;
2474 }
2475 }
2476
David Hendrickse0512a72014-07-15 20:30:47 -07002477 range_found = 1;
2478 break;
2479 }
2480 }
2481
2482 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11002483 msg_cerr("%s: matching range not found\n", __func__);
David Hendrickse0512a72014-07-15 20:30:47 -07002484 return -1;
2485 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002486
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002487 *check_mask = generic_get_status_check_mask(sr1);
David Hendrickse0512a72014-07-15 20:30:47 -07002488 return 0;
2489}
2490
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002491static int generic_status_to_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002492 const uint8_t sr1, unsigned int *start, unsigned int *len)
2493{
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002494 struct status_register_layout sr1_layout;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002495 struct wp_range_descriptor *r;
Duncan Laurie04ca1172015-03-12 09:25:34 -07002496 int num_entries, i, status_found = 0;
David Hendrickse0512a72014-07-15 20:30:47 -07002497 uint8_t sr1_bp;
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +11002498 struct modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -07002499
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002500 if (get_sr1_layout(flash, &sr1_layout))
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002501 return -1;
2502
2503 if (generic_range_table(flash, &r, &num_entries))
David Hendrickse0512a72014-07-15 20:30:47 -07002504 return -1;
2505
David Hendricks148a4bf2015-03-13 21:02:42 -07002506 /* modifier bits may be compared more than once, so get them here */
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002507 if (use_s25f_modifier_bits(flash) && s25f_get_modifier_bits(flash, &m) < 0)
2508 return -1;
David Hendricks148a4bf2015-03-13 21:02:42 -07002509
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002510 sr1_bp = (sr1 >> sr1_layout.bp0_pos) & ((1 << sr1_layout.bp_bits) - 1);
David Hendrickse0512a72014-07-15 20:30:47 -07002511
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002512 for (i = 0; i < num_entries; i++, r++) {
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002513 if (use_s25f_modifier_bits(flash)) {
David Hendricks148a4bf2015-03-13 21:02:42 -07002514 if (memcmp(&m, &r->m, sizeof(m)))
2515 continue;
2516 }
David Hendrickse0512a72014-07-15 20:30:47 -07002517 msg_cspew("comparing 0x%02x 0x%02x\n", sr1_bp, r->bp);
2518 if (sr1_bp == r->bp) {
2519 *start = r->range.start;
2520 *len = r->range.len;
2521 status_found = 1;
2522 break;
2523 }
2524 }
2525
2526 if (!status_found) {
2527 msg_cerr("matching status not found\n");
2528 return -1;
2529 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002530
David Hendrickse0512a72014-07-15 20:30:47 -07002531 return 0;
2532}
2533
2534/* Given a [start, len], this function calls generic_range_to_status() to
2535 * convert it to flash-chip-specific range bits, then sets into status register.
2536 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002537static int generic_set_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002538 unsigned int start, unsigned int len)
2539{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002540 uint8_t status, expected, check_mask;
David Hendrickse0512a72014-07-15 20:30:47 -07002541
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002542 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002543 msg_cdbg("%s: old status: 0x%02x\n", __func__, status);
2544
2545 expected = status; /* preserve non-bp bits */
Marco Chen9d5bddb2020-02-11 17:12:56 +08002546 if (generic_range_to_status(flash, start, len, &expected, &check_mask))
David Hendrickse0512a72014-07-15 20:30:47 -07002547 return -1;
2548
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002549 spi_write_status_register(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002550
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002551 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002552 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002553 if ((status & check_mask) != (expected & check_mask)) {
2554 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2555 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002556 return 1;
2557 }
David Hendrickse0512a72014-07-15 20:30:47 -07002558 return 0;
2559}
2560
2561/* Set/clear the status regsiter write protect bit in SR1. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002562static int generic_set_srp0(const struct flashctx *flash, int enable)
David Hendrickse0512a72014-07-15 20:30:47 -07002563{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002564 uint8_t status, expected, check_mask;
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002565 struct status_register_layout sr1;
David Hendrickse0512a72014-07-15 20:30:47 -07002566
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002567 if (get_sr1_layout(flash, &sr1))
David Hendrickse0512a72014-07-15 20:30:47 -07002568 return -1;
2569
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002570 expected = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002571 msg_cdbg("%s: old status: 0x%02x\n", __func__, expected);
2572
2573 if (enable)
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002574 expected |= 1 << sr1.srp_pos;
David Hendrickse0512a72014-07-15 20:30:47 -07002575 else
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002576 expected &= ~(1 << sr1.srp_pos);
David Hendrickse0512a72014-07-15 20:30:47 -07002577
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002578 spi_write_status_register(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002579
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002580 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002581 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002582
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002583 check_mask = generic_get_status_check_mask(sr1);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002584 msg_cdbg("%s: check mask: 0x%02x\n", __func__, check_mask);
2585 if ((status & check_mask) != (expected & check_mask)) {
2586 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2587 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002588 return -1;
Marco Chen9d5bddb2020-02-11 17:12:56 +08002589 }
David Hendrickse0512a72014-07-15 20:30:47 -07002590
2591 return 0;
2592}
2593
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002594static int generic_enable_writeprotect(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002595 enum wp_mode wp_mode)
2596{
2597 int ret;
2598
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002599 if (wp_mode != WP_MODE_HARDWARE) {
David Hendrickse0512a72014-07-15 20:30:47 -07002600 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
2601 return 1;
2602 }
2603
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002604 ret = generic_set_srp0(flash, 1);
David Hendrickse0512a72014-07-15 20:30:47 -07002605 if (ret)
2606 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002607
David Hendrickse0512a72014-07-15 20:30:47 -07002608 return ret;
2609}
2610
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002611static int generic_disable_writeprotect(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002612{
2613 int ret;
2614
2615 ret = generic_set_srp0(flash, 0);
2616 if (ret)
2617 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002618
David Hendrickse0512a72014-07-15 20:30:47 -07002619 return ret;
2620}
2621
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002622static int generic_list_ranges(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002623{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002624 struct wp_range_descriptor *r;
David Hendrickse0512a72014-07-15 20:30:47 -07002625 int i, num_entries;
2626
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002627 if (generic_range_table(flash, &r, &num_entries))
David Hendrickse0512a72014-07-15 20:30:47 -07002628 return -1;
2629
David Hendrickse0512a72014-07-15 20:30:47 -07002630 for (i = 0; i < num_entries; i++) {
2631 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
2632 r->range.start, r->range.len);
2633 r++;
2634 }
2635
2636 return 0;
2637}
2638
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002639static int wp_context_status(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002640{
2641 uint8_t sr1;
2642 unsigned int start, len;
2643 int ret = 0;
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002644 struct status_register_layout sr1_layout;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002645 int wp_en;
David Hendrickse0512a72014-07-15 20:30:47 -07002646
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002647 if (get_sr1_layout(flash, &sr1_layout))
David Hendrickse0512a72014-07-15 20:30:47 -07002648 return -1;
2649
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002650 sr1 = spi_read_status_register(flash);
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002651 wp_en = (sr1 >> sr1_layout.srp_pos) & 1;
David Hendrickse0512a72014-07-15 20:30:47 -07002652
2653 msg_cinfo("WP: status: 0x%04x\n", sr1);
2654 msg_cinfo("WP: status.srp0: %x\n", wp_en);
2655 /* FIXME: SRP1 is not really generic, but we probably should print
2656 * it anyway to have consistent output. #legacycruft */
2657 msg_cinfo("WP: status.srp1: %x\n", 0);
2658 msg_cinfo("WP: write protect is %s.\n",
2659 wp_en ? "enabled" : "disabled");
2660
2661 msg_cinfo("WP: write protect range: ");
2662 if (generic_status_to_range(flash, sr1, &start, &len)) {
2663 msg_cinfo("(cannot resolve the range)\n");
2664 ret = -1;
2665 } else {
2666 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
2667 }
2668
2669 return ret;
2670}
2671
2672struct wp wp_generic = {
2673 .list_ranges = generic_list_ranges,
2674 .set_range = generic_set_range,
2675 .enable = generic_enable_writeprotect,
2676 .disable = generic_disable_writeprotect,
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002677 .wp_status = wp_context_status,
David Hendrickse0512a72014-07-15 20:30:47 -07002678};