summaryrefslogtreecommitdiffstats
path: root/sfdp.c
blob: f7daf58cac93e9b4cb9a5946f2d9cd5255edafa5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
/*
 * This file is part of the flashrom project.
 *
 * Copyright (C) 2011-2012 Stefan Tauner
 * Copyright (C) 2014 Boris Baykov
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

/*
 *   History of changes:
 *	05/01/2015  Added compliance to JESD216B standard and SFDP revision 1.6
 *	07/01/2015  Modified to support SFDP revision 1.5 (for Micron flash chips)
 */

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "flash.h"
#include "spi.h"
#include "spi4ba.h"
#include "chipdrivers.h"

/* Default four bytes addressing behavior:
   1) 4-Bytes Addressing Mode (FBA_USE_EXT_ADDR_REG_BY_DEFAULT not defined)
   2) 3-bytes mode with Ext.Addr.Register (FBA_USE_EXT_ADDR_REG_BY_DEFAULT defined) */
/* #define FBA_USE_EXT_ADDR_REG_BY_DEFAULT 1 */

/* For testing purposes only. Tests JESD216B SFDP compliance without proper flash chip */
/* #define JESD216B_SIMULATION 1 */

static int spi_sfdp_read_sfdp_chunk(struct flashctx *flash, uint32_t address, uint8_t *buf, int len)
{
	int i, ret;
	uint8_t *newbuf;
	const unsigned char cmd[JEDEC_SFDP_OUTSIZE] = {
		JEDEC_SFDP,
		(address >> 16) & 0xff,
		(address >> 8) & 0xff,
		(address >> 0) & 0xff,
		/* FIXME: the following dummy byte explodes on some programmers.
		 * One workaround is to read the dummy byte
		 * instead and discard its value.
		 */
		0
	};
	msg_cspew("%s: addr=0x%x, len=%d, data:\n", __func__, address, len);
	newbuf = malloc(len + 1);
	if (!newbuf)
		return SPI_PROGRAMMER_ERROR;
	ret = spi_send_command(flash, sizeof(cmd) - 1, len + 1, cmd, newbuf);
	memmove(buf, newbuf + 1, len);
	free(newbuf);
	if (ret)
		return ret;
	for (i = 0; i < len; i++)
		msg_cspew(" 0x%02x", buf[i]);
	msg_cspew("\n");
	return 0;
}

static int spi_sfdp_read_sfdp(struct flashctx *flash, uint32_t address, uint8_t *buf, int len)
{
	/* FIXME: There are different upper bounds for the number of bytes to
	 * read on the various programmers (even depending on the rest of the
	 * structure of the transaction). 2 is a safe bet. */
	int maxstep = 2;
	int ret = 0;
	while (len > 0) {
		int step = min(len, maxstep);
		ret = spi_sfdp_read_sfdp_chunk(flash, address, buf, step);
		if (ret)
			return ret;
		address += step;
		buf += step;
		len -= step;
	}
	return ret;
}

struct sfdp_tbl_hdr {
	uint16_t id;
	uint8_t v_minor;
	uint8_t v_major;
	uint8_t len;
	uint32_t ptp; /* 24b pointer */
};

