// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2017, Nicholas Piggin, IBM Corporation
*/
#define pr_fmt(fmt) "dt-cpu-ftrs: " fmt
#include <linux/export.h>
#include <linux/init.h>
#include <linux/jump_label.h>
#include <linux/libfdt.h>
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <linux/printk.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/threads.h>
#include <asm/cputable.h>
#include <asm/dt_cpu_ftrs.h>
#include <asm/mce.h>
#include <asm/mmu.h>
#include <asm/setup.h>
/* Device-tree visible constants follow */
#define ISA_V3_0B 3000
#define ISA_V3_1 3100
#define USABLE_PR (1U << 0)
#define USABLE_OS (1U << 1)
#define USABLE_HV (1U << 2)
#define HV_SUPPORT_HFSCR (1U << 0)
#define OS_SUPPORT_FSCR (1U << 0)
/* For parsing, we define all bits set as "NONE" case */
#define HV_SUPPORT_NONE 0xffffffffU
#define OS_SUPPORT_NONE 0xffffffffU
struct dt_cpu_feature {
const char *name;
uint32_t isa;
uint32_t usable_privilege;
uint32_t hv_support;
uint32_t os_support;
uint32_t hfscr_bit_nr;
uint32_t fscr_bit_nr;
uint32_t hwcap_bit_nr;
/* fdt parsing */
unsigned long node;
int enabled;
int disabled;
};
#define MMU_FTRS_HASH_BASE (MMU_FTRS_POWER8)
#define COMMON_USER_BASE (PPC_FEATURE_32 | PPC_FEATURE_64 | \
PPC_FEATURE_ARCH_2_06 |\
PPC_FEATURE_ICACHE_SNOOP)
#define COMMON_USER2_BASE (PPC_FEATURE2_ARCH_2_07 | \
PPC_FEATURE2_ISEL)
/*
* Set up the base CPU
*/
static int hv_mode;
static struct {
u64 lpcr;
u64 hfscr;
u64 fscr;
u64 pcr;
} system_registers;
static void (*init_pmu_registers)(void);
static void __restore_cpu_cpufeatures(void)
{
mtspr(SPRN_LPCR, system_registers.lpcr);
if (hv_mode) {
mtspr(SPRN_LPID, 0);
mtspr(SPRN_AMOR, ~0);
mtspr(SPRN_HFSCR, system_registers.hfscr);
mtspr(SPRN_PCR, system_registers.pcr);
}
mtspr(SPRN_FSCR, system_registers.fscr);
if (init_pmu_registers)
init_pmu_registers();
}
static char dt_cpu_name[64];
static struct cpu_spec __initdata base_cpu_spec = {
.cpu_name = NULL,
.cpu_features = CPU_FTRS_DT_CPU_BASE,
.cpu_user_features = COMMON_USER_BASE,
.cpu_user_features2 = COMMON_USER2_BASE,
.mmu_features = 0,
.icache_bsize = 32, /* minimum block size, fixed by */
.dcache_bsize = 32, /* cache info init. */
.num_pmcs = 0,
.pmc_type = PPC_PMC_DEFAULT,
.cpu_setup = NULL,
.cpu_restore = __restore_cpu_cpufeatures,
.machine_check_early = NULL,
.platform = NULL,
};
static void __init cpufeatures_setup_cpu(void)
{
set_cur_cpu_spec(&base_cpu_spec);
cur_cpu_spec->pvr_mask = -1;
cur_cpu_spec->pvr_value = mfspr(SPRN_PVR);
/* Initialize the base environment -- clear FSCR/HFSCR. */
hv_mode = !!(mfmsr() & MSR_HV);
if (hv_mode) {
cur_cpu_spec->cpu_features |= CPU_FTR_HVMODE;
mtspr(SPRN_HFSCR, 0);
}
mtspr(SPRN_FSCR, 0);
mtspr(SPRN_PCR, PCR_MASK);
/*
* LPCR does not get cleared, to match behaviour with secondaries
* in __restore_cpu_cpufeatures. Once the idle code is fixed, this
* could clear LPCR too.
*/
}
static int __init feat_try_enable_unknown(struct dt_cpu_feature *f)
{
if (f->hv_support == HV_SUPPORT_NONE) {
} else if (f->hv_support & HV_SUPPORT_HFSCR) {
u64 hfscr = mfspr(SPRN_HFSCR);
hfscr |= 1UL << f->hfscr_bit_nr;
mtspr(SPRN_HFSCR, hfscr);
} else {
/* Does not have a known recipe */
return 0;
}
if (f->os_support == OS_SUPPORT_NONE) {
} else if (f->os_support & OS_SUPPORT_FSCR) {
u64 fscr = mfspr(SPRN_FSCR);
fscr |= 1UL << f->fscr_bit_nr;
mtspr(SPRN_FSCR, fscr);
} else {
/* Does not have a known recipe */
return 0;
}
if ((f->usable_privilege & USABLE_PR) && (f->hwcap_bit_nr != -1)) {
uint32_t word = f->hwcap_bit_nr / 32;
uint32_t bit = f->hwcap_bit_nr % 32;
if (word == 0)
cur_cpu_spec->cpu_user_features |= 1U << bit;
else if (word == 1)
cur_cpu_spec->cpu_user_features2 |= 1U << bit;
else
pr_err("%s could not advertise to user (no hwcap bits)\n", f->name);
}
return 1;
}
static int __init feat_enable(struct dt_cpu_feature *f)
{
if (f->hv_support != HV_SUPPORT_NONE) {
if (f->hfscr_bit_nr != -1) {
u64 hfscr = mfspr(SPRN_HFSCR);
hfscr |=