blob: 7dbc1fdddbcb3bc4ba02618457a597c7eb32ebb3 [file] [log] [blame]
David Hendricksd1c55d72010-08-24 15:14:19 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2010 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
David Hendricksd1c55d72010-08-24 15:14:19 -070016 */
17
David Hendricksf7924d12010-06-10 21:26:44 -070018#include <stdlib.h>
19#include <string.h>
Edward O'Callaghanb4300ca2019-09-03 16:15:21 +100020#include <strings.h>
David Hendricksf7924d12010-06-10 21:26:44 -070021
22#include "flash.h"
23#include "flashchips.h"
24#include "chipdrivers.h"
Louis Yung-Chieh Lo52aa9302010-09-06 10:45:02 +080025#include "spi.h"
David Hendricks23cd7782010-08-25 12:42:38 -070026#include "writeprotect.h"
David Hendricksf7924d12010-06-10 21:26:44 -070027
David Hendricks1c09f802012-10-03 11:03:48 -070028/*
David Hendricksf7924d12010-06-10 21:26:44 -070029 * The following procedures rely on look-up tables to match the user-specified
30 * range with the chip's supported ranges. This turned out to be the most
31 * elegant approach since diferent flash chips use different levels of
32 * granularity and methods to determine protected ranges. In other words,
David Hendrickse0512a72014-07-15 20:30:47 -070033 * be stupid and simple since clever arithmetic will not work for many chips.
David Hendricksf7924d12010-06-10 21:26:44 -070034 */
35
36struct wp_range {
37 unsigned int start; /* starting address */
38 unsigned int len; /* len */
39};
40
41enum bit_state {
42 OFF = 0,
43 ON = 1,
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +080044 X = -1 /* don't care. Must be bigger than max # of bp. */
David Hendricksf7924d12010-06-10 21:26:44 -070045};
46
David Hendrickse0512a72014-07-15 20:30:47 -070047/*
48 * Generic write-protection schema for 25-series SPI flash chips. This assumes
49 * there is a status register that contains one or more consecutive bits which
50 * determine which address range is protected.
51 */
52
53struct status_register_layout {
54 int bp0_pos; /* position of BP0 */
55 int bp_bits; /* number of block protect bits */
56 int srp_pos; /* position of status register protect enable bit */
57};
58
Edward O'Callaghan91b38272019-12-04 17:12:43 +110059/*
60 * The following ranges and functions are useful for representing the
61 * writeprotect schema in which there are typically 5 bits of
62 * relevant information stored in status register 1:
63 * m.sec: This bit indicates the units (sectors vs. blocks)
64 * m.tb: The top-bottom bit indicates if the affected range is at the top of
65 * the flash memory's address space or at the bottom.
66 * bp: Bitmask representing the number of affected sectors/blocks.
67 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +110068struct wp_range_descriptor {
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +110069 struct modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -070070 unsigned int bp; /* block protect bitfield */
71 struct wp_range range;
72};
73
Edward O'Callaghana3edcb22019-12-05 14:30:50 +110074struct wp_context {
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +100075 struct status_register_layout sr1; /* status register 1 */
David Hendrickse0512a72014-07-15 20:30:47 -070076};
77
Edward O'Callaghanc69f6b82019-12-05 16:49:21 +110078struct w25q_status {
79 /* this maps to register layout -- do not change ordering */
80 unsigned char busy : 1;
81 unsigned char wel : 1;
82 unsigned char bp0 : 1;
83 unsigned char bp1 : 1;
84 unsigned char bp2 : 1;
85 unsigned char tb : 1;
86 unsigned char sec : 1;
87 unsigned char srp0 : 1;
88} __attribute__ ((packed));
89
90/* Status register for large flash layouts with 4 BP bits */
91struct w25q_status_large {
92 unsigned char busy : 1;
93 unsigned char wel : 1;
94 unsigned char bp0 : 1;
95 unsigned char bp1 : 1;
96 unsigned char bp2 : 1;
97 unsigned char bp3 : 1;
98 unsigned char tb : 1;
99 unsigned char srp0 : 1;
100} __attribute__ ((packed));
101
102struct w25q_status_2 {
103 unsigned char srp1 : 1;
104 unsigned char qe : 1;
105 unsigned char rsvd : 6;
106} __attribute__ ((packed));
107
108int w25_range_to_status(const struct flashctx *flash,
109 unsigned int start, unsigned int len,
110 struct w25q_status *status);
111int w25_status_to_range(const struct flashctx *flash,
112 const struct w25q_status *status,
113 unsigned int *start, unsigned int *len);
114
David Hendrickse0512a72014-07-15 20:30:47 -0700115/*
David Hendrickse0512a72014-07-15 20:30:47 -0700116 * Mask to extract write-protect enable and range bits
117 * Status register 1:
118 * SRP0: bit 7
119 * range(BP2-BP0): bit 4-2
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800120 * range(BP3-BP0): bit 5-2 (large chips)
David Hendrickse0512a72014-07-15 20:30:47 -0700121 * Status register 2:
122 * SRP1: bit 1
123 */
124#define MASK_WP_AREA (0x9C)
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800125#define MASK_WP_AREA_LARGE (0x9C)
David Hendrickse0512a72014-07-15 20:30:47 -0700126#define MASK_WP2_AREA (0x01)
127
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000128static struct wp_range_descriptor en25f40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100129 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
130 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
131 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
132 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
133 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 448 * 1024} },
134 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 384 * 1024} },
135 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 256 * 1024} },
136 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 512 * 1024} },
David Hendricks57566ed2010-08-16 18:24:45 -0700137};
138
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000139static struct wp_range_descriptor en25q40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100140 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
141 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 504 * 1024} },
142 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 496 * 1024} },
143 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 480 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700144
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100145 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 448 * 1024} },
146 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 384 * 1024} },
147 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
148 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700149};
150
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000151static struct wp_range_descriptor en25q80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100152 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
153 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 1016 * 1024} },
154 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 1008 * 1024} },
155 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 992 * 1024} },
156 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 960 * 1024} },
157 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 896 * 1024} },
158 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 768 * 1024} },
159 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 1024 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700160};
161
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000162static struct wp_range_descriptor en25q32_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100163 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
164 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 4032 * 1024} },
165 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 3968 * 1024} },
166 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 3840 * 1024} },
167 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 3584 * 1024} },
168 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 3072 * 1024} },
169 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 2048 * 1024} },
170 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700171
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100172 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
173 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 4032 * 1024} },
174 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 3968 * 1024} },
175 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 3840 * 1024} },
176 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 3584 * 1024} },
177 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 3072 * 1024} },
178 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 2048 * 1024} },
179 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700180};
181
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000182static struct wp_range_descriptor en25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100183 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
184 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8128 * 1024} },
185 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 8064 * 1024} },
186 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7936 * 1024} },
187 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7680 * 1024} },
188 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 7168 * 1024} },
189 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 6144 * 1024} },
190 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700191
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100192 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
193 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 8128 * 1024} },
194 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 8064 * 1024} },
195 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 7936 * 1024} },
196 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 7680 * 1024} },
197 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 7168 * 1024} },
198 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 6144 * 1024} },
199 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700200};
201
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000202static struct wp_range_descriptor en25q128_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100203 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
204 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16320 * 1024} },
205 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 16256 * 1024} },
206 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 16128 * 1024} },
207 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 15872 * 1024} },
208 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 15360 * 1024} },
209 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 14336 * 1024} },
210 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 16384 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700211
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100212 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
213 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 16320 * 1024} },
214 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 16256 * 1024} },
215 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 16128 * 1024} },
216 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x080000, 15872 * 1024} },
217 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x100000, 15360 * 1024} },
218 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x200000, 14336 * 1024} },
219 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 16384 * 1024} },
David Hendrickse185bf22011-05-24 15:34:18 -0700220};
221
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000222static struct wp_range_descriptor en25s64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100223 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
224 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 8064 * 1024} },
225 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 7936 * 1024} },
226 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 7680 * 1024} },
227 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 7168 * 1024} },
228 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 6144 * 1024} },
229 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 4096 * 1024} },
230 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 8192 * 1024} },
Marc Jonesb2f90022014-04-29 17:37:23 -0600231
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100232 { .m = { .sec = 0, .tb = 1 }, 0, {0, 0} }, /* none */
233 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x7e0000, 128 * 1024} },
234 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x7c0000, 256 * 1024} },
235 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x780000, 512 * 1024} },
236 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x700000, 1024 * 1024} },
237 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x600000, 2048 * 1024} },
238 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x400000, 4096 * 1024} },
239 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 8192 * 1024} },
Marc Jonesb2f90022014-04-29 17:37:23 -0600240};
241
David Hendricksf8f00c72011-02-01 12:39:46 -0800242/* mx25l1005 ranges also work for the mx25l1005c */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100243static struct wp_range_descriptor mx25l1005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100244 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
245 { .m = { .sec = X, .tb = X }, 0x1, {0x010000, 64 * 1024} },
246 { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
247 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800248};
249
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100250static struct wp_range_descriptor mx25l2005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100251 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
252 { .m = { .sec = X, .tb = X }, 0x1, {0x030000, 64 * 1024} },
253 { .m = { .sec = X, .tb = X }, 0x2, {0x020000, 128 * 1024} },
254 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 256 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800255};
256
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100257static struct wp_range_descriptor mx25l4005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100258 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
259 { .m = { .sec = X, .tb = X }, 0x1, {0x070000, 64 * 1 * 1024} }, /* block 7 */
260 { .m = { .sec = X, .tb = X }, 0x2, {0x060000, 64 * 2 * 1024} }, /* blocks 6-7 */
261 { .m = { .sec = X, .tb = X }, 0x3, {0x040000, 64 * 4 * 1024} }, /* blocks 4-7 */
262 { .m = { .sec = X, .tb = X }, 0x4, {0x000000, 512 * 1024} },
263 { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 512 * 1024} },
264 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 512 * 1024} },
265 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 512 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800266};
267
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100268static struct wp_range_descriptor mx25l8005_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100269 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
270 { .m = { .sec = X, .tb = X }, 0x1, {0x0f0000, 64 * 1 * 1024} }, /* block 15 */
271 { .m = { .sec = X, .tb = X }, 0x2, {0x0e0000, 64 * 2 * 1024} }, /* blocks 14-15 */
272 { .m = { .sec = X, .tb = X }, 0x3, {0x0c0000, 64 * 4 * 1024} }, /* blocks 12-15 */
273 { .m = { .sec = X, .tb = X }, 0x4, {0x080000, 64 * 8 * 1024} }, /* blocks 8-15 */
274 { .m = { .sec = X, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
275 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
276 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf8f00c72011-02-01 12:39:46 -0800277};
278
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100279static struct wp_range_descriptor mx25l1605d_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100280 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
281 { .m = { .sec = X, .tb = 0 }, 0x1, {0x1f0000, 64 * 1 * 1024} }, /* block 31 */
282 { .m = { .sec = X, .tb = 0 }, 0x2, {0x1e0000, 64 * 2 * 1024} }, /* blocks 30-31 */
283 { .m = { .sec = X, .tb = 0 }, 0x3, {0x1c0000, 64 * 4 * 1024} }, /* blocks 28-31 */
284 { .m = { .sec = X, .tb = 0 }, 0x4, {0x180000, 64 * 8 * 1024} }, /* blocks 24-31 */
285 { .m = { .sec = X, .tb = 0 }, 0x5, {0x100000, 64 * 16 * 1024} }, /* blocks 16-31 */
286 { .m = { .sec = X, .tb = 0 }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
287 { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
David Hendricksf8f00c72011-02-01 12:39:46 -0800288
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100289 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 2048 * 1024} },
290 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
291 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
292 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 64 * 24 * 1024} }, /* blocks 0-23 */
293 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 64 * 28 * 1024} }, /* blocks 0-27 */
294 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 64 * 30 * 1024} }, /* blocks 0-29 */
295 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 64 * 31 * 1024} }, /* blocks 0-30 */
296 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
David Hendricksf8f00c72011-02-01 12:39:46 -0800297};
298
299/* FIXME: Is there an mx25l3205 (without a trailing letter)? */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100300static struct wp_range_descriptor mx25l3205d_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100301 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
302 { .m = { .sec = X, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
303 { .m = { .sec = X, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
304 { .m = { .sec = X, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
305 { .m = { .sec = X, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
306 { .m = { .sec = X, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
307 { .m = { .sec = X, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
308 { .m = { .sec = X, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksac72e362010-08-16 18:20:03 -0700309
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100310 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
311 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
312 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
313 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
314 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
315 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
316 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
317 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksac72e362010-08-16 18:20:03 -0700318};
319
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100320static struct wp_range_descriptor mx25u3235e_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100321 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
322 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
323 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
324 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
325 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
326 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
327 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
328 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x000000, 4096 * 1024} },
Vincent Palatin87e092a2013-02-28 15:46:14 -0800329
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100330 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 4096 * 1024} },
331 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 2048 * 1024} },
332 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 3072 * 1024} },
333 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 3584 * 1024} },
334 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 3840 * 1024} },
335 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 3968 * 1024} },
336 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4032 * 1024} },
337 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 4096 * 1024} },
Vincent Palatin87e092a2013-02-28 15:46:14 -0800338};
339
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100340static struct wp_range_descriptor mx25u6435e_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100341 { .m = { .sec = X, .tb = 0 }, 0, {0, 0} }, /* none */
342 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 1 * 64 * 1024} }, /* block 127 */
343 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
344 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
345 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
346 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
347 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
348 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
Jongpil66a96492014-08-14 17:59:06 +0900349
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100350 { .m = { .sec = 0, .tb = 1 }, 0x0, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
351 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 96 * 64 * 1024} }, /* blocks 0-95 */
352 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 112 * 64 * 1024} }, /* blocks 0-111 */
353 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 120 * 64 * 1024} }, /* blocks 0-119 */
354 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 124 * 64 * 1024} }, /* blocks 0-123 */
355 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 126 * 64 * 1024} }, /* blocks 0-125 */
356 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 127 * 64 * 1024} }, /* blocks 0-126 */
357 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
Jongpil66a96492014-08-14 17:59:06 +0900358};
359
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600360#define MX25U12835E_TB (1 << 3)
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100361static struct wp_range_descriptor mx25u12835e_tb0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100362 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
363 { .m = { .sec = 0, .tb = 0 }, 0x1, {0xff0000, 1 * 64 * 1024} }, /* block 255 */
364 { .m = { .sec = 0, .tb = 0 }, 0x2, {0xfe0000, 2 * 64 * 1024} }, /* blocks 254-255 */
365 { .m = { .sec = 0, .tb = 0 }, 0x3, {0xfc0000, 4 * 64 * 1024} }, /* blocks 252-255 */
366 { .m = { .sec = 0, .tb = 0 }, 0x4, {0xf80000, 8 * 64 * 1024} }, /* blocks 248-255 */
367 { .m = { .sec = 0, .tb = 0 }, 0x5, {0xf00000, 16 * 64 * 1024} }, /* blocks 240-255 */
368 { .m = { .sec = 0, .tb = 0 }, 0x6, {0xe00000, 32 * 64 * 1024} }, /* blocks 224-255 */
369 { .m = { .sec = 0, .tb = 0 }, 0x7, {0xc00000, 64 * 64 * 1024} }, /* blocks 192-255 */
370 { .m = { .sec = 0, .tb = 0 }, 0x8, {0x800000, 128 * 64 * 1024} }, /* blocks 128-255 */
371 { .m = { .sec = 0, .tb = 0 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
372 { .m = { .sec = 0, .tb = 0 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
373 { .m = { .sec = 0, .tb = 0 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
374 { .m = { .sec = 0, .tb = 0 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
375 { .m = { .sec = 0, .tb = 0 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
376 { .m = { .sec = 0, .tb = 0 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
377 { .m = { .sec = 0, .tb = 0 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600378};
Alex Lu831c6092017-11-02 23:19:34 -0700379
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100380static struct wp_range_descriptor mx25u12835e_tb1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100381 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 1 * 64 * 1024} }, /* block 0 */
382 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
383 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
384 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
385 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
386 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
387 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
388 { .m = { .sec = 0, .tb = 1 }, 0x8, {0x000000, 128 * 64 * 1024} }, /* blocks 0-127 */
389 { .m = { .sec = 0, .tb = 1 }, 0x9, {0x000000, 256 * 64 * 1024} }, /* blocks all */
390 { .m = { .sec = 0, .tb = 1 }, 0xa, {0x000000, 256 * 64 * 1024} }, /* blocks all */
391 { .m = { .sec = 0, .tb = 1 }, 0xb, {0x000000, 256 * 64 * 1024} }, /* blocks all */
392 { .m = { .sec = 0, .tb = 1 }, 0xc, {0x000000, 256 * 64 * 1024} }, /* blocks all */
393 { .m = { .sec = 0, .tb = 1 }, 0xd, {0x000000, 256 * 64 * 1024} }, /* blocks all */
394 { .m = { .sec = 0, .tb = 1 }, 0xe, {0x000000, 256 * 64 * 1024} }, /* blocks all */
395 { .m = { .sec = 0, .tb = 1 }, 0xf, {0x000000, 256 * 64 * 1024} }, /* blocks all */
Alex Lu831c6092017-11-02 23:19:34 -0700396};
397
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100398static struct wp_range_descriptor n25q064_ranges[] = {
David Hendricksfe9123b2015-04-21 13:18:31 -0700399 /*
400 * Note: For N25Q064, sec (usually in bit position 6) is called BP3
401 * (block protect bit 3). It is only useful when all blocks are to
402 * be write-protected.
403 */
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100404 { .m = { .sec = 0, .tb = 0 }, 0, {0, 0} }, /* none */
David Hendricksbfa624b2012-07-24 12:47:59 -0700405
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100406 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7f0000, 64 * 1024} }, /* block 127 */
407 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
408 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
409 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
410 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
411 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
412 { .m = { .sec = 0, .tb = 0 }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700413
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100414 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, /* block 0 */
415 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 2 * 64 * 1024} }, /* blocks 0-1 */
416 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 4 * 64 * 1024} }, /* blocks 0-3 */
417 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 8 * 64 * 1024} }, /* blocks 0-7 */
418 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 16 * 64 * 1024} }, /* blocks 0-15 */
419 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 32 * 64 * 1024} }, /* blocks 0-31 */
420 { .m = { .sec = 0, .tb = 1 }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
David Hendricksbfa624b2012-07-24 12:47:59 -0700421
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100422 { .m = { .sec = X, .tb = 1 }, 0x0, {0x000000, 128 * 64 * 1024} }, /* all */
423 { .m = { .sec = X, .tb = 1 }, 0x1, {0x000000, 128 * 64 * 1024} }, /* all */
424 { .m = { .sec = X, .tb = 1 }, 0x2, {0x000000, 128 * 64 * 1024} }, /* all */
425 { .m = { .sec = X, .tb = 1 }, 0x3, {0x000000, 128 * 64 * 1024} }, /* all */
426 { .m = { .sec = X, .tb = 1 }, 0x4, {0x000000, 128 * 64 * 1024} }, /* all */
427 { .m = { .sec = X, .tb = 1 }, 0x5, {0x000000, 128 * 64 * 1024} }, /* all */
428 { .m = { .sec = X, .tb = 1 }, 0x6, {0x000000, 128 * 64 * 1024} }, /* all */
429 { .m = { .sec = X, .tb = 1 }, 0x7, {0x000000, 128 * 64 * 1024} }, /* all */
David Hendricksbfa624b2012-07-24 12:47:59 -0700430};
431
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100432static struct wp_range_descriptor w25q16_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100433 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
434 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x1f0000, 64 * 1024} },
435 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x1e0000, 128 * 1024} },
436 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x1c0000, 256 * 1024} },
437 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x180000, 512 * 1024} },
438 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x100000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700439
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100440 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
441 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
442 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
443 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
444 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
445 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 2048 * 1024} },
446 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700447
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100448 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
449 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
450 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
451 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
452 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700453
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100454 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
455 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
456 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
457 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
458 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700459};
460
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100461static struct wp_range_descriptor w25q32_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100462 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
463 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} },
464 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} },
465 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} },
466 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} },
467 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} },
468 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700469
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100470 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
471 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
472 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
473 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
474 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} },
475 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 2048 * 1024} },
476 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 4096 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700477
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100478 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x3ff000, 4 * 1024} },
479 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x3fe000, 8 * 1024} },
480 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x3fc000, 16 * 1024} },
481 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x3f8000, 32 * 1024} },
482 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x3f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700483
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100484 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
485 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
486 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
487 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
488 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700489};
490
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100491static struct wp_range_descriptor w25q80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100492 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
493 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0f0000, 64 * 1024} },
494 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0e0000, 128 * 1024} },
495 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0c0000, 256 * 1024} },
496 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700497
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100498 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
499 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
500 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
501 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
502 { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
503 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700504
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100505 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} },
506 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} },
507 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} },
508 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} },
509 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700510
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100511 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
512 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
513 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
514 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
515 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700516};
517
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100518static struct wp_range_descriptor w25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100519 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
David Hendricks2c4a76c2010-06-28 14:00:43 -0700520
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100521 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
522 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
523 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
524 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
525 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
526 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700527
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100528 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
529 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
530 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
531 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
532 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
533 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
534 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700535
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100536 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
537 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
538 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
539 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
540 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700541
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100542 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
543 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
544 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
545 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
546 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
David Hendricks2c4a76c2010-06-28 14:00:43 -0700547};
548
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100549static struct wp_range_descriptor w25rq128_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100550 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* NONE */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530551
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100552 { .m = { .sec = 0, .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* Upper 1/64 */
553 { .m = { .sec = 0, .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* Upper 1/32 */
554 { .m = { .sec = 0, .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* Upper 1/16 */
555 { .m = { .sec = 0, .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* Upper 1/8 */
556 { .m = { .sec = 0, .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* Upper 1/4 */
557 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* Upper 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530558
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100559 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* Lower 1/64 */
560 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* Lower 1/32 */
561 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* Lower 1/16 */
562 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* Lower 1/8 */
563 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* Lower 1/4 */
564 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* Lower 1/2 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530565
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100566 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 16384 * 1024} }, /* ALL */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530567
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100568 { .m = { .sec = 1, .tb = 0 }, 0x1, {0xfff000, 4 * 1024} }, /* Upper 1/4096 */
569 { .m = { .sec = 1, .tb = 0 }, 0x2, {0xffe000, 8 * 1024} }, /* Upper 1/2048 */
570 { .m = { .sec = 1, .tb = 0 }, 0x3, {0xffc000, 16 * 1024} }, /* Upper 1/1024 */
571 { .m = { .sec = 1, .tb = 0 }, 0x4, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
572 { .m = { .sec = 1, .tb = 0 }, 0x5, {0xff8000, 32 * 1024} }, /* Upper 1/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700573
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100574 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, /* Lower 1/4096 */
575 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, /* Lower 1/2048 */
576 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, /* Lower 1/1024 */
577 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, /* Lower 1/512 */
578 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, /* Lower 1/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700579};
580
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100581static struct wp_range_descriptor w25rq128_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100582 { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 16 * 1024 * 1024} }, /* ALL */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700583
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100584 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16128 * 1024} }, /* Lower 63/64 */
585 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 15872 * 1024} }, /* Lower 31/32 */
586 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 15 * 1024 * 1024} }, /* Lower 15/16 */
587 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 14 * 1024 * 1024} }, /* Lower 7/8 */
588 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 12 * 1024 * 1024} }, /* Lower 3/4 */
589 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 8 * 1024 * 1024} }, /* Lower 1/2 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700590
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100591 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x040000, 16128 * 1024} }, /* Upper 63/64 */
592 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x080000, 15872 * 1024} }, /* Upper 31/32 */
593 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x100000, 15 * 1024 * 1024} }, /* Upper 15/16 */
594 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x200000, 14 * 1024 * 1024} }, /* Upper 7/8 */
595 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x400000, 12 * 1024 * 1024} }, /* Upper 3/4 */
596 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x800000, 8 * 1024 * 1024} }, /* Upper 1/2 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700597
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100598 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 0} }, /* NONE */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700599
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100600 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 16380 * 1024} }, /* Lower 4095/4096 */
601 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 16376 * 1024} }, /* Lower 2048/2048 */
602 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 16368 * 1024} }, /* Lower 1023/1024 */
603 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
604 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 16352 * 1024} }, /* Lower 511/512 */
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700605
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100606 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 16380 * 1024} }, /* Upper 4095/4096 */
607 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 16376 * 1024} }, /* Upper 2047/2048 */
608 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 16368 * 1024} }, /* Upper 1023/1024 */
609 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
610 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 16352 * 1024} }, /* Upper 511/512 */
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +0530611};
612
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100613static struct wp_range_descriptor w25rq256_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100614 { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 0x0000000} }, /* NONE */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800615
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100616 { .m = { .sec = X, .tb = 0 }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* Upper 1/512 */
617 { .m = { .sec = X, .tb = 0 }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* Upper 1/256 */
618 { .m = { .sec = X, .tb = 0 }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* Upper 1/128 */
619 { .m = { .sec = X, .tb = 0 }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* Upper 1/64 */
620 { .m = { .sec = X, .tb = 0 }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* Upper 1/32 */
621 { .m = { .sec = X, .tb = 0 }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* Upper 1/16 */
622 { .m = { .sec = X, .tb = 0 }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* Upper 1/8 */
623 { .m = { .sec = X, .tb = 0 }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* Upper 1/4 */
624 { .m = { .sec = X, .tb = 0 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800625
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100626 { .m = { .sec = X, .tb = 1 }, 0x1, {0x0000000, 64 * 1 * 1024} }, /* Lower 1/512 */
627 { .m = { .sec = X, .tb = 1 }, 0x2, {0x0000000, 64 * 2 * 1024} }, /* Lower 1/256 */
628 { .m = { .sec = X, .tb = 1 }, 0x3, {0x0000000, 64 * 4 * 1024} }, /* Lower 1/128 */
629 { .m = { .sec = X, .tb = 1 }, 0x4, {0x0000000, 64 * 8 * 1024} }, /* Lower 1/64 */
630 { .m = { .sec = X, .tb = 1 }, 0x5, {0x0000000, 64 * 16 * 1024} }, /* Lower 1/32 */
631 { .m = { .sec = X, .tb = 1 }, 0x6, {0x0000000, 64 * 32 * 1024} }, /* Lower 1/16 */
632 { .m = { .sec = X, .tb = 1 }, 0x7, {0x0000000, 64 * 64 * 1024} }, /* Lower 1/8 */
633 { .m = { .sec = X, .tb = 1 }, 0x8, {0x0000000, 64 * 128 * 1024} }, /* Lower 1/4 */
634 { .m = { .sec = X, .tb = 1 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800635
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100636 { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* ALL */
637 { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* ALL */
638 { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* ALL */
639 { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* ALL */
640 { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* ALL */
641 { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* ALL */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800642};
643
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100644static struct wp_range_descriptor w25rq256_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100645 { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 64 * 512 * 1024} }, /* ALL */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800646
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100647 { .m = { .sec = X, .tb = 0 }, 0x1, {0x0000000, 64 * 511 * 1024} }, /* Lower 511/512 */
648 { .m = { .sec = X, .tb = 0 }, 0x2, {0x0000000, 64 * 510 * 1024} }, /* Lower 255/256 */
649 { .m = { .sec = X, .tb = 0 }, 0x3, {0x0000000, 64 * 508 * 1024} }, /* Lower 127/128 */
650 { .m = { .sec = X, .tb = 0 }, 0x4, {0x0000000, 64 * 504 * 1024} }, /* Lower 63/64 */
651 { .m = { .sec = X, .tb = 0 }, 0x5, {0x0000000, 64 * 496 * 1024} }, /* Lower 31/32 */
652 { .m = { .sec = X, .tb = 0 }, 0x6, {0x0000000, 64 * 480 * 1024} }, /* Lower 15/16 */
653 { .m = { .sec = X, .tb = 0 }, 0x7, {0x0000000, 64 * 448 * 1024} }, /* Lower 7/8 */
654 { .m = { .sec = X, .tb = 0 }, 0x8, {0x0000000, 64 * 384 * 1024} }, /* Lower 3/4 */
655 { .m = { .sec = X, .tb = 0 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800656
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100657 { .m = { .sec = X, .tb = 1 }, 0x1, {0x0010000, 64 * 511 * 1024} }, /* Upper 511/512 */
658 { .m = { .sec = X, .tb = 1 }, 0x2, {0x0020000, 64 * 510 * 1024} }, /* Upper 255/256 */
659 { .m = { .sec = X, .tb = 1 }, 0x3, {0x0040000, 64 * 508 * 1024} }, /* Upper 127/128 */
660 { .m = { .sec = X, .tb = 1 }, 0x4, {0x0080000, 64 * 504 * 1024} }, /* Upper 63/64 */
661 { .m = { .sec = X, .tb = 1 }, 0x5, {0x0100000, 64 * 496 * 1024} }, /* Upper 31/32 */
662 { .m = { .sec = X, .tb = 1 }, 0x6, {0x0200000, 64 * 480 * 1024} }, /* Upper 15/16 */
663 { .m = { .sec = X, .tb = 1 }, 0x7, {0x0400000, 64 * 448 * 1024} }, /* Upper 7/8 */
664 { .m = { .sec = X, .tb = 1 }, 0x8, {0x0800000, 64 * 384 * 1024} }, /* Upper 3/4 */
665 { .m = { .sec = X, .tb = 1 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800666
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100667 { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 0x0000000} }, /* NONE */
668 { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 0x0000000} }, /* NONE */
669 { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 0x0000000} }, /* NONE */
670 { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 0x0000000} }, /* NONE */
671 { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 0x0000000} }, /* NONE */
672 { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 0x0000000} }, /* NONE */
Duncan Laurie1801f7c2019-01-09 18:02:51 -0800673};
674
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000675static struct wp_range_descriptor w25x10_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100676 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
677 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x010000, 64 * 1024} },
678 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
679 { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} },
680 { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800681};
682
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000683static struct wp_range_descriptor w25x20_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100684 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
685 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x030000, 64 * 1024} },
686 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x020000, 128 * 1024} },
687 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
688 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
689 { .m = { .sec = 0, .tb = X }, 0x3, {0x000000, 256 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800690};
691
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000692static struct wp_range_descriptor w25x40_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100693 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
694 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
695 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
696 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
697 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
698 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
699 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
700 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} },
701 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} },
702 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} },
703 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} },
David Hendricks470ca952010-08-13 14:01:53 -0700704};
705
Edward O'Callaghan3b996502020-04-12 20:46:51 +1000706static struct wp_range_descriptor w25x80_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100707 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
708 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0F0000, 64 * 1024} },
709 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0E0000, 128 * 1024} },
710 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0C0000, 256 * 1024} },
711 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} },
712 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
713 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
714 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
715 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} },
716 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 1024 * 1024} },
717 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 1024 * 1024} },
718 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 1024 * 1024} },
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800719};
720
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100721static struct wp_range_descriptor gd25q40_cmp0_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100722 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* None */
723 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} },
724 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} },
725 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} },
726 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} },
727 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} },
728 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} },
729 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} }, /* All */
730 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} }, /* All */
731 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} }, /* All */
732 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
733 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x07F000, 4 * 1024} },
734 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x07E000, 8 * 1024} },
735 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x07C000, 16 * 1024} },
736 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x078000, 32 * 1024} },
737 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x078000, 32 * 1024} },
738 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x078000, 32 * 1024} },
739 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
740 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
741 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
742 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
743 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
744 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
745 { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 512 * 1024} }, /* All */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600746};
747
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100748static struct wp_range_descriptor gd25q40_cmp1_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100749 { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 512 * 1024} }, /* ALL */
750 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 448 * 1024} },
751 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 384 * 1024} },
752 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 256 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600753
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100754 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x010000, 448 * 1024} },
755 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x020000, 384 * 1024} },
756 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x040000, 256 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600757
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100758 { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 0} }, /* None */
759 { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 0} }, /* None */
760 { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 0} }, /* None */
761 { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 0} }, /* None */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600762
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100763 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 508 * 1024} },
764 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 504 * 1024} },
765 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 496 * 1024} },
766 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 480 * 1024} },
767 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 480 * 1024} },
768 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x000000, 480 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600769
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100770 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 508 * 1024} },
771 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 504 * 1024} },
772 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 496 * 1024} },
773 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 480 * 1024} },
774 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 480 * 1024} },
775 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x008000, 480 * 1024} },
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600776
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100777 { .m = { .sec = 1, .tb = X }, 0x7, {0x000000, 0} }, /* None */
Martin Rothf3c3d5f2017-04-28 14:56:41 -0600778};
779
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100780static struct wp_range_descriptor gd25q64_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100781 { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */
782 { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} },
783 { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} },
784 { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} },
785 { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} },
786 { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} },
787 { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700788
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100789 { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} },
790 { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} },
791 { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} },
792 { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} },
793 { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} },
794 { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} },
795 { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700796
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100797 { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} },
798 { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} },
799 { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} },
800 { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} },
801 { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} },
802 { .m = { .sec = 1, .tb = 0 }, 0x6, {0x7f8000, 32 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700803
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100804 { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} },
805 { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} },
806 { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} },
807 { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} },
808 { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} },
809 { .m = { .sec = 1, .tb = 1 }, 0x6, {0x000000, 32 * 1024} },
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -0700810};
811
Edward O'Callaghane146f9a2019-12-05 14:27:24 +1100812static struct wp_range_descriptor a25l040_ranges[] = {
Edward O'Callaghan91b38272019-12-04 17:12:43 +1100813 { .m = { .sec = X, .tb = X }, 0x0, {0, 0} }, /* none */
814 { .m = { .sec = X, .tb = X }, 0x1, {0x70000, 64 * 1024} },
815 { .m = { .sec = X, .tb = X }, 0x2, {0x60000, 128 * 1024} },
816 { .m = { .sec = X, .tb = X }, 0x3, {0x40000, 256 * 1024} },
817 { .m = { .sec = X, .tb = X }, 0x4, {0x00000, 512 * 1024} },
818 { .m = { .sec = X, .tb = X }, 0x5, {0x00000, 512 * 1024} },
819 { .m = { .sec = X, .tb = X }, 0x6, {0x00000, 512 * 1024} },
820 { .m = { .sec = X, .tb = X }, 0x7, {0x00000, 512 * 1024} },
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +0800821};
822
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100823struct wp *get_wp_for_flashchip(const struct flashchip *chip) {
824 // FIXME: The .wp field should be deleted from from struct flashchip
825 // completly, but linux_mtd and cros_ec still assign their own values
826 // to it. When they are cleaned up we can delete this.
827 if(chip->wp) return chip->wp;
828
829 switch (chip->manufacture_id) {
830 case WINBOND_NEX_ID:
831 switch(chip->model_id) {
832 case WINBOND_NEX_W25X10:
833 case WINBOND_NEX_W25X20:
834 case WINBOND_NEX_W25X40:
835 case WINBOND_NEX_W25X80:
836 case WINBOND_NEX_W25Q128_V_M:
837 return &wp_w25;
838 case WINBOND_NEX_W25Q80_V:
839 case WINBOND_NEX_W25Q16_V:
840 case WINBOND_NEX_W25Q32_V:
841 case WINBOND_NEX_W25Q32_W:
842 case WINBOND_NEX_W25Q32JW:
843 case WINBOND_NEX_W25Q64_V:
844 case WINBOND_NEX_W25Q64_W:
845 // W25Q64JW does not have a range table entry, but the flashchip
846 // set .wp to wp_25q, so keep it here until the issue is resolved
847 case WINBOND_NEX_W25Q64JW:
848 case WINBOND_NEX_W25Q128_DTR:
849 case WINBOND_NEX_W25Q128_V:
850 case WINBOND_NEX_W25Q128_W:
851 return &wp_w25q;
852 case WINBOND_NEX_W25Q256_V:
853 case WINBOND_NEX_W25Q256JV_M:
854 return &wp_w25q_large;
855 }
856 break;
857 case EON_ID_NOPREFIX:
858 switch (chip->model_id) {
859 case EON_EN25F40:
860 case EON_EN25Q40:
861 case EON_EN25Q80:
862 case EON_EN25Q32:
863 case EON_EN25Q64:
864 case EON_EN25Q128:
865 case EON_EN25QH128:
866 case EON_EN25S64:
867 return &wp_w25;
868 }
869 break;
870 case MACRONIX_ID:
871 switch (chip->model_id) {
872 case MACRONIX_MX25L1005:
873 case MACRONIX_MX25L2005:
874 case MACRONIX_MX25L4005:
875 case MACRONIX_MX25L8005:
876 case MACRONIX_MX25L1605:
877 case MACRONIX_MX25L3205:
878 case MACRONIX_MX25U3235E:
879 case MACRONIX_MX25U6435E:
880 return &wp_w25;
881 case MACRONIX_MX25U12835E:
882 return &wp_w25q_large;
883 case MACRONIX_MX25L6405:
884 case MACRONIX_MX25L6495F:
885 case MACRONIX_MX25L25635F:
886 return &wp_generic;
887 }
888 break;
889 case ST_ID:
890 switch(chip->model_id) {
891 case ST_N25Q064__1E:
892 case ST_N25Q064__3E:
893 return &wp_w25;
894 }
895 break;
896 case GIGADEVICE_ID:
897 switch(chip->model_id) {
898 case GIGADEVICE_GD25LQ32:
899 // GD25Q40 does not have a .wp field in flashchips.c, but
900 // it is in the w25 range table function, so note it here
901 // until the issue is resolved:
902 // case GIGADEVICE_GD25Q40:
903 case GIGADEVICE_GD25Q64:
904 case GIGADEVICE_GD25LQ64:
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100905 case GIGADEVICE_GD25Q128:
906 return &wp_w25;
907 case GIGADEVICE_GD25Q256D:
908 return &wp_w25q_large;
Nikolai Artemiev9d3980e2021-03-30 22:26:37 +1100909 case GIGADEVICE_GD25LQ128CD:
910 case GIGADEVICE_GD25Q32:
911 return &wp_generic;
912 }
913 break;
914 case AMIC_ID_NOPREFIX:
915 switch(chip->model_id) {
916 case AMIC_A25L040:
917 return &wp_w25;
918 }
919 break;
920 case ATMEL_ID:
921 switch(chip->model_id) {
922 case ATMEL_AT25SF128A:
923 case ATMEL_AT25SL128A:
924 return &wp_w25q;
925 }
926 break;
927 case PROGMANUF_ID:
928 switch(chip->model_id) {
929 case PROGDEV_ID:
930 return &wp_w25;
931 }
932 break;
933 case SPANSION_ID:
934 switch (chip->model_id) {
935 case SPANSION_S25FS128S_L:
936 case SPANSION_S25FS128S_S:
937 case SPANSION_S25FL256S_UL:
938 case SPANSION_S25FL256S_US:
939 // SPANSION_S25FL128S_UL does not have a range table entry,
940 // but its flashchip set .wp to wp_generic, so keep it here
941 // until the issue resolved
942 case SPANSION_S25FL128S_UL:
943 // SPANSION_S25FL128S_US does not have a range table entry,
944 // but its flashchip set .wp to wp_generic, so keep it here
945 // until the issue resolved
946 case SPANSION_S25FL128S_US:
947 return &wp_generic;
948 }
949 break;
950 }
951
952
953 return NULL;
954}
955
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700956/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700957static uint8_t w25q_read_status_register_2(const struct flashctx *flash)
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700958{
959 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 };
960 unsigned char readarr[2];
961 int ret;
962
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +1100963 if (flash->chip->read_status) {
964 msg_cdbg("RDSR2 failed! cmd=0x35 unimpl for opaque chips\n");
965 return 0;
966 }
967
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700968 /* Read Status Register */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700969 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
Duncan Laurieed32d7b2015-05-27 11:28:18 -0700970 if (ret) {
971 /*
972 * FIXME: make this a benign failure for now in case we are
973 * unable to execute the opcode
974 */
975 msg_cdbg("RDSR2 failed!\n");
976 readarr[0] = 0x00;
977 }
978
979 return readarr[0];
980}
981
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600982/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */
Edward O'Callaghandf43e902020-11-13 23:08:26 +1100983static uint8_t mx25l_read_config_register(const struct flashctx *flash)
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600984{
985 static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x15 };
986 unsigned char readarr[2]; /* leave room for dummy byte */
987 int ret;
988
Edward O'Callaghan70f3e8f2020-12-21 12:50:52 +1100989 if (flash->chip->read_status) {
990 msg_cdbg("RDCR failed! cmd=0x15 unimpl for opaque chips\n");
991 return 0;
992 }
993
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -0600994 ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
995 if (ret) {
996 msg_cdbg("RDCR failed!\n");
997 readarr[0] = 0x00;
998 }
999
1000 return readarr[0];
1001}
1002
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001003/* Given a flash chip, this function returns its range table. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001004static int w25_range_table(const struct flashctx *flash,
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001005 struct wp_range_descriptor **descrs,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001006 int *num_entries)
David Hendricksf7924d12010-06-10 21:26:44 -07001007{
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001008 uint8_t cr;
1009
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001010 *descrs = 0;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001011 *num_entries = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001012
Patrick Georgif3fa2992017-02-02 16:24:44 +01001013 switch (flash->chip->manufacture_id) {
David Hendricksd494b0a2010-08-16 16:28:50 -07001014 case WINBOND_NEX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001015 switch(flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -08001016 case WINBOND_NEX_W25X10:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001017 *descrs = w25x10_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001018 *num_entries = ARRAY_SIZE(w25x10_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001019 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001020 case WINBOND_NEX_W25X20:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001021 *descrs = w25x20_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001022 *num_entries = ARRAY_SIZE(w25x20_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001023 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001024 case WINBOND_NEX_W25X40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001025 *descrs = w25x40_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001026 *num_entries = ARRAY_SIZE(w25x40_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001027 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001028 case WINBOND_NEX_W25X80:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001029 *descrs = w25x80_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001030 *num_entries = ARRAY_SIZE(w25x80_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +08001031 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001032 case WINBOND_NEX_W25Q80_V:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001033 *descrs = w25q80_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001034 *num_entries = ARRAY_SIZE(w25q80_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001035 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001036 case WINBOND_NEX_W25Q16_V:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001037 *descrs = w25q16_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001038 *num_entries = ARRAY_SIZE(w25q16_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001039 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001040 case WINBOND_NEX_W25Q32_V:
1041 case WINBOND_NEX_W25Q32_W:
Edward O'Callaghand80cf712019-05-24 22:06:36 +10001042 case WINBOND_NEX_W25Q32JW:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001043 *descrs = w25q32_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001044 *num_entries = ARRAY_SIZE(w25q32_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001045 break;
Patrick Georgicc04a452017-02-06 12:14:43 +01001046 case WINBOND_NEX_W25Q64_V:
1047 case WINBOND_NEX_W25Q64_W:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001048 *descrs = w25q64_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001049 *num_entries = ARRAY_SIZE(w25q64_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -07001050 break;
Edward O'Callaghan517cb822019-11-21 14:08:32 +11001051 case WINBOND_NEX_W25Q128_DTR:
Alan Green77a95de2019-07-01 16:40:39 +10001052 case WINBOND_NEX_W25Q128_V_M:
Patrick Georgicc04a452017-02-06 12:14:43 +01001053 case WINBOND_NEX_W25Q128_V:
1054 case WINBOND_NEX_W25Q128_W:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001055 if (w25q_read_status_register_2(flash) & (1 << 6)) {
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001056 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001057 *descrs = w25rq128_cmp1_ranges;
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001058 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1059 } else {
1060 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001061 *descrs = w25rq128_cmp0_ranges;
Duncan Laurieed32d7b2015-05-27 11:28:18 -07001062 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1063 }
Ramya Vijaykumare6a7ca82015-05-12 14:27:29 +05301064 break;
Justin TerAvest40083232020-08-17 16:34:46 -06001065 case WINBOND_NEX_W25Q256_V:
Alan Green77a95de2019-07-01 16:40:39 +10001066 case WINBOND_NEX_W25Q256JV_M:
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001067 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1068 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001069 *descrs = w25rq256_cmp1_ranges;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001070 *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges);
1071 } else {
1072 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001073 *descrs = w25rq256_cmp0_ranges;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001074 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
1075 }
1076 break;
David Hendricksd494b0a2010-08-16 16:28:50 -07001077 default:
1078 msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
1079 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001080 flash->chip->model_id);
David Hendricksd494b0a2010-08-16 16:28:50 -07001081 return -1;
1082 }
David Hendricks2c4a76c2010-06-28 14:00:43 -07001083 break;
David Hendricks57566ed2010-08-16 18:24:45 -07001084 case EON_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001085 switch (flash->chip->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -08001086 case EON_EN25F40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001087 *descrs = en25f40_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001088 *num_entries = ARRAY_SIZE(en25f40_ranges);
David Hendricks57566ed2010-08-16 18:24:45 -07001089 break;
David Hendrickse185bf22011-05-24 15:34:18 -07001090 case EON_EN25Q40:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001091 *descrs = en25q40_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001092 *num_entries = ARRAY_SIZE(en25q40_ranges);
1093 break;
1094 case EON_EN25Q80:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001095 *descrs = en25q80_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001096 *num_entries = ARRAY_SIZE(en25q80_ranges);
1097 break;
1098 case EON_EN25Q32:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001099 *descrs = en25q32_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001100 *num_entries = ARRAY_SIZE(en25q32_ranges);
1101 break;
1102 case EON_EN25Q64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001103 *descrs = en25q64_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001104 *num_entries = ARRAY_SIZE(en25q64_ranges);
1105 break;
1106 case EON_EN25Q128:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001107 *descrs = en25q128_ranges;
David Hendrickse185bf22011-05-24 15:34:18 -07001108 *num_entries = ARRAY_SIZE(en25q128_ranges);
1109 break;
Tim Chen136fd0a2020-06-30 19:12:50 +08001110 case EON_EN25QH128:
1111 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1112 /* CMP == 1 */
1113 *descrs = w25rq128_cmp1_ranges;
1114 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1115 } else {
1116 /* CMP == 0 */
1117 *descrs = w25rq128_cmp0_ranges;
1118 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1119 }
1120 break;
Marc Jonesb2f90022014-04-29 17:37:23 -06001121 case EON_EN25S64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001122 *descrs = en25s64_ranges;
Marc Jonesb2f90022014-04-29 17:37:23 -06001123 *num_entries = ARRAY_SIZE(en25s64_ranges);
1124 break;
David Hendricks57566ed2010-08-16 18:24:45 -07001125 default:
1126 msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
1127 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001128 flash->chip->model_id);
David Hendricks57566ed2010-08-16 18:24:45 -07001129 return -1;
1130 }
1131 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001132 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001133 switch (flash->chip->model_id) {
David Hendricksf8f00c72011-02-01 12:39:46 -08001134 case MACRONIX_MX25L1005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001135 *descrs = mx25l1005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001136 *num_entries = ARRAY_SIZE(mx25l1005_ranges);
1137 break;
1138 case MACRONIX_MX25L2005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001139 *descrs = mx25l2005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001140 *num_entries = ARRAY_SIZE(mx25l2005_ranges);
1141 break;
1142 case MACRONIX_MX25L4005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001143 *descrs = mx25l4005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001144 *num_entries = ARRAY_SIZE(mx25l4005_ranges);
1145 break;
1146 case MACRONIX_MX25L8005:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001147 *descrs = mx25l8005_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001148 *num_entries = ARRAY_SIZE(mx25l8005_ranges);
1149 break;
1150 case MACRONIX_MX25L1605:
1151 /* FIXME: MX25L1605 and MX25L1605D have different write
1152 * protection capabilities, but share IDs */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001153 *descrs = mx25l1605d_ranges;
David Hendricksf8f00c72011-02-01 12:39:46 -08001154 *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
1155 break;
David Hendricksc801adb2010-12-09 16:58:56 -08001156 case MACRONIX_MX25L3205:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001157 *descrs = mx25l3205d_ranges;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001158 *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
David Hendricksac72e362010-08-16 18:20:03 -07001159 break;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001160 case MACRONIX_MX25U3235E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001161 *descrs = mx25u3235e_ranges;
Vincent Palatin87e092a2013-02-28 15:46:14 -08001162 *num_entries = ARRAY_SIZE(mx25u3235e_ranges);
1163 break;
Jongpil66a96492014-08-14 17:59:06 +09001164 case MACRONIX_MX25U6435E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001165 *descrs = mx25u6435e_ranges;
Jongpil66a96492014-08-14 17:59:06 +09001166 *num_entries = ARRAY_SIZE(mx25u6435e_ranges);
1167 break;
Alan Greendc0792e2019-07-01 15:01:34 +10001168 case MACRONIX_MX25U12835E:
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001169 cr = mx25l_read_config_register(flash);
1170 if (cr & MX25U12835E_TB) { /* T/B == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001171 *descrs = mx25u12835e_tb1_ranges;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001172 *num_entries = ARRAY_SIZE(mx25u12835e_tb1_ranges);
1173 } else { /* T/B == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001174 *descrs = mx25u12835e_tb0_ranges;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001175 *num_entries = ARRAY_SIZE(mx25u12835e_tb0_ranges);
1176 }
Alex Lu831c6092017-11-02 23:19:34 -07001177 break;
David Hendricksac72e362010-08-16 18:20:03 -07001178 default:
1179 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
1180 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001181 flash->chip->model_id);
David Hendricksac72e362010-08-16 18:20:03 -07001182 return -1;
1183 }
1184 break;
David Hendricksbfa624b2012-07-24 12:47:59 -07001185 case ST_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001186 switch(flash->chip->model_id) {
David Hendricksbfa624b2012-07-24 12:47:59 -07001187 case ST_N25Q064__1E:
1188 case ST_N25Q064__3E:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001189 *descrs = n25q064_ranges;
David Hendricksbfa624b2012-07-24 12:47:59 -07001190 *num_entries = ARRAY_SIZE(n25q064_ranges);
1191 break;
1192 default:
1193 msg_cerr("%s() %d: Micron flash chip mismatch"
1194 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001195 flash->chip->model_id);
David Hendricksbfa624b2012-07-24 12:47:59 -07001196 return -1;
1197 }
1198 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001199 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001200 switch(flash->chip->model_id) {
Bryan Freed9a0051f2012-05-22 16:06:09 -07001201 case GIGADEVICE_GD25LQ32:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001202 *descrs = w25q32_ranges;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001203 *num_entries = ARRAY_SIZE(w25q32_ranges);
1204 break;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001205 case GIGADEVICE_GD25Q40:
1206 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1207 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001208 *descrs = gd25q40_cmp1_ranges;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001209 *num_entries = ARRAY_SIZE(gd25q40_cmp1_ranges);
1210 } else {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001211 *descrs = gd25q40_cmp0_ranges;
Martin Rothf3c3d5f2017-04-28 14:56:41 -06001212 *num_entries = ARRAY_SIZE(gd25q40_cmp0_ranges);
1213 }
1214 break;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001215 case GIGADEVICE_GD25Q64:
Marc Jonesb18734f2014-04-03 16:19:47 -06001216 case GIGADEVICE_GD25LQ64:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001217 *descrs = gd25q64_ranges;
Shawn Nematbakhsh9e8ef492012-09-01 21:58:03 -07001218 *num_entries = ARRAY_SIZE(gd25q64_ranges);
1219 break;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001220 case GIGADEVICE_GD25Q128:
1221 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1222 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001223 *descrs = w25rq128_cmp1_ranges;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001224 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1225 } else {
1226 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001227 *descrs = w25rq128_cmp0_ranges;
Martin Roth1fd87ed2017-02-27 20:50:50 -07001228 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1229 }
1230 break;
Duncan Laurie0c383552019-03-16 12:35:16 -07001231 case GIGADEVICE_GD25Q256D:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001232 *descrs = w25rq256_cmp0_ranges;
Duncan Laurie0c383552019-03-16 12:35:16 -07001233 *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges);
1234 break;
Bryan Freed9a0051f2012-05-22 16:06:09 -07001235 default:
1236 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
1237 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001238 flash->chip->model_id);
Bryan Freed9a0051f2012-05-22 16:06:09 -07001239 return -1;
1240 }
1241 break;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001242 case AMIC_ID_NOPREFIX:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001243 switch(flash->chip->model_id) {
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001244 case AMIC_A25L040:
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001245 *descrs = a25l040_ranges;
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001246 *num_entries = ARRAY_SIZE(a25l040_ranges);
1247 break;
1248 default:
1249 msg_cerr("%s() %d: AMIC flash chip mismatch"
1250 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001251 flash->chip->model_id);
Louis Yung-Chieh Loc8ec7152012-09-17 17:38:35 +08001252 return -1;
1253 }
1254 break;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001255 case ATMEL_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01001256 switch(flash->chip->model_id) {
Edward O'Callaghan1fa87e02019-05-03 02:27:24 -04001257 case ATMEL_AT25SF128A:
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001258 case ATMEL_AT25SL128A:
1259 if (w25q_read_status_register_2(flash) & (1 << 6)) {
1260 /* CMP == 1 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001261 *descrs = w25rq128_cmp1_ranges;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001262 *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges);
1263 } else {
1264 /* CMP == 0 */
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001265 *descrs = w25rq128_cmp0_ranges;
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001266 *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges);
1267 }
1268 break;
1269 default:
1270 msg_cerr("%s() %d: Atmel flash chip mismatch"
1271 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01001272 flash->chip->model_id);
Furquan Shaikhb4df8ef2017-01-05 15:05:35 -08001273 return -1;
1274 }
1275 break;
David Hendricksf7924d12010-06-10 21:26:44 -07001276 default:
David Hendricksd494b0a2010-08-16 16:28:50 -07001277 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01001278 __func__, flash->chip->manufacture_id);
David Hendricksf7924d12010-06-10 21:26:44 -07001279 return -1;
1280 }
1281
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001282 return 0;
1283}
1284
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001285int w25_range_to_status(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001286 unsigned int start, unsigned int len,
1287 struct w25q_status *status)
1288{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001289 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001290 int i, range_found = 0;
1291 int num_entries;
1292
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001293 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001294 return -1;
1295
David Hendricksf7924d12010-06-10 21:26:44 -07001296 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001297 struct wp_range *r = &descrs[i].range;
David Hendricksf7924d12010-06-10 21:26:44 -07001298
1299 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1300 start, len, r->start, r->len);
1301 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001302 status->bp0 = descrs[i].bp & 1;
1303 status->bp1 = descrs[i].bp >> 1;
1304 status->bp2 = descrs[i].bp >> 2;
1305 status->tb = descrs[i].m.tb;
1306 status->sec = descrs[i].m.sec;
David Hendricksf7924d12010-06-10 21:26:44 -07001307
1308 range_found = 1;
1309 break;
1310 }
1311 }
1312
1313 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001314 msg_cerr("%s: matching range not found\n", __func__);
David Hendricksf7924d12010-06-10 21:26:44 -07001315 return -1;
1316 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001317
David Hendricksd494b0a2010-08-16 16:28:50 -07001318 return 0;
1319}
1320
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001321int w25_status_to_range(const struct flashctx *flash,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001322 const struct w25q_status *status,
1323 unsigned int *start, unsigned int *len)
1324{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001325 struct wp_range_descriptor *descrs;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001326 int i, status_found = 0;
1327 int num_entries;
1328
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001329 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001330 return -1;
1331
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001332 for (i = 0; i < num_entries; i++) {
1333 int bp;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001334 int table_bp, table_tb, table_sec;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001335
1336 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
1337 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001338 bp, descrs[i].bp,
1339 status->tb, descrs[i].m.tb,
1340 status->sec, descrs[i].m.sec);
1341 table_bp = descrs[i].bp;
1342 table_tb = descrs[i].m.tb;
1343 table_sec = descrs[i].m.sec;
Louis Yung-Chieh Loedd39302011-11-10 15:43:06 +08001344 if ((bp == table_bp || table_bp == X) &&
1345 (status->tb == table_tb || table_tb == X) &&
1346 (status->sec == table_sec || table_sec == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001347 *start = descrs[i].range.start;
1348 *len = descrs[i].range.len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001349
1350 status_found = 1;
1351 break;
1352 }
1353 }
1354
1355 if (!status_found) {
1356 msg_cerr("matching status not found\n");
1357 return -1;
1358 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001359
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001360 return 0;
1361}
1362
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001363/* Given a [start, len], this function calls w25_range_to_status() to convert
1364 * it to flash-chip-specific range bits, then sets into status register.
1365 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001366static int w25_set_range(const struct flashctx *flash,
David Hendricksd494b0a2010-08-16 16:28:50 -07001367 unsigned int start, unsigned int len)
1368{
1369 struct w25q_status status;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001370 int tmp = 0;
1371 int expected = 0;
David Hendricksd494b0a2010-08-16 16:28:50 -07001372
1373 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001374 tmp = spi_read_status_register(flash);
David Hendricksd494b0a2010-08-16 16:28:50 -07001375 memcpy(&status, &tmp, 1);
1376 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1377
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001378 if (w25_range_to_status(flash, start, len, &status))
1379 return -1;
David Hendricksf7924d12010-06-10 21:26:44 -07001380
1381 msg_cdbg("status.busy: %x\n", status.busy);
1382 msg_cdbg("status.wel: %x\n", status.wel);
1383 msg_cdbg("status.bp0: %x\n", status.bp0);
1384 msg_cdbg("status.bp1: %x\n", status.bp1);
1385 msg_cdbg("status.bp2: %x\n", status.bp2);
1386 msg_cdbg("status.tb: %x\n", status.tb);
1387 msg_cdbg("status.sec: %x\n", status.sec);
1388 msg_cdbg("status.srp0: %x\n", status.srp0);
1389
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001390 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001391 spi_write_status_register(flash, expected);
David Hendricksf7924d12010-06-10 21:26:44 -07001392
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001393 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001394 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001395 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) {
David Hendricksc801adb2010-12-09 16:58:56 -08001396 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001397 expected, tmp);
1398 return 1;
1399 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001400
1401 return 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001402}
1403
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001404/* Print out the current status register value with human-readable text. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001405static int w25_wp_status(const struct flashctx *flash)
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001406{
1407 struct w25q_status status;
1408 int tmp;
David Hendricksce8ded32010-10-08 11:23:38 -07001409 unsigned int start, len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001410 int ret = 0;
1411
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001412 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001413 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001414 memcpy(&status, &tmp, 1);
1415 msg_cinfo("WP: status: 0x%02x\n", tmp);
1416 msg_cinfo("WP: status.srp0: %x\n", status.srp0);
1417 msg_cinfo("WP: write protect is %s.\n",
1418 status.srp0 ? "enabled" : "disabled");
1419
1420 msg_cinfo("WP: write protect range: ");
1421 if (w25_status_to_range(flash, &status, &start, &len)) {
1422 msg_cinfo("(cannot resolve the range)\n");
1423 ret = -1;
1424 } else {
1425 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1426 }
1427
1428 return ret;
1429}
1430
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001431static int w25q_large_range_to_status(const struct flashctx *flash,
1432 unsigned int start, unsigned int len,
1433 struct w25q_status_large *status)
1434{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001435 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001436 int i, range_found = 0;
1437 int num_entries;
1438
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001439 if (w25_range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001440 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001441
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001442 for (i = 0; i < num_entries; i++) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001443 struct wp_range *r = &descrs[i].range;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001444
1445 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
1446 start, len, r->start, r->len);
1447 if ((start == r->start) && (len == r->len)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001448 status->bp0 = descrs[i].bp & 1;
1449 status->bp1 = descrs[i].bp >> 1;
1450 status->bp2 = descrs[i].bp >> 2;
1451 status->bp3 = descrs[i].bp >> 3;
Karthikeyan Ramasubramanianfb166b72019-06-24 12:38:55 -06001452 /*
1453 * For MX25U12835E chip, Top/Bottom (T/B) bit is not
1454 * part of status register and in that bit position is
1455 * Quad Enable (QE)
1456 */
1457 if (flash->chip->manufacture_id != MACRONIX_ID ||
1458 flash->chip->model_id != MACRONIX_MX25U12835E)
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001459 status->tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001460
1461 range_found = 1;
1462 break;
1463 }
1464 }
1465
1466 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11001467 msg_cerr("%s: matching range not found\n", __func__);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001468 return -1;
1469 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001470
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001471 return 0;
1472}
1473
1474static int w25_large_status_to_range(const struct flashctx *flash,
1475 const struct w25q_status_large *status,
1476 unsigned int *start, unsigned int *len)
1477{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001478 struct wp_range_descriptor *descrs;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001479 int i, status_found = 0;
1480 int num_entries;
1481
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001482 if (w25_range_table(flash, &descrs, &num_entries))
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001483 return -1;
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001484
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001485 for (i = 0; i < num_entries; i++) {
1486 int bp;
1487 int table_bp, table_tb;
1488
1489 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) |
1490 (status->bp3 << 3);
1491 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001492 bp, descrs[i].bp,
1493 status->tb, descrs[i].m.tb);
1494 table_bp = descrs[i].bp;
1495 table_tb = descrs[i].m.tb;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001496 if ((bp == table_bp || table_bp == X) &&
1497 (status->tb == table_tb || table_tb == X)) {
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001498 *start = descrs[i].range.start;
1499 *len = descrs[i].range.len;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001500
1501 status_found = 1;
1502 break;
1503 }
1504 }
1505
1506 if (!status_found) {
1507 msg_cerr("matching status not found\n");
1508 return -1;
1509 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001510
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001511 return 0;
1512}
1513
1514/* Given a [start, len], this function calls w25_range_to_status() to convert
1515 * it to flash-chip-specific range bits, then sets into status register.
1516 * Returns 0 if successful, -1 on error, and 1 if reading back was different.
1517 */
1518static int w25q_large_set_range(const struct flashctx *flash,
1519 unsigned int start, unsigned int len)
1520{
1521 struct w25q_status_large status;
1522 int tmp;
1523 int expected = 0;
1524
1525 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001526 tmp = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001527 memcpy(&status, &tmp, 1);
1528 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1529
1530 if (w25q_large_range_to_status(flash, start, len, &status))
1531 return -1;
1532
1533 msg_cdbg("status.busy: %x\n", status.busy);
1534 msg_cdbg("status.wel: %x\n", status.wel);
1535 msg_cdbg("status.bp0: %x\n", status.bp0);
1536 msg_cdbg("status.bp1: %x\n", status.bp1);
1537 msg_cdbg("status.bp2: %x\n", status.bp2);
1538 msg_cdbg("status.bp3: %x\n", status.bp3);
1539 msg_cdbg("status.tb: %x\n", status.tb);
1540 msg_cdbg("status.srp0: %x\n", status.srp0);
1541
1542 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001543 spi_write_status_register(flash, expected);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001544
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001545 tmp = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001546 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001547 if ((tmp & MASK_WP_AREA_LARGE) != (expected & MASK_WP_AREA_LARGE)) {
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001548 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
1549 expected, tmp);
1550 return 1;
1551 }
Edward O'Callaghan2672fb92019-12-04 14:47:58 +11001552
1553 return 0;
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001554}
1555
1556static int w25q_large_wp_status(const struct flashctx *flash)
1557{
1558 struct w25q_status_large sr1;
1559 struct w25q_status_2 sr2;
1560 uint8_t tmp[2];
1561 unsigned int start, len;
1562 int ret = 0;
1563
1564 memset(&sr1, 0, sizeof(sr1));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001565 tmp[0] = spi_read_status_register(flash);
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001566 memcpy(&sr1, &tmp[0], 1);
1567
1568 memset(&sr2, 0, sizeof(sr2));
1569 tmp[1] = w25q_read_status_register_2(flash);
1570 memcpy(&sr2, &tmp[1], 1);
1571
1572 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
1573 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1574 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1575 msg_cinfo("WP: write protect is %s.\n",
1576 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1577
1578 msg_cinfo("WP: write protect range: ");
1579 if (w25_large_status_to_range(flash, &sr1, &start, &len)) {
1580 msg_cinfo("(cannot resolve the range)\n");
1581 ret = -1;
1582 } else {
1583 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1584 }
1585
1586 return ret;
1587}
1588
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001589/* Set/clear the SRP0 bit in the status register. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001590static int w25_set_srp0(const struct flashctx *flash, int enable)
David Hendricksf7924d12010-06-10 21:26:44 -07001591{
1592 struct w25q_status status;
1593 int tmp = 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001594 int expected = 0;
David Hendricksf7924d12010-06-10 21:26:44 -07001595
1596 memset(&status, 0, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001597 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001598 /* FIXME: this is NOT endian-free copy. */
David Hendricksf7924d12010-06-10 21:26:44 -07001599 memcpy(&status, &tmp, 1);
1600 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
1601
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001602 status.srp0 = enable ? 1 : 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001603 memcpy(&expected, &status, sizeof(status));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001604 spi_write_status_register(flash, expected);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001605
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001606 tmp = spi_read_status_register(flash);
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +08001607 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
1608 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
1609 return 1;
David Hendricksf7924d12010-06-10 21:26:44 -07001610
1611 return 0;
1612}
1613
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001614static int w25_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001615 enum wp_mode wp_mode)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001616{
1617 int ret;
1618
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001619 if (wp_mode != WP_MODE_HARDWARE) {
David Hendricks1c09f802012-10-03 11:03:48 -07001620 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
1621 return 1;
1622 }
1623
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11001624 ret = w25_set_srp0(flash, 1);
David Hendricksc801adb2010-12-09 16:58:56 -08001625 if (ret)
1626 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001627 return ret;
1628}
1629
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001630static int w25_disable_writeprotect(const struct flashctx *flash)
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001631{
1632 int ret;
1633
1634 ret = w25_set_srp0(flash, 0);
David Hendricksc801adb2010-12-09 16:58:56 -08001635 if (ret)
1636 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001637
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001638 return ret;
1639}
1640
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001641static int w25_list_ranges(const struct flashctx *flash)
David Hendricks0f7f5382011-02-11 18:12:31 -08001642{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001643 struct wp_range_descriptor *descrs;
David Hendricks0f7f5382011-02-11 18:12:31 -08001644 int i, num_entries;
1645
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001646 if (w25_range_table(flash, &descrs, &num_entries))
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001647 return -1;
1648
David Hendricks0f7f5382011-02-11 18:12:31 -08001649 for (i = 0; i < num_entries; i++) {
1650 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11001651 descrs[i].range.start,
1652 descrs[i].range.len);
David Hendricks0f7f5382011-02-11 18:12:31 -08001653 }
1654
1655 return 0;
1656}
1657
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001658static int w25q_wp_status(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001659{
1660 struct w25q_status sr1;
1661 struct w25q_status_2 sr2;
David Hendricksf1bd8802012-10-30 11:37:57 -07001662 uint8_t tmp[2];
David Hendricks1c09f802012-10-03 11:03:48 -07001663 unsigned int start, len;
1664 int ret = 0;
1665
1666 memset(&sr1, 0, sizeof(sr1));
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001667 tmp[0] = spi_read_status_register(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001668 memcpy(&sr1, &tmp[0], 1);
David Hendricks1c09f802012-10-03 11:03:48 -07001669
David Hendricksf1bd8802012-10-30 11:37:57 -07001670 memset(&sr2, 0, sizeof(sr2));
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001671 tmp[1] = w25q_read_status_register_2(flash);
David Hendricksf1bd8802012-10-30 11:37:57 -07001672 memcpy(&sr2, &tmp[1], 1);
1673
1674 msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]);
David Hendricks1c09f802012-10-03 11:03:48 -07001675 msg_cinfo("WP: status.srp0: %x\n", sr1.srp0);
1676 msg_cinfo("WP: status.srp1: %x\n", sr2.srp1);
1677 msg_cinfo("WP: write protect is %s.\n",
1678 (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled");
1679
1680 msg_cinfo("WP: write protect range: ");
1681 if (w25_status_to_range(flash, &sr1, &start, &len)) {
1682 msg_cinfo("(cannot resolve the range)\n");
1683 ret = -1;
1684 } else {
1685 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
1686 }
1687
1688 return ret;
1689}
1690
1691/*
1692 * W25Q adds an optional byte to the standard WRSR opcode. If /CS is
1693 * de-asserted after the first byte, then it acts like a JEDEC-standard
1694 * WRSR command. if /CS is asserted, then the next data byte is written
1695 * into status register 2.
1696 */
1697#define W25Q_WRSR_OUTSIZE 0x03
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001698static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2)
David Hendricks1c09f802012-10-03 11:03:48 -07001699{
1700 int result;
1701 struct spi_command cmds[] = {
1702 {
1703 /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
1704 .writecnt = JEDEC_WREN_OUTSIZE,
1705 .writearr = (const unsigned char[]){ JEDEC_WREN },
1706 .readcnt = 0,
1707 .readarr = NULL,
1708 }, {
1709 .writecnt = W25Q_WRSR_OUTSIZE,
1710 .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 },
1711 .readcnt = 0,
1712 .readarr = NULL,
1713 }, {
1714 .writecnt = 0,
1715 .writearr = NULL,
1716 .readcnt = 0,
1717 .readarr = NULL,
1718 }};
1719
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001720 result = spi_send_multicommand(flash, cmds);
David Hendricks1c09f802012-10-03 11:03:48 -07001721 if (result) {
1722 msg_cerr("%s failed during command execution\n",
1723 __func__);
1724 }
1725
1726 /* WRSR performs a self-timed erase before the changes take effect. */
David Hendricks60824042014-12-11 17:22:06 -08001727 programmer_delay(100 * 1000);
David Hendricks1c09f802012-10-03 11:03:48 -07001728
1729 return result;
1730}
1731
1732/*
1733 * Set/clear the SRP1 bit in status register 2.
1734 * FIXME: make this more generic if other chips use the same SR2 layout
1735 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001736static int w25q_set_srp1(const struct flashctx *flash, int enable)
David Hendricks1c09f802012-10-03 11:03:48 -07001737{
1738 struct w25q_status sr1;
1739 struct w25q_status_2 sr2;
1740 uint8_t tmp, expected;
1741
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001742 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001743 memcpy(&sr1, &tmp, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001744 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001745 memcpy(&sr2, &tmp, 1);
1746
1747 msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp);
1748
1749 sr2.srp1 = enable ? 1 : 0;
1750
1751 memcpy(&expected, &sr2, 1);
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001752 w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2));
David Hendricks1c09f802012-10-03 11:03:48 -07001753
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001754 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001755 msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp);
1756 if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA))
1757 return 1;
1758
1759 return 0;
1760}
1761
1762enum wp_mode get_wp_mode(const char *mode_str)
1763{
1764 enum wp_mode wp_mode = WP_MODE_UNKNOWN;
1765
1766 if (!strcasecmp(mode_str, "hardware"))
1767 wp_mode = WP_MODE_HARDWARE;
1768 else if (!strcasecmp(mode_str, "power_cycle"))
1769 wp_mode = WP_MODE_POWER_CYCLE;
1770 else if (!strcasecmp(mode_str, "permanent"))
1771 wp_mode = WP_MODE_PERMANENT;
1772
1773 return wp_mode;
1774}
1775
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001776static int w25q_disable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001777 enum wp_mode wp_mode)
1778{
1779 int ret = 1;
David Hendricks1c09f802012-10-03 11:03:48 -07001780 struct w25q_status_2 sr2;
1781 uint8_t tmp;
1782
1783 switch (wp_mode) {
1784 case WP_MODE_HARDWARE:
1785 ret = w25_set_srp0(flash, 0);
1786 break;
1787 case WP_MODE_POWER_CYCLE:
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001788 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001789 memcpy(&sr2, &tmp, 1);
1790 if (sr2.srp1) {
1791 msg_cerr("%s(): must disconnect power to disable "
1792 "write-protection\n", __func__);
1793 } else {
1794 ret = 0;
1795 }
1796 break;
1797 case WP_MODE_PERMANENT:
1798 msg_cerr("%s(): cannot disable permanent write-protection\n",
1799 __func__);
1800 break;
1801 default:
1802 msg_cerr("%s(): invalid mode specified\n", __func__);
1803 break;
1804 }
1805
1806 if (ret)
1807 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11001808
David Hendricks1c09f802012-10-03 11:03:48 -07001809 return ret;
1810}
1811
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001812static int w25q_disable_writeprotect_default(const struct flashctx *flash)
David Hendricks1c09f802012-10-03 11:03:48 -07001813{
1814 return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE);
1815}
1816
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001817static int w25q_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -07001818 enum wp_mode wp_mode)
1819{
1820 int ret = 1;
1821 struct w25q_status sr1;
1822 struct w25q_status_2 sr2;
1823 uint8_t tmp;
1824
1825 switch (wp_mode) {
1826 case WP_MODE_HARDWARE:
1827 if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) {
1828 msg_cerr("%s(): cannot disable power cycle WP mode\n",
1829 __func__);
1830 break;
1831 }
1832
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001833 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001834 memcpy(&sr1, &tmp, 1);
1835 if (sr1.srp0)
1836 ret = 0;
1837 else
1838 ret = w25_set_srp0(flash, 1);
1839
1840 break;
1841 case WP_MODE_POWER_CYCLE:
1842 if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) {
1843 msg_cerr("%s(): cannot disable hardware WP mode\n",
1844 __func__);
1845 break;
1846 }
1847
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001848 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001849 memcpy(&sr2, &tmp, 1);
1850 if (sr2.srp1)
1851 ret = 0;
1852 else
1853 ret = w25q_set_srp1(flash, 1);
1854
1855 break;
1856 case WP_MODE_PERMANENT:
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10001857 tmp = spi_read_status_register(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001858 memcpy(&sr1, &tmp, 1);
1859 if (sr1.srp0 == 0) {
1860 ret = w25_set_srp0(flash, 1);
1861 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001862 msg_perr("%s(): cannot enable SRP0 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001863 "permanent WP\n", __func__);
1864 break;
1865 }
1866 }
1867
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001868 tmp = w25q_read_status_register_2(flash);
David Hendricks1c09f802012-10-03 11:03:48 -07001869 memcpy(&sr2, &tmp, 1);
1870 if (sr2.srp1 == 0) {
1871 ret = w25q_set_srp1(flash, 1);
1872 if (ret) {
David Hendricksf1bd8802012-10-30 11:37:57 -07001873 msg_perr("%s(): cannot enable SRP1 for "
David Hendricks1c09f802012-10-03 11:03:48 -07001874 "permanent WP\n", __func__);
1875 break;
1876 }
1877 }
1878
1879 break;
David Hendricksf1bd8802012-10-30 11:37:57 -07001880 default:
1881 msg_perr("%s(): invalid mode %d\n", __func__, wp_mode);
1882 break;
David Hendricks1c09f802012-10-03 11:03:48 -07001883 }
1884
1885 if (ret)
1886 msg_cerr("%s(): error=%d.\n", __func__, ret);
1887 return ret;
1888}
1889
1890/* W25P, W25X, and many flash chips from various vendors */
David Hendricksf7924d12010-06-10 21:26:44 -07001891struct wp wp_w25 = {
David Hendricks0f7f5382011-02-11 18:12:31 -08001892 .list_ranges = w25_list_ranges,
David Hendricksf7924d12010-06-10 21:26:44 -07001893 .set_range = w25_set_range,
1894 .enable = w25_enable_writeprotect,
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +08001895 .disable = w25_disable_writeprotect,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +08001896 .wp_status = w25_wp_status,
David Hendricks1c09f802012-10-03 11:03:48 -07001897
1898};
1899
1900/* W25Q series has features such as a second status register and SFDP */
1901struct wp wp_w25q = {
1902 .list_ranges = w25_list_ranges,
1903 .set_range = w25_set_range,
1904 .enable = w25q_enable_writeprotect,
1905 /*
1906 * By default, disable hardware write-protection. We may change
1907 * this later if we want to add fine-grained write-protect disable
1908 * as a command-line option.
1909 */
1910 .disable = w25q_disable_writeprotect_default,
1911 .wp_status = w25q_wp_status,
David Hendricksf7924d12010-06-10 21:26:44 -07001912};
David Hendrickse0512a72014-07-15 20:30:47 -07001913
Duncan Laurie1801f7c2019-01-09 18:02:51 -08001914/* W25Q large series has 4 block-protect bits */
1915struct wp wp_w25q_large = {
1916 .list_ranges = w25_list_ranges,
1917 .set_range = w25q_large_set_range,
1918 .enable = w25q_enable_writeprotect,
1919 /*
1920 * By default, disable hardware write-protection. We may change
1921 * this later if we want to add fine-grained write-protect disable
1922 * as a command-line option.
1923 */
1924 .disable = w25q_disable_writeprotect_default,
1925 .wp_status = w25q_large_wp_status,
1926};
1927
Edward O'Callaghan3b996502020-04-12 20:46:51 +10001928static struct wp_range_descriptor gd25q32_cmp0_ranges[] = {
David Hendricksaf3944a2014-07-28 18:37:40 -07001929 /* none, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001930 { { }, 0x00, {0, 0} },
1931 { { }, 0x08, {0, 0} },
1932 { { }, 0x10, {0, 0} },
1933 { { }, 0x18, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001934
David Hendricks148a4bf2015-03-13 21:02:42 -07001935 { { }, 0x01, {0x3f0000, 64 * 1024} },
1936 { { }, 0x02, {0x3e0000, 128 * 1024} },
1937 { { }, 0x03, {0x3c0000, 256 * 1024} },
1938 { { }, 0x04, {0x380000, 512 * 1024} },
1939 { { }, 0x05, {0x300000, 1024 * 1024} },
1940 { { }, 0x06, {0x200000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001941
David Hendricks148a4bf2015-03-13 21:02:42 -07001942 { { }, 0x09, {0x000000, 64 * 1024} },
1943 { { }, 0x0a, {0x000000, 128 * 1024} },
1944 { { }, 0x0b, {0x000000, 256 * 1024} },
1945 { { }, 0x0c, {0x000000, 512 * 1024} },
1946 { { }, 0x0d, {0x000000, 1024 * 1024} },
1947 { { }, 0x0e, {0x000000, 2048 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001948
1949 /* all, bp4 and bp3 => don't care */
David Hendricks148a4bf2015-03-13 21:02:42 -07001950 { { }, 0x07, {0x000000, 4096 * 1024} },
1951 { { }, 0x0f, {0x000000, 4096 * 1024} },
1952 { { }, 0x17, {0x000000, 4096 * 1024} },
1953 { { }, 0x1f, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001954
David Hendricks148a4bf2015-03-13 21:02:42 -07001955 { { }, 0x11, {0x3ff000, 4 * 1024} },
1956 { { }, 0x12, {0x3fe000, 8 * 1024} },
1957 { { }, 0x13, {0x3fc000, 16 * 1024} },
1958 { { }, 0x14, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1959 { { }, 0x15, {0x3f8000, 32 * 1024} }, /* bp0 => don't care */
1960 { { }, 0x16, {0x3f8000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001961
David Hendricks148a4bf2015-03-13 21:02:42 -07001962 { { }, 0x19, {0x000000, 4 * 1024} },
1963 { { }, 0x1a, {0x000000, 8 * 1024} },
1964 { { }, 0x1b, {0x000000, 16 * 1024} },
1965 { { }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1966 { { }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
1967 { { }, 0x1e, {0x000000, 32 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001968};
1969
Edward O'Callaghan3b996502020-04-12 20:46:51 +10001970static struct wp_range_descriptor gd25q32_cmp1_ranges[] = {
Martin Roth563a1fe2017-04-18 14:26:27 -06001971 /* All, bp4 and bp3 => don't care */
1972 { { }, 0x00, {0x000000, 4096 * 1024} }, /* All */
1973 { { }, 0x08, {0x000000, 4096 * 1024} },
1974 { { }, 0x10, {0x000000, 4096 * 1024} },
1975 { { }, 0x18, {0x000000, 4096 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001976
David Hendricks148a4bf2015-03-13 21:02:42 -07001977 { { }, 0x01, {0x000000, 4032 * 1024} },
1978 { { }, 0x02, {0x000000, 3968 * 1024} },
1979 { { }, 0x03, {0x000000, 3840 * 1024} },
1980 { { }, 0x04, {0x000000, 3584 * 1024} },
1981 { { }, 0x05, {0x000000, 3 * 1024 * 1024} },
1982 { { }, 0x06, {0x000000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001983
David Hendricks148a4bf2015-03-13 21:02:42 -07001984 { { }, 0x09, {0x010000, 4032 * 1024} },
1985 { { }, 0x0a, {0x020000, 3968 * 1024} },
1986 { { }, 0x0b, {0x040000, 3840 * 1024} },
1987 { { }, 0x0c, {0x080000, 3584 * 1024} },
1988 { { }, 0x0d, {0x100000, 3 * 1024 * 1024} },
1989 { { }, 0x0e, {0x200000, 2 * 1024 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001990
Martin Roth563a1fe2017-04-18 14:26:27 -06001991 /* None, bp4 and bp3 => don't care */
1992 { { }, 0x07, {0, 0} }, /* None */
1993 { { }, 0x0f, {0, 0} },
1994 { { }, 0x17, {0, 0} },
1995 { { }, 0x1f, {0, 0} },
David Hendricksaf3944a2014-07-28 18:37:40 -07001996
David Hendricks148a4bf2015-03-13 21:02:42 -07001997 { { }, 0x11, {0x000000, 4092 * 1024} },
1998 { { }, 0x12, {0x000000, 4088 * 1024} },
1999 { { }, 0x13, {0x000000, 4080 * 1024} },
2000 { { }, 0x14, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
2001 { { }, 0x15, {0x000000, 4064 * 1024} }, /* bp0 => don't care */
2002 { { }, 0x16, {0x000000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002003
David Hendricks148a4bf2015-03-13 21:02:42 -07002004 { { }, 0x19, {0x001000, 4092 * 1024} },
2005 { { }, 0x1a, {0x002000, 4088 * 1024} },
2006 { { }, 0x1b, {0x040000, 4080 * 1024} },
2007 { { }, 0x1c, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
2008 { { }, 0x1d, {0x080000, 4064 * 1024} }, /* bp0 => don't care */
2009 { { }, 0x1e, {0x080000, 4064 * 1024} },
David Hendricksaf3944a2014-07-28 18:37:40 -07002010};
2011
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002012static struct wp_context gd25q32_wp = {
David Hendricksaf3944a2014-07-28 18:37:40 -07002013 /* TODO: map second status register */
2014 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
2015};
2016
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002017static struct wp_range_descriptor gd25q128_cmp0_ranges[] = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002018 /* none, bp4 and bp3 => don't care, others = 0 */
2019 { { .tb = 0 }, 0x00, {0, 0} },
2020 { { .tb = 0 }, 0x08, {0, 0} },
2021 { { .tb = 0 }, 0x10, {0, 0} },
2022 { { .tb = 0 }, 0x18, {0, 0} },
2023
2024 { { .tb = 0 }, 0x01, {0xfc0000, 256 * 1024} },
2025 { { .tb = 0 }, 0x02, {0xf80000, 512 * 1024} },
2026 { { .tb = 0 }, 0x03, {0xf00000, 1024 * 1024} },
2027 { { .tb = 0 }, 0x04, {0xe00000, 2048 * 1024} },
2028 { { .tb = 0 }, 0x05, {0xc00000, 4096 * 1024} },
2029 { { .tb = 0 }, 0x06, {0x800000, 8192 * 1024} },
2030
2031 { { .tb = 0 }, 0x09, {0x000000, 256 * 1024} },
2032 { { .tb = 0 }, 0x0a, {0x000000, 512 * 1024} },
2033 { { .tb = 0 }, 0x0b, {0x000000, 1024 * 1024} },
2034 { { .tb = 0 }, 0x0c, {0x000000, 2048 * 1024} },
2035 { { .tb = 0 }, 0x0d, {0x000000, 4096 * 1024} },
2036 { { .tb = 0 }, 0x0e, {0x000000, 8192 * 1024} },
2037
2038 /* all, bp4 and bp3 => don't care, others = 1 */
2039 { { .tb = 0 }, 0x07, {0x000000, 16384 * 1024} },
2040 { { .tb = 0 }, 0x0f, {0x000000, 16384 * 1024} },
2041 { { .tb = 0 }, 0x17, {0x000000, 16384 * 1024} },
2042 { { .tb = 0 }, 0x1f, {0x000000, 16384 * 1024} },
2043
2044 { { .tb = 0 }, 0x11, {0xfff000, 4 * 1024} },
2045 { { .tb = 0 }, 0x12, {0xffe000, 8 * 1024} },
2046 { { .tb = 0 }, 0x13, {0xffc000, 16 * 1024} },
2047 { { .tb = 0 }, 0x14, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
2048 { { .tb = 0 }, 0x15, {0xff8000, 32 * 1024} }, /* bp0 => don't care */
2049
2050 { { .tb = 0 }, 0x19, {0x000000, 4 * 1024} },
2051 { { .tb = 0 }, 0x1a, {0x000000, 8 * 1024} },
2052 { { .tb = 0 }, 0x1b, {0x000000, 16 * 1024} },
2053 { { .tb = 0 }, 0x1c, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2054 { { .tb = 0 }, 0x1d, {0x000000, 32 * 1024} }, /* bp0 => don't care */
2055 { { .tb = 0 }, 0x1e, {0x000000, 32 * 1024} },
2056};
2057
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002058static struct wp_range_descriptor gd25q128_cmp1_ranges[] = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002059 /* none, bp4 and bp3 => don't care, others = 0 */
2060 { { .tb = 1 }, 0x00, {0x000000, 16384 * 1024} },
2061 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
2062 { { .tb = 1 }, 0x10, {0x000000, 16384 * 1024} },
2063 { { .tb = 1 }, 0x18, {0x000000, 16384 * 1024} },
2064
2065 { { .tb = 1 }, 0x01, {0x000000, 16128 * 1024} },
2066 { { .tb = 1 }, 0x02, {0x000000, 15872 * 1024} },
2067 { { .tb = 1 }, 0x03, {0x000000, 15360 * 1024} },
2068 { { .tb = 1 }, 0x04, {0x000000, 14336 * 1024} },
2069 { { .tb = 1 }, 0x05, {0x000000, 12288 * 1024} },
2070 { { .tb = 1 }, 0x06, {0x000000, 8192 * 1024} },
2071
2072 { { .tb = 1 }, 0x09, {0x000000, 16128 * 1024} },
2073 { { .tb = 1 }, 0x0a, {0x000000, 15872 * 1024} },
2074 { { .tb = 1 }, 0x0b, {0x000000, 15360 * 1024} },
2075 { { .tb = 1 }, 0x0c, {0x000000, 14336 * 1024} },
2076 { { .tb = 1 }, 0x0d, {0x000000, 12288 * 1024} },
2077 { { .tb = 1 }, 0x0e, {0x000000, 8192 * 1024} },
2078
2079 /* none, bp4 and bp3 => don't care, others = 1 */
2080 { { .tb = 1 }, 0x07, {0x000000, 16384 * 1024} },
2081 { { .tb = 1 }, 0x08, {0x000000, 16384 * 1024} },
2082 { { .tb = 1 }, 0x0f, {0x000000, 16384 * 1024} },
2083 { { .tb = 1 }, 0x17, {0x000000, 16384 * 1024} },
2084 { { .tb = 1 }, 0x1f, {0x000000, 16384 * 1024} },
2085
2086 { { .tb = 1 }, 0x11, {0x000000, 16380 * 1024} },
2087 { { .tb = 1 }, 0x12, {0x000000, 16376 * 1024} },
2088 { { .tb = 1 }, 0x13, {0x000000, 16368 * 1024} },
2089 { { .tb = 1 }, 0x14, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
2090 { { .tb = 1 }, 0x15, {0x000000, 16352 * 1024} }, /* bp0 => don't care */
2091
2092 { { .tb = 1 }, 0x19, {0x001000, 16380 * 1024} },
2093 { { .tb = 1 }, 0x1a, {0x002000, 16376 * 1024} },
2094 { { .tb = 1 }, 0x1b, {0x004000, 16368 * 1024} },
2095 { { .tb = 1 }, 0x1c, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
2096 { { .tb = 1 }, 0x1d, {0x008000, 16352 * 1024} }, /* bp0 => don't care */
2097 { { .tb = 1 }, 0x1e, {0x008000, 16352 * 1024} },
2098};
2099
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002100static struct wp_context gd25q128_wp = {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002101 /* TODO: map second and third status registers */
2102 .sr1 = { .bp0_pos = 2, .bp_bits = 5, .srp_pos = 7 },
2103};
2104
David Hendricks83541d32014-07-15 20:58:21 -07002105/* FIXME: MX25L6406 has same ID as MX25L6405D */
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002106static struct wp_range_descriptor mx25l6406e_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002107 { { }, 0, {0, 0} }, /* none */
2108 { { }, 0x1, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
2109 { { }, 0x2, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
2110 { { }, 0x3, {0x7a0000, 64 * 8 * 1024} }, /* blocks 120-127 */
2111 { { }, 0x4, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
2112 { { }, 0x5, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
2113 { { }, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
David Hendricks83541d32014-07-15 20:58:21 -07002114
David Hendricks148a4bf2015-03-13 21:02:42 -07002115 { { }, 0x7, {0x000000, 64 * 128 * 1024} }, /* all */
2116 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2117 { { }, 0x9, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2118 { { }, 0xa, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
2119 { { }, 0xb, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
2120 { { }, 0xc, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
2121 { { }, 0xd, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
2122 { { }, 0xe, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
2123 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricks83541d32014-07-15 20:58:21 -07002124};
2125
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002126static struct wp_context mx25l6406e_wp = {
David Hendricks83541d32014-07-15 20:58:21 -07002127 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
David Hendricks83541d32014-07-15 20:58:21 -07002128};
David Hendrickse0512a72014-07-15 20:30:47 -07002129
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002130static struct wp_range_descriptor mx25l6495f_tb0_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002131 { { }, 0, {0, 0} }, /* none */
2132 { { }, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
2133 { { }, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
2134 { { }, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
David Hendricksc3496092014-11-13 17:20:55 -08002135
David Hendricks148a4bf2015-03-13 21:02:42 -07002136 { { }, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
2137 { { }, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
2138 { { }, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
2139 { { }, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
2140 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2141 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2142 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2143 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2144 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2145 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2146 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2147 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002148};
2149
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002150static struct wp_range_descriptor mx25l6495f_tb1_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002151 { { }, 0, {0, 0} }, /* none */
2152 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2153 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2154 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2155 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2156 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2157 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2158 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2159 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* all */
2160 { { }, 0x9, {0x000000, 64 * 128 * 1024} }, /* all */
2161 { { }, 0xa, {0x000000, 64 * 128 * 1024} }, /* all */
2162 { { }, 0xb, {0x000000, 64 * 128 * 1024} }, /* all */
2163 { { }, 0xc, {0x000000, 64 * 128 * 1024} }, /* all */
2164 { { }, 0xd, {0x000000, 64 * 128 * 1024} }, /* all */
2165 { { }, 0xe, {0x000000, 64 * 128 * 1024} }, /* all */
2166 { { }, 0xf, {0x000000, 64 * 128 * 1024} }, /* all */
David Hendricksc3496092014-11-13 17:20:55 -08002167};
2168
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002169static struct wp_context mx25l6495f_wp = {
David Hendricksc3496092014-11-13 17:20:55 -08002170 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2171};
2172
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002173static struct wp_range_descriptor mx25l25635f_tb0_ranges[] = {
Vic Yang848bfd12018-03-23 10:24:07 -07002174 { { }, 0, {0, 0} }, /* none */
2175 { { }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* block 511 */
2176 { { }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* blocks 510-511 */
2177 { { }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* blocks 508-511 */
2178 { { }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* blocks 504-511 */
2179 { { }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* blocks 496-511 */
2180 { { }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* blocks 480-511 */
2181 { { }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* blocks 448-511 */
2182 { { }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* blocks 384-511 */
2183 { { }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* blocks 256-511 */
2184 { { }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* all */
2185 { { }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* all */
2186 { { }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* all */
2187 { { }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* all */
2188 { { }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* all */
2189 { { }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* all */
2190};
2191
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002192static struct wp_range_descriptor mx25l25635f_tb1_ranges[] = {
Vic Yang848bfd12018-03-23 10:24:07 -07002193 { { }, 0, {0, 0} }, /* none */
2194 { { }, 0x1, {0x000000, 64 * 1 * 1024} }, /* block 0 */
2195 { { }, 0x2, {0x000000, 64 * 2 * 1024} }, /* blocks 0-1 */
2196 { { }, 0x3, {0x000000, 64 * 4 * 1024} }, /* blocks 0-3 */
2197 { { }, 0x4, {0x000000, 64 * 8 * 1024} }, /* blocks 0-7 */
2198 { { }, 0x5, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
2199 { { }, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
2200 { { }, 0x7, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
2201 { { }, 0x8, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
2202 { { }, 0x9, {0x000000, 64 * 256 * 1024} }, /* blocks 0-255 */
2203 { { }, 0xa, {0x000000, 64 * 512 * 1024} }, /* all */
2204 { { }, 0xb, {0x000000, 64 * 512 * 1024} }, /* all */
2205 { { }, 0xc, {0x000000, 64 * 512 * 1024} }, /* all */
2206 { { }, 0xd, {0x000000, 64 * 512 * 1024} }, /* all */
2207 { { }, 0xe, {0x000000, 64 * 512 * 1024} }, /* all */
2208 { { }, 0xf, {0x000000, 64 * 512 * 1024} }, /* all */
2209};
2210
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002211static struct wp_context mx25l25635f_wp = {
Vic Yang848bfd12018-03-23 10:24:07 -07002212 .sr1 = { .bp0_pos = 2, .bp_bits = 4, .srp_pos = 7 },
2213};
2214
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002215static struct wp_range_descriptor s25fs128s_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002216 { { .tb = 1 }, 0, {0, 0} }, /* none */
2217 { { .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* lower 64th */
2218 { { .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* lower 32nd */
2219 { { .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* lower 16th */
2220 { { .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* lower 8th */
2221 { { .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* lower 4th */
2222 { { .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* lower half */
2223 { { .tb = 1 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002224
David Hendricks148a4bf2015-03-13 21:02:42 -07002225 { { .tb = 0 }, 0, {0, 0} }, /* none */
2226 { { .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* upper 64th */
2227 { { .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* upper 32nd */
2228 { { .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* upper 16th */
2229 { { .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* upper 8th */
2230 { { .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* upper 4th */
2231 { { .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* upper half */
2232 { { .tb = 0 }, 0x7, {0x000000, 16384 * 1024} }, /* all */
David Hendricksa9884852014-12-11 15:31:12 -08002233};
2234
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002235static struct wp_context s25fs128s_wp = {
David Hendricksa9884852014-12-11 15:31:12 -08002236 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
2237};
2238
David Hendricksc694bb82015-02-25 14:52:17 -08002239
Edward O'Callaghan3b996502020-04-12 20:46:51 +10002240static struct wp_range_descriptor s25fl256s_ranges[] = {
David Hendricks148a4bf2015-03-13 21:02:42 -07002241 { { .tb = 1 }, 0, {0, 0} }, /* none */
2242 { { .tb = 1 }, 0x1, {0x000000, 512 * 1024} }, /* lower 64th */
2243 { { .tb = 1 }, 0x2, {0x000000, 1024 * 1024} }, /* lower 32nd */
2244 { { .tb = 1 }, 0x3, {0x000000, 2048 * 1024} }, /* lower 16th */
2245 { { .tb = 1 }, 0x4, {0x000000, 4096 * 1024} }, /* lower 8th */
2246 { { .tb = 1 }, 0x5, {0x000000, 8192 * 1024} }, /* lower 4th */
2247 { { .tb = 1 }, 0x6, {0x000000, 16384 * 1024} }, /* lower half */
2248 { { .tb = 1 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
2249
2250 { { .tb = 0 }, 0, {0, 0} }, /* none */
2251 { { .tb = 0 }, 0x1, {0x1f80000, 512 * 1024} }, /* upper 64th */
2252 { { .tb = 0 }, 0x2, {0x1f00000, 1024 * 1024} }, /* upper 32nd */
2253 { { .tb = 0 }, 0x3, {0x1e00000, 2048 * 1024} }, /* upper 16th */
2254 { { .tb = 0 }, 0x4, {0x1c00000, 4096 * 1024} }, /* upper 8th */
2255 { { .tb = 0 }, 0x5, {0x1800000, 8192 * 1024} }, /* upper 4th */
2256 { { .tb = 0 }, 0x6, {0x1000000, 16384 * 1024} }, /* upper half */
2257 { { .tb = 0 }, 0x7, {0x000000, 32768 * 1024} }, /* all */
David Hendricksc694bb82015-02-25 14:52:17 -08002258};
2259
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002260static struct wp_context s25fl256s_wp = {
David Hendricksc694bb82015-02-25 14:52:17 -08002261 .sr1 = { .bp0_pos = 2, .bp_bits = 3, .srp_pos = 7 },
2262};
2263
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002264static int get_wp_context(
2265 const struct flashctx *flash, struct wp_context **wp)
2266{
2267 switch (flash->chip->manufacture_id) {
2268 case GIGADEVICE_ID:
2269 switch(flash->chip->model_id) {
2270
2271 case GIGADEVICE_GD25Q32:
2272 *wp = &gd25q32_wp;
2273 return 0;
2274 case GIGADEVICE_GD25LQ128CD:
2275 *wp = &gd25q128_wp;
2276 return 0;
2277 }
2278 break;
2279 case MACRONIX_ID:
2280 switch (flash->chip->model_id) {
2281 case MACRONIX_MX25L6405:
2282 *wp = &mx25l6406e_wp;
2283 return 0;
2284 case MACRONIX_MX25L6495F:
2285 *wp = &mx25l6495f_wp;
2286 return 0;
2287 case MACRONIX_MX25L25635F:
2288 *wp = &mx25l25635f_wp;
2289 return 0;
2290 }
2291 break;
2292 case SPANSION_ID:
2293 switch (flash->chip->model_id) {
2294 case SPANSION_S25FS128S_L:
2295 case SPANSION_S25FS128S_S:
2296 *wp = &s25fs128s_wp;
2297 return 0;
2298 case SPANSION_S25FL256S_UL:
2299 case SPANSION_S25FL256S_US:
2300 *wp = &s25fl256s_wp;
2301 return 0;
2302 }
2303 break;
2304 }
2305
2306 return 1;
2307}
2308
David Hendrickse0512a72014-07-15 20:30:47 -07002309/* Given a flash chip, this function returns its writeprotect info. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002310static int generic_range_table(const struct flashctx *flash,
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002311 struct wp_range_descriptor **descrs,
David Hendrickse0512a72014-07-15 20:30:47 -07002312 int *num_entries)
2313{
David Hendrickse0512a72014-07-15 20:30:47 -07002314 *num_entries = 0;
2315
Patrick Georgif3fa2992017-02-02 16:24:44 +01002316 switch (flash->chip->manufacture_id) {
David Hendricksaf3944a2014-07-28 18:37:40 -07002317 case GIGADEVICE_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002318 switch(flash->chip->model_id) {
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002319
David Hendricksaf3944a2014-07-28 18:37:40 -07002320 case GIGADEVICE_GD25Q32: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002321 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002322
David Hendricksaf3944a2014-07-28 18:37:40 -07002323 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002324 *descrs = &gd25q32_cmp0_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002325 *num_entries = ARRAY_SIZE(gd25q32_cmp0_ranges);
2326 } else { /* CMP == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002327 *descrs = &gd25q32_cmp1_ranges[0];
David Hendricksaf3944a2014-07-28 18:37:40 -07002328 *num_entries = ARRAY_SIZE(gd25q32_cmp1_ranges);
2329 }
2330
2331 break;
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002332 }
Aaron Durbin6c957d72018-08-20 09:31:01 -06002333 case GIGADEVICE_GD25LQ128CD: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002334 uint8_t sr1 = w25q_read_status_register_2(flash);
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002335
2336 if (!(sr1 & (1 << 6))) { /* CMP == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002337 *descrs = &gd25q128_cmp0_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002338 *num_entries = ARRAY_SIZE(gd25q128_cmp0_ranges);
2339 } else { /* CMP == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002340 *descrs = &gd25q128_cmp1_ranges[0];
David Hendricks1e9d7ca2016-03-14 15:50:34 -07002341 *num_entries = ARRAY_SIZE(gd25q128_cmp1_ranges);
2342 }
2343
2344 break;
David Hendricksaf3944a2014-07-28 18:37:40 -07002345 }
2346 default:
2347 msg_cerr("%s() %d: GigaDevice flash chip mismatch"
2348 " (0x%04x), aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002349 flash->chip->model_id);
David Hendricksaf3944a2014-07-28 18:37:40 -07002350 return -1;
2351 }
2352 break;
David Hendricks83541d32014-07-15 20:58:21 -07002353 case MACRONIX_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002354 switch (flash->chip->model_id) {
David Hendricks83541d32014-07-15 20:58:21 -07002355 case MACRONIX_MX25L6405:
2356 /* FIXME: MX25L64* chips have mixed capabilities and
2357 share IDs */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002358 *descrs = &mx25l6406e_ranges[0];
David Hendricks83541d32014-07-15 20:58:21 -07002359 *num_entries = ARRAY_SIZE(mx25l6406e_ranges);
2360 break;
David Hendricksc3496092014-11-13 17:20:55 -08002361 case MACRONIX_MX25L6495F: {
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002362 uint8_t cr = mx25l_read_config_register(flash);
David Hendricksc3496092014-11-13 17:20:55 -08002363
David Hendricksc3496092014-11-13 17:20:55 -08002364 if (!(cr & (1 << 3))) { /* T/B == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002365 *descrs = &mx25l6495f_tb0_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002366 *num_entries = ARRAY_SIZE(mx25l6495f_tb0_ranges);
2367 } else { /* T/B == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002368 *descrs = &mx25l6495f_tb1_ranges[0];
David Hendricksc3496092014-11-13 17:20:55 -08002369 *num_entries = ARRAY_SIZE(mx25l6495f_tb1_ranges);
2370 }
2371 break;
2372 }
Vic Yang848bfd12018-03-23 10:24:07 -07002373 case MACRONIX_MX25L25635F: {
2374 uint8_t cr = mx25l_read_config_register(flash);
2375
Vic Yang848bfd12018-03-23 10:24:07 -07002376 if (!(cr & (1 << 3))) { /* T/B == 0 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002377 *descrs = &mx25l25635f_tb0_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002378 *num_entries = ARRAY_SIZE(mx25l25635f_tb0_ranges);
2379 } else { /* T/B == 1 */
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002380 *descrs = &mx25l25635f_tb1_ranges[0];
Vic Yang848bfd12018-03-23 10:24:07 -07002381 *num_entries = ARRAY_SIZE(mx25l25635f_tb1_ranges);
2382 }
2383 break;
2384 }
David Hendricks83541d32014-07-15 20:58:21 -07002385 default:
2386 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
2387 ", aborting\n", __func__, __LINE__,
Patrick Georgif3fa2992017-02-02 16:24:44 +01002388 flash->chip->model_id);
David Hendricks83541d32014-07-15 20:58:21 -07002389 return -1;
2390 }
2391 break;
David Hendricksa9884852014-12-11 15:31:12 -08002392 case SPANSION_ID:
Patrick Georgif3fa2992017-02-02 16:24:44 +01002393 switch (flash->chip->model_id) {
David Hendricksa9884852014-12-11 15:31:12 -08002394 case SPANSION_S25FS128S_L:
2395 case SPANSION_S25FS128S_S: {
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002396 *descrs = s25fs128s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002397 *num_entries = ARRAY_SIZE(s25fs128s_ranges);
David Hendricksa9884852014-12-11 15:31:12 -08002398 break;
2399 }
David Hendricksc694bb82015-02-25 14:52:17 -08002400 case SPANSION_S25FL256S_UL:
2401 case SPANSION_S25FL256S_US: {
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002402 *descrs = s25fl256s_ranges;
David Hendricks148a4bf2015-03-13 21:02:42 -07002403 *num_entries = ARRAY_SIZE(s25fl256s_ranges);
David Hendricksc694bb82015-02-25 14:52:17 -08002404 break;
2405 }
David Hendricksa9884852014-12-11 15:31:12 -08002406 default:
2407 msg_cerr("%s():%d Spansion flash chip mismatch (0x%04x)"
Patrick Georgif3fa2992017-02-02 16:24:44 +01002408 ", aborting\n", __func__, __LINE__,
2409 flash->chip->model_id);
David Hendricksa9884852014-12-11 15:31:12 -08002410 return -1;
2411 }
2412 break;
David Hendrickse0512a72014-07-15 20:30:47 -07002413 default:
2414 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
Patrick Georgif3fa2992017-02-02 16:24:44 +01002415 __func__, flash->chip->manufacture_id);
David Hendrickse0512a72014-07-15 20:30:47 -07002416 return -1;
2417 }
2418
2419 return 0;
2420}
2421
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002422/* Determines if special s25f-specific functions need to be used to access a
2423 * given chip's modifier bits. Very much a hard-coded special case hack, but it
2424 * is also very easy to replace once a proper abstraction for accessing
2425 * specific modifier bits is added. */
2426static int use_s25f_modifier_bits(const struct flashctx *flash)
2427{
2428 bool model_match =
2429 flash->chip->model_id == SPANSION_S25FS128S_L ||
2430 flash->chip->model_id == SPANSION_S25FS128S_S ||
2431 flash->chip->model_id == SPANSION_S25FL256S_UL ||
2432 flash->chip->model_id == SPANSION_S25FL256S_US;
2433 return (flash->chip->manufacture_id == SPANSION_ID) && model_match;
2434}
2435
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002436static uint8_t generic_get_bp_mask(struct wp_context *wp)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002437{
2438 return ((1 << (wp->sr1.bp0_pos + wp->sr1.bp_bits)) - 1) ^ \
2439 ((1 << wp->sr1.bp0_pos) - 1);
2440}
2441
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002442static uint8_t generic_get_status_check_mask(struct wp_context *wp)
Marco Chen9d5bddb2020-02-11 17:12:56 +08002443{
2444 return generic_get_bp_mask(wp) | 1 << wp->sr1.srp_pos;
2445}
2446
David Hendrickse0512a72014-07-15 20:30:47 -07002447/* Given a [start, len], this function finds a block protect bit combination
2448 * (if possible) and sets the corresponding bits in "status". Remaining bits
2449 * are preserved. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002450static int generic_range_to_status(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002451 unsigned int start, unsigned int len,
Marco Chen9d5bddb2020-02-11 17:12:56 +08002452 uint8_t *status, uint8_t *check_mask)
David Hendrickse0512a72014-07-15 20:30:47 -07002453{
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002454 struct wp_context *wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002455 struct wp_range_descriptor *r;
David Hendrickse0512a72014-07-15 20:30:47 -07002456 int i, range_found = 0, num_entries;
2457 uint8_t bp_mask;
2458
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002459 if (get_wp_context(flash, &wp))
2460 return -1;
2461
2462 if (generic_range_table(flash, &r, &num_entries))
David Hendrickse0512a72014-07-15 20:30:47 -07002463 return -1;
2464
Marco Chen9d5bddb2020-02-11 17:12:56 +08002465 bp_mask = generic_get_bp_mask(wp);
David Hendrickse0512a72014-07-15 20:30:47 -07002466
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002467 for (i = 0; i < num_entries; i++, r++) {
David Hendrickse0512a72014-07-15 20:30:47 -07002468 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
2469 start, len, r->range.start, r->range.len);
2470 if ((start == r->range.start) && (len == r->range.len)) {
2471 *status &= ~(bp_mask);
2472 *status |= r->bp << (wp->sr1.bp0_pos);
David Hendricks148a4bf2015-03-13 21:02:42 -07002473
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002474 if (use_s25f_modifier_bits(flash)) {
2475 if (s25f_set_modifier_bits(flash, &r->m) < 0) {
Edward O'Callaghan0b662c12021-01-22 00:30:24 +11002476 msg_cerr("error setting modifier bits for range.\n");
David Hendricks148a4bf2015-03-13 21:02:42 -07002477 return -1;
2478 }
2479 }
2480
David Hendrickse0512a72014-07-15 20:30:47 -07002481 range_found = 1;
2482 break;
2483 }
2484 }
2485
2486 if (!range_found) {
Edward O'Callaghan3be63e02020-03-27 14:44:24 +11002487 msg_cerr("%s: matching range not found\n", __func__);
David Hendrickse0512a72014-07-15 20:30:47 -07002488 return -1;
2489 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002490
Marco Chen9d5bddb2020-02-11 17:12:56 +08002491 *check_mask = generic_get_status_check_mask(wp);
David Hendrickse0512a72014-07-15 20:30:47 -07002492 return 0;
2493}
2494
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002495static int generic_status_to_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002496 const uint8_t sr1, unsigned int *start, unsigned int *len)
2497{
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002498 struct wp_context *wp;
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002499 struct wp_range_descriptor *r;
Duncan Laurie04ca1172015-03-12 09:25:34 -07002500 int num_entries, i, status_found = 0;
David Hendrickse0512a72014-07-15 20:30:47 -07002501 uint8_t sr1_bp;
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +11002502 struct modifier_bits m;
David Hendrickse0512a72014-07-15 20:30:47 -07002503
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002504 if (get_wp_context(flash, &wp))
2505 return -1;
2506
2507 if (generic_range_table(flash, &r, &num_entries))
David Hendrickse0512a72014-07-15 20:30:47 -07002508 return -1;
2509
David Hendricks148a4bf2015-03-13 21:02:42 -07002510 /* modifier bits may be compared more than once, so get them here */
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002511 if (use_s25f_modifier_bits(flash) && s25f_get_modifier_bits(flash, &m) < 0)
2512 return -1;
David Hendricks148a4bf2015-03-13 21:02:42 -07002513
David Hendrickse0512a72014-07-15 20:30:47 -07002514 sr1_bp = (sr1 >> wp->sr1.bp0_pos) & ((1 << wp->sr1.bp_bits) - 1);
2515
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002516 for (i = 0; i < num_entries; i++, r++) {
Nikolai Artemiev9b0c3ec2021-04-06 15:56:36 +10002517 if (use_s25f_modifier_bits(flash)) {
David Hendricks148a4bf2015-03-13 21:02:42 -07002518 if (memcmp(&m, &r->m, sizeof(m)))
2519 continue;
2520 }
David Hendrickse0512a72014-07-15 20:30:47 -07002521 msg_cspew("comparing 0x%02x 0x%02x\n", sr1_bp, r->bp);
2522 if (sr1_bp == r->bp) {
2523 *start = r->range.start;
2524 *len = r->range.len;
2525 status_found = 1;
2526 break;
2527 }
2528 }
2529
2530 if (!status_found) {
2531 msg_cerr("matching status not found\n");
2532 return -1;
2533 }
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002534
David Hendrickse0512a72014-07-15 20:30:47 -07002535 return 0;
2536}
2537
2538/* Given a [start, len], this function calls generic_range_to_status() to
2539 * convert it to flash-chip-specific range bits, then sets into status register.
2540 */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002541static int generic_set_range(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002542 unsigned int start, unsigned int len)
2543{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002544 uint8_t status, expected, check_mask;
David Hendrickse0512a72014-07-15 20:30:47 -07002545
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002546 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002547 msg_cdbg("%s: old status: 0x%02x\n", __func__, status);
2548
2549 expected = status; /* preserve non-bp bits */
Marco Chen9d5bddb2020-02-11 17:12:56 +08002550 if (generic_range_to_status(flash, start, len, &expected, &check_mask))
David Hendrickse0512a72014-07-15 20:30:47 -07002551 return -1;
2552
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002553 spi_write_status_register(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002554
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002555 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002556 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002557 if ((status & check_mask) != (expected & check_mask)) {
2558 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2559 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002560 return 1;
2561 }
David Hendrickse0512a72014-07-15 20:30:47 -07002562 return 0;
2563}
2564
2565/* Set/clear the status regsiter write protect bit in SR1. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002566static int generic_set_srp0(const struct flashctx *flash, int enable)
David Hendrickse0512a72014-07-15 20:30:47 -07002567{
Marco Chen9d5bddb2020-02-11 17:12:56 +08002568 uint8_t status, expected, check_mask;
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002569 struct wp_context *wp;
David Hendrickse0512a72014-07-15 20:30:47 -07002570
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002571 if (get_wp_context(flash, &wp))
David Hendrickse0512a72014-07-15 20:30:47 -07002572 return -1;
2573
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002574 expected = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002575 msg_cdbg("%s: old status: 0x%02x\n", __func__, expected);
2576
2577 if (enable)
2578 expected |= 1 << wp->sr1.srp_pos;
2579 else
2580 expected &= ~(1 << wp->sr1.srp_pos);
2581
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002582 spi_write_status_register(flash, expected);
David Hendrickse0512a72014-07-15 20:30:47 -07002583
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002584 status = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002585 msg_cdbg("%s: new status: 0x%02x\n", __func__, status);
Marco Chen9d5bddb2020-02-11 17:12:56 +08002586
2587 check_mask = generic_get_status_check_mask(wp);
2588 msg_cdbg("%s: check mask: 0x%02x\n", __func__, check_mask);
2589 if ((status & check_mask) != (expected & check_mask)) {
2590 msg_cerr("expected=0x%02x, but actual=0x%02x. check mask=0x%02x\n",
2591 expected, status, check_mask);
David Hendrickse0512a72014-07-15 20:30:47 -07002592 return -1;
Marco Chen9d5bddb2020-02-11 17:12:56 +08002593 }
David Hendrickse0512a72014-07-15 20:30:47 -07002594
2595 return 0;
2596}
2597
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002598static int generic_enable_writeprotect(const struct flashctx *flash,
David Hendrickse0512a72014-07-15 20:30:47 -07002599 enum wp_mode wp_mode)
2600{
2601 int ret;
2602
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002603 if (wp_mode != WP_MODE_HARDWARE) {
David Hendrickse0512a72014-07-15 20:30:47 -07002604 msg_cerr("%s(): unsupported write-protect mode\n", __func__);
2605 return 1;
2606 }
2607
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002608 ret = generic_set_srp0(flash, 1);
David Hendrickse0512a72014-07-15 20:30:47 -07002609 if (ret)
2610 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanca44e5c2019-12-04 14:23:54 +11002611
David Hendrickse0512a72014-07-15 20:30:47 -07002612 return ret;
2613}
2614
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002615static int generic_disable_writeprotect(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002616{
2617 int ret;
2618
2619 ret = generic_set_srp0(flash, 0);
2620 if (ret)
2621 msg_cerr("%s(): error=%d.\n", __func__, ret);
Edward O'Callaghanbea239e2019-12-04 14:42:54 +11002622
David Hendrickse0512a72014-07-15 20:30:47 -07002623 return ret;
2624}
2625
Souvik Ghoshd75cd672016-06-17 14:21:39 -07002626static int generic_list_ranges(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002627{
Edward O'Callaghane146f9a2019-12-05 14:27:24 +11002628 struct wp_range_descriptor *r;
David Hendrickse0512a72014-07-15 20:30:47 -07002629 int i, num_entries;
2630
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002631 if (generic_range_table(flash, &r, &num_entries))
David Hendrickse0512a72014-07-15 20:30:47 -07002632 return -1;
2633
David Hendrickse0512a72014-07-15 20:30:47 -07002634 for (i = 0; i < num_entries; i++) {
2635 msg_cinfo("start: 0x%06x, length: 0x%06x\n",
2636 r->range.start, r->range.len);
2637 r++;
2638 }
2639
2640 return 0;
2641}
2642
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002643static int wp_context_status(const struct flashctx *flash)
David Hendrickse0512a72014-07-15 20:30:47 -07002644{
2645 uint8_t sr1;
2646 unsigned int start, len;
2647 int ret = 0;
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002648 struct wp_context *wp;
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002649 int wp_en;
David Hendrickse0512a72014-07-15 20:30:47 -07002650
Nikolai Artemiev9221c8a2021-04-06 15:59:35 +10002651 if (get_wp_context(flash, &wp))
David Hendrickse0512a72014-07-15 20:30:47 -07002652 return -1;
2653
Nikolai Artemiev48b424b2021-04-06 14:12:02 +10002654 sr1 = spi_read_status_register(flash);
David Hendrickse0512a72014-07-15 20:30:47 -07002655 wp_en = (sr1 >> wp->sr1.srp_pos) & 1;
2656
2657 msg_cinfo("WP: status: 0x%04x\n", sr1);
2658 msg_cinfo("WP: status.srp0: %x\n", wp_en);
2659 /* FIXME: SRP1 is not really generic, but we probably should print
2660 * it anyway to have consistent output. #legacycruft */
2661 msg_cinfo("WP: status.srp1: %x\n", 0);
2662 msg_cinfo("WP: write protect is %s.\n",
2663 wp_en ? "enabled" : "disabled");
2664
2665 msg_cinfo("WP: write protect range: ");
2666 if (generic_status_to_range(flash, sr1, &start, &len)) {
2667 msg_cinfo("(cannot resolve the range)\n");
2668 ret = -1;
2669 } else {
2670 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
2671 }
2672
2673 return ret;
2674}
2675
2676struct wp wp_generic = {
2677 .list_ranges = generic_list_ranges,
2678 .set_range = generic_set_range,
2679 .enable = generic_enable_writeprotect,
2680 .disable = generic_disable_writeprotect,
Edward O'Callaghana3edcb22019-12-05 14:30:50 +11002681 .wp_status = wp_context_status,
David Hendrickse0512a72014-07-15 20:30:47 -07002682};