static int sfdp_add_uniform_eraser(struct flashchip *chip, int eraser_type, uint8_t opcode, uint32_t block_size)
{
	int i;
	uint32_t total_size = chip->total_size * 1024;

	/* choosing different eraser functions for 3-bytes and 4-bytes addressing */
	erasefunc_t *erasefn = (chip->feature_bits & FEATURE_4BA_SUPPORT) ?
		spi_get_erasefn_from_opcode_4ba(opcode) : spi_get_erasefn_from_opcode(opcode);

	if (erasefn == NULL || total_size == 0 || block_size == 0 ||
	    total_size % block_size != 0) {
		msg_cdbg("%s: invalid input, please report to "
			 "flashrom@flashrom.org\n", __func__);
		return 1;
	}

	for (i = 0; i < NUM_ERASEFUNCTIONS; i++) {
		struct block_eraser *eraser = &chip->block_erasers[i];
		/* Check for duplicates (including (some) non-uniform ones). */
		if (eraser->eraseblocks[0].size == block_size &&
		    eraser->block_erase == erasefn) {
			msg_cdbg2("  Tried to add a duplicate block eraser: "
				  "%d x %d B with opcode 0x%02x.\n",
				  total_size/block_size, block_size, opcode);
			return 1;
		}
		if (eraser->eraseblocks[0].size != 0 ||
		    eraser->block_erase != NULL) {
			msg_cspew("  Block Eraser %d is already occupied.\n",
				  i);
			continue;
		}

		eraser->type = eraser_type;
		eraser->block_erase = erasefn;
		eraser->eraseblocks[0].size = block_size;
		eraser->eraseblocks[0].count = total_size/block_size;
		msg_cdbg2("  Block eraser %d: %d x %d B with opcode "
			  "0x%02x\n", i, total_size/block_size, block_size,
			  opcode);
		return 0;
	}
	msg_cinfo("%s: Not enough space to store another eraser (i=%d)."
		  " Please report this at flashrom@flashrom.org\n",
		  __func__, i);
	return 1;
}

/* Try of replace exist erasers to new direct 4-bytes addressing erasers
   which can be called from ANY addressing mode: 3-byte or 4-bytes.
   These erasers opcodes defines in SFDP 4-byte address instruction table
   from SFDP revision 1.6 that is defined by JESD216B standard. */
static int sfdp_change_uniform_eraser_4ba_direct(struct flashchip *chip, int eraser_type, uint8_t opcode)
{
	int i;
	erasefunc_t *erasefn = spi_get_erasefn_from_opcode_4ba_direct(opcode);

	if (erasefn == NULL) {
		msg_cdbg("%s: invalid input, please report to "
			 "flashrom@flashrom.org\n", __func__);
		return 1;
	}

	for (i = 0; i < NUM_ERASEFUNCTIONS; i++) {
		struct block_eraser *eraser = &chip->block_erasers[i];
		if (eraser->eraseblocks[0].size == 0)
			break;
		if (eraser->type != eraser_type)
			continue;

		eraser->block_erase = erasefn;
		msg_cdbg2("  Block eraser %d (type %d) changed to opcode "
			  "0x%02x\n", i, eraser_type, opcode);
		return 0;
	}

	msg_cspew("%s: Block Eraser type %d isn't found."
		  " Please report this at flashrom@flashrom.org\n",
		  __func__, eraser_type);
	return 1;
}

