forked from flashrom/flashrom
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ichspi.c
2497 lines (2204 loc) · 77.7 KB
/
ichspi.c
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
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* This file is part of the flashrom project.
*
* Copyright (C) 2008 Stefan Wildemann <stefan.wildemann@kontron.com>
* Copyright (C) 2008 Claus Gindhart <claus.gindhart@kontron.com>
* Copyright (C) 2008 Dominik Geyer <dominik.geyer@kontron.com>
* Copyright (C) 2008 coresystems GmbH <info@coresystems.de>
* Copyright (C) 2009, 2010 Carl-Daniel Hailfinger
* Copyright (C) 2011 Stefan Tauner
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*/
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include "flash.h"
#include "programmer.h"
#include "hwaccess_physmap.h"
#include "spi.h"
#include "ich_descriptors.h"
/* Apollo Lake */
#define APL_REG_FREG12 0xe0 /* 32 Bytes Flash Region 12 */
/* Sunrise Point */
/* Added HSFS Status bits */
#define HSFS_WRSDIS_OFF 11 /* 11: Flash Configuration Lock-Down */
#define HSFS_WRSDIS (0x1 << HSFS_WRSDIS_OFF)
#define HSFS_PRR34_LOCKDN_OFF 12 /* 12: PRR3 PRR4 Lock-Down */
#define HSFS_PRR34_LOCKDN (0x1 << HSFS_PRR34_LOCKDN_OFF)
/* HSFS_BERASE vanished */
/*
* HSFC and HSFS 16-bit registers are combined into the 32-bit
* BIOS_HSFSTS_CTL register in the Sunrise Point datasheet,
* however we still treat them separately in order to reuse code.
*/
/*
* HSFC Control bits
*
* FCYCLE is a 2 bit field (HSFC bits 1-2) on ICH9 and 4 bit field
* (HSFC bits 1-4) on PCH100.
*
* ICH9 and PCH100 use the same FCYCLE values for flash operations,
* however FCYCLE values above 3 are only supported by PCH100.
*
* 0: SPI Read
* 2: SPI Write
* 3: SPI Erase 4K
* 4: SPI Erase 64K
* 6: SPI RDID
* 7: SPI Write Status
* 8: SPI Read Status
*/
#define HSFC_FGO_OFF 0
#define HSFC_FGO (0x1 << HSFC_FGO_OFF)
#define HSFC_FCYCLE_MASK(n) ((n) << HSFC_FCYCLE_OFF)
#define HSFC_FCYCLE_OFF 1
#define HSFC_CYCLE_READ HSFC_FCYCLE_MASK(0x0)
#define HSFC_CYCLE_WRITE HSFC_FCYCLE_MASK(0x2)
#define HSFC_CYCLE_BLOCK_ERASE HSFC_FCYCLE_MASK(0x3)
#define HSFC_CYCLE_RDID HSFC_FCYCLE_MASK(0x6)
#define HSFC_CYCLE_WR_STATUS HSFC_FCYCLE_MASK(0x7)
#define HSFC_CYCLE_RD_STATUS HSFC_FCYCLE_MASK(0x8)
/* PCH100 controller register definition */
#define PCH100_HSFC_FCYCLE_OFF 1
#define PCH100_HSFC_FCYCLE_BIT_WIDTH 0xf
#define PCH100_HSFC_FCYCLE HSFC_FCYCLE_MASK(PCH100_HSFC_FCYCLE_BIT_WIDTH)
/* New HSFC Control bit */
#define PCH100_HSFC_WET_OFF (21 - 16) /* 5: Write Enable Type */
#define PCH100_HSFC_WET (0x1 << PCH100_HSFC_WET_OFF)
#define PCH100_FADDR_FLA 0x07ffffff
#define PCH100_REG_DLOCK 0x0c /* 32 Bits Discrete Lock Bits */
#define DLOCK_BMWAG_LOCKDN_OFF 0
#define DLOCK_BMWAG_LOCKDN (0x1 << DLOCK_BMWAG_LOCKDN_OFF)
#define DLOCK_BMRAG_LOCKDN_OFF 1
#define DLOCK_BMRAG_LOCKDN (0x1 << DLOCK_BMRAG_LOCKDN_OFF)
#define DLOCK_SBMWAG_LOCKDN_OFF 2
#define DLOCK_SBMWAG_LOCKDN (0x1 << DLOCK_SBMWAG_LOCKDN_OFF)
#define DLOCK_SBMRAG_LOCKDN_OFF 3
#define DLOCK_SBMRAG_LOCKDN (0x1 << DLOCK_SBMRAG_LOCKDN_OFF)
#define DLOCK_PR0_LOCKDN_OFF 8
#define DLOCK_PR0_LOCKDN (0x1 << DLOCK_PR0_LOCKDN_OFF)
#define DLOCK_PR1_LOCKDN_OFF 9
#define DLOCK_PR1_LOCKDN (0x1 << DLOCK_PR1_LOCKDN_OFF)
#define DLOCK_PR2_LOCKDN_OFF 10
#define DLOCK_PR2_LOCKDN (0x1 << DLOCK_PR2_LOCKDN_OFF)
#define DLOCK_PR3_LOCKDN_OFF 11
#define DLOCK_PR3_LOCKDN (0x1 << DLOCK_PR3_LOCKDN_OFF)
#define DLOCK_PR4_LOCKDN_OFF 12
#define DLOCK_PR4_LOCKDN (0x1 << DLOCK_PR4_LOCKDN_OFF)
#define DLOCK_SSEQ_LOCKDN_OFF 16
#define DLOCK_SSEQ_LOCKDN (0x1 << DLOCK_SSEQ_LOCKDN_OFF)
#define PCH100_REG_FPR0 0x84 /* 32 Bits Protected Range 0 */
#define PCH100_REG_GPR0 0x98 /* 32 Bits Global Protected Range 0 */
#define PCH100_REG_SSFSC 0xA0 /* 32 Bits Status (8) + Control (24) */
#define PCH100_REG_PREOP 0xA4 /* 16 Bits */
#define PCH100_REG_OPTYPE 0xA6 /* 16 Bits */
#define PCH100_REG_OPMENU 0xA8 /* 64 Bits */
/* ICH9 controller register definition */
#define ICH9_REG_HSFS 0x04 /* 16 Bits Hardware Sequencing Flash Status */
#define HSFS_FDONE_OFF 0 /* 0: Flash Cycle Done */
#define HSFS_FDONE (0x1 << HSFS_FDONE_OFF)
#define HSFS_FCERR_OFF 1 /* 1: Flash Cycle Error */
#define HSFS_FCERR (0x1 << HSFS_FCERR_OFF)
#define HSFS_AEL_OFF 2 /* 2: Access Error Log */
#define HSFS_AEL (0x1 << HSFS_AEL_OFF)
#define HSFS_BERASE_OFF 3 /* 3-4: Block/Sector Erase Size */
#define HSFS_BERASE (0x3 << HSFS_BERASE_OFF)
#define HSFS_SCIP_OFF 5 /* 5: SPI Cycle In Progress */
#define HSFS_SCIP (0x1 << HSFS_SCIP_OFF)
/* 6-12: reserved */
#define HSFS_FDOPSS_OFF 13 /* 13: Flash Descriptor Override Pin-Strap Status */
#define HSFS_FDOPSS (0x1 << HSFS_FDOPSS_OFF)
#define HSFS_FDV_OFF 14 /* 14: Flash Descriptor Valid */
#define HSFS_FDV (0x1 << HSFS_FDV_OFF)
#define HSFS_FLOCKDN_OFF 15 /* 15: Flash Configuration Lock-Down */
#define HSFS_FLOCKDN (0x1 << HSFS_FLOCKDN_OFF)
#define ICH9_REG_HSFC 0x06 /* 16 Bits Hardware Sequencing Flash Control */
/* 0: Flash Cycle Go */
/* 1-2: FLASH Cycle */
#define ICH9_HSFC_FCYCLE_OFF 1
#define ICH9_HSFC_FCYCLE_BIT_WIDTH 3
#define ICH9_HSFC_FCYCLE HSFC_FCYCLE_MASK(ICH9_HSFC_FCYCLE_BIT_WIDTH)
/* 3-7: reserved */
#define HSFC_FDBC_OFF 8 /* 8-13: Flash Data Byte Count */
#define HSFC_FDBC (0x3f << HSFC_FDBC_OFF)
#define HSFC_FDBC_VAL(n) (((n) << HSFC_FDBC_OFF) & HSFC_FDBC)
/* 14: reserved */
#define HSFC_SME_OFF 15 /* 15: SPI SMI# Enable */
#define HSFC_SME (0x1 << HSFC_SME_OFF)
#define ICH9_REG_FADDR 0x08 /* 32 Bits */
#define ICH9_FADDR_FLA 0x01ffffff
#define ICH9_REG_FDATA0 0x10 /* 64 Bytes */
#define ICH_REG_BIOS_BM_RAP 0x118 /* 16 Bits BIOS Master Read Access Permissions */
#define ICH_REG_BIOS_BM_WAP 0x11c /* 16 Bits BIOS Master Write Access Permissions */
#define ICH9_REG_FRAP 0x50 /* 32 Bytes Flash Region Access Permissions */
#define ICH9_REG_FREG0 0x54 /* 32 Bytes Flash Region 0 */
#define ICH9_REG_PR0 0x74 /* 32 Bytes Protected Range 0 */
#define PR_WP_OFF 31 /* 31: write protection enable */
#define PR_RP_OFF 15 /* 15: read protection enable */
#define ICH9_REG_SSFS 0x90 /* 08 Bits */
#define SSFS_SCIP_OFF 0 /* SPI Cycle In Progress */
#define SSFS_SCIP (0x1 << SSFS_SCIP_OFF)
#define SSFS_FDONE_OFF 2 /* Cycle Done Status */
#define SSFS_FDONE (0x1 << SSFS_FDONE_OFF)
#define SSFS_FCERR_OFF 3 /* Flash Cycle Error */
#define SSFS_FCERR (0x1 << SSFS_FCERR_OFF)
#define SSFS_AEL_OFF 4 /* Access Error Log */
#define SSFS_AEL (0x1 << SSFS_AEL_OFF)
/* The following bits are reserved in SSFS: 1,5-7. */
#define SSFS_RESERVED_MASK 0x000000e2
#define ICH9_REG_SSFC 0x91 /* 24 Bits */
/* We combine SSFS and SSFC to one 32-bit word,
* therefore SSFC bits are off by 8. */
/* 0: reserved */
#define SSFC_SCGO_OFF (1 + 8) /* 1: SPI Cycle Go */
#define SSFC_SCGO (0x1 << SSFC_SCGO_OFF)
#define SSFC_ACS_OFF (2 + 8) /* 2: Atomic Cycle Sequence */
#define SSFC_ACS (0x1 << SSFC_ACS_OFF)
#define SSFC_SPOP_OFF (3 + 8) /* 3: Sequence Prefix Opcode Pointer */
#define SSFC_SPOP (0x1 << SSFC_SPOP_OFF)
#define SSFC_COP_OFF (4 + 8) /* 4-6: Cycle Opcode Pointer */
#define SSFC_COP (0x7 << SSFC_COP_OFF)
/* 7: reserved */
#define SSFC_DBC_OFF (8 + 8) /* 8-13: Data Byte Count */
#define SSFC_DBC (0x3f << SSFC_DBC_OFF)
#define SSFC_DS_OFF (14 + 8) /* 14: Data Cycle */
#define SSFC_DS (0x1 << SSFC_DS_OFF)
#define SSFC_SME_OFF (15 + 8) /* 15: SPI SMI# Enable */
#define SSFC_SME (0x1 << SSFC_SME_OFF)
#define SSFC_SCF_OFF (16 + 8) /* 16-18: SPI Cycle Frequency */
#define SSFC_SCF (0x7 << SSFC_SCF_OFF)
#define SSFC_SCF_20MHZ 0x00000000
#define SSFC_SCF_33MHZ 0x01000000
/* 19-23: reserved */
#define SSFC_RESERVED_MASK 0xf8008100
#define ICH9_REG_PREOP 0x94 /* 16 Bits */
#define ICH9_REG_OPTYPE 0x96 /* 16 Bits */
#define ICH9_REG_OPMENU 0x98 /* 64 Bits */
#define ICH9_REG_BBAR 0xA0 /* 32 Bits BIOS Base Address Configuration */
#define BBAR_MASK 0x00ffff00 /* 8-23: Bottom of System Flash */
#define ICH8_REG_VSCC 0xC1 /* 32 Bits Vendor Specific Component Capabilities */
#define ICH9_REG_LVSCC 0xC4 /* 32 Bits Host Lower Vendor Specific Component Capabilities */
#define ICH9_REG_UVSCC 0xC8 /* 32 Bits Host Upper Vendor Specific Component Capabilities */
/* The individual fields of the VSCC registers are defined in the file
* ich_descriptors.h. The reason is that the same layout is also used in the
* flash descriptor to define the properties of the different flash chips
* supported. The BIOS (or the ME?) is responsible to populate the ICH registers
* with the information from the descriptor on startup depending on the actual
* chip(s) detected. */
#define ICH9_REG_FPB 0xD0 /* 32 Bits Flash Partition Boundary */
#define FPB_FPBA_OFF 0 /* 0-12: Block/Sector Erase Size */
#define FPB_FPBA (0x1FFF << FPB_FPBA_OFF)
// ICH9R SPI commands
#define SPI_OPCODE_TYPE_READ_NO_ADDRESS 0
#define SPI_OPCODE_TYPE_WRITE_NO_ADDRESS 1
#define SPI_OPCODE_TYPE_READ_WITH_ADDRESS 2
#define SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS 3
// ICH7 registers
#define ICH7_REG_SPIS 0x00 /* 16 Bits */
#define SPIS_SCIP 0x0001
#define SPIS_GRANT 0x0002
#define SPIS_CDS 0x0004
#define SPIS_FCERR 0x0008
#define SPIS_RESERVED_MASK 0x7ff0
/* VIA SPI is compatible with ICH7, but maxdata
to transfer is 16 bytes.
DATA byte count on ICH7 is 8:13, on VIA 8:11
bit 12 is port select CS0 CS1
bit 13 is FAST READ enable
bit 7 is used with fast read and one shot controls CS de-assert?
*/
#define ICH7_REG_SPIC 0x02 /* 16 Bits */
#define SPIC_SCGO 0x0002
#define SPIC_ACS 0x0004
#define SPIC_SPOP 0x0008
#define SPIC_DS 0x4000
#define ICH7_REG_SPIA 0x04 /* 32 Bits */
#define ICH7_REG_SPID0 0x08 /* 64 Bytes */
#define ICH7_REG_PREOP 0x54 /* 16 Bits */
#define ICH7_REG_OPTYPE 0x56 /* 16 Bits */
#define ICH7_REG_OPMENU 0x58 /* 64 Bits */
enum ich_access_protection {
NO_PROT = 0,
READ_PROT = 1,
WRITE_PROT = 2,
LOCKED = 3,
};
/* ICH SPI configuration lock-down. May be set during chipset enabling. */
static bool ichspi_lock = false;
static enum ich_chipset ich_generation = CHIPSET_ICH_UNKNOWN;
static uint32_t ichspi_bbar;
static void *ich_spibar = NULL;
typedef struct _OPCODE {
uint8_t opcode; //This commands spi opcode
uint8_t spi_type; //This commands spi type
uint8_t atomic; //Use preop: (0: none, 1: preop0, 2: preop1
} OPCODE;
/* Suggested opcode definition:
* Preop 1: Write Enable
* Preop 2: Write Status register enable
*
* OP 0: Write address
* OP 1: Read Address
* OP 2: ERASE block
* OP 3: Read Status register
* OP 4: Read ID
* OP 5: Write Status register
* OP 6: chip private (read JEDEC id)
* OP 7: Chip erase
*/
typedef struct _OPCODES {
uint8_t preop[2];
OPCODE opcode[8];
} OPCODES;
static OPCODES *curopcodes = NULL;
/* HW access functions */
static uint32_t REGREAD32(int X)
{
return mmio_readl(ich_spibar + X);
}
static uint16_t REGREAD16(int X)
{
return mmio_readw(ich_spibar + X);
}
static uint16_t REGREAD8(int X)
{
return mmio_readb(ich_spibar + X);
}
#define REGWRITE32(off, val) mmio_writel(val, ich_spibar+(off))
#define REGWRITE16(off, val) mmio_writew(val, ich_spibar+(off))
#define REGWRITE8(off, val) mmio_writeb(val, ich_spibar+(off))
/* Common SPI functions */
static int find_opcode(OPCODES *op, uint8_t opcode)
{
int a;
if (op == NULL) {
msg_perr("\n%s: null OPCODES pointer!\n", __func__);
return -1;
}
for (a = 0; a < 8; a++) {
if (op->opcode[a].opcode == opcode)
return a;
}
return -1;
}
static int find_preop(OPCODES *op, uint8_t preop)
{
int a;
if (op == NULL) {
msg_perr("\n%s: null OPCODES pointer!\n", __func__);
return -1;
}
for (a = 0; a < 2; a++) {
if (op->preop[a] == preop)
return a;
}
return -1;
}
/* for pairing opcodes with their required preop */
struct preop_opcode_pair {
uint8_t preop;
uint8_t opcode;
};
/* List of opcodes which need preopcodes and matching preopcodes. Unused. */
const struct preop_opcode_pair pops[] = {
{JEDEC_WREN, JEDEC_BYTE_PROGRAM},
{JEDEC_WREN, JEDEC_SE}, /* sector erase */
{JEDEC_WREN, JEDEC_BE_52}, /* block erase */
{JEDEC_WREN, JEDEC_BE_D8}, /* block erase */
{JEDEC_WREN, JEDEC_CE_60}, /* chip erase */
{JEDEC_WREN, JEDEC_CE_C7}, /* chip erase */
/* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
{JEDEC_WREN, JEDEC_WRSR},
{JEDEC_EWSR, JEDEC_WRSR},
{0,}
};
/* Reasonable default configuration. Needs ad-hoc modifications if we
* encounter unlisted opcodes. Fun.
*/
static OPCODES O_ST_M25P = {
{
JEDEC_WREN,
JEDEC_EWSR,
},
{
{JEDEC_BYTE_PROGRAM, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Write Byte
{JEDEC_READ, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Read Data
{JEDEC_SE, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Erase Sector
{JEDEC_RDSR, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read Device Status Reg
{JEDEC_REMS, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Read Electronic Manufacturer Signature
{JEDEC_WRSR, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Write Status Register
{JEDEC_RDID, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read JDEC ID
{JEDEC_CE_C7, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Bulk erase
}
};
/* List of opcodes with their corresponding spi_type
* It is used to reprogram the chipset OPCODE table on-the-fly if an opcode
* is needed which is currently not in the chipset OPCODE table
*/
static OPCODE POSSIBLE_OPCODES[] = {
{JEDEC_BYTE_PROGRAM, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Write Byte
{JEDEC_READ, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Read Data
{JEDEC_BE_D8, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Erase Sector
{JEDEC_RDSR, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read Device Status Reg
{JEDEC_REMS, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Read Electronic Manufacturer Signature
{JEDEC_WRSR, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Write Status Register
{JEDEC_RDID, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read JDEC ID
{JEDEC_CE_C7, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Bulk erase
{JEDEC_SE, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Sector erase
{JEDEC_BE_52, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Block erase
{JEDEC_AAI_WORD_PROGRAM, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Auto Address Increment
};
static OPCODES O_EXISTING = {};
/* pretty printing functions */
static void prettyprint_opcodes(OPCODES *ops)
{
OPCODE oc;
const char *t;
const char *a;
uint8_t i;
static const char *const spi_type[4] = {
"read w/o addr",
"write w/o addr",
"read w/ addr",
"write w/ addr"
};
static const char *const atomic_type[3] = {
"none",
" 0 ",
" 1 "
};
if (ops == NULL)
return;
msg_pdbg2(" OP Type Pre-OP\n");
for (i = 0; i < 8; i++) {
oc = ops->opcode[i];
t = (oc.spi_type > 3) ? "invalid" : spi_type[oc.spi_type];
a = (oc.atomic > 2) ? "invalid" : atomic_type[oc.atomic];
msg_pdbg2("op[%d]: 0x%02x, %s, %s\n", i, oc.opcode, t, a);
}
msg_pdbg2("Pre-OP 0: 0x%02x, Pre-OP 1: 0x%02x\n", ops->preop[0],
ops->preop[1]);
}
#define pprint_reg16(reg, bit, val, sep) msg_pdbg("%s=%"PRId16"" sep, #bit, (val & reg##_##bit) >> reg##_##bit##_OFF)
#define pprint_reg32(reg, bit, val, sep) msg_pdbg("%s=%"PRId32"" sep, #bit, (val & reg##_##bit) >> reg##_##bit##_OFF)
static void prettyprint_ich9_reg_hsfs(uint16_t reg_val, enum ich_chipset ich_gen)
{
msg_pdbg("HSFS: ");
pprint_reg16(HSFS, FDONE, reg_val, ", ");
pprint_reg16(HSFS, FCERR, reg_val, ", ");
pprint_reg16(HSFS, AEL, reg_val, ", ");
switch (ich_gen) {
case CHIPSET_100_SERIES_SUNRISE_POINT:
case CHIPSET_C620_SERIES_LEWISBURG:
case CHIPSET_C740_SERIES_EMMITSBURG:
case CHIPSET_300_SERIES_CANNON_POINT:
case CHIPSET_400_SERIES_COMET_POINT:
case CHIPSET_500_SERIES_TIGER_POINT:
case CHIPSET_ELKHART_LAKE:
break;
default:
pprint_reg16(HSFS, BERASE, reg_val, ", ");
break;
}
pprint_reg16(HSFS, SCIP, reg_val, ", ");
switch (ich_gen) {
case CHIPSET_100_SERIES_SUNRISE_POINT:
case CHIPSET_C620_SERIES_LEWISBURG:
case CHIPSET_C740_SERIES_EMMITSBURG:
case CHIPSET_300_SERIES_CANNON_POINT:
case CHIPSET_400_SERIES_COMET_POINT:
case CHIPSET_500_SERIES_TIGER_POINT:
case CHIPSET_ELKHART_LAKE:
pprint_reg16(HSFS, PRR34_LOCKDN, reg_val, ", ");
pprint_reg16(HSFS, WRSDIS, reg_val, ", ");
break;
default:
break;
}
pprint_reg16(HSFS, FDOPSS, reg_val, ", ");
pprint_reg16(HSFS, FDV, reg_val, ", ");
pprint_reg16(HSFS, FLOCKDN, reg_val, "\n");
}
static void prettyprint_ich9_reg_hsfc(uint16_t reg_val, enum ich_chipset ich_gen)
{
msg_pdbg("HSFC: ");
pprint_reg16(HSFC, FGO, reg_val, ", ");
switch (ich_gen) {
case CHIPSET_100_SERIES_SUNRISE_POINT:
case CHIPSET_C620_SERIES_LEWISBURG:
case CHIPSET_C740_SERIES_EMMITSBURG:
case CHIPSET_300_SERIES_CANNON_POINT:
case CHIPSET_400_SERIES_COMET_POINT:
case CHIPSET_500_SERIES_TIGER_POINT:
case CHIPSET_ELKHART_LAKE:
pprint_reg16(PCH100_HSFC, FCYCLE, reg_val, ", ");
pprint_reg16(PCH100_HSFC, WET, reg_val, ", ");
break;
default:
pprint_reg16(ICH9_HSFC, FCYCLE, reg_val, ", ");
break;
}
pprint_reg16(HSFC, FDBC, reg_val, ", ");
pprint_reg16(HSFC, SME, reg_val, "\n");
}
static void prettyprint_ich9_reg_ssfs(uint32_t reg_val)
{
msg_pdbg("SSFS: ");
pprint_reg32(SSFS, SCIP, reg_val, ", ");
pprint_reg32(SSFS, FDONE, reg_val, ", ");
pprint_reg32(SSFS, FCERR, reg_val, ", ");
pprint_reg32(SSFS, AEL, reg_val, "\n");
}
static void prettyprint_ich9_reg_ssfc(uint32_t reg_val)
{
msg_pdbg("SSFC: ");
pprint_reg32(SSFC, SCGO, reg_val, ", ");
pprint_reg32(SSFC, ACS, reg_val, ", ");
pprint_reg32(SSFC, SPOP, reg_val, ", ");
pprint_reg32(SSFC, COP, reg_val, ", ");
pprint_reg32(SSFC, DBC, reg_val, ", ");
pprint_reg32(SSFC, SME, reg_val, ", ");
pprint_reg32(SSFC, SCF, reg_val, "\n");
}
static void prettyprint_pch100_reg_dlock(const uint32_t reg_val)
{
msg_pdbg("DLOCK: ");
pprint_reg32(DLOCK, BMWAG_LOCKDN, reg_val, ", ");
pprint_reg32(DLOCK, BMRAG_LOCKDN, reg_val, ", ");
pprint_reg32(DLOCK, SBMWAG_LOCKDN, reg_val, ", ");
pprint_reg32(DLOCK, SBMRAG_LOCKDN, reg_val, ",\n ");
pprint_reg32(DLOCK, PR0_LOCKDN, reg_val, ", ");
pprint_reg32(DLOCK, PR1_LOCKDN, reg_val, ", ");
pprint_reg32(DLOCK, PR2_LOCKDN, reg_val, ", ");
pprint_reg32(DLOCK, PR3_LOCKDN, reg_val, ", ");
pprint_reg32(DLOCK, PR4_LOCKDN, reg_val, ",\n ");
pprint_reg32(DLOCK, SSEQ_LOCKDN, reg_val, "\n");
}
static struct swseq_data {
size_t reg_ssfsc;
size_t reg_preop;
size_t reg_optype;
size_t reg_opmenu;
} swseq_data;
static uint8_t lookup_spi_type(uint8_t opcode)
{
unsigned int a;
for (a = 0; a < ARRAY_SIZE(POSSIBLE_OPCODES); a++) {
if (POSSIBLE_OPCODES[a].opcode == opcode)
return POSSIBLE_OPCODES[a].spi_type;
}
return 0xFF;
}
static int program_opcodes(OPCODES *op, int enable_undo, enum ich_chipset ich_gen)
{
uint8_t a;
uint16_t preop, optype;
uint32_t opmenu[2];
/* Program Prefix Opcodes */
/* 0:7 Prefix Opcode 1 */
preop = (op->preop[0]);
/* 8:16 Prefix Opcode 2 */
preop |= ((uint16_t) op->preop[1]) << 8;
/* Program Opcode Types 0 - 7 */
optype = 0;
for (a = 0; a < 8; a++) {
optype |= ((uint16_t) op->opcode[a].spi_type) << (a * 2);
}
/* Program Allowable Opcodes 0 - 3 */
opmenu[0] = 0;
for (a = 0; a < 4; a++) {
opmenu[0] |= ((uint32_t) op->opcode[a].opcode) << (a * 8);
}
/* Program Allowable Opcodes 4 - 7 */
opmenu[1] = 0;
for (a = 4; a < 8; a++) {
opmenu[1] |= ((uint32_t) op->opcode[a].opcode) << ((a - 4) * 8);
}
msg_pdbg2("\n%s: preop=%04x optype=%04x opmenu=%08"PRIx32"%08"PRIx32"\n", __func__, preop, optype, opmenu[0], opmenu[1]);
switch (ich_gen) {
case CHIPSET_ICH7:
case CHIPSET_TUNNEL_CREEK:
case CHIPSET_CENTERTON:
/* Register undo only for enable_undo=1, i.e. first call. */
if (enable_undo) {
rmmio_valw(ich_spibar + ICH7_REG_PREOP);
rmmio_valw(ich_spibar + ICH7_REG_OPTYPE);
rmmio_vall(ich_spibar + ICH7_REG_OPMENU);
rmmio_vall(ich_spibar + ICH7_REG_OPMENU + 4);
}
mmio_writew(preop, ich_spibar + ICH7_REG_PREOP);
mmio_writew(optype, ich_spibar + ICH7_REG_OPTYPE);
mmio_writel(opmenu[0], ich_spibar + ICH7_REG_OPMENU);
mmio_writel(opmenu[1], ich_spibar + ICH7_REG_OPMENU + 4);
break;
case CHIPSET_ICH8:
default: /* Future version might behave the same */
/* Register undo only for enable_undo=1, i.e. first call. */
if (enable_undo) {
rmmio_valw(ich_spibar + swseq_data.reg_preop);
rmmio_valw(ich_spibar + swseq_data.reg_optype);
rmmio_vall(ich_spibar + swseq_data.reg_opmenu);
rmmio_vall(ich_spibar + swseq_data.reg_opmenu + 4);
}
mmio_writew(preop, ich_spibar + swseq_data.reg_preop);
mmio_writew(optype, ich_spibar + swseq_data.reg_optype);
mmio_writel(opmenu[0], ich_spibar + swseq_data.reg_opmenu);
mmio_writel(opmenu[1], ich_spibar + swseq_data.reg_opmenu + 4);
break;
}
return 0;
}
static int reprogram_opcode_on_the_fly(uint8_t opcode, unsigned int writecnt, unsigned int readcnt)
{
uint8_t spi_type;
spi_type = lookup_spi_type(opcode);
if (spi_type > 3) {
/* Try to guess spi type from read/write sizes.
* The following valid writecnt/readcnt combinations exist:
* writecnt = 4, readcnt >= 0
* writecnt = 1, readcnt >= 0
* writecnt >= 4, readcnt = 0
* writecnt >= 1, readcnt = 0
* writecnt >= 1 is guaranteed for all commands.
*/
if (readcnt == 0)
/* if readcnt=0 and writecount >= 4, we don't know if it is WRITE_NO_ADDRESS
* or WRITE_WITH_ADDRESS. But if we use WRITE_NO_ADDRESS and the first 3 data
* bytes are actual the address, they go to the bus anyhow
*/
spi_type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS;
else if (writecnt == 1) // and readcnt is > 0
spi_type = SPI_OPCODE_TYPE_READ_NO_ADDRESS;
else if (writecnt == 4) // and readcnt is > 0
spi_type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
else // we have an invalid case
return SPI_INVALID_LENGTH;
}
int oppos = 2; // use original JEDEC_BE_D8 offset
curopcodes->opcode[oppos].opcode = opcode;
curopcodes->opcode[oppos].spi_type = spi_type;
program_opcodes(curopcodes, 0, ich_generation);
oppos = find_opcode(curopcodes, opcode);
msg_pdbg2("on-the-fly OPCODE (0x%02X) re-programmed, op-pos=%d\n", opcode, oppos);
return oppos;
}
/*
* Returns -1 if at least one mandatory opcode is inaccessible, 0 otherwise.
* FIXME: this should also check for
* - at least one probing opcode (RDID (incl. AT25F variants?), REMS, RES?)
* - at least one erasing opcode (lots.)
* - at least one program opcode (BYTE_PROGRAM, AAI_WORD_PROGRAM, ...?)
* - necessary preops? (EWSR, WREN, ...?)
*/
static int ich_missing_opcodes(void)
{
uint8_t ops[] = {
JEDEC_READ,
JEDEC_RDSR,
0
};
int i = 0;
while (ops[i] != 0) {
msg_pspew("checking for opcode 0x%02x\n", ops[i]);
if (find_opcode(curopcodes, ops[i]) == -1)
return -1;
i++;
}
return 0;
}
/*
* Try to set BBAR (BIOS Base Address Register), but read back the value in case
* it didn't stick.
*/
static void ich_set_bbar(uint32_t min_addr, enum ich_chipset ich_gen)
{
int bbar_off;
switch (ich_gen) {
case CHIPSET_ICH7:
case CHIPSET_TUNNEL_CREEK:
case CHIPSET_CENTERTON:
bbar_off = 0x50;
break;
case CHIPSET_ICH8:
case CHIPSET_BAYTRAIL:
msg_pdbg("BBAR offset is unknown!\n");
return;
case CHIPSET_ICH9:
default: /* Future version might behave the same */
bbar_off = ICH9_REG_BBAR;
break;
}
ichspi_bbar = mmio_readl(ich_spibar + bbar_off) & ~BBAR_MASK;
if (ichspi_bbar) {
msg_pdbg("Reserved bits in BBAR not zero: 0x%08"PRIx32"\n",
ichspi_bbar);
}
min_addr &= BBAR_MASK;
ichspi_bbar |= min_addr;
rmmio_writel(ichspi_bbar, ich_spibar + bbar_off);
ichspi_bbar = mmio_readl(ich_spibar + bbar_off) & BBAR_MASK;
/* We don't have any option except complaining. And if the write
* failed, the restore will fail as well, so no problem there.
*/
if (ichspi_bbar != min_addr)
msg_perr("Setting BBAR to 0x%08"PRIx32" failed! New value: 0x%08"PRIx32".\n",
min_addr, ichspi_bbar);
}
/* Create a struct OPCODES based on what we find in the locked down chipset. */
static int generate_opcodes(OPCODES * op, enum ich_chipset ich_gen)
{
int a;
uint16_t preop, optype;
uint32_t opmenu[2];
if (op == NULL) {
msg_perr("\n%s: null OPCODES pointer!\n", __func__);
return -1;
}
switch (ich_gen) {
case CHIPSET_ICH7:
case CHIPSET_TUNNEL_CREEK:
case CHIPSET_CENTERTON:
preop = REGREAD16(ICH7_REG_PREOP);
optype = REGREAD16(ICH7_REG_OPTYPE);
opmenu[0] = REGREAD32(ICH7_REG_OPMENU);
opmenu[1] = REGREAD32(ICH7_REG_OPMENU + 4);
break;
case CHIPSET_ICH8:
default: /* Future version might behave the same */
preop = REGREAD16(swseq_data.reg_preop);
optype = REGREAD16(swseq_data.reg_optype);
opmenu[0] = REGREAD32(swseq_data.reg_opmenu);
opmenu[1] = REGREAD32(swseq_data.reg_opmenu + 4);
break;
}
op->preop[0] = (uint8_t) preop;
op->preop[1] = (uint8_t) (preop >> 8);
for (a = 0; a < 8; a++) {
op->opcode[a].spi_type = (uint8_t) (optype & 0x3);
optype >>= 2;
}
for (a = 0; a < 4; a++) {
op->opcode[a].opcode = (uint8_t) (opmenu[0] & 0xff);
opmenu[0] >>= 8;
}
for (a = 4; a < 8; a++) {
op->opcode[a].opcode = (uint8_t) (opmenu[1] & 0xff);
opmenu[1] >>= 8;
}
/* No preopcodes used by default. */
for (a = 0; a < 8; a++)
op->opcode[a].atomic = 0;
return 0;
}
/* This function generates OPCODES from or programs OPCODES to ICH according to
* the chipset's SPI configuration lock.
*
* It should be called before ICH sends any spi command.
*/
static int ich_init_opcodes(enum ich_chipset ich_gen)
{
int rc = 0;
OPCODES *curopcodes_done;
if (curopcodes)
return 0;
if (ichspi_lock) {
msg_pdbg("Reading OPCODES... ");
curopcodes_done = &O_EXISTING;
rc = generate_opcodes(curopcodes_done, ich_gen);
} else {
msg_pdbg("Programming OPCODES... ");
curopcodes_done = &O_ST_M25P;
rc = program_opcodes(curopcodes_done, 1, ich_gen);
}
if (rc) {
curopcodes = NULL;
msg_perr("failed\n");
return 1;
} else {
curopcodes = curopcodes_done;
msg_pdbg("done\n");
prettyprint_opcodes(curopcodes);
return 0;
}
}
/* Fill len bytes from the data array into the fdata/spid registers.
*
* Note that using len > flash->mst->spi.max_data_write will trash the registers
* following the data registers.
*/
static void ich_fill_data(const uint8_t *data, int len, int reg0_off)
{
uint32_t temp32 = 0;
int i;
if (len <= 0)
return;
for (i = 0; i < len; i++) {
if ((i % 4) == 0)
temp32 = 0;
temp32 |= ((uint32_t) data[i]) << ((i % 4) * 8);
if ((i % 4) == 3) /* 32 bits are full, write them to regs. */
REGWRITE32(reg0_off + (i - (i % 4)), temp32);
}
i--;
if ((i % 4) != 3) /* Write remaining data to regs. */
REGWRITE32(reg0_off + (i - (i % 4)), temp32);
}
/* Read len bytes from the fdata/spid register into the data array.
*
* Note that using len > flash->mst->spi.max_data_read will return garbage or
* may even crash.
*/
static void ich_read_data(uint8_t *data, int len, int reg0_off)
{
int i;
uint32_t temp32 = 0;
for (i = 0; i < len; i++) {
if ((i % 4) == 0)
temp32 = REGREAD32(reg0_off + i);
data[i] = (temp32 >> ((i % 4) * 8)) & 0xff;
}
}
static int ich7_run_opcode(OPCODE op, uint32_t offset,
uint8_t datalength, uint8_t * data, int maxdata)
{
bool write_cmd = false;
int timeout;
uint32_t temp32;
uint16_t temp16;
uint64_t opmenu;
int opcode_index;
/* Is it a write command? */
if ((op.spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS)
|| (op.spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS)) {
write_cmd = true;
}
timeout = 100 * 60; /* 60 ms are 9.6 million cycles at 16 MHz. */
while ((REGREAD16(ICH7_REG_SPIS) & SPIS_SCIP) && --timeout) {
default_delay(10);
}
if (!timeout) {
msg_perr("Error: SCIP never cleared!\n");
return 1;
}
/* Program offset in flash into SPIA while preserving reserved bits. */
temp32 = REGREAD32(ICH7_REG_SPIA) & ~0x00FFFFFF;
REGWRITE32(ICH7_REG_SPIA, (offset & 0x00FFFFFF) | temp32);
/* Program data into SPID0 to N */
if (write_cmd && (datalength != 0))
ich_fill_data(data, datalength, ICH7_REG_SPID0);
/* Assemble SPIS */
temp16 = REGREAD16(ICH7_REG_SPIS);
/* keep reserved bits */
temp16 &= SPIS_RESERVED_MASK;
/* clear error status registers */
temp16 |= (SPIS_CDS | SPIS_FCERR);
REGWRITE16(ICH7_REG_SPIS, temp16);
/* Assemble SPIC */
temp16 = 0;
if (datalength != 0) {
temp16 |= SPIC_DS;
temp16 |= ((uint32_t) ((datalength - 1) & (maxdata - 1))) << 8;
}
/* Select opcode */
opmenu = REGREAD32(ICH7_REG_OPMENU);
opmenu |= ((uint64_t)REGREAD32(ICH7_REG_OPMENU + 4)) << 32;
for (opcode_index = 0; opcode_index < 8; opcode_index++) {
if ((opmenu & 0xff) == op.opcode) {
break;
}
opmenu >>= 8;
}
if (opcode_index == 8) {
msg_pdbg("Opcode %x not found.\n", op.opcode);
return 1;
}
temp16 |= ((uint16_t) (opcode_index & 0x07)) << 4;
timeout = 100 * 60; /* 60 ms are 9.6 million cycles at 16 MHz. */
/* Handle Atomic. Atomic commands include three steps:
- sending the preop (mainly EWSR or WREN)
- sending the main command
- waiting for the busy bit (WIP) to be cleared
This means the timeout must be sufficient for chip erase
of slow high-capacity chips.
*/
switch (op.atomic) {
case 2:
/* Select second preop. */
temp16 |= SPIC_SPOP;
/* Fall through. */
case 1:
/* Atomic command (preop+op) */
temp16 |= SPIC_ACS;
timeout = 100 * 1000 * 60; /* 60 seconds */
break;
}
/* Start */
temp16 |= SPIC_SCGO;
/* write it */
REGWRITE16(ICH7_REG_SPIC, temp16);
/* Wait for Cycle Done Status or Flash Cycle Error. */
while (((REGREAD16(ICH7_REG_SPIS) & (SPIS_CDS | SPIS_FCERR)) == 0) &&
--timeout) {
default_delay(10);
}
if (!timeout) {
msg_perr("timeout, ICH7_REG_SPIS=0x%04x\n", REGREAD16(ICH7_REG_SPIS));
return 1;
}
/* FIXME: make sure we do not needlessly cause transaction errors. */
temp16 = REGREAD16(ICH7_REG_SPIS);
if (temp16 & SPIS_FCERR) {
msg_perr("Transaction error!\n");
/* keep reserved bits */
temp16 &= SPIS_RESERVED_MASK;
REGWRITE16(ICH7_REG_SPIS, temp16 | SPIS_FCERR);
return 1;
}
if ((!write_cmd) && (datalength != 0))
ich_read_data(data, datalength, ICH7_REG_SPID0);
return 0;
}
static int ich9_run_opcode(OPCODE op, uint32_t offset,
uint8_t datalength, uint8_t * data)
{
bool write_cmd = false;
int timeout;
uint32_t temp32;
uint64_t opmenu;
int opcode_index;
/* Is it a write command? */