blob: b0b1b6416864c1277128a15efe8f97c8ca40fb5e [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.
Nikolai Artemiev144243b2020-11-10 11:57:39 +110033 */
34
35/*
David Hendricks43d9afd2015-03-13 20:54:23 -070036 * s25f.c - Helper functions for Spansion S25FL and S25FS SPI flash chips.
Vadim Bendebury3a501162014-10-21 20:38:13 -070037 * Uses 24 bit addressing for the FS chips and 32 bit addressing for the FL
38 * chips (which is required by the overlayed sector size devices).
39 * TODO: Implement fancy hybrid sector architecture helpers.
David Hendricks398714f2014-07-03 17:49:41 -070040 */
41
Vadim Bendebury3a501162014-10-21 20:38:13 -070042#include <string.h>
43
David Hendricks398714f2014-07-03 17:49:41 -070044#include "chipdrivers.h"
45#include "spi.h"
David Hendricks43d9afd2015-03-13 20:54:23 -070046#include "writeprotect.h"
David Hendricks398714f2014-07-03 17:49:41 -070047
David Hendricks43d9afd2015-03-13 20:54:23 -070048/*
49 * RDAR and WRAR are supported on chips which have more than one set of status
50 * and control registers and take an address of the register to read/write.
51 * WRR, RDSR2, and RDCR are used on chips with a more limited set of control/
52 * status registers.
53 *
54 * WRR is somewhat peculiar. It shares the same opcode as JEDEC_WRSR, and if
55 * given one data byte (following the opcode) it acts the same way. If it's
56 * given two data bytes, the first data byte overwrites status register 1
57 * and the second data byte overwrites config register 1.
58 */
59#define CMD_WRR 0x01
60#define CMD_WRDI 0x04
61#define CMD_RDSR2 0x07 /* note: read SR1 with JEDEC RDSR opcode */
62#define CMD_RDCR 0x35
David Hendricks398714f2014-07-03 17:49:41 -070063#define CMD_RDAR 0x65
64#define CMD_WRAR 0x71
David Hendricks43d9afd2015-03-13 20:54:23 -070065
66/* TODO: For now, commands which use an address assume 24-bit addressing */
67#define CMD_WRR_LEN 3
68#define CMD_WRDI_LEN 1
David Hendricks398714f2014-07-03 17:49:41 -070069#define CMD_RDAR_LEN 4
70#define CMD_WRAR_LEN 5
71
72#define CMD_RSTEN 0x66
73#define CMD_RST 0x99
74
David Hendricksa9884852014-12-11 15:31:12 -080075#define CR1NV_ADDR 0x000002
David Hendricks43d9afd2015-03-13 20:54:23 -070076#define CR1_BPNV_O (1 << 3)
77#define CR1_TBPROT_O (1 << 5)
David Hendricks398714f2014-07-03 17:49:41 -070078#define CR3NV_ADDR 0x000004
79#define CR3NV_20H_NV (1 << 3)
80
David Hendricks43d9afd2015-03-13 20:54:23 -070081/* See "Embedded Algorithm Performance Tables for additional timing specs. */
82#define T_W 145 * 1000 /* NV register write time (145ms) */
83#define T_RPH 35 /* Reset pulse hold time (35us) */
84#define S25FS_T_SE 145 * 1000 /* Sector Erase Time (145ms) */
85#define S25FL_T_SE 130 * 1000 /* Sector Erase Time (130ms) */
86
Souvik Ghoshd75cd672016-06-17 14:21:39 -070087static int s25f_legacy_software_reset(const struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -070088{
89 int result;
90 struct spi_command cmds[] = {
91 {
92 .writecnt = 1,
93 .writearr = (const unsigned char[]){ CMD_RSTEN },
94 .readcnt = 0,
95 .readarr = NULL,
96 }, {
97 .writecnt = 1,
98 .writearr = (const unsigned char[]){ 0xf0 },
99 .readcnt = 0,
100 .readarr = NULL,
101 }, {
102 .writecnt = 0,
103 .writearr = NULL,
104 .readcnt = 0,
105 .readarr = NULL,
106 }};
107
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700108 result = spi_send_multicommand(flash, cmds);
David Hendricks43d9afd2015-03-13 20:54:23 -0700109 if (result) {
110 msg_cerr("%s failed during command execution\n", __func__);
111 return result;
112 }
113
114 /* Allow time for reset command to execute. The datasheet specifies
115 * Trph = 35us, double that to be safe. */
116 programmer_delay(T_RPH * 2);
117
118 return 0;
119}
120
121/* "Legacy software reset" is disabled by default on S25FS, use this instead. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700122static int s25fs_software_reset(struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -0700123{
124 int result;
125 struct spi_command cmds[] = {
126 {
127 .writecnt = 1,
128 .writearr = (const unsigned char[]){ CMD_RSTEN },
129 .readcnt = 0,
130 .readarr = NULL,
131 }, {
132 .writecnt = 1,
133 .writearr = (const unsigned char[]){ CMD_RST },
134 .readcnt = 0,
135 .readarr = NULL,
136 }, {
137 .writecnt = 0,
138 .writearr = NULL,
139 .readcnt = 0,
140 .readarr = NULL,
141 }};
142
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700143 result = spi_send_multicommand(flash, cmds);
David Hendricks43d9afd2015-03-13 20:54:23 -0700144 if (result) {
145 msg_cerr("%s failed during command execution\n", __func__);
146 return result;
147 }
148
149 /* Allow time for reset command to execute. Double tRPH to be safe. */
150 programmer_delay(T_RPH * 2);
151
152 return 0;
153}
154
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700155static int s25f_poll_status(const struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -0700156{
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530157 uint8_t tmp = spi_read_status_register(flash);
David Hendricks43d9afd2015-03-13 20:54:23 -0700158
Edward O'Callaghan8b5e4732019-03-05 15:27:53 +1100159 while (tmp & SPI_SR_WIP) {
David Hendricks43d9afd2015-03-13 20:54:23 -0700160 /*
161 * The WIP bit on S25F chips remains set to 1 if erase or
162 * programming errors occur, so we must check for those
163 * errors here. If an error is encountered, do a software
164 * reset to clear WIP and other volatile bits, otherwise
165 * the chip will be unresponsive to further commands.
166 */
Edward O'Callaghan1945f1e2019-03-18 13:12:51 +1100167 if (tmp & SPI_SR_ERA_ERR) {
David Hendricks43d9afd2015-03-13 20:54:23 -0700168 msg_cerr("Erase error occurred\n");
169 s25f_legacy_software_reset(flash);
170 return -1;
171 }
172
173 if (tmp & (1 << 6)) {
174 msg_cerr("Programming error occurred\n");
175 s25f_legacy_software_reset(flash);
176 return -1;
177 }
178
179 programmer_delay(1000 * 10);
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530180 tmp = spi_read_status_register(flash);
David Hendricks43d9afd2015-03-13 20:54:23 -0700181 }
182
183 return 0;
184}
185
186/* "Read Any Register" instruction only supported on S25FS */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700187static int s25fs_read_cr(const struct flashctx *flash, uint32_t addr)
David Hendricks398714f2014-07-03 17:49:41 -0700188{
189 int result;
190 uint8_t cfg;
191 /* By default, 8 dummy cycles are necessary for variable-latency
192 commands such as RDAR (see CR2NV[3:0]). */
193 unsigned char read_cr_cmd[] = {
Nikolai Artemiev144243b2020-11-10 11:57:39 +1100194 CMD_RDAR,
195 (addr >> 16) & 0xff,
196 (addr >> 8) & 0xff,
197 (addr & 0xff),
198 0x00, 0x00, 0x00, 0x00,
199 0x00, 0x00, 0x00, 0x00,
David Hendricks398714f2014-07-03 17:49:41 -0700200 };
201
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700202 result = spi_send_command(flash, sizeof(read_cr_cmd), 1, read_cr_cmd, &cfg);
David Hendricks398714f2014-07-03 17:49:41 -0700203 if (result) {
204 msg_cerr("%s failed during command execution at address 0x%x\n",
205 __func__, addr);
David Hendricks688b5e22014-12-12 11:15:44 -0800206 return -1;
David Hendricks398714f2014-07-03 17:49:41 -0700207 }
208
209 return cfg;
210}
211
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700212static int s25f_read_cr1(const struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -0700213{
214 int result;
215 uint8_t cfg;
216 unsigned char read_cr_cmd[] = { CMD_RDCR };
217
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700218 result = spi_send_command(flash, sizeof(read_cr_cmd), 1, read_cr_cmd, &cfg);
David Hendricks43d9afd2015-03-13 20:54:23 -0700219 if (result) {
220 msg_cerr("%s failed during command execution\n", __func__);
221 return -1;
222 }
223
224 return cfg;
225}
226
227/* "Write Any Register" instruction only supported on S25FS */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700228static int s25fs_write_cr(const struct flashctx *flash,
David Hendricks43d9afd2015-03-13 20:54:23 -0700229 uint32_t addr, uint8_t data)
David Hendricks398714f2014-07-03 17:49:41 -0700230{
231 int result;
232 struct spi_command cmds[] = {
233 {
234 .writecnt = JEDEC_WREN_OUTSIZE,
235 .writearr = (const unsigned char[]){ JEDEC_WREN },
236 .readcnt = 0,
237 .readarr = NULL,
238 }, {
239 .writecnt = CMD_WRAR_LEN,
240 .writearr = (const unsigned char[]){
241 CMD_WRAR,
242 (addr >> 16) & 0xff,
243 (addr >> 8) & 0xff,
244 (addr & 0xff),
245 data
246 },
247 .readcnt = 0,
248 .readarr = NULL,
249 }, {
250 .writecnt = 0,
251 .writearr = NULL,
252 .readcnt = 0,
253 .readarr = NULL,
254 }};
255
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700256 result = spi_send_multicommand(flash, cmds);
David Hendricks398714f2014-07-03 17:49:41 -0700257 if (result) {
258 msg_cerr("%s failed during command execution at address 0x%x\n",
259 __func__, addr);
David Hendricks688b5e22014-12-12 11:15:44 -0800260 return -1;
David Hendricks398714f2014-07-03 17:49:41 -0700261 }
262
David Hendricks43d9afd2015-03-13 20:54:23 -0700263 programmer_delay(T_W);
264 return s25f_poll_status(flash);
David Hendricks398714f2014-07-03 17:49:41 -0700265}
266
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700267static int s25f_write_cr1(const struct flashctx *flash, uint8_t data)
David Hendricks398714f2014-07-03 17:49:41 -0700268{
269 int result;
270 struct spi_command cmds[] = {
271 {
David Hendricks43d9afd2015-03-13 20:54:23 -0700272 .writecnt = JEDEC_WREN_OUTSIZE,
273 .writearr = (const unsigned char[]){ JEDEC_WREN },
David Hendricks398714f2014-07-03 17:49:41 -0700274 .readcnt = 0,
275 .readarr = NULL,
276 }, {
David Hendricks43d9afd2015-03-13 20:54:23 -0700277 .writecnt = CMD_WRR_LEN,
278 .writearr = (const unsigned char[]){
279 CMD_WRR,
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530280 spi_read_status_register(flash),
David Hendricks43d9afd2015-03-13 20:54:23 -0700281 data,
282 },
David Hendricks398714f2014-07-03 17:49:41 -0700283 .readcnt = 0,
284 .readarr = NULL,
285 }, {
286 .writecnt = 0,
287 .writearr = NULL,
288 .readcnt = 0,
289 .readarr = NULL,
290 }};
291
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700292 result = spi_send_multicommand(flash, cmds);
David Hendricks398714f2014-07-03 17:49:41 -0700293 if (result) {
294 msg_cerr("%s failed during command execution\n", __func__);
David Hendricks43d9afd2015-03-13 20:54:23 -0700295 return -1;
David Hendricks398714f2014-07-03 17:49:41 -0700296 }
297
David Hendricks43d9afd2015-03-13 20:54:23 -0700298 programmer_delay(T_W);
299 return s25f_poll_status(flash);
David Hendricks398714f2014-07-03 17:49:41 -0700300}
301
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700302static int s25fs_restore_cr3nv(struct flashctx *flash, uint8_t cfg)
David Hendricks398714f2014-07-03 17:49:41 -0700303{
304 int ret = 0;
305
306 msg_cdbg("Restoring CR3NV value to 0x%02x\n", cfg);
David Hendricks636c74a2014-12-12 11:30:00 -0800307 ret |= s25fs_write_cr(flash, CR3NV_ADDR, cfg);
David Hendricks398714f2014-07-03 17:49:41 -0700308 ret |= s25fs_software_reset(flash);
309 return ret;
310}
311
David Hendricksa9884852014-12-11 15:31:12 -0800312/* returns state of top/bottom block protection, or <0 to indicate error */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700313static int s25f_get_tbprot_o(const struct flashctx *flash)
David Hendricksa9884852014-12-11 15:31:12 -0800314{
David Hendricks43d9afd2015-03-13 20:54:23 -0700315 int cr1 = s25f_read_cr1(flash);
David Hendricksa9884852014-12-11 15:31:12 -0800316
David Hendricks43d9afd2015-03-13 20:54:23 -0700317 if (cr1 < 0)
David Hendricksa9884852014-12-11 15:31:12 -0800318 return -1;
319
320 /*
321 * 1 = BP starts at bottom (low address)
322 * 0 = BP start at top (high address)
323 */
David Hendricks43d9afd2015-03-13 20:54:23 -0700324 return cr1 & CR1_TBPROT_O ? 1 : 0;
David Hendricksa9884852014-12-11 15:31:12 -0800325}
326
David Hendricks148a4bf2015-03-13 21:02:42 -0700327/* fills modifier_bits struct, returns 0 to indicate success */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700328int s25f_get_modifier_bits(const struct flashctx *flash,
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +1100329 struct modifier_bits *m)
David Hendricks148a4bf2015-03-13 21:02:42 -0700330{
331 int tmp;
332
333 memset(m, 0, sizeof(*m));
334
335 tmp = s25f_get_tbprot_o(flash);
336 if (tmp < 0)
337 return -1;
338 m->tb = tmp;
339
340 return 0;
341}
342
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700343int s25f_set_modifier_bits(const struct flashctx *flash,
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +1100344 struct modifier_bits *m)
David Hendricks148a4bf2015-03-13 21:02:42 -0700345{
346 int cr1, cr1_orig;
347
348 cr1 = cr1_orig = s25f_read_cr1(flash);
349 if (cr1 < 0)
350 return -1;
351
352 /*
353 * Clear BPNV so that setting BP2-0 in status register gets
354 * written to non-volatile memory.
355 *
356 * For TBPROT:
357 * 1 = BP starts at bottom (low address)
358 * 0 = BP start at top (high address)
359 */
360 cr1 &= ~(CR1_BPNV_O | CR1_TBPROT_O);
361 cr1 |= m->tb ? CR1_TBPROT_O : 0;
362
363 if (cr1 != cr1_orig) {
364 msg_cdbg("%s: setting cr1 bits to 0x%02x\n", __func__, cr1);
365 if (s25f_write_cr1(flash, cr1) < 0)
366 return -1;
367 if (s25f_read_cr1(flash) != cr1) {
368 msg_cerr("%s: failed to set CR1 value\n", __func__);
369 return -1;
370 }
371 } else {
372 msg_cdbg("%s: cr1 bits already match desired value: "
373 "0x%02x\n", __func__, cr1);
374 }
375
376 return 0;
377}
378
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700379int s25fs_block_erase_d8(struct flashctx *flash,
David Hendricks398714f2014-07-03 17:49:41 -0700380 unsigned int addr, unsigned int blocklen)
381{
382 unsigned char cfg;
383 int result;
384 static int cr3nv_checked = 0;
385
386 struct spi_command erase_cmds[] = {
387 {
388 .writecnt = JEDEC_WREN_OUTSIZE,
389 .writearr = (const unsigned char[]){ JEDEC_WREN },
390 .readcnt = 0,
391 .readarr = NULL,
392 }, {
393 .writecnt = JEDEC_BE_D8_OUTSIZE,
394 .writearr = (const unsigned char[]){
395 JEDEC_BE_D8,
396 (addr >> 16) & 0xff,
397 (addr >> 8) & 0xff,
398 (addr & 0xff)
399 },
400 .readcnt = 0,
401 .readarr = NULL,
402 }, {
403 .writecnt = 0,
404 .writearr = NULL,
405 .readcnt = 0,
406 .readarr = NULL,
407 }};
408
409 /* Check if hybrid sector architecture is in use and, if so,
410 * switch to uniform sectors. */
411 if (!cr3nv_checked) {
David Hendricks636c74a2014-12-12 11:30:00 -0800412 cfg = s25fs_read_cr(flash, CR3NV_ADDR);
David Hendricks398714f2014-07-03 17:49:41 -0700413 if (!(cfg & CR3NV_20H_NV)) {
David Hendricks636c74a2014-12-12 11:30:00 -0800414 s25fs_write_cr(flash, CR3NV_ADDR, cfg | CR3NV_20H_NV);
David Hendricks398714f2014-07-03 17:49:41 -0700415 s25fs_software_reset(flash);
416
David Hendricks636c74a2014-12-12 11:30:00 -0800417 cfg = s25fs_read_cr(flash, CR3NV_ADDR);
David Hendricks398714f2014-07-03 17:49:41 -0700418 if (!(cfg & CR3NV_20H_NV)) {
419 msg_cerr("%s: Unable to enable uniform "
420 "block sizes.\n", __func__);
421 return 1;
422 }
423
424 msg_cdbg("\n%s: CR3NV updated (0x%02x -> 0x%02x)\n",
425 __func__, cfg,
David Hendricks636c74a2014-12-12 11:30:00 -0800426 s25fs_read_cr(flash, CR3NV_ADDR));
David Hendricks398714f2014-07-03 17:49:41 -0700427 /* Restore CR3V when flashrom exits */
428 register_chip_restore(s25fs_restore_cr3nv, flash, cfg);
429 }
430
431 cr3nv_checked = 1;
432 }
433
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700434 result = spi_send_multicommand(flash, erase_cmds);
David Hendricks398714f2014-07-03 17:49:41 -0700435 if (result) {
436 msg_cerr("%s failed during command execution at address 0x%x\n",
437 __func__, addr);
438 return result;
439 }
440
David Hendricks43d9afd2015-03-13 20:54:23 -0700441 programmer_delay(S25FS_T_SE);
442 return s25f_poll_status(flash);
David Hendricks398714f2014-07-03 17:49:41 -0700443}
Vadim Bendebury3a501162014-10-21 20:38:13 -0700444
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700445int s25fl_block_erase(struct flashctx *flash,
Vadim Bendebury3a501162014-10-21 20:38:13 -0700446 unsigned int addr, unsigned int blocklen)
447{
Vadim Bendebury3a501162014-10-21 20:38:13 -0700448 int result;
Vadim Bendebury3a501162014-10-21 20:38:13 -0700449
450 struct spi_command erase_cmds[] = {
451 {
452 .writecnt = JEDEC_WREN_OUTSIZE,
453 .writearr = (const unsigned char[]){
454 JEDEC_WREN
455 },
456 .readcnt = 0,
457 .readarr = NULL,
458 }, {
459 .writecnt = JEDEC_BE_DC_OUTSIZE,
460 .writearr = (const unsigned char[]){
461 JEDEC_BE_DC,
462 (addr >> 24) & 0xff,
463 (addr >> 16) & 0xff,
464 (addr >> 8) & 0xff,
465 (addr & 0xff)
466 },
467 .readcnt = 0,
468 .readarr = NULL,
469 }, {
470 .writecnt = 0,
471 .readcnt = 0,
472 }
473 };
474
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700475 result = spi_send_multicommand(flash, erase_cmds);
Vadim Bendebury3a501162014-10-21 20:38:13 -0700476 if (result) {
477 msg_cerr("%s failed during command execution at address 0x%x\n",
478 __func__, addr);
479 return result;
480 }
481
David Hendricks43d9afd2015-03-13 20:54:23 -0700482 programmer_delay(S25FL_T_SE);
483 return s25f_poll_status(flash);
Vadim Bendebury3a501162014-10-21 20:38:13 -0700484}
485
486
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700487int probe_spi_big_spansion(struct flashctx *flash)
Vadim Bendebury3a501162014-10-21 20:38:13 -0700488{
489 static const unsigned char cmd = JEDEC_RDID;
Vadim Bendebury3a501162014-10-21 20:38:13 -0700490 unsigned char dev_id[6]; /* We care only about 6 first bytes */
491
Nikolai Artemiev077b66c2020-11-10 13:38:38 +1100492 if (spi_send_command(flash, sizeof(cmd), sizeof(dev_id), &cmd, dev_id))
493 return 0;
Vadim Bendebury3a501162014-10-21 20:38:13 -0700494
Nikolai Artemiev077b66c2020-11-10 13:38:38 +1100495 for (size_t i = 0; i < sizeof(dev_id); i++)
496 msg_gdbg(" 0x%02x", dev_id[i]);
497 msg_gdbg(".\n");
Vadim Bendebury3a501162014-10-21 20:38:13 -0700498
Vadim Bendebury3a501162014-10-21 20:38:13 -0700499 /*
500 * The structure of the RDID output is as follows:
501 *
502 * offset value meaning
503 * 00h 01h Manufacturer ID for Spansion
504 * 01h 20h 128 Mb capacity
505 * 01h 02h 256 Mb capacity
506 * 02h 18h 128 Mb capacity
507 * 02h 19h 256 Mb capacity
508 * 03h 4Dh Full size of the RDID output (ignored)
509 * 04h 00h FS: 256-kB physical sectors
510 * 04h 01h FS: 64-kB physical sectors
511 * 04h 00h FL: 256-kB physical sectors
512 * 04h 01h FL: Mix of 64-kB and 4KB overlayed sectors
513 * 05h 80h FL family
514 * 05h 81h FS family
515 *
516 * Need to use bytes 1, 2, 4, and 5 to properly identify one of eight
517 * possible chips:
518 *
519 * 2 types * 2 possible sizes * 2 possible sector layouts
520 *
521 */
Nikolai Artemiev4ce3bd22020-11-10 13:41:12 +1100522
523 uint32_t model_id =
524 dev_id[1] << 24 |
525 dev_id[2] << 16 |
526 dev_id[4] << 8 |
527 dev_id[5] << 0;
528
529 if (dev_id[0] == flash->chip->manufacture_id && model_id == flash->chip->model_id)
530 return 1;
531
Vadim Bendebury3a501162014-10-21 20:38:13 -0700532 return 0;
533}