blob: b629d15f215f9abeb920a5a244cfb000ba50a485 [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 Artemiev423ab802021-04-06 16:55:48 +1000819static struct wp_range_descriptor gd25q32_cmp0_ranges[] = {
820 /* none, bp4 and bp3 => don't care */
821 { { }, 0x00, {0, 0} },
822 { { }, 0x08, {0, 0} },
823 { { }, 0x10, {0, 0} },
824 { { }, 0x18, {0, 0} },
825
826 { { }, 0x01, {0x3f0000, 64 * 1024} },
827 { { }, 0x02, {0x3e0000, 128 * 1024} },
828 { { }, 0x03, {0x3c0000, 256 * 1024} },
829 { { }, 0x04, {0x380000, 512 * 1024} },
830 { { }, 0x05, {0x300000, 1024 * 1024} },
831 { { }, 0x06, {0x200000, 2048 * 1024} },
832
833 { { }, 0x09, {0x000000, 64 * 1024} },
834 { { }, 0x0a, {0x000000, 128 * 1024} },
835 { { }, 0x0b, {0x000000, 256 * 1024} },
836 { { }, 0x0c, {0x000000, 512 * 1024} },
837 { { }, 0x0d, {0x000000, 1024 * 1024} },
838 { { }, 0x0e, {0x000000, 2048 * 1024} },
839
840 /* all, bp4 and bp3 => don't care */
841 { { }, 0x07, {0x000000, 4096 * 1024} },
842 { { }, 0x0f, {0x000000, 4096 * 1024} },
843 { { }, 0x17, {0x000000, 4096 * 1024} },
844 { { }, 0x1f, {0x000000, 4096 * 1024} },
845
846 { { }, 0x11, {0x3ff000, 4 * 1024} },
847 { { }, 0x12, {0x3fe000, 8 * 1024} },
848 { { }, 0x13, {0x3fc000, 16 * 1024} },
849 { { }, 0x14, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
850 { { }, 0x15, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
851 { { }, 0x16, {0x3f8000, 32 * 1024} },
852
853 { { }, 0x19, {0x000000, 4 * 1024} },
854 { { }, 0x1a, {0x000000, 8 * 1024} },
855 { { }, 0x1b, {0x000000, 16 * 1024} },
856 { { }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
857 { { }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
858 { { }, 0x1e, {0x000000, 32 * 1024} },
859};
860
861static struct wp_range_descriptor gd25q32_cmp1_ranges[] = {
862 /* All, bp4 and bp3 => don't care */
863 { { }, 0x00, {0x000000, 4096 * 1024} }, /* All */
864 { { }, 0x08, {0x000000, 4096 * 1024} },
865 { { }, 0x10, {0x000000, 4096 * 1024} },
866 { { }, 0x18, {0x000000, 4096 * 1024} },
867
868 { { }, 0x01, {0x000000, 4032 * 1024} },
869 { { }, 0x02, {0x000000, 3968 * 1024} },
870 { { }, 0x03, {0x000000, 3840 * 1024} },
871 { { }, 0x04, {0x000000, 3584 * 1024} },
872 { { }, 0x05, {0x000000, 3 * 1024 * 1024} },
873 { { }, 0x06, {0x000000, 2 * 1024 * 1024} },
874
875 { { }, 0x09, {0x010000, 4032 * 1024} },
876 { { }, 0x0a, {0x020000, 3968 * 1024} },
877 { { }, 0x0b, {0x040000, 3840 * 1024} },
878 { { }, 0x0c, {0x080000, 3584 * 1024} },
879 { { }, 0x0d, {0x100000, 3 * 1024 * 1024} },
880 { { }, 0x0e, {0x200000, 2 * 1024 * 1024} },
881
882 /* None, bp4 and bp3 => don't care */
883 { { }, 0x07, {0, 0} }, /* None */
884 { { }, 0x0f, {0, 0} },
885 { { }, 0x17, {0, 0} },
886 { { }, 0x1f, {0, 0} },
887
888 { { }, 0x11, {0x000000, 4092 * 1024} },
889 { { }, 0x12, {0x000000, 4088 * 1024} },
890 { { }, 0x13, {0x000000, 4080 * 1024} },
891 { { }, 0x14, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
892 { { }, 0x15, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
893 { { }, 0x16, {0x000000, 4064 * 1024} },
894
895 { { }, 0x19, {0x001000, 4092 * 1024} },
896 { { }, 0x1a, {0x002000, 4088 * 1024} },
897 { { }, 0x1b, {0x040000, 4080 * 1024} },
898 { { }, 0x1c, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
899 { { }, 0x1d, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
900 { { }, 0x1e, {0x080000, 4064 * 1024} },
901};
902
903static struct status_register_layout gd25q32_sr1 = {
904 /* TODO: map second status register */
905 .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7
906};
907
908static struct wp_range_descriptor gd25q128_cmp0_ranges[] = {
909 /* none, bp4 and bp3 => don't care, others = 0 */
910 { { .tb = 0 }, 0x00, {0, 0} },
911 { { .tb = 0 }, 0x08, {0, 0} },
912 { { .tb = 0 }, 0x10, {0, 0} },
913 { { .tb = 0 }, 0x18, {0, 0} },
914
915 { { .tb = 0 }, 0x01, {0xfc0000, 256 * 1024} },
916 { { .tb = 0 }, 0x02, {0xf80000, 512 * 1024} },
917 { { .tb = 0 }, 0x03, {0xf00000, 1024 * 1024} },
918 { { .tb = 0 }, 0x04, {0xe00000, 2048 * 1024} },
919 { { .tb = 0 }, 0x05, {0xc00000, 4096 * 1024} },
920 { { .tb = 0 }, 0x06, {0x800000, 8192 * 1024} },
921
922 { { .tb = 0 }, 0x09, {0x000000, 256 * 1024} },
923 { { .tb = 0 }, 0x0a, {0x000000, 512 * 1024} },
924 { { .tb = 0 }, 0x0b, {0x000000, 1024 * 1024} },
925 { { .tb = 0 }, 0x0c, {0x000000, 2048 * 1024} },
926 { { .tb = 0 }, 0x0d, {0x000000, 4096 * 1024} },
927 { { .tb = 0 }, 0x0e, {0x000000, 8192 * 1024} },
928
929 /* all, bp4 and bp3 => don't care, others = 1 */
930 { { .tb = 0 }, 0x07, {0x000000, 16384 * 1024} },
931 { { .tb = 0 }, 0x0f, {0x000000, 16384 * 1024} },
932 { { .tb = 0 }, 0x17, {0x000000, 16384 * 1024} },
933 { { .tb = 0 }, 0x1f, {0x000000, 16384 * 1024} },
934
935 { { .tb = 0 }, 0x11, {0xfff000, 4 * 1024} },
936 { { .tb = 0 }, 0x12, {0xffe000, 8 * 1024} },
937 { { .tb = 0 }, 0x13, {0xffc000, 16 * 1024} },
938 { { .tb = 0 }, 0x14, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
939 { { .tb = 0 }, 0x15, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
940
941 { { .tb = 0 }, 0x19, {0x000000, 4 * 1024} },
942 { { .tb = 0 }, 0x1a, {0x000000, 8 * 1024} },
943 { { .tb = 0 }, 0x1b, {0x000000, 16 * 1024} },
944 { { .tb = 0 }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
945 { { .tb = 0 }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
946 { { .tb = 0 }, 0x1e, {0x000000, 32 * 1024} },
947};
948
949static struct wp_range_descriptor gd25q128_cmp1_ranges[] = {
950 /* none, bp4 and bp3 => don't care, others = 0 */
951 { { .tb = 1 }, 0x00, {0x000000, 16384 * 1024} },
952 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
953 { { .tb = 1 }, 0x10, {0x000000, 16384 * 1024} },
954 { { .tb = 1 }, 0x18, {0x000000, 16384 * 1024} },
955
956 { { .tb = 1 }, 0x01, {0x000000, 16128 * 1024} },
957 { { .tb = 1 }, 0x02, {0x000000, 15872 * 1024} },
958 { { .tb = 1 }, 0x03, {0x000000, 15360 * 1024} },
959 { { .tb = 1 }, 0x04, {0x000000, 14336 * 1024} },
960 { { .tb = 1 }, 0x05, {0x000000, 12288 * 1024} },
961 { { .tb = 1 }, 0x06, {0x000000, 8192 * 1024} },
962
963 { { .tb = 1 }, 0x09, {0x000000, 16128 * 1024} },
964 { { .tb = 1 }, 0x0a, {0x000000, 15872 * 1024} },
965 { { .tb = 1 }, 0x0b, {0x000000, 15360 * 1024} },
966 { { .tb = 1 }, 0x0c, {0x000000, 14336 * 1024} },
967 { { .tb = 1 }, 0x0d, {0x000000, 12288 * 1024} },
968 { { .tb = 1 }, 0x0e, {0x000000, 8192 * 1024} },
969
970 /* none, bp4 and bp3 => don't care, others = 1 */
971 { { .tb = 1 }, 0x07, {0x000000, 16384 * 1024} },
972 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
973 { { .tb = 1 }, 0x0f, {0x000000, 16384 * 1024} },
974 { { .tb = 1 }, 0x17, {0x000000, 16384 * 1024} },
975 { { .tb = 1 }, 0x1f, {0x000000, 16384 * 1024} },
976
977 { { .tb = 1 }, 0x11, {0x000000, 16380 * 1024} },
978 { { .tb = 1 }, 0x12, {0x000000, 16376 * 1024} },
979 { { .tb = 1 }, 0x13, {0x000000, 16368 * 1024} },
980 { { .tb = 1 }, 0x14, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
981 { { .tb = 1 }, 0x15, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
982
983 { { .tb = 1 }, 0x19, {0x001000, 16380 * 1024} },
984 { { .tb = 1 }, 0x1a, {0x002000, 16376 * 1024} },
985 { { .tb = 1 }, 0x1b, {0x004000, 16368 * 1024} },
986 { { .tb = 1 }, 0x1c, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
987 { { .tb = 1 }, 0x1d, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
988 { { .tb = 1 }, 0x1e, {0x008000, 16352 * 1024} },
989};
990
991static struct status_register_layout gd25q128_sr1 = {
992 /* TODO: map second and third status registers */
993 .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7
994};
995
996/* FIXME: MX25L6406 has same ID as MX25L6405D */
997static struct wp_range_descriptor mx25l6406e_ranges[] = {
998 { { }, 0, {0, 0} }, /* none */
999 { { }, 0x1, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
1000 { { }, 0x2, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
1001 { { }, 0x3, {0x7a0000, 64 * 8 * 1024} }, /* blocks 120-127 */
1002 { { }, 0x4, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
1003 { { }, 0x5, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
1004 { { }, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
1005
1006 { { }, 0x7, {0x000000, 64 * 128 * 1024} }, /* all */
1007 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
1008 { { }, 0x9, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
1009 { { }, 0xa, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
1010 { { }, 0xb, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
1011 { { }, 0xc, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
1012 { { }, 0xd, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
1013 { { }, 0xe, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
1014 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
1015};
1016
1017static struct status_register_layout mx25l6406e_sr1 = {
1018 .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7
1019};
1020
1021static struct wp_range_descriptor mx25l6495f_tb0_ranges[] = {
1022 { { }, 0, {0, 0} }, /* none */
1023 { { }, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
1024 { { }, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
1025 { { }, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
1026
1027 { { }, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
1028 { { }, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
1029 { { }, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
1030 { { }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
1031 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
1032 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
1033 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
1034 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
1035 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
1036 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
1037 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
1038 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
1039};
1040
1041static struct wp_range_descriptor mx25l6495f_tb1_ranges[] = {
1042 { { }, 0, {0, 0} }, /* none */
1043 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
1044 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
1045 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
1046 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
1047 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
1048 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
1049 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
1050 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
1051 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
1052 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
1053 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
1054 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
1055 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
1056 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
1057 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
1058};
1059
1060static struct status_register_layout mx25l6495f_sr1 = {
1061 .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7
1062};
1063
1064static struct wp_range_descriptor mx25l25635f_tb0_ranges[] = {
1065 { { }, 0, {0, 0} }, /* none */
1066 { { }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* block 511 */
1067 { { }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* blocks 510-511 */
1068 { { }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* blocks 508-511 */
1069 { { }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* blocks 504-511 */
1070 { { }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* blocks 496-511 */
1071 { { }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* blocks 480-511 */
1072 { { }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* blocks 448-511 */
1073 { { }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* blocks 384-511 */
1074 { { }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* blocks 256-511 */
1075 { { }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* all */
1076 { { }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* all */
1077 { { }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* all */
1078 { { }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* all */
1079 { { }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* all */
1080 { { }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* all */
1081};
1082
1083static struct wp_range_descriptor mx25l25635f_tb1_ranges[] = {
1084 { { }, 0, {0, 0} }, /* none */
1085 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
1086 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
1087 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
1088 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
1089 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
1090 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
1091 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
1092 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
1093 { { }, 0x9, {0x000000, 64 * 256 * 1024} }, /* blocks 0-255 */
1094 { { }, 0xa, {0x000000, 64 * 512 * 1024} }, /* all */
1095 { { }, 0xb, {0x000000, 64 * 512 * 1024} }, /* all */
1096 { { }, 0xc, {0x000000, 64 * 512 * 1024} }, /* all */
1097 { { }, 0xd, {0x000000, 64 * 512 * 1024} }, /* all */
1098 { { }, 0xe, {0x000000, 64 * 512 * 1024} }, /* all */
1099 { { }, 0xf, {0x000000, 64 * 512 * 1024} }, /* all */
1100};
1101
1102static struct status_register_layout mx25l25635f_sr1 = {
1103 .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7
1104};
1105
1106static struct wp_range_descriptor s25fs128s_ranges[] = {
1107 { { .tb = 1 }, 0, {0, 0} }, /* none */
1108 { { .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* lower 64th */
1109 { { .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* lower 32nd */
1110 { { .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* lower 16th */
1111 { { .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* lower 8th */
1112 { { .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* lower 4th */
1113 { { .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* lower half */
1114 { { .tb = 1 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
1115
1116 { { .tb = 0 }, 0, {0, 0} }, /* none */
1117 { { .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* upper 64th */
1118 { { .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* upper 32nd */
1119 { { .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* upper 16th */
1120 { { .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* upper 8th */
1121 { { .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* upper 4th */
1122 { { .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* upper half */
1123 { { .tb = 0 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
1124};
1125
1126static struct status_register_layout s25fs128s_sr1 = {
1127 .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7
1128};
1129
1130
1131static struct wp_range_descriptor s25fl256s_ranges[] = {
1132 { { .tb = 1 }, 0, {0, 0} }, /* none */
1133 { { .tb = 1 }, 0x1, {0x000000, 512 * 1024} }, /* lower 64th */
1134 { { .tb = 1 }, 0x2, {0x000000, 1024 * 1024} }, /* lower 32nd */
1135 { { .tb = 1 }, 0x3, {0x000000, 2048 * 1024} }, /* lower 16th */
1136 { { .tb = 1 }, 0x4, {0x000000, 4096 * 1024} }, /* lower 8th */
1137 { { .tb = 1 }, 0x5, {0x000000, 8192 * 1024} }, /* lower 4th */
1138 { { .tb = 1 }, 0x6, {0x000000, 16384 * 1024} }, /* lower half */
1139 { { .tb = 1 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
1140
1141 { { .tb = 0 }, 0, {0, 0} }, /* none */
1142 { { .tb = 0 }, 0x1, {0x1f80000, 512 * 1024} }, /* upper 64th */
1143 { { .tb = 0 }, 0x2, {0x1f00000, 1024 * 1024} }, /* upper 32nd */
1144 { { .tb = 0 }, 0x3, {0x1e00000, 2048 * 1024} }, /* upper 16th */
1145 { { .tb = 0 }, 0x4, {0x1c00000, 4096 * 1024} }, /* upper 8th */
1146 { { .tb = 0 }, 0x5, {0x1800000, 8192 * 1024} }, /* upper 4th */
1147 { { .tb = 0 }, 0x6, {0x1000000, 16384 * 1024} }, /* upper half */
1148 { { .tb = 0 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
1149};
1150
1151static struct status_register_layout s25fl256s_sr1 = {
1152 .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7
1153};
1154
1155
Nikolai Artemiev9daffd92021-04-06 16:54:46 +10001156static int range_table(const struct flashctx *flash,
1157 struct wp_range_descriptor **descrs,
1158 int *num_entries);
1159
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +11001160struct wp *get_wp_for_flashchip(const struct flashchip *chip) {
1161 // FIXME: The .wp field should be deleted from from struct flashchip
1162 // completly, but linux_mtd and cros_ec still assign their own values
1163 // to it. When they are cleaned up we can delete this.
1164 if(chip->wp) return chip->wp;
1165
1166 switch (chip->manufacture_id) {
1167 case WINBOND_NEX_ID:
1168 switch(chip->model_id) {
1169 case WINBOND_NEX_W25X10:
1170 case WINBOND_NEX_W25X20:
1171 case WINBOND_NEX_W25X40:
1172 case WINBOND_NEX_W25X80:
1173 case WINBOND_NEX_W25Q128_V_M:
1174 return &wp_w25;
1175 case WINBOND_NEX_W25Q80_V:
1176 case WINBOND_NEX_W25Q16_V:
1177 case WINBOND_NEX_W25Q32_V:
1178 case WINBOND_NEX_W25Q32_W:
1179 case WINBOND_NEX_W25Q32JW:
1180 case WINBOND_NEX_W25Q64_V:
1181 case WINBOND_NEX_W25Q64_W:
1182 // W25Q64JW does not have a range table entry, but the flashchip
1183 // set .wp to wp_25q, so keep it here until the issue is resolved
1184 case WINBOND_NEX_W25Q64JW:
1185 case WINBOND_NEX_W25Q128_DTR:
1186 case WINBOND_NEX_W25Q128_V:
1187 case WINBOND_NEX_W25Q128_W:
1188 return &wp_w25q;
1189 case WINBOND_NEX_W25Q256_V:
1190 case WINBOND_NEX_W25Q256JV_M:
1191 return &wp_w25q_large;
1192 }
1193 break;
1194 case EON_ID_NOPREFIX:
1195 switch (chip->model_id) {
1196 case EON_EN25F40:
1197 case EON_EN25Q40:
1198 case EON_EN25Q80:
1199 case EON_EN25Q32:
1200 case EON_EN25Q64:
1201 case EON_EN25Q128:
1202 case EON_EN25QH128:
1203 case EON_EN25S64:
1204 return &wp_w25;
1205 }
1206 break;
1207 case MACRONIX_ID:
1208 switch (chip->model_id) {
1209 case MACRONIX_MX25L1005:
1210 case MACRONIX_MX25L2005:
1211 case MACRONIX_MX25L4005:
1212 case MACRONIX_MX25L8005:
1213 case MACRONIX_MX25L1605:
1214 case MACRONIX_MX25L3205:
1215 case MACRONIX_MX25U3235E:
1216 case MACRONIX_MX25U6435E:
1217 return &wp_w25;
1218 case MACRONIX_MX25U12835E:
1219 return &wp_w25q_large;
1220 case MACRONIX_MX25L6405:
1221 case MACRONIX_MX25L6495F:
1222 case MACRONIX_MX25L25635F:
1223 return &wp_generic;
1224 }
1225 break;
1226 case ST_ID:
1227 switch(chip->model_id) {
1228 case ST_N25Q064__1E:
1229 case ST_N25Q064__3E:
1230 return &wp_w25;
1231 }
1232 break;
1233 case GIGADEVICE_ID:
1234 switch(chip->model_id) {
1235 case GIGADEVICE_GD25LQ32:
1236 // GD25Q40 does not have a .wp field in flashchips.c, but
1237 // it is in the w25 range table function, so note it here
1238 // until the issue is resolved:
1239 // case GIGADEVICE_GD25Q40:
1240 case GIGADEVICE_GD25Q64:
1241 case GIGADEVICE_GD25LQ64:
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +11001242 case GIGADEVICE_GD25Q128:
1243 return &wp_w25;
1244 case GIGADEVICE_GD25Q256D:
1245 return &wp_w25q_large;
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +11001246 case GIGADEVICE_GD25LQ128CD:
1247 case GIGADEVICE_GD25Q32:
1248 return &wp_generic;
1249 }
1250 break;
1251 case AMIC_ID_NOPREFIX:
1252 switch(chip->model_id) {
1253 case AMIC_A25L040:
1254 return &wp_w25;
1255 }
1256 break;
1257 case ATMEL_ID:
1258 switch(chip->model_id) {
1259 case ATMEL_AT25SF128A:
1260 case ATMEL_AT25SL128A:
1261 return &wp_w25q;
1262 }
1263 break;
1264 case PROGMANUF_ID:
1265 switch(chip->model_id) {
1266 case PROGDEV_ID:
1267 return &wp_w25;
1268 }
1269 break;
1270 case SPANSION_ID:
1271 switch (chip->model_id) {
1272 case SPANSION_S25FS128S_L:
1273 case SPANSION_S25FS128S_S:
1274 case SPANSION_S25FL256S_UL:
1275 case SPANSION_S25FL256S_US:
1276 // SPANSION_S25FL128S_UL does not have a range table entry,
1277 // but its flashchip set .wp to wp_generic, so keep it here
1278 // until the issue resolved
1279 case SPANSION_S25FL128S_UL:
1280 // SPANSION_S25FL128S_US does not have a range table entry,
1281 // but its flashchip set .wp to wp_generic, so keep it here
1282 // until the issue resolved
1283 case SPANSION_S25FL128S_US:
1284 return &wp_generic;
1285 }
1286 break;
1287 }
1288
1289
1290 return NULL;
1291}
1292
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001293/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001294static uint8_t w25q_read_status_register_2(const struct flashctx *flash)
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001295{
1296 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 };
1297 unsigned char readarr[2];
1298 int ret;
1299
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +11001300 if (flash->chip->read_status) {
1301 msg_cdbg("RDSR2 failed! cmd=0x35 unimpl for opaque chips\n");
1302 return 0;
1303 }
1304
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001305 /* Read Status Register */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001306 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001307 if (ret) {
1308 /*
1309 * FIXME: make this a benign failure for now in case we are
1310 * unable to execute the opcode
1311 */
1312 msg_cdbg("RDSR2 failed!\n");
1313 readarr[0] = 0x00;
1314 }
1315
1316 return readarr[0];
1317}
1318
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001319/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Edward O'Callaghandf43e902020-11-13 23:08:26 +11001320static uint8_t mx25l_read_config_register(const struct flashctx *flash)
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001321{
1322 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x15 };
1323 unsigned char readarr[2]; /* leave room for dummy byte */
1324 int ret;
1325
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +11001326 if (flash->chip->read_status) {
1327 msg_cdbg("RDCR failed! cmd=0x15 unimpl for opaque chips\n");
1328 return 0;
1329 }
1330
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001331 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
1332 if (ret) {
1333 msg_cdbg("RDCR failed!\n");
1334 readarr[0] = 0x00;
1335 }
1336
1337 return readarr[0];
1338}
1339
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001340int w25_range_to_status(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001341 unsigned int start, unsigned int len,
1342 struct w25q_status *status)
1343{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001344 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001345 int i, range_found = 0;
1346 int num_entries;
1347
Nikolai Artemiev9daffd92021-04-06 16:54:46 +10001348 if (range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001349 return -1;
1350
David Hendricksf7924d12010-06-10 21:26:44 -07001351 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001352 struct wp_range *r = &descrs[i].range;
David Hendricksf7924d12010-06-10 21:26:44 -07001353
1354 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1355 start, len, r->start, r->len);
1356 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001357 status->bp0 = descrs[i].bp & 1;
1358 status->bp1 = descrs[i].bp >> 1;
1359 status->bp2 = descrs[i].bp >> 2;
1360 status->tb = descrs[i].m.tb;
1361 status->sec = descrs[i].m.sec;
David Hendricksf7924d12010-06-10 21:26:44 -07001362
1363 range_found = 1;
1364 break;
1365 }
1366 }
1367
1368 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001369 msg_cerr("%s: matching range not found\n", __func__);
David Hendricksf7924d12010-06-10 21:26:44 -07001370 return -1;
1371 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001372
David Hendricksd494b0a2010-08-16 16:28:50 -07001373 return 0;
1374}
1375
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001376int w25_status_to_range(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001377 const struct w25q_status *status,
1378 unsigned int *start, unsigned int *len)
1379{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001380 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001381 int i, status_found = 0;
1382 int num_entries;
1383
Nikolai Artemiev9daffd92021-04-06 16:54:46 +10001384 if (range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001385 return -1;
1386
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001387 for (i = 0; i < num_entries; i++) {
1388 int bp;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001389 int table_bp, table_tb, table_sec;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001390
1391 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
1392 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001393 bp, descrs[i].bp,
1394 status->tb, descrs[i].m.tb,
1395 status->sec, descrs[i].m.sec);
1396 table_bp = descrs[i].bp;
1397 table_tb = descrs[i].m.tb;
1398 table_sec = descrs[i].m.sec;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001399 if ((bp == table_bp || table_bp == X) &&
1400 (status->tb == table_tb || table_tb == X) &&
1401 (status->sec == table_sec || table_sec == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001402 *start = descrs[i].range.start;
1403 *len = descrs[i].range.len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001404
1405 status_found = 1;
1406 break;
1407 }
1408 }
1409
1410 if (!status_found) {
1411 msg_cerr("matching status not found\n");
1412 return -1;
1413 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001414
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001415 return 0;
1416}
1417
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001418/* Given a [start, len], this function calls w25_range_to_status() to convert
1419 * it to flash-chip-specific range bits, then sets into status register.
1420 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001421static int w25_set_range(const struct flashctx *flash,
David Hendricksd494b0a2010-08-16 16:28:50 -07001422 unsigned int start, unsigned int len)
1423{
1424 struct w25q_status status;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001425 int tmp = 0;
1426 int expected = 0;
David Hendricksd494b0a2010-08-16 16:28:50 -07001427
1428 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001429 tmp = spi_read_status_register(flash);
David Hendricksd494b0a2010-08-16 16:28:50 -07001430 memcpy(&status, &tmp, 1);
1431 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1432
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001433 if (w25_range_to_status(flash, start, len, &status))
1434 return -1;
David Hendricksf7924d12010-06-10 21:26:44 -07001435
1436 msg_cdbg("status.busy: %x\n", status.busy);
1437 msg_cdbg("status.wel: %x\n", status.wel);
1438 msg_cdbg("status.bp0: %x\n", status.bp0);
1439 msg_cdbg("status.bp1: %x\n", status.bp1);
1440 msg_cdbg("status.bp2: %x\n", status.bp2);
1441 msg_cdbg("status.tb: %x\n", status.tb);
1442 msg_cdbg("status.sec: %x\n", status.sec);
1443 msg_cdbg("status.srp0: %x\n", status.srp0);
1444
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001445 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001446 spi_write_status_register(flash, expected);
David Hendricksf7924d12010-06-10 21:26:44 -07001447
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001448 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001449 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001450 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) {
David Hendricksc801adb2010-12-09 16:58:56 -08001451 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001452 expected, tmp);
1453 return 1;
1454 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001455
1456 return 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001457}
1458
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001459/* Print out the current status register value with human-readable text. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001460static int w25_wp_status(const struct flashctx *flash)
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001461{
1462 struct w25q_status status;
1463 int tmp;
David Hendricksce8ded32010-10-08 11:23:38 -07001464 unsigned int start, len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001465 int ret = 0;
1466
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001467 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001468 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001469 memcpy(&status, &tmp, 1);
1470 msg_cinfo("WP: status: 0x%02x\n", tmp);
1471 msg_cinfo("WP: status.srp0: %x\n", status.srp0);
1472 msg_cinfo("WP: write protect is %s.\n",
1473 status.srp0 ? "enabled" : "disabled");
1474
1475 msg_cinfo("WP: write protect range: ");
1476 if (w25_status_to_range(flash, &status, &start, &len)) {
1477 msg_cinfo("(cannot resolve the range)\n");
1478 ret = -1;
1479 } else {
1480 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1481 }
1482
1483 return ret;
1484}
1485
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001486static int w25q_large_range_to_status(const struct flashctx *flash,
1487 unsigned int start, unsigned int len,
1488 struct w25q_status_large *status)
1489{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001490 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001491 int i, range_found = 0;
1492 int num_entries;
1493
Nikolai Artemiev9daffd92021-04-06 16:54:46 +10001494 if (range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001495 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001496
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001497 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001498 struct wp_range *r = &descrs[i].range;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001499
1500 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1501 start, len, r->start, r->len);
1502 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001503 status->bp0 = descrs[i].bp & 1;
1504 status->bp1 = descrs[i].bp >> 1;
1505 status->bp2 = descrs[i].bp >> 2;
1506 status->bp3 = descrs[i].bp >> 3;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001507 /*
1508 * For MX25U12835E chip, Top/Bottom (T/B) bit is not
1509 * part of status register and in that bit position is
1510 * Quad Enable (QE)
1511 */
1512 if (flash->chip->manufacture_id != MACRONIX_ID ||
1513 flash->chip->model_id != MACRONIX_MX25U12835E)
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001514 status->tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001515
1516 range_found = 1;
1517 break;
1518 }
1519 }
1520
1521 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001522 msg_cerr("%s: matching range not found\n", __func__);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001523 return -1;
1524 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001525
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001526 return 0;
1527}
1528
1529static int w25_large_status_to_range(const struct flashctx *flash,
1530 const struct w25q_status_large *status,
1531 unsigned int *start, unsigned int *len)
1532{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001533 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001534 int i, status_found = 0;
1535 int num_entries;
1536
Nikolai Artemiev9daffd92021-04-06 16:54:46 +10001537 if (range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001538 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001539
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001540 for (i = 0; i < num_entries; i++) {
1541 int bp;
1542 int table_bp, table_tb;
1543
1544 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) |
1545 (status->bp3 << 3);
1546 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001547 bp, descrs[i].bp,
1548 status->tb, descrs[i].m.tb);
1549 table_bp = descrs[i].bp;
1550 table_tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001551 if ((bp == table_bp || table_bp == X) &&
1552 (status->tb == table_tb || table_tb == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001553 *start = descrs[i].range.start;
1554 *len = descrs[i].range.len;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001555
1556 status_found = 1;
1557 break;
1558 }
1559 }
1560
1561 if (!status_found) {
1562 msg_cerr("matching status not found\n");
1563 return -1;
1564 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001565
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001566 return 0;
1567}
1568
1569/* Given a [start, len], this function calls w25_range_to_status() to convert
1570 * it to flash-chip-specific range bits, then sets into status register.
1571 * Returns 0 if successful, -1 on error, and 1 if reading back was different.
1572 */
1573static int w25q_large_set_range(const struct flashctx *flash,
1574 unsigned int start, unsigned int len)
1575{
1576 struct w25q_status_large status;
1577 int tmp;
1578 int expected = 0;
1579
1580 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001581 tmp = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001582 memcpy(&status, &tmp, 1);
1583 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1584
1585 if (w25q_large_range_to_status(flash, start, len, &status))
1586 return -1;
1587
1588 msg_cdbg("status.busy: %x\n", status.busy);
1589 msg_cdbg("status.wel: %x\n", status.wel);
1590 msg_cdbg("status.bp0: %x\n", status.bp0);
1591 msg_cdbg("status.bp1: %x\n", status.bp1);
1592 msg_cdbg("status.bp2: %x\n", status.bp2);
1593 msg_cdbg("status.bp3: %x\n", status.bp3);
1594 msg_cdbg("status.tb: %x\n", status.tb);
1595 msg_cdbg("status.srp0: %x\n", status.srp0);
1596
1597 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001598 spi_write_status_register(flash, expected);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001599
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001600 tmp = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001601 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001602 if ((tmp & MASK_WP_AREA_LARGE) != (expected & MASK_WP_AREA_LARGE)) {
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001603 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
1604 expected, tmp);
1605 return 1;
1606 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001607
1608 return 0;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001609}
1610
1611static int w25q_large_wp_status(const struct flashctx *flash)
1612{
1613 struct w25q_status_large sr1;
1614 struct w25q_status_2 sr2;
1615 uint8_t tmp[2];
1616 unsigned int start, len;
1617 int ret = 0;
1618
1619 memset(&sr1, 0, sizeof(sr1));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001620 tmp[0] = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001621 memcpy(&sr1, &tmp[0], 1);
1622
1623 memset(&sr2, 0, sizeof(sr2));
1624 tmp[1] = w25q_read_status_register_2(flash);
1625 memcpy(&sr2, &tmp[1], 1);
1626
1627 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
1628 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1629 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1630 msg_cinfo("WP: write protect is %s.\n",
1631 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1632
1633 msg_cinfo("WP: write protect range: ");
1634 if (w25_large_status_to_range(flash, &sr1, &start, &len)) {
1635 msg_cinfo("(cannot resolve the range)\n");
1636 ret = -1;
1637 } else {
1638 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1639 }
1640
1641 return ret;
1642}
1643
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001644/* Set/clear the SRP0 bit in the status register. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001645static int w25_set_srp0(const struct flashctx *flash, int enable)
David Hendricksf7924d12010-06-10 21:26:44 -07001646{
1647 struct w25q_status status;
1648 int tmp = 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001649 int expected = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001650
1651 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001652 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001653 /* FIXME: this is NOT endian-free copy. */
David Hendricksf7924d12010-06-10 21:26:44 -07001654 memcpy(&status, &tmp, 1);
1655 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1656
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001657 status.srp0 = enable ? 1 : 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001658 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001659 spi_write_status_register(flash, expected);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001660
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001661 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001662 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
1663 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
1664 return 1;
David Hendricksf7924d12010-06-10 21:26:44 -07001665
1666 return 0;
1667}
1668
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001669static int w25_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001670 enum wp_mode wp_mode)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001671{
1672 int ret;
1673
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001674 if (wp_mode != WP_MODE_HARDWARE) {
David Hendricks1c09f802012-10-03 11:03:48 -07001675 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
1676 return 1;
1677 }
1678
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001679 ret = w25_set_srp0(flash, 1);
David Hendricksc801adb2010-12-09 16:58:56 -08001680 if (ret)
1681 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001682 return ret;
1683}
1684
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001685static int w25_disable_writeprotect(const struct flashctx *flash)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001686{
1687 int ret;
1688
1689 ret = w25_set_srp0(flash, 0);
David Hendricksc801adb2010-12-09 16:58:56 -08001690 if (ret)
1691 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001692
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001693 return ret;
1694}
1695
Nikolai Artemiev3b666da2021-04-06 16:59:01 +10001696static int list_ranges(const struct flashctx *flash)
David Hendricks0f7f5382011-02-11 18:12:31 -08001697{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001698 struct wp_range_descriptor *descrs;
David Hendricks0f7f5382011-02-11 18:12:31 -08001699 int i, num_entries;
1700
Nikolai Artemiev9daffd92021-04-06 16:54:46 +10001701 if (range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001702 return -1;
1703
David Hendricks0f7f5382011-02-11 18:12:31 -08001704 for (i = 0; i < num_entries; i++) {
1705 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001706 descrs[i].range.start,
1707 descrs[i].range.len);
David Hendricks0f7f5382011-02-11 18:12:31 -08001708 }
1709
1710 return 0;
1711}
1712
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001713static int w25q_wp_status(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001714{
1715 struct w25q_status sr1;
1716 struct w25q_status_2 sr2;
David Hendricksf1bd8802012-10-30 11:37:57 -07001717 uint8_t tmp[2];
David Hendricks1c09f802012-10-03 11:03:48 -07001718 unsigned int start, len;
1719 int ret = 0;
1720
1721 memset(&sr1, 0, sizeof(sr1));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001722 tmp[0] = spi_read_status_register(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001723 memcpy(&sr1, &tmp[0], 1);
David Hendricks1c09f802012-10-03 11:03:48 -07001724
David Hendricksf1bd8802012-10-30 11:37:57 -07001725 memset(&sr2, 0, sizeof(sr2));
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001726 tmp[1] = w25q_read_status_register_2(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001727 memcpy(&sr2, &tmp[1], 1);
1728
1729 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
David Hendricks1c09f802012-10-03 11:03:48 -07001730 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1731 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1732 msg_cinfo("WP: write protect is %s.\n",
1733 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1734
1735 msg_cinfo("WP: write protect range: ");
1736 if (w25_status_to_range(flash, &sr1, &start, &len)) {
1737 msg_cinfo("(cannot resolve the range)\n");
1738 ret = -1;
1739 } else {
1740 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1741 }
1742
1743 return ret;
1744}
1745
1746/*
1747 * W25Q adds an optional byte to the standard WRSR opcode. If /CS is
1748 * de-asserted after the first byte, then it acts like a JEDEC-standard
1749 * WRSR command. if /CS is asserted, then the next data byte is written
1750 * into status register 2.
1751 */
1752#define W25Q_WRSR_OUTSIZE 0x03
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001753static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2)
David Hendricks1c09f802012-10-03 11:03:48 -07001754{
1755 int result;
1756 struct spi_command cmds[] = {
1757 {
1758 /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
1759 .writecnt = JEDEC_WREN_OUTSIZE,
1760 .writearr = (const unsigned char[]){ JEDEC_WREN },
1761 .readcnt = 0,
1762 .readarr = NULL,
1763 }, {
1764 .writecnt = W25Q_WRSR_OUTSIZE,
1765 .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 },
1766 .readcnt = 0,
1767 .readarr = NULL,
1768 }, {
1769 .writecnt = 0,
1770 .writearr = NULL,
1771 .readcnt = 0,
1772 .readarr = NULL,
1773 }};
1774
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001775 result = spi_send_multicommand(flash, cmds);
David Hendricks1c09f802012-10-03 11:03:48 -07001776 if (result) {
1777 msg_cerr("%s failed during command execution\n",
1778 __func__);
1779 }
1780
1781 /* WRSR performs a self-timed erase before the changes take effect. */
David Hendricks60824042014-12-11 17:22:06 -08001782 programmer_delay(100 * 1000);
David Hendricks1c09f802012-10-03 11:03:48 -07001783
1784 return result;
1785}
1786
1787/*
1788 * Set/clear the SRP1 bit in status register 2.
1789 * FIXME: make this more generic if other chips use the same SR2 layout
1790 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001791static int w25q_set_srp1(const struct flashctx *flash, int enable)
David Hendricks1c09f802012-10-03 11:03:48 -07001792{
1793 struct w25q_status sr1;
1794 struct w25q_status_2 sr2;
1795 uint8_t tmp, expected;
1796
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001797 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001798 memcpy(&sr1, &tmp, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001799 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001800 memcpy(&sr2, &tmp, 1);
1801
1802 msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp);
1803
1804 sr2.srp1 = enable ? 1 : 0;
1805
1806 memcpy(&expected, &sr2, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001807 w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2));
David Hendricks1c09f802012-10-03 11:03:48 -07001808
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001809 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001810 msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp);
1811 if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA))
1812 return 1;
1813
1814 return 0;
1815}
1816
1817enum wp_mode get_wp_mode(const char *mode_str)
1818{
1819 enum wp_mode wp_mode = WP_MODE_UNKNOWN;
1820
1821 if (!strcasecmp(mode_str, "hardware"))
1822 wp_mode = WP_MODE_HARDWARE;
1823 else if (!strcasecmp(mode_str, "power_cycle"))
1824 wp_mode = WP_MODE_POWER_CYCLE;
1825 else if (!strcasecmp(mode_str, "permanent"))
1826 wp_mode = WP_MODE_PERMANENT;
1827
1828 return wp_mode;
1829}
1830
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001831static int w25q_disable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001832 enum wp_mode wp_mode)
1833{
1834 int ret = 1;
David Hendricks1c09f802012-10-03 11:03:48 -07001835 struct w25q_status_2 sr2;
1836 uint8_t tmp;
1837
1838 switch (wp_mode) {
1839 case WP_MODE_HARDWARE:
1840 ret = w25_set_srp0(flash, 0);
1841 break;
1842 case WP_MODE_POWER_CYCLE:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001843 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001844 memcpy(&sr2, &tmp, 1);
1845 if (sr2.srp1) {
1846 msg_cerr("%s(): must disconnect power to disable "
1847 "write-protection\n", __func__);
1848 } else {
1849 ret = 0;
1850 }
1851 break;
1852 case WP_MODE_PERMANENT:
1853 msg_cerr("%s(): cannot disable permanent write-protection\n",
1854 __func__);
1855 break;
1856 default:
1857 msg_cerr("%s(): invalid mode specified\n", __func__);
1858 break;
1859 }
1860
1861 if (ret)
1862 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001863
David Hendricks1c09f802012-10-03 11:03:48 -07001864 return ret;
1865}
1866
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001867static int w25q_disable_writeprotect_default(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001868{
1869 return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE);
1870}
1871
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001872static int w25q_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001873 enum wp_mode wp_mode)
1874{
1875 int ret = 1;
1876 struct w25q_status sr1;
1877 struct w25q_status_2 sr2;
1878 uint8_t tmp;
1879
1880 switch (wp_mode) {
1881 case WP_MODE_HARDWARE:
1882 if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) {
1883 msg_cerr("%s(): cannot disable power cycle WP mode\n",
1884 __func__);
1885 break;
1886 }
1887
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001888 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001889 memcpy(&sr1, &tmp, 1);
1890 if (sr1.srp0)
1891 ret = 0;
1892 else
1893 ret = w25_set_srp0(flash, 1);
1894
1895 break;
1896 case WP_MODE_POWER_CYCLE:
1897 if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) {
1898 msg_cerr("%s(): cannot disable hardware WP mode\n",
1899 __func__);
1900 break;
1901 }
1902
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001903 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001904 memcpy(&sr2, &tmp, 1);
1905 if (sr2.srp1)
1906 ret = 0;
1907 else
1908 ret = w25q_set_srp1(flash, 1);
1909
1910 break;
1911 case WP_MODE_PERMANENT:
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001912 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001913 memcpy(&sr1, &tmp, 1);
1914 if (sr1.srp0 == 0) {
1915 ret = w25_set_srp0(flash, 1);
1916 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001917 msg_perr("%s(): cannot enable SRP0 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001918 "permanent WP\n", __func__);
1919 break;
1920 }
1921 }
1922
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001923 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001924 memcpy(&sr2, &tmp, 1);
1925 if (sr2.srp1 == 0) {
1926 ret = w25q_set_srp1(flash, 1);
1927 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001928 msg_perr("%s(): cannot enable SRP1 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001929 "permanent WP\n", __func__);
1930 break;
1931 }
1932 }
1933
1934 break;
David Hendricksf1bd8802012-10-30 11:37:57 -07001935 default:
1936 msg_perr("%s(): invalid mode %d\n", __func__, wp_mode);
1937 break;
David Hendricks1c09f802012-10-03 11:03:48 -07001938 }
1939
1940 if (ret)
1941 msg_cerr("%s(): error=%d.\n", __func__, ret);
1942 return ret;
1943}
1944
1945/* W25P, W25X, and many flash chips from various vendors */
David Hendricksf7924d12010-06-10 21:26:44 -07001946struct wp wp_w25 = {
Nikolai Artemiev3b666da2021-04-06 16:59:01 +10001947 .list_ranges = list_ranges,
David Hendricksf7924d12010-06-10 21:26:44 -07001948 .set_range = w25_set_range,
1949 .enable = w25_enable_writeprotect,
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001950 .disable = w25_disable_writeprotect,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001951 .wp_status = w25_wp_status,
David Hendricks1c09f802012-10-03 11:03:48 -07001952
1953};
1954
1955/* W25Q series has features such as a second status register and SFDP */
1956struct wp wp_w25q = {
Nikolai Artemiev3b666da2021-04-06 16:59:01 +10001957 .list_ranges = list_ranges,
David Hendricks1c09f802012-10-03 11:03:48 -07001958 .set_range = w25_set_range,
1959 .enable = w25q_enable_writeprotect,
1960 /*
1961 * By default, disable hardware write-protection. We may change
1962 * this later if we want to add fine-grained write-protect disable
1963 * as a command-line option.
1964 */
1965 .disable = w25q_disable_writeprotect_default,
1966 .wp_status = w25q_wp_status,
David Hendricksf7924d12010-06-10 21:26:44 -07001967};
David Hendrickse0512a72014-07-15 20:30:47 -07001968
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001969/* W25Q large series has 4 block-protect bits */
1970struct wp wp_w25q_large = {
Nikolai Artemiev3b666da2021-04-06 16:59:01 +10001971 .list_ranges = list_ranges,
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001972 .set_range = w25q_large_set_range,
1973 .enable = w25q_enable_writeprotect,
1974 /*
1975 * By default, disable hardware write-protection. We may change
1976 * this later if we want to add fine-grained write-protect disable
1977 * as a command-line option.
1978 */
1979 .disable = w25q_disable_writeprotect_default,
1980 .wp_status = w25q_large_wp_status,
1981};
1982
Nikolai Artemiev33b91062021-04-06 16:34:10 +10001983static int get_sr1_layout(
1984 const struct flashctx *flash, struct status_register_layout *sr1)
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10001985{
1986 switch (flash->chip->manufacture_id) {
1987 case GIGADEVICE_ID:
1988 switch(flash->chip->model_id) {
1989
1990 case GIGADEVICE_GD25Q32:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10001991 *sr1 = gd25q32_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10001992 return 0;
1993 case GIGADEVICE_GD25LQ128CD:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10001994 *sr1 = gd25q128_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10001995 return 0;
1996 }
1997 break;
1998 case MACRONIX_ID:
1999 switch (flash->chip->model_id) {
2000 case MACRONIX_MX25L6405:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002001 *sr1 = mx25l6406e_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002002 return 0;
2003 case MACRONIX_MX25L6495F:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002004 *sr1 = mx25l6495f_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002005 return 0;
2006 case MACRONIX_MX25L25635F:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002007 *sr1 = mx25l25635f_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002008 return 0;
2009 }
2010 break;
2011 case SPANSION_ID:
2012 switch (flash->chip->model_id) {
2013 case SPANSION_S25FS128S_L:
2014 case SPANSION_S25FS128S_S:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002015 *sr1 = s25fs128s_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002016 return 0;
2017 case SPANSION_S25FL256S_UL:
2018 case SPANSION_S25FL256S_US:
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002019 *sr1 = s25fl256s_sr1;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002020 return 0;
2021 }
2022 break;
2023 }
2024
2025 return 1;
2026}
2027
David Hendrickse0512a72014-07-15 20:30:47 -07002028/* Given a flash chip, this function returns its writeprotect info. */
Nikolai Artemiev9daffd92021-04-06 16:54:46 +10002029static int range_table(const struct flashctx *flash,
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002030 struct wp_range_descriptor **descrs,
David Hendrickse0512a72014-07-15 20:30:47 -07002031 int *num_entries)
2032{
David Hendrickse0512a72014-07-15 20:30:47 -07002033 *num_entries = 0;
2034
Patrick Georgif3fa2992017-02-02 16:24:44 +01002035 switch (flash->chip->manufacture_id) {
Nikolai Artemiev4b50b5a2021-04-06 16:52:34 +10002036 case AMIC_ID_NOPREFIX:
2037 switch(flash->chip->model_id) {
2038 case AMIC_A25L040:
2039 *descrs = a25l040_ranges;
2040 *num_entries = ARRAY_SIZE(a25l040_ranges);
2041 break;
2042 default:
2043 msg_cerr("%s() %d: AMIC flash chip mismatch"
2044 " (0x%04x), aborting\n", __func__, __LINE__,
2045 flash->chip->model_id);
2046 return -1;
2047 }
2048 break;
2049 case ATMEL_ID:
2050 switch(flash->chip->model_id) {
2051 case ATMEL_AT25SF128A:
2052 case ATMEL_AT25SL128A:
2053 if (w25q_read_status_register_2(flash) & (1 << 6)) {
2054 /* CMP == 1 */
2055 *descrs = w25rq128_cmp1_ranges;
2056 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
2057 } else {
2058 /* CMP == 0 */
2059 *descrs = w25rq128_cmp0_ranges;
2060 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
2061 }
2062 break;
2063 default:
2064 msg_cerr("%s() %d: Atmel flash chip mismatch"
2065 " (0x%04x), aborting\n", __func__, __LINE__,
2066 flash->chip->model_id);
2067 return -1;
2068 }
2069 break;
Nikolai Artemiev06afe3e2021-04-06 16:40:29 +10002070 case WINBOND_NEX_ID:
2071 switch(flash->chip->model_id) {
2072 case WINBOND_NEX_W25X10:
2073 *descrs = w25x10_ranges;
2074 *num_entries = ARRAY_SIZE(w25x10_ranges);
2075 break;
2076 case WINBOND_NEX_W25X20:
2077 *descrs = w25x20_ranges;
2078 *num_entries = ARRAY_SIZE(w25x20_ranges);
2079 break;
2080 case WINBOND_NEX_W25X40:
2081 *descrs = w25x40_ranges;
2082 *num_entries = ARRAY_SIZE(w25x40_ranges);
2083 break;
2084 case WINBOND_NEX_W25X80:
2085 *descrs = w25x80_ranges;
2086 *num_entries = ARRAY_SIZE(w25x80_ranges);
2087 break;
2088 case WINBOND_NEX_W25Q80_V:
2089 *descrs = w25q80_ranges;
2090 *num_entries = ARRAY_SIZE(w25q80_ranges);
2091 break;
2092 case WINBOND_NEX_W25Q16_V:
2093 *descrs = w25q16_ranges;
2094 *num_entries = ARRAY_SIZE(w25q16_ranges);
2095 break;
2096 case WINBOND_NEX_W25Q32_V:
2097 case WINBOND_NEX_W25Q32_W:
2098 case WINBOND_NEX_W25Q32JW:
2099 *descrs = w25q32_ranges;
2100 *num_entries = ARRAY_SIZE(w25q32_ranges);
2101 break;
2102 case WINBOND_NEX_W25Q64_V:
2103 case WINBOND_NEX_W25Q64_W:
2104 *descrs = w25q64_ranges;
2105 *num_entries = ARRAY_SIZE(w25q64_ranges);
2106 break;
2107 case WINBOND_NEX_W25Q128_DTR:
2108 case WINBOND_NEX_W25Q128_V_M:
2109 case WINBOND_NEX_W25Q128_V:
2110 case WINBOND_NEX_W25Q128_W:
2111 if (w25q_read_status_register_2(flash) & (1 << 6)) {
2112 /* CMP == 1 */
2113 *descrs = w25rq128_cmp1_ranges;
2114 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
2115 } else {
2116 /* CMP == 0 */
2117 *descrs = w25rq128_cmp0_ranges;
2118 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
2119 }
2120 break;
2121 case WINBOND_NEX_W25Q256_V:
2122 case WINBOND_NEX_W25Q256JV_M:
2123 if (w25q_read_status_register_2(flash) & (1 << 6)) {
2124 /* CMP == 1 */
2125 *descrs = w25rq256_cmp1_ranges;
2126 *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges);
2127 } else {
2128 /* CMP == 0 */
2129 *descrs = w25rq256_cmp0_ranges;
2130 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
2131 }
2132 break;
2133 default:
2134 msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
2135 ", aborting\n", __func__, __LINE__,
2136 flash->chip->model_id);
2137 return -1;
2138 }
2139 break;
2140
Nikolai Artemiev12a84fa2021-04-06 16:41:56 +10002141 case EON_ID_NOPREFIX:
2142 switch (flash->chip->model_id) {
2143 case EON_EN25F40:
2144 *descrs = en25f40_ranges;
2145 *num_entries = ARRAY_SIZE(en25f40_ranges);
2146 break;
2147 case EON_EN25Q40:
2148 *descrs = en25q40_ranges;
2149 *num_entries = ARRAY_SIZE(en25q40_ranges);
2150 break;
2151 case EON_EN25Q80:
2152 *descrs = en25q80_ranges;
2153 *num_entries = ARRAY_SIZE(en25q80_ranges);
2154 break;
2155 case EON_EN25Q32:
2156 *descrs = en25q32_ranges;
2157 *num_entries = ARRAY_SIZE(en25q32_ranges);
2158 break;
2159 case EON_EN25Q64:
2160 *descrs = en25q64_ranges;
2161 *num_entries = ARRAY_SIZE(en25q64_ranges);
2162 break;
2163 case EON_EN25Q128:
2164 *descrs = en25q128_ranges;
2165 *num_entries = ARRAY_SIZE(en25q128_ranges);
2166 break;
2167 case EON_EN25QH128:
2168 if (w25q_read_status_register_2(flash) & (1 << 6)) {
2169 /* CMP == 1 */
2170 *descrs = w25rq128_cmp1_ranges;
2171 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
2172 } else {
2173 /* CMP == 0 */
2174 *descrs = w25rq128_cmp0_ranges;
2175 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
2176 }
2177 break;
2178 case EON_EN25S64:
2179 *descrs = en25s64_ranges;
2180 *num_entries = ARRAY_SIZE(en25s64_ranges);
2181 break;
2182 default:
2183 msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
2184 ", aborting\n", __func__, __LINE__,
2185 flash->chip->model_id);
2186 return -1;
2187 }
2188 break;
2189
David Hendricksaf3944a2014-07-28 18:37:40 -07002190 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002191 switch(flash->chip->model_id) {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002192
David Hendricksaf3944a2014-07-28 18:37:40 -07002193 case GIGADEVICE_GD25Q32: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002194 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002195
David Hendricksaf3944a2014-07-28 18:37:40 -07002196 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002197 *descrs = &gd25q32_cmp0_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002198 *num_entries = ARRAY_SIZE(gd25q32_cmp0_ranges);
2199 } else { /* CMP == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002200 *descrs = &gd25q32_cmp1_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002201 *num_entries = ARRAY_SIZE(gd25q32_cmp1_ranges);
2202 }
2203
2204 break;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002205 }
Aaron Durbin6c957d72018-08-20 09:31:01 -06002206 case GIGADEVICE_GD25LQ128CD: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002207 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002208
2209 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002210 *descrs = &gd25q128_cmp0_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002211 *num_entries = ARRAY_SIZE(gd25q128_cmp0_ranges);
2212 } else { /* CMP == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002213 *descrs = &gd25q128_cmp1_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002214 *num_entries = ARRAY_SIZE(gd25q128_cmp1_ranges);
2215 }
2216
2217 break;
David Hendricksaf3944a2014-07-28 18:37:40 -07002218 }
Nikolai Artemievacada712021-04-06 16:50:04 +10002219 case GIGADEVICE_GD25LQ32:
2220 *descrs = w25q32_ranges;
2221 *num_entries = ARRAY_SIZE(w25q32_ranges);
2222 break;
2223 case GIGADEVICE_GD25Q40:
2224 if (w25q_read_status_register_2(flash) & (1 << 6)) {
2225 /* CMP == 1 */
2226 *descrs = gd25q40_cmp1_ranges;
2227 *num_entries = ARRAY_SIZE(gd25q40_cmp1_ranges);
2228 } else {
2229 *descrs = gd25q40_cmp0_ranges;
2230 *num_entries = ARRAY_SIZE(gd25q40_cmp0_ranges);
2231 }
2232 break;
2233 case GIGADEVICE_GD25Q64:
2234 case GIGADEVICE_GD25LQ64:
2235 *descrs = gd25q64_ranges;
2236 *num_entries = ARRAY_SIZE(gd25q64_ranges);
2237 break;
2238 case GIGADEVICE_GD25Q128:
2239 if (w25q_read_status_register_2(flash) & (1 << 6)) {
2240 /* CMP == 1 */
2241 *descrs = w25rq128_cmp1_ranges;
2242 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
2243 } else {
2244 /* CMP == 0 */
2245 *descrs = w25rq128_cmp0_ranges;
2246 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
2247 }
2248 break;
2249 case GIGADEVICE_GD25Q256D:
2250 *descrs = w25rq256_cmp0_ranges;
2251 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
2252 break;
David Hendricksaf3944a2014-07-28 18:37:40 -07002253 default:
2254 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
2255 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002256 flash->chip->model_id);
David Hendricksaf3944a2014-07-28 18:37:40 -07002257 return -1;
2258 }
2259 break;
David Hendricks83541d32014-07-15 20:58:21 -07002260 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002261 switch (flash->chip->model_id) {
David Hendricks83541d32014-07-15 20:58:21 -07002262 case MACRONIX_MX25L6405:
2263 /* FIXME: MX25L64* chips have mixed capabilities and
2264 share IDs */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002265 *descrs = &mx25l6406e_ranges[0];
David Hendricks83541d32014-07-15 20:58:21 -07002266 *num_entries = ARRAY_SIZE(mx25l6406e_ranges);
2267 break;
David Hendricksc3496092014-11-13 17:20:55 -08002268 case MACRONIX_MX25L6495F: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002269 uint8_t cr = mx25l_read_config_register(flash);
David Hendricksc3496092014-11-13 17:20:55 -08002270
David Hendricksc3496092014-11-13 17:20:55 -08002271 if (!(cr & (1 << 3))) { /* T/B == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002272 *descrs = &mx25l6495f_tb0_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002273 *num_entries = ARRAY_SIZE(mx25l6495f_tb0_ranges);
2274 } else { /* T/B == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002275 *descrs = &mx25l6495f_tb1_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002276 *num_entries = ARRAY_SIZE(mx25l6495f_tb1_ranges);
2277 }
2278 break;
2279 }
Vic Yang848bfd12018-03-23 10:24:07 -07002280 case MACRONIX_MX25L25635F: {
2281 uint8_t cr = mx25l_read_config_register(flash);
2282
Vic Yang848bfd12018-03-23 10:24:07 -07002283 if (!(cr & (1 << 3))) { /* T/B == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002284 *descrs = &mx25l25635f_tb0_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002285 *num_entries = ARRAY_SIZE(mx25l25635f_tb0_ranges);
2286 } else { /* T/B == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002287 *descrs = &mx25l25635f_tb1_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002288 *num_entries = ARRAY_SIZE(mx25l25635f_tb1_ranges);
2289 }
2290 break;
Nikolai Artemiev0e560ae2021-04-06 16:45:00 +10002291 }
2292 case MACRONIX_MX25L1005:
2293 *descrs = mx25l1005_ranges;
2294 *num_entries = ARRAY_SIZE(mx25l1005_ranges);
2295 break;
2296 case MACRONIX_MX25L2005:
2297 *descrs = mx25l2005_ranges;
2298 *num_entries = ARRAY_SIZE(mx25l2005_ranges);
2299 break;
2300 case MACRONIX_MX25L4005:
2301 *descrs = mx25l4005_ranges;
2302 *num_entries = ARRAY_SIZE(mx25l4005_ranges);
2303 break;
2304 case MACRONIX_MX25L8005:
2305 *descrs = mx25l8005_ranges;
2306 *num_entries = ARRAY_SIZE(mx25l8005_ranges);
2307 break;
2308 case MACRONIX_MX25L1605:
2309 /* FIXME: MX25L1605 and MX25L1605D have different write
2310 * protection capabilities, but share IDs */
2311 *descrs = mx25l1605d_ranges;
2312 *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
2313 break;
2314 case MACRONIX_MX25L3205:
2315 *descrs = mx25l3205d_ranges;
2316 *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
2317 break;
2318 case MACRONIX_MX25U3235E:
2319 *descrs = mx25u3235e_ranges;
2320 *num_entries = ARRAY_SIZE(mx25u3235e_ranges);
2321 break;
2322 case MACRONIX_MX25U6435E:
2323 *descrs = mx25u6435e_ranges;
2324 *num_entries = ARRAY_SIZE(mx25u6435e_ranges);
2325 break;
2326 case MACRONIX_MX25U12835E: {
2327 uint8_t cr = mx25l_read_config_register(flash);
2328 if (cr & MX25U12835E_TB) { /* T/B == 1 */
2329 *descrs = mx25u12835e_tb1_ranges;
2330 *num_entries = ARRAY_SIZE(mx25u12835e_tb1_ranges);
2331 } else { /* T/B == 0 */
2332 *descrs = mx25u12835e_tb0_ranges;
2333 *num_entries = ARRAY_SIZE(mx25u12835e_tb0_ranges);
2334 }
2335 }
2336 break;
David Hendricks83541d32014-07-15 20:58:21 -07002337 default:
2338 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
2339 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002340 flash->chip->model_id);
David Hendricks83541d32014-07-15 20:58:21 -07002341 return -1;
2342 }
2343 break;
Nikolai Artemiev158b3702021-04-06 16:46:06 +10002344 case ST_ID:
2345 switch(flash->chip->model_id) {
2346 case ST_N25Q064__1E:
2347 case ST_N25Q064__3E:
2348 *descrs = n25q064_ranges;
2349 *num_entries = ARRAY_SIZE(n25q064_ranges);
2350 break;
2351 default:
2352 msg_cerr("%s() %d: Micron flash chip mismatch"
2353 " (0x%04x), aborting\n", __func__, __LINE__,
2354 flash->chip->model_id);
2355 return -1;
2356 }
2357 break;
David Hendricksa9884852014-12-11 15:31:12 -08002358 case SPANSION_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002359 switch (flash->chip->model_id) {
David Hendricksa9884852014-12-11 15:31:12 -08002360 case SPANSION_S25FS128S_L:
2361 case SPANSION_S25FS128S_S: {
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002362 *descrs = s25fs128s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002363 *num_entries = ARRAY_SIZE(s25fs128s_ranges);
David Hendricksa9884852014-12-11 15:31:12 -08002364 break;
2365 }
David Hendricksc694bb82015-02-25 14:52:17 -08002366 case SPANSION_S25FL256S_UL:
2367 case SPANSION_S25FL256S_US: {
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002368 *descrs = s25fl256s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002369 *num_entries = ARRAY_SIZE(s25fl256s_ranges);
David Hendricksc694bb82015-02-25 14:52:17 -08002370 break;
2371 }
David Hendricksa9884852014-12-11 15:31:12 -08002372 default:
2373 msg_cerr("%s():%d Spansion flash chip mismatch (0x%04x)"
Patrick Georgif3fa2992017-02-02 16:24:44 +01002374 ", aborting\n", __func__, __LINE__,
2375 flash->chip->model_id);
David Hendricksa9884852014-12-11 15:31:12 -08002376 return -1;
2377 }
2378 break;
David Hendrickse0512a72014-07-15 20:30:47 -07002379 default:
2380 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01002381 __func__, flash->chip->manufacture_id);
David Hendrickse0512a72014-07-15 20:30:47 -07002382 return -1;
2383 }
2384
2385 return 0;
2386}
2387
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002388/* Determines if special s25f-specific functions need to be used to access a
2389 * given chip's modifier bits. Very much a hard-coded special case hack, but it
2390 * is also very easy to replace once a proper abstraction for accessing
2391 * specific modifier bits is added. */
2392static int use_s25f_modifier_bits(const struct flashctx *flash)
2393{
2394 bool model_match =
2395 flash->chip->model_id == SPANSION_S25FS128S_L ||
2396 flash->chip->model_id == SPANSION_S25FS128S_S ||
2397 flash->chip->model_id == SPANSION_S25FL256S_UL ||
2398 flash->chip->model_id == SPANSION_S25FL256S_US;
2399 return (flash->chip->manufacture_id == SPANSION_ID) && model_match;
2400}
2401
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002402static uint8_t generic_get_bp_mask(struct status_register_layout sr1)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002403{
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002404 return ((1 << (sr1.bp0_pos + sr1.bp_bits)) - 1) ^ \
2405 ((1 << sr1.bp0_pos) - 1);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002406}
2407
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002408static uint8_t generic_get_status_check_mask(struct status_register_layout sr1)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002409{
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002410 return generic_get_bp_mask(sr1) | 1 << sr1.srp_pos;
Marco Chen9d5bddb2020-02-11 17:12:56 +08002411}
2412
David Hendrickse0512a72014-07-15 20:30:47 -07002413/* Given a [start, len], this function finds a block protect bit combination
2414 * (if possible) and sets the corresponding bits in "status". Remaining bits
2415 * are preserved. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002416static int generic_range_to_status(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002417 unsigned int start, unsigned int len,
Marco Chen9d5bddb2020-02-11 17:12:56 +08002418 uint8_t *status, uint8_t *check_mask)
David Hendrickse0512a72014-07-15 20:30:47 -07002419{
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002420 struct status_register_layout sr1;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002421 struct wp_range_descriptor *r;
David Hendrickse0512a72014-07-15 20:30:47 -07002422 int i, range_found = 0, num_entries;
2423 uint8_t bp_mask;
2424
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002425 if (get_sr1_layout(flash, &sr1))
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002426 return -1;
2427
Nikolai Artemiev9daffd92021-04-06 16:54:46 +10002428 if (range_table(flash, &r, &num_entries))
David Hendrickse0512a72014-07-15 20:30:47 -07002429 return -1;
2430
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002431 bp_mask = generic_get_bp_mask(sr1);
David Hendrickse0512a72014-07-15 20:30:47 -07002432
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002433 for (i = 0; i < num_entries; i++, r++) {
David Hendrickse0512a72014-07-15 20:30:47 -07002434 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
2435 start, len, r->range.start, r->range.len);
2436 if ((start == r->range.start) && (len == r->range.len)) {
2437 *status &= ~(bp_mask);
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002438 *status |= r->bp << (sr1.bp0_pos);
David Hendricks148a4bf2015-03-13 21:02:42 -07002439
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002440 if (use_s25f_modifier_bits(flash)) {
2441 if (s25f_set_modifier_bits(flash, &r->m) < 0) {
Edward O'Callaghan0b662c12021-01-22 00:30:24 +11002442 msg_cerr("error setting modifier bits for range.\n");
David Hendricks148a4bf2015-03-13 21:02:42 -07002443 return -1;
2444 }
2445 }
2446
David Hendrickse0512a72014-07-15 20:30:47 -07002447 range_found = 1;
2448 break;
2449 }
2450 }
2451
2452 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11002453 msg_cerr("%s: matching range not found\n", __func__);
David Hendrickse0512a72014-07-15 20:30:47 -07002454 return -1;
2455 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002456
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002457 *check_mask = generic_get_status_check_mask(sr1);
David Hendrickse0512a72014-07-15 20:30:47 -07002458 return 0;
2459}
2460
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002461static int generic_status_to_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002462 const uint8_t sr1, unsigned int *start, unsigned int *len)
2463{
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002464 struct status_register_layout sr1_layout;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002465 struct wp_range_descriptor *r;
Duncan Laurie04ca1172015-03-12 09:25:34 -07002466 int num_entries, i, status_found = 0;
David Hendrickse0512a72014-07-15 20:30:47 -07002467 uint8_t sr1_bp;
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +11002468 struct modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -07002469
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002470 if (get_sr1_layout(flash, &sr1_layout))
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002471 return -1;
2472
Nikolai Artemiev9daffd92021-04-06 16:54:46 +10002473 if (range_table(flash, &r, &num_entries))
David Hendrickse0512a72014-07-15 20:30:47 -07002474 return -1;
2475
David Hendricks148a4bf2015-03-13 21:02:42 -07002476 /* modifier bits may be compared more than once, so get them here */
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002477 if (use_s25f_modifier_bits(flash) && s25f_get_modifier_bits(flash, &m) < 0)
2478 return -1;
David Hendricks148a4bf2015-03-13 21:02:42 -07002479
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002480 sr1_bp = (sr1 >> sr1_layout.bp0_pos) & ((1 << sr1_layout.bp_bits) - 1);
David Hendrickse0512a72014-07-15 20:30:47 -07002481
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002482 for (i = 0; i < num_entries; i++, r++) {
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002483 if (use_s25f_modifier_bits(flash)) {
David Hendricks148a4bf2015-03-13 21:02:42 -07002484 if (memcmp(&m, &r->m, sizeof(m)))
2485 continue;
2486 }
David Hendrickse0512a72014-07-15 20:30:47 -07002487 msg_cspew("comparing 0x%02x 0x%02x\n", sr1_bp, r->bp);
2488 if (sr1_bp == r->bp) {
2489 *start = r->range.start;
2490 *len = r->range.len;
2491 status_found = 1;
2492 break;
2493 }
2494 }
2495
2496 if (!status_found) {
2497 msg_cerr("matching status not found\n");
2498 return -1;
2499 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002500
David Hendrickse0512a72014-07-15 20:30:47 -07002501 return 0;
2502}
2503
2504/* Given a [start, len], this function calls generic_range_to_status() to
2505 * convert it to flash-chip-specific range bits, then sets into status register.
2506 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002507static int generic_set_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002508 unsigned int start, unsigned int len)
2509{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002510 uint8_t status, expected, check_mask;
David Hendrickse0512a72014-07-15 20:30:47 -07002511
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002512 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002513 msg_cdbg("%s: old status: 0x%02x\n", __func__, status);
2514
2515 expected = status; /* preserve non-bp bits */
Marco Chen9d5bddb2020-02-11 17:12:56 +08002516 if (generic_range_to_status(flash, start, len, &expected, &check_mask))
David Hendrickse0512a72014-07-15 20:30:47 -07002517 return -1;
2518
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002519 spi_write_status_register(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002520
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002521 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002522 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002523 if ((status & check_mask) != (expected & check_mask)) {
2524 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2525 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002526 return 1;
2527 }
David Hendrickse0512a72014-07-15 20:30:47 -07002528 return 0;
2529}
2530
2531/* Set/clear the status regsiter write protect bit in SR1. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002532static int generic_set_srp0(const struct flashctx *flash, int enable)
David Hendrickse0512a72014-07-15 20:30:47 -07002533{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002534 uint8_t status, expected, check_mask;
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002535 struct status_register_layout sr1;
David Hendrickse0512a72014-07-15 20:30:47 -07002536
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002537 if (get_sr1_layout(flash, &sr1))
David Hendrickse0512a72014-07-15 20:30:47 -07002538 return -1;
2539
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002540 expected = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002541 msg_cdbg("%s: old status: 0x%02x\n", __func__, expected);
2542
2543 if (enable)
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002544 expected |= 1 << sr1.srp_pos;
David Hendrickse0512a72014-07-15 20:30:47 -07002545 else
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002546 expected &= ~(1 << sr1.srp_pos);
David Hendrickse0512a72014-07-15 20:30:47 -07002547
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002548 spi_write_status_register(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002549
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002550 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002551 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002552
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002553 check_mask = generic_get_status_check_mask(sr1);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002554 msg_cdbg("%s: check mask: 0x%02x\n", __func__, check_mask);
2555 if ((status & check_mask) != (expected & check_mask)) {
2556 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2557 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002558 return -1;
Marco Chen9d5bddb2020-02-11 17:12:56 +08002559 }
David Hendrickse0512a72014-07-15 20:30:47 -07002560
2561 return 0;
2562}
2563
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002564static int generic_enable_writeprotect(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002565 enum wp_mode wp_mode)
2566{
2567 int ret;
2568
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002569 if (wp_mode != WP_MODE_HARDWARE) {
David Hendrickse0512a72014-07-15 20:30:47 -07002570 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
2571 return 1;
2572 }
2573
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002574 ret = generic_set_srp0(flash, 1);
David Hendrickse0512a72014-07-15 20:30:47 -07002575 if (ret)
2576 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002577
David Hendrickse0512a72014-07-15 20:30:47 -07002578 return ret;
2579}
2580
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002581static int generic_disable_writeprotect(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002582{
2583 int ret;
2584
2585 ret = generic_set_srp0(flash, 0);
2586 if (ret)
2587 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002588
David Hendrickse0512a72014-07-15 20:30:47 -07002589 return ret;
2590}
2591
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002592static int wp_context_status(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002593{
2594 uint8_t sr1;
2595 unsigned int start, len;
2596 int ret = 0;
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002597 struct status_register_layout sr1_layout;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002598 int wp_en;
David Hendrickse0512a72014-07-15 20:30:47 -07002599
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002600 if (get_sr1_layout(flash, &sr1_layout))
David Hendrickse0512a72014-07-15 20:30:47 -07002601 return -1;
2602
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002603 sr1 = spi_read_status_register(flash);
Nikolai Artemiev33b91062021-04-06 16:34:10 +10002604 wp_en = (sr1 >> sr1_layout.srp_pos) & 1;
David Hendrickse0512a72014-07-15 20:30:47 -07002605
2606 msg_cinfo("WP: status: 0x%04x\n", sr1);
2607 msg_cinfo("WP: status.srp0: %x\n", wp_en);
2608 /* FIXME: SRP1 is not really generic, but we probably should print
2609 * it anyway to have consistent output. #legacycruft */
2610 msg_cinfo("WP: status.srp1: %x\n", 0);
2611 msg_cinfo("WP: write protect is %s.\n",
2612 wp_en ? "enabled" : "disabled");
2613
2614 msg_cinfo("WP: write protect range: ");
2615 if (generic_status_to_range(flash, sr1, &start, &len)) {
2616 msg_cinfo("(cannot resolve the range)\n");
2617 ret = -1;
2618 } else {
2619 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
2620 }
2621
2622 return ret;
2623}
2624
2625struct wp wp_generic = {
Nikolai Artemiev3b666da2021-04-06 16:59:01 +10002626 .list_ranges = list_ranges,
David Hendrickse0512a72014-07-15 20:30:47 -07002627 .set_range = generic_set_range,
2628 .enable = generic_enable_writeprotect,
2629 .disable = generic_disable_writeprotect,
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002630 .wp_status = wp_context_status,
David Hendrickse0512a72014-07-15 20:30:47 -07002631};