/* Parse of JEDEC SFDP Basic Flash Parameter Table */
static int sfdp_fill_flash(struct flashchip *chip, uint8_t *buf, uint16_t len, int sfdp_rev_15)
{
	uint8_t opcode_4k_erase = 0xFF;
	uint32_t tmp32;
	uint8_t tmp8;
	uint32_t total_size; /* in bytes */
	uint32_t block_size;
	int j;

	msg_cdbg("Parsing JEDEC flash parameter table... ");
	if (len != 16 * 4 && len != 9 * 4 && len != 4 * 4) {
		msg_cdbg("%s: len out of spec\n", __func__);
		return 1;
	}
	msg_cdbg2("\n");

	/* 1. double word */
	tmp32 =  ((unsigned int)buf[(4 * 0) + 0]);
	tmp32 |= ((unsigned int)buf[(4 * 0) + 1]) << 8;
	tmp32 |= ((unsigned int)buf[(4 * 0) + 2]) << 16;
	tmp32 |= ((unsigned int)buf[(4 * 0) + 3]) << 24;

	chip->feature_bits = 0;

	tmp8 = (tmp32 >> 17) & 0x3;
	switch (tmp8) {
	case 0x0:
		msg_cdbg2("  3-Byte only addressing.\n");
		break;
	case 0x1:
		msg_cdbg2("  3-Byte (and optionally 4-Byte) addressing.\n");
#ifndef FBA_USE_EXT_ADDR_REG_BY_DEFAULT
		/* assuming that 4-bytes addressing mode can be entered
		   by CMD B7h preceded with WREN and all read, write and
		   erase commands will be able to receive 4-bytes address */
		chip->feature_bits |= FEATURE_4BA_SUPPORT;
		chip->four_bytes_addr_funcs.enter_4ba = spi_enter_4ba_b7_we;
		chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba;
		chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba;
		chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba;
#else /* if FBA_USE_EXT_ADDR_REG_BY_DEFAULT defined */
		/* assuming that 4-bytes addressing is working using
		   extended address register which can be assigned
		   throught CMD C5h and then all commands will use
		   3-bytes address as usual */
		chip->feature_bits |= ( FEATURE_4BA_SUPPORT |
					FEATURE_4BA_EXTENDED_ADDR_REG );
		chip->four_bytes_addr_funcs.enter_4ba = NULL;
		chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba_ereg;
		chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba_ereg;
		chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba_ereg;
#endif /* FBA_USE_EXT_ADDR_REG_BY_DEFAULT */
		break;
	case 0x2:
		msg_cdbg2("  4-Byte only addressing.\n");
		chip->feature_bits |= ( FEATURE_4BA_SUPPORT |
					FEATURE_4BA_ONLY );
		chip->four_bytes_addr_funcs.enter_4ba = NULL;
		chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba;
		chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba;
		chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba;
		break;
	default:
		msg_cdbg("  Required addressing mode (0x%x) not supported.\n",
			 tmp8);
		return 1;
	}

	msg_cdbg2("  Status register is ");
	if (tmp32 & (1 << 3)) {
		msg_cdbg2("volatile and writes to the status register have to "
			  "be enabled with ");
		if (tmp32 & (1 << 4)) {
			chip->feature_bits |= FEATURE_WRSR_WREN;
			msg_cdbg2("WREN (0x06).\n");
		} else {
			chip->feature_bits |= FEATURE_WRSR_EWSR;
			msg_cdbg2("EWSR (0x50).\n");
		}
	} else {
		msg_cdbg2("non-volatile and the standard does not allow "
			  "vendors to tell us whether EWSR/WREN is needed for "
			  "status register writes - assuming EWSR.\n");
			chip->feature_bits |= FEATURE_WRSR_EWSR;
		}

	msg_cdbg2("  Write chunk size is ");
	if (tmp32 & (1 << 2)) {
		msg_cdbg2("at least 64 B.\n");
		chip->page_size = 64;
		chip->write = spi_chip_write_256;
	} else {
		msg_cdbg2("1 B only.\n");
		chip->page_size = 256;
		chip->write = spi_chip_write_1;
	}

	if ((tmp32 & 0x3) == 0x1) {
		opcode_4k_erase = (tmp32 >> 8) & 0xFF;
		msg_cspew("  4kB erase opcode is 0x%02x.\n", opcode_4k_erase);
		/* add the eraser later, because we don't know total_size yet */
	} else
		msg_cspew("  4kB erase opcode is not defined.\n");

	/* 2. double word */
	tmp32 =  ((unsigned int)buf[(4 * 1) + 0]);
	tmp32 |= ((unsigned int)buf[(4 * 1) + 1]) << 8;
	tmp32 |= ((unsigned int)buf[(4 * 1) + 2]) << 16;
	tmp32 |= ((unsigned int)buf[(4 * 1) + 3]) << 24;

	if (tmp32 & (1 << 31)) {
		msg_cdbg("Flash chip size >= 4 Gb/512 MB not supported.\n");
		return 1;
	}
	total_size = ((tmp32 & 0x7FFFFFFF) + 1) / 8;
	chip->total_size = total_size / 1024;
	msg_cdbg2("  Flash chip size is %d kB.\n", chip->total_size);

	if (total_size > (1 << 24)) {
		if(!sfdp_rev_15) {
			msg_cdbg("Flash chip size is bigger than what 3-Byte addressing "
				 "can access but chip's SFDP revision is lower than 1.6 "
				 "(1.5).\nConsequently 4-bytes addressing can NOT be "
				 "properly configured using current SFDP information.\n");
#ifndef FBA_USE_EXT_ADDR_REG_BY_DEFAULT
			msg_cdbg("Assuming that 4-bytes addressing mode can be "
				 "entered by CMD B7h with WREN.\n");
#else
			msg_cdbg("Assuming that 4-bytes addressing is working via "
				 "an Extended Address Register which can be written "
				 "by CMD C5h.\n");
#endif
		}
	}

	/* FIXME: double words 3-7 contain unused fast read information */

	if (len < 9 * 4) {
		msg_cdbg("  It seems like this chip supports the preliminary "
			 "Intel version of SFDP, skipping processing of double "
			 "words 3-9.\n");

		/* in the case if BFPT erasers array is not present
		   trying to add default 4k-eraser */
		if (opcode_4k_erase != 0xFF)
			sfdp_add_uniform_eraser(chip, 0, opcode_4k_erase, 4 * 1024);

		goto done;
	}

	/* 8. double word & 9. double word */
	/* for by block eraser types, from Type 1 to Type 4 */
	for (j = 0; j < 4; j++) {
		/* 7 double words from the start + 2 bytes for every eraser */
		tmp8 = buf[(4 * 7) + (j * 2)];
		msg_cspew("   Erase Sector (Type %d) Size: 0x%02x\n", j + 1, tmp8);
		if (tmp8 == 0) {
			msg_cspew("  Erase Sector (Type %d) is unused.\n", j + 1);
			continue;
		}
		if (tmp8 >= 31) {
			msg_cdbg2("  Block size of erase Sector (Type %d): 2^%d "
				 "is too big for flashrom.\n", j + 1, tmp8);
			continue;
		}
		block_size = 1 << (tmp8); /* block_size = 2 ^ field */

		tmp8 = buf[(4 * 7) + (j * 2) + 1];
		msg_cspew("   Erase Sector (Type %d) Opcode: 0x%02x\n", j + 1, tmp8);
		sfdp_add_uniform_eraser(chip, j + 1, tmp8, block_size);
	}

	/* Trying to add the default 4k eraser after parsing erasers info.
	   In most cases this eraser has already been added before. */
	if (opcode_4k_erase != 0xFF)
		sfdp_add_uniform_eraser(chip, 0, opcode_4k_erase, 4 * 1024);

	/* Trying to read the exact page size if it's available */
	if (len >= 11 * 4) {
		/* 11. double word */
		tmp8 = buf[(4*10) + 0] >> 4; /* get upper nibble of LSB of 11th dword */
		chip->page_size = 1 << tmp8; /* page_size = 2 ^ N */
		msg_cdbg2("  Page size is %d B.\n", chip->page_size);
	}

	/* If the chip doesn't support 4-bytes addressing mode we don't have
	   to read and analyze 16th DWORD of Basic Flash Parameter Table */
	if (!(chip->feature_bits & FEATURE_4BA_SUPPORT))
		goto done;

	/* In the case if the chip is working in 4-bytes addressing mode ONLY we
	   don't have to read and analyze 16th DWORD of Basic Flash Parameter Table
	   because we don't have to know how to switch to 4-bytes mode and back
	   when we are already in 4-bytes mode permanently. */
	if (chip->feature_bits & FEATURE_4BA_ONLY)
		goto done;

	/* If the SFDP revision supported by the chip is lower that 1.6 (1.5)
	   we can not read and analyze 16th DWORD of Basic Flash Parameter Table.
	   Using defaults by FBA_USE_EXT_ADDR_REG_BY_DEFAULT define. */
	if(!sfdp_rev_15)
		goto done;

	if (len < 16 * 4) {
		msg_cdbg("%s: len of BFPT is out of spec\n", __func__);
		msg_cerr("ERROR: Unable read 4-bytes addressing parameters.\n");
		return 1;
	}

	/* 16. double word */
	tmp32 =  ((unsigned int)buf[(4 * 15) + 0]);
	tmp32 |= ((unsigned int)buf[(4 * 15) + 1]) << 8;
	tmp32 |= ((unsigned int)buf[(4 * 15) + 2]) << 16;
	tmp32 |= ((unsigned int)buf[(4 * 15) + 3]) << 24;

	/* Parsing 16th DWORD of Basic Flash Parameter Table according to JESD216B */

	if(tmp32 & JEDEC_BFPT_DW16_ENTER_B7) {
		msg_cdbg2("  Enter 4-bytes addressing mode by CMD B7h\n");
		chip->four_bytes_addr_funcs.enter_4ba = spi_enter_4ba_b7;
		chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba;
		chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba;
		chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba;
		/* if can go to 4BA-mode -> not need to use Ext.Addr.Reg */
		chip->feature_bits &= ~FEATURE_4BA_EXTENDED_ADDR_REG;
	}
	else if(tmp32 & JEDEC_BFPT_DW16_ENTER_B7_WE) {
		msg_cdbg2("  Enter 4-bytes addressing mode by CMD B7h with WREN\n");
		chip->four_bytes_addr_funcs.enter_4ba = spi_enter_4ba_b7_we;
		chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba;
		chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba;
		chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba;
		/* if can go to 4BA-mode -> not need to use Ext.Addr.Reg */
		chip->feature_bits &= ~FEATURE_4BA_EXTENDED_ADDR_REG;
	}
	else if(tmp32 & JEDEC_BFPT_DW16_ENTER_EXTENDED_ADDR_REG) {
		msg_cdbg2("  Extended Address Register used for 4-bytes addressing\n");
		chip->four_bytes_addr_funcs.enter_4ba = NULL;
		chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba_ereg;
		chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba_ereg;
		chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba_ereg;
		/* this flag signals to all '*_selector' functions
		   to use Ext.Addr.Reg while erase operations */
		chip->feature_bits |= FEATURE_4BA_EXTENDED_ADDR_REG;
	}
	else {
		msg_cerr("ERROR: Unable to use 4-bytes addressing for this chip.\n"
			 " Please report this at flashrom@flashrom.org\n\n");
		return 1;
	}

done:
	msg_cdbg("done.\n");
	return 0;
}

