Files
opensbi/platform/generic/include/eswin/eic770x.h
T
Bo Gan 7bff4e529e platform: generic: eswin: Add eic770x_hsm and fix warm reset issues
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>
2026-06-15 10:38:09 +05:30

112 lines
3.9 KiB
C

/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Bo Gan <ganboing@gmail.com>
*
*/
#ifndef __EIC770X_H__
#define __EIC770X_H__
#include <sbi/riscv_asm.h>
struct eic770x_board_override {
struct sbi_system_reset_device *reset_dev;
};
void eic770x_cease_other_harts(void);
/* 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_UART0 (EIC770X_SYSPORT_LOCAL + 0x10900000UL)
#define EIC770X_UART_SIZE 0x10000UL
#define EIC770X_UART(x) (EIC770X_UART0 + EIC770X_UART_SIZE * (x))
#define EIC770X_UART_REG_SHIFT 2
#define EIC770X_UART_REG_WIDTH 4
#define EIC770X_SYSCON(d) (EIC770X_SYSPORT_BASE(d) + 0x11810000UL)
#define EIC770X_MCPU_STATUS(d) (EIC770X_SYSCON(d) + 0x608UL)
#define EIC770X_MC_CEASE_BIT(c) (1UL << (15 - c))
#define EIC770X_SYSCRG(d) (EIC770X_SYSPORT_BASE(d) + 0x11828000UL)
#define EIC770X_SYSCRG_LOCAL (EIC770X_SYSPORT_LOCAL + 0x11828000UL)
#define EIC770X_SYSCRG_LSPCLK0 (EIC770X_SYSCRG_LOCAL + 0x200UL)
#define EIC770X_SYSCRG_MCCLK(d) (EIC770X_SYSCRG(d) + 0x208UL)
#define EIC770X_SYSCRG_SYSCLK (EIC770X_SYSCRG_LOCAL + 0x20cUL)
#define EIC770X_SYSCRG_SYSRST (EIC770X_SYSCRG_LOCAL + 0x300UL)
/* 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)
/* Clock definitions */
#define EIC770X_XTAL_CLK_RATE 24000000UL
#define EIC770X_SPLL0_OUT1_RATE 1600000000UL
#define EIC770X_SPLL0_OUT2_RATE 800000000UL
#define EIC770X_SPLL0_OUT3_RATE 400000000UL
#define EIC770X_UART_CLK_BIT(x) (1UL << ((x) + 17))
#define EIC770X_SYSCLK_SEL(x) ((x) & 1)
#define EIC770X_SYSCLK_DIV(x) \
({ \
uint32_t divisor = ((x) >> 4) & 7; \
divisor > 2 ? divisor : 2; \
})
/* Reset definitions */
#define EIC770X_SYSRST_VAL 0x1AC0FFE6UL
#endif