blob: b43e379f64de9f202750aa98bb4a59009cf86503 [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 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
David Hendricksf7924d12010-06-10 21:26:44 -070021#include <stdlib.h>
22#include <string.h>
23
24#include "flash.h"
25#include "flashchips.h"
26#include "chipdrivers.h"
Louis Yung-Chieh Lo52aa9302010-09-06 10:45:02 +080027#include "spi.h"
David Hendricks23cd7782010-08-25 12:42:38 -070028#include "writeprotect.h"
David Hendricksf7924d12010-06-10 21:26:44 -070029
Louis Yung-Chieh Lo96222b12010-11-01 11:48:11 +080030/* When update flash's status register, it takes few time to erase register.
31 * After surveying some flash vendor specs, such as Winbond, MXIC, EON,
32 * all of their update time are less than 20ms. After refering the spi25.c,
33 * use 100ms delay.
34 */
35#define WRITE_STATUS_REGISTER_DELAY 100 * 1000 /* unit: us */
36
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +080037/* Mask to extract SRP0 and range bits in status register.
38 * SRP0: bit 7
39 * range(BP2-BP0): bit 4-2
40 */
41#define MASK_WP_AREA (0x9C)
42
David Hendricksf7924d12010-06-10 21:26:44 -070043/*
44 * The following procedures rely on look-up tables to match the user-specified
45 * range with the chip's supported ranges. This turned out to be the most
46 * elegant approach since diferent flash chips use different levels of
47 * granularity and methods to determine protected ranges. In other words,
48 * be stupid and simple since clever arithmetic will not for many chips.
49 */
50
51struct wp_range {
52 unsigned int start; /* starting address */
53 unsigned int len; /* len */
54};
55
56enum bit_state {
57 OFF = 0,
58 ON = 1,
59 X = 0 /* don't care */
60};
61
62struct w25q_range {
63 enum bit_state sec; /* if 1, bp[2:0] describe sectors */
64 enum bit_state tb; /* top/bottom select */
65 unsigned short int bp : 3; /* block protect bitfield */
66 struct wp_range range;
67};
68
David Hendricks57566ed2010-08-16 18:24:45 -070069struct w25q_range en25f40_ranges[] = {
70 { X, X, 0, {0, 0} }, /* none */
71 { 0, 0, 0x1, {0x000000, 504 * 1024} },
72 { 0, 0, 0x2, {0x000000, 496 * 1024} },
73 { 0, 0, 0x3, {0x000000, 480 * 1024} },
74 { 0, 0, 0x4, {0x000000, 448 * 1024} },
75 { 0, 0, 0x5, {0x000000, 384 * 1024} },
76 { 0, 0, 0x6, {0x000000, 256 * 1024} },
77 { 0, 0, 0x7, {0x000000, 512 * 1024} },
78};
79
David Hendricksf8f00c72011-02-01 12:39:46 -080080/* mx25l1005 ranges also work for the mx25l1005c */
81static struct w25q_range mx25l1005_ranges[] = {
82 { X, X, 0, {0, 0} }, /* none */
83 { X, X, 0x1, {0x010000, 64 * 1024} },
84 { X, X, 0x2, {0x000000, 128 * 1024} },
85 { X, X, 0x3, {0x000000, 128 * 1024} },
86};
87
88static struct w25q_range mx25l2005_ranges[] = {
89 { X, X, 0, {0, 0} }, /* none */
90 { X, X, 0x1, {0x030000, 64 * 1024} },
91 { X, X, 0x2, {0x020000, 128 * 1024} },
92 { X, X, 0x3, {0x000000, 256 * 1024} },
93};
94
95static struct w25q_range mx25l4005_ranges[] = {
96 { X, X, 0, {0, 0} }, /* none */
97 { X, X, 0x1, {0x070000, 64 * 1 * 1024} }, /* block 7 */
98 { X, X, 0x2, {0x060000, 64 * 2 * 1024} }, /* blocks 6-7 */
99 { X, X, 0x3, {0x040000, 64 * 4 * 1024} }, /* blocks 4-7 */
100 { X, X, 0x4, {0x000000, 512 * 1024} },
101 { X, X, 0x5, {0x000000, 512 * 1024} },
102 { X, X, 0x6, {0x000000, 512 * 1024} },
103 { X, X, 0x7, {0x000000, 512 * 1024} },
104};
105
106static struct w25q_range mx25l8005_ranges[] = {
107 { X, X, 0, {0, 0} }, /* none */
108 { X, X, 0x1, {0x0f0000, 64 * 1 * 1024} }, /* block 15 */
109 { X, X, 0x2, {0x0e0000, 64 * 2 * 1024} }, /* blocks 14-15 */
110 { X, X, 0x3, {0x0c0000, 64 * 4 * 1024} }, /* blocks 12-15 */
111 { X, X, 0x4, {0x080000, 64 * 8 * 1024} }, /* blocks 8-15 */
112 { X, X, 0x5, {0x000000, 1024 * 1024} },
113 { X, X, 0x6, {0x000000, 1024 * 1024} },
114 { X, X, 0x7, {0x000000, 1024 * 1024} },
115};
116
117#if 0
118/* FIXME: mx25l1605 has the same IDs as the mx25l1605d */
119static struct w25q_range mx25l1605_ranges[] = {
120 { X, X, 0, {0, 0} }, /* none */
121 { X, X, 0x1, {0x1f0000, 64 * 1024} }, /* block 31 */
122 { X, X, 0x2, {0x1e0000, 128 * 1024} }, /* blocks 30-31 */
123 { X, X, 0x3, {0x1c0000, 256 * 1024} }, /* blocks 28-31 */
124 { X, X, 0x4, {0x180000, 512 * 1024} }, /* blocks 24-31 */
125 { X, X, 0x4, {0x100000, 1024 * 1024} }, /* blocks 16-31 */
126 { X, X, 0x6, {0x000000, 2048 * 1024} },
127 { X, X, 0x7, {0x000000, 2048 * 1024} },
128};
129#endif
130
131#if 0
132/* FIXME: mx25l6405 has the same IDs as the mx25l6405d */
133static struct w25q_range mx25l6405_ranges[] = {
134 { X, 0, 0, {0, 0} }, /* none */
135 { X, 0, 0x1, {0x7f0000, 64 * 1 * 1024} }, /* block 127 */
136 { X, 0, 0x2, {0x7e0000, 64 * 2 * 1024} }, /* blocks 126-127 */
137 { X, 0, 0x3, {0x7c0000, 64 * 4 * 1024} }, /* blocks 124-127 */
138 { X, 0, 0x4, {0x780000, 64 * 8 * 1024} }, /* blocks 120-127 */
139 { X, 0, 0x5, {0x700000, 64 * 16 * 1024} }, /* blocks 112-127 */
140 { X, 0, 0x6, {0x600000, 64 * 32 * 1024} }, /* blocks 96-127 */
141 { X, 0, 0x7, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
142
143 { X, 1, 0x0, {0x000000, 8192 * 1024} },
144 { X, 1, 0x1, {0x000000, 8192 * 1024} },
145 { X, 1, 0x2, {0x000000, 8192 * 1024} },
146 { X, 1, 0x3, {0x000000, 8192 * 1024} },
147 { X, 1, 0x4, {0x000000, 8192 * 1024} },
148 { X, 1, 0x5, {0x000000, 8192 * 1024} },
149 { X, 1, 0x6, {0x000000, 8192 * 1024} },
150 { X, 1, 0x7, {0x000000, 8192 * 1024} },
151};
152#endif
153
154static struct w25q_range mx25l1605d_ranges[] = {
155 { X, 0, 0, {0, 0} }, /* none */
156 { X, 0, 0x1, {0x1f0000, 64 * 1 * 1024} }, /* block 31 */
157 { X, 0, 0x2, {0x1e0000, 64 * 2 * 1024} }, /* blocks 30-31 */
158 { X, 0, 0x3, {0x1c0000, 64 * 4 * 1024} }, /* blocks 28-31 */
159 { X, 0, 0x4, {0x180000, 64 * 8 * 1024} }, /* blocks 24-31 */
160 { X, 0, 0x5, {0x100000, 64 * 16 * 1024} }, /* blocks 16-31 */
161 { X, 0, 0x6, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
162 { X, 0, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
163
164 { X, 1, 0x0, {0x000000, 2048 * 1024} },
165 { X, 1, 0x1, {0x000000, 2048 * 1024} },
166 { X, 1, 0x2, {0x000000, 64 * 16 * 1024} }, /* blocks 0-15 */
167 { X, 1, 0x3, {0x000000, 64 * 24 * 1024} }, /* blocks 0-23 */
168 { X, 1, 0x4, {0x000000, 64 * 28 * 1024} }, /* blocks 0-27 */
169 { X, 1, 0x5, {0x000000, 64 * 30 * 1024} }, /* blocks 0-29 */
170 { X, 1, 0x6, {0x000000, 64 * 31 * 1024} }, /* blocks 0-30 */
171 { X, 1, 0x7, {0x000000, 64 * 32 * 1024} }, /* blocks 0-31 */
172};
173
174/* FIXME: Is there an mx25l3205 (without a trailing letter)? */
David Hendricksac72e362010-08-16 18:20:03 -0700175static struct w25q_range mx25l3205d_ranges[] = {
176 { X, 0, 0, {0, 0} }, /* none */
177 { X, 0, 0x1, {0x3f0000, 64 * 1024} },
178 { X, 0, 0x2, {0x3e0000, 128 * 1024} },
179 { X, 0, 0x3, {0x3c0000, 256 * 1024} },
180 { X, 0, 0x4, {0x380000, 512 * 1024} },
181 { X, 0, 0x5, {0x300000, 1024 * 1024} },
182 { X, 0, 0x6, {0x200000, 2048 * 1024} },
183 { X, 0, 0x7, {0x000000, 4096 * 1024} },
184
185 { X, 1, 0x0, {0x000000, 4096 * 1024} },
186 { X, 1, 0x1, {0x000000, 2048 * 1024} },
187 { X, 1, 0x2, {0x000000, 3072 * 1024} },
188 { X, 1, 0x3, {0x000000, 3584 * 1024} },
189 { X, 1, 0x4, {0x000000, 3840 * 1024} },
190 { X, 1, 0x5, {0x000000, 3968 * 1024} },
191 { X, 1, 0x6, {0x000000, 4032 * 1024} },
192 { X, 1, 0x7, {0x000000, 4096 * 1024} },
193};
194
David Hendricksf8f00c72011-02-01 12:39:46 -0800195static struct w25q_range mx25l6405d_ranges[] = {
196 { X, 0, 0, {0, 0} }, /* none */
197 { X, 0, 0x1, {0x7e0000, 2 * 64 * 1024} }, /* blocks 126-127 */
198 { X, 0, 0x2, {0x7c0000, 4 * 64 * 1024} }, /* blocks 124-127 */
199 { X, 0, 0x3, {0x780000, 8 * 64 * 1024} }, /* blocks 120-127 */
200 { X, 0, 0x4, {0x700000, 16 * 64 * 1024} }, /* blocks 112-127 */
201 { X, 0, 0x5, {0x600000, 32 * 64 * 1024} }, /* blocks 96-127 */
202 { X, 0, 0x6, {0x400000, 64 * 64 * 1024} }, /* blocks 64-127 */
203 { X, 0, 0x7, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
204
205 { X, 1, 0x0, {0x000000, 8192 * 1024} },
206 { X, 1, 0x1, {0x000000, 64 * 64 * 1024} }, /* blocks 0-63 */
207 { X, 1, 0x2, {0x000000, 64 * 96 * 1024} }, /* blocks 0-95 */
208 { X, 1, 0x3, {0x000000, 64 * 112 * 1024} }, /* blocks 0-111 */
209 { X, 1, 0x4, {0x000000, 64 * 120 * 1024} }, /* blocks 0-119 */
210 { X, 1, 0x5, {0x000000, 64 * 124 * 1024} }, /* blocks 0-123 */
211 { X, 1, 0x6, {0x000000, 64 * 126 * 1024} }, /* blocks 0-125 */
212 { X, 1, 0x7, {0x000000, 64 * 128 * 1024} }, /* blocks 0-127 */
213};
214
David Hendricksf7924d12010-06-10 21:26:44 -0700215static struct w25q_range w25q16_ranges[] = {
216 { X, X, 0, {0, 0} }, /* none */
217 { 0, 0, 0x1, {0x1f0000, 64 * 1024} },
218 { 0, 0, 0x2, {0x1e0000, 128 * 1024} },
219 { 0, 0, 0x3, {0x1c0000, 256 * 1024} },
220 { 0, 0, 0x4, {0x180000, 512 * 1024} },
221 { 0, 0, 0x5, {0x100000, 1024 * 1024} },
222
223 { 0, 1, 0x1, {0x000000, 64 * 1024} },
224 { 0, 1, 0x2, {0x000000, 128 * 1024} },
225 { 0, 1, 0x3, {0x000000, 256 * 1024} },
226 { 0, 1, 0x4, {0x000000, 512 * 1024} },
227 { 0, 1, 0x5, {0x000000, 1024 * 1024} },
228 { X, X, 0x6, {0x000000, 2048 * 1024} },
229 { X, X, 0x7, {0x000000, 2048 * 1024} },
230
231 { 1, 0, 0x1, {0x1ff000, 4 * 1024} },
232 { 1, 0, 0x2, {0x1fe000, 8 * 1024} },
233 { 1, 0, 0x3, {0x1fc000, 16 * 1024} },
234 { 1, 0, 0x4, {0x1f8000, 32 * 1024} },
235 { 1, 0, 0x5, {0x1f8000, 32 * 1024} },
236
237 { 1, 1, 0x1, {0x000000, 4 * 1024} },
238 { 1, 1, 0x2, {0x000000, 8 * 1024} },
239 { 1, 1, 0x3, {0x000000, 16 * 1024} },
240 { 1, 1, 0x4, {0x000000, 32 * 1024} },
241 { 1, 1, 0x5, {0x000000, 32 * 1024} },
242};
243
244static struct w25q_range w25q32_ranges[] = {
245 { X, X, 0, {0, 0} }, /* none */
246 { 0, 0, 0x1, {0x3f0000, 64 * 1024} },
247 { 0, 0, 0x2, {0x3e0000, 128 * 1024} },
248 { 0, 0, 0x3, {0x3c0000, 256 * 1024} },
249 { 0, 0, 0x4, {0x380000, 512 * 1024} },
250 { 0, 0, 0x5, {0x300000, 1024 * 1024} },
David Hendricks05653ff2010-06-15 16:05:12 -0700251 { 0, 0, 0x6, {0x200000, 2048 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700252
253 { 0, 1, 0x1, {0x000000, 64 * 1024} },
254 { 0, 1, 0x2, {0x000000, 128 * 1024} },
255 { 0, 1, 0x3, {0x000000, 256 * 1024} },
256 { 0, 1, 0x4, {0x000000, 512 * 1024} },
257 { 0, 1, 0x5, {0x000000, 1024 * 1024} },
258 { 0, 1, 0x6, {0x000000, 2048 * 1024} },
259 { X, X, 0x7, {0x000000, 4096 * 1024} },
260
261 { 1, 0, 0x1, {0x3ff000, 4 * 1024} },
262 { 1, 0, 0x2, {0x3fe000, 8 * 1024} },
263 { 1, 0, 0x3, {0x3fc000, 16 * 1024} },
264 { 1, 0, 0x4, {0x3f8000, 32 * 1024} },
265 { 1, 0, 0x5, {0x3f8000, 32 * 1024} },
266
267 { 1, 1, 0x1, {0x000000, 4 * 1024} },
268 { 1, 1, 0x2, {0x000000, 8 * 1024} },
269 { 1, 1, 0x3, {0x000000, 16 * 1024} },
270 { 1, 1, 0x4, {0x000000, 32 * 1024} },
271 { 1, 1, 0x5, {0x000000, 32 * 1024} },
272};
273
274static struct w25q_range w25q80_ranges[] = {
275 { X, X, 0, {0, 0} }, /* none */
276 { 0, 0, 0x1, {0x0f0000, 64 * 1024} },
277 { 0, 0, 0x2, {0x0e0000, 128 * 1024} },
278 { 0, 0, 0x3, {0x0c0000, 256 * 1024} },
279 { 0, 0, 0x4, {0x080000, 512 * 1024} },
280
281 { 0, 1, 0x1, {0x000000, 64 * 1024} },
282 { 0, 1, 0x2, {0x000000, 128 * 1024} },
283 { 0, 1, 0x3, {0x000000, 256 * 1024} },
284 { 0, 1, 0x4, {0x000000, 512 * 1024} },
David Hendricks05653ff2010-06-15 16:05:12 -0700285 { X, X, 0x6, {0x000000, 1024 * 1024} },
286 { X, X, 0x7, {0x000000, 1024 * 1024} },
David Hendricksf7924d12010-06-10 21:26:44 -0700287
288 { 1, 0, 0x1, {0x1ff000, 4 * 1024} },
289 { 1, 0, 0x2, {0x1fe000, 8 * 1024} },
290 { 1, 0, 0x3, {0x1fc000, 16 * 1024} },
291 { 1, 0, 0x4, {0x1f8000, 32 * 1024} },
292 { 1, 0, 0x5, {0x1f8000, 32 * 1024} },
293
294 { 1, 1, 0x1, {0x000000, 4 * 1024} },
295 { 1, 1, 0x2, {0x000000, 8 * 1024} },
296 { 1, 1, 0x3, {0x000000, 16 * 1024} },
297 { 1, 1, 0x4, {0x000000, 32 * 1024} },
298 { 1, 1, 0x5, {0x000000, 32 * 1024} },
299};
300
David Hendricks2c4a76c2010-06-28 14:00:43 -0700301static struct w25q_range w25q64_ranges[] = {
302 { X, X, 0, {0, 0} }, /* none */
303
304 { 0, 0, 0x1, {0x7e0000, 128 * 1024} },
305 { 0, 0, 0x2, {0x7c0000, 256 * 1024} },
306 { 0, 0, 0x3, {0x780000, 512 * 1024} },
307 { 0, 0, 0x4, {0x700000, 1024 * 1024} },
308 { 0, 0, 0x5, {0x600000, 2048 * 1024} },
309 { 0, 0, 0x6, {0x400000, 4096 * 1024} },
310
311 { 0, 1, 0x1, {0x000000, 128 * 1024} },
312 { 0, 1, 0x2, {0x000000, 256 * 1024} },
313 { 0, 1, 0x3, {0x000000, 512 * 1024} },
314 { 0, 1, 0x4, {0x000000, 1024 * 1024} },
315 { 0, 1, 0x5, {0x000000, 2048 * 1024} },
316 { 0, 1, 0x6, {0x000000, 4096 * 1024} },
317 { X, X, 0x7, {0x000000, 8192 * 1024} },
318
319 { 1, 0, 0x1, {0x7ff000, 4 * 1024} },
320 { 1, 0, 0x2, {0x7fe000, 8 * 1024} },
321 { 1, 0, 0x3, {0x7fc000, 16 * 1024} },
322 { 1, 0, 0x4, {0x7f8000, 32 * 1024} },
323 { 1, 0, 0x5, {0x7f8000, 32 * 1024} },
324
325 { 1, 1, 0x1, {0x000000, 4 * 1024} },
326 { 1, 1, 0x2, {0x000000, 8 * 1024} },
327 { 1, 1, 0x3, {0x000000, 16 * 1024} },
328 { 1, 1, 0x4, {0x000000, 32 * 1024} },
329 { 1, 1, 0x5, {0x000000, 32 * 1024} },
330};
331
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800332struct w25q_range w25x10_ranges[] = {
333 { X, X, 0, {0, 0} }, /* none */
334 { 0, 0, 0x1, {0x010000, 64 * 1024} },
335 { 0, 1, 0x1, {0x000000, 64 * 1024} },
336 { X, X, 0x2, {0x000000, 128 * 1024} },
337 { X, X, 0x3, {0x000000, 128 * 1024} },
338};
339
340struct w25q_range w25x20_ranges[] = {
341 { X, X, 0, {0, 0} }, /* none */
342 { 0, 0, 0x1, {0x030000, 64 * 1024} },
343 { 0, 0, 0x2, {0x020000, 128 * 1024} },
344 { 0, 1, 0x1, {0x000000, 64 * 1024} },
345 { 0, 1, 0x2, {0x000000, 128 * 1024} },
346 { 0, X, 0x3, {0x000000, 256 * 1024} },
347};
348
David Hendricks470ca952010-08-13 14:01:53 -0700349struct w25q_range w25x40_ranges[] = {
350 { X, X, 0, {0, 0} }, /* none */
351 { 0, 0, 0x1, {0x070000, 64 * 1024} },
352 { 0, 0, 0x2, {0x060000, 128 * 1024} },
353 { 0, 0, 0x3, {0x040000, 256 * 1024} },
354 { 0, 1, 0x1, {0x000000, 64 * 1024} },
355 { 0, 1, 0x2, {0x000000, 128 * 1024} },
356 { 0, 1, 0x3, {0x000000, 256 * 1024} },
357 { 0, X, 0x4, {0x000000, 512 * 1024} },
358};
359
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800360struct w25q_range w25x80_ranges[] = {
361 { X, X, 0, {0, 0} }, /* none */
362 { 0, 0, 0x1, {0x0F0000, 64 * 1024} },
363 { 0, 0, 0x2, {0x0E0000, 128 * 1024} },
364 { 0, 0, 0x3, {0x0C0000, 256 * 1024} },
365 { 0, 0, 0x4, {0x080000, 512 * 1024} },
366 { 0, 1, 0x1, {0x000000, 64 * 1024} },
367 { 0, 1, 0x2, {0x000000, 128 * 1024} },
368 { 0, 1, 0x3, {0x000000, 256 * 1024} },
369 { 0, 1, 0x4, {0x000000, 512 * 1024} },
370 { 0, X, 0x5, {0x000000, 1024 * 1024} },
371 { 0, X, 0x6, {0x000000, 1024 * 1024} },
372 { 0, X, 0x7, {0x000000, 1024 * 1024} },
373};
374
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800375/* Given a flash chip, this function returns its range table. */
376static int w25_range_table(const struct flashchip *flash,
377 struct w25q_range **w25q_ranges,
378 int *num_entries)
David Hendricksf7924d12010-06-10 21:26:44 -0700379{
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800380 *w25q_ranges = 0;
381 *num_entries = 0;
David Hendricksf7924d12010-06-10 21:26:44 -0700382
David Hendricksd494b0a2010-08-16 16:28:50 -0700383 switch (flash->manufacture_id) {
384 case WINBOND_NEX_ID:
385 switch(flash->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -0800386 case WINBOND_NEX_W25X10:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800387 *w25q_ranges = w25x10_ranges;
388 *num_entries = ARRAY_SIZE(w25x10_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800389 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800390 case WINBOND_NEX_W25X20:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800391 *w25q_ranges = w25x20_ranges;
392 *num_entries = ARRAY_SIZE(w25x20_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800393 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800394 case WINBOND_NEX_W25X40:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800395 *w25q_ranges = w25x40_ranges;
396 *num_entries = ARRAY_SIZE(w25x40_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700397 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800398 case WINBOND_NEX_W25X80:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800399 *w25q_ranges = w25x80_ranges;
400 *num_entries = ARRAY_SIZE(w25x80_ranges);
Louis Yung-Chieh Lo232951f2010-09-16 11:30:00 +0800401 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800402 case WINBOND_NEX_W25Q80:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800403 *w25q_ranges = w25q80_ranges;
404 *num_entries = ARRAY_SIZE(w25q80_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700405 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800406 case WINBOND_NEX_W25Q16:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800407 *w25q_ranges = w25q16_ranges;
408 *num_entries = ARRAY_SIZE(w25q16_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700409 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800410 case WINBOND_NEX_W25Q32:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800411 *w25q_ranges = w25q32_ranges;
412 *num_entries = ARRAY_SIZE(w25q32_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700413 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800414 case WINBOND_NEX_W25Q64:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800415 *w25q_ranges = w25q64_ranges;
416 *num_entries = ARRAY_SIZE(w25q64_ranges);
David Hendricksd494b0a2010-08-16 16:28:50 -0700417 break;
418 default:
419 msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)"
420 ", aborting\n", __func__, __LINE__,
421 flash->model_id);
422 return -1;
423 }
David Hendricks2c4a76c2010-06-28 14:00:43 -0700424 break;
David Hendricks57566ed2010-08-16 18:24:45 -0700425 case EON_ID_NOPREFIX:
426 switch (flash->model_id) {
David Hendricksc801adb2010-12-09 16:58:56 -0800427 case EON_EN25F40:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800428 *w25q_ranges = en25f40_ranges;
429 *num_entries = ARRAY_SIZE(en25f40_ranges);
David Hendricks57566ed2010-08-16 18:24:45 -0700430 break;
431 default:
432 msg_cerr("%s():%d: EON flash chip mismatch (0x%04x)"
433 ", aborting\n", __func__, __LINE__,
434 flash->model_id);
435 return -1;
436 }
437 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800438 case MACRONIX_ID:
David Hendricksac72e362010-08-16 18:20:03 -0700439 switch (flash->model_id) {
David Hendricksf8f00c72011-02-01 12:39:46 -0800440 case MACRONIX_MX25L1005:
441 *w25q_ranges = mx25l1005_ranges;
442 *num_entries = ARRAY_SIZE(mx25l1005_ranges);
443 break;
444 case MACRONIX_MX25L2005:
445 *w25q_ranges = mx25l2005_ranges;
446 *num_entries = ARRAY_SIZE(mx25l2005_ranges);
447 break;
448 case MACRONIX_MX25L4005:
449 *w25q_ranges = mx25l4005_ranges;
450 *num_entries = ARRAY_SIZE(mx25l4005_ranges);
451 break;
452 case MACRONIX_MX25L8005:
453 *w25q_ranges = mx25l8005_ranges;
454 *num_entries = ARRAY_SIZE(mx25l8005_ranges);
455 break;
456 case MACRONIX_MX25L1605:
457 /* FIXME: MX25L1605 and MX25L1605D have different write
458 * protection capabilities, but share IDs */
459 *w25q_ranges = mx25l1605d_ranges;
460 *num_entries = ARRAY_SIZE(mx25l1605d_ranges);
461 break;
David Hendricksc801adb2010-12-09 16:58:56 -0800462 case MACRONIX_MX25L3205:
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800463 *w25q_ranges = mx25l3205d_ranges;
464 *num_entries = ARRAY_SIZE(mx25l3205d_ranges);
David Hendricksac72e362010-08-16 18:20:03 -0700465 break;
David Hendricksf8f00c72011-02-01 12:39:46 -0800466 case MACRONIX_MX25L6405:
467 /* FIXME: MX25L6405 and MX25L6405D have different write
468 * protection capabilities, but share IDs */
469 *w25q_ranges = mx25l6405d_ranges;
470 *num_entries = ARRAY_SIZE(mx25l6405d_ranges);
471 break;
David Hendricksac72e362010-08-16 18:20:03 -0700472 default:
473 msg_cerr("%s():%d: MXIC flash chip mismatch (0x%04x)"
474 ", aborting\n", __func__, __LINE__,
475 flash->model_id);
476 return -1;
477 }
478 break;
David Hendricksf7924d12010-06-10 21:26:44 -0700479 default:
David Hendricksd494b0a2010-08-16 16:28:50 -0700480 msg_cerr("%s: flash vendor (0x%x) not found, aborting\n",
481 __func__, flash->manufacture_id);
David Hendricksf7924d12010-06-10 21:26:44 -0700482 return -1;
483 }
484
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800485 return 0;
486}
487
488int w25_range_to_status(const struct flashchip *flash,
489 unsigned int start, unsigned int len,
490 struct w25q_status *status)
491{
492 struct w25q_range *w25q_ranges;
493 int i, range_found = 0;
494 int num_entries;
495
496 if (w25_range_table(flash, &w25q_ranges, &num_entries)) return -1;
David Hendricksf7924d12010-06-10 21:26:44 -0700497 for (i = 0; i < num_entries; i++) {
498 struct wp_range *r = &w25q_ranges[i].range;
499
500 msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n",
501 start, len, r->start, r->len);
502 if ((start == r->start) && (len == r->len)) {
David Hendricksd494b0a2010-08-16 16:28:50 -0700503 status->bp0 = w25q_ranges[i].bp & 1;
504 status->bp1 = w25q_ranges[i].bp >> 1;
505 status->bp2 = w25q_ranges[i].bp >> 2;
506 status->tb = w25q_ranges[i].tb;
507 status->sec = w25q_ranges[i].sec;
David Hendricksf7924d12010-06-10 21:26:44 -0700508
509 range_found = 1;
510 break;
511 }
512 }
513
514 if (!range_found) {
515 msg_cerr("matching range not found\n");
516 return -1;
517 }
David Hendricksd494b0a2010-08-16 16:28:50 -0700518 return 0;
519}
520
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800521int w25_status_to_range(const struct flashchip *flash,
522 const struct w25q_status *status,
523 unsigned int *start, unsigned int *len)
524{
525 struct w25q_range *w25q_ranges;
526 int i, status_found = 0;
527 int num_entries;
528
529 if (w25_range_table(flash, &w25q_ranges, &num_entries)) return -1;
530 for (i = 0; i < num_entries; i++) {
531 int bp;
532
533 bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
534 msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
535 bp, w25q_ranges[i].bp,
536 status->tb, w25q_ranges[i].tb,
537 status->sec, w25q_ranges[i].sec);
538 if ((bp == w25q_ranges[i].bp) &&
539 (status->tb == w25q_ranges[i].tb) &&
540 (status->sec == w25q_ranges[i].sec)) {
541 *start = w25q_ranges[i].range.start;
542 *len = w25q_ranges[i].range.len;
543
544 status_found = 1;
545 break;
546 }
547 }
548
549 if (!status_found) {
550 msg_cerr("matching status not found\n");
551 return -1;
552 }
553 return 0;
554}
555
Louis Yung-Chieh Lo52aa9302010-09-06 10:45:02 +0800556/* Since most chips we use must be WREN-ed before WRSR,
557 * we copy a write status function here before we have a good solution. */
558static int spi_write_status_register_WREN(int status)
559{
560 int result;
561 struct spi_command cmds[] = {
562 {
563 /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
564 .writecnt = JEDEC_WREN_OUTSIZE,
565 .writearr = (const unsigned char[]){ JEDEC_WREN },
566 .readcnt = 0,
567 .readarr = NULL,
568 }, {
569 .writecnt = JEDEC_WRSR_OUTSIZE,
570 .writearr = (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status },
571 .readcnt = 0,
572 .readarr = NULL,
573 }, {
574 .writecnt = 0,
575 .writearr = NULL,
576 .readcnt = 0,
577 .readarr = NULL,
578 }};
579
580 result = spi_send_multicommand(cmds);
581 if (result) {
582 msg_cerr("%s failed during command execution\n",
583 __func__);
584 }
Louis Yung-Chieh Lo96222b12010-11-01 11:48:11 +0800585
586 /* WRSR performs a self-timed erase before the changes take effect. */
587 programmer_delay(WRITE_STATUS_REGISTER_DELAY);
588
Louis Yung-Chieh Lo52aa9302010-09-06 10:45:02 +0800589 return result;
590}
591
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800592/* Given a [start, len], this function calls w25_range_to_status() to convert
593 * it to flash-chip-specific range bits, then sets into status register.
594 */
David Hendricksd494b0a2010-08-16 16:28:50 -0700595static int w25_set_range(struct flashchip *flash,
596 unsigned int start, unsigned int len)
597{
598 struct w25q_status status;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800599 int tmp = 0;
600 int expected = 0;
David Hendricksd494b0a2010-08-16 16:28:50 -0700601
602 memset(&status, 0, sizeof(status));
603 tmp = spi_read_status_register();
604 memcpy(&status, &tmp, 1);
605 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
606
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800607 if (w25_range_to_status(flash, start, len, &status)) return -1;
David Hendricksf7924d12010-06-10 21:26:44 -0700608
609 msg_cdbg("status.busy: %x\n", status.busy);
610 msg_cdbg("status.wel: %x\n", status.wel);
611 msg_cdbg("status.bp0: %x\n", status.bp0);
612 msg_cdbg("status.bp1: %x\n", status.bp1);
613 msg_cdbg("status.bp2: %x\n", status.bp2);
614 msg_cdbg("status.tb: %x\n", status.tb);
615 msg_cdbg("status.sec: %x\n", status.sec);
616 msg_cdbg("status.srp0: %x\n", status.srp0);
617
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800618 memcpy(&expected, &status, sizeof(status));
619 spi_write_status_register_WREN(expected);
David Hendricksf7924d12010-06-10 21:26:44 -0700620
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800621 tmp = spi_read_status_register();
622 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
623 if ((tmp & MASK_WP_AREA) == (expected & MASK_WP_AREA)) {
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800624 return 0;
625 } else {
David Hendricksc801adb2010-12-09 16:58:56 -0800626 msg_cerr("expected=0x%02x, but actual=0x%02x.\n",
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800627 expected, tmp);
628 return 1;
629 }
David Hendricksf7924d12010-06-10 21:26:44 -0700630}
631
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800632/* Print out the current status register value with human-readable text. */
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800633static int w25_wp_status(struct flashchip *flash)
634{
635 struct w25q_status status;
636 int tmp;
David Hendricksce8ded32010-10-08 11:23:38 -0700637 unsigned int start, len;
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800638 int ret = 0;
639
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800640 memset(&status, 0, sizeof(status));
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800641 tmp = spi_read_status_register();
642 /* FIXME: this is NOT endian-free copy. */
643 memcpy(&status, &tmp, 1);
644 msg_cinfo("WP: status: 0x%02x\n", tmp);
645 msg_cinfo("WP: status.srp0: %x\n", status.srp0);
646 msg_cinfo("WP: write protect is %s.\n",
647 status.srp0 ? "enabled" : "disabled");
648
649 msg_cinfo("WP: write protect range: ");
650 if (w25_status_to_range(flash, &status, &start, &len)) {
651 msg_cinfo("(cannot resolve the range)\n");
652 ret = -1;
653 } else {
654 msg_cinfo("start=0x%08x, len=0x%08x\n", start, len);
655 }
656
657 return ret;
658}
659
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800660/* Set/clear the SRP0 bit in the status register. */
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +0800661static int w25_set_srp0(struct flashchip *flash, int enable)
David Hendricksf7924d12010-06-10 21:26:44 -0700662{
663 struct w25q_status status;
664 int tmp = 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800665 int expected = 0;
David Hendricksf7924d12010-06-10 21:26:44 -0700666
667 memset(&status, 0, sizeof(status));
668 tmp = spi_read_status_register();
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800669 /* FIXME: this is NOT endian-free copy. */
David Hendricksf7924d12010-06-10 21:26:44 -0700670 memcpy(&status, &tmp, 1);
671 msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp);
672
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +0800673 status.srp0 = enable ? 1 : 0;
Louis Yung-Chieh Lo165b4642010-11-26 16:35:26 +0800674 memcpy(&expected, &status, sizeof(status));
675 spi_write_status_register_WREN(expected);
676
677 tmp = spi_read_status_register();
678 msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp);
679 if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA))
680 return 1;
David Hendricksf7924d12010-06-10 21:26:44 -0700681
682 return 0;
683}
684
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +0800685static int w25_enable_writeprotect(struct flashchip *flash)
686{
687 int ret;
688
689 ret = w25_set_srp0(flash, 1);
David Hendricksc801adb2010-12-09 16:58:56 -0800690 if (ret)
691 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +0800692 return ret;
693}
694
695static int w25_disable_writeprotect(struct flashchip *flash)
696{
697 int ret;
698
699 ret = w25_set_srp0(flash, 0);
David Hendricksc801adb2010-12-09 16:58:56 -0800700 if (ret)
701 msg_cerr("%s(): error=%d.\n", __func__, ret);
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +0800702 return ret;
703}
704
David Hendricksf7924d12010-06-10 21:26:44 -0700705struct wp wp_w25 = {
706 .set_range = w25_set_range,
707 .enable = w25_enable_writeprotect,
Louis Yung-Chieh Loc19d3c52010-10-08 11:59:16 +0800708 .disable = w25_disable_writeprotect,
Louis Yung-Chieh Loa92e8b22010-10-08 13:31:27 +0800709 .wp_status = w25_wp_status,
David Hendricksf7924d12010-06-10 21:26:44 -0700710};
David Hendricksf8f00c72011-02-01 12:39:46 -0800711
712