/* Parse of JEDEC SFDP 4-byte address instruction table. From SFDP revision 1.6 only.
   This parsing shoukd be called after basic flash parameter table is parsed. */
static int sfdp_parse_4ba_table(struct flashchip *chip, uint8_t *buf, uint16_t len)
{
	uint32_t tmp32;
	uint8_t tmp8;
	int j, direct_erasers;
	int direct_count;

	msg_cdbg("Parsing JEDEC 4-byte address instuction table... ");
	if (len != 2 * 4) {
		msg_cdbg("%s: len out of spec\n", __func__);
		return 1;
	}
	msg_cdbg2("\n");

	/* 1. double word */
	tmp32 =  ((unsigned int)buf[(4 * 0) + 0]);
	tmp32 |= ((unsigned int)buf[(4 * 0) + 1]) << 8;
	tmp32 |= ((unsigned int)buf[(4 * 0) + 2]) << 16;
	tmp32 |= ((unsigned int)buf[(4 * 0) + 3]) << 24;

	direct_count = 0;

	if(tmp32 & JEDEC_4BAIT_READ_SUPPORT) {
		msg_cdbg2("  Found Read CMD 13h with 4-bytes address\n");
		chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba_direct;
		/* read function has changed to direct 4-bytes function,
		   so entering 4-bytes mode isn't required for reading bytes */
		chip->feature_bits |= FEATURE_4BA_DIRECT_READ;
		direct_count++;
	}

	if(tmp32 & JEDEC_4BAIT_PROGRAM_SUPPORT) {
		msg_cdbg2("  Found Write CMD 12h with 4-bytes address\n");
		chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba_direct;
		chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba_direct;
		/* write (program) functions have changed to direct 4-bytes functions,
		   so entering 4-bytes mode isn't required for writing bytes */
		chip->feature_bits |= FEATURE_4BA_DIRECT_WRITE;
		direct_count++;
	}

	direct_erasers = 0;

	/* 2. double word */
	for (j = 0; j < 4; j++) {
		if(!(tmp32 & (JEDEC_4BAIT_ERASE_TYPE_1_SUPPORT << j)))
			continue;

		tmp8 = buf[(4 * 1) + j];

		msg_cdbg2("  Found Erase (type %d) CMD %02Xh with 4-bytes address\n", j + 1, tmp8);

		if(tmp8 == 0xFF) {
			msg_cdbg("%s: Eraser (type %d) is supported, but opcode = 0xFF\n"
				 "  Please report to flashrom@flashrom.org\n\n", __func__, j + 1);
			continue;
		}

		/* try of replacing the eraser with direct 4-bytes eraser */
		if(!sfdp_change_uniform_eraser_4ba_direct(chip, j + 1, tmp8))
			direct_erasers++;
	}

	for (j = 0; j < NUM_ERASEFUNCTIONS; j++) {
		if (chip->block_erasers[j].eraseblocks[0].size == 0)
			break;
	}

	if( j == direct_erasers ) {
		/* if all erasers have been changed to direct 4-bytes ones,
		   then we don't have to enter 4-bytes mode for erase */
		chip->feature_bits |= FEATURE_4BA_ALL_ERASERS_DIRECT;
		direct_count++;
		msg_cspew("All erasers have changed to direct ones.\n");
	}

	if( direct_count == 3 ) {
		/* if all read/write/erase functions are direct 4-bytes now,
		   then we don't have to use extended address register */
		chip->feature_bits &= ~FEATURE_4BA_EXTENDED_ADDR_REG;
		msg_cspew("All read/write/erase functions have changed to direct ones.\n");
	}

	msg_cdbg("done.\n");
	return 0;
}

