mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
6.1 KiB
227 lines
6.1 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
|
|
#include <linux/smp.h> |
|
#include <linux/types.h> |
|
#include <asm/cpu.h> |
|
#include <asm/cpu-info.h> |
|
#include <asm/elf.h> |
|
|
|
#include <loongson_regs.h> |
|
#include <cpucfg-emul.h> |
|
|
|
static bool is_loongson(struct cpuinfo_mips *c) |
|
{ |
|
switch (c->processor_id & PRID_COMP_MASK) { |
|
case PRID_COMP_LEGACY: |
|
return ((c->processor_id & PRID_IMP_MASK) == |
|
PRID_IMP_LOONGSON_64C); |
|
|
|
case PRID_COMP_LOONGSON: |
|
return true; |
|
|
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
static u32 get_loongson_fprev(struct cpuinfo_mips *c) |
|
{ |
|
return c->fpu_id & LOONGSON_FPREV_MASK; |
|
} |
|
|
|
static bool cpu_has_uca(void) |
|
{ |
|
u32 diag = read_c0_diag(); |
|
u32 new_diag; |
|
|
|
if (diag & LOONGSON_DIAG_UCAC) |
|
/* UCA is already enabled. */ |
|
return true; |
|
|
|
/* See if UCAC bit can be flipped on. This should be safe. */ |
|
new_diag = diag | LOONGSON_DIAG_UCAC; |
|
write_c0_diag(new_diag); |
|
new_diag = read_c0_diag(); |
|
write_c0_diag(diag); |
|
|
|
return (new_diag & LOONGSON_DIAG_UCAC) != 0; |
|
} |
|
|
|
static void probe_uca(struct cpuinfo_mips *c) |
|
{ |
|
if (cpu_has_uca()) |
|
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LSUCA; |
|
} |
|
|
|
static void decode_loongson_config6(struct cpuinfo_mips *c) |
|
{ |
|
u32 config6 = read_c0_config6(); |
|
|
|
if (config6 & LOONGSON_CONF6_SFBEN) |
|
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SFBP; |
|
if (config6 & LOONGSON_CONF6_LLEXC) |
|
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LLEXC; |
|
if (config6 & LOONGSON_CONF6_SCRAND) |
|
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SCRAND; |
|
} |
|
|
|
static void patch_cpucfg_sel1(struct cpuinfo_mips *c) |
|
{ |
|
u64 ases = c->ases; |
|
u64 options = c->options; |
|
u32 data = c->loongson3_cpucfg_data[0]; |
|
|
|
if (options & MIPS_CPU_FPU) { |
|
data |= LOONGSON_CFG1_FP; |
|
data |= get_loongson_fprev(c) << LOONGSON_CFG1_FPREV_OFFSET; |
|
} |
|
if (ases & MIPS_ASE_LOONGSON_MMI) |
|
data |= LOONGSON_CFG1_MMI; |
|
if (ases & MIPS_ASE_MSA) |
|
data |= LOONGSON_CFG1_MSA1; |
|
|
|
c->loongson3_cpucfg_data[0] = data; |
|
} |
|
|
|
static void patch_cpucfg_sel2(struct cpuinfo_mips *c) |
|
{ |
|
u64 ases = c->ases; |
|
u64 options = c->options; |
|
u32 data = c->loongson3_cpucfg_data[1]; |
|
|
|
if (ases & MIPS_ASE_LOONGSON_EXT) |
|
data |= LOONGSON_CFG2_LEXT1; |
|
if (ases & MIPS_ASE_LOONGSON_EXT2) |
|
data |= LOONGSON_CFG2_LEXT2; |
|
if (options & MIPS_CPU_LDPTE) |
|
data |= LOONGSON_CFG2_LSPW; |
|
|
|
if (ases & MIPS_ASE_VZ) |
|
data |= LOONGSON_CFG2_LVZP; |
|
else |
|
data &= ~LOONGSON_CFG2_LVZREV; |
|
|
|
c->loongson3_cpucfg_data[1] = data; |
|
} |
|
|
|
static void patch_cpucfg_sel3(struct cpuinfo_mips *c) |
|
{ |
|
u64 ases = c->ases; |
|
u32 data = c->loongson3_cpucfg_data[2]; |
|
|
|
if (ases & MIPS_ASE_LOONGSON_CAM) { |
|
data |= LOONGSON_CFG3_LCAMP; |
|
} else { |
|
data &= ~LOONGSON_CFG3_LCAMREV; |
|
data &= ~LOONGSON_CFG3_LCAMNUM; |
|
data &= ~LOONGSON_CFG3_LCAMKW; |
|
data &= ~LOONGSON_CFG3_LCAMVW; |
|
} |
|
|
|
c->loongson3_cpucfg_data[2] = data; |
|
} |
|
|
|
void loongson3_cpucfg_synthesize_data(struct cpuinfo_mips *c) |
|
{ |
|
/* Only engage the logic on Loongson processors. */ |
|
if (!is_loongson(c)) |
|
return; |
|
|
|
/* CPUs with CPUCFG support don't need to synthesize anything. */ |
|
if (cpu_has_cfg()) |
|
goto have_cpucfg_now; |
|
|
|
c->loongson3_cpucfg_data[0] = 0; |
|
c->loongson3_cpucfg_data[1] = 0; |
|
c->loongson3_cpucfg_data[2] = 0; |
|
|
|
/* Add CPUCFG features non-discoverable otherwise. */ |
|
switch (c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) { |
|
case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_0: |
|
case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_1: |
|
case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_2: |
|
case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_3: |
|
decode_loongson_config6(c); |
|
probe_uca(c); |
|
|
|
c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | |
|
LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LLSYNC | |
|
LOONGSON_CFG1_TGTSYNC); |
|
c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | |
|
LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LPMP | |
|
LOONGSON_CFG2_LPM_REV2); |
|
c->loongson3_cpucfg_data[2] = 0; |
|
break; |
|
|
|
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R1: |
|
c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | |
|
LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA | |
|
LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); |
|
c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | |
|
LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1); |
|
c->loongson3_cpucfg_data[2] |= ( |
|
LOONGSON_CFG3_LCAM_REV1 | |
|
LOONGSON_CFG3_LCAMNUM_REV1 | |
|
LOONGSON_CFG3_LCAMKW_REV1 | |
|
LOONGSON_CFG3_LCAMVW_REV1); |
|
break; |
|
|
|
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R1: |
|
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R2: |
|
c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | |
|
LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA | |
|
LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); |
|
c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | |
|
LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1); |
|
c->loongson3_cpucfg_data[2] |= ( |
|
LOONGSON_CFG3_LCAM_REV1 | |
|
LOONGSON_CFG3_LCAMNUM_REV1 | |
|
LOONGSON_CFG3_LCAMKW_REV1 | |
|
LOONGSON_CFG3_LCAMVW_REV1); |
|
break; |
|
|
|
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_0: |
|
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_1: |
|
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_0: |
|
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_1: |
|
decode_loongson_config6(c); |
|
probe_uca(c); |
|
|
|
c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_CNT64 | |
|
LOONGSON_CFG1_LSLDR0 | LOONGSON_CFG1_LSPREF | |
|
LOONGSON_CFG1_LSPREFX | LOONGSON_CFG1_LSSYNCI | |
|
LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); |
|
c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | |
|
LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LBTMMU | |
|
LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1 | |
|
LOONGSON_CFG2_LVZ_REV1); |
|
c->loongson3_cpucfg_data[2] |= (LOONGSON_CFG3_LCAM_REV1 | |
|
LOONGSON_CFG3_LCAMNUM_REV1 | |
|
LOONGSON_CFG3_LCAMKW_REV1 | |
|
LOONGSON_CFG3_LCAMVW_REV1); |
|
break; |
|
|
|
default: |
|
/* It is possible that some future Loongson cores still do |
|
* not have CPUCFG, so do not emulate anything for these |
|
* cores. |
|
*/ |
|
return; |
|
} |
|
|
|
/* This feature is set by firmware, but all known Loongson-64 systems |
|
* are configured this way. |
|
*/ |
|
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_CDMAP; |
|
|
|
/* Patch in dynamically probed bits. */ |
|
patch_cpucfg_sel1(c); |
|
patch_cpucfg_sel2(c); |
|
patch_cpucfg_sel3(c); |
|
|
|
have_cpucfg_now: |
|
/* We have usable CPUCFG now, emulated or not. |
|
* Announce CPUCFG availability to userspace via hwcap. |
|
*/ |
|
elf_hwcap |= HWCAP_LOONGSON_CPUCFG; |
|
}
|
|
|