[PATCH v3 4/4] platform: generic: eswin: add EIC7700
Yu-Chien Peter Lin
peter.lin at sifive.com
Sun Nov 30 23:35:03 PST 2025
Hi Bo,
On 11/20/25 5:34 PM, Bo Gan wrote:
> Initial platform support for ESWIN Computing EIC7700 based on public SoC
> datasheet[1] and tested on HiFive Premier P550. Vendor U-boot/Linux boots
> fine, and I've tested Geekbench 6.5.0 Preview and got scores on par with
> the vendor OpenSBI. System shutdown is not implemented due to the lack of
> public doc regarding the UART communication protocol between the SoC and
> the onboard BMC, which controls ATX power on HiFive P550 [2].
>
> The files and functions are intentionally named as eic770x in many places
> for future enhancements to support the 2 die version of the same SoC,
> namely EIC7702, seen on DC-ROMA AI PC FML13V03 [3]. This patch set only
> deals with the single die version, and the assumption is we can be either
> die with id=0 or id=1, but there's only a single die in the system, or we
> are only using a single die out of 2. However, the way the SoC handles 2-
> die greatly affects how we configure it in a 1-die setup. EIC770X address
> map has die 0/1 memory regions interleaved (see comments in eic770x.c).
> If only 1 die is connected or active, it creates holes in the address map
> for those regions corresponding to the remote die. When speculative-
> execution or HW prefetcher touches data-cacheable regions that happen to
> fall into those holes, it can trigger bus error. Specifically:
>
> - Remote (non-existent) die L3 zero device
> - Remote (non-existent) die cached memory region
> - Other holes in Memory Port
>
> To make matters worse, EIC770X doesn't have cache coherent DMA, and due
> to the fact that the P550 core lacks Svpbmt, the SoC maps main memory
> twice as different regions, so it can bypass cache and fetch the data
> directly from memory. In address space, we have two memory regions, one
> as cached, the other as uncached. Thus, we also need an extra PMP entry
> to protect OpenSBI blob from the uncached window. To do this, platform
> code requires single_fw_region, otherwise, we'll run out of PMP entries.
>
> EIC770X also have several feature disable/enable CSRs accessible in M
> mode. By default many core features such as speculation and HW prefetch
> are disabled, and M mode software is responsible of enabling. Hence,
> introduce 4 new build time tunable parameters to Kconfig, which reflects
> the values get updated to those CSRs:
> - ESWIN_EIC770X_FEAT0_CFG
> - ESWIN_EIC770X_FEAT1_CFG
> - ESWIN_EIC770X_L1_HWPF_CFG
> - ESWIN_EIC770X_L2_HWPF_CFG
>
> The default values are somewhat optimal for generic workloads. They are
> dumped when running SiFive's vendor OpenSBI, and in addition, with my
> own tuning to address the perf regression reported by drmpeg [4]
>
> To build the firmware+u-boot blob, Use the following, and docs [5] for
> testing it with UART boot without flashing:
>
> make FW_TEXT_START=0x80000000 \
> FW_PAYLOAD_OFFSET=0x200000 \
> FW_PAYLOAD_PATH=u-boot-nodtb.bin \
> FW_PAYLOAD_FDT_ADDR=0xf8000000 \
> FW_FDT_PATH=u-boot.dtb
>
> [1] https://github.com/eswincomputing/EIC7700X-SoC-Technical-Reference-Manual
> [2] https://www.sifive.com/boards/hifive-premier-p550#documentation
> [3] https://github.com/geerlingguy/sbc-reviews/issues/82
> [4] https://forums.sifive.com/t/low-1-core-stream-bandwidth/7274/15
> [5] https://github.com/ganboing/EIC770x-Docs/blob/main/p550/bootchain/UART-Boot.md
>
> Signed-off-by: Bo Gan <ganboing at gmail.com>
> ---
> platform/generic/Kconfig | 5 +
> platform/generic/configs/defconfig | 1 +
> platform/generic/eswin/Kconfig | 29 ++
> platform/generic/eswin/eic770x.c | 385 +++++++++++++++++++++++
> platform/generic/eswin/objects.mk | 11 +
> platform/generic/include/eswin/eic770x.h | 73 +++++
> 6 files changed, 504 insertions(+)
> create mode 100644 platform/generic/eswin/Kconfig
> create mode 100644 platform/generic/eswin/eic770x.c
> create mode 100644 platform/generic/eswin/objects.mk
> create mode 100644 platform/generic/include/eswin/eic770x.h
>
> diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
> index aedc59a5..047e13ca 100644
> --- a/platform/generic/Kconfig
> +++ b/platform/generic/Kconfig
> @@ -88,7 +88,12 @@ config PLATFORM_SPACEMIT_K1
> select FDT_HSM_SPACEMIT
> default n
>
> +config PLATFORM_ESWIN_EIC770X
> + bool "ESWIN EIC770X support"
> + default n
> +
> source "$(OPENSBI_SRC_DIR)/platform/generic/andes/Kconfig"
> +source "$(OPENSBI_SRC_DIR)/platform/generic/eswin/Kconfig"
> source "$(OPENSBI_SRC_DIR)/platform/generic/thead/Kconfig"
>
> endif
> diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
> index 9e040ac3..60c5449e 100644
> --- a/platform/generic/configs/defconfig
> +++ b/platform/generic/configs/defconfig
> @@ -10,6 +10,7 @@ CONFIG_PLATFORM_STARFIVE_JH7110=y
> CONFIG_PLATFORM_THEAD=y
> CONFIG_PLATFORM_MIPS_P8700=y
> CONFIG_PLATFORM_SPACEMIT_K1=y
> +CONFIG_PLATFORM_ESWIN_EIC770X=y
> CONFIG_FDT_CACHE=y
> CONFIG_FDT_CACHE_SIFIVE_CCACHE=y
> CONFIG_FDT_CPPC=y
> diff --git a/platform/generic/eswin/Kconfig b/platform/generic/eswin/Kconfig
> new file mode 100644
> index 00000000..84d0f43a
> --- /dev/null
> +++ b/platform/generic/eswin/Kconfig
> @@ -0,0 +1,29 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +config ESWIN_EIC770X_FEAT0_CFG
> + int "ESWIN EIC7700X Feature Disable 0 CSR Configuration"
> + default 0x4000
> + help
> + CSR Value to initialize EIC770X_FEAT0 (0x7c1) with.
> + Refer to EIC770X SoC TRM for recommendations.
> +
> +config ESWIN_EIC770X_FEAT1_CFG
> + int "ESWIN EIC7700X Feature Disable 1 CSR Configuration"
> + default 0x80
> + help
> + CSR Value to initialize EIC770X_FEAT1 (0x7c2) with.
> + Refer to EIC770X SoC TRM for recommendations.
> +
> +config ESWIN_EIC770X_L1_HWPF_CFG
> + int "ESWIN EIC7700X L1 HW Prefetcher CSR Configuration"
> + default 0x1005c1be649
> + help
> + CSR Value to initialize EIC770X_L1_HWPF (0x7c3) with.
> + Refer to EIC770X SoC TRM for recommendations.
> +
> +config ESWIN_EIC770X_L2_HWPF_CFG
> + int "ESWIN EIC7700X L2 HW Prefetcher CSR Configuration"
> + default 0x929f
> + help
> + CSR Value to initialize EIC770X_L2_HWPF (0x7c4) with.
> + Refer to EIC770X SoC TRM for recommendations.
> diff --git a/platform/generic/eswin/eic770x.c b/platform/generic/eswin/eic770x.c
> new file mode 100644
> index 00000000..5cf14a05
> --- /dev/null
> +++ b/platform/generic/eswin/eic770x.c
> @@ -0,0 +1,385 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2025 Bo Gan <ganboing at 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 <eswin/eic770x.h>
> +
> +static int eic770x_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 eic770x_system_reset(u32 type, u32 reason)
> +{
> + switch (type) {
> + case SBI_SRST_RESET_TYPE_COLD_REBOOT:
> + case SBI_SRST_RESET_TYPE_WARM_REBOOT:
> + writel(EIC770X_SYSCRG_RST_VAL, (void *)EIC770X_SYSCRG_RST);
> + }
> +
> + sbi_printf("%s: Unable to reset system\n", __func__);
> + sbi_hart_hang();
> +}
> +
> +static struct sbi_system_reset_device eic770x_reset = {
> + .name = "eic770x_reset",
> + .system_reset_check = eic770x_system_reset_check,
> + .system_reset = eic770x_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
>
> I noticed that some PMP entries such as PMP[2] and PMP[6] are configured
> with pmpcfg.L set. During domain context switch, all PMP entries managed by
> OpenSBI domain will be disabled [1], so locked entries will remain active,
> and if a hole entry not using identical PMP id across domains, other regions
> that program the locked PMP id cannot be applied. This will block the use
> of e.g. OP-TEE which relies on domain context switch mechanism.
>
> I am proposing reserved PMP allocator [2]. Hopefully, it can address this requirement.
> By specifying reserved 3 entries on P550 platform, the PMP usage should become:
>
> PMP[0]: Reserved NAPOT [ 0x3a000000, 0x3a400000) L___ Die 1 L3 zero device
> PMP[1]: Reserved TOR1 [0x10_00000000, L___ Die 1 cached mem + holes
> PMP[2]: Reserved TOR2 0x80_00000000) L___ Die 1 cached mem + holes
> PMP[3]: Domain0Region0 [ 0x80000000, 0x80080000) ____ Firmware in cached mem
> PMP[4]: Domain0Region1 [0xc0_00000000, 0xc0_00080000) ____ Firmware in uncached
> PMP[5]: Domain0Region2 [ 0x2000000 0x2010000) ____ CLINT
> PMP[6]: Domain0Region3 [ 0x0, 0xffffffffffffffff] _RWX Everything
> PMP[7]: unused
>
> One aspect that requires careful attention is CPU suspend with power gating scenarios.
> When reserved PMP states can be removed, additional reserved PMP management
> (save/restore operations for PMPs that protect holes) must be included in the
> suspend/resume flow.
>
> [1] https://github.com/riscv-software-src/opensbi/blob/825d0e918a9e41cc57097a8cb913f26550699911/lib/sbi/sbi_domain_context.c#L123-L131
> [2] https://lore.kernel.org/all/20251130111643.1291462-7-peter.lin@sifive.com/
>
> + *
> + * 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);
> +
> + /* 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_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));
> +
> + 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_system_reset_add_device(&eic770x_reset);
> +
> + /**
> + * 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;
> + }
> + pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
> + reg->base, reg->order);
> + }
> +
> + 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.
> + */
> + pmp_set(PMP_FREE_A_START + PMP_FREE_A_COUNT - 1, 0, 0,
> + log2roundup(EIC770X_MEMPORT_BASE));
> +
> + 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(unsigned int pmp_count,
> + unsigned int pmp_log2gran,
> + unsigned long pmp_addr_max)
> +{
> + 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;
> +
> + pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
> + reg->base, reg->order);
> + prev = reg;
> + }
> + /* Disable the rest */
> + while (pmp_idx < pmp_max)
> + 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;
> +
> + pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
> + reg->base, reg->order);
> + }
> + /* Disable the rest */
> + while (pmp_idx < pmp_max)
> + pmp_disable(pmp_idx++);
> +
> + return 0;
> +no_more_pmp:
> + sbi_printf("%s: insufficient PMP entries\n", __func__);
> + return SBI_EFAIL;
> +}
> +
> +static void eswin_eic7700_pmp_unconfigure(void)
> +{
> + /* Enable P550 internal + System Port */
> + 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++)
> + pmp_disable(i + PMP_FREE_A_START);
> +
> + for (unsigned int i = 0; i < PMP_FREE_B_COUNT; i++)
> + pmp_disable(i + PMP_FREE_B_START);
> +}
> +
> +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)
> +{
> + generic_platform_ops.early_init = eswin_eic7700_early_init;
> + generic_platform_ops.final_init = eswin_eic7700_final_init;
> + generic_platform_ops.pmp_configure = eswin_eic7700_pmp_configure;
> + generic_platform_ops.pmp_unconfigure = eswin_eic7700_pmp_unconfigure;
> + generic_platform_ops.single_fw_region = eswin_eic7700_single_fw_region;
> +
> + return 0;
> +}
> +
> +static const struct fdt_match eswin_eic7700_match[] = {
> + { .compatible = "eswin,eic7700" },
> + { },
> +};
> +
> +const struct fdt_driver eswin_eic7700 = {
> + .match_table = eswin_eic7700_match,
> + .init = eswin_eic7700_platform_init,
> +};
> diff --git a/platform/generic/eswin/objects.mk b/platform/generic/eswin/objects.mk
> new file mode 100644
> index 00000000..6942a107
> --- /dev/null
> +++ b/platform/generic/eswin/objects.mk
> @@ -0,0 +1,11 @@
> +#
> +# SPDX-License-Identifier: BSD-2-Clause
> +#
> +# Copyright (C) 2025 Bo Gan <ganboing at gmail.com>
> +#
> +
> +carray-platform_override_modules-$(CONFIG_PLATFORM_ESWIN_EIC770X) += eswin_eic7700
> +platform-objs-$(CONFIG_PLATFORM_ESWIN_EIC770X) += eswin/eic770x.o
> +
> +FW_PAYLOAD=y
> +FW_PAYLOAD_OFFSET=0x200000
> diff --git a/platform/generic/include/eswin/eic770x.h b/platform/generic/include/eswin/eic770x.h
> new file mode 100644
> index 00000000..8ce23283
> --- /dev/null
> +++ b/platform/generic/include/eswin/eic770x.h
> @@ -0,0 +1,73 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2025 Bo Gan <ganboing at gmail.com>
> + *
> + */
> +
> +#ifndef __EIC770X_H__
> +#define __EIC770X_H__
> +
> +/* CSRs */
> +#define EIC770X_CSR_BRPREDICT 0x7c0
> +#define EIC770X_CSR_FEAT0 0x7c1
> +#define EIC770X_CSR_FEAT1 0x7c2
> +#define EIC770X_CSR_L1_HWPF 0x7c3
> +#define EIC770X_CSR_L2_HWPF 0x7c4
> +
> +/* Hart ID to core/die conversion */
> +#define CPU_CORE_BITS 2
> +#define CPU_CORE_MASK ((1 << CPU_CORE_BITS) - 1)
> +#define CPU_DIE_SHIFT CPU_CORE_BITS
> +#define CPU_DIE_BITS 1
> +#define CPU_DIE_MASK ((1 << CPU_DIE_SHIFT) - 1)
> +
> +#define hart_core(i) ((i) & CPU_CORE_MASK)
> +#define hart_die(i) (((i) >> CPU_DIE_SHIFT) & CPU_DIE_MASK)
> +#define current_hart_core() hart_core(current_hartid())
> +#define current_hart_die() hart_die(current_hartid())
> +
> +/* P550 Internal and System Port 0 */
> +#define EIC770X_P550INT_SIZE 0x20000000UL
> +#define EIC770X_P550INT_BASE(d) (0UL + EIC770X_P550INT_SIZE * (d))
> +#define EIC770X_P550INT_LOCAL EIC770X_P550INT_BASE(current_hart_die())
> +#define EIC770X_TL64D2D_OUT (EIC770X_P550INT_LOCAL + 0x200000)
> +#define EIC770X_TL256D2D_OUT (EIC770X_P550INT_LOCAL + 0x202000)
> +#define EIC770X_TL256D2D_IN (EIC770X_P550INT_LOCAL + 0x204000)
> +#define EIC770X_L3_ZERO_SIZE 0x400000UL
> +#define EIC770X_L3_ZERO_BASE(d) (EIC770X_P550INT_BASE(d) + 0x1a000000)
> +#define EIC770X_L3_ZERO_LOCAL EIC770X_L3_ZERO_BASE(current_hart_die())
> +#define EIC770X_L3_ZERO_REMOTE EIC770X_L3_ZERO_BASE(1 - current_hart_die())
> +
> +#define EIC770X_SYSPORT_SIZE 0x20000000UL
> +#define EIC770X_SYSPORT_BASE(d) (0x40000000UL + EIC770X_SYSPORT_SIZE * (d))
> +#define EIC770X_SYSPORT_LOCAL EIC770X_SYSPORT_BASE(current_hart_die())
> +#define EIC770X_SYSCRG (EIC770X_SYSPORT_LOCAL + 0x11828000UL)
> +#define EIC770X_SYSCRG_RST (EIC770X_SYSCRG + 0x300UL)
> +#define EIC770X_SYSCRG_RST_VAL 0x1AC0FFE6UL
> +
> +/* Memory Ports */
> +#define EIC770X_MEMPORT_BASE 0x0080000000UL // 2G
> +#define EIC770X_MEMPORT_SIZE 0x7f80000000UL // +510G
> +#define EIC770X_MEMPORT_LIMIT (EIC770X_MEMPORT_BASE + EIC770X_MEMPORT_SIZE)
> +#define EIC770X_D0_MEM_BASE 0x0080000000UL // 2G
> +#define EIC770X_D0_MEM_SIZE 0x0f80000000UL // +62G
> +#define EIC770X_D0_MEM_LIMIT (EIC770X_D0_MEM_BASE + EIC770X_D0_MEM_SIZE)
> +#define EIC770X_D1_MEM_BASE 0x2000000000UL // 128G
>
> These definitions break RV32 builds; please ensure the generic platform compiles
> cleanly on both RV32 and RV64 toolchains.
>
> Best regards,
> Peter Lin
>
> +#define EIC770X_D1_MEM_SIZE 0x1000000000UL // +64G
> +#define EIC770X_D1_MEM_LIMIT (EIC770X_D1_MEM_BASE + EIC770X_D1_MEM_SIZE)
> +#define EIC770X_CACHED_BASE (current_hart_die() ? \
> + EIC770X_D1_MEM_BASE : \
> + EIC770X_D0_MEM_BASE)
> +
> +/* Uncached memory mapped in System Port 1 */
> +#define EIC770X_D0_UC_BASE 0xc000000000UL
> +#define EIC770X_D1_UC_BASE 0xe000000000UL
> +#define EIC770X_UNCACHED_BASE (current_hart_die() ? \
> + EIC770X_D1_UC_BASE : \
> + EIC770X_D0_UC_BASE)
> +
> +#define EIC770X_TO_UNCACHED(x) ((x) - EIC770X_CACHED_BASE + \
> + EIC770X_UNCACHED_BASE)
> +
> +#endif
More information about the opensbi
mailing list