#ifdef JESD216B_SIMULATION
/* This simulation increases size of Basic Flash Parameter Table
   to have 16 dwords size and fills 16th dword with fake information
   that is required to test JESD216B compliance. */
int sfdp_jesd216b_simulation_dw16(uint8_t** ptbuf, uint16_t* plen)
{
	uint8_t* tbufsim;
	uint16_t lensim = 16 * 4;

	tbufsim = malloc(lensim);
	if (tbufsim == NULL) {
		msg_gerr("Out of memory!\n");
		return 1;
	}

	msg_cdbg("\n=== SIMULATION of JESD216B 16th Dword of Basic Flash Parameter Table\n");

	memset(tbufsim, 0, 16 * 4);
	memcpy(tbufsim, *ptbuf, min(*plen, 15 * 4));

	tbufsim[(4*10) + 0] = 8 << 4; /* page size = 256 */

	*((uint32_t*)&tbufsim[15 * 4]) = /*JEDEC_BFPT_DW16_ENTER_B7 | */
					 JEDEC_BFPT_DW16_ENTER_B7_WE |
					 JEDEC_BFPT_DW16_ENTER_EXTENDED_ADDR_REG /* |
					 JEDEC_BFPT_DW16_ENTER_BANK_ADDR_REG_EN_BIT |
					 JEDEC_BFPT_DW16_ENTER_NV_CONFIG_REG |
					 JEDEC_BFPT_DW16_VENDOR_SET |
					 JEDEC_BFPT_DW16_4_BYTES_ADDRESS_ONLY */ ;

	free(*ptbuf);
	*ptbuf = tbufsim;
	*plen = lensim;
	return 0;
}

