// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2013 Imagination Technologies
* Author: Paul Burton <paul.burton@mips.com>
*/
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/memblock.h>
#include <linux/sched/task_stack.h>
#include <linux/sched/hotplug.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/types.h>
#include <linux/irq.h>
#include <asm/bcache.h>
#include <asm/mips-cps.h>
#include <asm/mips_mt.h>
#include <asm/mipsregs.h>
#include <asm/pm-cps.h>
#include <asm/r4kcache.h>
#include <asm/regdef.h>
#include <asm/smp.h>
#include <asm/smp-cps.h>
#include <asm/time.h>
#include <asm/uasm.h>
#define BEV_VEC_SIZE 0x500
#define BEV_VEC_ALIGN 0x1000
enum label_id {
label_not_nmi = 1,
};
UASM_L_LA(_not_nmi)
static u64 core_entry_reg;
static phys_addr_t cps_vec_pa;
struct cluster_boot_config *mips_cps_cluster_bootcfg;
static void power_up_other_cluster(unsigned int cluster)
{
u32 stat, seq_state;
unsigned int timeout;
mips_cm_lock_other(cluster, CM_GCR_Cx_OTHER_CORE_CM, 0,
CM_GCR_Cx_OTHER_BLOCK_LOCAL);
stat = read_cpc_co_stat_conf();
mips_cm_unlock_other();
seq_state = stat & CPC_Cx_STAT_CONF_SEQSTATE;
seq_state >>= __ffs(CPC_Cx_STAT_CONF_SEQSTATE);
if (seq_state == CPC_Cx_STAT_CONF_SEQSTATE_U5)
return;
/* Set endianness & power up the CM */
mips_cm_lock_other(cluster, 0, 0, CM_GCR_Cx_OTHER_BLOCK_GLOBAL);
write_cpc_redir_sys_config(IS_ENABLED(CONFIG_CPU_BIG_ENDIAN));
write_cpc_redir_pwrup_ctl(1);
mips_cm_unlock_other();
/* Wait for the CM to start up */
timeout = 1000;
mips_cm_lock_other(cluster, CM_GCR_Cx_OTHER_CORE_CM, 0,
CM_GCR_Cx_OTHER_BLOCK_LOCAL);
while (1) {
stat = read_cpc_co_stat_conf();
seq_state = stat & CPC_Cx_STAT_CONF_SEQSTATE;
seq_state >>= __ffs(CPC_Cx_STAT_CONF_SEQSTATE);
if (seq_state == CPC_Cx_STAT_CONF_SEQSTATE_U5)
break;
if (timeout) {
mdelay(1);
timeout--;
} else {
pr_warn("Waiting for cluster %u CM to power up... STAT_CONF=0x%x\n",
cluster, stat);
mdelay(1000);
}
}
mips_cm_unlock_other();
}
static unsigned __init core_vpe_count(unsigned int cluster, unsigned core)
{
return min(smp_max_threads, mips_cps_numvps(cluster, core));
}
static void __init *mips_cps_build_core_entry(void *addr)
{
extern void (*nmi_handler)(void);
u32 *p = addr;
u32 val;
struct uasm_label labels[2];
struct uasm_reloc relocs[2];
struct uasm_label *l = labels;
struct uasm_reloc *r = relocs;
memset(labels, 0, sizeof(labels));
memset(relocs, 0, sizeof(relocs));
uasm_i_mfc0(&p, GPR_K0, C0_STATUS);
UASM_i_LA(&p, GPR_T9, ST0_NMI);
uasm_i_and(&p, GPR_K0, GPR_K0, GPR_T9);
uasm_il_bnez(&p, &r, GPR_K0, label_not_nmi);
uasm_i_nop(&p);
UASM_i_LA(&p, GPR_K0, (long)&nmi_handler);
uasm_l_not_nmi(&l, p);
val = CAUSEF_IV;
uasm_i_lui(&p, GPR_K0, val >> 16);
uasm_i_ori(&p, GPR_K0, GPR_K0, val & 0xffff);
uasm_i_mtc0(&p, GPR_K0, C0_CAUSE);
val = ST0_CU1 | ST0_CU0 | ST0_BEV | ST0_KX_IF_64;
uasm_i_lui(&p, GPR_K0, val >> 16);
uasm_i_ori(&p, GPR_K0, GPR_K0, val & 0xffff);
uasm_i_mtc0(&p, GPR_K0, C0_STATUS);
uasm_i_ehb(&p);
uasm_i_ori(&p, GPR_A0, 0, read_c0_config() & CONF_CM_CMASK);
UASM_i_LA(&p, GPR_A1, (long)mips_gcr_base);
#if defined(KBUILD_64BIT_SYM32) || defined(CONFIG_32BIT)
UASM_i_LA(&p, GPR_T9, CKSEG1ADDR(__pa_symbol(mips_cps_core_boot)));
#else
UASM_i_LA(&p, GPR_T9, TO_UNCAC(__pa_symbol(mips_cps_core_boot)));
#endif
uasm_i_jr(&p, GPR_T9);
uasm_i_nop(&p);
uasm_resolve_relocs(relocs, labels);
return p;
}
static bool __init check_64bit_reset(void)
{
bool cx_64bit_reset = false;
mips_cm_lock_other(0, 0, 0