[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