mirror of https://gitee.com/openkylin/linux.git
Merge branch 'pm-cpufreq'
* pm-cpufreq: cpufreq: OMAP: remove loops_per_jiffy recalculate for smp sections: fix section conflicts in drivers/cpufreq cpufreq: conservative: update frequency when limits are relaxed cpufreq / ondemand: update frequency when limits are relaxed cpufreq: Add a generic cpufreq-cpu0 driver PM / OPP: Initialize OPP table from device tree ARM: add cpufreq transiton notifier to adjust loops_per_jiffy for smp cpufreq: Remove support for hardware P-state chips from powernow-k8 acpi-cpufreq: Add compatibility for legacy AMD cpb sysfs knob acpi-cpufreq: Add support for disabling dynamic overclocking ACPI: Add fixups for AMD P-state figures powernow-k8: delay info messages until initialization has succeeded cpufreq: Add warning message to powernow-k8 acpi-cpufreq: Add quirk to disable _PSD usage on all AMD CPUs acpi-cpufreq: Add support for modern AMD CPUs cpufreq / powernow-k8: Fixup missing _PSS objects message PM / cpufreq: Initialise the cpu field during conservative governor start
This commit is contained in:
commit
fa373abbbd
|
@ -176,3 +176,14 @@ Description: Disable L3 cache indices
|
|||
All AMD processors with L3 caches provide this functionality.
|
||||
For details, see BKDGs at
|
||||
http://developer.amd.com/documentation/guides/Pages/default.aspx
|
||||
|
||||
|
||||
What: /sys/devices/system/cpu/cpufreq/boost
|
||||
Date: August 2012
|
||||
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
|
||||
Description: Processor frequency boosting control
|
||||
|
||||
This switch controls the boost setting for the whole system.
|
||||
Boosting allows the CPU and the firmware to run at a frequency
|
||||
beyound it's nominal limit.
|
||||
More details can be found in Documentation/cpu-freq/boost.txt
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
Processor boosting control
|
||||
|
||||
- information for users -
|
||||
|
||||
Quick guide for the impatient:
|
||||
--------------------
|
||||
/sys/devices/system/cpu/cpufreq/boost
|
||||
controls the boost setting for the whole system. You can read and write
|
||||
that file with either "0" (boosting disabled) or "1" (boosting allowed).
|
||||
Reading or writing 1 does not mean that the system is boosting at this
|
||||
very moment, but only that the CPU _may_ raise the frequency at it's
|
||||
discretion.
|
||||
--------------------
|
||||
|
||||
Introduction
|
||||
-------------
|
||||
Some CPUs support a functionality to raise the operating frequency of
|
||||
some cores in a multi-core package if certain conditions apply, mostly
|
||||
if the whole chip is not fully utilized and below it's intended thermal
|
||||
budget. This is done without operating system control by a combination
|
||||
of hardware and firmware.
|
||||
On Intel CPUs this is called "Turbo Boost", AMD calls it "Turbo-Core",
|
||||
in technical documentation "Core performance boost". In Linux we use
|
||||
the term "boost" for convenience.
|
||||
|
||||
Rationale for disable switch
|
||||
----------------------------
|
||||
|
||||
Though the idea is to just give better performance without any user
|
||||
intervention, sometimes the need arises to disable this functionality.
|
||||
Most systems offer a switch in the (BIOS) firmware to disable the
|
||||
functionality at all, but a more fine-grained and dynamic control would
|
||||
be desirable:
|
||||
1. While running benchmarks, reproducible results are important. Since
|
||||
the boosting functionality depends on the load of the whole package,
|
||||
single thread performance can vary. By explicitly disabling the boost
|
||||
functionality at least for the benchmark's run-time the system will run
|
||||
at a fixed frequency and results are reproducible again.
|
||||
2. To examine the impact of the boosting functionality it is helpful
|
||||
to do tests with and without boosting.
|
||||
3. Boosting means overclocking the processor, though under controlled
|
||||
conditions. By raising the frequency and the voltage the processor
|
||||
will consume more power than without the boosting, which may be
|
||||
undesirable for instance for mobile users. Disabling boosting may
|
||||
save power here, though this depends on the workload.
|
||||
|
||||
|
||||
User controlled switch
|
||||
----------------------
|
||||
|
||||
To allow the user to toggle the boosting functionality, the acpi-cpufreq
|
||||
driver exports a sysfs knob to disable it. There is a file:
|
||||
/sys/devices/system/cpu/cpufreq/boost
|
||||
which can either read "0" (boosting disabled) or "1" (boosting enabled).
|
||||
Reading the file is always supported, even if the processor does not
|
||||
support boosting. In this case the file will be read-only and always
|
||||
reads as "0". Explicitly changing the permissions and writing to that
|
||||
file anyway will return EINVAL.
|
||||
|
||||
On supported CPUs one can write either a "0" or a "1" into this file.
|
||||
This will either disable the boost functionality on all cores in the
|
||||
whole system (0) or will allow the hardware to boost at will (1).
|
||||
|
||||
Writing a "1" does not explicitly boost the system, but just allows the
|
||||
CPU (and the firmware) to boost at their discretion. Some implementations
|
||||
take external factors like the chip's temperature into account, so
|
||||
boosting once does not necessarily mean that it will occur every time
|
||||
even using the exact same software setup.
|
||||
|
||||
|
||||
AMD legacy cpb switch
|
||||
---------------------
|
||||
The AMD powernow-k8 driver used to support a very similar switch to
|
||||
disable or enable the "Core Performance Boost" feature of some AMD CPUs.
|
||||
This switch was instantiated in each CPU's cpufreq directory
|
||||
(/sys/devices/system/cpu[0-9]*/cpufreq) and was called "cpb".
|
||||
Though the per CPU existence hints at a more fine grained control, the
|
||||
actual implementation only supported a system-global switch semantics,
|
||||
which was simply reflected into each CPU's file. Writing a 0 or 1 into it
|
||||
would pull the other CPUs to the same state.
|
||||
For compatibility reasons this file and its behavior is still supported
|
||||
on AMD CPUs, though it is now protected by a config switch
|
||||
(X86_ACPI_CPUFREQ_CPB). On Intel CPUs this file will never be created,
|
||||
even with the config option set.
|
||||
This functionality is considered legacy and will be removed in some future
|
||||
kernel version.
|
||||
|
||||
More fine grained boosting control
|
||||
----------------------------------
|
||||
|
||||
Technically it is possible to switch the boosting functionality at least
|
||||
on a per package basis, for some CPUs even per core. Currently the driver
|
||||
does not support it, but this may be implemented in the future.
|
|
@ -0,0 +1,55 @@
|
|||
Generic CPU0 cpufreq driver
|
||||
|
||||
It is a generic cpufreq driver for CPU0 frequency management. It
|
||||
supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
|
||||
systems which share clock and voltage across all CPUs.
|
||||
|
||||
Both required and optional properties listed below must be defined
|
||||
under node /cpus/cpu@0.
|
||||
|
||||
Required properties:
|
||||
- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt
|
||||
for details
|
||||
|
||||
Optional properties:
|
||||
- clock-latency: Specify the possible maximum transition latency for clock,
|
||||
in unit of nanoseconds.
|
||||
- voltage-tolerance: Specify the CPU voltage tolerance in percentage.
|
||||
|
||||
Examples:
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu@0 {
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <0>;
|
||||
next-level-cache = <&L2>;
|
||||
operating-points = <
|
||||
/* kHz uV */
|
||||
792000 1100000
|
||||
396000 950000
|
||||
198000 850000
|
||||
>;
|
||||
transition-latency = <61036>; /* two CLK32 periods */
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <1>;
|
||||
next-level-cache = <&L2>;
|
||||
};
|
||||
|
||||
cpu@2 {
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <2>;
|
||||
next-level-cache = <&L2>;
|
||||
};
|
||||
|
||||
cpu@3 {
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <3>;
|
||||
next-level-cache = <&L2>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
* Generic OPP Interface
|
||||
|
||||
SoCs have a standard set of tuples consisting of frequency and
|
||||
voltage pairs that the device will support per voltage domain. These
|
||||
are called Operating Performance Points or OPPs.
|
||||
|
||||
Properties:
|
||||
- operating-points: An array of 2-tuples items, and each item consists
|
||||
of frequency and voltage like <freq-kHz vol-uV>.
|
||||
freq: clock frequency in kHz
|
||||
vol: voltage in microvolt
|
||||
|
||||
Examples:
|
||||
|
||||
cpu@0 {
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <0>;
|
||||
next-level-cache = <&L2>;
|
||||
operating-points = <
|
||||
/* kHz uV */
|
||||
792000 1100000
|
||||
396000 950000
|
||||
198000 850000
|
||||
>;
|
||||
};
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/percpu.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/cpufreq.h>
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
@ -584,3 +585,56 @@ int setup_profiling_timer(unsigned int multiplier)
|
|||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
|
||||
static DEFINE_PER_CPU(unsigned long, l_p_j_ref);
|
||||
static DEFINE_PER_CPU(unsigned long, l_p_j_ref_freq);
|
||||
static unsigned long global_l_p_j_ref;
|
||||
static unsigned long global_l_p_j_ref_freq;
|
||||
|
||||
static int cpufreq_callback(struct notifier_block *nb,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct cpufreq_freqs *freq = data;
|
||||
int cpu = freq->cpu;
|
||||
|
||||
if (freq->flags & CPUFREQ_CONST_LOOPS)
|
||||
return NOTIFY_OK;
|
||||
|
||||
if (!per_cpu(l_p_j_ref, cpu)) {
|
||||
per_cpu(l_p_j_ref, cpu) =
|
||||
per_cpu(cpu_data, cpu).loops_per_jiffy;
|
||||
per_cpu(l_p_j_ref_freq, cpu) = freq->old;
|
||||
if (!global_l_p_j_ref) {
|
||||
global_l_p_j_ref = loops_per_jiffy;
|
||||
global_l_p_j_ref_freq = freq->old;
|
||||
}
|
||||
}
|
||||
|
||||
if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) ||
|
||||
(val == CPUFREQ_POSTCHANGE && freq->old > freq->new) ||
|
||||
(val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
|
||||
loops_per_jiffy = cpufreq_scale(global_l_p_j_ref,
|
||||
global_l_p_j_ref_freq,
|
||||
freq->new);
|
||||
per_cpu(cpu_data, cpu).loops_per_jiffy =
|
||||
cpufreq_scale(per_cpu(l_p_j_ref, cpu),
|
||||
per_cpu(l_p_j_ref_freq, cpu),
|
||||
freq->new);
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block cpufreq_notifier = {
|
||||
.notifier_call = cpufreq_callback,
|
||||
};
|
||||
|
||||
static int __init register_cpufreq_notifier(void)
|
||||
{
|
||||
return cpufreq_register_notifier(&cpufreq_notifier,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
}
|
||||
core_initcall(register_cpufreq_notifier);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -248,6 +248,9 @@
|
|||
|
||||
#define MSR_IA32_PERF_STATUS 0x00000198
|
||||
#define MSR_IA32_PERF_CTL 0x00000199
|
||||
#define MSR_AMD_PSTATE_DEF_BASE 0xc0010064
|
||||
#define MSR_AMD_PERF_STATUS 0xc0010063
|
||||
#define MSR_AMD_PERF_CTL 0xc0010062
|
||||
|
||||
#define MSR_IA32_MPERF 0x000000e7
|
||||
#define MSR_IA32_APERF 0x000000e8
|
||||
|
|
|
@ -324,6 +324,34 @@ static int acpi_processor_get_performance_control(struct acpi_processor *pr)
|
|||
return result;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
/*
|
||||
* Some AMDs have 50MHz frequency multiples, but only provide 100MHz rounding
|
||||
* in their ACPI data. Calculate the real values and fix up the _PSS data.
|
||||
*/
|
||||
static void amd_fixup_frequency(struct acpi_processor_px *px, int i)
|
||||
{
|
||||
u32 hi, lo, fid, did;
|
||||
int index = px->control & 0x00000007;
|
||||
|
||||
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
|
||||
return;
|
||||
|
||||
if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10)
|
||||
|| boot_cpu_data.x86 == 0x11) {
|
||||
rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi);
|
||||
fid = lo & 0x3f;
|
||||
did = (lo >> 6) & 7;
|
||||
if (boot_cpu_data.x86 == 0x10)
|
||||
px->core_frequency = (100 * (fid + 0x10)) >> did;
|
||||
else
|
||||
px->core_frequency = (100 * (fid + 8)) >> did;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void amd_fixup_frequency(struct acpi_processor_px *px, int i) {};
|
||||
#endif
|
||||
|
||||
static int acpi_processor_get_performance_states(struct acpi_processor *pr)
|
||||
{
|
||||
int result = 0;
|
||||
|
@ -379,6 +407,8 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr)
|
|||
goto end;
|
||||
}
|
||||
|
||||
amd_fixup_frequency(px, i);
|
||||
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",
|
||||
i,
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/opp.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/*
|
||||
* Internal data structure organization with the OPP layer library is as
|
||||
|
@ -674,3 +675,49 @@ struct srcu_notifier_head *opp_get_notifier(struct device *dev)
|
|||
|
||||
return &dev_opp->head;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/**
|
||||
* of_init_opp_table() - Initialize opp table from device tree
|
||||
* @dev: device pointer used to lookup device OPPs.
|
||||
*
|
||||
* Register the initial OPP table with the OPP library for given device.
|
||||
*/
|
||||
int of_init_opp_table(struct device *dev)
|
||||
{
|
||||
const struct property *prop;
|
||||
const __be32 *val;
|
||||
int nr;
|
||||
|
||||
prop = of_find_property(dev->of_node, "operating-points", NULL);
|
||||
if (!prop)
|
||||
return -ENODEV;
|
||||
if (!prop->value)
|
||||
return -ENODATA;
|
||||
|
||||
/*
|
||||
* Each OPP is a set of tuples consisting of frequency and
|
||||
* voltage like <freq-kHz vol-uV>.
|
||||
*/
|
||||
nr = prop->length / sizeof(u32);
|
||||
if (nr % 2) {
|
||||
dev_err(dev, "%s: Invalid OPP list\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = prop->value;
|
||||
while (nr) {
|
||||
unsigned long freq = be32_to_cpup(val++) * 1000;
|
||||
unsigned long volt = be32_to_cpup(val++);
|
||||
|
||||
if (opp_add(dev, freq, volt)) {
|
||||
dev_warn(dev, "%s: Failed to add OPP %ld\n",
|
||||
__func__, freq);
|
||||
continue;
|
||||
}
|
||||
nr -= 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -179,6 +179,17 @@ config CPU_FREQ_GOV_CONSERVATIVE
|
|||
|
||||
If in doubt, say N.
|
||||
|
||||
config GENERIC_CPUFREQ_CPU0
|
||||
bool "Generic CPU0 cpufreq driver"
|
||||
depends on HAVE_CLK && REGULATOR && PM_OPP && OF
|
||||
select CPU_FREQ_TABLE
|
||||
help
|
||||
This adds a generic cpufreq driver for CPU0 frequency management.
|
||||
It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
|
||||
systems which share clock and voltage across all CPUs.
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
menu "x86 CPU frequency scaling drivers"
|
||||
depends on X86
|
||||
source "drivers/cpufreq/Kconfig.x86"
|
||||
|
|
|
@ -23,7 +23,8 @@ config X86_ACPI_CPUFREQ
|
|||
help
|
||||
This driver adds a CPUFreq driver which utilizes the ACPI
|
||||
Processor Performance States.
|
||||
This driver also supports Intel Enhanced Speedstep.
|
||||
This driver also supports Intel Enhanced Speedstep and newer
|
||||
AMD CPUs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called acpi-cpufreq.
|
||||
|
@ -32,6 +33,18 @@ config X86_ACPI_CPUFREQ
|
|||
|
||||
If in doubt, say N.
|
||||
|
||||
config X86_ACPI_CPUFREQ_CPB
|
||||
default y
|
||||
bool "Legacy cpb sysfs knob support for AMD CPUs"
|
||||
depends on X86_ACPI_CPUFREQ && CPU_SUP_AMD
|
||||
help
|
||||
The powernow-k8 driver used to provide a sysfs knob called "cpb"
|
||||
to disable the Core Performance Boosting feature of AMD CPUs. This
|
||||
file has now been superseeded by the more generic "boost" entry.
|
||||
|
||||
By enabling this option the acpi_cpufreq driver provides the old
|
||||
entry in addition to the new boost ones, for compatibility reasons.
|
||||
|
||||
config ELAN_CPUFREQ
|
||||
tristate "AMD Elan SC400 and SC410"
|
||||
select CPU_FREQ_TABLE
|
||||
|
@ -95,7 +108,8 @@ config X86_POWERNOW_K8
|
|||
select CPU_FREQ_TABLE
|
||||
depends on ACPI && ACPI_PROCESSOR
|
||||
help
|
||||
This adds the CPUFreq driver for K8/K10 Opteron/Athlon64 processors.
|
||||
This adds the CPUFreq driver for K8/early Opteron/Athlon64 processors.
|
||||
Support for K10 and newer processors is now in acpi-cpufreq.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called powernow-k8.
|
||||
|
|
|
@ -13,13 +13,15 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o
|
|||
# CPUfreq cross-arch helpers
|
||||
obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o
|
||||
|
||||
obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o
|
||||
|
||||
##################################################################################
|
||||
# x86 drivers.
|
||||
# Link order matters. K8 is preferred to ACPI because of firmware bugs in early
|
||||
# K8 systems. ACPI is preferred to all other hardware-specific drivers.
|
||||
# speedstep-* is preferred over p4-clockmod.
|
||||
|
||||
obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o mperf.o
|
||||
obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o
|
||||
obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o mperf.o
|
||||
obj-$(CONFIG_X86_PCC_CPUFREQ) += pcc-cpufreq.o
|
||||
obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o
|
||||
|
|
|
@ -51,13 +51,19 @@ MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");
|
|||
MODULE_DESCRIPTION("ACPI Processor P-States Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define PFX "acpi-cpufreq: "
|
||||
|
||||
enum {
|
||||
UNDEFINED_CAPABLE = 0,
|
||||
SYSTEM_INTEL_MSR_CAPABLE,
|
||||
SYSTEM_AMD_MSR_CAPABLE,
|
||||
SYSTEM_IO_CAPABLE,
|
||||
};
|
||||
|
||||
#define INTEL_MSR_RANGE (0xffff)
|
||||
#define AMD_MSR_RANGE (0x7)
|
||||
|
||||
#define MSR_K7_HWCR_CPB_DIS (1ULL << 25)
|
||||
|
||||
struct acpi_cpufreq_data {
|
||||
struct acpi_processor_performance *acpi_data;
|
||||
|
@ -74,6 +80,116 @@ static struct acpi_processor_performance __percpu *acpi_perf_data;
|
|||
static struct cpufreq_driver acpi_cpufreq_driver;
|
||||
|
||||
static unsigned int acpi_pstate_strict;
|
||||
static bool boost_enabled, boost_supported;
|
||||
static struct msr __percpu *msrs;
|
||||
|
||||
static bool boost_state(unsigned int cpu)
|
||||
{
|
||||
u32 lo, hi;
|
||||
u64 msr;
|
||||
|
||||
switch (boot_cpu_data.x86_vendor) {
|
||||
case X86_VENDOR_INTEL:
|
||||
rdmsr_on_cpu(cpu, MSR_IA32_MISC_ENABLE, &lo, &hi);
|
||||
msr = lo | ((u64)hi << 32);
|
||||
return !(msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE);
|
||||
case X86_VENDOR_AMD:
|
||||
rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi);
|
||||
msr = lo | ((u64)hi << 32);
|
||||
return !(msr & MSR_K7_HWCR_CPB_DIS);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void boost_set_msrs(bool enable, const struct cpumask *cpumask)
|
||||
{
|
||||
u32 cpu;
|
||||
u32 msr_addr;
|
||||
u64 msr_mask;
|
||||
|
||||
switch (boot_cpu_data.x86_vendor) {
|
||||
case X86_VENDOR_INTEL:
|
||||
msr_addr = MSR_IA32_MISC_ENABLE;
|
||||
msr_mask = MSR_IA32_MISC_ENABLE_TURBO_DISABLE;
|
||||
break;
|
||||
case X86_VENDOR_AMD:
|
||||
msr_addr = MSR_K7_HWCR;
|
||||
msr_mask = MSR_K7_HWCR_CPB_DIS;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
rdmsr_on_cpus(cpumask, msr_addr, msrs);
|
||||
|
||||
for_each_cpu(cpu, cpumask) {
|
||||
struct msr *reg = per_cpu_ptr(msrs, cpu);
|
||||
if (enable)
|
||||
reg->q &= ~msr_mask;
|
||||
else
|
||||
reg->q |= msr_mask;
|
||||
}
|
||||
|
||||
wrmsr_on_cpus(cpumask, msr_addr, msrs);
|
||||
}
|
||||
|
||||
static ssize_t _store_boost(const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
unsigned long val = 0;
|
||||
|
||||
if (!boost_supported)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret || (val > 1))
|
||||
return -EINVAL;
|
||||
|
||||
if ((val && boost_enabled) || (!val && !boost_enabled))
|
||||
return count;
|
||||
|
||||
get_online_cpus();
|
||||
|
||||
boost_set_msrs(val, cpu_online_mask);
|
||||
|
||||
put_online_cpus();
|
||||
|
||||
boost_enabled = val;
|
||||
pr_debug("Core Boosting %sabled.\n", val ? "en" : "dis");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t store_global_boost(struct kobject *kobj, struct attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return _store_boost(buf, count);
|
||||
}
|
||||
|
||||
static ssize_t show_global_boost(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n", boost_enabled);
|
||||
}
|
||||
|
||||
static struct global_attr global_boost = __ATTR(boost, 0644,
|
||||
show_global_boost,
|
||||
store_global_boost);
|
||||
|
||||
#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB
|
||||
static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
return _store_boost(buf, count);
|
||||
}
|
||||
|
||||
static ssize_t show_cpb(struct cpufreq_policy *policy, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n", boost_enabled);
|
||||
}
|
||||
|
||||
static struct freq_attr cpb = __ATTR(cpb, 0644, show_cpb, store_cpb);
|
||||
#endif
|
||||
|
||||
static int check_est_cpu(unsigned int cpuid)
|
||||
{
|
||||
|
@ -82,6 +198,13 @@ static int check_est_cpu(unsigned int cpuid)
|
|||
return cpu_has(cpu, X86_FEATURE_EST);
|
||||
}
|
||||
|
||||
static int check_amd_hwpstate_cpu(unsigned int cpuid)
|
||||
{
|
||||
struct cpuinfo_x86 *cpu = &cpu_data(cpuid);
|
||||
|
||||
return cpu_has(cpu, X86_FEATURE_HW_PSTATE);
|
||||
}
|
||||
|
||||
static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data)
|
||||
{
|
||||
struct acpi_processor_performance *perf;
|
||||
|
@ -101,7 +224,11 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
|
|||
int i;
|
||||
struct acpi_processor_performance *perf;
|
||||
|
||||
msr &= INTEL_MSR_RANGE;
|
||||
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
|
||||
msr &= AMD_MSR_RANGE;
|
||||
else
|
||||
msr &= INTEL_MSR_RANGE;
|
||||
|
||||
perf = data->acpi_data;
|
||||
|
||||
for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
|
||||
|
@ -115,6 +242,7 @@ static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data)
|
|||
{
|
||||
switch (data->cpu_feature) {
|
||||
case SYSTEM_INTEL_MSR_CAPABLE:
|
||||
case SYSTEM_AMD_MSR_CAPABLE:
|
||||
return extract_msr(val, data);
|
||||
case SYSTEM_IO_CAPABLE:
|
||||
return extract_io(val, data);
|
||||
|
@ -150,6 +278,7 @@ static void do_drv_read(void *_cmd)
|
|||
|
||||
switch (cmd->type) {
|
||||
case SYSTEM_INTEL_MSR_CAPABLE:
|
||||
case SYSTEM_AMD_MSR_CAPABLE:
|
||||
rdmsr(cmd->addr.msr.reg, cmd->val, h);
|
||||
break;
|
||||
case SYSTEM_IO_CAPABLE:
|
||||
|
@ -174,6 +303,9 @@ static void do_drv_write(void *_cmd)
|
|||
lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE);
|
||||
wrmsr(cmd->addr.msr.reg, lo, hi);
|
||||
break;
|
||||
case SYSTEM_AMD_MSR_CAPABLE:
|
||||
wrmsr(cmd->addr.msr.reg, cmd->val, 0);
|
||||
break;
|
||||
case SYSTEM_IO_CAPABLE:
|
||||
acpi_os_write_port((acpi_io_address)cmd->addr.io.port,
|
||||
cmd->val,
|
||||
|
@ -217,6 +349,10 @@ static u32 get_cur_val(const struct cpumask *mask)
|
|||
cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
|
||||
cmd.addr.msr.reg = MSR_IA32_PERF_STATUS;
|
||||
break;
|
||||
case SYSTEM_AMD_MSR_CAPABLE:
|
||||
cmd.type = SYSTEM_AMD_MSR_CAPABLE;
|
||||
cmd.addr.msr.reg = MSR_AMD_PERF_STATUS;
|
||||
break;
|
||||
case SYSTEM_IO_CAPABLE:
|
||||
cmd.type = SYSTEM_IO_CAPABLE;
|
||||
perf = per_cpu(acfreq_data, cpumask_first(mask))->acpi_data;
|
||||
|
@ -326,6 +462,11 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
|
|||
cmd.addr.msr.reg = MSR_IA32_PERF_CTL;
|
||||
cmd.val = (u32) perf->states[next_perf_state].control;
|
||||
break;
|
||||
case SYSTEM_AMD_MSR_CAPABLE:
|
||||
cmd.type = SYSTEM_AMD_MSR_CAPABLE;
|
||||
cmd.addr.msr.reg = MSR_AMD_PERF_CTL;
|
||||
cmd.val = (u32) perf->states[next_perf_state].control;
|
||||
break;
|
||||
case SYSTEM_IO_CAPABLE:
|
||||
cmd.type = SYSTEM_IO_CAPABLE;
|
||||
cmd.addr.io.port = perf->control_register.address;
|
||||
|
@ -419,6 +560,44 @@ static void free_acpi_perf_data(void)
|
|||
free_percpu(acpi_perf_data);
|
||||
}
|
||||
|
||||
static int boost_notify(struct notifier_block *nb, unsigned long action,
|
||||
void *hcpu)
|
||||
{
|
||||
unsigned cpu = (long)hcpu;
|
||||
const struct cpumask *cpumask;
|
||||
|
||||
cpumask = get_cpu_mask(cpu);
|
||||
|
||||
/*
|
||||
* Clear the boost-disable bit on the CPU_DOWN path so that
|
||||
* this cpu cannot block the remaining ones from boosting. On
|
||||
* the CPU_UP path we simply keep the boost-disable flag in
|
||||
* sync with the current global state.
|
||||
*/
|
||||
|
||||
switch (action) {
|
||||
case CPU_UP_PREPARE:
|
||||
case CPU_UP_PREPARE_FROZEN:
|
||||
boost_set_msrs(boost_enabled, cpumask);
|
||||
break;
|
||||
|
||||
case CPU_DOWN_PREPARE:
|
||||
case CPU_DOWN_PREPARE_FROZEN:
|
||||
boost_set_msrs(1, cpumask);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
|
||||
static struct notifier_block boost_nb = {
|
||||
.notifier_call = boost_notify,
|
||||
};
|
||||
|
||||
/*
|
||||
* acpi_cpufreq_early_init - initialize ACPI P-States library
|
||||
*
|
||||
|
@ -559,6 +738,14 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|||
policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
|
||||
cpumask_copy(policy->cpus, cpu_core_mask(cpu));
|
||||
}
|
||||
|
||||
if (check_amd_hwpstate_cpu(cpu) && !acpi_pstate_strict) {
|
||||
cpumask_clear(policy->cpus);
|
||||
cpumask_set_cpu(cpu, policy->cpus);
|
||||
cpumask_copy(policy->related_cpus, cpu_sibling_mask(cpu));
|
||||
policy->shared_type = CPUFREQ_SHARED_TYPE_HW;
|
||||
pr_info_once(PFX "overriding BIOS provided _PSD data\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* capability check */
|
||||
|
@ -580,12 +767,16 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|||
break;
|
||||
case ACPI_ADR_SPACE_FIXED_HARDWARE:
|
||||
pr_debug("HARDWARE addr space\n");
|
||||
if (!check_est_cpu(cpu)) {
|
||||
result = -ENODEV;
|
||||
goto err_unreg;
|
||||
if (check_est_cpu(cpu)) {
|
||||
data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE;
|
||||
break;
|
||||
}
|
||||
data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE;
|
||||
break;
|
||||
if (check_amd_hwpstate_cpu(cpu)) {
|
||||
data->cpu_feature = SYSTEM_AMD_MSR_CAPABLE;
|
||||
break;
|
||||
}
|
||||
result = -ENODEV;
|
||||
goto err_unreg;
|
||||
default:
|
||||
pr_debug("Unknown addr space %d\n",
|
||||
(u32) (perf->control_register.space_id));
|
||||
|
@ -718,6 +909,7 @@ static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
|
|||
|
||||
static struct freq_attr *acpi_cpufreq_attr[] = {
|
||||
&cpufreq_freq_attr_scaling_available_freqs,
|
||||
NULL, /* this is a placeholder for cpb, do not remove */
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -733,6 +925,49 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
|
|||
.attr = acpi_cpufreq_attr,
|
||||
};
|
||||
|
||||
static void __init acpi_cpufreq_boost_init(void)
|
||||
{
|
||||
if (boot_cpu_has(X86_FEATURE_CPB) || boot_cpu_has(X86_FEATURE_IDA)) {
|
||||
msrs = msrs_alloc();
|
||||
|
||||
if (!msrs)
|
||||
return;
|
||||
|
||||
boost_supported = true;
|
||||
boost_enabled = boost_state(0);
|
||||
|
||||
get_online_cpus();
|
||||
|
||||
/* Force all MSRs to the same value */
|
||||
boost_set_msrs(boost_enabled, cpu_online_mask);
|
||||
|
||||
register_cpu_notifier(&boost_nb);
|
||||
|
||||
put_online_cpus();
|
||||
} else
|
||||
global_boost.attr.mode = 0444;
|
||||
|
||||
/* We create the boost file in any case, though for systems without
|
||||
* hardware support it will be read-only and hardwired to return 0.
|
||||
*/
|
||||
if (sysfs_create_file(cpufreq_global_kobject, &(global_boost.attr)))
|
||||
pr_warn(PFX "could not register global boost sysfs file\n");
|
||||
else
|
||||
pr_debug("registered global boost sysfs file\n");
|
||||
}
|
||||
|
||||
static void __exit acpi_cpufreq_boost_exit(void)
|
||||
{
|
||||
sysfs_remove_file(cpufreq_global_kobject, &(global_boost.attr));
|
||||
|
||||
if (msrs) {
|
||||
unregister_cpu_notifier(&boost_nb);
|
||||
|
||||
msrs_free(msrs);
|
||||
msrs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init acpi_cpufreq_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -746,9 +981,32 @@ static int __init acpi_cpufreq_init(void)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB
|
||||
/* this is a sysfs file with a strange name and an even stranger
|
||||
* semantic - per CPU instantiation, but system global effect.
|
||||
* Lets enable it only on AMD CPUs for compatibility reasons and
|
||||
* only if configured. This is considered legacy code, which
|
||||
* will probably be removed at some point in the future.
|
||||
*/
|
||||
if (check_amd_hwpstate_cpu(0)) {
|
||||
struct freq_attr **iter;
|
||||
|
||||
pr_debug("adding sysfs entry for cpb\n");
|
||||
|
||||
for (iter = acpi_cpufreq_attr; *iter != NULL; iter++)
|
||||
;
|
||||
|
||||
/* make sure there is a terminator behind it */
|
||||
if (iter[1] == NULL)
|
||||
*iter = &cpb;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = cpufreq_register_driver(&acpi_cpufreq_driver);
|
||||
if (ret)
|
||||
free_acpi_perf_data();
|
||||
else
|
||||
acpi_cpufreq_boost_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -757,6 +1015,8 @@ static void __exit acpi_cpufreq_exit(void)
|
|||
{
|
||||
pr_debug("acpi_cpufreq_exit\n");
|
||||
|
||||
acpi_cpufreq_boost_exit();
|
||||
|
||||
cpufreq_unregister_driver(&acpi_cpufreq_driver);
|
||||
|
||||
free_acpi_perf_data();
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The OPP code in function cpu0_set_target() is reused from
|
||||
* drivers/cpufreq/omap-cpufreq.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/opp.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static unsigned int transition_latency;
|
||||
static unsigned int voltage_tolerance; /* in percentage */
|
||||
|
||||
static struct device *cpu_dev;
|
||||
static struct clk *cpu_clk;
|
||||
static struct regulator *cpu_reg;
|
||||
static struct cpufreq_frequency_table *freq_table;
|
||||
|
||||
static int cpu0_verify_speed(struct cpufreq_policy *policy)
|
||||
{
|
||||
return cpufreq_frequency_table_verify(policy, freq_table);
|
||||
}
|
||||
|
||||
static unsigned int cpu0_get_speed(unsigned int cpu)
|
||||
{
|
||||
return clk_get_rate(cpu_clk) / 1000;
|
||||
}
|
||||
|
||||
static int cpu0_set_target(struct cpufreq_policy *policy,
|
||||
unsigned int target_freq, unsigned int relation)
|
||||
{
|
||||
struct cpufreq_freqs freqs;
|
||||
struct opp *opp;
|
||||
unsigned long freq_Hz, volt = 0, volt_old = 0, tol = 0;
|
||||
unsigned int index, cpu;
|
||||
int ret;
|
||||
|
||||
ret = cpufreq_frequency_table_target(policy, freq_table, target_freq,
|
||||
relation, &index);
|
||||
if (ret) {
|
||||
pr_err("failed to match target freqency %d: %d\n",
|
||||
target_freq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
|
||||
if (freq_Hz < 0)
|
||||
freq_Hz = freq_table[index].frequency * 1000;
|
||||
freqs.new = freq_Hz / 1000;
|
||||
freqs.old = clk_get_rate(cpu_clk) / 1000;
|
||||
|
||||
if (freqs.old == freqs.new)
|
||||
return 0;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
freqs.cpu = cpu;
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||
}
|
||||
|
||||
if (cpu_reg) {
|
||||
opp = opp_find_freq_ceil(cpu_dev, &freq_Hz);
|
||||
if (IS_ERR(opp)) {
|
||||
pr_err("failed to find OPP for %ld\n", freq_Hz);
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
volt = opp_get_voltage(opp);
|
||||
tol = volt * voltage_tolerance / 100;
|
||||
volt_old = regulator_get_voltage(cpu_reg);
|
||||
}
|
||||
|
||||
pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n",
|
||||
freqs.old / 1000, volt_old ? volt_old / 1000 : -1,
|
||||
freqs.new / 1000, volt ? volt / 1000 : -1);
|
||||
|
||||
/* scaling up? scale voltage before frequency */
|
||||
if (cpu_reg && freqs.new > freqs.old) {
|
||||
ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
|
||||
if (ret) {
|
||||
pr_err("failed to scale voltage up: %d\n", ret);
|
||||
freqs.new = freqs.old;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_set_rate(cpu_clk, freqs.new * 1000);
|
||||
if (ret) {
|
||||
pr_err("failed to set clock rate: %d\n", ret);
|
||||
if (cpu_reg)
|
||||
regulator_set_voltage_tol(cpu_reg, volt_old, tol);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* scaling down? scale voltage after frequency */
|
||||
if (cpu_reg && freqs.new < freqs.old) {
|
||||
ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
|
||||
if (ret) {
|
||||
pr_err("failed to scale voltage down: %d\n", ret);
|
||||
clk_set_rate(cpu_clk, freqs.old * 1000);
|
||||
freqs.new = freqs.old;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
freqs.cpu = cpu;
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpu0_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (policy->cpu != 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
||||
if (ret) {
|
||||
pr_err("invalid frequency table: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
policy->cpuinfo.transition_latency = transition_latency;
|
||||
policy->cur = clk_get_rate(cpu_clk) / 1000;
|
||||
|
||||
/*
|
||||
* The driver only supports the SMP configuartion where all processors
|
||||
* share the clock and voltage and clock. Use cpufreq affected_cpus
|
||||
* interface to have all CPUs scaled together.
|
||||
*/
|
||||
policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
|
||||
cpumask_setall(policy->cpus);
|
||||
|
||||
cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpu0_cpufreq_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
cpufreq_frequency_table_put_attr(policy->cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct freq_attr *cpu0_cpufreq_attr[] = {
|
||||
&cpufreq_freq_attr_scaling_available_freqs,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct cpufreq_driver cpu0_cpufreq_driver = {
|
||||
.flags = CPUFREQ_STICKY,
|
||||
.verify = cpu0_verify_speed,
|
||||
.target = cpu0_set_target,
|
||||
.get = cpu0_get_speed,
|
||||
.init = cpu0_cpufreq_init,
|
||||
.exit = cpu0_cpufreq_exit,
|
||||
.name = "generic_cpu0",
|
||||
.attr = cpu0_cpufreq_attr,
|
||||
};
|
||||
|
||||
static int __devinit cpu0_cpufreq_driver_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
np = of_find_node_by_path("/cpus/cpu@0");
|
||||
if (!np) {
|
||||
pr_err("failed to find cpu0 node\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
cpu_dev = get_cpu_device(0);
|
||||
if (!cpu_dev) {
|
||||
pr_err("failed to get cpu0 device\n");
|
||||
ret = -ENODEV;
|
||||
goto out_put_node;
|
||||
}
|
||||
|
||||
cpu_dev->of_node = np;
|
||||
|
||||
cpu_clk = clk_get(cpu_dev, NULL);
|
||||
if (IS_ERR(cpu_clk)) {
|
||||
ret = PTR_ERR(cpu_clk);
|
||||
pr_err("failed to get cpu0 clock: %d\n", ret);
|
||||
goto out_put_node;
|
||||
}
|
||||
|
||||
cpu_reg = regulator_get(cpu_dev, "cpu0");
|
||||
if (IS_ERR(cpu_reg)) {
|
||||
pr_warn("failed to get cpu0 regulator\n");
|
||||
cpu_reg = NULL;
|
||||
}
|
||||
|
||||
ret = of_init_opp_table(cpu_dev);
|
||||
if (ret) {
|
||||
pr_err("failed to init OPP table: %d\n", ret);
|
||||
goto out_put_node;
|
||||
}
|
||||
|
||||
ret = opp_init_cpufreq_table(cpu_dev, &freq_table);
|
||||
if (ret) {
|
||||
pr_err("failed to init cpufreq table: %d\n", ret);
|
||||
goto out_put_node;
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance);
|
||||
|
||||
if (of_property_read_u32(np, "clock-latency", &transition_latency))
|
||||
transition_latency = CPUFREQ_ETERNAL;
|
||||
|
||||
if (cpu_reg) {
|
||||
struct opp *opp;
|
||||
unsigned long min_uV, max_uV;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* OPP is maintained in order of increasing frequency, and
|
||||
* freq_table initialised from OPP is therefore sorted in the
|
||||
* same order.
|
||||
*/
|
||||
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
|
||||
;
|
||||
opp = opp_find_freq_exact(cpu_dev,
|
||||
freq_table[0].frequency * 1000, true);
|
||||
min_uV = opp_get_voltage(opp);
|
||||
opp = opp_find_freq_exact(cpu_dev,
|
||||
freq_table[i-1].frequency * 1000, true);
|
||||
max_uV = opp_get_voltage(opp);
|
||||
ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
|
||||
if (ret > 0)
|
||||
transition_latency += ret * 1000;
|
||||
}
|
||||
|
||||
ret = cpufreq_register_driver(&cpu0_cpufreq_driver);
|
||||
if (ret) {
|
||||
pr_err("failed register driver: %d\n", ret);
|
||||
goto out_free_table;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
|
||||
out_free_table:
|
||||
opp_free_cpufreq_table(cpu_dev, &freq_table);
|
||||
out_put_node:
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
late_initcall(cpu0_cpufreq_driver_init);
|
||||
|
||||
MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
|
||||
MODULE_DESCRIPTION("Generic CPU0 cpufreq driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -504,6 +504,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
|
|||
j_dbs_info->prev_cpu_nice =
|
||||
kcpustat_cpu(j).cpustat[CPUTIME_NICE];
|
||||
}
|
||||
this_dbs_info->cpu = cpu;
|
||||
this_dbs_info->down_skip = 0;
|
||||
this_dbs_info->requested_freq = policy->cur;
|
||||
|
||||
|
@ -583,6 +584,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
|
|||
__cpufreq_driver_target(
|
||||
this_dbs_info->cur_policy,
|
||||
policy->min, CPUFREQ_RELATION_L);
|
||||
dbs_check_cpu(this_dbs_info);
|
||||
mutex_unlock(&this_dbs_info->timer_mutex);
|
||||
|
||||
break;
|
||||
|
|
|
@ -761,6 +761,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
|
|||
else if (policy->min > this_dbs_info->cur_policy->cur)
|
||||
__cpufreq_driver_target(this_dbs_info->cur_policy,
|
||||
policy->min, CPUFREQ_RELATION_L);
|
||||
dbs_check_cpu(this_dbs_info);
|
||||
mutex_unlock(&this_dbs_info->timer_mutex);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ union msr_longhaul {
|
|||
/*
|
||||
* VIA C3 Samuel 1 & Samuel 2 (stepping 0)
|
||||
*/
|
||||
static const int __cpuinitdata samuel1_mults[16] = {
|
||||
static const int __cpuinitconst samuel1_mults[16] = {
|
||||
-1, /* 0000 -> RESERVED */
|
||||
30, /* 0001 -> 3.0x */
|
||||
40, /* 0010 -> 4.0x */
|
||||
|
@ -75,7 +75,7 @@ static const int __cpuinitdata samuel1_mults[16] = {
|
|||
-1, /* 1111 -> RESERVED */
|
||||
};
|
||||
|
||||
static const int __cpuinitdata samuel1_eblcr[16] = {
|
||||
static const int __cpuinitconst samuel1_eblcr[16] = {
|
||||
50, /* 0000 -> RESERVED */
|
||||
30, /* 0001 -> 3.0x */
|
||||
40, /* 0010 -> 4.0x */
|
||||
|
@ -97,7 +97,7 @@ static const int __cpuinitdata samuel1_eblcr[16] = {
|
|||
/*
|
||||
* VIA C3 Samuel2 Stepping 1->15
|
||||
*/
|
||||
static const int __cpuinitdata samuel2_eblcr[16] = {
|
||||
static const int __cpuinitconst samuel2_eblcr[16] = {
|
||||
50, /* 0000 -> 5.0x */
|
||||
30, /* 0001 -> 3.0x */
|
||||
40, /* 0010 -> 4.0x */
|
||||
|
@ -119,7 +119,7 @@ static const int __cpuinitdata samuel2_eblcr[16] = {
|
|||
/*
|
||||
* VIA C3 Ezra
|
||||
*/
|
||||
static const int __cpuinitdata ezra_mults[16] = {
|
||||
static const int __cpuinitconst ezra_mults[16] = {
|
||||
100, /* 0000 -> 10.0x */
|
||||
30, /* 0001 -> 3.0x */
|
||||
40, /* 0010 -> 4.0x */
|
||||
|
@ -138,7 +138,7 @@ static const int __cpuinitdata ezra_mults[16] = {
|
|||
120, /* 1111 -> 12.0x */
|
||||
};
|
||||
|
||||
static const int __cpuinitdata ezra_eblcr[16] = {
|
||||
static const int __cpuinitconst ezra_eblcr[16] = {
|
||||
50, /* 0000 -> 5.0x */
|
||||
30, /* 0001 -> 3.0x */
|
||||
40, /* 0010 -> 4.0x */
|
||||
|
@ -160,7 +160,7 @@ static const int __cpuinitdata ezra_eblcr[16] = {
|
|||
/*
|
||||
* VIA C3 (Ezra-T) [C5M].
|
||||
*/
|
||||
static const int __cpuinitdata ezrat_mults[32] = {
|
||||
static const int __cpuinitconst ezrat_mults[32] = {
|
||||
100, /* 0000 -> 10.0x */
|
||||
30, /* 0001 -> 3.0x */
|
||||
40, /* 0010 -> 4.0x */
|
||||
|
@ -196,7 +196,7 @@ static const int __cpuinitdata ezrat_mults[32] = {
|
|||
-1, /* 1111 -> RESERVED (12.0x) */
|
||||
};
|
||||
|
||||
static const int __cpuinitdata ezrat_eblcr[32] = {
|
||||
static const int __cpuinitconst ezrat_eblcr[32] = {
|
||||
50, /* 0000 -> 5.0x */
|
||||
30, /* 0001 -> 3.0x */
|
||||
40, /* 0010 -> 4.0x */
|
||||
|
@ -235,7 +235,7 @@ static const int __cpuinitdata ezrat_eblcr[32] = {
|
|||
/*
|
||||
* VIA C3 Nehemiah */
|
||||
|
||||
static const int __cpuinitdata nehemiah_mults[32] = {
|
||||
static const int __cpuinitconst nehemiah_mults[32] = {
|
||||
100, /* 0000 -> 10.0x */
|
||||
-1, /* 0001 -> 16.0x */
|
||||
40, /* 0010 -> 4.0x */
|
||||
|
@ -270,7 +270,7 @@ static const int __cpuinitdata nehemiah_mults[32] = {
|
|||
-1, /* 1111 -> 12.0x */
|
||||
};
|
||||
|
||||
static const int __cpuinitdata nehemiah_eblcr[32] = {
|
||||
static const int __cpuinitconst nehemiah_eblcr[32] = {
|
||||
50, /* 0000 -> 5.0x */
|
||||
160, /* 0001 -> 16.0x */
|
||||
40, /* 0010 -> 4.0x */
|
||||
|
@ -315,7 +315,7 @@ struct mV_pos {
|
|||
unsigned short pos;
|
||||
};
|
||||
|
||||
static const struct mV_pos __cpuinitdata vrm85_mV[32] = {
|
||||
static const struct mV_pos __cpuinitconst vrm85_mV[32] = {
|
||||
{1250, 8}, {1200, 6}, {1150, 4}, {1100, 2},
|
||||
{1050, 0}, {1800, 30}, {1750, 28}, {1700, 26},
|
||||
{1650, 24}, {1600, 22}, {1550, 20}, {1500, 18},
|
||||
|
@ -326,14 +326,14 @@ static const struct mV_pos __cpuinitdata vrm85_mV[32] = {
|
|||
{1475, 17}, {1425, 15}, {1375, 13}, {1325, 11}
|
||||
};
|
||||
|
||||
static const unsigned char __cpuinitdata mV_vrm85[32] = {
|
||||
static const unsigned char __cpuinitconst mV_vrm85[32] = {
|
||||
0x04, 0x14, 0x03, 0x13, 0x02, 0x12, 0x01, 0x11,
|
||||
0x00, 0x10, 0x0f, 0x1f, 0x0e, 0x1e, 0x0d, 0x1d,
|
||||
0x0c, 0x1c, 0x0b, 0x1b, 0x0a, 0x1a, 0x09, 0x19,
|
||||
0x08, 0x18, 0x07, 0x17, 0x06, 0x16, 0x05, 0x15
|
||||
};
|
||||
|
||||
static const struct mV_pos __cpuinitdata mobilevrm_mV[32] = {
|
||||
static const struct mV_pos __cpuinitconst mobilevrm_mV[32] = {
|
||||
{1750, 31}, {1700, 30}, {1650, 29}, {1600, 28},
|
||||
{1550, 27}, {1500, 26}, {1450, 25}, {1400, 24},
|
||||
{1350, 23}, {1300, 22}, {1250, 21}, {1200, 20},
|
||||
|
@ -344,7 +344,7 @@ static const struct mV_pos __cpuinitdata mobilevrm_mV[32] = {
|
|||
{675, 3}, {650, 2}, {625, 1}, {600, 0}
|
||||
};
|
||||
|
||||
static const unsigned char __cpuinitdata mV_mobilevrm[32] = {
|
||||
static const unsigned char __cpuinitconst mV_mobilevrm[32] = {
|
||||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
|
|
|
@ -40,16 +40,6 @@
|
|||
/* OPP tolerance in percentage */
|
||||
#define OPP_TOLERANCE 4
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
struct lpj_info {
|
||||
unsigned long ref;
|
||||
unsigned int freq;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct lpj_info, lpj_ref);
|
||||
static struct lpj_info global_lpj_ref;
|
||||
#endif
|
||||
|
||||
static struct cpufreq_frequency_table *freq_table;
|
||||
static atomic_t freq_table_users = ATOMIC_INIT(0);
|
||||
static struct clk *mpu_clk;
|
||||
|
@ -161,31 +151,6 @@ static int omap_target(struct cpufreq_policy *policy,
|
|||
}
|
||||
|
||||
freqs.new = omap_getspeed(policy->cpu);
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* Note that loops_per_jiffy is not updated on SMP systems in
|
||||
* cpufreq driver. So, update the per-CPU loops_per_jiffy value
|
||||
* on frequency transition. We need to update all dependent CPUs.
|
||||
*/
|
||||
for_each_cpu(i, policy->cpus) {
|
||||
struct lpj_info *lpj = &per_cpu(lpj_ref, i);
|
||||
if (!lpj->freq) {
|
||||
lpj->ref = per_cpu(cpu_data, i).loops_per_jiffy;
|
||||
lpj->freq = freqs.old;
|
||||
}
|
||||
|
||||
per_cpu(cpu_data, i).loops_per_jiffy =
|
||||
cpufreq_scale(lpj->ref, lpj->freq, freqs.new);
|
||||
}
|
||||
|
||||
/* And don't forget to adjust the global one */
|
||||
if (!global_lpj_ref.freq) {
|
||||
global_lpj_ref.ref = loops_per_jiffy;
|
||||
global_lpj_ref.freq = freqs.old;
|
||||
}
|
||||
loops_per_jiffy = cpufreq_scale(global_lpj_ref.ref, global_lpj_ref.freq,
|
||||
freqs.new);
|
||||
#endif
|
||||
|
||||
done:
|
||||
/* notifiers */
|
||||
|
|
|
@ -49,22 +49,12 @@
|
|||
#define PFX "powernow-k8: "
|
||||
#define VERSION "version 2.20.00"
|
||||
#include "powernow-k8.h"
|
||||
#include "mperf.h"
|
||||
|
||||
/* serialize freq changes */
|
||||
static DEFINE_MUTEX(fidvid_mutex);
|
||||
|
||||
static DEFINE_PER_CPU(struct powernow_k8_data *, powernow_data);
|
||||
|
||||
static int cpu_family = CPU_OPTERON;
|
||||
|
||||
/* array to map SW pstate number to acpi state */
|
||||
static u32 ps_to_as[8];
|
||||
|
||||
/* core performance boost */
|
||||
static bool cpb_capable, cpb_enabled;
|
||||
static struct msr __percpu *msrs;
|
||||
|
||||
static struct cpufreq_driver cpufreq_amd64_driver;
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
|
@ -86,12 +76,6 @@ static u32 find_khz_freq_from_fid(u32 fid)
|
|||
return 1000 * find_freq_from_fid(fid);
|
||||
}
|
||||
|
||||
static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data,
|
||||
u32 pstate)
|
||||
{
|
||||
return data[ps_to_as[pstate]].frequency;
|
||||
}
|
||||
|
||||
/* Return the vco fid for an input fid
|
||||
*
|
||||
* Each "low" fid has corresponding "high" fid, and you can get to "low" fids
|
||||
|
@ -114,9 +98,6 @@ static int pending_bit_stuck(void)
|
|||
{
|
||||
u32 lo, hi;
|
||||
|
||||
if (cpu_family == CPU_HW_PSTATE)
|
||||
return 0;
|
||||
|
||||
rdmsr(MSR_FIDVID_STATUS, lo, hi);
|
||||
return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
|
||||
}
|
||||
|
@ -130,20 +111,6 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
|
|||
u32 lo, hi;
|
||||
u32 i = 0;
|
||||
|
||||
if (cpu_family == CPU_HW_PSTATE) {
|
||||
rdmsr(MSR_PSTATE_STATUS, lo, hi);
|
||||
i = lo & HW_PSTATE_MASK;
|
||||
data->currpstate = i;
|
||||
|
||||
/*
|
||||
* a workaround for family 11h erratum 311 might cause
|
||||
* an "out-of-range Pstate if the core is in Pstate-0
|
||||
*/
|
||||
if ((boot_cpu_data.x86 == 0x11) && (i >= data->numps))
|
||||
data->currpstate = HW_PSTATE_0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
do {
|
||||
if (i++ > 10000) {
|
||||
pr_debug("detected change pending stuck\n");
|
||||
|
@ -300,14 +267,6 @@ static int decrease_vid_code_by_step(struct powernow_k8_data *data,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Change hardware pstate by single MSR write */
|
||||
static int transition_pstate(struct powernow_k8_data *data, u32 pstate)
|
||||
{
|
||||
wrmsr(MSR_PSTATE_CTRL, pstate, 0);
|
||||
data->currpstate = pstate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Change Opteron/Athlon64 fid and vid, by the 3 phases. */
|
||||
static int transition_fid_vid(struct powernow_k8_data *data,
|
||||
u32 reqfid, u32 reqvid)
|
||||
|
@ -524,8 +483,6 @@ static int core_voltage_post_transition(struct powernow_k8_data *data,
|
|||
static const struct x86_cpu_id powernow_k8_ids[] = {
|
||||
/* IO based frequency switching */
|
||||
{ X86_VENDOR_AMD, 0xf },
|
||||
/* MSR based frequency switching supported */
|
||||
X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, powernow_k8_ids);
|
||||
|
@ -561,15 +518,8 @@ static void check_supported_cpu(void *_rc)
|
|||
"Power state transitions not supported\n");
|
||||
return;
|
||||
}
|
||||
} else { /* must be a HW Pstate capable processor */
|
||||
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
|
||||
if ((edx & USE_HW_PSTATE) == USE_HW_PSTATE)
|
||||
cpu_family = CPU_HW_PSTATE;
|
||||
else
|
||||
return;
|
||||
*rc = 0;
|
||||
}
|
||||
|
||||
*rc = 0;
|
||||
}
|
||||
|
||||
static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst,
|
||||
|
@ -633,18 +583,11 @@ static void print_basics(struct powernow_k8_data *data)
|
|||
for (j = 0; j < data->numps; j++) {
|
||||
if (data->powernow_table[j].frequency !=
|
||||
CPUFREQ_ENTRY_INVALID) {
|
||||
if (cpu_family == CPU_HW_PSTATE) {
|
||||
printk(KERN_INFO PFX
|
||||
" %d : pstate %d (%d MHz)\n", j,
|
||||
data->powernow_table[j].index,
|
||||
data->powernow_table[j].frequency/1000);
|
||||
} else {
|
||||
printk(KERN_INFO PFX
|
||||
"fid 0x%x (%d MHz), vid 0x%x\n",
|
||||
data->powernow_table[j].index & 0xff,
|
||||
data->powernow_table[j].frequency/1000,
|
||||
data->powernow_table[j].index >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data->batps)
|
||||
|
@ -652,20 +595,6 @@ static void print_basics(struct powernow_k8_data *data)
|
|||
data->batps);
|
||||
}
|
||||
|
||||
static u32 freq_from_fid_did(u32 fid, u32 did)
|
||||
{
|
||||
u32 mhz = 0;
|
||||
|
||||
if (boot_cpu_data.x86 == 0x10)
|
||||
mhz = (100 * (fid + 0x10)) >> did;
|
||||
else if (boot_cpu_data.x86 == 0x11)
|
||||
mhz = (100 * (fid + 8)) >> did;
|
||||
else
|
||||
BUG();
|
||||
|
||||
return mhz * 1000;
|
||||
}
|
||||
|
||||
static int fill_powernow_table(struct powernow_k8_data *data,
|
||||
struct pst_s *pst, u8 maxvid)
|
||||
{
|
||||
|
@ -825,7 +754,7 @@ static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data,
|
|||
{
|
||||
u64 control;
|
||||
|
||||
if (!data->acpi_data.state_count || (cpu_family == CPU_HW_PSTATE))
|
||||
if (!data->acpi_data.state_count)
|
||||
return;
|
||||
|
||||
control = data->acpi_data.states[index].control;
|
||||
|
@ -876,10 +805,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
|
|||
data->numps = data->acpi_data.state_count;
|
||||
powernow_k8_acpi_pst_values(data, 0);
|
||||
|
||||
if (cpu_family == CPU_HW_PSTATE)
|
||||
ret_val = fill_powernow_table_pstate(data, powernow_table);
|
||||
else
|
||||
ret_val = fill_powernow_table_fidvid(data, powernow_table);
|
||||
ret_val = fill_powernow_table_fidvid(data, powernow_table);
|
||||
if (ret_val)
|
||||
goto err_out_mem;
|
||||
|
||||
|
@ -916,51 +842,6 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
|
|||
return ret_val;
|
||||
}
|
||||
|
||||
static int fill_powernow_table_pstate(struct powernow_k8_data *data,
|
||||
struct cpufreq_frequency_table *powernow_table)
|
||||
{
|
||||
int i;
|
||||
u32 hi = 0, lo = 0;
|
||||
rdmsr(MSR_PSTATE_CUR_LIMIT, lo, hi);
|
||||
data->max_hw_pstate = (lo & HW_PSTATE_MAX_MASK) >> HW_PSTATE_MAX_SHIFT;
|
||||
|
||||
for (i = 0; i < data->acpi_data.state_count; i++) {
|
||||
u32 index;
|
||||
|
||||
index = data->acpi_data.states[i].control & HW_PSTATE_MASK;
|
||||
if (index > data->max_hw_pstate) {
|
||||
printk(KERN_ERR PFX "invalid pstate %d - "
|
||||
"bad value %d.\n", i, index);
|
||||
printk(KERN_ERR PFX "Please report to BIOS "
|
||||
"manufacturer\n");
|
||||
invalidate_entry(powernow_table, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
ps_to_as[index] = i;
|
||||
|
||||
/* Frequency may be rounded for these */
|
||||
if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10)
|
||||
|| boot_cpu_data.x86 == 0x11) {
|
||||
|
||||
rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi);
|
||||
if (!(hi & HW_PSTATE_VALID_MASK)) {
|
||||
pr_debug("invalid pstate %d, ignoring\n", index);
|
||||
invalidate_entry(powernow_table, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
powernow_table[i].frequency =
|
||||
freq_from_fid_did(lo & 0x3f, (lo >> 6) & 7);
|
||||
} else
|
||||
powernow_table[i].frequency =
|
||||
data->acpi_data.states[i].core_frequency * 1000;
|
||||
|
||||
powernow_table[i].index = index;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fill_powernow_table_fidvid(struct powernow_k8_data *data,
|
||||
struct cpufreq_frequency_table *powernow_table)
|
||||
{
|
||||
|
@ -1037,15 +918,7 @@ static int get_transition_latency(struct powernow_k8_data *data)
|
|||
max_latency = cur_latency;
|
||||
}
|
||||
if (max_latency == 0) {
|
||||
/*
|
||||
* Fam 11h and later may return 0 as transition latency. This
|
||||
* is intended and means "very fast". While cpufreq core and
|
||||
* governors currently can handle that gracefully, better set it
|
||||
* to 1 to avoid problems in the future.
|
||||
*/
|
||||
if (boot_cpu_data.x86 < 0x11)
|
||||
printk(KERN_ERR FW_WARN PFX "Invalid zero transition "
|
||||
"latency\n");
|
||||
pr_err(FW_WARN PFX "Invalid zero transition latency\n");
|
||||
max_latency = 1;
|
||||
}
|
||||
/* value in usecs, needs to be in nanoseconds */
|
||||
|
@ -1105,40 +978,6 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data,
|
|||
return res;
|
||||
}
|
||||
|
||||
/* Take a frequency, and issue the hardware pstate transition command */
|
||||
static int transition_frequency_pstate(struct powernow_k8_data *data,
|
||||
unsigned int index)
|
||||
{
|
||||
u32 pstate = 0;
|
||||
int res, i;
|
||||
struct cpufreq_freqs freqs;
|
||||
|
||||
pr_debug("cpu %d transition to index %u\n", smp_processor_id(), index);
|
||||
|
||||
/* get MSR index for hardware pstate transition */
|
||||
pstate = index & HW_PSTATE_MASK;
|
||||
if (pstate > data->max_hw_pstate)
|
||||
return -EINVAL;
|
||||
|
||||
freqs.old = find_khz_freq_from_pstate(data->powernow_table,
|
||||
data->currpstate);
|
||||
freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
|
||||
|
||||
for_each_cpu(i, data->available_cores) {
|
||||
freqs.cpu = i;
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||
}
|
||||
|
||||
res = transition_pstate(data, pstate);
|
||||
freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
|
||||
|
||||
for_each_cpu(i, data->available_cores) {
|
||||
freqs.cpu = i;
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver entry point to switch to the target frequency */
|
||||
static int powernowk8_target(struct cpufreq_policy *pol,
|
||||
unsigned targfreq, unsigned relation)
|
||||
|
@ -1180,18 +1019,15 @@ static int powernowk8_target(struct cpufreq_policy *pol,
|
|||
if (query_current_values_with_pending_wait(data))
|
||||
goto err_out;
|
||||
|
||||
if (cpu_family != CPU_HW_PSTATE) {
|
||||
pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
|
||||
data->currfid, data->currvid);
|
||||
pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
|
||||
data->currfid, data->currvid);
|
||||
|
||||
if ((checkvid != data->currvid) ||
|
||||
(checkfid != data->currfid)) {
|
||||
printk(KERN_INFO PFX
|
||||
"error - out of sync, fix 0x%x 0x%x, "
|
||||
"vid 0x%x 0x%x\n",
|
||||
checkfid, data->currfid,
|
||||
checkvid, data->currvid);
|
||||
}
|
||||
if ((checkvid != data->currvid) ||
|
||||
(checkfid != data->currfid)) {
|
||||
pr_info(PFX
|
||||
"error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
|
||||
checkfid, data->currfid,
|
||||
checkvid, data->currvid);
|
||||
}
|
||||
|
||||
if (cpufreq_frequency_table_target(pol, data->powernow_table,
|
||||
|
@ -1202,11 +1038,8 @@ static int powernowk8_target(struct cpufreq_policy *pol,
|
|||
|
||||
powernow_k8_acpi_pst_values(data, newstate);
|
||||
|
||||
if (cpu_family == CPU_HW_PSTATE)
|
||||
ret = transition_frequency_pstate(data,
|
||||
data->powernow_table[newstate].index);
|
||||
else
|
||||
ret = transition_frequency_fidvid(data, newstate);
|
||||
ret = transition_frequency_fidvid(data, newstate);
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_ERR PFX "transition frequency failed\n");
|
||||
ret = 1;
|
||||
|
@ -1215,11 +1048,7 @@ static int powernowk8_target(struct cpufreq_policy *pol,
|
|||
}
|
||||
mutex_unlock(&fidvid_mutex);
|
||||
|
||||
if (cpu_family == CPU_HW_PSTATE)
|
||||
pol->cur = find_khz_freq_from_pstate(data->powernow_table,
|
||||
data->powernow_table[newstate].index);
|
||||
else
|
||||
pol->cur = find_khz_freq_from_fid(data->currfid);
|
||||
pol->cur = find_khz_freq_from_fid(data->currfid);
|
||||
ret = 0;
|
||||
|
||||
err_out:
|
||||
|
@ -1259,22 +1088,23 @@ static void __cpuinit powernowk8_cpu_init_on_cpu(void *_init_on_cpu)
|
|||
return;
|
||||
}
|
||||
|
||||
if (cpu_family == CPU_OPTERON)
|
||||
fidvid_msr_init();
|
||||
fidvid_msr_init();
|
||||
|
||||
init_on_cpu->rc = 0;
|
||||
}
|
||||
|
||||
static const char missing_pss_msg[] =
|
||||
KERN_ERR
|
||||
FW_BUG PFX "No compatible ACPI _PSS objects found.\n"
|
||||
FW_BUG PFX "First, make sure Cool'N'Quiet is enabled in the BIOS.\n"
|
||||
FW_BUG PFX "If that doesn't help, try upgrading your BIOS.\n";
|
||||
|
||||
/* per CPU init entry point to the driver */
|
||||
static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
||||
{
|
||||
static const char ACPI_PSS_BIOS_BUG_MSG[] =
|
||||
KERN_ERR FW_BUG PFX "No compatible ACPI _PSS objects found.\n"
|
||||
FW_BUG PFX "Try again with latest BIOS.\n";
|
||||
struct powernow_k8_data *data;
|
||||
struct init_on_cpu init_on_cpu;
|
||||
int rc;
|
||||
struct cpuinfo_x86 *c = &cpu_data(pol->cpu);
|
||||
|
||||
if (!cpu_online(pol->cpu))
|
||||
return -ENODEV;
|
||||
|
@ -1290,7 +1120,6 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|||
}
|
||||
|
||||
data->cpu = pol->cpu;
|
||||
data->currpstate = HW_PSTATE_INVALID;
|
||||
|
||||
if (powernow_k8_cpu_init_acpi(data)) {
|
||||
/*
|
||||
|
@ -1298,7 +1127,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|||
* an UP version, and is deprecated by AMD.
|
||||
*/
|
||||
if (num_online_cpus() != 1) {
|
||||
printk_once(ACPI_PSS_BIOS_BUG_MSG);
|
||||
printk_once(missing_pss_msg);
|
||||
goto err_out;
|
||||
}
|
||||
if (pol->cpu != 0) {
|
||||
|
@ -1327,17 +1156,10 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|||
if (rc != 0)
|
||||
goto err_out_exit_acpi;
|
||||
|
||||
if (cpu_family == CPU_HW_PSTATE)
|
||||
cpumask_copy(pol->cpus, cpumask_of(pol->cpu));
|
||||
else
|
||||
cpumask_copy(pol->cpus, cpu_core_mask(pol->cpu));
|
||||
cpumask_copy(pol->cpus, cpu_core_mask(pol->cpu));
|
||||
data->available_cores = pol->cpus;
|
||||
|
||||
if (cpu_family == CPU_HW_PSTATE)
|
||||
pol->cur = find_khz_freq_from_pstate(data->powernow_table,
|
||||
data->currpstate);
|
||||
else
|
||||
pol->cur = find_khz_freq_from_fid(data->currfid);
|
||||
pol->cur = find_khz_freq_from_fid(data->currfid);
|
||||
pr_debug("policy current frequency %d kHz\n", pol->cur);
|
||||
|
||||
/* min/max the cpu is capable of */
|
||||
|
@ -1349,18 +1171,10 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check for APERF/MPERF support in hardware */
|
||||
if (cpu_has(c, X86_FEATURE_APERFMPERF))
|
||||
cpufreq_amd64_driver.getavg = cpufreq_get_measured_perf;
|
||||
|
||||
cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
|
||||
|
||||
if (cpu_family == CPU_HW_PSTATE)
|
||||
pr_debug("cpu_init done, current pstate 0x%x\n",
|
||||
data->currpstate);
|
||||
else
|
||||
pr_debug("cpu_init done, current fid 0x%x, vid 0x%x\n",
|
||||
data->currfid, data->currvid);
|
||||
pr_debug("cpu_init done, current fid 0x%x, vid 0x%x\n",
|
||||
data->currfid, data->currvid);
|
||||
|
||||
per_cpu(powernow_data, pol->cpu) = data;
|
||||
|
||||
|
@ -1413,88 +1227,15 @@ static unsigned int powernowk8_get(unsigned int cpu)
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
if (cpu_family == CPU_HW_PSTATE)
|
||||
khz = find_khz_freq_from_pstate(data->powernow_table,
|
||||
data->currpstate);
|
||||
else
|
||||
khz = find_khz_freq_from_fid(data->currfid);
|
||||
khz = find_khz_freq_from_fid(data->currfid);
|
||||
|
||||
|
||||
out:
|
||||
return khz;
|
||||
}
|
||||
|
||||
static void _cpb_toggle_msrs(bool t)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
|
||||
rdmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs);
|
||||
|
||||
for_each_cpu(cpu, cpu_online_mask) {
|
||||
struct msr *reg = per_cpu_ptr(msrs, cpu);
|
||||
if (t)
|
||||
reg->l &= ~BIT(25);
|
||||
else
|
||||
reg->l |= BIT(25);
|
||||
}
|
||||
wrmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs);
|
||||
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch on/off core performance boosting.
|
||||
*
|
||||
* 0=disable
|
||||
* 1=enable.
|
||||
*/
|
||||
static void cpb_toggle(bool t)
|
||||
{
|
||||
if (!cpb_capable)
|
||||
return;
|
||||
|
||||
if (t && !cpb_enabled) {
|
||||
cpb_enabled = true;
|
||||
_cpb_toggle_msrs(t);
|
||||
printk(KERN_INFO PFX "Core Boosting enabled.\n");
|
||||
} else if (!t && cpb_enabled) {
|
||||
cpb_enabled = false;
|
||||
_cpb_toggle_msrs(t);
|
||||
printk(KERN_INFO PFX "Core Boosting disabled.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
unsigned long val = 0;
|
||||
|
||||
ret = strict_strtoul(buf, 10, &val);
|
||||
if (!ret && (val == 0 || val == 1) && cpb_capable)
|
||||
cpb_toggle(val);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_cpb(struct cpufreq_policy *policy, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n", cpb_enabled);
|
||||
}
|
||||
|
||||
#define define_one_rw(_name) \
|
||||
static struct freq_attr _name = \
|
||||
__ATTR(_name, 0644, show_##_name, store_##_name)
|
||||
|
||||
define_one_rw(cpb);
|
||||
|
||||
static struct freq_attr *powernow_k8_attr[] = {
|
||||
&cpufreq_freq_attr_scaling_available_freqs,
|
||||
&cpb,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -1510,53 +1251,18 @@ static struct cpufreq_driver cpufreq_amd64_driver = {
|
|||
.attr = powernow_k8_attr,
|
||||
};
|
||||
|
||||
/*
|
||||
* Clear the boost-disable flag on the CPU_DOWN path so that this cpu
|
||||
* cannot block the remaining ones from boosting. On the CPU_UP path we
|
||||
* simply keep the boost-disable flag in sync with the current global
|
||||
* state.
|
||||
*/
|
||||
static int cpb_notify(struct notifier_block *nb, unsigned long action,
|
||||
void *hcpu)
|
||||
{
|
||||
unsigned cpu = (long)hcpu;
|
||||
u32 lo, hi;
|
||||
|
||||
switch (action) {
|
||||
case CPU_UP_PREPARE:
|
||||
case CPU_UP_PREPARE_FROZEN:
|
||||
|
||||
if (!cpb_enabled) {
|
||||
rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi);
|
||||
lo |= BIT(25);
|
||||
wrmsr_on_cpu(cpu, MSR_K7_HWCR, lo, hi);
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU_DOWN_PREPARE:
|
||||
case CPU_DOWN_PREPARE_FROZEN:
|
||||
rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi);
|
||||
lo &= ~BIT(25);
|
||||
wrmsr_on_cpu(cpu, MSR_K7_HWCR, lo, hi);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block cpb_nb = {
|
||||
.notifier_call = cpb_notify,
|
||||
};
|
||||
|
||||
/* driver entry point for init */
|
||||
static int __cpuinit powernowk8_init(void)
|
||||
{
|
||||
unsigned int i, supported_cpus = 0, cpu;
|
||||
unsigned int i, supported_cpus = 0;
|
||||
int rv;
|
||||
|
||||
if (static_cpu_has(X86_FEATURE_HW_PSTATE)) {
|
||||
pr_warn(PFX "this CPU is not supported anymore, using acpi-cpufreq instead.\n");
|
||||
request_module("acpi-cpufreq");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!x86_match_cpu(powernow_k8_ids))
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -1570,38 +1276,13 @@ static int __cpuinit powernowk8_init(void)
|
|||
if (supported_cpus != num_online_cpus())
|
||||
return -ENODEV;
|
||||
|
||||
printk(KERN_INFO PFX "Found %d %s (%d cpu cores) (" VERSION ")\n",
|
||||
num_online_nodes(), boot_cpu_data.x86_model_id, supported_cpus);
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_CPB)) {
|
||||
|
||||
cpb_capable = true;
|
||||
|
||||
msrs = msrs_alloc();
|
||||
if (!msrs) {
|
||||
printk(KERN_ERR "%s: Error allocating msrs!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
register_cpu_notifier(&cpb_nb);
|
||||
|
||||
rdmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs);
|
||||
|
||||
for_each_cpu(cpu, cpu_online_mask) {
|
||||
struct msr *reg = per_cpu_ptr(msrs, cpu);
|
||||
cpb_enabled |= !(!!(reg->l & BIT(25)));
|
||||
}
|
||||
|
||||
printk(KERN_INFO PFX "Core Performance Boosting: %s.\n",
|
||||
(cpb_enabled ? "on" : "off"));
|
||||
}
|
||||
|
||||
rv = cpufreq_register_driver(&cpufreq_amd64_driver);
|
||||
if (rv < 0 && boot_cpu_has(X86_FEATURE_CPB)) {
|
||||
unregister_cpu_notifier(&cpb_nb);
|
||||
msrs_free(msrs);
|
||||
msrs = NULL;
|
||||
}
|
||||
|
||||
if (!rv)
|
||||
pr_info(PFX "Found %d %s (%d cpu cores) (" VERSION ")\n",
|
||||
num_online_nodes(), boot_cpu_data.x86_model_id,
|
||||
supported_cpus);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -1610,13 +1291,6 @@ static void __exit powernowk8_exit(void)
|
|||
{
|
||||
pr_debug("exit\n");
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_CPB)) {
|
||||
msrs_free(msrs);
|
||||
msrs = NULL;
|
||||
|
||||
unregister_cpu_notifier(&cpb_nb);
|
||||
}
|
||||
|
||||
cpufreq_unregister_driver(&cpufreq_amd64_driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,24 +5,11 @@
|
|||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
enum pstate {
|
||||
HW_PSTATE_INVALID = 0xff,
|
||||
HW_PSTATE_0 = 0,
|
||||
HW_PSTATE_1 = 1,
|
||||
HW_PSTATE_2 = 2,
|
||||
HW_PSTATE_3 = 3,
|
||||
HW_PSTATE_4 = 4,
|
||||
HW_PSTATE_5 = 5,
|
||||
HW_PSTATE_6 = 6,
|
||||
HW_PSTATE_7 = 7,
|
||||
};
|
||||
|
||||
struct powernow_k8_data {
|
||||
unsigned int cpu;
|
||||
|
||||
u32 numps; /* number of p-states */
|
||||
u32 batps; /* number of p-states supported on battery */
|
||||
u32 max_hw_pstate; /* maximum legal hardware pstate */
|
||||
|
||||
/* these values are constant when the PSB is used to determine
|
||||
* vid/fid pairings, but are modified during the ->target() call
|
||||
|
@ -37,7 +24,6 @@ struct powernow_k8_data {
|
|||
/* keep track of the current fid / vid or pstate */
|
||||
u32 currvid;
|
||||
u32 currfid;
|
||||
enum pstate currpstate;
|
||||
|
||||
/* the powernow_table includes all frequency and vid/fid pairings:
|
||||
* fid are the lower 8 bits of the index, vid are the upper 8 bits.
|
||||
|
@ -97,23 +83,6 @@ struct powernow_k8_data {
|
|||
#define MSR_S_HI_CURRENT_VID 0x0000003f
|
||||
#define MSR_C_HI_STP_GNT_BENIGN 0x00000001
|
||||
|
||||
|
||||
/* Hardware Pstate _PSS and MSR definitions */
|
||||
#define USE_HW_PSTATE 0x00000080
|
||||
#define HW_PSTATE_MASK 0x00000007
|
||||
#define HW_PSTATE_VALID_MASK 0x80000000
|
||||
#define HW_PSTATE_MAX_MASK 0x000000f0
|
||||
#define HW_PSTATE_MAX_SHIFT 4
|
||||
#define MSR_PSTATE_DEF_BASE 0xc0010064 /* base of Pstate MSRs */
|
||||
#define MSR_PSTATE_STATUS 0xc0010063 /* Pstate Status MSR */
|
||||
#define MSR_PSTATE_CTRL 0xc0010062 /* Pstate control MSR */
|
||||
#define MSR_PSTATE_CUR_LIMIT 0xc0010061 /* pstate current limit MSR */
|
||||
|
||||
/* define the two driver architectures */
|
||||
#define CPU_OPTERON 0
|
||||
#define CPU_HW_PSTATE 1
|
||||
|
||||
|
||||
/*
|
||||
* There are restrictions frequencies have to follow:
|
||||
* - only 1 entry in the low fid table ( <=1.4GHz )
|
||||
|
@ -218,5 +187,4 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid);
|
|||
|
||||
static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
|
||||
|
||||
static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
|
||||
static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
|
||||
|
|
|
@ -48,6 +48,14 @@ int opp_disable(struct device *dev, unsigned long freq);
|
|||
|
||||
struct srcu_notifier_head *opp_get_notifier(struct device *dev);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
int of_init_opp_table(struct device *dev);
|
||||
#else
|
||||
static inline int of_init_opp_table(struct device *dev)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
#else
|
||||
static inline unsigned long opp_get_voltage(struct opp *opp)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue