mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2026-06-17 16:41:19 +01:00
7bff4e529e
During warm reset, my EIC770X/Hifive Premier P550 can sometimes
encounter memory corruption issue crashing Linux boot. Currently the
issue is mitigated by having a sbi_printf before writing to the reset
register. I analyzed the issue further since then. From the SoC
datasheet[1], it's recommended to implement power-down flow as:
a. Designate a primary core, and let it broadcast requests to other
cores to execute a CEASE insn. Primary core also notifies an
"Externel Agent" to start monitoring.
b. Primary core waits for other cores to CEASE before it CEASEs.
c. "External Agent" waits for primary core to CEASE before resets
the Core Complex.
It's possible that EIC770X can trigger undefined behavior if the core
complex is reset while the harts are actively running. The sbi_printf
in the reset handler effectively hides the problem by delaying the
reset -- by the time sbi_printf finishes, all other harts will have
already landed in the loop in sbi_hsm_hart_wait(), which parks the hart.
Without the sbi_printf, I confirmed that other harts haven't reached
sbi_hsm_hart_wait yet before current hart resets the SoC. (by debugging)
To safely reset, and inspired by the datasheet, the warm reset logic
in eic770x.c now use the current hart as both primary core and the
"External Agent", and other harts as secondary cores. It leverages
the HSM framework and a new eic770x_hsm device to CEASE other harts,
and wait for them to CEASE before resets the SoC. with the sbi_printf
before reset removed, and this logic in place, stress test shows that
the memory corruption issue no longer occurs.
The new eic770x_hsm device is only used for the reset-CEASE logic at
the moment, and may be extended to a fully functional HSM device in
the future.
[1] https://github.com/eswincomputing/EIC7700X-SoC-Technical-Reference-Manual
Fixes: e5797e0688 ("platform: generic: eswin: add EIC7700")
Signed-off-by: Bo Gan <ganboing@gmail.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20260605075708.96-3-ganboing@gmail.com
Signed-off-by: Anup Patel <anup@brainfault.org>
505 lines
15 KiB
C
505 lines
15 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2025 Bo Gan <ganboing@gmail.com>
|
|
*
|
|
*/
|
|
|
|
#include <platform_override.h>
|
|
#include <sbi/riscv_io.h>
|
|
#include <sbi/sbi_console.h>
|
|
#include <sbi/sbi_system.h>
|
|
#include <sbi/sbi_math.h>
|
|
#include <sbi/sbi_hart.h>
|
|
#include <sbi/sbi_hsm.h>
|
|
#include <sbi/sbi_ipi.h>
|
|
#include <sbi/sbi_hart_pmp.h>
|
|
#include <sbi/sbi_hart_protection.h>
|
|
#include <sbi_utils/hsm/fdt_hsm_sifive_inst.h>
|
|
#include <eswin/eic770x.h>
|
|
#include <eswin/hfp.h>
|
|
|
|
static struct sbi_hart_protection eswin_eic7700_pmp_protection;
|
|
static volatile bool eic770x_power_down = false;
|
|
|
|
static int eic770x_hart_start(u32 hartid, ulong saddr)
|
|
{
|
|
u32 hartindex = sbi_hartid_to_hartindex(hartid);
|
|
|
|
/*
|
|
* saddr is ignored intentionally.
|
|
* For non-power-down scenarios, eic770x_hart_stop simply
|
|
* returns, putting the hart in atomic_read(&hdata->state)
|
|
* loop in sbi_hsm_hart_wait. We wake it up if it's in wfi()
|
|
*/
|
|
return sbi_ipi_raw_send(hartindex, true);
|
|
}
|
|
|
|
static int eic770x_hart_stop()
|
|
{
|
|
/*
|
|
* fence to enforce all previous ipi clears are done
|
|
* Refer to comments below in eic770x_cease_other_harts
|
|
*/
|
|
asm volatile ("fence o, r");
|
|
|
|
if (!eic770x_power_down)
|
|
return SBI_ENOTSUPP;
|
|
|
|
/*
|
|
* bit 0: disableDCacheClockGate
|
|
* When some or all warm boot harts haven't gone under at least 1
|
|
* cycle of hsm start/stop, (happens if reset is issued in pre-
|
|
* boot environment u-boot/UEFI where all warm boot harts are
|
|
* pending start), the FEAT0 CSR still holds the SoC reset values,
|
|
* and disableDCacheClockGate is set. A CEASE instruction executed
|
|
* when disableDCacheClockGate=1 will not properly reflect its
|
|
* ceased status in mcput_cease_from_tile_x. Thus, clear it before
|
|
* CEASE.
|
|
*/
|
|
csr_clear(EIC770X_CSR_FEAT0, 0x1);
|
|
|
|
sifive_cease();
|
|
}
|
|
|
|
void eic770x_cease_other_harts(void)
|
|
{
|
|
u32 to_cease[2] = {};
|
|
|
|
eic770x_power_down = true;
|
|
sbi_for_each_hartindex(i) {
|
|
u32 hartid = sbi_hartindex_to_hartid(i);
|
|
u32 die = hart_die(hartid);
|
|
u32 core = hart_core(hartid);
|
|
|
|
/* Only wait for other harts */
|
|
if (i == current_hartindex())
|
|
continue;
|
|
/*
|
|
* Bring harts out of WFI in sbi_hsm_hart_wait
|
|
* Harts won't miss this IPI, because:
|
|
* 1. If hart goes to wfi() in sbi_hsm_hart_wait,
|
|
* it must have not observed eic770x_power_down
|
|
* 2. If it hasn't observed eic770x_power_down,
|
|
* then it must haven't observed the IPI sent,
|
|
* given the wmb() in sbi_ipi_raw_send
|
|
* 3. Given the fence o, r, any previous ipi_clear
|
|
* can't fall-through the read of eic770x_power_down
|
|
*/
|
|
sbi_ipi_raw_send(i, false);
|
|
to_cease[die] |= EIC770X_MC_CEASE_BIT(core);
|
|
}
|
|
|
|
for (u32 die = 0; die < array_size(to_cease); die++) {
|
|
/*
|
|
* MCPU status indicates the wfi/debug/halt/cease status
|
|
* of each individual harts in the same die. The value
|
|
* can change on the fly, but for ceased harts, the cease
|
|
* bit remains high until reset
|
|
*/
|
|
u32 *status = (u32*)EIC770X_MCPU_STATUS(die);
|
|
|
|
if (!to_cease[die])
|
|
continue;
|
|
|
|
/* Wait for mcput_cease_from_tile_x */
|
|
while ((readl(status) & to_cease[die]) != to_cease[die]);
|
|
}
|
|
}
|
|
|
|
static const struct sbi_hsm_device eswin_eic770x_hsm = {
|
|
.name = "eic770x_hsm",
|
|
.hart_start = eic770x_hart_start,
|
|
.hart_stop = eic770x_hart_stop,
|
|
};
|
|
|
|
static int eic7700_system_reset_check(u32 type, u32 reason)
|
|
{
|
|
switch (type) {
|
|
case SBI_SRST_RESET_TYPE_COLD_REBOOT:
|
|
case SBI_SRST_RESET_TYPE_WARM_REBOOT:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void eic7700_system_reset(u32 type, u32 reason)
|
|
{
|
|
switch (type) {
|
|
case SBI_SRST_RESET_TYPE_COLD_REBOOT:
|
|
case SBI_SRST_RESET_TYPE_WARM_REBOOT:
|
|
eic770x_cease_other_harts();
|
|
writel(EIC770X_SYSRST_VAL, (void *)EIC770X_SYSCRG_SYSRST);
|
|
}
|
|
|
|
sifive_cease();
|
|
}
|
|
|
|
static struct sbi_system_reset_device *board_reset = NULL;
|
|
static struct sbi_system_reset_device eic7700_reset = {
|
|
.name = "eic7700_reset",
|
|
.system_reset_check = eic7700_system_reset_check,
|
|
.system_reset = eic7700_system_reset,
|
|
};
|
|
|
|
#define add_root_mem_chk(...) do { \
|
|
rc = sbi_domain_root_add_memrange(__VA_ARGS__); \
|
|
if (rc) \
|
|
return rc; \
|
|
} while (0)
|
|
|
|
/**
|
|
* EIC7700 special arrangement of PMP entries:
|
|
*
|
|
* We have to use extra PMPs to block data cacheable regions that
|
|
* that doesn't belong to the current hart's die in order to prevent
|
|
* speculative accesses or HW prefetcher from generating bus error:
|
|
*
|
|
* bus error of cause event: 9, accrued: 0x220,
|
|
* physical address: 0x24ffffffa0
|
|
*
|
|
* The data cacheable regions (per datasheet) include:
|
|
*
|
|
* - [0x1a000000, 0x1a400000) -- Die 0 L3 zero device
|
|
* - [0x3a000000, 0x3a400000) -- Die 1 L3 zero device
|
|
* - [0x80000000, 0x80_00000000) -- memory port
|
|
*
|
|
* To make the blocker effective for M mode too, the extra PMPs need
|
|
* LOCK bit to be set, and once set, we can't change them later.
|
|
* We also have to to use 1 extra PMP to protect OpenSBI in uncached
|
|
* memory. EIC770X maps main memory (DRAM) twice -- one in memory
|
|
* port (cached), the other in system port (uncached). P550 doesn't
|
|
* support Svpbmt, so EIC770X use the uncached window to handle DMA
|
|
* that are cache incoherent -- pretty much all peripherals
|
|
*
|
|
* Final PMP configuration:
|
|
*
|
|
* From die 0 point of view, block
|
|
* - [0x3a000000, 0x3a400000) -- Die 1 L3 zero device
|
|
* - [0x10_00000000, 0x80_00000000) -- Die 1 cached mem + holes
|
|
*
|
|
* Root domain Harts:
|
|
* PMP[0]: [ 0x80000000, 0x80080000) ____ Firmware in cached mem
|
|
* PMP[1]: [0xc0_00000000, 0xc0_00080000) ____ Firmware in uncached
|
|
* PMP[2]: [ 0x3a000000, 0x3a400000) L___ Die 1 L3 zero device
|
|
* PMP[3]: [ 0x2000000 0x2010000) ____ CLINT
|
|
* PMP[4]: [ 0x0, 0x10_00000000) _RWX P550/System/Die 0 cached mem
|
|
* PMP[5]: <Free>
|
|
* PMP[6]: [ 0x0, 0x80_00000000) L___ P550/System/Memory Port
|
|
* PMP[7]: [ 0x0, 0xffffffffffffffff] _RWX Everything
|
|
*
|
|
* From die 1 point of view, block
|
|
* - [0x1a000000, 0x1a400000) -- Die 0 L3 zero device
|
|
* - [0x80000000, 0x20_00000000) -- Die 0 cached mem + holes
|
|
* - [0x30_00000000, 0x80_00000000) -- other holes in Memory port
|
|
*
|
|
* Root domain Harts:
|
|
* PMP[0]: [0x20_00000000, 0x20_00080000) ____ Firmware in cached mem
|
|
* PMP[1]: [0xe0_00000000, 0xe0_00080000) ____ Firmware in uncached
|
|
* PMP[2]: [ 0x1a000000, 0x1a400000) L___ Die 0 L3 zero dev
|
|
* PMP[3]: [ 0x22000000 0x22010000) ____ CLINT
|
|
* PMP[4]: [ 0x0, 0x80000000) _RWX Die 0/1 P550 internal
|
|
* PMP[5]: [0x20_00000000, 0x30_00000000) _RWX Die 1 cached memory
|
|
* PMP[6]: [ 0x0, 0x80_00000000) L___ P550/System/Memory Port
|
|
* PMP[7]: [ 0x0, 0xffffffffffffffff] _RWX Everything
|
|
*
|
|
* EIC770X memory port map:
|
|
* P550 Internal
|
|
* ├─ 0x0000_0000 - 0x2000_0000 die 0 internal
|
|
* └─ 0x2000_0000 - 0x4000_0000 die 1 internal
|
|
* System Port 0
|
|
* ├─ 0x4000_0000 - 0x6000_0000 die 0 low MMIO
|
|
* └─ 0x6000_0000 - 0x8000_0000 die 1 low MMIO
|
|
* Memory Port
|
|
* ├─ 0x8000_0000 - 0x10_8000_0000 die 0 memory (cached)
|
|
* ├─ 0x20_0000_0000 - 0x30_0000_0000 die 1 memory (cached)
|
|
* └─ 0x40_0000_0000 - 0x60_0000_0000 interleaved memory (cached)
|
|
* System Port 1
|
|
* ├─ 0x80_0000_0000 - 0xa0_0000_0000 die 0 high MMIO
|
|
* ├─ 0xa0_0000_0000 - 0xc0_0000_0000 die 1 high MMIO
|
|
* ├─ 0xc0_0000_0000 - 0xd0_0000_0000 die 0 memory (uncached)
|
|
* ├─ 0xe0_0000_0000 - 0xf0_0000_0000 die 1 memory (uncached)
|
|
* ├─ 0x100_0000_0000 - 0x120_0000_0000 interleaved memory (uncached)
|
|
* └─ ...
|
|
*
|
|
* In early_init, add memory regions such that lib/ code has the knowledge
|
|
* of blocked ranges. When the driver code inserts new regions, lib/ code
|
|
* can optimize away unnecessary ones. Next, in final_init, we program the
|
|
* PMPs to a default state that'll keep ourselves functional (CLINT/...
|
|
* accessible). Later, in pmp_configure, do the actual configuration of
|
|
* PMP, using domain memory regions and permissions.
|
|
*/
|
|
|
|
static int eswin_eic7700_early_init(bool cold_boot)
|
|
{
|
|
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
|
int rc;
|
|
|
|
if (!cold_boot)
|
|
return generic_early_init(cold_boot);
|
|
|
|
if (board_reset)
|
|
sbi_system_reset_add_device(board_reset);
|
|
sbi_system_reset_add_device(&eic7700_reset);
|
|
|
|
/* Enable bus blocker */
|
|
writel(1, (void*)EIC770X_TL64D2D_OUT);
|
|
writel(1, (void*)EIC770X_TL256D2D_OUT);
|
|
writel(1, (void*)EIC770X_TL256D2D_IN);
|
|
asm volatile ("fence o, rw");
|
|
|
|
/* Block firmware in uncached memory */
|
|
add_root_mem_chk(EIC770X_TO_UNCACHED(
|
|
scratch->fw_start),
|
|
1UL << log2roundup(scratch->fw_size),
|
|
1UL << log2roundup(scratch->fw_size),
|
|
(SBI_DOMAIN_MEMREGION_M_READABLE |
|
|
SBI_DOMAIN_MEMREGION_M_WRITABLE |
|
|
SBI_DOMAIN_MEMREGION_M_EXECUTABLE |
|
|
SBI_DOMAIN_MEMREGION_MMIO |
|
|
SBI_DOMAIN_MEMREGION_FW));
|
|
|
|
/* Allow SURW of P550 + System Port */
|
|
add_root_mem_chk(0,
|
|
EIC770X_MEMPORT_BASE,
|
|
EIC770X_MEMPORT_BASE,
|
|
(SBI_DOMAIN_MEMREGION_MMIO |
|
|
SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW));
|
|
|
|
if (current_hart_die()) {
|
|
/* Allow SURWX of die 1 cached memory */
|
|
add_root_mem_chk(EIC770X_D1_MEM_BASE,
|
|
EIC770X_D1_MEM_SIZE,
|
|
EIC770X_D1_MEM_SIZE,
|
|
(SBI_DOMAIN_MEMREGION_M_READABLE |
|
|
SBI_DOMAIN_MEMREGION_M_WRITABLE |
|
|
SBI_DOMAIN_MEMREGION_SU_RWX));
|
|
} else {
|
|
/* Allow SURWX of P550 + System Port + die 0 cached memory */
|
|
add_root_mem_chk(0,
|
|
EIC770X_D0_MEM_LIMIT,
|
|
EIC770X_D0_MEM_LIMIT,
|
|
(SBI_DOMAIN_MEMREGION_M_READABLE |
|
|
SBI_DOMAIN_MEMREGION_M_WRITABLE |
|
|
SBI_DOMAIN_MEMREGION_SU_RWX));
|
|
}
|
|
|
|
/* Block P550 + System Port 0 + Memory Port (enforced) */
|
|
add_root_mem_chk(0,
|
|
EIC770X_MEMPORT_LIMIT,
|
|
EIC770X_MEMPORT_LIMIT,
|
|
(SBI_DOMAIN_MEMREGION_MMIO |
|
|
SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS));
|
|
|
|
rc = sbi_hart_protection_register(&eswin_eic7700_pmp_protection);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return generic_early_init(cold_boot);
|
|
}
|
|
|
|
#define PMP_FW_START 0
|
|
#define PMP_FW_COUNT 2
|
|
#define PMP_RESERVED_A 2
|
|
#define PMP_FREE_A_START 3
|
|
#define PMP_FREE_A_COUNT 3
|
|
#define PMP_RESERVED_B 6
|
|
#define PMP_FREE_B_START 7
|
|
#define PMP_FREE_B_COUNT 1
|
|
|
|
static int eswin_eic7700_final_init(bool cold_boot)
|
|
{
|
|
/**
|
|
* For both dies after final_init:
|
|
*
|
|
* PMP[0]: Protect OpenSBI in cached memory
|
|
* PMP[1]: Protect OpenSBI in uncached memory
|
|
* PMP[2]: Block remote die P550 L3 Zero Device
|
|
* PMP[3-5]: <Free ranges A>
|
|
* PMP[5]: Temporary enable P550 + System Port
|
|
* PMP[6]: Block all P550 + System + Memory Port
|
|
* PMP[7-7]: <Free ranges B>
|
|
*/
|
|
struct sbi_domain_memregion *reg;
|
|
unsigned int pmp_idx = PMP_FW_START,
|
|
pmp_max = PMP_FW_START + PMP_FW_COUNT;
|
|
int rc;
|
|
|
|
|
|
if (cold_boot)
|
|
sbi_hsm_set_device(&eswin_eic770x_hsm);
|
|
|
|
/**
|
|
* Do generic_final_init stuff first, because it touchs FDT.
|
|
* After final_init, we'll block entire memory port with the
|
|
* LOCK bit set, which means we can't access memory outside
|
|
* of [fw_start, fw_start + fw_size). The FDT could very well
|
|
* reside outside of firmware region. Later, pmp_configure()
|
|
* may unblock it with some preceding entries for root domain
|
|
* harts. It may not unblock it, however, for non-root harts.
|
|
*/
|
|
rc = generic_final_init(cold_boot);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Process firmware regions */
|
|
sbi_domain_for_each_memregion(&root, reg) {
|
|
if (!SBI_DOMAIN_MEMREGION_IS_FIRMWARE(reg->flags))
|
|
continue;
|
|
|
|
if (pmp_idx >= pmp_max) {
|
|
sbi_printf("%s: insufficient FW PMP entries\n",
|
|
__func__);
|
|
return SBI_EFAIL;
|
|
}
|
|
sbi_hart_pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
|
|
reg->base, reg->order);
|
|
}
|
|
|
|
sbi_hart_pmp_set(PMP_RESERVED_A, PMP_L, EIC770X_L3_ZERO_REMOTE,
|
|
log2roundup(EIC770X_L3_ZERO_SIZE));
|
|
/**
|
|
* Enable P550 internal + System Port, so OpenSBI can access
|
|
* CLINT/PLIC/UART. Might be overwritten in pmp_configure.
|
|
*/
|
|
sbi_hart_pmp_set(PMP_FREE_A_START + PMP_FREE_A_COUNT - 1, 0, 0,
|
|
log2roundup(EIC770X_MEMPORT_BASE));
|
|
|
|
sbi_hart_pmp_set(PMP_RESERVED_B, PMP_L, 0,
|
|
log2roundup(EIC770X_MEMPORT_LIMIT));
|
|
/**
|
|
* These must come after the setup of PMP, as we are about to
|
|
* enable speculation and HW prefetcher bits
|
|
*/
|
|
csr_write(EIC770X_CSR_FEAT0, CONFIG_ESWIN_EIC770X_FEAT0_CFG);
|
|
csr_write(EIC770X_CSR_FEAT1, CONFIG_ESWIN_EIC770X_FEAT1_CFG);
|
|
csr_write(EIC770X_CSR_L1_HWPF, CONFIG_ESWIN_EIC770X_L1_HWPF_CFG);
|
|
csr_write(EIC770X_CSR_L2_HWPF, CONFIG_ESWIN_EIC770X_L2_HWPF_CFG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int eswin_eic7700_pmp_configure(struct sbi_scratch *scratch)
|
|
{
|
|
struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
|
struct sbi_domain_memregion *reg, *prev = NULL;
|
|
unsigned int pmp_idx, pmp_max;
|
|
unsigned int i, j;
|
|
|
|
/* Process the first free range A [3-5] */
|
|
pmp_idx = PMP_FREE_A_START,
|
|
pmp_max = PMP_FREE_A_START + PMP_FREE_A_COUNT;
|
|
|
|
sbi_domain_for_each_memregion_idx(dom, reg, i) {
|
|
if (SBI_DOMAIN_MEMREGION_IS_FIRMWARE(reg->flags))
|
|
continue;
|
|
|
|
/**
|
|
* This must be the one blocking P550 + System Port +
|
|
* Memory Port we setup in early_init, or a superset
|
|
* of it. If seen, break, and program the rest in
|
|
* free range B.
|
|
*/
|
|
if (reg->base == 0 &&
|
|
reg->order >= log2roundup(EIC770X_MEMPORT_LIMIT))
|
|
break;
|
|
|
|
/**
|
|
* Relaxation:
|
|
* Treat a previous region with SURW as SURWX if the
|
|
* current has SURWX, and current region with MMIO
|
|
* if previous has MMIO, and see if it can be merged.
|
|
* This saves 1 PMP entry on die 0/
|
|
*/
|
|
if (prev && sbi_domain_memregion_is_subset(prev, reg) &&
|
|
(reg->flags | SBI_DOMAIN_MEMREGION_MMIO) ==
|
|
(prev->flags | SBI_DOMAIN_MEMREGION_SU_EXECUTABLE))
|
|
pmp_idx--;
|
|
|
|
if (pmp_idx >= pmp_max)
|
|
goto no_more_pmp;
|
|
|
|
sbi_hart_pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
|
|
reg->base, reg->order);
|
|
prev = reg;
|
|
}
|
|
/* Disable the rest */
|
|
while (pmp_idx < pmp_max)
|
|
sbi_hart_pmp_disable(pmp_idx++);
|
|
|
|
/* Process the second free range B [7-7] */
|
|
pmp_idx = PMP_FREE_B_START,
|
|
pmp_max = PMP_FREE_B_START + PMP_FREE_B_COUNT;
|
|
|
|
sbi_domain_for_each_memregion_idx(dom, reg, j) {
|
|
if (i >= j)
|
|
continue;
|
|
|
|
if (pmp_idx >= pmp_max)
|
|
goto no_more_pmp;
|
|
|
|
sbi_hart_pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
|
|
reg->base, reg->order);
|
|
}
|
|
/* Disable the rest */
|
|
while (pmp_idx < pmp_max)
|
|
sbi_hart_pmp_disable(pmp_idx++);
|
|
|
|
sbi_hart_pmp_fence();
|
|
return 0;
|
|
no_more_pmp:
|
|
sbi_printf("%s: insufficient PMP entries\n", __func__);
|
|
return SBI_EFAIL;
|
|
}
|
|
|
|
static void eswin_eic7700_pmp_unconfigure(struct sbi_scratch *scratch)
|
|
{
|
|
/* Enable P550 internal + System Port */
|
|
sbi_hart_pmp_set(PMP_FREE_A_START + PMP_FREE_A_COUNT - 1, 0, 0,
|
|
log2roundup(EIC770X_MEMPORT_BASE));
|
|
|
|
for (unsigned int i = 0; i < PMP_FREE_A_COUNT - 1; i++)
|
|
sbi_hart_pmp_disable(i + PMP_FREE_A_START);
|
|
|
|
for (unsigned int i = 0; i < PMP_FREE_B_COUNT; i++)
|
|
sbi_hart_pmp_disable(i + PMP_FREE_B_START);
|
|
}
|
|
|
|
static struct sbi_hart_protection eswin_eic7700_pmp_protection = {
|
|
.name = "eic7700_pmp",
|
|
.rating = -1UL,
|
|
.configure = eswin_eic7700_pmp_configure,
|
|
.unconfigure = eswin_eic7700_pmp_unconfigure,
|
|
};
|
|
|
|
static bool eswin_eic7700_single_fw_region(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static int eswin_eic7700_platform_init(const void *fdt, int nodeoff,
|
|
const struct fdt_match *match)
|
|
{
|
|
const struct eic770x_board_override *board_override = match->data;
|
|
|
|
generic_platform_ops.early_init = eswin_eic7700_early_init;
|
|
generic_platform_ops.final_init = eswin_eic7700_final_init;
|
|
generic_platform_ops.single_fw_region = eswin_eic7700_single_fw_region;
|
|
|
|
if (board_override)
|
|
board_reset = board_override->reset_dev;
|
|
return 0;
|
|
}
|
|
|
|
static const struct fdt_match eswin_eic7700_match[] = {
|
|
{ .compatible = "sifive,hifive-premier-p550", .data = &hfp_override },
|
|
{ .compatible = "eswin,eic7700" },
|
|
{ },
|
|
};
|
|
|
|
const struct fdt_driver eswin_eic7700 = {
|
|
.match_table = eswin_eic7700_match,
|
|
.init = eswin_eic7700_platform_init,
|
|
};
|