blob: f6d19bc530a9629fab3396017fcd00f9a18f2507 [file] [log] [blame]
David Hendricks398714f2014-07-03 17:49:41 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2014 Google Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * Neither the name of Google or the names of contributors or
18 * licensors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * This software is provided "AS IS," without a warranty of any kind.
22 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
23 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
24 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
25 * GOOGLE INC AND ITS LICENSORS SHALL NOT BE LIABLE
26 * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
27 * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
28 * GOOGLE OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
29 * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
30 * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
31 * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
32 * EVEN IF GOOGLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
33 *
David Hendricks43d9afd2015-03-13 20:54:23 -070034 * s25f.c - Helper functions for Spansion S25FL and S25FS SPI flash chips.
Vadim Bendebury3a501162014-10-21 20:38:13 -070035 * Uses 24 bit addressing for the FS chips and 32 bit addressing for the FL
36 * chips (which is required by the overlayed sector size devices).
37 * TODO: Implement fancy hybrid sector architecture helpers.
David Hendricks398714f2014-07-03 17:49:41 -070038 */
39
Vadim Bendebury3a501162014-10-21 20:38:13 -070040#include <string.h>
41
David Hendricks398714f2014-07-03 17:49:41 -070042#include "chipdrivers.h"
43#include "spi.h"
David Hendricks43d9afd2015-03-13 20:54:23 -070044#include "writeprotect.h"
David Hendricks398714f2014-07-03 17:49:41 -070045
David Hendricks43d9afd2015-03-13 20:54:23 -070046/*
47 * RDAR and WRAR are supported on chips which have more than one set of status
48 * and control registers and take an address of the register to read/write.
49 * WRR, RDSR2, and RDCR are used on chips with a more limited set of control/
50 * status registers.
51 *
52 * WRR is somewhat peculiar. It shares the same opcode as JEDEC_WRSR, and if
53 * given one data byte (following the opcode) it acts the same way. If it's
54 * given two data bytes, the first data byte overwrites status register 1
55 * and the second data byte overwrites config register 1.
56 */
57#define CMD_WRR 0x01
58#define CMD_WRDI 0x04
59#define CMD_RDSR2 0x07 /* note: read SR1 with JEDEC RDSR opcode */
60#define CMD_RDCR 0x35
David Hendricks398714f2014-07-03 17:49:41 -070061#define CMD_RDAR 0x65
62#define CMD_WRAR 0x71
David Hendricks43d9afd2015-03-13 20:54:23 -070063
64/* TODO: For now, commands which use an address assume 24-bit addressing */
65#define CMD_WRR_LEN 3
66#define CMD_WRDI_LEN 1
David Hendricks398714f2014-07-03 17:49:41 -070067#define CMD_RDAR_LEN 4
68#define CMD_WRAR_LEN 5
69
70#define CMD_RSTEN 0x66
71#define CMD_RST 0x99
72
David Hendricksa9884852014-12-11 15:31:12 -080073#define CR1NV_ADDR 0x000002
David Hendricks43d9afd2015-03-13 20:54:23 -070074#define CR1_BPNV_O (1 << 3)
75#define CR1_TBPROT_O (1 << 5)
David Hendricks398714f2014-07-03 17:49:41 -070076#define CR3NV_ADDR 0x000004
77#define CR3NV_20H_NV (1 << 3)
78
David Hendricks43d9afd2015-03-13 20:54:23 -070079/* See "Embedded Algorithm Performance Tables for additional timing specs. */
80#define T_W 145 * 1000 /* NV register write time (145ms) */
81#define T_RPH 35 /* Reset pulse hold time (35us) */
82#define S25FS_T_SE 145 * 1000 /* Sector Erase Time (145ms) */
83#define S25FL_T_SE 130 * 1000 /* Sector Erase Time (130ms) */
84
Souvik Ghoshd75cd672016-06-17 14:21:39 -070085static int s25f_legacy_software_reset(const struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -070086{
87 int result;
88 struct spi_command cmds[] = {
89 {
90 .writecnt = 1,
91 .writearr = (const unsigned char[]){ CMD_RSTEN },
92 .readcnt = 0,
93 .readarr = NULL,
94 }, {
95 .writecnt = 1,
96 .writearr = (const unsigned char[]){ 0xf0 },
97 .readcnt = 0,
98 .readarr = NULL,
99 }, {
100 .writecnt = 0,
101 .writearr = NULL,
102 .readcnt = 0,
103 .readarr = NULL,
104 }};
105
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700106 result = spi_send_multicommand(flash, cmds);
David Hendricks43d9afd2015-03-13 20:54:23 -0700107 if (result) {
108 msg_cerr("%s failed during command execution\n", __func__);
109 return result;
110 }
111
112 /* Allow time for reset command to execute. The datasheet specifies
113 * Trph = 35us, double that to be safe. */
114 programmer_delay(T_RPH * 2);
115
116 return 0;
117}
118
119/* "Legacy software reset" is disabled by default on S25FS, use this instead. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700120static int s25fs_software_reset(struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -0700121{
122 int result;
123 struct spi_command cmds[] = {
124 {
125 .writecnt = 1,
126 .writearr = (const unsigned char[]){ CMD_RSTEN },
127 .readcnt = 0,
128 .readarr = NULL,
129 }, {
130 .writecnt = 1,
131 .writearr = (const unsigned char[]){ CMD_RST },
132 .readcnt = 0,
133 .readarr = NULL,
134 }, {
135 .writecnt = 0,
136 .writearr = NULL,
137 .readcnt = 0,
138 .readarr = NULL,
139 }};
140
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700141 result = spi_send_multicommand(flash, cmds);
David Hendricks43d9afd2015-03-13 20:54:23 -0700142 if (result) {
143 msg_cerr("%s failed during command execution\n", __func__);
144 return result;
145 }
146
147 /* Allow time for reset command to execute. Double tRPH to be safe. */
148 programmer_delay(T_RPH * 2);
149
150 return 0;
151}
152
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700153static int s25f_poll_status(const struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -0700154{
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530155 uint8_t tmp = spi_read_status_register(flash);
David Hendricks43d9afd2015-03-13 20:54:23 -0700156
157 while (tmp & JEDEC_RDSR_BIT_WIP) {
158 /*
159 * The WIP bit on S25F chips remains set to 1 if erase or
160 * programming errors occur, so we must check for those
161 * errors here. If an error is encountered, do a software
162 * reset to clear WIP and other volatile bits, otherwise
163 * the chip will be unresponsive to further commands.
164 */
165 if (tmp & JEDEC_RDSR_BIT_ERASE_ERR) {
166 msg_cerr("Erase error occurred\n");
167 s25f_legacy_software_reset(flash);
168 return -1;
169 }
170
171 if (tmp & (1 << 6)) {
172 msg_cerr("Programming error occurred\n");
173 s25f_legacy_software_reset(flash);
174 return -1;
175 }
176
177 programmer_delay(1000 * 10);
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530178 tmp = spi_read_status_register(flash);
David Hendricks43d9afd2015-03-13 20:54:23 -0700179 }
180
181 return 0;
182}
183
184/* "Read Any Register" instruction only supported on S25FS */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700185static int s25fs_read_cr(const struct flashctx *flash, uint32_t addr)
David Hendricks398714f2014-07-03 17:49:41 -0700186{
187 int result;
188 uint8_t cfg;
189 /* By default, 8 dummy cycles are necessary for variable-latency
190 commands such as RDAR (see CR2NV[3:0]). */
191 unsigned char read_cr_cmd[] = {
192 CMD_RDAR,
193 (addr >> 16) & 0xff,
194 (addr >> 8) & 0xff,
195 (addr & 0xff),
196 0x00, 0x00, 0x00, 0x00,
197 0x00, 0x00, 0x00, 0x00,
198 };
199
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700200 result = spi_send_command(flash, sizeof(read_cr_cmd), 1, read_cr_cmd, &cfg);
David Hendricks398714f2014-07-03 17:49:41 -0700201 if (result) {
202 msg_cerr("%s failed during command execution at address 0x%x\n",
203 __func__, addr);
David Hendricks688b5e22014-12-12 11:15:44 -0800204 return -1;
David Hendricks398714f2014-07-03 17:49:41 -0700205 }
206
207 return cfg;
208}
209
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700210static int s25f_read_cr1(const struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -0700211{
212 int result;
213 uint8_t cfg;
214 unsigned char read_cr_cmd[] = { CMD_RDCR };
215
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700216 result = spi_send_command(flash, sizeof(read_cr_cmd), 1, read_cr_cmd, &cfg);
David Hendricks43d9afd2015-03-13 20:54:23 -0700217 if (result) {
218 msg_cerr("%s failed during command execution\n", __func__);
219 return -1;
220 }
221
222 return cfg;
223}
224
225/* "Write Any Register" instruction only supported on S25FS */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700226static int s25fs_write_cr(const struct flashctx *flash,
David Hendricks43d9afd2015-03-13 20:54:23 -0700227 uint32_t addr, uint8_t data)
David Hendricks398714f2014-07-03 17:49:41 -0700228{
229 int result;
230 struct spi_command cmds[] = {
231 {
232 .writecnt = JEDEC_WREN_OUTSIZE,
233 .writearr = (const unsigned char[]){ JEDEC_WREN },
234 .readcnt = 0,
235 .readarr = NULL,
236 }, {
237 .writecnt = CMD_WRAR_LEN,
238 .writearr = (const unsigned char[]){
239 CMD_WRAR,
240 (addr >> 16) & 0xff,
241 (addr >> 8) & 0xff,
242 (addr & 0xff),
243 data
244 },
245 .readcnt = 0,
246 .readarr = NULL,
247 }, {
248 .writecnt = 0,
249 .writearr = NULL,
250 .readcnt = 0,
251 .readarr = NULL,
252 }};
253
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700254 result = spi_send_multicommand(flash, cmds);
David Hendricks398714f2014-07-03 17:49:41 -0700255 if (result) {
256 msg_cerr("%s failed during command execution at address 0x%x\n",
257 __func__, addr);
David Hendricks688b5e22014-12-12 11:15:44 -0800258 return -1;
David Hendricks398714f2014-07-03 17:49:41 -0700259 }
260
David Hendricks43d9afd2015-03-13 20:54:23 -0700261 programmer_delay(T_W);
262 return s25f_poll_status(flash);
David Hendricks398714f2014-07-03 17:49:41 -0700263}
264
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700265static int s25f_write_cr1(const struct flashctx *flash, uint8_t data)
David Hendricks398714f2014-07-03 17:49:41 -0700266{
267 int result;
268 struct spi_command cmds[] = {
269 {
David Hendricks43d9afd2015-03-13 20:54:23 -0700270 .writecnt = JEDEC_WREN_OUTSIZE,
271 .writearr = (const unsigned char[]){ JEDEC_WREN },
David Hendricks398714f2014-07-03 17:49:41 -0700272 .readcnt = 0,
273 .readarr = NULL,
274 }, {
David Hendricks43d9afd2015-03-13 20:54:23 -0700275 .writecnt = CMD_WRR_LEN,
276 .writearr = (const unsigned char[]){
277 CMD_WRR,
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530278 spi_read_status_register(flash),
David Hendricks43d9afd2015-03-13 20:54:23 -0700279 data,
280 },
David Hendricks398714f2014-07-03 17:49:41 -0700281 .readcnt = 0,
282 .readarr = NULL,
283 }, {
284 .writecnt = 0,
285 .writearr = NULL,
286 .readcnt = 0,
287 .readarr = NULL,
288 }};
289
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700290 result = spi_send_multicommand(flash, cmds);
David Hendricks398714f2014-07-03 17:49:41 -0700291 if (result) {
292 msg_cerr("%s failed during command execution\n", __func__);
David Hendricks43d9afd2015-03-13 20:54:23 -0700293 return -1;
David Hendricks398714f2014-07-03 17:49:41 -0700294 }
295
David Hendricks43d9afd2015-03-13 20:54:23 -0700296 programmer_delay(T_W);
297 return s25f_poll_status(flash);
David Hendricks398714f2014-07-03 17:49:41 -0700298}
299
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700300static int s25fs_restore_cr3nv(struct flashctx *flash, uint8_t cfg)
David Hendricks398714f2014-07-03 17:49:41 -0700301{
302 int ret = 0;
303
304 msg_cdbg("Restoring CR3NV value to 0x%02x\n", cfg);
David Hendricks636c74a2014-12-12 11:30:00 -0800305 ret |= s25fs_write_cr(flash, CR3NV_ADDR, cfg);
David Hendricks398714f2014-07-03 17:49:41 -0700306 ret |= s25fs_software_reset(flash);
307 return ret;
308}
309
David Hendricksa9884852014-12-11 15:31:12 -0800310/* returns state of top/bottom block protection, or <0 to indicate error */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700311static int s25f_get_tbprot_o(const struct flashctx *flash)
David Hendricksa9884852014-12-11 15:31:12 -0800312{
David Hendricks43d9afd2015-03-13 20:54:23 -0700313 int cr1 = s25f_read_cr1(flash);
David Hendricksa9884852014-12-11 15:31:12 -0800314
David Hendricks43d9afd2015-03-13 20:54:23 -0700315 if (cr1 < 0)
David Hendricksa9884852014-12-11 15:31:12 -0800316 return -1;
317
318 /*
319 * 1 = BP starts at bottom (low address)
320 * 0 = BP start at top (high address)
321 */
David Hendricks43d9afd2015-03-13 20:54:23 -0700322 return cr1 & CR1_TBPROT_O ? 1 : 0;
David Hendricksa9884852014-12-11 15:31:12 -0800323}
324
David Hendricks148a4bf2015-03-13 21:02:42 -0700325/* fills modifier_bits struct, returns 0 to indicate success */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700326int s25f_get_modifier_bits(const struct flashctx *flash,
David Hendricks148a4bf2015-03-13 21:02:42 -0700327 struct generic_modifier_bits *m)
328{
329 int tmp;
330
331 memset(m, 0, sizeof(*m));
332
333 tmp = s25f_get_tbprot_o(flash);
334 if (tmp < 0)
335 return -1;
336 m->tb = tmp;
337
338 return 0;
339}
340
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700341int s25f_set_modifier_bits(const struct flashctx *flash,
David Hendricks148a4bf2015-03-13 21:02:42 -0700342 struct generic_modifier_bits *m)
343{
344 int cr1, cr1_orig;
345
346 cr1 = cr1_orig = s25f_read_cr1(flash);
347 if (cr1 < 0)
348 return -1;
349
350 /*
351 * Clear BPNV so that setting BP2-0 in status register gets
352 * written to non-volatile memory.
353 *
354 * For TBPROT:
355 * 1 = BP starts at bottom (low address)
356 * 0 = BP start at top (high address)
357 */
358 cr1 &= ~(CR1_BPNV_O | CR1_TBPROT_O);
359 cr1 |= m->tb ? CR1_TBPROT_O : 0;
360
361 if (cr1 != cr1_orig) {
362 msg_cdbg("%s: setting cr1 bits to 0x%02x\n", __func__, cr1);
363 if (s25f_write_cr1(flash, cr1) < 0)
364 return -1;
365 if (s25f_read_cr1(flash) != cr1) {
366 msg_cerr("%s: failed to set CR1 value\n", __func__);
367 return -1;
368 }
369 } else {
370 msg_cdbg("%s: cr1 bits already match desired value: "
371 "0x%02x\n", __func__, cr1);
372 }
373
374 return 0;
375}
376
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700377int s25fs_block_erase_d8(struct flashctx *flash,
David Hendricks398714f2014-07-03 17:49:41 -0700378 unsigned int addr, unsigned int blocklen)
379{
380 unsigned char cfg;
381 int result;
382 static int cr3nv_checked = 0;
383
384 struct spi_command erase_cmds[] = {
385 {
386 .writecnt = JEDEC_WREN_OUTSIZE,
387 .writearr = (const unsigned char[]){ JEDEC_WREN },
388 .readcnt = 0,
389 .readarr = NULL,
390 }, {
391 .writecnt = JEDEC_BE_D8_OUTSIZE,
392 .writearr = (const unsigned char[]){
393 JEDEC_BE_D8,
394 (addr >> 16) & 0xff,
395 (addr >> 8) & 0xff,
396 (addr & 0xff)
397 },
398 .readcnt = 0,
399 .readarr = NULL,
400 }, {
401 .writecnt = 0,
402 .writearr = NULL,
403 .readcnt = 0,
404 .readarr = NULL,
405 }};
406
407 /* Check if hybrid sector architecture is in use and, if so,
408 * switch to uniform sectors. */
409 if (!cr3nv_checked) {
David Hendricks636c74a2014-12-12 11:30:00 -0800410 cfg = s25fs_read_cr(flash, CR3NV_ADDR);
David Hendricks398714f2014-07-03 17:49:41 -0700411 if (!(cfg & CR3NV_20H_NV)) {
David Hendricks636c74a2014-12-12 11:30:00 -0800412 s25fs_write_cr(flash, CR3NV_ADDR, cfg | CR3NV_20H_NV);
David Hendricks398714f2014-07-03 17:49:41 -0700413 s25fs_software_reset(flash);
414
David Hendricks636c74a2014-12-12 11:30:00 -0800415 cfg = s25fs_read_cr(flash, CR3NV_ADDR);
David Hendricks398714f2014-07-03 17:49:41 -0700416 if (!(cfg & CR3NV_20H_NV)) {
417 msg_cerr("%s: Unable to enable uniform "
418 "block sizes.\n", __func__);
419 return 1;
420 }
421
422 msg_cdbg("\n%s: CR3NV updated (0x%02x -> 0x%02x)\n",
423 __func__, cfg,
David Hendricks636c74a2014-12-12 11:30:00 -0800424 s25fs_read_cr(flash, CR3NV_ADDR));
David Hendricks398714f2014-07-03 17:49:41 -0700425 /* Restore CR3V when flashrom exits */
426 register_chip_restore(s25fs_restore_cr3nv, flash, cfg);
427 }
428
429 cr3nv_checked = 1;
430 }
431
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700432 result = spi_send_multicommand(flash, erase_cmds);
David Hendricks398714f2014-07-03 17:49:41 -0700433 if (result) {
434 msg_cerr("%s failed during command execution at address 0x%x\n",
435 __func__, addr);
436 return result;
437 }
438
David Hendricks43d9afd2015-03-13 20:54:23 -0700439 programmer_delay(S25FS_T_SE);
440 return s25f_poll_status(flash);
David Hendricks398714f2014-07-03 17:49:41 -0700441}
Vadim Bendebury3a501162014-10-21 20:38:13 -0700442
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700443int s25fl_block_erase(struct flashctx *flash,
Vadim Bendebury3a501162014-10-21 20:38:13 -0700444 unsigned int addr, unsigned int blocklen)
445{
Vadim Bendebury3a501162014-10-21 20:38:13 -0700446 int result;
Vadim Bendebury3a501162014-10-21 20:38:13 -0700447
448 struct spi_command erase_cmds[] = {
449 {
450 .writecnt = JEDEC_WREN_OUTSIZE,
451 .writearr = (const unsigned char[]){
452 JEDEC_WREN
453 },
454 .readcnt = 0,
455 .readarr = NULL,
456 }, {
457 .writecnt = JEDEC_BE_DC_OUTSIZE,
458 .writearr = (const unsigned char[]){
459 JEDEC_BE_DC,
460 (addr >> 24) & 0xff,
461 (addr >> 16) & 0xff,
462 (addr >> 8) & 0xff,
463 (addr & 0xff)
464 },
465 .readcnt = 0,
466 .readarr = NULL,
467 }, {
468 .writecnt = 0,
469 .readcnt = 0,
470 }
471 };
472
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700473 result = spi_send_multicommand(flash, erase_cmds);
Vadim Bendebury3a501162014-10-21 20:38:13 -0700474 if (result) {
475 msg_cerr("%s failed during command execution at address 0x%x\n",
476 __func__, addr);
477 return result;
478 }
479
David Hendricks43d9afd2015-03-13 20:54:23 -0700480 programmer_delay(S25FL_T_SE);
481 return s25f_poll_status(flash);
Vadim Bendebury3a501162014-10-21 20:38:13 -0700482}
483
484
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700485int probe_spi_big_spansion(struct flashctx *flash)
Vadim Bendebury3a501162014-10-21 20:38:13 -0700486{
487 static const unsigned char cmd = JEDEC_RDID;
488 int ret;
489 unsigned char dev_id[6]; /* We care only about 6 first bytes */
490
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700491 ret = spi_send_command(flash, sizeof(cmd), sizeof(dev_id), &cmd, dev_id);
Vadim Bendebury3a501162014-10-21 20:38:13 -0700492
493 if (!ret) {
494 int i;
495
496 for (i = 0; i < sizeof(dev_id); i++)
497 msg_gdbg(" 0x%02x", dev_id[i]);
498 msg_gdbg(".\n");
499
500 if (dev_id[0] == flash->manufacture_id) {
501 union {
502 uint8_t array[4];
503 uint32_t whole;
504 } model_id;
505
506 /*
507 * The structure of the RDID output is as follows:
508 *
509 * offset value meaning
510 * 00h 01h Manufacturer ID for Spansion
511 * 01h 20h 128 Mb capacity
512 * 01h 02h 256 Mb capacity
513 * 02h 18h 128 Mb capacity
514 * 02h 19h 256 Mb capacity
515 * 03h 4Dh Full size of the RDID output (ignored)
516 * 04h 00h FS: 256-kB physical sectors
517 * 04h 01h FS: 64-kB physical sectors
518 * 04h 00h FL: 256-kB physical sectors
519 * 04h 01h FL: Mix of 64-kB and 4KB overlayed sectors
520 * 05h 80h FL family
521 * 05h 81h FS family
522 *
523 * Need to use bytes 1, 2, 4, and 5 to properly identify one of eight
524 * possible chips:
525 *
526 * 2 types * 2 possible sizes * 2 possible sector layouts
527 *
528 */
529 memcpy(model_id.array, dev_id + 1, 2);
530 memcpy(model_id.array + 2, dev_id + 4, 2);
531 if (be_to_cpu32(model_id.whole) == flash->model_id)
532 return 1;
533 }
534 }
535 return 0;
536}