/* This simulation created fake 4-bytes Address Instruction Table
   with features information to test JESD216B compliance. */
int sfdp_jesd216b_simulation_4bait(uint8_t** ptbuf, uint16_t* plen)
{
	uint8_t* tbufsim;
	uint16_t lensim = 2 * 4;

	tbufsim = malloc(lensim);
	if (tbufsim == NULL) {
		msg_gerr("Out of memory!\n");
		return 1;
	}

	msg_cdbg("\n=== SIMULATION of JESD216B 4-bytes Address Instruction Table\n");

	*((uint32_t*)&tbufsim[0]) = JEDEC_4BAIT_READ_SUPPORT /*|
				    JEDEC_4BAIT_PROGRAM_SUPPORT |
				    JEDEC_4BAIT_ERASE_TYPE_1_SUPPORT |
				    JEDEC_4BAIT_ERASE_TYPE_2_SUPPORT |
				    JEDEC_4BAIT_ERASE_TYPE_3_SUPPORT |
				    JEDEC_4BAIT_ERASE_TYPE_4_SUPPORT */;
	*((uint32_t*)&tbufsim[4]) = 0xFFFFFFFF;
	/* *((uint32_t*)&tbufsim[4]) = 0xFFDC5C21; */

	free(*ptbuf);
	*ptbuf = tbufsim;
	*plen = lensim;
	return 0;
}
#endif

