[PATCH v6 5/7] platform: generic: eswin: add EIC7700

Anup Patel anup at brainfault.org
Sun Dec 21 06:49:25 PST 2025


On Thu, Dec 18, 2025 at 4:14 PM Bo Gan <ganboing at gmail.com> 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/reboot for HiFive Premier P550 and
> other boards will be implemented in subsequent commits. At this point,
> only SoC-level warm reset is implemented.
>
> 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 [2]. 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 [3]
>
> To build the firmware+u-boot blob, Use the following, and docs [4] 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://github.com/geerlingguy/sbc-reviews/issues/82
> [3] https://forums.sifive.com/t/low-1-core-stream-bandwidth/7274/15
> [4] https://github.com/ganboing/EIC770x-Docs/blob/main/p550/bootchain/UART-Boot.md
>
> Signed-off-by: Bo Gan <ganboing at gmail.com>

LGTM.

Reviewed-by: Anup Patel <anup at brainfault.org>

Regards,
Anup

> ---
>  platform/generic/Kconfig                 |   5 +
>  platform/generic/configs/defconfig       |   1 +
>  platform/generic/eswin/Kconfig           |  29 ++
>  platform/generic/eswin/eic770x.c         | 403 +++++++++++++++++++++++
>  platform/generic/eswin/objects.mk        |  10 +
>  platform/generic/include/eswin/eic770x.h |  79 +++++
>  6 files changed, 527 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 b1808012..716fab42 100644
> --- a/platform/generic/Kconfig
> +++ b/platform/generic/Kconfig
> @@ -48,6 +48,10 @@ config PLATFORM_ANDES_QILAI
>         select ANDES_SBI
>         default n
>
> +config PLATFORM_ESWIN_EIC770X
> +       bool "ESWIN EIC770X support"
> +       default n
> +
>  config PLATFORM_OPENHWGROUP_OPENPITON
>         bool "OpenHWGroup Openpiton support"
>         default n
> @@ -98,6 +102,7 @@ config PLATFORM_SPACEMIT_K1
>         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 d0e2ec26..aab1560f 100644
> --- a/platform/generic/configs/defconfig
> +++ b/platform/generic/configs/defconfig
> @@ -1,6 +1,7 @@
>  CONFIG_PLATFORM_ALLWINNER_D1=y
>  CONFIG_PLATFORM_ANDES_AE350=y
>  CONFIG_PLATFORM_ANDES_QILAI=y
> +CONFIG_PLATFORM_ESWIN_EIC770X=y
>  CONFIG_PLATFORM_OPENHWGROUP_ARIANE=y
>  CONFIG_PLATFORM_OPENHWGROUP_OPENPITON=y
>  CONFIG_PLATFORM_RENESAS_RZFIVE=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..bce53a19
> --- /dev/null
> +++ b/platform/generic/eswin/eic770x.c
> @@ -0,0 +1,403 @@
> +/*
> + * 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 <sbi/sbi_hart_pmp.h>
> +#include <sbi/sbi_hart_protection.h>
> +#include <eswin/eic770x.h>
> +
> +static struct sbi_hart_protection eswin_eic7700_pmp_protection;
> +
> +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:
> +               sbi_printf("%s: resetting...\n", __func__);
> +               writel(EIC770X_SYSCRG_RST_VAL, (void *)EIC770X_SYSCRG_RST);
> +       }
> +
> +       sbi_hart_hang();
> +}
> +
> +static struct sbi_system_reset_device *board_reset = NULL;
> +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
> + *
> + * 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(&eic770x_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;
> +
> +
> +       /**
> +        * 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(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;
> +
> +               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++);
> +
> +       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 */
> +       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 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 = "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..be5420ce
> --- /dev/null
> +++ b/platform/generic/eswin/objects.mk
> @@ -0,0 +1,10 @@
> +#
> +# SPDX-License-Identifier: BSD-2-Clause
> +#
> +# Copyright (C) 2025 Bo Gan <ganboing at gmail.com>
> +#
> +
> +ifeq ($(PLATFORM_RISCV_XLEN), 64)
> +carray-platform_override_modules-$(CONFIG_PLATFORM_ESWIN_EIC770X) += eswin_eic7700
> +platform-objs-$(CONFIG_PLATFORM_ESWIN_EIC770X) += eswin/eic770x.o
> +endif
> diff --git a/platform/generic/include/eswin/eic770x.h b/platform/generic/include/eswin/eic770x.h
> new file mode 100644
> index 00000000..b1994669
> --- /dev/null
> +++ b/platform/generic/include/eswin/eic770x.h
> @@ -0,0 +1,79 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2025 Bo Gan <ganboing at gmail.com>
> + *
> + */
> +
> +#ifndef __EIC770X_H__
> +#define __EIC770X_H__
> +
> +#include <sbi/riscv_asm.h>
> +
> +struct eic770x_board_override {
> +       struct sbi_system_reset_device *reset_dev;
> +};
> +
> +/* 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_BITS) - 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
> +#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
> --
> 2.34.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi



More information about the opensbi mailing list