Repository navigation
Expand file tree
/
Copy pathmpfs250.c
More file actions
2109 lines (1874 loc) · 75.4 KB
/
Copy pathmpfs250.c
File metadata and controls
2109 lines (1874 loc) · 75.4 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
/* mpfs250.c
*
* Copyright (C) 2026 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
*/
/* Microchip PolarFire SoC MPFS250T HAL for wolfBoot */
/* Supports:
* RISC-V 64-bit architecture
* External flash operations
* UART communication
* System initialization
*/
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "target.h"
#include "mpfs250.h"
#include "riscv.h"
#include "image.h"
#ifndef ARCH_RISCV64
# error "wolfBoot mpfs250 HAL: wrong architecture selected. Please compile with ARCH=RISCV64."
#endif
#include "printf.h"
#include "loader.h"
#include "hal.h"
#include "gpt.h"
#include "fdt.h"
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
#include "sdhci.h"
/* Forward declaration of SDHCI IRQ handler */
extern void sdhci_irq_handler(void);
#endif
/* Video Kit DDR/Clock configuration is included in mpfs250.h */
/* ------------------------------------------------------------------------
* File-scope globals
* ---------------------------------------------------------------------- */
/* APB (PCLK) frequency for UART baud divisors. Starts at the mode's
* compile-time value (40 MHz E51 reset clock in M-mode, 150 MHz under
* HSS) and is updated when M-mode wolfBoot raises the MSS PLL. */
uint32_t mpfs_apb_clk_hz = MSS_APB_AHB_CLK;
#ifdef WOLFBOOT_RISCV_MMODE
/* CPU frequency in MHz for mcycle-based udelay(); seeded at the E51 reset
* clock and bumped to the PLL rate after mss_pll_init() (hal/mpfs250_ddr.c).
* A wrong value here skews timing-sensitive paths (e.g. SD power-up). */
uint32_t mpfs_cpu_freq_mhz = MPFS_CPU_FREQ_RESET_MHZ;
/* Saved boot ROM watchdog values, restored in hal_prepare_boot(). */
static uint32_t mpfs_wdt_default_mvrp = 0;
static uint32_t mpfs_wdt_default_ctrl = 0;
/* Snapshots captured at hal_init entry, printed after uart_init;
* RESET_SR shows the cause of the most-recent reset. */
static uint32_t mpfs_boot_wdt_snap[6];
static uint32_t mpfs_boot_reset_sr_snap;
/* Configure L2 cache: enable ways 0,1,3 (0x0B) and set way masks for all masters */
static void mpfs_config_l2_cache(void)
{
L2_WAY_ENABLE = 0x0B; /* WayEnable INDEX (not a mask): ways 0..11 are
* cache-capable; the masters' way masks (0xFF)
* restrict cache fills to ways 0-7, leaving
* 8-11 as the scratchpad carve-out. Matches
* HSS/Libero (LIBERO_SETTING_WAY_ENABLE=0xB). */
SYSREG_L2_SHUTDOWN_CR = 0;
L2_WAY_MASK_DMA = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_AXI4_PORT0 = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_AXI4_PORT1 = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_AXI4_PORT2 = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_AXI4_PORT3 = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_E51_DCACHE = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_E51_ICACHE = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_U54_1_DCACHE = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_U54_1_ICACHE = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_U54_2_DCACHE = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_U54_2_ICACHE = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_U54_3_DCACHE = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_U54_3_ICACHE = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_U54_4_DCACHE = L2_WAY_MASK_CACHE_ONLY;
L2_WAY_MASK_U54_4_ICACHE = L2_WAY_MASK_CACHE_ONLY;
__asm__ volatile("fence iorw, iorw" ::: "memory");
}
/* mcycle-based microsecond delay. MTIME is not running in M-mode without
* HSS, but mcycle ticks at the CPU clock rate and is monotonic. */
__attribute__((noinline)) void udelay(uint32_t us)
{
uint64_t start, now, target;
__asm__ volatile("rdcycle %0" : "=r"(start));
target = (uint64_t)us * (uint64_t)mpfs_cpu_freq_mhz;
do {
__asm__ volatile("rdcycle %0" : "=r"(now));
} while ((now - start) < target);
}
#endif /* WOLFBOOT_RISCV_MMODE */
/* Multi-Hart Support */
#ifdef WOLFBOOT_RISCV_MMODE
extern uint8_t _main_hart_hls; /* linker-provided address symbol; typed as uint8_t to avoid size confusion */
/* Watchdog timeout configuration.
* WATCHDOG=0 (default): WDT disabled in hal_init() then restored to boot
* ROM defaults in hal_prepare_boot() before do_boot.
* WATCHDOG=1: WDT kept enabled with WATCHDOG_TIMEOUT_MS during wolfBoot.
* Verify is bounded at ~5s; default 30s leaves ample headroom and avoids
* the need to pet the WDT during the long ECDSA verify call. */
#ifdef WATCHDOG
# ifndef WATCHDOG_TIMEOUT_MS
# define WATCHDOG_TIMEOUT_MS 30000U
# endif
/* MPFS MSS WDT clock is AHB / 256 ~= 150 MHz / 256 ~= 585 kHz at S-mode rate
* but ~80 MHz / 256 ~= 312 kHz on E51 reset clocks. Use a conservative
* 300 ticks/ms; the actual rate may be a bit higher but a slightly longer
* timeout is safe. Caller can override WATCHDOG_TIMEOUT_MS at build time. */
# define WATCHDOG_TIMEOUT_TICKS ((WATCHDOG_TIMEOUT_MS) * 300U)
#endif
/* CLINT MSIP register for IPI delivery */
#define CLINT_MSIP_REG(hart) (*(volatile uint32_t*)(CLINT_BASE + (hart) * 4))
/* CLINT machine-timer comparator (per hart) and MTIME counter */
#define CLINT_MTIMECMP_REG(hart) \
(*(volatile uint64_t*)(CLINT_BASE + 0x4000UL + (hart) * 8UL))
#define CLINT_MTIME_REG (*(volatile uint64_t*)(CLINT_BASE + 0xBFF8UL))
/* Signal secondary harts that E51 (main hart) is ready. */
static void mpfs_signal_main_hart_started(void)
{
HLS_DATA* hls = (HLS_DATA*)&_main_hart_hls;
hls->in_wfi_indicator = HLS_MAIN_HART_STARTED;
hls->my_hart_id = MPFS_FIRST_HART;
/* The eNVM secondary-hart gate polls the DTIM copy of this flag, not
* the L2-scratch HLS above: a cacheable store to the scratchpad can
* be lost on dirty-line eviction (layout-dependent), which parked
* the secondaries until the kernel's hart_start IPI -- too late for
* its 1s online window. DTIM is uncached and visible to all harts. */
*(volatile uint32_t *)MPFS_DTIM_MAIN_STARTED_ADDR =
(uint32_t)HLS_MAIN_HART_STARTED;
__asm__ volatile("fence iorw, iorw" ::: "memory");
}
#if defined(MPFS_DDR_INIT) && defined(WOLFBOOT_MMODE_SMODE_BOOT)
/* Per-hart S-mode start mailboxes, written by the E51 (boot hart release)
* or by the SBI HSM hart_start backend (on the calling U54), and consumed
* by the target hart's park loop in secondary_hart_entry().
*
* These live in the E51 DTIM, NOT in L2-scratch BSS: cacheable stores to
* the scratchpad can be lost on cache-line eviction, so cross-hart
* mailboxes written by a U54 would silently vanish (observed: the SBI
* HSM hart-state writes never became visible). The DTIM is small,
* uncached and coherent for every hart. The SBI shared state occupies
* DTIM+0x000 (see src/riscv_sbi.c); the mailboxes sit at +0x100. */
typedef struct {
volatile uint32_t marker; /* MPFS_KERNEL_HANDOFF_MARKER when valid */
volatile uint64_t entry; /* S-mode entry point */
volatile uint64_t opaque; /* a1 at entry (dtb for the boot hart) */
} mpfs_kernel_handoff_t;
#define MPFS_KERNEL_HANDOFF_MARKER 0x4C4E5858UL /* "LNXX" */
#define mpfs_kernel_handoff \
((mpfs_kernel_handoff_t *)(0x01000000UL + 0x100UL))
/* Provided by src/boot_riscv.c. */
extern void riscv_mmode_to_smode(unsigned long entry, unsigned long hartid,
unsigned long dtb) __attribute__((noreturn));
#endif /* MPFS_DDR_INIT && WOLFBOOT_MMODE_SMODE_BOOT */
/* Secondary hart (U54) entry: jump into the waiting Linux kernel (when a
* hand-off context has been staged for us) or park in WFI waiting for an
* SBI/Linux IPI.
*
* Keep this path FAST and free of UART access: the secondaries reach it
* via the kernel's HSM hart_start IPI, inside the kernel's 1s online
* window. A per-hart UART banner here once spent multiple seconds
* spinning on LSR (layout-dependent), making harts miss that window. */
void secondary_hart_entry(unsigned long hartid, HLS_DATA* hls)
{
(void)hls;
while (1) {
#if defined(MPFS_DDR_INIT) && defined(WOLFBOOT_MMODE_SMODE_BOOT)
/* Check the hand-off context BEFORE sleeping: the release IPI was
* already consumed (MSIP cleared) by the eNVM wake path, so a
* wfi-first loop sleeps through an already-staged hand-off. The
* old wfi-first order only appeared to work because mtimecmp's
* reset value of 0 left MTIP permanently pending, making the wfi
* fall through; parking the comparators exposed it. */
if (hartid < (unsigned long)MPFS_NUM_HARTS &&
mpfs_kernel_handoff[hartid].marker
== MPFS_KERNEL_HANDOFF_MARKER) {
unsigned long kentry;
unsigned long opq;
/* Acquire fence: pair with the writer's release fence so we
* are guaranteed to observe entry / opaque after seeing
* marker. Without this, RISC-V's relaxed memory model
* permits the reader to use stale field values cached before
* marker was published. */
__asm__ volatile("fence r,rw" ::: "memory");
kentry = (unsigned long)mpfs_kernel_handoff[hartid].entry;
opq = (unsigned long)mpfs_kernel_handoff[hartid].opaque;
riscv_mmode_to_smode(kentry, hartid, opq);
/* never returns */
}
/* Sleep until the next IPI (e.g. a future SBI HSM hart_start),
* then clear it and re-check the mailbox. */
__asm__ volatile("wfi");
CLINT_MSIP_REG(hartid) = 0;
__asm__ volatile("fence iorw, iorw" ::: "memory");
#else
__asm__ volatile("wfi");
#endif /* MPFS_DDR_INIT && WOLFBOOT_MMODE_SMODE_BOOT */
}
}
#if defined(MPFS_DDR_INIT) && defined(WOLFBOOT_MMODE_SMODE_BOOT)
/* Enable the CLINT MTIME counter via the SYSREG RTC/time-base clock divider
* (HSS set_RTC_divisor equivalent). Without this MTIME never advances and an
* S-mode OS has no time source for its scheduler tick. */
static void mpfs_enable_mtime(void)
{
volatile uint32_t *rtc_cr = (volatile uint32_t *)(SYSREG_BASE + 0x0CUL);
uint32_t div;
#ifdef LIBERO_SETTING_MSS_RTC_CLOCK_CR
div = (uint32_t)LIBERO_SETTING_MSS_RTC_CLOCK_CR & 0xFFFU;
#else
div = 125U; /* 125 MHz reference / 1 MHz RTC */
#endif
*rtc_cr = div; /* program divider (bits 11:0), enable off */
*rtc_cr |= (1UL << 16); /* enable RTC/time-base clock */
__asm__ volatile("fence iorw, iorw" ::: "memory");
}
/* SBI HSM hart_start backend (called from src/riscv_sbi.c on the boot
* hart): stage the target hart's start mailbox and ring its MSIP; the
* parked hart consumes it in secondary_hart_entry() and enters S-mode at
* saddr with a0=hartid, a1=opaque. */
int sbi_hal_hart_start(unsigned long hartid, unsigned long saddr,
unsigned long opaque)
{
if (hartid < (unsigned long)MPFS_FIRST_U54_HART ||
hartid > (unsigned long)MPFS_LAST_U54_HART) {
return -1;
}
mpfs_kernel_handoff[hartid].entry = (uint64_t)saddr;
mpfs_kernel_handoff[hartid].opaque = (uint64_t)opaque;
__asm__ volatile("fence iorw, iorw" ::: "memory");
mpfs_kernel_handoff[hartid].marker = MPFS_KERNEL_HANDOFF_MARKER;
__asm__ volatile("fence iorw, iorw" ::: "memory");
CLINT_MSIP_REG(hartid) = 0x01;
__asm__ volatile("fence iorw, iorw" ::: "memory");
return 0;
}
/* Override of the weak hal_smode_boot in src/boot_riscv.c. The E51 cannot
* run Linux (cpu@0 is marked disabled in the Yocto MPFS DTB), so instead of
* dropping to S-mode on hart 0 we stage the kernel/DTB pointers, IPI a U54,
* and park hart 0 in M-mode. The released U54 picks up the context from
* its WFI loop in secondary_hart_entry() and performs the actual M->S jump. */
void __attribute__((noreturn))
hal_smode_boot(unsigned long entry, unsigned long hartid, unsigned long dtb)
{
static const unsigned long park_wdt_bases[5] = {
MSS_WDT_E51_BASE, MSS_WDT_U54_1_BASE, MSS_WDT_U54_2_BASE,
MSS_WDT_U54_3_BASE, MSS_WDT_U54_4_BASE
};
unsigned int w;
(void)hartid; /* the calling E51 hart is not the kernel boot hart */
/* Bring up the MTIME time base before releasing the U54 into S-mode. */
mpfs_enable_mtime();
/* Enable the clocks and release the soft resets of MMUART1-4 for the
* OS: the kernel's mpfs clock driver gates SUBBLK_CLOCK_CR but does
* not release peripheral soft resets (HSS normally does), so without
* this the serial console (MMUART1) stays dead. Done here on the
* E51, single-threaded, so no SYSREG read-modify-write races. */
SYSREG_SUBBLK_CLOCK_CR |= (MSS_PERIPH_MMUART0 << 1) |
(MSS_PERIPH_MMUART0 << 2) |
(MSS_PERIPH_MMUART0 << 3) |
(MSS_PERIPH_MMUART0 << 4);
__asm__ volatile("fence iorw, iorw" ::: "memory");
SYSREG_SOFT_RESET_CR &= ~((MSS_PERIPH_MMUART0 << 1) |
(MSS_PERIPH_MMUART0 << 2) |
(MSS_PERIPH_MMUART0 << 3) |
(MSS_PERIPH_MMUART0 << 4));
__asm__ volatile("fence iorw, iorw" ::: "memory");
mpfs_kernel_handoff[MPFS_FIRST_U54_HART].entry = (uint64_t)entry;
mpfs_kernel_handoff[MPFS_FIRST_U54_HART].opaque = (uint64_t)dtb;
__asm__ volatile("fence iorw, iorw" ::: "memory");
mpfs_kernel_handoff[MPFS_FIRST_U54_HART].marker =
MPFS_KERNEL_HANDOFF_MARKER;
__asm__ volatile("fence iorw, iorw" ::: "memory");
wolfBoot_printf("Releasing hart %d into S-mode at 0x%lx (dtb=0x%lx)\n",
MPFS_FIRST_U54_HART, entry, dtb);
CLINT_MSIP_REG(MPFS_FIRST_U54_HART) = 0x01;
__asm__ volatile("fence iorw, iorw" ::: "memory");
/* Park the E51 as the platform monitor (HSS's watchdog-service role):
* doze on the machine timer and pet all watchdogs every few seconds.
* The MSS watchdogs always count and reset the chip on timeout
* (~28 s at the boot-time settings), so the parked E51 must keep
* them refreshed while the OS boots and runs.
*
* Wake only on the machine timer; mstatus.MIE stays clear so the
* pending timer wakes WFI without vectoring into the trap path.
* Pet ALL watchdogs (HSS's WDog-service role): the MSS WDTs always
* count and RESET the chip on timeout even with CONTROL=0, and the
* OS watchdog driver is disabled in the dtb fixup (when it owned
* them, its refresh-forbidden window made our blind refreshes trip
* it). With MVRP at maximum a refresh is always permitted. */
__asm__ volatile("csrw mie, %0" :: "r"(0x80UL)); /* MTIE */
while (1) {
for (w = 0; w < 5U; w++) {
MSS_WDT_REFRESH(park_wdt_bases[w]) = 0xDEADC0DEU;
}
CLINT_MTIMECMP_REG(MPFS_FIRST_HART) =
CLINT_MTIME_REG + (5UL * RTC_CLOCK_FREQ);
__asm__ volatile("wfi");
}
__builtin_unreachable();
}
#endif /* MPFS_DDR_INIT && WOLFBOOT_MMODE_SMODE_BOOT */
#endif /* WOLFBOOT_RISCV_MMODE */
#if defined(EXT_FLASH) && defined(TEST_EXT_FLASH) && defined(__WOLFBOOT)
static int test_ext_flash(void);
#endif
#if defined(EXT_FLASH) && defined(UART_QSPI_PROGRAM) && defined(__WOLFBOOT)
static void qspi_uart_program(void);
#endif
void hal_init(void)
{
#ifdef WOLFBOOT_RISCV_MMODE
volatile uint32_t *wdt_e51 = (volatile uint32_t *)0x20001000UL;
volatile uint32_t *sysreg_reset_sr = (volatile uint32_t *)0x20002020UL;
int h;
#if defined(MPFS_DDR_INIT) && defined(WOLFBOOT_MMODE_SMODE_BOOT)
volatile uint32_t *dtim = (volatile uint32_t *)0x01000000UL;
unsigned int k;
#endif
#ifndef WATCHDOG
static const unsigned long wdt_bases[5] = {
MSS_WDT_E51_BASE, MSS_WDT_U54_1_BASE, MSS_WDT_U54_2_BASE,
MSS_WDT_U54_3_BASE, MSS_WDT_U54_4_BASE
};
unsigned int w;
#endif
#ifdef MPFS_DDR_INIT
unsigned int outer_retry;
int ddr_ok = 0;
#endif
/* Park every hart's machine-timer comparator at maximum. CLINT MTIME
* is 0 (the RTC time base is not running yet) and mtimecmp resets to 0,
* so MTIP is pending on every hart out of reset. A pending interrupt
* makes WFI return immediately, so the parked secondary harts' eNVM
* wait loop SPINS continuously (fetching from eNVM for the entire
* boot) instead of sleeping. Parking the comparators clears MTIP so
* WFI really waits. */
for (h = 0; h < MPFS_NUM_HARTS; h++) {
CLINT_MTIMECMP_REG(h) = ~(uint64_t)0;
}
__asm__ volatile("fence iorw, iorw" ::: "memory");
#if defined(MPFS_DDR_INIT) && defined(WOLFBOOT_MMODE_SMODE_BOOT)
/* Clear the DTIM-resident cross-hart state (start mailboxes + SBI
* shared block): DTIM content is undefined at power-on. */
for (k = 0; k < (0x200U / sizeof(uint32_t)); k++) {
dtim[k] = 0;
}
__asm__ volatile("fence iorw, iorw" ::: "memory");
#endif
/* Capture boot ROM WDT defaults for restoration in hal_prepare_boot() */
mpfs_wdt_default_mvrp = MSS_WDT_MVRP(MSS_WDT_E51_BASE);
mpfs_wdt_default_ctrl = MSS_WDT_CONTROL(MSS_WDT_E51_BASE);
/* Snapshot boot-ROM WDT state and SYSREG RESET_SR (reset status
* cause) so we can print them AFTER uart_init. RESET_SR is W1C --
* we clear after reading. */
mpfs_boot_wdt_snap[0] = wdt_e51[0];
mpfs_boot_wdt_snap[1] = wdt_e51[1];
mpfs_boot_wdt_snap[2] = wdt_e51[2];
mpfs_boot_wdt_snap[3] = wdt_e51[3];
mpfs_boot_wdt_snap[4] = wdt_e51[4];
mpfs_boot_wdt_snap[5] = wdt_e51[5];
mpfs_boot_reset_sr_snap = *sysreg_reset_sr;
*sysreg_reset_sr = mpfs_boot_reset_sr_snap; /* W1C: clear seen bits */
#ifndef WATCHDOG
/* WATCHDOG=0 (default): disable WDT for the duration of wolfBoot; it is
* re-enabled in hal_prepare_boot() before do_boot. The MPFS MSS WDOG
* cannot be disabled outright (it always counts), so clear DEVRST
* (bit 5) -- timeout then raises an NMI instead of a chip reset -- set
* TIME/MVRP to max for the longest window, and refresh with the magic
* key (a refresh inside the triggered window would arm a reset). */
for (w = 0; w < 5; w++) {
MSS_WDT_REFRESH(wdt_bases[w]) = 0xDEADC0DEU;
MSS_WDT_TIME(wdt_bases[w]) = 0x00FFFFFFUL;
MSS_WDT_MVRP(wdt_bases[w]) = 0x00FFFFFFUL;
MSS_WDT_CONTROL(wdt_bases[w]) = 0;
MSS_WDT_REFRESH(wdt_bases[w]) = 0xDEADC0DEU;
}
#else
/* WATCHDOG=1: keep WDT enabled with a generous timeout for crypto.
* Verify is bounded at ~5s; configure a much larger timeout so we
* never have to pet the WDT during ECDSA verify. */
MSS_WDT_REFRESH(MSS_WDT_E51_BASE) = 0xDEADC0DEU;
MSS_WDT_MVRP(MSS_WDT_E51_BASE) = WATCHDOG_TIMEOUT_TICKS;
MSS_WDT_CONTROL(MSS_WDT_E51_BASE) |= MSS_WDT_CTRL_ENABLE;
#endif
mpfs_config_l2_cache();
mpfs_signal_main_hart_started();
#endif
#ifdef DEBUG_UART
SYSREG_SUBBLK_CLOCK_CR |= (MSS_PERIPH_MMUART0 << DEBUG_UART_PORT);
SYSREG_SOFT_RESET_CR &= ~(MSS_PERIPH_MMUART0 << DEBUG_UART_PORT);
uart_init();
#endif
#ifdef WOLFBOOT_REPRODUCIBLE_BUILD
wolfBoot_printf("wolfBoot Version: %s\n", LIBWOLFBOOT_VERSION_STRING);
#else
wolfBoot_printf("wolfBoot Version: %s (%s %s)\n",
LIBWOLFBOOT_VERSION_STRING, __DATE__, __TIME__);
#endif
#ifdef WOLFBOOT_RISCV_MMODE
wolfBoot_printf("Running on E51 (hart 0) in M-mode\n");
DBG_DDR("Boot WDT_E51: REFRESH=%x CTRL=%x STATUS=%x TIME=%x MVRP=%x TRIG=%x\n",
mpfs_boot_wdt_snap[0], mpfs_boot_wdt_snap[1], mpfs_boot_wdt_snap[2],
mpfs_boot_wdt_snap[3], mpfs_boot_wdt_snap[4], mpfs_boot_wdt_snap[5]);
wolfBoot_printf("Boot RESET_SR: %x (bit0=PERIPH bit1=MSS bit2=CPU bit3=DBG "
"bit4=FABRIC bit5=WDOG bit6=GPIO bit7=BUS bit8=SOFT)\n",
mpfs_boot_reset_sr_snap);
#ifdef MPFS_DDR_INIT
/* Bring up LPDDR4 before any DDR-resident operations.
*
* Outer retry loop: each call to mpfs_ddr_init() does a SYSREG DDRC
* soft-reset pulse, which clears the MTC engine state. If the
* inner retry inside mpfs_ddr_init() exhausts (typically because
* MTC wedged after the first failure), come back here for a full
* controller re-init. Empirical: per-attempt failure rate ~30%, so
* MPFS_DDR_MAX_OUTER_RETRY (6) outer attempts cover ~99.9% of boots. */
for (outer_retry = 0; outer_retry < MPFS_DDR_MAX_OUTER_RETRY;
outer_retry++) {
if (outer_retry > 0) {
wolfBoot_printf(
"DDR: Outer retry %u/%u (full DDRC re-init)\n",
outer_retry, MPFS_DDR_MAX_OUTER_RETRY);
}
if (mpfs_ddr_init(outer_retry) == 0) {
ddr_ok = 1;
break;
}
}
if (!ddr_ok) {
/* No safe path forward: WOLFBOOT_LOAD_ADDRESS is in DDR, so a
* subsequent disk-load would write to a non-functional
* controller and hang silently inside the AXI master. Halt
* with a clear message so the operator can power-cycle. */
wolfBoot_printf(
"DDR: Init FAILED after %u outer retries -- halting.\n"
"DDR: Power-cycle the board and retry.\n",
MPFS_DDR_MAX_OUTER_RETRY);
while (1) {
/* spin */
}
}
#endif
#endif
#ifdef EXT_FLASH
if (qspi_init() != 0) {
wolfBoot_printf("QSPI: Init failed\n");
} else {
#if defined(TEST_EXT_FLASH) && defined(__WOLFBOOT)
test_ext_flash();
#endif
#if defined(UART_QSPI_PROGRAM) && defined(__WOLFBOOT)
qspi_uart_program();
#endif
}
#endif /* EXT_FLASH */
}
/* System Controller Mailbox */
static int mpfs_scb_mailbox_busy(void)
{
return (SCBCTRL_REG(SERVICES_SR_OFFSET) & SERVICES_SR_BUSY_MASK);
}
/* Read 16-byte device serial number via SCB system service (opcode 0x00). */
int mpfs_read_serial_number(uint8_t *serial)
{
uint32_t cmd, status;
int i, timeout;
if (serial == NULL) {
return -1;
}
/* Check if mailbox is busy */
if (mpfs_scb_mailbox_busy()) {
wolfBoot_printf("SCB mailbox busy\n");
return -2;
}
/* Send serial number request command (opcode 0x00)
* Command format: [31:16] = opcode, [0] = request bit */
cmd = (SYS_SERV_CMD_SERIAL_NUMBER << SERVICES_CR_COMMAND_SHIFT) |
SERVICES_CR_REQ_MASK;
SCBCTRL_REG(SERVICES_CR_OFFSET) = cmd;
/* Wait for request bit to clear (command accepted) */
timeout = MPFS_SCB_TIMEOUT;
while ((SCBCTRL_REG(SERVICES_CR_OFFSET) & SERVICES_CR_REQ_MASK) && timeout > 0) {
timeout--;
}
if (timeout == 0) {
wolfBoot_printf("SCB mailbox request timeout\n");
return -3;
}
/* Wait for busy bit to clear (command completed) */
timeout = MPFS_SCB_TIMEOUT;
while (mpfs_scb_mailbox_busy() && timeout > 0) {
timeout--;
}
if (timeout == 0) {
wolfBoot_printf("SCB mailbox busy timeout\n");
return -4;
}
/* Check status (upper 16 bits of status register) */
status = (SCBCTRL_REG(SERVICES_SR_OFFSET) >> SERVICES_SR_STATUS_SHIFT) & 0xFFFF;
if (status != 0) {
wolfBoot_printf("SCB mailbox error: 0x%x\n", status);
return -5;
}
/* Read serial number from mailbox RAM (16 bytes) */
for (i = 0; i < DEVICE_SERIAL_NUMBER_SIZE; i++) {
serial[i] = SCBMBOX_BYTE(i);
}
return 0;
}
/* Linux kernel command line arguments */
/* Must stay below the fdt.h include: the LINUX_BOOTARGS_OVERRIDE default
* there keys off build-supplied macros only, not this fallback. */
#ifndef LINUX_BOOTARGS
#ifndef LINUX_BOOTARGS_ROOT
/* wolfBoot SD layout (tools/scripts/program-sdcard.sh): p1=boot FIT,
* p2=update, p3=rootfs. */
#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p3"
#endif
#define LINUX_BOOTARGS \
"earlycon=sbi root="LINUX_BOOTARGS_ROOT" rootwait uio_pdrv_genirq.of_id=generic-uio"
#endif
/* Microchip OUI (Organizationally Unique Identifier) for MAC address */
#define MICROCHIP_OUI_0 0x00
#define MICROCHIP_OUI_1 0x04
#define MICROCHIP_OUI_2 0xA3
static int mpfs_dts_fixup_inplace(void* dts_addr, uint32_t capacity)
{
fdt_ctx ctx;
int off, ret;
uint8_t device_serial_number[DEVICE_SERIAL_NUMBER_SIZE];
uint8_t mac_addr[6];
#if defined(MPFS_DDR_INIT) && defined(WOLFBOOT_MMODE_SMODE_BOOT)
/* Nodes disabled below. Watchdogs: the MSS WDTs always count; the OS
* driver arms them at probe but nothing pings them (no userspace
* watchdog daemon in the default image), so the system would reset
* ~28 s into boot. Disabled, they only latch a harmless tripped
* status and the parked E51 monitor keeps them refreshed. Re-enable
* when an OS-side petting story exists. */
static const char *const cpu_off[] = {
"watchdog@20001000", "watchdog@20101000", "watchdog@20103000",
"watchdog@20105000", "watchdog@20107000" };
unsigned int i;
#endif
/* Validate the blob against the window it actually occupies. */
ret = fdt_open(&ctx, dts_addr, capacity);
if (ret != 0) {
wolfBoot_printf("FDT: Invalid header! %d\n", ret);
return ret;
}
wolfBoot_printf("FDT: Size %d\n", (int)fdt_size(&ctx));
/* Reserve free space to allow adding/modifying properties.
* Sizing comes from WOLFBOOT_FDT_FIXUP_HEADROOM in include/fdt.h. */
ret = fdt_grow(&ctx, WOLFBOOT_FDT_FIXUP_HEADROOM);
if (ret != 0) {
wolfBoot_printf("FDT: No headroom for fixups (%d)\n", ret);
return ret;
}
/* Later fixups (watchdog disable, serial number) must run even if the
* bootargs fixup fails, so log and continue rather than return. */
ret = fdt_fixup_bootargs(&ctx, LINUX_BOOTARGS, LINUX_BOOTARGS_OVERRIDE);
if (ret < 0) {
wolfBoot_printf("FDT: Failed to set bootargs (%d)\n", ret);
}
#ifdef WOLFBOOT_UPDATE_DISK
(void)wolfBoot_disk_dts_fixup(&ctx);
#endif
#if defined(MPFS_DDR_INIT) && defined(WOLFBOOT_MMODE_SMODE_BOOT)
/* Disable the MSS watchdog dtb nodes BEFORE the serial-number read: this
* does not depend on the serial number and MUST run even if the SC serial
* read below fails. Otherwise the OS watchdog driver would arm them at
* probe while the parked E51 monitor (hal_smode_boot) also refreshes all
* five every cycle -- a conflict that can trip a watchdog-driven reset.
*
* Do NOT override the stock /memory nodes: the 32-bit cached DDR window at
* 0x80000000 is only 1 GB wide (0xC0000000 up is the NON-CACHED alias onto
* the same DDR) and the stock Video Kit DTB already describes the full
* 2 GB; an earlier fixup that forced memory@80000000 to 2 GB made the
* kernel treat the alias window as extra RAM (self-aliasing corruption;
* boot hung at the first deep memblock allocation).
*
* cpu@2..cpu@4 stay ENABLED: the SBI HSM hart_start backend releases the
* parked harts on the kernel's request (SMP). cpu@0 (E51) is already
* disabled in the Yocto DTB; cpu@1 stays enabled so Linux boots on it. */
for (i = 0; i < sizeof(cpu_off) / sizeof(cpu_off[0]); i++) {
off = fdt_find_node_offset(&ctx, -1, cpu_off[i]);
if (off >= 0) {
ret = fdt_fixup_str(&ctx, off, cpu_off[i], "status",
"disabled");
if (ret != 0) {
wolfBoot_printf("FDT: Failed to disable %s (%d)\n",
cpu_off[i], ret);
}
}
else {
wolfBoot_printf("FDT: %s not found\n", cpu_off[i]);
}
}
#endif /* MPFS_DDR_INIT && WOLFBOOT_MMODE_SMODE_BOOT */
/* Read device serial number from system controller */
ret = mpfs_read_serial_number(device_serial_number);
if (ret != 0) {
wolfBoot_printf("FDT: Failed to read serial number (%d)\n", ret);
/* Continue without setting MAC addresses */
return 0;
}
wolfBoot_printf("FDT: Device serial: %02x%02x%02x%02x-%02x%02x%02x%02x-"
"%02x%02x%02x%02x-%02x%02x%02x%02x\n",
device_serial_number[15], device_serial_number[14],
device_serial_number[13], device_serial_number[12],
device_serial_number[11], device_serial_number[10],
device_serial_number[9], device_serial_number[8],
device_serial_number[7], device_serial_number[6],
device_serial_number[5], device_serial_number[4],
device_serial_number[3], device_serial_number[2],
device_serial_number[1], device_serial_number[0]);
/* Build MAC address: Microchip OUI + lower 3 bytes of serial number
* Format: {0x00, 0x04, 0xA3, serial[2], serial[1], serial[0]} */
mac_addr[0] = MICROCHIP_OUI_0;
mac_addr[1] = MICROCHIP_OUI_1;
mac_addr[2] = MICROCHIP_OUI_2;
mac_addr[3] = device_serial_number[2];
mac_addr[4] = device_serial_number[1];
mac_addr[5] = device_serial_number[0];
wolfBoot_printf("FDT: MAC0 = %02x:%02x:%02x:%02x:%02x:%02x\n",
mac_addr[0], mac_addr[1], mac_addr[2],
mac_addr[3], mac_addr[4], mac_addr[5]);
/* Set local-mac-address for ethernet@20110000 (mac0) */
off = fdt_find_node_offset(&ctx, -1, "ethernet@20110000");
if (off >= 0) {
ret = fdt_setprop(&ctx, off, "local-mac-address", mac_addr, 6);
if (ret != 0) {
wolfBoot_printf("FDT: Failed to set mac0 address (%d)\n", ret);
}
}
else {
wolfBoot_printf("FDT: ethernet@20110000 not found\n");
}
/* Set local-mac-address for ethernet@20112000 (mac1)
* Use MAC address + 1 for the second interface */
mac_addr[5] = device_serial_number[0] + 1;
wolfBoot_printf("FDT: MAC1 = %02x:%02x:%02x:%02x:%02x:%02x\n",
mac_addr[0], mac_addr[1], mac_addr[2],
mac_addr[3], mac_addr[4], mac_addr[5]);
off = fdt_find_node_offset(&ctx, -1, "ethernet@20112000");
if (off >= 0) {
ret = fdt_setprop(&ctx, off, "local-mac-address", mac_addr, 6);
if (ret != 0) {
wolfBoot_printf("FDT: Failed to set mac1 address (%d)\n", ret);
}
}
else {
wolfBoot_printf("FDT: ethernet@20112000 not found\n");
}
return 0;
}
#if defined(WOLFBOOT_RISCV_MMODE) && defined(MPFS_DDR_INIT)
/* FIT subimage copy via PDMA (overrides the weak default in src/fdt.c).
* CPU writes to DDR do not land on this board, so route kernel/dtb copies
* through the PDMA master. A DDR source is read via its non-cached alias so
* PDMA sees real DDR; mpfs_pdma_memcpy remaps the dst 0x8x->0xCx and flushes
* L2. Chunked + WDT-petted for kernel-sized copies. */
int wolfBoot_fit_memcpy(void *dst, const void *src, uint32_t len)
{
uintptr_t d = (uintptr_t)dst;
uintptr_t s = (uintptr_t)src;
volatile const uint8_t *ncd;
const uint8_t *ncs;
uint32_t off = 0;
uint32_t chunk;
uint32_t k;
int retry;
int mism;
int rc = 0;
if ((s & 0xF0000000UL) == 0x80000000UL) {
s |= 0x40000000UL; /* non-cached source alias */
}
while (off < len) {
chunk = len - off;
if (chunk > (1024U * 1024U)) {
chunk = 1024U * 1024U;
}
/* mpfs_pdma_memcpy always returns 0, so the read-back verify below is
* the authoritative success check for this chunk. The PDMA->DDR write
* intermittently drops a block, so re-PDMA on a mismatch (same pattern
* as sdhci_platform_block_copy). A DDR destination (0x8xxxxxxx) is
* read back through its non-cached alias (| 0x40000000) so we compare
* what actually landed in DDR, not stale L2; this makes the caller's
* fail-closed rc real for the signature-uncovered kernel/dtb copies.
* A non-DDR destination (e.g. an L2 scratch buffer) lands directly, so
* a single copy suffices. */
mism = 1;
for (retry = 0; retry < 8 && mism != 0; retry++) {
(void)mpfs_pdma_memcpy((void *)(d + off),
(const void *)(s + off), chunk);
/* Refresh all five MSS watchdogs (they always count and reset the
* chip and cannot be disabled) during the multi-MB kernel copy
* and its read-back verify. */
MSS_WDT_REFRESH(MSS_WDT_E51_BASE) = 0xDEADC0DEU;
MSS_WDT_REFRESH(MSS_WDT_U54_1_BASE) = 0xDEADC0DEU;
MSS_WDT_REFRESH(MSS_WDT_U54_2_BASE) = 0xDEADC0DEU;
MSS_WDT_REFRESH(MSS_WDT_U54_3_BASE) = 0xDEADC0DEU;
MSS_WDT_REFRESH(MSS_WDT_U54_4_BASE) = 0xDEADC0DEU;
if ((d & 0xF0000000UL) != 0x80000000UL) {
mism = 0; /* non-DDR dst lands on the first copy */
break;
}
__asm__ volatile("fence iorw,iorw" ::: "memory");
ncd = (volatile const uint8_t *)((d + off) | 0x40000000UL);
ncs = (const uint8_t *)(s + off);
mism = 0;
for (k = 0; k < chunk; k++) {
if (ncd[k] != ncs[k]) {
mism = 1;
break;
}
}
}
if (mism != 0) {
/* Copy could not be verified within the retry budget; remember the
* failure so the caller fails closed rather than boot corrupt,
* no-longer-signature-covered data. */
rc = -1;
}
off += chunk;
}
return rc;
}
/* L2 round-trip wrapper around mpfs_dts_fixup_inplace(). The dtb lives in DDR
* (WOLFBOOT_LOAD_DTS_ADDRESS) but CPU writes to DDR do not land here, so copy
* it (non-cached read) into an L2 scratch buffer, run the FDT fixups there
* (CPU L2 writes work), then PDMA the result back to DDR. */
int hal_dts_fixup(void* dts_addr, uint32_t capacity)
{
static uint8_t l2_dtb[64 * 1024] __attribute__((aligned(8)));
fdt_ctx ctx;
const uint8_t *ddr_nc;
uint32_t sz;
int ret;
if (dts_addr == NULL) {
return -1;
}
ddr_nc = (const uint8_t *)((uintptr_t)dts_addr | 0x40000000UL);
/* The source is bounded by whichever is smaller: the caller's DDR
* window, or what the L2 scratch buffer can hold once the fixup
* headroom is set aside. fdt_open() enforces it, so the memcpy below
* cannot overrun l2_dtb however corrupt the header is. */
sz = (uint32_t)(sizeof(l2_dtb) - WOLFBOOT_FDT_FIXUP_HEADROOM);
if (capacity < sz) {
sz = capacity;
}
if (fdt_open(&ctx, (void *)ddr_nc, sz) != 0) {
wolfBoot_printf("FDT: invalid header at %p\n", dts_addr);
return -1;
}
sz = fdt_size(&ctx);
/* DDR (non-cached) -> L2 */
memcpy(l2_dtb, ddr_nc, sz);
/* fixup in the CPU-writable L2 buffer, which may use the whole of it */
ret = mpfs_dts_fixup_inplace(l2_dtb, (uint32_t)sizeof(l2_dtb));
/* L2 -> DDR via PDMA (expanded totalsize) */
if (fdt_open(&ctx, l2_dtb, (uint32_t)sizeof(l2_dtb)) != 0) {
wolfBoot_printf("FDT: fixed-up dtb rejected\n");
return -1;
}
if (wolfBoot_fit_memcpy(dts_addr, l2_dtb, fdt_size(&ctx)) != 0) {
wolfBoot_printf("FDT: dtb copy-back to DDR failed\n");
return -1;
}
return ret;
}
#else
/* Without the M-mode DDR constraints the dtb buffer is CPU-writable, so
* run the fixups directly in place (the original behavior, kept so
* FDT-enabled non-DDR builds do not silently fall back to the weak
* no-op hal_dts_fixup). */
int hal_dts_fixup(void* dts_addr, uint32_t capacity)
{
if (dts_addr == NULL) {
return -1;
}
return mpfs_dts_fixup_inplace(dts_addr, capacity);
}
#endif /* WOLFBOOT_RISCV_MMODE && MPFS_DDR_INIT */
void hal_prepare_boot(void)
{
#ifdef WOLFBOOT_RISCV_MMODE
#ifndef WOLFBOOT_MMODE_SMODE_BOOT
/* Restore boot ROM WDT defaults so the application sees a normal WDT.
* Refresh first so the timer doesn't fire immediately after we apply
* the new MVRP. Restore the original CONTROL value (including the
* enable bit) rather than unconditionally enabling. */
MSS_WDT_REFRESH(MSS_WDT_E51_BASE) = 0xDEADC0DEU;
MSS_WDT_MVRP(MSS_WDT_E51_BASE) = mpfs_wdt_default_mvrp;
MSS_WDT_CONTROL(MSS_WDT_E51_BASE) = mpfs_wdt_default_ctrl;
#else
/* Booting an S-mode OS: keep the watchdogs in the safe state set in
* hal_init (no device reset, maximum window) and give every hart's
* watchdog one final refresh so the OS inherits a full window (the
* OS watchdog driver hangs at probe if it finds an already-tripped
* watchdog). The OS watchdog nodes are disabled in the dtb, so after
* hand-off the parked E51 monitor loop in hal_smode_boot refreshes all
* five each cycle for the life of the OS. */
MSS_WDT_REFRESH(MSS_WDT_E51_BASE) = 0xDEADC0DEU;
MSS_WDT_REFRESH(MSS_WDT_U54_1_BASE) = 0xDEADC0DEU;
MSS_WDT_REFRESH(MSS_WDT_U54_2_BASE) = 0xDEADC0DEU;
MSS_WDT_REFRESH(MSS_WDT_U54_3_BASE) = 0xDEADC0DEU;
MSS_WDT_REFRESH(MSS_WDT_U54_4_BASE) = 0xDEADC0DEU;
/* Hand the OS a clean SD controller: wolfBoot just used it for the
* image load, and the leftover state makes the OS driver's re-init
* and tuning intermittently fail ("Waiting for root device"). Guarded
* on the disk backend: an S-mode-boot config that loads the kernel from
* a non-SDHCI source (e.g. QSPI) pulls in no sdhci.h declaration. */
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
sdhci_shutdown();
#endif
#endif
#endif
}
void RAMFUNCTION hal_flash_unlock(void)
{
}
void RAMFUNCTION hal_flash_lock(void)
{
}
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
{
(void)address;
(void)data;
(void)len;
return 0;
}
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
{
(void)address;
(void)len;
return 0;
}
/* Wait for SCB register bits to clear, with timeout */
static int mpfs_scb_wait_clear(uint32_t reg_offset, uint32_t mask,
uint32_t timeout)
{
while ((SCBCTRL_REG(reg_offset) & mask) && --timeout)
;
return (timeout == 0) ? -1 : 0;
}
#ifdef EXT_FLASH
/* ==========================================================================
* QSPI Flash Controller Implementation
*
* Both MSS QSPI (0x21000000) and SC QSPI (0x37020100) use CoreQSPI v2
* with identical register layouts. The controller is selected at build
* time via MPFS_SC_SPI, which changes QSPI_BASE in the header.
* ========================================================================== */
/* Microsecond delay using RISC-V time CSR (1 MHz tick rate) */
#ifndef WOLFBOOT_RISCV_MMODE
static void udelay(uint32_t us)
{
uint64_t start = csr_read(time);
while ((uint64_t)(csr_read(time) - start) < us)
;
}
#endif
/* Forward declarations */
static int qspi_transfer_block(uint8_t read_mode, const uint8_t *cmd,
uint32_t cmd_len, uint8_t *data,
uint32_t data_len, uint8_t dummy_cycles);
static int qspi_read_id(uint8_t *id_buf);
static int qspi_enter_4byte_mode(void);
/* Send Release from Deep Power-Down / Wake up command */
static void qspi_flash_wakeup(void)
{
uint8_t cmd = 0xAB; /* Release from Deep Power-Down */
qspi_transfer_block(QSPI_MODE_WRITE, &cmd, 1, NULL, 0, 0);
/* Flash needs tRES1 (3us typ) to wake up */
udelay(10);
}
int qspi_init(void)
{
uint8_t id[3];
uint32_t timeout;
#ifdef MPFS_SC_SPI
wolfBoot_printf("QSPI: Using SC QSPI Controller (0x%x)\n", QSPI_BASE);
/* Wait for system controller to finish any pending operations before
* taking direct control of the SC QSPI peripheral */
mpfs_scb_wait_clear(SERVICES_SR_OFFSET, SERVICES_SR_BUSY_MASK,
QSPI_TIMEOUT_TRIES);