int probe_spi_sfdp(struct flashctx *flash)
{
	int ret = 0;
	uint8_t buf[8];
	uint32_t tmp32;
	uint8_t nph;
	/* need to limit the table loop by comparing i to uint8_t nph hence: */
	uint16_t i;
	struct sfdp_tbl_hdr *hdrs;
	uint8_t *hbuf;
	uint8_t *tbuf;
	int sfdp_rev_16 = 0, sfdp_rev_15 = 0;

	if (spi_sfdp_read_sfdp(flash, 0x00, buf, 4)) {
		msg_cdbg("Receiving SFDP signature failed.\n");
		return 0;
	}
	tmp32 = buf[0];
	tmp32 |= ((unsigned int)buf[1]) << 8;
	tmp32 |= ((unsigned int)buf[2]) << 16;
	tmp32 |= ((unsigned int)buf[3]) << 24;

	if (tmp32 != 0x50444653) {
		msg_cdbg2("Signature = 0x%08x (should be 0x50444653)\n", tmp32);
		msg_cdbg("No SFDP signature found.\n");
		return 0;
	}

	if (spi_sfdp_read_sfdp(flash, 0x04, buf, 3)) {
		msg_cdbg("Receiving SFDP revision and number of parameter "
			 "headers (NPH) failed. ");
		return 0;
	}
	msg_cdbg2("SFDP revision = %d.%d\n", buf[1], buf[0]);
	if (buf[1] != 0x01) {
		msg_cdbg("The chip supports an unknown version of SFDP. "
			  "Aborting SFDP probe!\n");
		return 0;
	}

	/* JEDEC JESD216B defines SFDP revision 1.6 and includes:
	   1) 16 dwords in Basic Flash Parameter Table
	   2) 16th dword has information how to enter
		and exit 4-bytes addressing mode
	   3) 4-Bytes Address Instruction Table with ID 0xFF84

		However we can see in the datasheet for Micron's
	   MT25Q 512Mb chip (MT25QL512AB/MT25QU512AB) that the
	   chip returnes SFDP revision 1.5 and has 16 dwords
	   in its Basic Flash Paramater Table. Also the information
	   about addressing mode switch is exist in the 16th dword.
	   But 4-Bytes Address Instruction Table is absent.

		So we will use 16th dword from SFDP revision 1.5
	   but 4-Bytes Address Instruction Table from SFDP 1.6 only.
	   This assumption is made for better support of Micron
	   flash chips.

		FIXME: SFDP revisions compliance should be checked
	   more carefully after more information about JESD216B
	   SFDP tables will be known from real flash chips.
	*/
	sfdp_rev_16 = (buf[1] == 1 && buf[0] >= 6) || buf[1] > 1;
	sfdp_rev_15 = (buf[1] == 1 && buf[0] >= 5) || buf[1] > 1;

	nph = buf[2];
	msg_cdbg2("SFDP number of parameter headers is %d (NPH = %d).\n",
		  nph + 1, nph);

	/* Fetch all parameter headers, even if we don't use them all (yet). */
	hbuf = malloc((nph + 1) * 8);
	hdrs = malloc((nph + 1) * sizeof(struct sfdp_tbl_hdr));
	if (hbuf == NULL || hdrs == NULL ) {
		msg_gerr("Out of memory!\n");
		goto cleanup_hdrs;
	}
	if (spi_sfdp_read_sfdp(flash, 0x08, hbuf, (nph + 1) * 8)) {
		msg_cdbg("Receiving SFDP parameter table headers failed.\n");
		goto cleanup_hdrs;
	}

	for (i = 0; i <= nph; i++) {
		uint16_t len;
		hdrs[i].id = hbuf[(8 * i) + 0]; /* ID LSB read */
		hdrs[i].v_minor = hbuf[(8 * i) + 1];
		hdrs[i].v_major = hbuf[(8 * i) + 2];
		hdrs[i].len = hbuf[(8 * i) + 3];
		hdrs[i].ptp = hbuf[(8 * i) + 4];
		hdrs[i].ptp |= ((unsigned int)hbuf[(8 * i) + 5]) << 8;
		hdrs[i].ptp |= ((unsigned int)hbuf[(8 * i) + 6]) << 16;
		hdrs[i].id |= ((uint16_t)hbuf[(8 * i) + 7]) << 8; /* ID MSB read */
		msg_cdbg2("\nSFDP parameter table header %d/%d:\n", i, nph);
		msg_cdbg2("  ID 0x%02x, version %d.%d\n", hdrs[i].id,
			  hdrs[i].v_major, hdrs[i].v_minor);
		len = hdrs[i].len * 4;
		tmp32 = hdrs[i].ptp;
		msg_cdbg2("  Length %d B, Parameter Table Pointer 0x%06x\n",
			  len, tmp32);

		if (tmp32 + len >= (1 << 24)) {
			msg_cdbg("SFDP Parameter Table %d supposedly overflows "
				  "addressable SFDP area. This most\nprobably "
				  "indicates a corrupt SFDP parameter table "
				  "header. Skipping it.\n", i);
			continue;
		}

		tbuf = malloc(len);
		if (tbuf == NULL) {
			msg_gerr("Out of memory!\n");
			goto cleanup_hdrs;
		}
		if (spi_sfdp_read_sfdp(flash, tmp32, tbuf, len)){
			msg_cdbg("Fetching SFDP parameter table %d failed.\n",
				 i);
			free(tbuf);
			continue;
		}
		msg_cspew("  Parameter table contents:\n");
		for (tmp32 = 0; tmp32 < len; tmp32++) {
			if ((tmp32 % 8) == 0) {
				msg_cspew("    0x%04x: ", tmp32);
			}
			msg_cspew(" %02x", tbuf[tmp32]);
			if ((tmp32 % 8) == 7) {
				msg_cspew("\n");
				continue;
			}
			if ((tmp32 % 8) == 3) {
				msg_cspew(" ");
				continue;
			}
		}
		msg_cspew("\n");

		if (i == 0) { /* Mandatory JEDEC SFDP parameter table */
			if (hdrs[i].id != JEDEC_BFPT_ID)
				msg_cdbg("ID of the mandatory JEDEC SFDP "
					 "parameter table is not 0xFF00 as"
					 "demanded by JESD216 (warning only)."
					 "\n");
#ifdef JESD216B_SIMULATION
			if(!sfdp_jesd216b_simulation_dw16(&tbuf, &len))
				sfdp_rev_16 = sfdp_rev_15 = 1; /* pretend as SFDP rev 1.6 */
#endif
			if (hdrs[i].v_major != 0x01) {
				msg_cdbg("The chip contains an unknown "
					  "version of the JEDEC flash "
					  "parameters table, skipping it.\n");
			} else if (len != 16 * 4 && len != 9 * 4 && len != 4 * 4) {
				msg_cdbg("Length of the mandatory JEDEC SFDP "
					 "parameter table is wrong (%d B), "
					 "skipping it.\n", len);
			} else if (sfdp_fill_flash(flash->chip, tbuf, len, sfdp_rev_15) == 0)
				ret = 1;
#ifdef JESD216B_SIMULATION
			if(ret == 1 && !sfdp_jesd216b_simulation_4bait(&tbuf, &len))
				sfdp_parse_4ba_table(flash->chip, tbuf, len);
#endif
		}
		/* JEDEC SFDP 4-byte address instruction table. From SFDP revision 1.6 only.
		   This parsing shoukd be called after basic flash parameter table is parsed. */
		else if(sfdp_rev_16 && hdrs[i].id == JEDEC_4BAIT_ID && ret == 1) {
			if (hdrs[i].v_major != 0x01) {
				msg_cdbg("The chip contains an unknown "
					  "version of the JEDEC 4-bytes "
					  "address instruction table, "
					  "skipping it.\n");
			}
			else {  /* no result check because this table is optional */
				sfdp_parse_4ba_table(flash->chip, tbuf, len);
			}
		}
		free(tbuf);
	}

cleanup_hdrs:
	free(hdrs);
	free(hbuf);
	return ret;
}
OpenPOWER on IntegriCloud