// SPDX-License-Identifier: GPL-2.0-only
#include <linux/device.h>
#include <linux/cpu.h>
#include <linux/smp.h>
#include <linux/percpu.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/export.h>
#include <linux/nodemask.h>
#include <linux/cpumask.h>
#include <linux/notifier.h>
#include <asm/current.h>
#include <asm/processor.h>
#include <asm/cputable.h>
#include <asm/hvcall.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/smp.h>
#include <asm/pmc.h>
#include <asm/firmware.h>
#include <asm/idle.h>
#include <asm/svm.h>
#include "cacheinfo.h"
#include "setup.h"
#ifdef CONFIG_PPC64
#include <asm/paca.h>
#include <asm/lppaca.h>
#endif
static DEFINE_PER_CPU(struct cpu, cpu_devices);
#ifdef CONFIG_PPC64
/*
* Snooze delay has not been hooked up since 3fa8cad82b94 ("powerpc/pseries/cpuidle:
* smt-snooze-delay cleanup.") and has been broken even longer. As was foretold in
* 2014:
*
* "ppc64_util currently utilises it. Once we fix ppc64_util, propose to clean
* up the kernel code."
*
* powerpc-utils stopped using it as of 1.3.8. At some point in the future this
* code should be removed.
*/
static ssize_t store_smt_snooze_delay(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
pr_warn_once("%s (%d) stored to unsupported smt_snooze_delay, which has no effect.\n",
current->comm, current->pid);
return count;
}
static ssize_t show_smt_snooze_delay(struct device *dev,
struct device_attribute *attr,
char *buf)
{
pr_warn_once("%s (%d) read from unsupported smt_snooze_delay\n",
current->comm, current->pid);
return sprintf(buf, "100\n");
}
static DEVICE_ATTR(smt_snooze_delay, 0644, show_smt_snooze_delay,
store_smt_snooze_delay);
static int __init setup_smt_snooze_delay(char *str)
{
if (!cpu_has_feature(CPU_FTR_SMT))
return 1;
pr_warn("smt-snooze-delay command line option has no effect\n");
return 1;
}
__setup("smt-snooze-delay=", setup_smt_snooze_delay);
#endif /* CONFIG_PPC64 */
#define __SYSFS_SPRSETUP_READ_WRITE(NAME, ADDRESS, EXTRA) \
static void read_##NAME(void *val) \
{ \
*(unsigned long *)val = mfspr(ADDRESS); \
} \
static void write_##NAME(void *val) \
{ \
EXTRA; \
mtspr(ADDRESS, *(unsigned long *)val); \
}
#define __SYSFS_SPRSETUP_SHOW_STORE(NAME) \
static ssize_t show_##NAME(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct cpu *cpu = container_of(dev, struct cpu, dev); \
unsigned long val; \
smp_call_function_single(cpu->dev.id, read_##NAME, &val, 1); \
return sprintf(buf, "%lx\n", val); \
} \
static ssize_t __used \
store_##NAME(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct cpu *cpu = container_of(dev, struct cpu, dev); \
unsigned long val; \
int ret = sscanf(buf, "%lx", &val); \
if (ret != 1) \
return -EINVAL; \
smp_call_function_single(cpu->dev.id, write_##NAME, &val, 1); \
return count; \
}
#define SYSFS_PMCSETUP(NAME, ADDRESS) \
__SYSFS_SPRSETUP_READ_WRITE(NAME, ADDRESS, ppc_enable_pmcs()) \
__SYSFS_SPRSETUP_SHOW_STORE(NAME)
#define SYSFS_SPRSETUP(NAME, ADDRESS) \
__SYSFS_SPRSETUP_READ_WRITE(NAME, ADDRESS, ) \
__SYSFS_SPRSETUP_SHOW_STORE(NAME)
#define SYSFS_SPRSETUP_SHOW_STORE(NAME) \
__SYSFS_SPRSETUP_SHOW_STORE(NAME)
#ifdef CONFIG_PPC64
/*
* This is the system wide DSCR register default value. Any
* change to this default value through the sysfs interface
* will update all per cpu DSCR default values across the
* system stored in their respective PACA structures.
*/
static unsigned long dscr_default;
/**
* read_dscr() - Fetch the cpu specific DSCR default
* @val: Returned cpu specific DSCR default value
*
* This function returns the per cpu DSCR default value
* for any cpu which is contained in it's PACA structure.
*/
static void read_dscr(void *val)
{
*(unsigned long *)val = get_paca()->dscr_default;
}
/**
* write_dscr() - Update the cpu specific DSCR default
* @val: New cpu specific DSCR default value to update
*
* This function updates the per cpu DSCR default value
* for any cpu which is contained in it's PACA structure.
*/
static void write_dscr(void *val)
{
get_paca()->dscr_default = *(unsigned long *)val;
if (!current->thread.dscr_inherit) {
current->thread.dscr = *(unsigned long *)val;
mtspr(SPRN_DSCR, *(unsigned long *)val);
}
}
SYSFS_SPRSETUP_SHOW_STORE(dscr);
static DEVICE_ATTR(dscr, 0600, show_dscr, store_dscr);
static void add_write_permission_dev_attr(struct device_attribute *attr)
{