-
Notifications
You must be signed in to change notification settings - Fork 147
Expand file tree
/
Copy pathnxp_t2080.c
More file actions
1109 lines (957 loc) · 38.9 KB
/
nxp_t2080.c
File metadata and controls
1109 lines (957 loc) · 38.9 KB
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
/* nxp_t2080.c
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot 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 3 of the License, or
* (at your option) any later version.
*
* wolfBoot 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 Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <stdint.h>
#include <stddef.h>
#include "target.h"
#include "printf.h"
#include "image.h" /* for RAMFUNCTION */
#include "nxp_ppc.h"
#include "nxp_t2080.h"
#define ENABLE_IFC
#define ENABLE_BUS_CLK_CALC
/* #define DEBUG_FLASH */
#ifndef BUILD_LOADER_STAGE1
#define ENABLE_MP /* multi-core support */
#endif
/* generic shared NXP QorIQ driver code */
#include "nxp_ppc.c"
/* Forward declarations */
static void RAMFUNCTION hal_flash_unlock_sector(uint32_t sector);
#ifdef ENABLE_MP
static void hal_mp_init(void);
#endif
/* AMD CFI Commands (Spansion/Cypress) */
#define AMD_CMD_RESET 0xF0
#define AMD_CMD_WRITE 0xA0
#define AMD_CMD_ERASE_START 0x80
#define AMD_CMD_ERASE_SECTOR 0x30
#define AMD_CMD_UNLOCK_START 0xAA
#define AMD_CMD_UNLOCK_ACK 0x55
#define AMD_CMD_WRITE_TO_BUFFER 0x25
#define AMD_CMD_WRITE_BUFFER_CONFIRM 0x29
#define AMD_CMD_SET_PPB_ENTRY 0xC0
#define AMD_CMD_SET_PPB_EXIT_BC1 0x90
#define AMD_CMD_SET_PPB_EXIT_BC2 0x00
#define AMD_CMD_PPB_UNLOCK_BC1 0x80
#define AMD_CMD_PPB_UNLOCK_BC2 0x30
#define AMD_CMD_PPB_LOCK_BC1 0xA0
#define AMD_CMD_PPB_LOCK_BC2 0x00
#define AMD_STATUS_TOGGLE 0x40
#define AMD_STATUS_ERROR 0x20
/* Flash unlock addresses */
#if FLASH_CFI_WIDTH == 16
#define FLASH_UNLOCK_ADDR1 0x555
#define FLASH_UNLOCK_ADDR2 0x2AA
#else
#define FLASH_UNLOCK_ADDR1 0xAAA
#define FLASH_UNLOCK_ADDR2 0x555
#endif
/* Flash IO Helpers */
#if FLASH_CFI_WIDTH == 16
#define FLASH_IO8_WRITE(sec, n, val) *((volatile uint16_t*)(FLASH_BASE_ADDR + (FLASH_SECTOR_SIZE * (sec)) + ((n) * 2))) = (((val) << 8) | (val))
#define FLASH_IO16_WRITE(sec, n, val) *((volatile uint16_t*)(FLASH_BASE_ADDR + (FLASH_SECTOR_SIZE * (sec)) + ((n) * 2))) = (val)
#define FLASH_IO8_READ(sec, n) (uint8_t)(*((volatile uint16_t*)(FLASH_BASE_ADDR + (FLASH_SECTOR_SIZE * (sec)) + ((n) * 2))))
#define FLASH_IO16_READ(sec, n) *((volatile uint16_t*)(FLASH_BASE_ADDR + (FLASH_SECTOR_SIZE * (sec)) + ((n) * 2)))
#else
#define FLASH_IO8_WRITE(sec, n, val) *((volatile uint8_t*)(FLASH_BASE_ADDR + (FLASH_SECTOR_SIZE * (sec)) + (n))) = (val)
#define FLASH_IO8_READ(sec, n) *((volatile uint8_t*)(FLASH_BASE_ADDR + (FLASH_SECTOR_SIZE * (sec)) + (n)))
#endif
void law_init(void)
{
/* Buffer Manager (BMan) (control) - probably not required */
set_law(3, 0xF, 0xF4000000, LAW_TRGT_BMAN, LAW_SIZE_32MB, 1);
}
/* Note: AMD Autoselect (READ_ID) mode is not used here because entering it
* affects the entire flash bank. Since wolfBoot runs XIP from the same
* bank (CS0), entering Autoselect would crash instruction fetch. */
static void hal_flash_init(void)
{
#ifdef ENABLE_IFC
uint32_t cspr;
/* IFC CS0 - NOR Flash
* Do NOT reprogram IFC CS0 base address, port size, AMASK, CSOR, or
* FTIM while executing from flash (XIP). The boot ROM already
* configured CS0 correctly.
*
* However, the boot ROM may set IFC_CSPR_WP (write-protect), which
* blocks all write cycles to the flash. This prevents AMD command
* sequences (erase/program) from reaching the chips. Clearing just
* the WP bit is safe during XIP — it doesn't change chip-select
* decode, only enables write forwarding. */
cspr = get32(IFC_CSPR(0));
#ifdef DEBUG_UART
wolfBoot_printf("IFC CSPR0: 0x%x%s\n", cspr,
(cspr & IFC_CSPR_WP) ? " (WP set)" : "");
#endif
/* WP clearing is done in hal_flash_clear_wp() from RAMFUNCTION code.
* T2080RM requires V=0 before modifying IFC_CSPR, which is not safe
* during XIP. The RAMFUNCTION code runs from DDR with flash TLB
* guarded, so it can safely toggle V=0 -> modify -> V=1. */
#endif /* ENABLE_IFC */
}
void hal_ddr_init(void)
{
#ifdef ENABLE_DDR
uint32_t reg;
/* Map LAW for DDR */
set_law(4, 0, DDR_ADDRESS, LAW_TRGT_DDR_1, LAW_SIZE_2GB, 0);
/* If DDR is already enabled then just return */
reg = get32(DDR_SDRAM_CFG);
if (reg & DDR_SDRAM_CFG_MEM_EN) {
return;
}
/* Set clock early for clock / pin */
set32(DDR_SDRAM_CLK_CNTL, DDR_SDRAM_CLK_CNTL_VAL);
/* Setup DDR CS (chip select) bounds */
set32(DDR_CS_BNDS(0), DDR_CS0_BNDS_VAL);
set32(DDR_CS_CONFIG(0), DDR_CS0_CONFIG_VAL);
set32(DDR_CS_CONFIG_2(0), DDR_CS_CONFIG_2_VAL);
set32(DDR_CS_BNDS(1), DDR_CS1_BNDS_VAL);
set32(DDR_CS_CONFIG(1), DDR_CS1_CONFIG_VAL);
set32(DDR_CS_CONFIG_2(1), DDR_CS_CONFIG_2_VAL);
set32(DDR_CS_BNDS(2), DDR_CS2_BNDS_VAL);
set32(DDR_CS_CONFIG(2), DDR_CS2_CONFIG_VAL);
set32(DDR_CS_CONFIG_2(2), DDR_CS_CONFIG_2_VAL);
set32(DDR_CS_BNDS(3), DDR_CS3_BNDS_VAL);
set32(DDR_CS_CONFIG(3), DDR_CS3_CONFIG_VAL);
set32(DDR_CS_CONFIG_2(3), DDR_CS_CONFIG_2_VAL);
/* DDR SDRAM timing configuration */
set32(DDR_TIMING_CFG_3, DDR_TIMING_CFG_3_VAL);
set32(DDR_TIMING_CFG_0, DDR_TIMING_CFG_0_VAL);
set32(DDR_TIMING_CFG_1, DDR_TIMING_CFG_1_VAL);
set32(DDR_TIMING_CFG_2, DDR_TIMING_CFG_2_VAL);
set32(DDR_TIMING_CFG_4, DDR_TIMING_CFG_4_VAL);
set32(DDR_TIMING_CFG_5, DDR_TIMING_CFG_5_VAL);
set32(DDR_ZQ_CNTL, DDR_ZQ_CNTL_VAL);
/* DDR SDRAM mode configuration */
set32(DDR_SDRAM_MODE, DDR_SDRAM_MODE_VAL);
set32(DDR_SDRAM_MODE_2, DDR_SDRAM_MODE_2_VAL);
set32(DDR_SDRAM_MODE_3, DDR_SDRAM_MODE_3_8_VAL);
set32(DDR_SDRAM_MODE_4, DDR_SDRAM_MODE_3_8_VAL);
set32(DDR_SDRAM_MODE_5, DDR_SDRAM_MODE_3_8_VAL);
set32(DDR_SDRAM_MODE_6, DDR_SDRAM_MODE_3_8_VAL);
set32(DDR_SDRAM_MODE_7, DDR_SDRAM_MODE_3_8_VAL);
set32(DDR_SDRAM_MODE_8, DDR_SDRAM_MODE_3_8_VAL);
set32(DDR_SDRAM_MD_CNTL, DDR_SDRAM_MD_CNTL_VAL);
/* DDR Configuration */
set32(DDR_SDRAM_INTERVAL, DDR_SDRAM_INTERVAL_VAL);
set32(DDR_DATA_INIT, DDR_DATA_INIT_VAL);
set32(DDR_WRLVL_CNTL, DDR_WRLVL_CNTL_VAL);
set32(DDR_WRLVL_CNTL_2, DDR_WRLVL_CNTL_2_VAL);
set32(DDR_WRLVL_CNTL_3, DDR_WRLVL_CNTL_3_VAL);
set32(DDR_SR_CNTR, 0);
set32(DDR_SDRAM_RCW_1, 0);
set32(DDR_SDRAM_RCW_2, 0);
set32(DDR_DDRCDR_1, DDR_DDRCDR_1_VAL);
set32(DDR_SDRAM_CFG_2, (DDR_SDRAM_CFG_2_VAL | DDR_SDRAM_CFG_2_D_INIT));
set32(DDR_INIT_ADDR, 0);
set32(DDR_INIT_EXT_ADDR, 0);
set32(DDR_DDRCDR_2, DDR_DDRCDR_2_VAL);
set32(DDR_ERR_DISABLE, 0);
set32(DDR_ERR_INT_EN, DDR_ERR_INT_EN_VAL);
set32(DDR_ERR_SBE, DDR_ERR_SBE_VAL);
/* Set values, but do not enable the DDR yet */
set32(DDR_SDRAM_CFG, DDR_SDRAM_CFG_VAL & ~DDR_SDRAM_CFG_MEM_EN);
__asm__ __volatile__("sync;isync");
/* busy wait for ~500us */
udelay(500);
__asm__ __volatile__("sync;isync");
/* Enable controller */
reg = get32(DDR_SDRAM_CFG) & ~DDR_SDRAM_CFG_BI;
set32(DDR_SDRAM_CFG, reg | DDR_SDRAM_CFG_MEM_EN);
__asm__ __volatile__("sync;isync");
/* Wait for data initialization to complete */
while (get32(DDR_SDRAM_CFG_2) & DDR_SDRAM_CFG_2_D_INIT) {
/* busy wait loop - throttle polling */
udelay(10000);
}
#endif /* ENABLE_DDR */
}
void hal_early_init(void)
{
/* Enable timebase on core 0 */
set32(RCPM_PCTBENR, (1 << 0));
/* Only invalidate the CPC if it is NOT configured as SRAM.
* When CPC SRAM is active (used as stack), writing CPCFI|CPCLFC
* without preserving CPCE would disable the CPC and corrupt the
* stack. Skip invalidation when SRAMEN is set (T2080RM 8.4.2.2). */
if (!(get32((volatile uint32_t*)(CPC_BASE + CPCSRCR0)) & CPCSRCR0_SRAMEN)) {
set32((volatile uint32_t*)(CPC_BASE + CPCCSR0),
(CPCCSR0_CPCFI | CPCCSR0_CPCLFC));
/* Wait for self-clearing invalidate bits */
while (get32((volatile uint32_t*)(CPC_BASE + CPCCSR0)) &
(CPCCSR0_CPCFI | CPCCSR0_CPCLFC));
}
/* Set DCSR space = 1G */
set32(DCFG_DCSR, (get32(DCFG_DCSR) | CORENET_DCSR_SZ_1G));
get32(DCFG_DCSR); /* read back to sync */
hal_ddr_init();
}
static void hal_cpld_init(void)
{
#ifdef ENABLE_CPLD
/* CPLD IFC Timing Parameters */
set32(IFC_FTIM0(3), (IFC_FTIM0_GPCM_TACSE(16UL) |
IFC_FTIM0_GPCM_TEADC(16UL) |
IFC_FTIM0_GPCM_TEAHC(16UL)));
set32(IFC_FTIM1(3), (IFC_FTIM1_GPCM_TACO(16UL) |
IFC_FTIM1_GPCM_TRAD(31UL)));
set32(IFC_FTIM2(3), (IFC_FTIM2_GPCM_TCS(16UL) |
IFC_FTIM2_GPCM_TCH(8UL) |
IFC_FTIM2_GPCM_TWP(31UL)));
set32(IFC_FTIM3(3), 0);
/* CPLD IFC Definitions (CS3) */
set32(IFC_CSPR_EXT(3), CPLD_BASE_PHYS_HIGH);
set32(IFC_CSPR(3), (IFC_CSPR_PHYS_ADDR(CPLD_BASE) |
IFC_CSPR_PORT_SIZE_16 |
IFC_CSPR_MSEL_GPCM |
IFC_CSPR_V));
set32(IFC_AMASK(3), IFC_AMASK_64KB);
set32(IFC_CSOR(3), 0);
/* IFC - CPLD (use LAW 5; LAW 2 is used for CPC SRAM) */
set_law(5, CPLD_BASE_PHYS_HIGH, CPLD_BASE,
LAW_TRGT_IFC, LAW_SIZE_4KB, 1);
/* CPLD - TBL=1, Entry 17 */
set_tlb(1, 17, CPLD_BASE, CPLD_BASE, CPLD_BASE_PHYS_HIGH,
MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I | MAS2_G,
0, BOOKE_PAGESZ_4K, 1);
#endif
}
#ifdef ENABLE_DDR
/* Release CPC SRAM back to L2 cache mode.
* Call after stack is relocated to DDR (done in boot_entry_C).
* This gives us the full 2MB CPC as L3 cache for better performance.
*
* Before releasing CPC SRAM, .ramcode (RAMFUNCTION) is copied to DDR
* and TLB9 is remapped: VA 0xF8F00000 -> PA DDR_RAMCODE_ADDR so that
* RAMFUNCTION code (memcpy, wolfBoot_start, etc.) continues to work. */
static void hal_reconfigure_cpc_as_cache(void)
{
volatile uint32_t *cpc_csr0 = (volatile uint32_t *)(CPC_BASE + CPCCSR0);
volatile uint32_t *cpc_srcr0 = (volatile uint32_t *)(CPC_BASE + CPCSRCR0);
uint32_t reg;
/* Linker symbols for .ramcode section boundaries */
extern unsigned int _start_ramcode;
extern unsigned int _end_ramcode;
uint32_t ramcode_size = (uint32_t)&_end_ramcode - (uint32_t)&_start_ramcode;
/* Step 1: Copy .ramcode from CPC SRAM to DDR.
* Must use volatile loop — memcpy itself is in .ramcode! */
if (ramcode_size > 0) {
volatile const uint32_t *src = (volatile const uint32_t *)&_start_ramcode;
volatile uint32_t *dst = (volatile uint32_t *)DDR_RAMCODE_ADDR;
volatile uint32_t *end = (volatile uint32_t *)(DDR_RAMCODE_ADDR +
ramcode_size);
while (dst < end) {
*dst++ = *src++;
}
/* Ensure all stores have drained before flushing cache lines */
__asm__ __volatile__("sync" ::: "memory");
/* Flush D-cache and invalidate I-cache for the DDR copy */
flush_cache(DDR_RAMCODE_ADDR, ramcode_size);
/* Step 2: Remap TLB9: same VA (0xF8F00000) -> DDR physical address.
* All .ramcode references use VA 0xF8F00000, so this makes them
* transparently access the DDR copy instead of CPC SRAM. */
set_tlb(1, 9,
L2SRAM_ADDR, DDR_RAMCODE_ADDR, 0,
MAS3_SX | MAS3_SW | MAS3_SR, MAS2_M, 0,
INITIAL_SRAM_BOOKE_SZ, 1);
/* Ensure TLB update and I-cache pick up new mapping */
invalidate_icache();
}
#ifdef DEBUG_UART
wolfBoot_printf("Ramcode: copied %d bytes to DDR, TLB9 remapped\n",
ramcode_size);
#endif
/* Step 3: Flush the CPC to push any dirty SRAM data out.
* Read-modify-write to preserve CPCE/CPCPE enable bits. */
reg = *cpc_csr0;
reg |= CPCCSR0_CPCFL;
*cpc_csr0 = reg;
__asm__ __volatile__("sync; isync" ::: "memory");
/* Step 4: Poll until flush completes (CPCFL clears) */
while (*cpc_csr0 & CPCCSR0_CPCFL);
/* Step 5: Disable SRAM mode - release all ways back to cache */
*cpc_srcr0 = 0;
__asm__ __volatile__("sync; isync" ::: "memory");
/* Step 6: Disable CPC SRAM LAW (no longer needed — TLB9 now routes
* to DDR via LAW4, not CPC SRAM via LAW2).
* Keep TLB9 — it's remapped to DDR and still in use. */
set32(LAWAR(2), 0);
/* Step 7: Flash invalidate CPC to start fresh as cache */
reg = *cpc_csr0;
reg |= CPCCSR0_CPCFI;
*cpc_csr0 = reg;
__asm__ __volatile__("sync; isync" ::: "memory");
while (*cpc_csr0 & CPCCSR0_CPCFI);
/* Step 8: Enable parity/ECC now that SRAM is released and cache is clean.
* CPCPE was intentionally omitted during ASM init to avoid ECC machine
* checks on uninitialized SRAM (cold power cycle). Safe to enable here:
* SRAM mode is off, CPC is freshly invalidated, no stale data. */
reg = *cpc_csr0;
reg |= CPCCSR0_CPCPE;
*cpc_csr0 = reg;
__asm__ __volatile__("sync; isync" ::: "memory");
/* CPC is now fully enabled (CPCE|CPCPE), all 2MB as L3 cache */
#ifdef DEBUG_UART
wolfBoot_printf("CPC: Released SRAM, full 2MB L3 CPC cache enabled\n");
#endif
}
/* Make flash TLB cacheable for XIP code performance.
* Changes TLB Entry 2 (flash) from MAS2_I|MAS2_G to MAS2_M.
* This enables L1 I-cache + L2 + CPC to cache flash instructions. */
static void hal_flash_enable_caching(void)
{
/* Rewrite flash TLB entry with cacheable attributes.
* MAS2_M = memory coherent, enables caching */
set_tlb(1, 2,
FLASH_BASE_ADDR, FLASH_BASE_ADDR, FLASH_BASE_PHYS_HIGH,
MAS3_SX | MAS3_SW | MAS3_SR, MAS2_M, 0,
FLASH_TLB_PAGESZ, 1);
/* Invalidate L1 I-cache so new TLB attributes take effect */
invalidate_icache();
#ifdef DEBUG_UART
wolfBoot_printf("Flash: caching enabled (L1+L2+CPC)\n");
#endif
}
#endif /* ENABLE_DDR */
void hal_init(void)
{
#if defined(DEBUG_UART) && defined(ENABLE_CPLD)
uint32_t fw;
#endif
/* Enable timebase on core 0 */
set32(RCPM_PCTBENR, (1 << 0));
law_init();
#ifdef DEBUG_UART
uart_init();
uart_write("wolfBoot Init\n", 14);
#ifndef WOLFBOOT_REPRODUCIBLE_BUILD
wolfBoot_printf("Build: %s %s\n", __DATE__, __TIME__);
#endif
wolfBoot_printf("System Clock: %lu MHz\n",
(unsigned long)(SYS_CLK / 1000000));
wolfBoot_printf("Platform Clock: %lu MHz\n",
(unsigned long)(hal_get_plat_clk() / 1000000));
wolfBoot_printf("Core Clock: %lu MHz\n",
(unsigned long)(hal_get_core_clk() / 1000000));
wolfBoot_printf("Bus Clock: %lu MHz\n",
(unsigned long)(hal_get_bus_clk() / 1000000));
wolfBoot_printf("Timebase: %lu MHz\n",
(unsigned long)(TIMEBASE_HZ / 1000000));
#endif
hal_flash_init();
hal_cpld_init();
#ifdef ENABLE_CPLD
set8(CPLD_DATA(CPLD_PROC_STATUS), 1); /* Enable proc reset */
set8(CPLD_DATA(CPLD_WR_TEMP_ALM_OVRD), 0); /* Enable temp alarm */
#ifdef DEBUG_UART
fw = get8(CPLD_DATA(CPLD_FW_REV));
wolfBoot_printf("CPLD FW Rev: 0x%x\n", fw);
#endif
#endif /* ENABLE_CPLD */
#ifdef ENABLE_DDR
/* Stack is already in DDR (relocated in boot_entry_C via
* ddr_call_with_stack trampoline before main() was called).
*
* Now release CPC SRAM back to L2 cache and enable flash caching.
* This dramatically improves ECC signature verification performance:
* - CPC (2MB) becomes L3 cache for all memory accesses
* - Flash code is cached by L1 I-cache + L2 + CPC
* - Stack/data in DDR is cached by L1 D-cache + L2 + CPC */
hal_reconfigure_cpc_as_cache();
hal_flash_enable_caching();
/* Enable branch prediction now that DDR stack and cache hierarchy
* are fully configured. Disabled during early ASM boot to avoid
* speculative fetches during hardware init. */
{
uint32_t bucsr = BUCSR_STAC_EN | BUCSR_LS_EN | BUCSR_BBFI | BUCSR_BPEN;
__asm__ __volatile__("mtspr %0, %1; isync" :: "i"(SPRN_BUCSR), "r"(bucsr));
}
#endif
#ifdef ENABLE_MP
/* Start secondary cores AFTER CPC release and flash caching.
* Secondary cores' L2 flash-invalidate on the shared cluster L2
* must not disrupt the CPC SRAM→cache transition. Starting them
* after ensures the cache hierarchy is fully stable. */
hal_mp_init();
#endif
}
/* Switch flash TLB to cache-inhibited + guarded for direct flash chip access.
* AMD flash commands require writes to reach the chip immediately and status
* reads to come directly from the chip. With MAS2_M (cacheable), stores go
* through the CPC coherency fabric; IFC does not support coherent writes and
* returns a bus error (DSI). */
static void RAMFUNCTION hal_flash_cache_disable(void)
{
set_tlb(1, 2, FLASH_BASE_ADDR, FLASH_BASE_ADDR, FLASH_BASE_PHYS_HIGH,
MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I | MAS2_G, 0, FLASH_TLB_PAGESZ, 1);
}
/* Restore flash TLB to cacheable mode after flash operation.
* Flash must be back in read-array mode before calling (AMD_CMD_RESET sent).
* Invalidate caches afterward so stale pre-erase data is not served. */
static void RAMFUNCTION hal_flash_cache_enable(void)
{
set_tlb(1, 2, FLASH_BASE_ADDR, FLASH_BASE_ADDR, FLASH_BASE_PHYS_HIGH,
MAS3_SX | MAS3_SW | MAS3_SR, MAS2_M, 0, FLASH_TLB_PAGESZ, 1);
invalidate_dcache();
invalidate_icache();
}
/* Clear IFC write-protect. T2080RM says IFC_CSPR should only be written
* when V=0. Must be called from RAMFUNCTION (DDR) with flash TLB set to
* guarded (MAS2_G) so no speculative access occurs while V is briefly 0. */
static void RAMFUNCTION hal_flash_clear_wp(void)
{
uint32_t cspr = get32(IFC_CSPR(0));
if (cspr & IFC_CSPR_WP) {
/* Clear V first, then modify WP, then re-enable V */
set32(IFC_CSPR(0), cspr & ~(IFC_CSPR_WP | IFC_CSPR_V));
__asm__ __volatile__("sync; isync");
set32(IFC_CSPR(0), (cspr & ~IFC_CSPR_WP) | IFC_CSPR_V);
__asm__ __volatile__("sync; isync");
/* Verify WP cleared */
cspr = get32(IFC_CSPR(0));
#ifdef DEBUG_FLASH
wolfBoot_printf("WP clear: CSPR0=0x%x%s\n", cspr,
(cspr & IFC_CSPR_WP) ? " (FAILED)" : " (OK)");
#endif
}
}
static void RAMFUNCTION hal_flash_unlock_sector(uint32_t sector)
{
/* AMD unlock sequence */
FLASH_IO8_WRITE(sector, FLASH_UNLOCK_ADDR1, AMD_CMD_UNLOCK_START);
FLASH_IO8_WRITE(sector, FLASH_UNLOCK_ADDR2, AMD_CMD_UNLOCK_ACK);
}
/* Check and clear PPB (Persistent Protection Bits) for a sector.
* S29GL01GS has per-sector non-volatile protection bits. If set, erase/program
* fails with DQ5 error. PPB erase is chip-wide (clears ALL sectors).
* Returns: 0 if unprotected or successfully cleared, -1 on failure. */
static int RAMFUNCTION hal_flash_ppb_unlock(uint32_t sector)
{
uint16_t ppb_status;
uint16_t read1, read2;
uint32_t timeout;
/* Enter PPB ASO (Address Space Overlay) */
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR1, AMD_CMD_UNLOCK_START);
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR2, AMD_CMD_UNLOCK_ACK);
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR1, AMD_CMD_SET_PPB_ENTRY);
/* Read PPB status for target sector: DQ0=0 means protected.
* On 16-bit bus, must read both chip lanes to check both devices. */
#if FLASH_CFI_WIDTH == 16
ppb_status = FLASH_IO16_READ(sector, 0);
if ((ppb_status & 0x0101) == 0x0101) {
#else
ppb_status = FLASH_IO8_READ(sector, 0);
if ((ppb_status & 0x01) == 0x01) {
#endif
/* Both chips report unprotected — exit PPB mode and return */
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC1);
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC2);
return 0;
}
/* Exit PPB ASO before calling printf (flash must be in read-array
* mode for I-cache misses to fetch valid instructions) */
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC1);
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC2);
FLASH_IO8_WRITE(0, 0, AMD_CMD_RESET);
udelay(50);
#ifdef DEBUG_FLASH
wolfBoot_printf("PPB: sector %d protected (0x%x), erasing all PPBs\n",
sector, ppb_status);
#endif
/* Re-enter PPB ASO for erase */
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR1, AMD_CMD_UNLOCK_START);
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR2, AMD_CMD_UNLOCK_ACK);
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR1, AMD_CMD_SET_PPB_ENTRY);
/* PPB Erase All (clears all sectors' PPBs) */
FLASH_IO8_WRITE(0, 0, AMD_CMD_PPB_UNLOCK_BC1); /* 0x80 */
FLASH_IO8_WRITE(0, 0, AMD_CMD_PPB_UNLOCK_BC2); /* 0x30 */
/* Wait for PPB erase completion — poll for toggle stop.
* On 16-bit bus, read both chip lanes to ensure both complete. */
timeout = 0;
do {
#if FLASH_CFI_WIDTH == 16
read1 = FLASH_IO16_READ(0, 0);
read2 = FLASH_IO16_READ(0, 0);
#else
read1 = FLASH_IO8_READ(0, 0);
read2 = FLASH_IO8_READ(0, 0);
#endif
if (read1 == read2)
break;
udelay(10);
} while (timeout++ < 100000); /* 1 second */
/* Exit PPB ASO */
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC1);
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC2);
/* Reset to read-array mode */
FLASH_IO8_WRITE(0, 0, AMD_CMD_RESET);
udelay(50);
if (timeout >= 100000) {
#ifdef DEBUG_FLASH
wolfBoot_printf("PPB: erase timeout\n");
#endif
return -1;
}
#ifdef DEBUG_FLASH
wolfBoot_printf("PPB: erase complete\n");
#endif
return 0;
}
/* wait for DQ6 toggle to stop within microsecond timeout.
* RAMFUNCTION: executes from DDR while flash is in program/erase command mode. */
static int RAMFUNCTION hal_flash_status_wait(uint32_t sector, uint32_t timeout_us)
{
int ret = 0;
uint32_t timeout = 0;
uint16_t read1, read2;
/* Replicate 8-bit AMD toggle/error bits to both bytes for parallel chips */
#if FLASH_CFI_WIDTH == 16
uint16_t toggle16 = (AMD_STATUS_TOGGLE << 8) | AMD_STATUS_TOGGLE;
uint16_t error16 = (AMD_STATUS_ERROR << 8) | AMD_STATUS_ERROR;
#else
uint16_t toggle16 = AMD_STATUS_TOGGLE;
uint16_t error16 = AMD_STATUS_ERROR;
#endif
do {
/* AMD toggle detection: DQ6 toggles on consecutive reads during
* program/erase. When the operation completes, DQ6 reflects actual
* data and consecutive reads return the same value.
* NOTE: Do NOT check programmed data bits against a mask here —
* after write completes, the data depends on what was written, not
* on any fixed status bits. Only erase guarantees 0xFF data. */
#if FLASH_CFI_WIDTH == 16
read1 = FLASH_IO16_READ(sector, 0);
read2 = FLASH_IO16_READ(sector, 0);
#else
read1 = FLASH_IO8_READ(sector, 0);
read2 = FLASH_IO8_READ(sector, 0);
#endif
#ifdef DEBUG_FLASH
wolfBoot_printf("Wait toggle %x -> %x\n", read1, read2);
#endif
/* DQ6 stopped toggling → operation complete */
if (((read1 ^ read2) & toggle16) == 0)
break;
/* Check DQ5 (error) on both chips while still toggling */
if (read1 & error16) {
/* Read one more time to confirm it's not a false DQ5 */
#if FLASH_CFI_WIDTH == 16
read1 = FLASH_IO16_READ(sector, 0);
read2 = FLASH_IO16_READ(sector, 0);
#else
read1 = FLASH_IO8_READ(sector, 0);
read2 = FLASH_IO8_READ(sector, 0);
#endif
if (((read1 ^ read2) & toggle16) == 0)
break; /* toggle stopped — was a race, not an error */
ret = -2; /* DQ5 error — program/erase failed */
break;
}
udelay(1);
} while (timeout++ < timeout_us);
if (timeout >= timeout_us) {
ret = -1; /* timeout */
}
#ifdef DEBUG_FLASH
wolfBoot_printf("Wait done (%d tries): %x -> %x\n",
timeout, read1, read2);
#endif
return ret;
}
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
{
int ret = 0;
uint32_t i, sector, offset, nwords;
const uint32_t width_bytes = FLASH_CFI_WIDTH / 8;
uint32_t addr_off = address;
/* Bounds check */
if (addr_off >= FLASH_BASE_ADDR)
addr_off -= FLASH_BASE_ADDR;
if (addr_off + (uint32_t)len > FLASH_BANK_SIZE)
return -1;
/* Enforce alignment to flash bus width */
if ((address % width_bytes) != 0 || (len % width_bytes) != 0) {
#ifdef DEBUG_FLASH
wolfBoot_printf("Flash Write: unaligned addr 0x%x or len %d "
"(need %d-byte alignment)\n", address, len, width_bytes);
#endif
return -1;
}
/* adjust for flash base */
if (address >= FLASH_BASE_ADDR)
address -= FLASH_BASE_ADDR;
#ifdef DEBUG_FLASH
wolfBoot_printf("Flash Write: Ptr %p -> Addr 0x%x (len %d)\n",
data, address, len);
#endif
/* Disable flash caching — AMD commands must reach the chip directly */
hal_flash_cache_disable();
hal_flash_clear_wp();
/* Reset flash to read-array mode in case previous operation left it
* in command mode (e.g. after a timeout or incomplete operation) */
FLASH_IO8_WRITE(0, 0, AMD_CMD_RESET);
udelay(50);
/* Program one word at a time using AMD single-word program (0xA0).
* Each word requires: unlock + 0xA0 + data → poll.
* Typical program time: 60-120us per word.
* This is simpler and more reliable than Write-Buffer-Program (WBP),
* which had DQ1 abort/timeout issues on this IFC + S29GL01GS
* combination. WBP can be re-enabled as an optimization once
* single-word program is verified working on hardware. */
nwords = (uint32_t)len / width_bytes;
for (i = 0; i < nwords; i++) {
sector = address / FLASH_SECTOR_SIZE;
offset = (address - (sector * FLASH_SECTOR_SIZE)) / width_bytes;
hal_flash_unlock_sector(sector);
FLASH_IO8_WRITE(sector, FLASH_UNLOCK_ADDR1, AMD_CMD_WRITE);
#if FLASH_CFI_WIDTH == 16
{
/* Build 16-bit value from bytes to avoid unaligned access */
const uint8_t *p = &data[i * 2];
uint16_t val = ((uint16_t)p[0] << 8) | (uint16_t)p[1];
FLASH_IO16_WRITE(sector, offset, val);
}
#else
FLASH_IO8_WRITE(sector, offset, data[i]);
#endif
/* Poll for program completion (typical 60-120us, max 200ms) */
ret = hal_flash_status_wait(sector, 200 * 1000);
if (ret != 0) {
FLASH_IO8_WRITE(sector, 0, AMD_CMD_RESET);
udelay(50);
#ifdef DEBUG_FLASH
wolfBoot_printf("Flash Write: %s at addr 0x%x\n",
ret == -2 ? "DQ5 error" : "Timeout",
(uint32_t)(FLASH_BASE_ADDR + address));
#endif
break;
}
address += width_bytes;
}
/* Restore flash caching — flash is back in read-array mode */
hal_flash_cache_enable();
return ret;
}
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
{
int ret = 0;
uint32_t sector;
uint32_t addr_off = address;
/* Bounds check */
if (addr_off >= FLASH_BASE_ADDR)
addr_off -= FLASH_BASE_ADDR;
if (addr_off + (uint32_t)len > FLASH_BANK_SIZE)
return -1;
/* adjust for flash base */
if (address >= FLASH_BASE_ADDR)
address -= FLASH_BASE_ADDR;
/* Disable flash caching — AMD commands must reach the chip directly */
hal_flash_cache_disable();
hal_flash_clear_wp();
/* Reset flash to read-array mode in case previous operation left it
* in command mode (e.g. after a timeout or incomplete operation) */
FLASH_IO8_WRITE(0, 0, AMD_CMD_RESET);
udelay(50);
while (len > 0) {
/* determine sector address */
sector = (address / FLASH_SECTOR_SIZE);
#ifdef DEBUG_FLASH
wolfBoot_printf("Flash Erase: Sector %d, Addr 0x%x, Len %d\n",
sector, address, len);
#endif
/* Check and clear PPB protection if set */
if (hal_flash_ppb_unlock(sector) != 0) {
#ifdef DEBUG_FLASH
wolfBoot_printf("Flash Erase: PPB unlock failed sector %d\n", sector);
#endif
ret = -1;
break;
}
#ifdef DEBUG_FLASH
wolfBoot_printf("Erasing sector %d...\n", sector);
#endif
hal_flash_unlock_sector(sector);
FLASH_IO8_WRITE(sector, FLASH_UNLOCK_ADDR1, AMD_CMD_ERASE_START);
hal_flash_unlock_sector(sector);
FLASH_IO8_WRITE(sector, 0, AMD_CMD_ERASE_SECTOR);
/* block erase timeout = 50us - for additional sectors */
/* Typical is 200ms (max 1100ms) */
/* poll for erase completion - max 1.1 sec
* NOTE: Do NOT call wolfBoot_printf while flash is in erase mode.
* With cache-inhibited TLB, I-cache misses fetch from flash which
* returns status data instead of instructions. */
ret = hal_flash_status_wait(sector, 1100*1000);
if (ret != 0) {
/* Reset flash to read-array mode BEFORE calling printf */
FLASH_IO8_WRITE(sector, 0, AMD_CMD_RESET);
udelay(50);
#ifdef DEBUG_FLASH
wolfBoot_printf("Flash Erase: Timeout at sector %d\n", sector);
#endif
break;
}
/* Erase succeeded — flash is back in read-array mode.
* Reset to be safe before any printf (I-cache may miss) */
FLASH_IO8_WRITE(sector, 0, AMD_CMD_RESET);
udelay(10);
#ifdef DEBUG_FLASH
wolfBoot_printf("Erase sector %d: OK\n", sector);
#endif
address += FLASH_SECTOR_SIZE;
len -= FLASH_SECTOR_SIZE;
}
/* Restore flash caching — flash is back in read-array mode */
hal_flash_cache_enable();
return ret;
}
void RAMFUNCTION hal_flash_unlock(void)
{
/* Per-sector unlock is done in hal_flash_write/erase before each operation.
* The previous non-volatile PPB protection mode (C0h) approach caused
* unnecessary wear on PPB cells since it was called on every boot. */
hal_flash_unlock_sector(0);
}
void hal_flash_lock(void)
{
/* intentional no-op: per-sector unlock is done in hal_flash_write/erase */
}
/* SMP Multi-Processor Driver */
#ifdef ENABLE_MP
/* from boot_ppc_mp.S */
extern uint32_t _secondary_start_page;
extern uint32_t _second_half_boot_page;
extern uint32_t _spin_table[];
extern uint32_t _spin_table_addr;
/* DDR address of the spin table, set during hal_mp_init() and reused in
* hal_dts_fixup() for cpu-release-addr fixups. */
static uint32_t g_spin_table_ddr = 0;
extern uint32_t _bootpg_addr;
/* Startup additional cores with spin table and synchronize the timebase.
* spin_table_ddr: DDR address of the spin table (for checking status) */
static void hal_mp_up(uint32_t bootpg, uint32_t spin_table_ddr)
{
uint32_t all_cores, active_cores, whoami;
int timeout = 10000, i; /* 10000 * 100us = 1s, matches U-Boot convention */
whoami = get32(PIC_WHOAMI); /* Get current running core number */
all_cores = ((1 << CPU_NUMCORES) - 1); /* mask of all cores */
active_cores = (1 << whoami); /* current running cores */
wolfBoot_printf("MP: Starting cores (boot page %p, spin table %p)\n",
bootpg, spin_table_ddr);
/* Set the boot page translation register */
set32(LCC_BSTRH, 0);
set32(LCC_BSTRL, bootpg);
set32(LCC_BSTAR, (LCC_BSTAR_EN |
LCC_BSTAR_LAWTRGT(LAW_TRGT_DDR_1) |
LAW_SIZE_4KB));
(void)get32(LCC_BSTAR); /* read back to sync */
/* Enable time base on current core only */
set32(RCPM_PCTBENR, (1 << whoami));
/* Release the CPU core(s) */
set32(DCFG_BRR, all_cores);
__asm__ __volatile__("sync; isync; msync");
/* wait for other core(s) to start */
while (timeout) {
for (i = 0; i < CPU_NUMCORES; i++) {
volatile uint32_t* entry = (volatile uint32_t*)(
spin_table_ddr + (i * ENTRY_SIZE) + ENTRY_ADDR_LOWER);
if (*entry) {
active_cores |= (1 << i);
}
}
if ((active_cores & all_cores) == all_cores) {
break;
}
udelay(100);
timeout--;
}
if (timeout == 0) {
wolfBoot_printf("MP: Timeout enabling additional cores!\n");
}
/* Synchronize and reset timebase across all cores.
* On e6500, mtspr to TBL/TBU (SPR 284/285) may cause an illegal
* instruction exception — skip timebase reset if secondary cores
* did not start (timebase sync only matters for multi-core). */
if ((active_cores & all_cores) == all_cores) {
/* Disable all timebases */
set32(RCPM_PCTBENR, 0);
/* Reset our timebase */
mtspr(SPRN_TBWU, 0);
mtspr(SPRN_TBWL, 0);
/* Enable timebase for all cores */
set32(RCPM_PCTBENR, all_cores);
} else {
/* Only re-enable timebase for boot core */
set32(RCPM_PCTBENR, (1 << whoami));
}
}
static void hal_mp_init(void)
{
uint32_t *fixup = (uint32_t*)&_secondary_start_page;
uint32_t bootpg, second_half_ddr, spin_table_ddr;
int i_tlb = 0; /* always 0 */
size_t i;
const volatile uint32_t *s;
volatile uint32_t *d;
/* Assign virtual boot page at end of LAW-mapped DDR region.
* DDR LAW maps 2GB (LAW_SIZE_2GB) starting at DDR_ADDRESS.
* DDR_SIZE may exceed 32-bit range (e.g. 8GB), so use the LAW-mapped
* size to ensure bootpg fits in 32 bits and is accessible. */
bootpg = DDR_ADDRESS + 0x80000000UL - BOOT_ROM_SIZE;
/* Second half boot page (spin loop + spin table) goes just below.
* For XIP flash builds, .bootmp is in flash — secondary cores can't
* write to flash, so the spin table MUST be in DDR. */
second_half_ddr = bootpg - BOOT_ROM_SIZE;
/* DDR addresses for second half symbols */
spin_table_ddr = second_half_ddr +
((uint32_t)_spin_table - (uint32_t)&_second_half_boot_page);
/* Flush DDR destination before copying */
flush_cache(bootpg, BOOT_ROM_SIZE);
flush_cache(second_half_ddr, BOOT_ROM_SIZE);
/* Map reset page to bootpg so we can copy code there.
* Boot page translation will redirect secondary core fetches from
* 0xFFFFF000 to bootpg in DDR. */
disable_tlb1(i_tlb);
set_tlb(1, i_tlb, BOOT_ROM_ADDR, bootpg, 0, /* tlb, epn, rpn, urpn */
(MAS3_SX | MAS3_SW | MAS3_SR), (MAS2_I | MAS2_G), /* perms, wimge */
0, BOOKE_PAGESZ_4K, 1); /* ts, esel, tsize, iprot */
/* Copy first half (startup code) to DDR via BOOT_ROM_ADDR mapping.
* Uses cache-inhibited TLB to ensure data reaches DDR immediately. */
s = (const uint32_t*)fixup;
d = (uint32_t*)BOOT_ROM_ADDR;
for (i = 0; i < BOOT_ROM_SIZE/4; i++) {
d[i] = s[i];
}
/* Write _bootpg_addr and _spin_table_addr into the DDR first-half copy.
* These variables are .long 0 in the linked .bootmp (flash), and direct
* stores to their flash addresses silently fail on XIP builds.
* Calculate offsets within the boot page and write via BOOT_ROM_ADDR. */
{
volatile uint32_t *bp = (volatile uint32_t*)(BOOT_ROM_ADDR +
((uint32_t)&_bootpg_addr - (uint32_t)&_secondary_start_page));
volatile uint32_t *st = (volatile uint32_t*)(BOOT_ROM_ADDR +
((uint32_t)&_spin_table_addr - (uint32_t)&_secondary_start_page));
*bp = second_half_ddr;
*st = spin_table_ddr;
}
/* Copy second half (spin loop + spin table) directly to DDR.
* Master has DDR TLB (entry 12, MAS2_M). Flush cache after copy
* to ensure secondary cores see the data. */
s = (const uint32_t*)&_second_half_boot_page;
d = (uint32_t*)second_half_ddr;
for (i = 0; i < BOOT_ROM_SIZE/4; i++) {
d[i] = s[i];
}
flush_cache(second_half_ddr, BOOT_ROM_SIZE);
/* Persist DDR spin-table base for use in hal_dts_fixup() */