/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2025 SpacemiT * Authors: * Xianbin Zhu * Troy Mitchell */ #include #include #include #include static const u64 cpu_wakeup_reg[] = { PMU_AP_CORE0_WAKEUP, PMU_AP_CORE1_WAKEUP, PMU_AP_CORE2_WAKEUP, PMU_AP_CORE3_WAKEUP, PMU_AP_CORE4_WAKEUP, PMU_AP_CORE5_WAKEUP, PMU_AP_CORE6_WAKEUP, PMU_AP_CORE7_WAKEUP, }; static const u64 cpu_idle_reg[] = { PMU_AP_CORE0_IDLE_CFG, PMU_AP_CORE1_IDLE_CFG, PMU_AP_CORE2_IDLE_CFG, PMU_AP_CORE3_IDLE_CFG, PMU_AP_CORE4_IDLE_CFG, PMU_AP_CORE5_IDLE_CFG, PMU_AP_CORE6_IDLE_CFG, PMU_AP_CORE7_IDLE_CFG, }; static inline void spacemit_set_cpu_power(u32 hartid, bool enable) { unsigned int value; unsigned int *cpu_idle_base = (unsigned int *)(unsigned long)cpu_idle_reg[hartid]; value = readl(cpu_idle_base); if (enable) value &= ~PMU_AP_IDLE_PWRDOWN_MASK; else value |= PMU_AP_IDLE_PWRDOWN_MASK; writel(value, cpu_idle_base); } static void spacemit_wakeup_cpu(u32 mpidr) { unsigned int *cpu_reset_base; unsigned int cur_hartid = current_hartid(); cpu_reset_base = (unsigned int *)(unsigned long)cpu_wakeup_reg[cur_hartid]; writel(1 << mpidr, cpu_reset_base); } static void spacemit_assert_cpu(void) { spacemit_set_cpu_power(current_hartid(), false); } static void spacemit_deassert_cpu(unsigned int hartid) { spacemit_set_cpu_power(hartid, true); } /* Start (or power-up) the given hart */ static int spacemit_hart_start(unsigned int hartid, unsigned long saddr) { spacemit_deassert_cpu(hartid); spacemit_wakeup_cpu(hartid); return 0; } /* * Stop (or power-down) the current hart from running. This call * doesn't expect to return if success. */ static int spacemit_hart_stop(void) { csr_write(CSR_STIMECMP, GENMASK_ULL(63, 0)); csr_clear(CSR_MIE, MIP_SSIP | MIP_MSIP | MIP_STIP | MIP_MTIP | MIP_SEIP | MIP_MEIP); /* disable data preftch */ csr_clear(CSR_MSETUP, MSETUP_PFE); asm volatile ("fence iorw, iorw"); /* flush local dcache */ csr_write(CSR_MRAOP, MRAOP_ICACHE_INVALID); asm volatile ("fence iorw, iorw"); /* disable dcache */ csr_clear(CSR_MSETUP, MSETUP_DE); asm volatile ("fence iorw, iorw"); /* * Core4-7 do not have dedicated bits in ML2SETUP; * instead, they reuse the same bits as core0-3. * * Thereforspacemit_deassert_cpue, use modulo with PLATFORM_MAX_CPUS_PER_CLUSTER * to select the proper bit. */ csr_clear(CSR_ML2SETUP, 1 << (current_hartid() % PLATFORM_MAX_CPUS_PER_CLUSTER)); asm volatile ("fence iorw, iorw"); spacemit_assert_cpu(); wfi(); return SBI_ENOTSUPP; } static const struct sbi_hsm_device spacemit_hsm_ops = { .name = "spacemit-hsm", .hart_start = spacemit_hart_start, .hart_stop = spacemit_hart_stop, }; static int spacemit_hsm_probe(const void *fdt, int nodeoff, const struct fdt_match *match) { sbi_hsm_set_device(&spacemit_hsm_ops); return 0; } static const struct fdt_match spacemit_hsm_match[] = { { .compatible = "spacemit,k1" }, { }, }; const struct fdt_driver fdt_hsm_spacemit = { .match_table = spacemit_hsm_match, .init = spacemit_hsm_probe, };