blob: b3c656141180e0a33b6f4088b8ab9c8b8fb60379 [file] [log] [blame]
David Hendricks398714f2014-07-03 17:49:41 -07001/*
2 * This file is part of the flashrom project.
3 *
Nikolai Artemiev2d60b332020-12-04 16:43:59 +11004 * Copyright (C) 2014 Google LLC.
David Hendricks398714f2014-07-03 17:49:41 -07005 *
Nikolai Artemiev2d60b332020-12-04 16:43:59 +11006 * 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.
David Hendricks398714f2014-07-03 17:49:41 -070010 *
Nikolai Artemiev2d60b332020-12-04 16:43:59 +110011 * 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.
Nikolai Artemiev144243b2020-11-10 11:57:39 +110015 */
16
17/*
David Hendricks43d9afd2015-03-13 20:54:23 -070018 * s25f.c - Helper functions for Spansion S25FL and S25FS SPI flash chips.
Vadim Bendebury3a501162014-10-21 20:38:13 -070019 * Uses 24 bit addressing for the FS chips and 32 bit addressing for the FL
20 * chips (which is required by the overlayed sector size devices).
21 * TODO: Implement fancy hybrid sector architecture helpers.
David Hendricks398714f2014-07-03 17:49:41 -070022 */
23
Vadim Bendebury3a501162014-10-21 20:38:13 -070024#include <string.h>
25
David Hendricks398714f2014-07-03 17:49:41 -070026#include "chipdrivers.h"
27#include "spi.h"
David Hendricks43d9afd2015-03-13 20:54:23 -070028#include "writeprotect.h"
David Hendricks398714f2014-07-03 17:49:41 -070029
David Hendricks43d9afd2015-03-13 20:54:23 -070030/*
31 * RDAR and WRAR are supported on chips which have more than one set of status
32 * and control registers and take an address of the register to read/write.
33 * WRR, RDSR2, and RDCR are used on chips with a more limited set of control/
34 * status registers.
35 *
36 * WRR is somewhat peculiar. It shares the same opcode as JEDEC_WRSR, and if
37 * given one data byte (following the opcode) it acts the same way. If it's
38 * given two data bytes, the first data byte overwrites status register 1
39 * and the second data byte overwrites config register 1.
40 */
41#define CMD_WRR 0x01
42#define CMD_WRDI 0x04
43#define CMD_RDSR2 0x07 /* note: read SR1 with JEDEC RDSR opcode */
44#define CMD_RDCR 0x35
David Hendricks398714f2014-07-03 17:49:41 -070045#define CMD_RDAR 0x65
46#define CMD_WRAR 0x71
David Hendricks43d9afd2015-03-13 20:54:23 -070047
48/* TODO: For now, commands which use an address assume 24-bit addressing */
49#define CMD_WRR_LEN 3
50#define CMD_WRDI_LEN 1
David Hendricks398714f2014-07-03 17:49:41 -070051#define CMD_RDAR_LEN 4
52#define CMD_WRAR_LEN 5
53
54#define CMD_RSTEN 0x66
55#define CMD_RST 0x99
56
David Hendricksa9884852014-12-11 15:31:12 -080057#define CR1NV_ADDR 0x000002
David Hendricks43d9afd2015-03-13 20:54:23 -070058#define CR1_BPNV_O (1 << 3)
59#define CR1_TBPROT_O (1 << 5)
David Hendricks398714f2014-07-03 17:49:41 -070060#define CR3NV_ADDR 0x000004
61#define CR3NV_20H_NV (1 << 3)
62
David Hendricks43d9afd2015-03-13 20:54:23 -070063/* See "Embedded Algorithm Performance Tables for additional timing specs. */
64#define T_W 145 * 1000 /* NV register write time (145ms) */
65#define T_RPH 35 /* Reset pulse hold time (35us) */
66#define S25FS_T_SE 145 * 1000 /* Sector Erase Time (145ms) */
67#define S25FL_T_SE 130 * 1000 /* Sector Erase Time (130ms) */
68
Souvik Ghoshd75cd672016-06-17 14:21:39 -070069static int s25f_legacy_software_reset(const struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -070070{
71 int result;
72 struct spi_command cmds[] = {
73 {
74 .writecnt = 1,
75 .writearr = (const unsigned char[]){ CMD_RSTEN },
76 .readcnt = 0,
77 .readarr = NULL,
78 }, {
79 .writecnt = 1,
80 .writearr = (const unsigned char[]){ 0xf0 },
81 .readcnt = 0,
82 .readarr = NULL,
83 }, {
84 .writecnt = 0,
85 .writearr = NULL,
86 .readcnt = 0,
87 .readarr = NULL,
88 }};
89
Souvik Ghoshd75cd672016-06-17 14:21:39 -070090 result = spi_send_multicommand(flash, cmds);
David Hendricks43d9afd2015-03-13 20:54:23 -070091 if (result) {
92 msg_cerr("%s failed during command execution\n", __func__);
93 return result;
94 }
95
96 /* Allow time for reset command to execute. The datasheet specifies
97 * Trph = 35us, double that to be safe. */
98 programmer_delay(T_RPH * 2);
99
100 return 0;
101}
102
103/* "Legacy software reset" is disabled by default on S25FS, use this instead. */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700104static int s25fs_software_reset(struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -0700105{
106 int result;
107 struct spi_command cmds[] = {
108 {
109 .writecnt = 1,
110 .writearr = (const unsigned char[]){ CMD_RSTEN },
111 .readcnt = 0,
112 .readarr = NULL,
113 }, {
114 .writecnt = 1,
115 .writearr = (const unsigned char[]){ CMD_RST },
116 .readcnt = 0,
117 .readarr = NULL,
118 }, {
119 .writecnt = 0,
120 .writearr = NULL,
121 .readcnt = 0,
122 .readarr = NULL,
123 }};
124
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700125 result = spi_send_multicommand(flash, cmds);
David Hendricks43d9afd2015-03-13 20:54:23 -0700126 if (result) {
127 msg_cerr("%s failed during command execution\n", __func__);
128 return result;
129 }
130
131 /* Allow time for reset command to execute. Double tRPH to be safe. */
132 programmer_delay(T_RPH * 2);
133
134 return 0;
135}
136
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700137static int s25f_poll_status(const struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -0700138{
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530139 uint8_t tmp = spi_read_status_register(flash);
David Hendricks43d9afd2015-03-13 20:54:23 -0700140
Edward O'Callaghan8b5e4732019-03-05 15:27:53 +1100141 while (tmp & SPI_SR_WIP) {
David Hendricks43d9afd2015-03-13 20:54:23 -0700142 /*
143 * The WIP bit on S25F chips remains set to 1 if erase or
144 * programming errors occur, so we must check for those
145 * errors here. If an error is encountered, do a software
146 * reset to clear WIP and other volatile bits, otherwise
147 * the chip will be unresponsive to further commands.
148 */
Edward O'Callaghan1945f1e2019-03-18 13:12:51 +1100149 if (tmp & SPI_SR_ERA_ERR) {
David Hendricks43d9afd2015-03-13 20:54:23 -0700150 msg_cerr("Erase error occurred\n");
151 s25f_legacy_software_reset(flash);
152 return -1;
153 }
154
155 if (tmp & (1 << 6)) {
156 msg_cerr("Programming error occurred\n");
157 s25f_legacy_software_reset(flash);
158 return -1;
159 }
160
161 programmer_delay(1000 * 10);
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530162 tmp = spi_read_status_register(flash);
David Hendricks43d9afd2015-03-13 20:54:23 -0700163 }
164
165 return 0;
166}
167
168/* "Read Any Register" instruction only supported on S25FS */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700169static int s25fs_read_cr(const struct flashctx *flash, uint32_t addr)
David Hendricks398714f2014-07-03 17:49:41 -0700170{
171 int result;
172 uint8_t cfg;
173 /* By default, 8 dummy cycles are necessary for variable-latency
174 commands such as RDAR (see CR2NV[3:0]). */
175 unsigned char read_cr_cmd[] = {
Nikolai Artemiev144243b2020-11-10 11:57:39 +1100176 CMD_RDAR,
177 (addr >> 16) & 0xff,
178 (addr >> 8) & 0xff,
179 (addr & 0xff),
180 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00,
David Hendricks398714f2014-07-03 17:49:41 -0700182 };
183
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700184 result = spi_send_command(flash, sizeof(read_cr_cmd), 1, read_cr_cmd, &cfg);
David Hendricks398714f2014-07-03 17:49:41 -0700185 if (result) {
186 msg_cerr("%s failed during command execution at address 0x%x\n",
187 __func__, addr);
David Hendricks688b5e22014-12-12 11:15:44 -0800188 return -1;
David Hendricks398714f2014-07-03 17:49:41 -0700189 }
190
191 return cfg;
192}
193
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700194static int s25f_read_cr1(const struct flashctx *flash)
David Hendricks43d9afd2015-03-13 20:54:23 -0700195{
196 int result;
197 uint8_t cfg;
198 unsigned char read_cr_cmd[] = { CMD_RDCR };
199
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700200 result = spi_send_command(flash, sizeof(read_cr_cmd), 1, read_cr_cmd, &cfg);
David Hendricks43d9afd2015-03-13 20:54:23 -0700201 if (result) {
202 msg_cerr("%s failed during command execution\n", __func__);
203 return -1;
204 }
205
206 return cfg;
207}
208
209/* "Write Any Register" instruction only supported on S25FS */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700210static int s25fs_write_cr(const struct flashctx *flash,
David Hendricks43d9afd2015-03-13 20:54:23 -0700211 uint32_t addr, uint8_t data)
David Hendricks398714f2014-07-03 17:49:41 -0700212{
213 int result;
214 struct spi_command cmds[] = {
215 {
216 .writecnt = JEDEC_WREN_OUTSIZE,
217 .writearr = (const unsigned char[]){ JEDEC_WREN },
218 .readcnt = 0,
219 .readarr = NULL,
220 }, {
221 .writecnt = CMD_WRAR_LEN,
222 .writearr = (const unsigned char[]){
223 CMD_WRAR,
224 (addr >> 16) & 0xff,
225 (addr >> 8) & 0xff,
226 (addr & 0xff),
227 data
228 },
229 .readcnt = 0,
230 .readarr = NULL,
231 }, {
232 .writecnt = 0,
233 .writearr = NULL,
234 .readcnt = 0,
235 .readarr = NULL,
236 }};
237
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700238 result = spi_send_multicommand(flash, cmds);
David Hendricks398714f2014-07-03 17:49:41 -0700239 if (result) {
240 msg_cerr("%s failed during command execution at address 0x%x\n",
241 __func__, addr);
David Hendricks688b5e22014-12-12 11:15:44 -0800242 return -1;
David Hendricks398714f2014-07-03 17:49:41 -0700243 }
244
David Hendricks43d9afd2015-03-13 20:54:23 -0700245 programmer_delay(T_W);
246 return s25f_poll_status(flash);
David Hendricks398714f2014-07-03 17:49:41 -0700247}
248
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700249static int s25f_write_cr1(const struct flashctx *flash, uint8_t data)
David Hendricks398714f2014-07-03 17:49:41 -0700250{
251 int result;
252 struct spi_command cmds[] = {
253 {
David Hendricks43d9afd2015-03-13 20:54:23 -0700254 .writecnt = JEDEC_WREN_OUTSIZE,
255 .writearr = (const unsigned char[]){ JEDEC_WREN },
David Hendricks398714f2014-07-03 17:49:41 -0700256 .readcnt = 0,
257 .readarr = NULL,
258 }, {
David Hendricks43d9afd2015-03-13 20:54:23 -0700259 .writecnt = CMD_WRR_LEN,
260 .writearr = (const unsigned char[]){
261 CMD_WRR,
Ramya Vijaykumar4af3f822016-01-27 11:51:27 +0530262 spi_read_status_register(flash),
David Hendricks43d9afd2015-03-13 20:54:23 -0700263 data,
264 },
David Hendricks398714f2014-07-03 17:49:41 -0700265 .readcnt = 0,
266 .readarr = NULL,
267 }, {
268 .writecnt = 0,
269 .writearr = NULL,
270 .readcnt = 0,
271 .readarr = NULL,
272 }};
273
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700274 result = spi_send_multicommand(flash, cmds);
David Hendricks398714f2014-07-03 17:49:41 -0700275 if (result) {
276 msg_cerr("%s failed during command execution\n", __func__);
David Hendricks43d9afd2015-03-13 20:54:23 -0700277 return -1;
David Hendricks398714f2014-07-03 17:49:41 -0700278 }
279
David Hendricks43d9afd2015-03-13 20:54:23 -0700280 programmer_delay(T_W);
281 return s25f_poll_status(flash);
David Hendricks398714f2014-07-03 17:49:41 -0700282}
283
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700284static int s25fs_restore_cr3nv(struct flashctx *flash, uint8_t cfg)
David Hendricks398714f2014-07-03 17:49:41 -0700285{
286 int ret = 0;
287
288 msg_cdbg("Restoring CR3NV value to 0x%02x\n", cfg);
David Hendricks636c74a2014-12-12 11:30:00 -0800289 ret |= s25fs_write_cr(flash, CR3NV_ADDR, cfg);
David Hendricks398714f2014-07-03 17:49:41 -0700290 ret |= s25fs_software_reset(flash);
291 return ret;
292}
293
David Hendricksa9884852014-12-11 15:31:12 -0800294/* returns state of top/bottom block protection, or <0 to indicate error */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700295static int s25f_get_tbprot_o(const struct flashctx *flash)
David Hendricksa9884852014-12-11 15:31:12 -0800296{
David Hendricks43d9afd2015-03-13 20:54:23 -0700297 int cr1 = s25f_read_cr1(flash);
David Hendricksa9884852014-12-11 15:31:12 -0800298
David Hendricks43d9afd2015-03-13 20:54:23 -0700299 if (cr1 < 0)
David Hendricksa9884852014-12-11 15:31:12 -0800300 return -1;
301
302 /*
303 * 1 = BP starts at bottom (low address)
304 * 0 = BP start at top (high address)
305 */
David Hendricks43d9afd2015-03-13 20:54:23 -0700306 return cr1 & CR1_TBPROT_O ? 1 : 0;
David Hendricksa9884852014-12-11 15:31:12 -0800307}
308
David Hendricks148a4bf2015-03-13 21:02:42 -0700309/* fills modifier_bits struct, returns 0 to indicate success */
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700310int s25f_get_modifier_bits(const struct flashctx *flash,
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +1100311 struct modifier_bits *m)
David Hendricks148a4bf2015-03-13 21:02:42 -0700312{
313 int tmp;
314
315 memset(m, 0, sizeof(*m));
316
317 tmp = s25f_get_tbprot_o(flash);
318 if (tmp < 0)
319 return -1;
320 m->tb = tmp;
321
322 return 0;
323}
324
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700325int s25f_set_modifier_bits(const struct flashctx *flash,
Edward O'Callaghan9c4c9a52019-12-04 18:18:01 +1100326 struct modifier_bits *m)
David Hendricks148a4bf2015-03-13 21:02:42 -0700327{
328 int cr1, cr1_orig;
329
330 cr1 = cr1_orig = s25f_read_cr1(flash);
331 if (cr1 < 0)
332 return -1;
333
334 /*
335 * Clear BPNV so that setting BP2-0 in status register gets
336 * written to non-volatile memory.
337 *
338 * For TBPROT:
339 * 1 = BP starts at bottom (low address)
340 * 0 = BP start at top (high address)
341 */
342 cr1 &= ~(CR1_BPNV_O | CR1_TBPROT_O);
343 cr1 |= m->tb ? CR1_TBPROT_O : 0;
344
345 if (cr1 != cr1_orig) {
346 msg_cdbg("%s: setting cr1 bits to 0x%02x\n", __func__, cr1);
347 if (s25f_write_cr1(flash, cr1) < 0)
348 return -1;
349 if (s25f_read_cr1(flash) != cr1) {
350 msg_cerr("%s: failed to set CR1 value\n", __func__);
351 return -1;
352 }
353 } else {
354 msg_cdbg("%s: cr1 bits already match desired value: "
355 "0x%02x\n", __func__, cr1);
356 }
357
358 return 0;
359}
360
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700361int s25fs_block_erase_d8(struct flashctx *flash,
David Hendricks398714f2014-07-03 17:49:41 -0700362 unsigned int addr, unsigned int blocklen)
363{
364 unsigned char cfg;
365 int result;
366 static int cr3nv_checked = 0;
367
368 struct spi_command erase_cmds[] = {
369 {
370 .writecnt = JEDEC_WREN_OUTSIZE,
371 .writearr = (const unsigned char[]){ JEDEC_WREN },
372 .readcnt = 0,
373 .readarr = NULL,
374 }, {
375 .writecnt = JEDEC_BE_D8_OUTSIZE,
376 .writearr = (const unsigned char[]){
377 JEDEC_BE_D8,
378 (addr >> 16) & 0xff,
379 (addr >> 8) & 0xff,
380 (addr & 0xff)
381 },
382 .readcnt = 0,
383 .readarr = NULL,
384 }, {
385 .writecnt = 0,
386 .writearr = NULL,
387 .readcnt = 0,
388 .readarr = NULL,
389 }};
390
391 /* Check if hybrid sector architecture is in use and, if so,
392 * switch to uniform sectors. */
393 if (!cr3nv_checked) {
David Hendricks636c74a2014-12-12 11:30:00 -0800394 cfg = s25fs_read_cr(flash, CR3NV_ADDR);
David Hendricks398714f2014-07-03 17:49:41 -0700395 if (!(cfg & CR3NV_20H_NV)) {
David Hendricks636c74a2014-12-12 11:30:00 -0800396 s25fs_write_cr(flash, CR3NV_ADDR, cfg | CR3NV_20H_NV);
David Hendricks398714f2014-07-03 17:49:41 -0700397 s25fs_software_reset(flash);
398
David Hendricks636c74a2014-12-12 11:30:00 -0800399 cfg = s25fs_read_cr(flash, CR3NV_ADDR);
David Hendricks398714f2014-07-03 17:49:41 -0700400 if (!(cfg & CR3NV_20H_NV)) {
401 msg_cerr("%s: Unable to enable uniform "
402 "block sizes.\n", __func__);
403 return 1;
404 }
405
406 msg_cdbg("\n%s: CR3NV updated (0x%02x -> 0x%02x)\n",
407 __func__, cfg,
David Hendricks636c74a2014-12-12 11:30:00 -0800408 s25fs_read_cr(flash, CR3NV_ADDR));
David Hendricks398714f2014-07-03 17:49:41 -0700409 /* Restore CR3V when flashrom exits */
410 register_chip_restore(s25fs_restore_cr3nv, flash, cfg);
411 }
412
413 cr3nv_checked = 1;
414 }
415
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700416 result = spi_send_multicommand(flash, erase_cmds);
David Hendricks398714f2014-07-03 17:49:41 -0700417 if (result) {
418 msg_cerr("%s failed during command execution at address 0x%x\n",
419 __func__, addr);
420 return result;
421 }
422
David Hendricks43d9afd2015-03-13 20:54:23 -0700423 programmer_delay(S25FS_T_SE);
424 return s25f_poll_status(flash);
David Hendricks398714f2014-07-03 17:49:41 -0700425}
Vadim Bendebury3a501162014-10-21 20:38:13 -0700426
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700427int s25fl_block_erase(struct flashctx *flash,
Vadim Bendebury3a501162014-10-21 20:38:13 -0700428 unsigned int addr, unsigned int blocklen)
429{
Vadim Bendebury3a501162014-10-21 20:38:13 -0700430 int result;
Vadim Bendebury3a501162014-10-21 20:38:13 -0700431
432 struct spi_command erase_cmds[] = {
433 {
434 .writecnt = JEDEC_WREN_OUTSIZE,
435 .writearr = (const unsigned char[]){
436 JEDEC_WREN
437 },
438 .readcnt = 0,
439 .readarr = NULL,
440 }, {
441 .writecnt = JEDEC_BE_DC_OUTSIZE,
442 .writearr = (const unsigned char[]){
443 JEDEC_BE_DC,
444 (addr >> 24) & 0xff,
445 (addr >> 16) & 0xff,
446 (addr >> 8) & 0xff,
447 (addr & 0xff)
448 },
449 .readcnt = 0,
450 .readarr = NULL,
451 }, {
452 .writecnt = 0,
453 .readcnt = 0,
454 }
455 };
456
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700457 result = spi_send_multicommand(flash, erase_cmds);
Vadim Bendebury3a501162014-10-21 20:38:13 -0700458 if (result) {
459 msg_cerr("%s failed during command execution at address 0x%x\n",
460 __func__, addr);
461 return result;
462 }
463
David Hendricks43d9afd2015-03-13 20:54:23 -0700464 programmer_delay(S25FL_T_SE);
465 return s25f_poll_status(flash);
Vadim Bendebury3a501162014-10-21 20:38:13 -0700466}
467
468
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700469int probe_spi_big_spansion(struct flashctx *flash)
Vadim Bendebury3a501162014-10-21 20:38:13 -0700470{
471 static const unsigned char cmd = JEDEC_RDID;
Vadim Bendebury3a501162014-10-21 20:38:13 -0700472 unsigned char dev_id[6]; /* We care only about 6 first bytes */
473
Nikolai Artemiev077b66c2020-11-10 13:38:38 +1100474 if (spi_send_command(flash, sizeof(cmd), sizeof(dev_id), &cmd, dev_id))
475 return 0;
Vadim Bendebury3a501162014-10-21 20:38:13 -0700476
Nikolai Artemiev077b66c2020-11-10 13:38:38 +1100477 for (size_t i = 0; i < sizeof(dev_id); i++)
478 msg_gdbg(" 0x%02x", dev_id[i]);
479 msg_gdbg(".\n");
Vadim Bendebury3a501162014-10-21 20:38:13 -0700480
Vadim Bendebury3a501162014-10-21 20:38:13 -0700481 /*
482 * The structure of the RDID output is as follows:
483 *
484 * offset value meaning
485 * 00h 01h Manufacturer ID for Spansion
486 * 01h 20h 128 Mb capacity
487 * 01h 02h 256 Mb capacity
488 * 02h 18h 128 Mb capacity
489 * 02h 19h 256 Mb capacity
490 * 03h 4Dh Full size of the RDID output (ignored)
491 * 04h 00h FS: 256-kB physical sectors
492 * 04h 01h FS: 64-kB physical sectors
493 * 04h 00h FL: 256-kB physical sectors
494 * 04h 01h FL: Mix of 64-kB and 4KB overlayed sectors
495 * 05h 80h FL family
496 * 05h 81h FS family
497 *
498 * Need to use bytes 1, 2, 4, and 5 to properly identify one of eight
499 * possible chips:
500 *
501 * 2 types * 2 possible sizes * 2 possible sector layouts
502 *
503 */
Nikolai Artemiev4ce3bd22020-11-10 13:41:12 +1100504
505 uint32_t model_id =
506 dev_id[1] << 24 |
507 dev_id[2] << 16 |
508 dev_id[4] << 8 |
509 dev_id[5] << 0;
510
511 if (dev_id[0] == flash->chip->manufacture_id && model_id == flash->chip->model_id)
512 return 1;
513
Vadim Bendebury3a501162014-10-21 20:38:13 -0700514 return 0;
515}