Merge branch 'pm-cpufreq'
* pm-cpufreq: (28 commits) MAINTAINERS: cpufreq: add bmips-cpufreq.c cpufreq: CPPC: add ACPI_PROCESSOR dependency cpufreq: make ti-cpufreq explicitly non-modular cpufreq: Do not clear real_cpus mask on policy init cpufreq: dt: Don't use generic platdev driver for ti-cpufreq platforms cpufreq: ti: Add cpufreq driver to determine available OPPs at runtime Documentation: dt: add bindings for ti-cpufreq cpufreq: qoriq: Don't look at clock implementation details cpufreq: qoriq: add ARM64 SoCs support cpufreq: brcmstb-avs-cpufreq: remove unnecessary platform_set_drvdata() cpufreq: s3c2416: double free on driver init error path MIPS: BMIPS: enable CPUfreq cpufreq: bmips-cpufreq: CPUfreq driver for Broadcom's BMIPS SoCs BMIPS: Enable prerequisites for CPUfreq in MIPS Kconfig. MIPS: BMIPS: Update defconfig cpufreq: Fix typos in comments cpufreq: intel_pstate: Calculate guaranteed performance for HWP cpufreq: intel_pstate: Make HWP limits compatible with legacy cpufreq: intel_pstate: Lower frequency than expected under no_turbo cpufreq: intel_pstate: Operation mode control from sysfs ...
This commit is contained in:
commit
f6cbe34f52
|
@ -8,6 +8,8 @@
|
|||
|
||||
Dominik Brodowski <linux@brodo.de>
|
||||
David Kimdon <dwhedon@debian.org>
|
||||
Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
Viresh Kumar <viresh.kumar@linaro.org>
|
||||
|
||||
|
||||
|
||||
|
@ -36,10 +38,11 @@ speed limits (like LCD drivers on ARM architecture). Additionally, the
|
|||
kernel "constant" loops_per_jiffy is updated on frequency changes
|
||||
here.
|
||||
|
||||
Reference counting is done by cpufreq_get_cpu and cpufreq_put_cpu,
|
||||
which make sure that the cpufreq processor driver is correctly
|
||||
registered with the core, and will not be unloaded until
|
||||
cpufreq_put_cpu is called.
|
||||
Reference counting of the cpufreq policies is done by cpufreq_cpu_get
|
||||
and cpufreq_cpu_put, which make sure that the cpufreq driver is
|
||||
correctly registered with the core, and will not be unloaded until
|
||||
cpufreq_put_cpu is called. That also ensures that the respective cpufreq
|
||||
policy doesn't get freed while being used.
|
||||
|
||||
2. CPUFreq notifiers
|
||||
====================
|
||||
|
@ -69,18 +72,16 @@ CPUFreq policy notifier is called twice for a policy transition:
|
|||
The phase is specified in the second argument to the notifier.
|
||||
|
||||
The third argument, a void *pointer, points to a struct cpufreq_policy
|
||||
consisting of five values: cpu, min, max, policy and max_cpu_freq. min
|
||||
and max are the lower and upper frequencies (in kHz) of the new
|
||||
policy, policy the new policy, cpu the number of the affected CPU; and
|
||||
max_cpu_freq the maximum supported CPU frequency. This value is given
|
||||
for informational purposes only.
|
||||
consisting of several values, including min, max (the lower and upper
|
||||
frequencies (in kHz) of the new policy).
|
||||
|
||||
|
||||
2.2 CPUFreq transition notifiers
|
||||
--------------------------------
|
||||
|
||||
These are notified twice when the CPUfreq driver switches the CPU core
|
||||
frequency and this change has any external implications.
|
||||
These are notified twice for each online CPU in the policy, when the
|
||||
CPUfreq driver switches the CPU core frequency and this change has no
|
||||
any external implications.
|
||||
|
||||
The second argument specifies the phase - CPUFREQ_PRECHANGE or
|
||||
CPUFREQ_POSTCHANGE.
|
||||
|
@ -90,6 +91,7 @@ values:
|
|||
cpu - number of the affected CPU
|
||||
old - old frequency
|
||||
new - new frequency
|
||||
flags - flags of the cpufreq driver
|
||||
|
||||
3. CPUFreq Table Generation with Operating Performance Point (OPP)
|
||||
==================================================================
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
|
||||
Dominik Brodowski <linux@brodo.de>
|
||||
Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
Viresh Kumar <viresh.kumar@linaro.org>
|
||||
|
||||
|
||||
|
||||
|
@ -49,49 +51,65 @@ using cpufreq_register_driver()
|
|||
|
||||
What shall this struct cpufreq_driver contain?
|
||||
|
||||
cpufreq_driver.name - The name of this driver.
|
||||
.name - The name of this driver.
|
||||
|
||||
cpufreq_driver.init - A pointer to the per-CPU initialization
|
||||
function.
|
||||
.init - A pointer to the per-policy initialization function.
|
||||
|
||||
cpufreq_driver.verify - A pointer to a "verification" function.
|
||||
.verify - A pointer to a "verification" function.
|
||||
|
||||
cpufreq_driver.setpolicy _or_
|
||||
cpufreq_driver.target/
|
||||
target_index - See below on the differences.
|
||||
.setpolicy _or_ .fast_switch _or_ .target _or_ .target_index - See
|
||||
below on the differences.
|
||||
|
||||
And optionally
|
||||
|
||||
cpufreq_driver.exit - A pointer to a per-CPU cleanup
|
||||
function called during CPU_POST_DEAD
|
||||
phase of cpu hotplug process.
|
||||
.flags - Hints for the cpufreq core.
|
||||
|
||||
cpufreq_driver.stop_cpu - A pointer to a per-CPU stop function
|
||||
called during CPU_DOWN_PREPARE phase of
|
||||
cpu hotplug process.
|
||||
.driver_data - cpufreq driver specific data.
|
||||
|
||||
cpufreq_driver.resume - A pointer to a per-CPU resume function
|
||||
which is called with interrupts disabled
|
||||
and _before_ the pre-suspend frequency
|
||||
and/or policy is restored by a call to
|
||||
->target/target_index or ->setpolicy.
|
||||
.resolve_freq - Returns the most appropriate frequency for a target
|
||||
frequency. Doesn't change the frequency though.
|
||||
|
||||
cpufreq_driver.attr - A pointer to a NULL-terminated list of
|
||||
"struct freq_attr" which allow to
|
||||
export values to sysfs.
|
||||
.get_intermediate and target_intermediate - Used to switch to stable
|
||||
frequency while changing CPU frequency.
|
||||
|
||||
cpufreq_driver.get_intermediate
|
||||
and target_intermediate Used to switch to stable frequency while
|
||||
changing CPU frequency.
|
||||
.get - Returns current frequency of the CPU.
|
||||
|
||||
.bios_limit - Returns HW/BIOS max frequency limitations for the CPU.
|
||||
|
||||
.exit - A pointer to a per-policy cleanup function called during
|
||||
CPU_POST_DEAD phase of cpu hotplug process.
|
||||
|
||||
.stop_cpu - A pointer to a per-policy stop function called during
|
||||
CPU_DOWN_PREPARE phase of cpu hotplug process.
|
||||
|
||||
.suspend - A pointer to a per-policy suspend function which is called
|
||||
with interrupts disabled and _after_ the governor is stopped for the
|
||||
policy.
|
||||
|
||||
.resume - A pointer to a per-policy resume function which is called
|
||||
with interrupts disabled and _before_ the governor is started again.
|
||||
|
||||
.ready - A pointer to a per-policy ready function which is called after
|
||||
the policy is fully initialized.
|
||||
|
||||
.attr - A pointer to a NULL-terminated list of "struct freq_attr" which
|
||||
allow to export values to sysfs.
|
||||
|
||||
.boost_enabled - If set, boost frequencies are enabled.
|
||||
|
||||
.set_boost - A pointer to a per-policy function to enable/disable boost
|
||||
frequencies.
|
||||
|
||||
|
||||
1.2 Per-CPU Initialization
|
||||
--------------------------
|
||||
|
||||
Whenever a new CPU is registered with the device model, or after the
|
||||
cpufreq driver registers itself, the per-CPU initialization function
|
||||
cpufreq_driver.init is called. It takes a struct cpufreq_policy
|
||||
*policy as argument. What to do now?
|
||||
cpufreq driver registers itself, the per-policy initialization function
|
||||
cpufreq_driver.init is called if no cpufreq policy existed for the CPU.
|
||||
Note that the .init() and .exit() routines are called only once for the
|
||||
policy and not for each CPU managed by the policy. It takes a struct
|
||||
cpufreq_policy *policy as argument. What to do now?
|
||||
|
||||
If necessary, activate the CPUfreq support on your CPU.
|
||||
|
||||
|
@ -117,47 +135,45 @@ policy->governor must contain the "default policy" for
|
|||
cpufreq_driver.setpolicy or
|
||||
cpufreq_driver.target/target_index is called
|
||||
with these values.
|
||||
policy->cpus Update this with the masks of the
|
||||
(online + offline) CPUs that do DVFS
|
||||
along with this CPU (i.e. that share
|
||||
clock/voltage rails with it).
|
||||
|
||||
For setting some of these values (cpuinfo.min[max]_freq, policy->min[max]), the
|
||||
frequency table helpers might be helpful. See the section 2 for more information
|
||||
on them.
|
||||
|
||||
SMP systems normally have same clock source for a group of cpus. For these the
|
||||
.init() would be called only once for the first online cpu. Here the .init()
|
||||
routine must initialize policy->cpus with mask of all possible cpus (Online +
|
||||
Offline) that share the clock. Then the core would copy this mask onto
|
||||
policy->related_cpus and will reset policy->cpus to carry only online cpus.
|
||||
|
||||
|
||||
1.3 verify
|
||||
------------
|
||||
----------
|
||||
|
||||
When the user decides a new policy (consisting of
|
||||
"policy,governor,min,max") shall be set, this policy must be validated
|
||||
so that incompatible values can be corrected. For verifying these
|
||||
values, a frequency table helper and/or the
|
||||
cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned
|
||||
int min_freq, unsigned int max_freq) function might be helpful. See
|
||||
section 2 for details on frequency table helpers.
|
||||
values cpufreq_verify_within_limits(struct cpufreq_policy *policy,
|
||||
unsigned int min_freq, unsigned int max_freq) function might be helpful.
|
||||
See section 2 for details on frequency table helpers.
|
||||
|
||||
You need to make sure that at least one valid frequency (or operating
|
||||
range) is within policy->min and policy->max. If necessary, increase
|
||||
policy->max first, and only if this is no solution, decrease policy->min.
|
||||
|
||||
|
||||
1.4 target/target_index or setpolicy?
|
||||
----------------------------
|
||||
1.4 target or target_index or setpolicy or fast_switch?
|
||||
-------------------------------------------------------
|
||||
|
||||
Most cpufreq drivers or even most cpu frequency scaling algorithms
|
||||
only allow the CPU to be set to one frequency. For these, you use the
|
||||
->target/target_index call.
|
||||
only allow the CPU frequency to be set to predefined fixed values. For
|
||||
these, you use the ->target(), ->target_index() or ->fast_switch()
|
||||
callbacks.
|
||||
|
||||
Some cpufreq-capable processors switch the frequency between certain
|
||||
limits on their own. These shall use the ->setpolicy call
|
||||
Some cpufreq capable processors switch the frequency between certain
|
||||
limits on their own. These shall use the ->setpolicy() callback.
|
||||
|
||||
|
||||
1.5. target/target_index
|
||||
-------------
|
||||
------------------------
|
||||
|
||||
The target_index call has two arguments: struct cpufreq_policy *policy,
|
||||
and unsigned int index (into the exposed frequency table).
|
||||
|
@ -186,9 +202,20 @@ actual frequency must be determined using the following rules:
|
|||
Here again the frequency table helper might assist you - see section 2
|
||||
for details.
|
||||
|
||||
1.6. fast_switch
|
||||
----------------
|
||||
|
||||
1.6 setpolicy
|
||||
---------------
|
||||
This function is used for frequency switching from scheduler's context.
|
||||
Not all drivers are expected to implement it, as sleeping from within
|
||||
this callback isn't allowed. This callback must be highly optimized to
|
||||
do switching as fast as possible.
|
||||
|
||||
This function has two arguments: struct cpufreq_policy *policy and
|
||||
unsigned int target_frequency.
|
||||
|
||||
|
||||
1.7 setpolicy
|
||||
-------------
|
||||
|
||||
The setpolicy call only takes a struct cpufreq_policy *policy as
|
||||
argument. You need to set the lower limit of the in-processor or
|
||||
|
@ -198,7 +225,7 @@ setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a
|
|||
powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
|
||||
the reference implementation in drivers/cpufreq/longrun.c
|
||||
|
||||
1.7 get_intermediate and target_intermediate
|
||||
1.8 get_intermediate and target_intermediate
|
||||
--------------------------------------------
|
||||
|
||||
Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset.
|
||||
|
@ -222,42 +249,36 @@ failures as core would send notifications for that.
|
|||
|
||||
As most cpufreq processors only allow for being set to a few specific
|
||||
frequencies, a "frequency table" with some functions might assist in
|
||||
some work of the processor driver. Such a "frequency table" consists
|
||||
of an array of struct cpufreq_frequency_table entries, with any value in
|
||||
"driver_data" you want to use, and the corresponding frequency in
|
||||
"frequency". At the end of the table, you need to add a
|
||||
cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END. And
|
||||
if you want to skip one entry in the table, set the frequency to
|
||||
CPUFREQ_ENTRY_INVALID. The entries don't need to be in ascending
|
||||
order.
|
||||
some work of the processor driver. Such a "frequency table" consists of
|
||||
an array of struct cpufreq_frequency_table entries, with driver specific
|
||||
values in "driver_data", the corresponding frequency in "frequency" and
|
||||
flags set. At the end of the table, you need to add a
|
||||
cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END.
|
||||
And if you want to skip one entry in the table, set the frequency to
|
||||
CPUFREQ_ENTRY_INVALID. The entries don't need to be in sorted in any
|
||||
particular order, but if they are cpufreq core will do DVFS a bit
|
||||
quickly for them as search for best match is faster.
|
||||
|
||||
By calling cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
|
||||
struct cpufreq_frequency_table *table);
|
||||
the cpuinfo.min_freq and cpuinfo.max_freq values are detected, and
|
||||
policy->min and policy->max are set to the same values. This is
|
||||
helpful for the per-CPU initialization stage.
|
||||
By calling cpufreq_table_validate_and_show(), the cpuinfo.min_freq and
|
||||
cpuinfo.max_freq values are detected, and policy->min and policy->max
|
||||
are set to the same values. This is helpful for the per-CPU
|
||||
initialization stage.
|
||||
|
||||
int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
|
||||
struct cpufreq_frequency_table *table);
|
||||
assures that at least one valid frequency is within policy->min and
|
||||
policy->max, and all other criteria are met. This is helpful for the
|
||||
->verify call.
|
||||
cpufreq_frequency_table_verify() assures that at least one valid
|
||||
frequency is within policy->min and policy->max, and all other criteria
|
||||
are met. This is helpful for the ->verify call.
|
||||
|
||||
int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
|
||||
unsigned int target_freq,
|
||||
unsigned int relation);
|
||||
|
||||
is the corresponding frequency table helper for the ->target
|
||||
stage. Just pass the values to this function, and this function
|
||||
returns the number of the frequency table entry which contains
|
||||
the frequency the CPU shall be set to.
|
||||
cpufreq_frequency_table_target() is the corresponding frequency table
|
||||
helper for the ->target stage. Just pass the values to this function,
|
||||
and this function returns the of the frequency table entry which
|
||||
contains the frequency the CPU shall be set to.
|
||||
|
||||
The following macros can be used as iterators over cpufreq_frequency_table:
|
||||
|
||||
cpufreq_for_each_entry(pos, table) - iterates over all entries of frequency
|
||||
table.
|
||||
|
||||
cpufreq-for_each_valid_entry(pos, table) - iterates over all entries,
|
||||
cpufreq_for_each_valid_entry(pos, table) - iterates over all entries,
|
||||
excluding CPUFREQ_ENTRY_INVALID frequencies.
|
||||
Use arguments "pos" - a cpufreq_frequency_table * as a loop cursor and
|
||||
"table" - the cpufreq_frequency_table * you want to iterate over.
|
||||
|
|
|
@ -34,10 +34,10 @@ cpufreq stats provides following statistics (explained in detail below).
|
|||
- total_trans
|
||||
- trans_table
|
||||
|
||||
All the statistics will be from the time the stats driver has been inserted
|
||||
to the time when a read of a particular statistic is done. Obviously, stats
|
||||
driver will not have any information about the frequency transitions before
|
||||
the stats driver insertion.
|
||||
All the statistics will be from the time the stats driver has been inserted
|
||||
(or the time the stats were reset) to the time when a read of a particular
|
||||
statistic is done. Obviously, stats driver will not have any information
|
||||
about the frequency transitions before the stats driver insertion.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
<mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # ls -l
|
||||
|
@ -110,25 +110,13 @@ Config Main Menu
|
|||
CPU Frequency scaling --->
|
||||
[*] CPU Frequency scaling
|
||||
[*] CPU frequency translation statistics
|
||||
[*] CPU frequency translation statistics details
|
||||
|
||||
|
||||
"CPU Frequency scaling" (CONFIG_CPU_FREQ) should be enabled to configure
|
||||
cpufreq-stats.
|
||||
|
||||
"CPU frequency translation statistics" (CONFIG_CPU_FREQ_STAT) provides the
|
||||
basic statistics which includes time_in_state and total_trans.
|
||||
statistics which includes time_in_state, total_trans and trans_table.
|
||||
|
||||
"CPU frequency translation statistics details" (CONFIG_CPU_FREQ_STAT_DETAILS)
|
||||
provides fine grained cpufreq stats by trans_table. The reason for having a
|
||||
separate config option for trans_table is:
|
||||
- trans_table goes against the traditional /sysfs rule of one value per
|
||||
interface. It provides a whole bunch of value in a 2 dimensional matrix
|
||||
form.
|
||||
|
||||
Once these two options are enabled and your CPU supports cpufrequency, you
|
||||
Once this option is enabled and your CPU supports cpufrequency, you
|
||||
will be able to see the CPU frequency statistics in /sysfs.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
Dominik Brodowski <linux@brodo.de>
|
||||
some additions and corrections by Nico Golde <nico@ngolde.de>
|
||||
Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
Viresh Kumar <viresh.kumar@linaro.org>
|
||||
|
||||
|
||||
|
||||
|
@ -28,32 +30,27 @@ Contents:
|
|||
2.3 Userspace
|
||||
2.4 Ondemand
|
||||
2.5 Conservative
|
||||
2.6 Schedutil
|
||||
|
||||
3. The Governor Interface in the CPUfreq Core
|
||||
|
||||
4. References
|
||||
|
||||
|
||||
1. What Is A CPUFreq Governor?
|
||||
==============================
|
||||
|
||||
Most cpufreq drivers (except the intel_pstate and longrun) or even most
|
||||
cpu frequency scaling algorithms only offer the CPU to be set to one
|
||||
frequency. In order to offer dynamic frequency scaling, the cpufreq
|
||||
core must be able to tell these drivers of a "target frequency". So
|
||||
these specific drivers will be transformed to offer a "->target/target_index"
|
||||
call instead of the existing "->setpolicy" call. For "longrun", all
|
||||
stays the same, though.
|
||||
cpu frequency scaling algorithms only allow the CPU frequency to be set
|
||||
to predefined fixed values. In order to offer dynamic frequency
|
||||
scaling, the cpufreq core must be able to tell these drivers of a
|
||||
"target frequency". So these specific drivers will be transformed to
|
||||
offer a "->target/target_index/fast_switch()" call instead of the
|
||||
"->setpolicy()" call. For set_policy drivers, all stays the same,
|
||||
though.
|
||||
|
||||
How to decide what frequency within the CPUfreq policy should be used?
|
||||
That's done using "cpufreq governors". Two are already in this patch
|
||||
-- they're the already existing "powersave" and "performance" which
|
||||
set the frequency statically to the lowest or highest frequency,
|
||||
respectively. At least two more such governors will be ready for
|
||||
addition in the near future, but likely many more as there are various
|
||||
different theories and models about dynamic frequency scaling
|
||||
around. Using such a generic interface as cpufreq offers to scaling
|
||||
governors, these can be tested extensively, and the best one can be
|
||||
selected for each specific use.
|
||||
That's done using "cpufreq governors".
|
||||
|
||||
Basically, it's the following flow graph:
|
||||
|
||||
|
@ -71,7 +68,7 @@ CPU can be set to switch independently | CPU can only be set
|
|||
/ the limits of policy->{min,max}
|
||||
/ \
|
||||
/ \
|
||||
Using the ->setpolicy call, Using the ->target/target_index call,
|
||||
Using the ->setpolicy call, Using the ->target/target_index/fast_switch call,
|
||||
the limits and the the frequency closest
|
||||
"policy" is set. to target_freq is set.
|
||||
It is assured that it
|
||||
|
@ -109,114 +106,159 @@ directory.
|
|||
2.4 Ondemand
|
||||
------------
|
||||
|
||||
The CPUfreq governor "ondemand" sets the CPU depending on the
|
||||
current usage. To do this the CPU must have the capability to
|
||||
switch the frequency very quickly. There are a number of sysfs file
|
||||
accessible parameters:
|
||||
The CPUfreq governor "ondemand" sets the CPU frequency depending on the
|
||||
current system load. Load estimation is triggered by the scheduler
|
||||
through the update_util_data->func hook; when triggered, cpufreq checks
|
||||
the CPU-usage statistics over the last period and the governor sets the
|
||||
CPU accordingly. The CPU must have the capability to switch the
|
||||
frequency very quickly.
|
||||
|
||||
sampling_rate: measured in uS (10^-6 seconds), this is how often you
|
||||
want the kernel to look at the CPU usage and to make decisions on
|
||||
what to do about the frequency. Typically this is set to values of
|
||||
around '10000' or more. It's default value is (cmp. with users-guide.txt):
|
||||
transition_latency * 1000
|
||||
Be aware that transition latency is in ns and sampling_rate is in us, so you
|
||||
get the same sysfs value by default.
|
||||
Sampling rate should always get adjusted considering the transition latency
|
||||
To set the sampling rate 750 times as high as the transition latency
|
||||
in the bash (as said, 1000 is default), do:
|
||||
echo `$(($(cat cpuinfo_transition_latency) * 750 / 1000)) \
|
||||
>ondemand/sampling_rate
|
||||
Sysfs files:
|
||||
|
||||
sampling_rate_min:
|
||||
The sampling rate is limited by the HW transition latency:
|
||||
transition_latency * 100
|
||||
Or by kernel restrictions:
|
||||
If CONFIG_NO_HZ_COMMON is set, the limit is 10ms fixed.
|
||||
If CONFIG_NO_HZ_COMMON is not set or nohz=off boot parameter is used, the
|
||||
limits depend on the CONFIG_HZ option:
|
||||
HZ=1000: min=20000us (20ms)
|
||||
HZ=250: min=80000us (80ms)
|
||||
HZ=100: min=200000us (200ms)
|
||||
The highest value of kernel and HW latency restrictions is shown and
|
||||
used as the minimum sampling rate.
|
||||
* sampling_rate:
|
||||
|
||||
up_threshold: defines what the average CPU usage between the samplings
|
||||
of 'sampling_rate' needs to be for the kernel to make a decision on
|
||||
whether it should increase the frequency. For example when it is set
|
||||
to its default value of '95' it means that between the checking
|
||||
intervals the CPU needs to be on average more than 95% in use to then
|
||||
decide that the CPU frequency needs to be increased.
|
||||
Measured in uS (10^-6 seconds), this is how often you want the kernel
|
||||
to look at the CPU usage and to make decisions on what to do about the
|
||||
frequency. Typically this is set to values of around '10000' or more.
|
||||
It's default value is (cmp. with users-guide.txt): transition_latency
|
||||
* 1000. Be aware that transition latency is in ns and sampling_rate
|
||||
is in us, so you get the same sysfs value by default. Sampling rate
|
||||
should always get adjusted considering the transition latency to set
|
||||
the sampling rate 750 times as high as the transition latency in the
|
||||
bash (as said, 1000 is default), do:
|
||||
|
||||
ignore_nice_load: this parameter takes a value of '0' or '1'. When
|
||||
set to '0' (its default), all processes are counted towards the
|
||||
'cpu utilisation' value. When set to '1', the processes that are
|
||||
run with a 'nice' value will not count (and thus be ignored) in the
|
||||
overall usage calculation. This is useful if you are running a CPU
|
||||
intensive calculation on your laptop that you do not care how long it
|
||||
takes to complete as you can 'nice' it and prevent it from taking part
|
||||
in the deciding process of whether to increase your CPU frequency.
|
||||
$ echo `$(($(cat cpuinfo_transition_latency) * 750 / 1000)) > ondemand/sampling_rate
|
||||
|
||||
sampling_down_factor: this parameter controls the rate at which the
|
||||
kernel makes a decision on when to decrease the frequency while running
|
||||
at top speed. When set to 1 (the default) decisions to reevaluate load
|
||||
are made at the same interval regardless of current clock speed. But
|
||||
when set to greater than 1 (e.g. 100) it acts as a multiplier for the
|
||||
scheduling interval for reevaluating load when the CPU is at its top
|
||||
speed due to high load. This improves performance by reducing the overhead
|
||||
of load evaluation and helping the CPU stay at its top speed when truly
|
||||
busy, rather than shifting back and forth in speed. This tunable has no
|
||||
effect on behavior at lower speeds/lower CPU loads.
|
||||
* sampling_rate_min:
|
||||
|
||||
powersave_bias: this parameter takes a value between 0 to 1000. It
|
||||
defines the percentage (times 10) value of the target frequency that
|
||||
will be shaved off of the target. For example, when set to 100 -- 10%,
|
||||
when ondemand governor would have targeted 1000 MHz, it will target
|
||||
1000 MHz - (10% of 1000 MHz) = 900 MHz instead. This is set to 0
|
||||
(disabled) by default.
|
||||
When AMD frequency sensitivity powersave bias driver --
|
||||
drivers/cpufreq/amd_freq_sensitivity.c is loaded, this parameter
|
||||
defines the workload frequency sensitivity threshold in which a lower
|
||||
frequency is chosen instead of ondemand governor's original target.
|
||||
The frequency sensitivity is a hardware reported (on AMD Family 16h
|
||||
Processors and above) value between 0 to 100% that tells software how
|
||||
the performance of the workload running on a CPU will change when
|
||||
frequency changes. A workload with sensitivity of 0% (memory/IO-bound)
|
||||
will not perform any better on higher core frequency, whereas a
|
||||
workload with sensitivity of 100% (CPU-bound) will perform better
|
||||
higher the frequency. When the driver is loaded, this is set to 400
|
||||
by default -- for CPUs running workloads with sensitivity value below
|
||||
40%, a lower frequency is chosen. Unloading the driver or writing 0
|
||||
will disable this feature.
|
||||
The sampling rate is limited by the HW transition latency:
|
||||
transition_latency * 100
|
||||
|
||||
Or by kernel restrictions:
|
||||
- If CONFIG_NO_HZ_COMMON is set, the limit is 10ms fixed.
|
||||
- If CONFIG_NO_HZ_COMMON is not set or nohz=off boot parameter is
|
||||
used, the limits depend on the CONFIG_HZ option:
|
||||
HZ=1000: min=20000us (20ms)
|
||||
HZ=250: min=80000us (80ms)
|
||||
HZ=100: min=200000us (200ms)
|
||||
|
||||
The highest value of kernel and HW latency restrictions is shown and
|
||||
used as the minimum sampling rate.
|
||||
|
||||
* up_threshold:
|
||||
|
||||
This defines what the average CPU usage between the samplings of
|
||||
'sampling_rate' needs to be for the kernel to make a decision on
|
||||
whether it should increase the frequency. For example when it is set
|
||||
to its default value of '95' it means that between the checking
|
||||
intervals the CPU needs to be on average more than 95% in use to then
|
||||
decide that the CPU frequency needs to be increased.
|
||||
|
||||
* ignore_nice_load:
|
||||
|
||||
This parameter takes a value of '0' or '1'. When set to '0' (its
|
||||
default), all processes are counted towards the 'cpu utilisation'
|
||||
value. When set to '1', the processes that are run with a 'nice'
|
||||
value will not count (and thus be ignored) in the overall usage
|
||||
calculation. This is useful if you are running a CPU intensive
|
||||
calculation on your laptop that you do not care how long it takes to
|
||||
complete as you can 'nice' it and prevent it from taking part in the
|
||||
deciding process of whether to increase your CPU frequency.
|
||||
|
||||
* sampling_down_factor:
|
||||
|
||||
This parameter controls the rate at which the kernel makes a decision
|
||||
on when to decrease the frequency while running at top speed. When set
|
||||
to 1 (the default) decisions to reevaluate load are made at the same
|
||||
interval regardless of current clock speed. But when set to greater
|
||||
than 1 (e.g. 100) it acts as a multiplier for the scheduling interval
|
||||
for reevaluating load when the CPU is at its top speed due to high
|
||||
load. This improves performance by reducing the overhead of load
|
||||
evaluation and helping the CPU stay at its top speed when truly busy,
|
||||
rather than shifting back and forth in speed. This tunable has no
|
||||
effect on behavior at lower speeds/lower CPU loads.
|
||||
|
||||
* powersave_bias:
|
||||
|
||||
This parameter takes a value between 0 to 1000. It defines the
|
||||
percentage (times 10) value of the target frequency that will be
|
||||
shaved off of the target. For example, when set to 100 -- 10%, when
|
||||
ondemand governor would have targeted 1000 MHz, it will target
|
||||
1000 MHz - (10% of 1000 MHz) = 900 MHz instead. This is set to 0
|
||||
(disabled) by default.
|
||||
|
||||
When AMD frequency sensitivity powersave bias driver --
|
||||
drivers/cpufreq/amd_freq_sensitivity.c is loaded, this parameter
|
||||
defines the workload frequency sensitivity threshold in which a lower
|
||||
frequency is chosen instead of ondemand governor's original target.
|
||||
The frequency sensitivity is a hardware reported (on AMD Family 16h
|
||||
Processors and above) value between 0 to 100% that tells software how
|
||||
the performance of the workload running on a CPU will change when
|
||||
frequency changes. A workload with sensitivity of 0% (memory/IO-bound)
|
||||
will not perform any better on higher core frequency, whereas a
|
||||
workload with sensitivity of 100% (CPU-bound) will perform better
|
||||
higher the frequency. When the driver is loaded, this is set to 400 by
|
||||
default -- for CPUs running workloads with sensitivity value below
|
||||
40%, a lower frequency is chosen. Unloading the driver or writing 0
|
||||
will disable this feature.
|
||||
|
||||
|
||||
2.5 Conservative
|
||||
----------------
|
||||
|
||||
The CPUfreq governor "conservative", much like the "ondemand"
|
||||
governor, sets the CPU depending on the current usage. It differs in
|
||||
behaviour in that it gracefully increases and decreases the CPU speed
|
||||
rather than jumping to max speed the moment there is any load on the
|
||||
CPU. This behaviour more suitable in a battery powered environment.
|
||||
The governor is tweaked in the same manner as the "ondemand" governor
|
||||
through sysfs with the addition of:
|
||||
governor, sets the CPU frequency depending on the current usage. It
|
||||
differs in behaviour in that it gracefully increases and decreases the
|
||||
CPU speed rather than jumping to max speed the moment there is any load
|
||||
on the CPU. This behaviour is more suitable in a battery powered
|
||||
environment. The governor is tweaked in the same manner as the
|
||||
"ondemand" governor through sysfs with the addition of:
|
||||
|
||||
freq_step: this describes what percentage steps the cpu freq should be
|
||||
increased and decreased smoothly by. By default the cpu frequency will
|
||||
increase in 5% chunks of your maximum cpu frequency. You can change this
|
||||
value to anywhere between 0 and 100 where '0' will effectively lock your
|
||||
CPU at a speed regardless of its load whilst '100' will, in theory, make
|
||||
it behave identically to the "ondemand" governor.
|
||||
* freq_step:
|
||||
|
||||
down_threshold: same as the 'up_threshold' found for the "ondemand"
|
||||
governor but for the opposite direction. For example when set to its
|
||||
default value of '20' it means that if the CPU usage needs to be below
|
||||
20% between samples to have the frequency decreased.
|
||||
This describes what percentage steps the cpu freq should be increased
|
||||
and decreased smoothly by. By default the cpu frequency will increase
|
||||
in 5% chunks of your maximum cpu frequency. You can change this value
|
||||
to anywhere between 0 and 100 where '0' will effectively lock your CPU
|
||||
at a speed regardless of its load whilst '100' will, in theory, make
|
||||
it behave identically to the "ondemand" governor.
|
||||
|
||||
* down_threshold:
|
||||
|
||||
Same as the 'up_threshold' found for the "ondemand" governor but for
|
||||
the opposite direction. For example when set to its default value of
|
||||
'20' it means that if the CPU usage needs to be below 20% between
|
||||
samples to have the frequency decreased.
|
||||
|
||||
* sampling_down_factor:
|
||||
|
||||
Similar functionality as in "ondemand" governor. But in
|
||||
"conservative", it controls the rate at which the kernel makes a
|
||||
decision on when to decrease the frequency while running in any speed.
|
||||
Load for frequency increase is still evaluated every sampling rate.
|
||||
|
||||
|
||||
2.6 Schedutil
|
||||
-------------
|
||||
|
||||
The "schedutil" governor aims at better integration with the Linux
|
||||
kernel scheduler. Load estimation is achieved through the scheduler's
|
||||
Per-Entity Load Tracking (PELT) mechanism, which also provides
|
||||
information about the recent load [1]. This governor currently does
|
||||
load based DVFS only for tasks managed by CFS. RT and DL scheduler tasks
|
||||
are always run at the highest frequency. Unlike all the other
|
||||
governors, the code is located under the kernel/sched/ directory.
|
||||
|
||||
Sysfs files:
|
||||
|
||||
* rate_limit_us:
|
||||
|
||||
This contains a value in microseconds. The governor waits for
|
||||
rate_limit_us time before reevaluating the load again, after it has
|
||||
evaluated the load once.
|
||||
|
||||
For an in-depth comparison with the other governors refer to [2].
|
||||
|
||||
sampling_down_factor: similar functionality as in "ondemand" governor.
|
||||
But in "conservative", it controls the rate at which the kernel makes
|
||||
a decision on when to decrease the frequency while running in any
|
||||
speed. Load for frequency increase is still evaluated every
|
||||
sampling rate.
|
||||
|
||||
3. The Governor Interface in the CPUfreq Core
|
||||
=============================================
|
||||
|
@ -225,26 +267,10 @@ A new governor must register itself with the CPUfreq core using
|
|||
"cpufreq_register_governor". The struct cpufreq_governor, which has to
|
||||
be passed to that function, must contain the following values:
|
||||
|
||||
governor->name - A unique name for this governor
|
||||
governor->governor - The governor callback function
|
||||
governor->owner - .THIS_MODULE for the governor module (if
|
||||
appropriate)
|
||||
|
||||
The governor->governor callback is called with the current (or to-be-set)
|
||||
cpufreq_policy struct for that CPU, and an unsigned int event. The
|
||||
following events are currently defined:
|
||||
|
||||
CPUFREQ_GOV_START: This governor shall start its duty for the CPU
|
||||
policy->cpu
|
||||
CPUFREQ_GOV_STOP: This governor shall end its duty for the CPU
|
||||
policy->cpu
|
||||
CPUFREQ_GOV_LIMITS: The limits for CPU policy->cpu have changed to
|
||||
policy->min and policy->max.
|
||||
|
||||
If you need other "events" externally of your driver, _only_ use the
|
||||
cpufreq_governor_l(unsigned int cpu, unsigned int event) call to the
|
||||
CPUfreq core to ensure proper locking.
|
||||
governor->name - A unique name for this governor.
|
||||
governor->owner - .THIS_MODULE for the governor module (if appropriate).
|
||||
|
||||
plus a set of hooks to the functions implementing the governor's logic.
|
||||
|
||||
The CPUfreq governor may call the CPU processor driver using one of
|
||||
these two functions:
|
||||
|
@ -258,12 +284,18 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
|
|||
unsigned int relation);
|
||||
|
||||
target_freq must be within policy->min and policy->max, of course.
|
||||
What's the difference between these two functions? When your governor
|
||||
still is in a direct code path of a call to governor->governor, the
|
||||
per-CPU cpufreq lock is still held in the cpufreq core, and there's
|
||||
no need to lock it again (in fact, this would cause a deadlock). So
|
||||
use __cpufreq_driver_target only in these cases. In all other cases
|
||||
(for example, when there's a "daemonized" function that wakes up
|
||||
every second), use cpufreq_driver_target to lock the cpufreq per-CPU
|
||||
lock before the command is passed to the cpufreq processor driver.
|
||||
What's the difference between these two functions? When your governor is
|
||||
in a direct code path of a call to governor callbacks, like
|
||||
governor->start(), the policy->rwsem is still held in the cpufreq core,
|
||||
and there's no need to lock it again (in fact, this would cause a
|
||||
deadlock). So use __cpufreq_driver_target only in these cases. In all
|
||||
other cases (for example, when there's a "daemonized" function that
|
||||
wakes up every second), use cpufreq_driver_target to take policy->rwsem
|
||||
before the command is passed to the cpufreq driver.
|
||||
|
||||
4. References
|
||||
=============
|
||||
|
||||
[1] Per-entity load tracking: https://lwn.net/Articles/531853/
|
||||
[2] Improvements in CPU frequency management: https://lwn.net/Articles/682391/
|
||||
|
||||
|
|
|
@ -18,16 +18,29 @@
|
|||
|
||||
Documents in this directory:
|
||||
----------------------------
|
||||
core.txt - General description of the CPUFreq core and
|
||||
of CPUFreq notifiers
|
||||
|
||||
cpu-drivers.txt - How to implement a new cpufreq processor driver
|
||||
amd-powernow.txt - AMD powernow driver specific file.
|
||||
|
||||
boost.txt - Frequency boosting support.
|
||||
|
||||
core.txt - General description of the CPUFreq core and
|
||||
of CPUFreq notifiers.
|
||||
|
||||
cpu-drivers.txt - How to implement a new cpufreq processor driver.
|
||||
|
||||
cpufreq-nforce2.txt - nVidia nForce2 platform specific file.
|
||||
|
||||
cpufreq-stats.txt - General description of sysfs cpufreq stats.
|
||||
|
||||
governors.txt - What are cpufreq governors and how to
|
||||
implement them?
|
||||
|
||||
index.txt - File index, Mailing list and Links (this document)
|
||||
|
||||
intel-pstate.txt - Intel pstate cpufreq driver specific file.
|
||||
|
||||
pcc-cpufreq.txt - PCC cpufreq driver specific file.
|
||||
|
||||
user-guide.txt - User Guide to CPUFreq
|
||||
|
||||
|
||||
|
@ -35,9 +48,7 @@ Mailing List
|
|||
------------
|
||||
There is a CPU frequency changing CVS commit and general list where
|
||||
you can report bugs, problems or submit patches. To post a message,
|
||||
send an email to linux-pm@vger.kernel.org, to subscribe go to
|
||||
http://vger.kernel.org/vger-lists.html#linux-pm and follow the
|
||||
instructions there.
|
||||
send an email to linux-pm@vger.kernel.org.
|
||||
|
||||
Links
|
||||
-----
|
||||
|
@ -48,7 +59,7 @@ how to access the CVS repository:
|
|||
* http://cvs.arm.linux.org.uk/
|
||||
|
||||
the CPUFreq Mailing list:
|
||||
* http://vger.kernel.org/vger-lists.html#cpufreq
|
||||
* http://vger.kernel.org/vger-lists.html#linux-pm
|
||||
|
||||
Clock and voltage scaling for the SA-1100:
|
||||
* http://www.lartmaker.nl/projects/scaling
|
||||
|
|
|
@ -85,6 +85,21 @@ Sysfs will show :
|
|||
Refer to "Intel® 64 and IA-32 Architectures Software Developer’s Manual
|
||||
Volume 3: System Programming Guide" to understand ratios.
|
||||
|
||||
There is one more sysfs attribute in /sys/devices/system/cpu/intel_pstate/
|
||||
that can be used for controlling the operation mode of the driver:
|
||||
|
||||
status: Three settings are possible:
|
||||
"off" - The driver is not in use at this time.
|
||||
"active" - The driver works as a P-state governor (default).
|
||||
"passive" - The driver works as a regular cpufreq one and collaborates
|
||||
with the generic cpufreq governors (it sets P-states as
|
||||
requested by those governors).
|
||||
The current setting is returned by reads from this attribute. Writing one
|
||||
of the above strings to it changes the operation mode as indicated by that
|
||||
string, if possible. If HW-managed P-states (HWP) are enabled, it is not
|
||||
possible to change the driver's operation mode and attempts to write to
|
||||
this attribute will fail.
|
||||
|
||||
cpufreq sysfs for Intel P-State
|
||||
|
||||
Since this driver registers with cpufreq, cpufreq sysfs is also presented.
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
Contents:
|
||||
---------
|
||||
1. Supported Architectures and Processors
|
||||
1.1 ARM
|
||||
1.1 ARM and ARM64
|
||||
1.2 x86
|
||||
1.3 sparc64
|
||||
1.4 ppc
|
||||
|
@ -37,16 +37,10 @@ Contents:
|
|||
1. Supported Architectures and Processors
|
||||
=========================================
|
||||
|
||||
1.1 ARM
|
||||
-------
|
||||
|
||||
The following ARM processors are supported by cpufreq:
|
||||
|
||||
ARM Integrator
|
||||
ARM-SA1100
|
||||
ARM-SA1110
|
||||
Intel PXA
|
||||
1.1 ARM and ARM64
|
||||
-----------------
|
||||
|
||||
Almost all ARM and ARM64 platforms support CPU frequency scaling.
|
||||
|
||||
1.2 x86
|
||||
-------
|
||||
|
@ -69,6 +63,7 @@ Transmeta Crusoe
|
|||
Transmeta Efficeon
|
||||
VIA Cyrix 3 / C3
|
||||
various processors on some ACPI 2.0-compatible systems [*]
|
||||
And many more
|
||||
|
||||
[*] Only if "ACPI Processor Performance States" are available
|
||||
to the ACPI<->BIOS interface.
|
||||
|
@ -147,10 +142,19 @@ mounted it at /sys, the cpufreq interface is located in a subdirectory
|
|||
"cpufreq" within the cpu-device directory
|
||||
(e.g. /sys/devices/system/cpu/cpu0/cpufreq/ for the first CPU).
|
||||
|
||||
affected_cpus : List of Online CPUs that require software
|
||||
coordination of frequency.
|
||||
|
||||
cpuinfo_cur_freq : Current frequency of the CPU as obtained from
|
||||
the hardware, in KHz. This is the frequency
|
||||
the CPU actually runs at.
|
||||
|
||||
cpuinfo_min_freq : this file shows the minimum operating
|
||||
frequency the processor can run at(in kHz)
|
||||
|
||||
cpuinfo_max_freq : this file shows the maximum operating
|
||||
frequency the processor can run at(in kHz)
|
||||
|
||||
cpuinfo_transition_latency The time it takes on this CPU to
|
||||
switch between two frequencies in nano
|
||||
seconds. If unknown or known to be
|
||||
|
@ -163,25 +167,30 @@ cpuinfo_transition_latency The time it takes on this CPU to
|
|||
userspace daemon. Make sure to not
|
||||
switch the frequency too often
|
||||
resulting in performance loss.
|
||||
scaling_driver : this file shows what cpufreq driver is
|
||||
used to set the frequency on this CPU
|
||||
|
||||
related_cpus : List of Online + Offline CPUs that need software
|
||||
coordination of frequency.
|
||||
|
||||
scaling_available_frequencies : List of available frequencies, in KHz.
|
||||
|
||||
scaling_available_governors : this file shows the CPUfreq governors
|
||||
available in this kernel. You can see the
|
||||
currently activated governor in
|
||||
|
||||
scaling_cur_freq : Current frequency of the CPU as determined by
|
||||
the governor and cpufreq core, in KHz. This is
|
||||
the frequency the kernel thinks the CPU runs
|
||||
at.
|
||||
|
||||
scaling_driver : this file shows what cpufreq driver is
|
||||
used to set the frequency on this CPU
|
||||
|
||||
scaling_governor, and by "echoing" the name of another
|
||||
governor you can change it. Please note
|
||||
that some governors won't load - they only
|
||||
work on some specific architectures or
|
||||
processors.
|
||||
|
||||
cpuinfo_cur_freq : Current frequency of the CPU as obtained from
|
||||
the hardware, in KHz. This is the frequency
|
||||
the CPU actually runs at.
|
||||
|
||||
scaling_available_frequencies : List of available frequencies, in KHz.
|
||||
|
||||
scaling_min_freq and
|
||||
scaling_max_freq show the current "policy limits" (in
|
||||
kHz). By echoing new values into these
|
||||
|
@ -190,16 +199,11 @@ scaling_max_freq show the current "policy limits" (in
|
|||
first set scaling_max_freq, then
|
||||
scaling_min_freq.
|
||||
|
||||
affected_cpus : List of Online CPUs that require software
|
||||
coordination of frequency.
|
||||
|
||||
related_cpus : List of Online + Offline CPUs that need software
|
||||
coordination of frequency.
|
||||
|
||||
scaling_cur_freq : Current frequency of the CPU as determined by
|
||||
the governor and cpufreq core, in KHz. This is
|
||||
the frequency the kernel thinks the CPU runs
|
||||
at.
|
||||
scaling_setspeed This can be read to get the currently programmed
|
||||
value by the governor. This can be written to
|
||||
change the current frequency for a group of
|
||||
CPUs, represented by a policy. This is supported
|
||||
currently only by the userspace governor.
|
||||
|
||||
bios_limit : If the BIOS tells the OS to limit a CPU to
|
||||
lower frequencies, the user can read out the
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
TI CPUFreq and OPP bindings
|
||||
================================
|
||||
|
||||
Certain TI SoCs, like those in the am335x, am437x, am57xx, and dra7xx
|
||||
families support different OPPs depending on the silicon variant in use.
|
||||
The ti-cpufreq driver can use revision and an efuse value from the SoC to
|
||||
provide the OPP framework with supported hardware information. This is
|
||||
used to determine which OPPs from the operating-points-v2 table get enabled
|
||||
when it is parsed by the OPP framework.
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
In 'cpus' nodes:
|
||||
- operating-points-v2: Phandle to the operating-points-v2 table to use.
|
||||
|
||||
In 'operating-points-v2' table:
|
||||
- compatible: Should be
|
||||
- 'operating-points-v2-ti-cpu' for am335x, am43xx, and dra7xx/am57xx SoCs
|
||||
- syscon: A phandle pointing to a syscon node representing the control module
|
||||
register space of the SoC.
|
||||
|
||||
Optional properties:
|
||||
--------------------
|
||||
For each opp entry in 'operating-points-v2' table:
|
||||
- opp-supported-hw: Two bitfields indicating:
|
||||
1. Which revision of the SoC the OPP is supported by
|
||||
2. Which eFuse bits indicate this OPP is available
|
||||
|
||||
A bitwise AND is performed against these values and if any bit
|
||||
matches, the OPP gets enabled.
|
||||
|
||||
Example:
|
||||
--------
|
||||
|
||||
/* From arch/arm/boot/dts/am33xx.dtsi */
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
cpu@0 {
|
||||
compatible = "arm,cortex-a8";
|
||||
device_type = "cpu";
|
||||
reg = <0>;
|
||||
|
||||
operating-points-v2 = <&cpu0_opp_table>;
|
||||
|
||||
clocks = <&dpll_mpu_ck>;
|
||||
clock-names = "cpu";
|
||||
|
||||
clock-latency = <300000>; /* From omap-cpufreq driver */
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* cpu0 has different OPPs depending on SoC revision and some on revisions
|
||||
* 0x2 and 0x4 have eFuse bits that indicate if they are available or not
|
||||
*/
|
||||
cpu0_opp_table: opp-table {
|
||||
compatible = "operating-points-v2-ti-cpu";
|
||||
syscon = <&scm_conf>;
|
||||
|
||||
/*
|
||||
* The three following nodes are marked with opp-suspend
|
||||
* because they can not be enabled simultaneously on a
|
||||
* single SoC.
|
||||
*/
|
||||
opp50@300000000 {
|
||||
opp-hz = /bits/ 64 <300000000>;
|
||||
opp-microvolt = <950000 931000 969000>;
|
||||
opp-supported-hw = <0x06 0x0010>;
|
||||
opp-suspend;
|
||||
};
|
||||
|
||||
opp100@275000000 {
|
||||
opp-hz = /bits/ 64 <275000000>;
|
||||
opp-microvolt = <1100000 1078000 1122000>;
|
||||
opp-supported-hw = <0x01 0x00FF>;
|
||||
opp-suspend;
|
||||
};
|
||||
|
||||
opp100@300000000 {
|
||||
opp-hz = /bits/ 64 <300000000>;
|
||||
opp-microvolt = <1100000 1078000 1122000>;
|
||||
opp-supported-hw = <0x06 0x0020>;
|
||||
opp-suspend;
|
||||
};
|
||||
|
||||
opp100@500000000 {
|
||||
opp-hz = /bits/ 64 <500000000>;
|
||||
opp-microvolt = <1100000 1078000 1122000>;
|
||||
opp-supported-hw = <0x01 0xFFFF>;
|
||||
};
|
||||
|
||||
opp100@600000000 {
|
||||
opp-hz = /bits/ 64 <600000000>;
|
||||
opp-microvolt = <1100000 1078000 1122000>;
|
||||
opp-supported-hw = <0x06 0x0040>;
|
||||
};
|
||||
|
||||
opp120@600000000 {
|
||||
opp-hz = /bits/ 64 <600000000>;
|
||||
opp-microvolt = <1200000 1176000 1224000>;
|
||||
opp-supported-hw = <0x01 0xFFFF>;
|
||||
};
|
||||
|
||||
opp120@720000000 {
|
||||
opp-hz = /bits/ 64 <720000000>;
|
||||
opp-microvolt = <1200000 1176000 1224000>;
|
||||
opp-supported-hw = <0x06 0x0080>;
|
||||
};
|
||||
|
||||
oppturbo@720000000 {
|
||||
opp-hz = /bits/ 64 <720000000>;
|
||||
opp-microvolt = <1260000 1234800 1285200>;
|
||||
opp-supported-hw = <0x01 0xFFFF>;
|
||||
};
|
||||
|
||||
oppturbo@800000000 {
|
||||
opp-hz = /bits/ 64 <800000000>;
|
||||
opp-microvolt = <1260000 1234800 1285200>;
|
||||
opp-supported-hw = <0x06 0x0100>;
|
||||
};
|
||||
|
||||
oppnitro@1000000000 {
|
||||
opp-hz = /bits/ 64 <1000000000>;
|
||||
opp-microvolt = <1325000 1298500 1351500>;
|
||||
opp-supported-hw = <0x04 0x0200>;
|
||||
};
|
||||
};
|
|
@ -2692,6 +2692,13 @@ F: drivers/irqchip/irq-brcmstb*
|
|||
F: include/linux/bcm963xx_nvram.h
|
||||
F: include/linux/bcm963xx_tag.h
|
||||
|
||||
BROADCOM BMIPS CPUFREQ DRIVER
|
||||
M: Markus Mayer <mmayer@broadcom.com>
|
||||
M: bcm-kernel-feedback-list@broadcom.com
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/cpufreq/bmips-cpufreq.c
|
||||
|
||||
BROADCOM TG3 GIGABIT ETHERNET DRIVER
|
||||
M: Siva Reddy Kallam <siva.kallam@broadcom.com>
|
||||
M: Prashant Sreedharan <prashant@broadcom.com>
|
||||
|
|
|
@ -24,7 +24,7 @@ CONFIG_ARM_APPENDED_DTB=y
|
|||
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
||||
CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc mem=256M"
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
||||
|
|
|
@ -58,7 +58,7 @@ CONFIG_ZBOOT_ROM_BSS=0x0
|
|||
CONFIG_ARM_APPENDED_DTB=y
|
||||
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_ARM_KIRKWOOD_CPUIDLE=y
|
||||
|
|
|
@ -132,7 +132,7 @@ CONFIG_ARM_ATAG_DTB_COMPAT=y
|
|||
CONFIG_KEXEC=y
|
||||
CONFIG_EFI=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
||||
|
|
|
@ -44,7 +44,7 @@ CONFIG_ZBOOT_ROM_BSS=0x0
|
|||
CONFIG_ARM_APPENDED_DTB=y
|
||||
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_ARM_KIRKWOOD_CPUIDLE=y
|
||||
|
|
|
@ -97,7 +97,7 @@ CONFIG_ZBOOT_ROM_BSS=0x0
|
|||
CONFIG_CMDLINE="root=/dev/ram0 ro"
|
||||
CONFIG_KEXEC=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
||||
|
|
|
@ -38,7 +38,7 @@ CONFIG_ZBOOT_ROM_BSS=0x0
|
|||
CONFIG_ARM_APPENDED_DTB=y
|
||||
CONFIG_KEXEC=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
||||
|
|
|
@ -1703,6 +1703,8 @@ config CPU_BMIPS
|
|||
select WEAK_ORDERING
|
||||
select CPU_SUPPORTS_HIGHMEM
|
||||
select CPU_HAS_PREFETCH
|
||||
select CPU_SUPPORTS_CPUFREQ
|
||||
select MIPS_EXTERNAL_TIMER
|
||||
help
|
||||
Support for BMIPS32/3300/4350/4380 and BMIPS5000 processors.
|
||||
|
||||
|
|
|
@ -9,13 +9,20 @@ CONFIG_MIPS_O32_FP64_SUPPORT=y
|
|||
# CONFIG_SWAP is not set
|
||||
CONFIG_NO_HZ=y
|
||||
CONFIG_BLK_DEV_INITRD=y
|
||||
CONFIG_RD_GZIP=y
|
||||
CONFIG_EXPERT=y
|
||||
# CONFIG_VM_EVENT_COUNTERS is not set
|
||||
# CONFIG_SLUB_DEBUG is not set
|
||||
# CONFIG_BLK_DEV_BSG is not set
|
||||
# CONFIG_IOSCHED_DEADLINE is not set
|
||||
# CONFIG_IOSCHED_CFQ is not set
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
|
||||
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
|
||||
CONFIG_BMIPS_CPUFREQ=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_PACKET_DIAG=y
|
||||
|
@ -24,7 +31,6 @@ CONFIG_INET=y
|
|||
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
|
||||
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
|
||||
# CONFIG_INET_XFRM_MODE_BEET is not set
|
||||
# CONFIG_INET_LRO is not set
|
||||
# CONFIG_INET_DIAG is not set
|
||||
CONFIG_CFG80211=y
|
||||
CONFIG_NL80211_TESTMODE=y
|
||||
|
@ -34,8 +40,6 @@ CONFIG_DEVTMPFS=y
|
|||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
# CONFIG_STANDALONE is not set
|
||||
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_BRCMSTB_GISB_ARB=y
|
||||
CONFIG_MTD=y
|
||||
CONFIG_MTD_CFI=y
|
||||
CONFIG_MTD_CFI_INTELEXT=y
|
||||
|
@ -51,16 +55,15 @@ CONFIG_USB_USBNET=y
|
|||
# CONFIG_INPUT is not set
|
||||
# CONFIG_SERIO is not set
|
||||
# CONFIG_VT is not set
|
||||
# CONFIG_DEVKMEM is not set
|
||||
CONFIG_SERIAL_8250=y
|
||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
||||
CONFIG_SERIAL_8250_CONSOLE=y
|
||||
CONFIG_SERIAL_OF_PLATFORM=y
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_BRCMSTB=y
|
||||
CONFIG_POWER_RESET_SYSCON=y
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
# CONFIG_HWMON is not set
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
|
@ -82,6 +85,7 @@ CONFIG_CIFS=y
|
|||
CONFIG_NLS_CODEPAGE_437=y
|
||||
CONFIG_NLS_ASCII=y
|
||||
CONFIG_NLS_ISO8859_1=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_DEBUG_FS=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_CMDLINE_BOOL=y
|
||||
|
|
|
@ -40,7 +40,6 @@ CONFIG_PM_STD_PARTITION="/dev/hda3"
|
|||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_DEBUG=y
|
||||
CONFIG_CPU_FREQ_STAT=m
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
||||
|
|
|
@ -62,7 +62,6 @@ CONFIG_MPC8610_HPCD=y
|
|||
CONFIG_GEF_SBC610=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT=m
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
|
||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||
|
|
|
@ -25,7 +25,7 @@ CONFIG_SH_SH7785LCR=y
|
|||
CONFIG_NO_HZ=y
|
||||
CONFIG_HIGH_RES_TIMERS=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
CONFIG_SH_CPU_FREQ=y
|
||||
CONFIG_HEARTBEAT=y
|
||||
|
|
|
@ -75,10 +75,8 @@ static int acpi_processor_ppc_notifier(struct notifier_block *nb,
|
|||
struct acpi_processor *pr;
|
||||
unsigned int ppc = 0;
|
||||
|
||||
if (event == CPUFREQ_START && ignore_ppc <= 0) {
|
||||
if (ignore_ppc < 0)
|
||||
ignore_ppc = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ignore_ppc)
|
||||
return 0;
|
||||
|
|
|
@ -37,14 +37,6 @@ config CPU_FREQ_STAT
|
|||
|
||||
If in doubt, say N.
|
||||
|
||||
config CPU_FREQ_STAT_DETAILS
|
||||
bool "CPU frequency transition statistics details"
|
||||
depends on CPU_FREQ_STAT
|
||||
help
|
||||
Show detailed CPU frequency transition table in sysfs.
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
choice
|
||||
prompt "Default CPUFreq governor"
|
||||
default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ
|
||||
|
@ -271,6 +263,16 @@ config IA64_ACPI_CPUFREQ
|
|||
endif
|
||||
|
||||
if MIPS
|
||||
config BMIPS_CPUFREQ
|
||||
tristate "BMIPS CPUfreq Driver"
|
||||
help
|
||||
This option adds a CPUfreq driver for BMIPS processors with
|
||||
support for configurable CPU frequency.
|
||||
|
||||
For now, BMIPS5 chips are supported (such as the Broadcom 7425).
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
config LOONGSON2_CPUFREQ
|
||||
tristate "Loongson2 CPUFreq Driver"
|
||||
help
|
||||
|
@ -332,7 +334,7 @@ endif
|
|||
|
||||
config QORIQ_CPUFREQ
|
||||
tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
|
||||
depends on OF && COMMON_CLK && (PPC_E500MC || ARM)
|
||||
depends on OF && COMMON_CLK && (PPC_E500MC || ARM || ARM64)
|
||||
depends on !CPU_THERMAL || THERMAL
|
||||
select CLK_QORIQ
|
||||
help
|
||||
|
|
|
@ -247,6 +247,17 @@ config ARM_TEGRA124_CPUFREQ
|
|||
help
|
||||
This adds the CPUFreq driver support for Tegra124 SOCs.
|
||||
|
||||
config ARM_TI_CPUFREQ
|
||||
bool "Texas Instruments CPUFreq support"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
help
|
||||
This driver enables valid OPPs on the running platform based on
|
||||
values contained within the SoC in use. Enable this in order to
|
||||
use the cpufreq-dt driver on all Texas Instruments platforms that
|
||||
provide dt based operating-points-v2 tables with opp-supported-hw
|
||||
data provided. Required for cpufreq support on AM335x, AM437x,
|
||||
DRA7x, and AM57x platforms.
|
||||
|
||||
config ARM_PXA2xx_CPUFREQ
|
||||
tristate "Intel PXA2xx CPUfreq driver"
|
||||
depends on PXA27x || PXA25x
|
||||
|
@ -257,7 +268,7 @@ config ARM_PXA2xx_CPUFREQ
|
|||
|
||||
config ACPI_CPPC_CPUFREQ
|
||||
tristate "CPUFreq driver based on the ACPI CPPC spec"
|
||||
depends on ACPI
|
||||
depends on ACPI_PROCESSOR
|
||||
select ACPI_CPPC_LIB
|
||||
default n
|
||||
help
|
||||
|
|
|
@ -77,6 +77,7 @@ obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
|
|||
obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o
|
||||
obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
|
||||
obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
|
||||
obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
|
||||
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
|
||||
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
|
||||
obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
|
||||
|
@ -98,6 +99,7 @@ obj-$(CONFIG_POWERNV_CPUFREQ) += powernv-cpufreq.o
|
|||
# Other platform drivers
|
||||
obj-$(CONFIG_AVR32_AT32AP_CPUFREQ) += at32ap-cpufreq.o
|
||||
obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-cpufreq.o
|
||||
obj-$(CONFIG_BMIPS_CPUFREQ) += bmips-cpufreq.o
|
||||
obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o
|
||||
obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o
|
||||
obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* CPU frequency scaling for Broadcom BMIPS SoCs
|
||||
*
|
||||
* Copyright (c) 2017 Broadcom
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* for mips_hpt_frequency */
|
||||
#include <asm/time.h>
|
||||
|
||||
#define BMIPS_CPUFREQ_PREFIX "bmips"
|
||||
#define BMIPS_CPUFREQ_NAME BMIPS_CPUFREQ_PREFIX "-cpufreq"
|
||||
|
||||
#define TRANSITION_LATENCY (25 * 1000) /* 25 us */
|
||||
|
||||
#define BMIPS5_CLK_DIV_SET_SHIFT 0x7
|
||||
#define BMIPS5_CLK_DIV_SHIFT 0x4
|
||||
#define BMIPS5_CLK_DIV_MASK 0xf
|
||||
|
||||
enum bmips_type {
|
||||
BMIPS5000,
|
||||
BMIPS5200,
|
||||
};
|
||||
|
||||
struct cpufreq_compat {
|
||||
const char *compatible;
|
||||
unsigned int bmips_type;
|
||||
unsigned int clk_mult;
|
||||
unsigned int max_freqs;
|
||||
};
|
||||
|
||||
#define BMIPS(c, t, m, f) { \
|
||||
.compatible = c, \
|
||||
.bmips_type = (t), \
|
||||
.clk_mult = (m), \
|
||||
.max_freqs = (f), \
|
||||
}
|
||||
|
||||
static struct cpufreq_compat bmips_cpufreq_compat[] = {
|
||||
BMIPS("brcm,bmips5000", BMIPS5000, 8, 4),
|
||||
BMIPS("brcm,bmips5200", BMIPS5200, 8, 4),
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct cpufreq_compat *priv;
|
||||
|
||||
static int htp_freq_to_cpu_freq(unsigned int clk_mult)
|
||||
{
|
||||
return mips_hpt_frequency * clk_mult / 1000;
|
||||
}
|
||||
|
||||
static struct cpufreq_frequency_table *
|
||||
bmips_cpufreq_get_freq_table(const struct cpufreq_policy *policy)
|
||||
{
|
||||
struct cpufreq_frequency_table *table;
|
||||
unsigned long cpu_freq;
|
||||
int i;
|
||||
|
||||
cpu_freq = htp_freq_to_cpu_freq(priv->clk_mult);
|
||||
|
||||
table = kmalloc((priv->max_freqs + 1) * sizeof(*table), GFP_KERNEL);
|
||||
if (!table)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < priv->max_freqs; i++) {
|
||||
table[i].frequency = cpu_freq / (1 << i);
|
||||
table[i].driver_data = i;
|
||||
}
|
||||
table[i].frequency = CPUFREQ_TABLE_END;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
static unsigned int bmips_cpufreq_get(unsigned int cpu)
|
||||
{
|
||||
unsigned int div;
|
||||
uint32_t mode;
|
||||
|
||||
switch (priv->bmips_type) {
|
||||
case BMIPS5200:
|
||||
case BMIPS5000:
|
||||
mode = read_c0_brcm_mode();
|
||||
div = ((mode >> BMIPS5_CLK_DIV_SHIFT) & BMIPS5_CLK_DIV_MASK);
|
||||
break;
|
||||
default:
|
||||
div = 0;
|
||||
}
|
||||
|
||||
return htp_freq_to_cpu_freq(priv->clk_mult) / (1 << div);
|
||||
}
|
||||
|
||||
static int bmips_cpufreq_target_index(struct cpufreq_policy *policy,
|
||||
unsigned int index)
|
||||
{
|
||||
unsigned int div = policy->freq_table[index].driver_data;
|
||||
|
||||
switch (priv->bmips_type) {
|
||||
case BMIPS5200:
|
||||
case BMIPS5000:
|
||||
change_c0_brcm_mode(BMIPS5_CLK_DIV_MASK << BMIPS5_CLK_DIV_SHIFT,
|
||||
(1 << BMIPS5_CLK_DIV_SET_SHIFT) |
|
||||
(div << BMIPS5_CLK_DIV_SHIFT));
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmips_cpufreq_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
kfree(policy->freq_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmips_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
int ret;
|
||||
|
||||
freq_table = bmips_cpufreq_get_freq_table(policy);
|
||||
if (IS_ERR(freq_table)) {
|
||||
ret = PTR_ERR(freq_table);
|
||||
pr_err("%s: couldn't determine frequency table (%d).\n",
|
||||
BMIPS_CPUFREQ_NAME, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cpufreq_generic_init(policy, freq_table, TRANSITION_LATENCY);
|
||||
if (ret)
|
||||
bmips_cpufreq_exit(policy);
|
||||
else
|
||||
pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct cpufreq_driver bmips_cpufreq_driver = {
|
||||
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
|
||||
.verify = cpufreq_generic_frequency_table_verify,
|
||||
.target_index = bmips_cpufreq_target_index,
|
||||
.get = bmips_cpufreq_get,
|
||||
.init = bmips_cpufreq_init,
|
||||
.exit = bmips_cpufreq_exit,
|
||||
.attr = cpufreq_generic_attr,
|
||||
.name = BMIPS_CPUFREQ_PREFIX,
|
||||
};
|
||||
|
||||
static int __init bmips_cpufreq_probe(void)
|
||||
{
|
||||
struct cpufreq_compat *cc;
|
||||
struct device_node *np;
|
||||
|
||||
for (cc = bmips_cpufreq_compat; cc->compatible; cc++) {
|
||||
np = of_find_compatible_node(NULL, "cpu", cc->compatible);
|
||||
if (np) {
|
||||
of_node_put(np);
|
||||
priv = cc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We hit the guard element of the array. No compatible CPU found. */
|
||||
if (!cc->compatible)
|
||||
return -ENODEV;
|
||||
|
||||
return cpufreq_register_driver(&bmips_cpufreq_driver);
|
||||
}
|
||||
device_initcall(bmips_cpufreq_probe);
|
||||
|
||||
MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
|
||||
MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -878,7 +878,6 @@ unmap_intr_base:
|
|||
iounmap(priv->avs_intr_base);
|
||||
unmap_base:
|
||||
iounmap(priv->base);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1042,7 +1041,6 @@ static int brcm_avs_cpufreq_remove(struct platform_device *pdev)
|
|||
priv = platform_get_drvdata(pdev);
|
||||
iounmap(priv->base);
|
||||
iounmap(priv->avs_intr_base);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -87,8 +87,6 @@ static const struct of_device_id machines[] __initconst = {
|
|||
{ .compatible = "socionext,uniphier-ld11", },
|
||||
{ .compatible = "socionext,uniphier-ld20", },
|
||||
|
||||
{ .compatible = "ti,am33xx", },
|
||||
{ .compatible = "ti,dra7", },
|
||||
{ .compatible = "ti,omap2", },
|
||||
{ .compatible = "ti,omap3", },
|
||||
{ .compatible = "ti,omap4", },
|
||||
|
|
|
@ -1078,15 +1078,11 @@ err_free_policy:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify)
|
||||
static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct kobject *kobj;
|
||||
struct completion *cmp;
|
||||
|
||||
if (notify)
|
||||
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
||||
CPUFREQ_REMOVE_POLICY, policy);
|
||||
|
||||
down_write(&policy->rwsem);
|
||||
cpufreq_stats_free_table(policy);
|
||||
kobj = &policy->kobj;
|
||||
|
@ -1104,7 +1100,7 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify)
|
|||
pr_debug("wait complete\n");
|
||||
}
|
||||
|
||||
static void cpufreq_policy_free(struct cpufreq_policy *policy, bool notify)
|
||||
static void cpufreq_policy_free(struct cpufreq_policy *policy)
|
||||
{
|
||||
unsigned long flags;
|
||||
int cpu;
|
||||
|
@ -1117,7 +1113,7 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy, bool notify)
|
|||
per_cpu(cpufreq_cpu_data, cpu) = NULL;
|
||||
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
||||
|
||||
cpufreq_policy_put_kobj(policy, notify);
|
||||
cpufreq_policy_put_kobj(policy);
|
||||
free_cpumask_var(policy->real_cpus);
|
||||
free_cpumask_var(policy->related_cpus);
|
||||
free_cpumask_var(policy->cpus);
|
||||
|
@ -1170,8 +1166,6 @@ static int cpufreq_online(unsigned int cpu)
|
|||
if (new_policy) {
|
||||
/* related_cpus should at least include policy->cpus. */
|
||||
cpumask_copy(policy->related_cpus, policy->cpus);
|
||||
/* Clear mask of registered CPUs */
|
||||
cpumask_clear(policy->real_cpus);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1244,17 +1238,12 @@ static int cpufreq_online(unsigned int cpu)
|
|||
goto out_exit_policy;
|
||||
|
||||
cpufreq_stats_create_table(policy);
|
||||
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
||||
CPUFREQ_CREATE_POLICY, policy);
|
||||
|
||||
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
||||
list_add(&policy->policy_list, &cpufreq_policy_list);
|
||||
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
||||
}
|
||||
|
||||
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
||||
CPUFREQ_START, policy);
|
||||
|
||||
ret = cpufreq_init_policy(policy);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to initialize policy for cpu: %d (%d)\n",
|
||||
|
@ -1282,7 +1271,7 @@ out_exit_policy:
|
|||
if (cpufreq_driver->exit)
|
||||
cpufreq_driver->exit(policy);
|
||||
out_free_policy:
|
||||
cpufreq_policy_free(policy, !new_policy);
|
||||
cpufreq_policy_free(policy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1403,7 +1392,7 @@ static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
|
|||
remove_cpu_dev_symlink(policy, dev);
|
||||
|
||||
if (cpumask_empty(policy->real_cpus))
|
||||
cpufreq_policy_free(policy, true);
|
||||
cpufreq_policy_free(policy);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,9 +25,7 @@ struct cpufreq_stats {
|
|||
unsigned int last_index;
|
||||
u64 *time_in_state;
|
||||
unsigned int *freq_table;
|
||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
||||
unsigned int *trans_table;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int cpufreq_stats_update(struct cpufreq_stats *stats)
|
||||
|
@ -46,9 +44,7 @@ static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
|
|||
unsigned int count = stats->max_state;
|
||||
|
||||
memset(stats->time_in_state, 0, count * sizeof(u64));
|
||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
||||
memset(stats->trans_table, 0, count * count * sizeof(int));
|
||||
#endif
|
||||
stats->last_time = get_jiffies_64();
|
||||
stats->total_trans = 0;
|
||||
}
|
||||
|
@ -84,7 +80,6 @@ static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
|
|||
return count;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
||||
static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
|
||||
{
|
||||
struct cpufreq_stats *stats = policy->stats;
|
||||
|
@ -129,7 +124,6 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
|
|||
return len;
|
||||
}
|
||||
cpufreq_freq_attr_ro(trans_table);
|
||||
#endif
|
||||
|
||||
cpufreq_freq_attr_ro(total_trans);
|
||||
cpufreq_freq_attr_ro(time_in_state);
|
||||
|
@ -139,9 +133,7 @@ static struct attribute *default_attrs[] = {
|
|||
&total_trans.attr,
|
||||
&time_in_state.attr,
|
||||
&reset.attr,
|
||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
||||
&trans_table.attr,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
static struct attribute_group stats_attr_group = {
|
||||
|
@ -200,9 +192,7 @@ void cpufreq_stats_create_table(struct cpufreq_policy *policy)
|
|||
|
||||
alloc_size = count * sizeof(int) + count * sizeof(u64);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
||||
alloc_size += count * count * sizeof(int);
|
||||
#endif
|
||||
|
||||
/* Allocate memory for time_in_state/freq_table/trans_table in one go */
|
||||
stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
|
||||
|
@ -211,9 +201,7 @@ void cpufreq_stats_create_table(struct cpufreq_policy *policy)
|
|||
|
||||
stats->freq_table = (unsigned int *)(stats->time_in_state + count);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
||||
stats->trans_table = stats->freq_table + count;
|
||||
#endif
|
||||
|
||||
stats->max_state = count;
|
||||
|
||||
|
@ -259,8 +247,6 @@ void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
|
|||
cpufreq_stats_update(stats);
|
||||
|
||||
stats->last_index = new_index;
|
||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
||||
stats->trans_table[old_index * stats->max_state + new_index]++;
|
||||
#endif
|
||||
stats->total_trans++;
|
||||
}
|
||||
|
|
|
@ -358,6 +358,8 @@ static struct pstate_funcs pstate_funcs __read_mostly;
|
|||
static int hwp_active __read_mostly;
|
||||
static bool per_cpu_limits __read_mostly;
|
||||
|
||||
static bool driver_registered __read_mostly;
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static bool acpi_ppc;
|
||||
#endif
|
||||
|
@ -394,6 +396,7 @@ static struct perf_limits *limits = &performance_limits;
|
|||
static struct perf_limits *limits = &powersave_limits;
|
||||
#endif
|
||||
|
||||
static DEFINE_MUTEX(intel_pstate_driver_lock);
|
||||
static DEFINE_MUTEX(intel_pstate_limits_lock);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
@ -538,7 +541,6 @@ static void intel_pstate_exit_perf_limits(struct cpufreq_policy *policy)
|
|||
|
||||
acpi_processor_unregister_performance(policy->cpu);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline void intel_pstate_init_acpi_perf_limits(struct cpufreq_policy *policy)
|
||||
{
|
||||
|
@ -873,7 +875,10 @@ static void intel_pstate_hwp_set(struct cpufreq_policy *policy)
|
|||
|
||||
rdmsrl_on_cpu(cpu, MSR_HWP_CAPABILITIES, &cap);
|
||||
hw_min = HWP_LOWEST_PERF(cap);
|
||||
hw_max = HWP_HIGHEST_PERF(cap);
|
||||
if (limits->no_turbo)
|
||||
hw_max = HWP_GUARANTEED_PERF(cap);
|
||||
else
|
||||
hw_max = HWP_HIGHEST_PERF(cap);
|
||||
range = hw_max - hw_min;
|
||||
|
||||
max_perf_pct = perf_limits->max_perf_pct;
|
||||
|
@ -887,11 +892,6 @@ static void intel_pstate_hwp_set(struct cpufreq_policy *policy)
|
|||
|
||||
adj_range = max_perf_pct * range / 100;
|
||||
max = hw_min + adj_range;
|
||||
if (limits->no_turbo) {
|
||||
hw_max = HWP_GUARANTEED_PERF(cap);
|
||||
if (hw_max < max)
|
||||
max = hw_max;
|
||||
}
|
||||
|
||||
value &= ~HWP_MAX_PERF(~0L);
|
||||
value |= HWP_MAX_PERF(max);
|
||||
|
@ -1007,37 +1007,59 @@ static int pid_param_get(void *data, u64 *val)
|
|||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_pid_param, pid_param_get, pid_param_set, "%llu\n");
|
||||
|
||||
static struct dentry *debugfs_parent;
|
||||
|
||||
struct pid_param {
|
||||
char *name;
|
||||
void *value;
|
||||
struct dentry *dentry;
|
||||
};
|
||||
|
||||
static struct pid_param pid_files[] = {
|
||||
{"sample_rate_ms", &pid_params.sample_rate_ms},
|
||||
{"d_gain_pct", &pid_params.d_gain_pct},
|
||||
{"i_gain_pct", &pid_params.i_gain_pct},
|
||||
{"deadband", &pid_params.deadband},
|
||||
{"setpoint", &pid_params.setpoint},
|
||||
{"p_gain_pct", &pid_params.p_gain_pct},
|
||||
{NULL, NULL}
|
||||
{"sample_rate_ms", &pid_params.sample_rate_ms, },
|
||||
{"d_gain_pct", &pid_params.d_gain_pct, },
|
||||
{"i_gain_pct", &pid_params.i_gain_pct, },
|
||||
{"deadband", &pid_params.deadband, },
|
||||
{"setpoint", &pid_params.setpoint, },
|
||||
{"p_gain_pct", &pid_params.p_gain_pct, },
|
||||
{NULL, NULL, }
|
||||
};
|
||||
|
||||
static void __init intel_pstate_debug_expose_params(void)
|
||||
static void intel_pstate_debug_expose_params(void)
|
||||
{
|
||||
struct dentry *debugfs_parent;
|
||||
int i = 0;
|
||||
int i;
|
||||
|
||||
debugfs_parent = debugfs_create_dir("pstate_snb", NULL);
|
||||
if (IS_ERR_OR_NULL(debugfs_parent))
|
||||
return;
|
||||
while (pid_files[i].name) {
|
||||
debugfs_create_file(pid_files[i].name, 0660,
|
||||
debugfs_parent, pid_files[i].value,
|
||||
&fops_pid_param);
|
||||
i++;
|
||||
|
||||
for (i = 0; pid_files[i].name; i++) {
|
||||
struct dentry *dentry;
|
||||
|
||||
dentry = debugfs_create_file(pid_files[i].name, 0660,
|
||||
debugfs_parent, pid_files[i].value,
|
||||
&fops_pid_param);
|
||||
if (!IS_ERR(dentry))
|
||||
pid_files[i].dentry = dentry;
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_pstate_debug_hide_params(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (IS_ERR_OR_NULL(debugfs_parent))
|
||||
return;
|
||||
|
||||
for (i = 0; pid_files[i].name; i++) {
|
||||
debugfs_remove(pid_files[i].dentry);
|
||||
pid_files[i].dentry = NULL;
|
||||
}
|
||||
|
||||
debugfs_remove(debugfs_parent);
|
||||
debugfs_parent = NULL;
|
||||
}
|
||||
|
||||
/************************** debugfs end ************************/
|
||||
|
||||
/************************** sysfs begin ************************/
|
||||
|
@ -1048,6 +1070,34 @@ static void __init intel_pstate_debug_expose_params(void)
|
|||
return sprintf(buf, "%u\n", limits->object); \
|
||||
}
|
||||
|
||||
static ssize_t intel_pstate_show_status(char *buf);
|
||||
static int intel_pstate_update_status(const char *buf, size_t size);
|
||||
|
||||
static ssize_t show_status(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
ret = intel_pstate_show_status(buf);
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t store_status(struct kobject *a, struct attribute *b,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
char *p = memchr(buf, '\n', count);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
ret = intel_pstate_update_status(buf, p ? p - buf : count);
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
|
||||
return ret < 0 ? ret : count;
|
||||
}
|
||||
|
||||
static ssize_t show_turbo_pct(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -1055,12 +1105,22 @@ static ssize_t show_turbo_pct(struct kobject *kobj,
|
|||
int total, no_turbo, turbo_pct;
|
||||
uint32_t turbo_fp;
|
||||
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
|
||||
if (!driver_registered) {
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
cpu = all_cpu_data[0];
|
||||
|
||||
total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1;
|
||||
no_turbo = cpu->pstate.max_pstate - cpu->pstate.min_pstate + 1;
|
||||
turbo_fp = div_fp(no_turbo, total);
|
||||
turbo_pct = 100 - fp_toint(mul_fp(turbo_fp, int_tofp(100)));
|
||||
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
|
||||
return sprintf(buf, "%u\n", turbo_pct);
|
||||
}
|
||||
|
||||
|
@ -1070,8 +1130,18 @@ static ssize_t show_num_pstates(struct kobject *kobj,
|
|||
struct cpudata *cpu;
|
||||
int total;
|
||||
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
|
||||
if (!driver_registered) {
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
cpu = all_cpu_data[0];
|
||||
total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1;
|
||||
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
|
||||
return sprintf(buf, "%u\n", total);
|
||||
}
|
||||
|
||||
|
@ -1080,12 +1150,21 @@ static ssize_t show_no_turbo(struct kobject *kobj,
|
|||
{
|
||||
ssize_t ret;
|
||||
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
|
||||
if (!driver_registered) {
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
update_turbo_state();
|
||||
if (limits->turbo_disabled)
|
||||
ret = sprintf(buf, "%u\n", limits->turbo_disabled);
|
||||
else
|
||||
ret = sprintf(buf, "%u\n", limits->no_turbo);
|
||||
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1099,12 +1178,20 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b,
|
|||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
|
||||
if (!driver_registered) {
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
mutex_lock(&intel_pstate_limits_lock);
|
||||
|
||||
update_turbo_state();
|
||||
if (limits->turbo_disabled) {
|
||||
pr_warn("Turbo disabled by BIOS or unavailable on processor\n");
|
||||
mutex_unlock(&intel_pstate_limits_lock);
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
|
@ -1114,6 +1201,8 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b,
|
|||
|
||||
intel_pstate_update_policies();
|
||||
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1127,6 +1216,13 @@ static ssize_t store_max_perf_pct(struct kobject *a, struct attribute *b,
|
|||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
|
||||
if (!driver_registered) {
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
mutex_lock(&intel_pstate_limits_lock);
|
||||
|
||||
limits->max_sysfs_pct = clamp_t(int, input, 0 , 100);
|
||||
|
@ -1142,6 +1238,8 @@ static ssize_t store_max_perf_pct(struct kobject *a, struct attribute *b,
|
|||
|
||||
intel_pstate_update_policies();
|
||||
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1155,6 +1253,13 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct attribute *b,
|
|||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
|
||||
if (!driver_registered) {
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
mutex_lock(&intel_pstate_limits_lock);
|
||||
|
||||
limits->min_sysfs_pct = clamp_t(int, input, 0 , 100);
|
||||
|
@ -1170,12 +1275,15 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct attribute *b,
|
|||
|
||||
intel_pstate_update_policies();
|
||||
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
show_one(max_perf_pct, max_perf_pct);
|
||||
show_one(min_perf_pct, min_perf_pct);
|
||||
|
||||
define_one_global_rw(status);
|
||||
define_one_global_rw(no_turbo);
|
||||
define_one_global_rw(max_perf_pct);
|
||||
define_one_global_rw(min_perf_pct);
|
||||
|
@ -1183,6 +1291,7 @@ define_one_global_ro(turbo_pct);
|
|||
define_one_global_ro(num_pstates);
|
||||
|
||||
static struct attribute *intel_pstate_attributes[] = {
|
||||
&status.attr,
|
||||
&no_turbo.attr,
|
||||
&turbo_pct.attr,
|
||||
&num_pstates.attr,
|
||||
|
@ -1364,48 +1473,71 @@ static int core_get_max_pstate_physical(void)
|
|||
return (value >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static int core_get_tdp_ratio(u64 plat_info)
|
||||
{
|
||||
/* Check how many TDP levels present */
|
||||
if (plat_info & 0x600000000) {
|
||||
u64 tdp_ctrl;
|
||||
u64 tdp_ratio;
|
||||
int tdp_msr;
|
||||
int err;
|
||||
|
||||
/* Get the TDP level (0, 1, 2) to get ratios */
|
||||
err = rdmsrl_safe(MSR_CONFIG_TDP_CONTROL, &tdp_ctrl);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* TDP MSR are continuous starting at 0x648 */
|
||||
tdp_msr = MSR_CONFIG_TDP_NOMINAL + (tdp_ctrl & 0x03);
|
||||
err = rdmsrl_safe(tdp_msr, &tdp_ratio);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* For level 1 and 2, bits[23:16] contain the ratio */
|
||||
if (tdp_ctrl & 0x03)
|
||||
tdp_ratio >>= 16;
|
||||
|
||||
tdp_ratio &= 0xff; /* ratios are only 8 bits long */
|
||||
pr_debug("tdp_ratio %x\n", (int)tdp_ratio);
|
||||
|
||||
return (int)tdp_ratio;
|
||||
}
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int core_get_max_pstate(void)
|
||||
{
|
||||
u64 tar;
|
||||
u64 plat_info;
|
||||
int max_pstate;
|
||||
int tdp_ratio;
|
||||
int err;
|
||||
|
||||
rdmsrl(MSR_PLATFORM_INFO, plat_info);
|
||||
max_pstate = (plat_info >> 8) & 0xFF;
|
||||
|
||||
tdp_ratio = core_get_tdp_ratio(plat_info);
|
||||
if (tdp_ratio <= 0)
|
||||
return max_pstate;
|
||||
|
||||
if (hwp_active) {
|
||||
/* Turbo activation ratio is not used on HWP platforms */
|
||||
return tdp_ratio;
|
||||
}
|
||||
|
||||
err = rdmsrl_safe(MSR_TURBO_ACTIVATION_RATIO, &tar);
|
||||
if (!err) {
|
||||
int tar_levels;
|
||||
|
||||
/* Do some sanity checking for safety */
|
||||
if (plat_info & 0x600000000) {
|
||||
u64 tdp_ctrl;
|
||||
u64 tdp_ratio;
|
||||
int tdp_msr;
|
||||
|
||||
err = rdmsrl_safe(MSR_CONFIG_TDP_CONTROL, &tdp_ctrl);
|
||||
if (err)
|
||||
goto skip_tar;
|
||||
|
||||
tdp_msr = MSR_CONFIG_TDP_NOMINAL + (tdp_ctrl & 0x3);
|
||||
err = rdmsrl_safe(tdp_msr, &tdp_ratio);
|
||||
if (err)
|
||||
goto skip_tar;
|
||||
|
||||
/* For level 1 and 2, bits[23:16] contain the ratio */
|
||||
if (tdp_ctrl)
|
||||
tdp_ratio >>= 16;
|
||||
|
||||
tdp_ratio &= 0xff; /* ratios are only 8 bits long */
|
||||
if (tdp_ratio - 1 == tar) {
|
||||
max_pstate = tar;
|
||||
pr_debug("max_pstate=TAC %x\n", max_pstate);
|
||||
} else {
|
||||
goto skip_tar;
|
||||
}
|
||||
tar_levels = tar & 0xff;
|
||||
if (tdp_ratio - 1 == tar_levels) {
|
||||
max_pstate = tar_levels;
|
||||
pr_debug("max_pstate=TAC %x\n", max_pstate);
|
||||
}
|
||||
}
|
||||
|
||||
skip_tar:
|
||||
return max_pstate;
|
||||
}
|
||||
|
||||
|
@ -2072,6 +2204,20 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
|
|||
|
||||
static int intel_pstate_verify_policy(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct cpudata *cpu = all_cpu_data[policy->cpu];
|
||||
struct perf_limits *perf_limits;
|
||||
|
||||
if (policy->policy == CPUFREQ_POLICY_PERFORMANCE)
|
||||
perf_limits = &performance_limits;
|
||||
else
|
||||
perf_limits = &powersave_limits;
|
||||
|
||||
update_turbo_state();
|
||||
policy->cpuinfo.max_freq = perf_limits->turbo_disabled ||
|
||||
perf_limits->no_turbo ?
|
||||
cpu->pstate.max_freq :
|
||||
cpu->pstate.turbo_freq;
|
||||
|
||||
cpufreq_verify_within_cpu_limits(policy);
|
||||
|
||||
if (policy->policy != CPUFREQ_POLICY_POWERSAVE &&
|
||||
|
@ -2299,6 +2445,111 @@ static struct cpufreq_driver intel_cpufreq = {
|
|||
|
||||
static struct cpufreq_driver *intel_pstate_driver = &intel_pstate;
|
||||
|
||||
static void intel_pstate_driver_cleanup(void)
|
||||
{
|
||||
unsigned int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu) {
|
||||
if (all_cpu_data[cpu]) {
|
||||
if (intel_pstate_driver == &intel_pstate)
|
||||
intel_pstate_clear_update_util_hook(cpu);
|
||||
|
||||
kfree(all_cpu_data[cpu]);
|
||||
all_cpu_data[cpu] = NULL;
|
||||
}
|
||||
}
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static int intel_pstate_register_driver(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = cpufreq_register_driver(intel_pstate_driver);
|
||||
if (ret) {
|
||||
intel_pstate_driver_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&intel_pstate_limits_lock);
|
||||
driver_registered = true;
|
||||
mutex_unlock(&intel_pstate_limits_lock);
|
||||
|
||||
if (intel_pstate_driver == &intel_pstate && !hwp_active &&
|
||||
pstate_funcs.get_target_pstate != get_target_pstate_use_cpu_load)
|
||||
intel_pstate_debug_expose_params();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_pstate_unregister_driver(void)
|
||||
{
|
||||
if (hwp_active)
|
||||
return -EBUSY;
|
||||
|
||||
if (intel_pstate_driver == &intel_pstate && !hwp_active &&
|
||||
pstate_funcs.get_target_pstate != get_target_pstate_use_cpu_load)
|
||||
intel_pstate_debug_hide_params();
|
||||
|
||||
mutex_lock(&intel_pstate_limits_lock);
|
||||
driver_registered = false;
|
||||
mutex_unlock(&intel_pstate_limits_lock);
|
||||
|
||||
cpufreq_unregister_driver(intel_pstate_driver);
|
||||
intel_pstate_driver_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t intel_pstate_show_status(char *buf)
|
||||
{
|
||||
if (!driver_registered)
|
||||
return sprintf(buf, "off\n");
|
||||
|
||||
return sprintf(buf, "%s\n", intel_pstate_driver == &intel_pstate ?
|
||||
"active" : "passive");
|
||||
}
|
||||
|
||||
static int intel_pstate_update_status(const char *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (size == 3 && !strncmp(buf, "off", size))
|
||||
return driver_registered ?
|
||||
intel_pstate_unregister_driver() : -EINVAL;
|
||||
|
||||
if (size == 6 && !strncmp(buf, "active", size)) {
|
||||
if (driver_registered) {
|
||||
if (intel_pstate_driver == &intel_pstate)
|
||||
return 0;
|
||||
|
||||
ret = intel_pstate_unregister_driver();
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
intel_pstate_driver = &intel_pstate;
|
||||
return intel_pstate_register_driver();
|
||||
}
|
||||
|
||||
if (size == 7 && !strncmp(buf, "passive", size)) {
|
||||
if (driver_registered) {
|
||||
if (intel_pstate_driver != &intel_pstate)
|
||||
return 0;
|
||||
|
||||
ret = intel_pstate_unregister_driver();
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
intel_pstate_driver = &intel_cpufreq;
|
||||
return intel_pstate_register_driver();
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int no_load __initdata;
|
||||
static int no_hwp __initdata;
|
||||
static int hwp_only __initdata;
|
||||
|
@ -2486,9 +2737,9 @@ static const struct x86_cpu_id hwp_support_ids[] __initconst = {
|
|||
|
||||
static int __init intel_pstate_init(void)
|
||||
{
|
||||
int cpu, rc = 0;
|
||||
const struct x86_cpu_id *id;
|
||||
struct cpu_defaults *cpu_def;
|
||||
int rc = 0;
|
||||
|
||||
if (no_load)
|
||||
return -ENODEV;
|
||||
|
@ -2520,45 +2771,29 @@ hwp_cpu_matched:
|
|||
if (intel_pstate_platform_pwr_mgmt_exists())
|
||||
return -ENODEV;
|
||||
|
||||
if (!hwp_active && hwp_only)
|
||||
return -ENOTSUPP;
|
||||
|
||||
pr_info("Intel P-state driver initializing\n");
|
||||
|
||||
all_cpu_data = vzalloc(sizeof(void *) * num_possible_cpus());
|
||||
if (!all_cpu_data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!hwp_active && hwp_only)
|
||||
goto out;
|
||||
|
||||
intel_pstate_request_control_from_smm();
|
||||
|
||||
rc = cpufreq_register_driver(intel_pstate_driver);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (intel_pstate_driver == &intel_pstate && !hwp_active &&
|
||||
pstate_funcs.get_target_pstate != get_target_pstate_use_cpu_load)
|
||||
intel_pstate_debug_expose_params();
|
||||
|
||||
intel_pstate_sysfs_expose_params();
|
||||
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
rc = intel_pstate_register_driver();
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (hwp_active)
|
||||
pr_info("HWP enabled\n");
|
||||
|
||||
return rc;
|
||||
out:
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu) {
|
||||
if (all_cpu_data[cpu]) {
|
||||
if (intel_pstate_driver == &intel_pstate)
|
||||
intel_pstate_clear_update_util_hook(cpu);
|
||||
|
||||
kfree(all_cpu_data[cpu]);
|
||||
}
|
||||
}
|
||||
|
||||
put_online_cpus();
|
||||
vfree(all_cpu_data);
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
device_initcall(intel_pstate_init);
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ static struct powernv_pstate_info {
|
|||
unsigned int max;
|
||||
unsigned int nominal;
|
||||
unsigned int nr_pstates;
|
||||
bool wof_enabled;
|
||||
} powernv_pstate_info;
|
||||
|
||||
/* Use following macros for conversions between pstate_id and index */
|
||||
|
@ -203,6 +204,7 @@ static int init_powernv_pstates(void)
|
|||
const __be32 *pstate_ids, *pstate_freqs;
|
||||
u32 len_ids, len_freqs;
|
||||
u32 pstate_min, pstate_max, pstate_nominal;
|
||||
u32 pstate_turbo, pstate_ultra_turbo;
|
||||
|
||||
power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
|
||||
if (!power_mgt) {
|
||||
|
@ -225,8 +227,29 @@ static int init_powernv_pstates(void)
|
|||
pr_warn("ibm,pstate-nominal not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(power_mgt, "ibm,pstate-ultra-turbo",
|
||||
&pstate_ultra_turbo)) {
|
||||
powernv_pstate_info.wof_enabled = false;
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(power_mgt, "ibm,pstate-turbo",
|
||||
&pstate_turbo)) {
|
||||
powernv_pstate_info.wof_enabled = false;
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (pstate_turbo == pstate_ultra_turbo)
|
||||
powernv_pstate_info.wof_enabled = false;
|
||||
else
|
||||
powernv_pstate_info.wof_enabled = true;
|
||||
|
||||
next:
|
||||
pr_info("cpufreq pstate min %d nominal %d max %d\n", pstate_min,
|
||||
pstate_nominal, pstate_max);
|
||||
pr_info("Workload Optimized Frequency is %s in the platform\n",
|
||||
(powernv_pstate_info.wof_enabled) ? "enabled" : "disabled");
|
||||
|
||||
pstate_ids = of_get_property(power_mgt, "ibm,pstate-ids", &len_ids);
|
||||
if (!pstate_ids) {
|
||||
|
@ -268,6 +291,13 @@ static int init_powernv_pstates(void)
|
|||
powernv_pstate_info.nominal = i;
|
||||
else if (id == pstate_min)
|
||||
powernv_pstate_info.min = i;
|
||||
|
||||
if (powernv_pstate_info.wof_enabled && id == pstate_turbo) {
|
||||
int j;
|
||||
|
||||
for (j = i - 1; j >= (int)powernv_pstate_info.max; j--)
|
||||
powernv_freqs[j].flags = CPUFREQ_BOOST_FREQ;
|
||||
}
|
||||
}
|
||||
|
||||
/* End of list marker entry */
|
||||
|
@ -305,9 +335,12 @@ static ssize_t cpuinfo_nominal_freq_show(struct cpufreq_policy *policy,
|
|||
struct freq_attr cpufreq_freq_attr_cpuinfo_nominal_freq =
|
||||
__ATTR_RO(cpuinfo_nominal_freq);
|
||||
|
||||
#define SCALING_BOOST_FREQS_ATTR_INDEX 2
|
||||
|
||||
static struct freq_attr *powernv_cpu_freq_attr[] = {
|
||||
&cpufreq_freq_attr_scaling_available_freqs,
|
||||
&cpufreq_freq_attr_cpuinfo_nominal_freq,
|
||||
&cpufreq_freq_attr_scaling_boost_freqs,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -1013,11 +1046,22 @@ static int __init powernv_cpufreq_init(void)
|
|||
register_reboot_notifier(&powernv_cpufreq_reboot_nb);
|
||||
opal_message_notifier_register(OPAL_MSG_OCC, &powernv_cpufreq_opal_nb);
|
||||
|
||||
rc = cpufreq_register_driver(&powernv_cpufreq_driver);
|
||||
if (!rc)
|
||||
return 0;
|
||||
if (powernv_pstate_info.wof_enabled)
|
||||
powernv_cpufreq_driver.boost_enabled = true;
|
||||
else
|
||||
powernv_cpu_freq_attr[SCALING_BOOST_FREQS_ATTR_INDEX] = NULL;
|
||||
|
||||
pr_info("Failed to register the cpufreq driver (%d)\n", rc);
|
||||
rc = cpufreq_register_driver(&powernv_cpufreq_driver);
|
||||
if (rc) {
|
||||
pr_info("Failed to register the cpufreq driver (%d)\n", rc);
|
||||
goto cleanup_notifiers;
|
||||
}
|
||||
|
||||
if (powernv_pstate_info.wof_enabled)
|
||||
cpufreq_enable_boost_support();
|
||||
|
||||
return 0;
|
||||
cleanup_notifiers:
|
||||
unregister_all_notifiers();
|
||||
clean_chip_info();
|
||||
out:
|
||||
|
|
|
@ -100,9 +100,6 @@ static int pmi_notifier(struct notifier_block *nb,
|
|||
/* Should this really be called for CPUFREQ_ADJUST and CPUFREQ_NOTIFY
|
||||
* policy events?)
|
||||
*/
|
||||
if (event == CPUFREQ_START)
|
||||
return 0;
|
||||
|
||||
node = cbe_cpu_to_node(policy->cpu);
|
||||
|
||||
pr_debug("got notified, event=%lu, node=%u\n", event, node);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/errno.h>
|
||||
|
@ -37,53 +38,20 @@ struct cpu_data {
|
|||
struct thermal_cooling_device *cdev;
|
||||
};
|
||||
|
||||
/*
|
||||
* Don't use cpufreq on this SoC -- used when the SoC would have otherwise
|
||||
* matched a more generic compatible.
|
||||
*/
|
||||
#define SOC_BLACKLIST 1
|
||||
|
||||
/**
|
||||
* struct soc_data - SoC specific data
|
||||
* @freq_mask: mask the disallowed frequencies
|
||||
* @flag: unique flags
|
||||
* @flags: SOC_xxx
|
||||
*/
|
||||
struct soc_data {
|
||||
u32 freq_mask[4];
|
||||
u32 flag;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
#define FREQ_MASK 1
|
||||
/* see hardware specification for the allowed frqeuencies */
|
||||
static const struct soc_data sdata[] = {
|
||||
{ /* used by p2041 and p3041 */
|
||||
.freq_mask = {0x8, 0x8, 0x2, 0x2},
|
||||
.flag = FREQ_MASK,
|
||||
},
|
||||
{ /* used by p5020 */
|
||||
.freq_mask = {0x8, 0x2},
|
||||
.flag = FREQ_MASK,
|
||||
},
|
||||
{ /* used by p4080, p5040 */
|
||||
.freq_mask = {0},
|
||||
.flag = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* the minimum allowed core frequency, in Hz
|
||||
* for chassis v1.0, >= platform frequency
|
||||
* for chassis v2.0, >= platform frequency / 2
|
||||
*/
|
||||
static u32 min_cpufreq;
|
||||
static const u32 *fmask;
|
||||
|
||||
#if defined(CONFIG_ARM)
|
||||
static int get_cpu_physical_id(int cpu)
|
||||
{
|
||||
return topology_core_id(cpu);
|
||||
}
|
||||
#else
|
||||
static int get_cpu_physical_id(int cpu)
|
||||
{
|
||||
return get_hard_smp_processor_id(cpu);
|
||||
}
|
||||
#endif
|
||||
|
||||
static u32 get_bus_freq(void)
|
||||
{
|
||||
struct device_node *soc;
|
||||
|
@ -101,9 +69,10 @@ static u32 get_bus_freq(void)
|
|||
return sysfreq;
|
||||
}
|
||||
|
||||
static struct device_node *cpu_to_clk_node(int cpu)
|
||||
static struct clk *cpu_to_clk(int cpu)
|
||||
{
|
||||
struct device_node *np, *clk_np;
|
||||
struct device_node *np;
|
||||
struct clk *clk;
|
||||
|
||||
if (!cpu_present(cpu))
|
||||
return NULL;
|
||||
|
@ -112,37 +81,28 @@ static struct device_node *cpu_to_clk_node(int cpu)
|
|||
if (!np)
|
||||
return NULL;
|
||||
|
||||
clk_np = of_parse_phandle(np, "clocks", 0);
|
||||
if (!clk_np)
|
||||
return NULL;
|
||||
|
||||
clk = of_clk_get(np, 0);
|
||||
of_node_put(np);
|
||||
|
||||
return clk_np;
|
||||
return clk;
|
||||
}
|
||||
|
||||
/* traverse cpu nodes to get cpu mask of sharing clock wire */
|
||||
static void set_affected_cpus(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct device_node *np, *clk_np;
|
||||
struct cpumask *dstp = policy->cpus;
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
np = cpu_to_clk_node(policy->cpu);
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
for_each_present_cpu(i) {
|
||||
clk_np = cpu_to_clk_node(i);
|
||||
if (!clk_np)
|
||||
clk = cpu_to_clk(i);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: no clock for cpu %d\n", __func__, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clk_np == np)
|
||||
if (clk_is_match(policy->clk, clk))
|
||||
cpumask_set_cpu(i, dstp);
|
||||
|
||||
of_node_put(clk_np);
|
||||
}
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
/* reduce the duplicated frequencies in frequency table */
|
||||
|
@ -198,10 +158,11 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
|
|||
|
||||
static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct device_node *np, *pnode;
|
||||
struct device_node *np;
|
||||
int i, count, ret;
|
||||
u32 freq, mask;
|
||||
u32 freq;
|
||||
struct clk *clk;
|
||||
const struct clk_hw *hwclk;
|
||||
struct cpufreq_frequency_table *table;
|
||||
struct cpu_data *data;
|
||||
unsigned int cpu = policy->cpu;
|
||||
|
@ -221,17 +182,13 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|||
goto err_nomem2;
|
||||
}
|
||||
|
||||
pnode = of_parse_phandle(np, "clocks", 0);
|
||||
if (!pnode) {
|
||||
pr_err("%s: could not get clock information\n", __func__);
|
||||
goto err_nomem2;
|
||||
}
|
||||
hwclk = __clk_get_hw(policy->clk);
|
||||
count = clk_hw_get_num_parents(hwclk);
|
||||
|
||||
count = of_property_count_strings(pnode, "clock-names");
|
||||
data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
|
||||
if (!data->pclk) {
|
||||
pr_err("%s: no memory\n", __func__);
|
||||
goto err_node;
|
||||
goto err_nomem2;
|
||||
}
|
||||
|
||||
table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
|
||||
|
@ -240,23 +197,11 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|||
goto err_pclk;
|
||||
}
|
||||
|
||||
if (fmask)
|
||||
mask = fmask[get_cpu_physical_id(cpu)];
|
||||
else
|
||||
mask = 0x0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
clk = of_clk_get(pnode, i);
|
||||
clk = clk_hw_get_parent_by_index(hwclk, i)->clk;
|
||||
data->pclk[i] = clk;
|
||||
freq = clk_get_rate(clk);
|
||||
/*
|
||||
* the clock is valid if its frequency is not masked
|
||||
* and large than minimum allowed frequency.
|
||||
*/
|
||||
if (freq < min_cpufreq || (mask & (1 << i)))
|
||||
table[i].frequency = CPUFREQ_ENTRY_INVALID;
|
||||
else
|
||||
table[i].frequency = freq / 1000;
|
||||
table[i].frequency = freq / 1000;
|
||||
table[i].driver_data = i;
|
||||
}
|
||||
freq_table_redup(table, count);
|
||||
|
@ -282,7 +227,6 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|||
policy->cpuinfo.transition_latency = u64temp + 1;
|
||||
|
||||
of_node_put(np);
|
||||
of_node_put(pnode);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -290,10 +234,7 @@ err_nomem1:
|
|||
kfree(table);
|
||||
err_pclk:
|
||||
kfree(data->pclk);
|
||||
err_node:
|
||||
of_node_put(pnode);
|
||||
err_nomem2:
|
||||
policy->driver_data = NULL;
|
||||
kfree(data);
|
||||
err_np:
|
||||
of_node_put(np);
|
||||
|
@ -357,12 +298,25 @@ static struct cpufreq_driver qoriq_cpufreq_driver = {
|
|||
.attr = cpufreq_generic_attr,
|
||||
};
|
||||
|
||||
static const struct soc_data blacklist = {
|
||||
.flags = SOC_BLACKLIST,
|
||||
};
|
||||
|
||||
static const struct of_device_id node_matches[] __initconst = {
|
||||
{ .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
|
||||
{ .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
|
||||
{ .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
|
||||
{ .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
|
||||
{ .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
|
||||
/* e6500 cannot use cpufreq due to erratum A-008083 */
|
||||
{ .compatible = "fsl,b4420-clockgen", &blacklist },
|
||||
{ .compatible = "fsl,b4860-clockgen", &blacklist },
|
||||
{ .compatible = "fsl,t2080-clockgen", &blacklist },
|
||||
{ .compatible = "fsl,t4240-clockgen", &blacklist },
|
||||
|
||||
{ .compatible = "fsl,ls1012a-clockgen", },
|
||||
{ .compatible = "fsl,ls1021a-clockgen", },
|
||||
{ .compatible = "fsl,ls1043a-clockgen", },
|
||||
{ .compatible = "fsl,ls1046a-clockgen", },
|
||||
{ .compatible = "fsl,ls1088a-clockgen", },
|
||||
{ .compatible = "fsl,ls2080a-clockgen", },
|
||||
{ .compatible = "fsl,p4080-clockgen", },
|
||||
{ .compatible = "fsl,qoriq-clockgen-1.0", },
|
||||
{ .compatible = "fsl,qoriq-clockgen-2.0", },
|
||||
{}
|
||||
};
|
||||
|
@ -380,16 +334,12 @@ static int __init qoriq_cpufreq_init(void)
|
|||
|
||||
match = of_match_node(node_matches, np);
|
||||
data = match->data;
|
||||
if (data) {
|
||||
if (data->flag)
|
||||
fmask = data->freq_mask;
|
||||
min_cpufreq = get_bus_freq();
|
||||
} else {
|
||||
min_cpufreq = get_bus_freq() / 2;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
if (data && data->flags & SOC_BLACKLIST)
|
||||
return -ENODEV;
|
||||
|
||||
ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
|
||||
if (!ret)
|
||||
pr_info("Freescale QorIQ CPU frequency scaling driver\n");
|
||||
|
|
|
@ -400,7 +400,6 @@ static int s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
|
|||
rate = clk_get_rate(s3c_freq->hclk);
|
||||
if (rate < 133 * 1000 * 1000) {
|
||||
pr_err("cpufreq: HCLK not at 133MHz\n");
|
||||
clk_put(s3c_freq->hclk);
|
||||
ret = -EINVAL;
|
||||
goto err_armclk;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* TI CPUFreq/OPP hw-supported driver
|
||||
*
|
||||
* Copyright (C) 2016-2017 Texas Instruments, Inc.
|
||||
* Dave Gerlach <d-gerlach@ti.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define REVISION_MASK 0xF
|
||||
#define REVISION_SHIFT 28
|
||||
|
||||
#define AM33XX_800M_ARM_MPU_MAX_FREQ 0x1E2F
|
||||
#define AM43XX_600M_ARM_MPU_MAX_FREQ 0xFFA
|
||||
|
||||
#define DRA7_EFUSE_HAS_OD_MPU_OPP 11
|
||||
#define DRA7_EFUSE_HAS_HIGH_MPU_OPP 15
|
||||
#define DRA7_EFUSE_HAS_ALL_MPU_OPP 23
|
||||
|
||||
#define DRA7_EFUSE_NOM_MPU_OPP BIT(0)
|
||||
#define DRA7_EFUSE_OD_MPU_OPP BIT(1)
|
||||
#define DRA7_EFUSE_HIGH_MPU_OPP BIT(2)
|
||||
|
||||
#define VERSION_COUNT 2
|
||||
|
||||
struct ti_cpufreq_data;
|
||||
|
||||
struct ti_cpufreq_soc_data {
|
||||
unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data,
|
||||
unsigned long efuse);
|
||||
unsigned long efuse_fallback;
|
||||
unsigned long efuse_offset;
|
||||
unsigned long efuse_mask;
|
||||
unsigned long efuse_shift;
|
||||
unsigned long rev_offset;
|
||||
};
|
||||
|
||||
struct ti_cpufreq_data {
|
||||
struct device *cpu_dev;
|
||||
struct device_node *opp_node;
|
||||
struct regmap *syscon;
|
||||
const struct ti_cpufreq_soc_data *soc_data;
|
||||
};
|
||||
|
||||
static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data,
|
||||
unsigned long efuse)
|
||||
{
|
||||
if (!efuse)
|
||||
efuse = opp_data->soc_data->efuse_fallback;
|
||||
/* AM335x and AM437x use "OPP disable" bits, so invert */
|
||||
return ~efuse;
|
||||
}
|
||||
|
||||
static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data,
|
||||
unsigned long efuse)
|
||||
{
|
||||
unsigned long calculated_efuse = DRA7_EFUSE_NOM_MPU_OPP;
|
||||
|
||||
/*
|
||||
* The efuse on dra7 and am57 parts contains a specific
|
||||
* value indicating the highest available OPP.
|
||||
*/
|
||||
|
||||
switch (efuse) {
|
||||
case DRA7_EFUSE_HAS_ALL_MPU_OPP:
|
||||
case DRA7_EFUSE_HAS_HIGH_MPU_OPP:
|
||||
calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP;
|
||||
case DRA7_EFUSE_HAS_OD_MPU_OPP:
|
||||
calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP;
|
||||
}
|
||||
|
||||
return calculated_efuse;
|
||||
}
|
||||
|
||||
static struct ti_cpufreq_soc_data am3x_soc_data = {
|
||||
.efuse_xlate = amx3_efuse_xlate,
|
||||
.efuse_fallback = AM33XX_800M_ARM_MPU_MAX_FREQ,
|
||||
.efuse_offset = 0x07fc,
|
||||
.efuse_mask = 0x1fff,
|
||||
.rev_offset = 0x600,
|
||||
};
|
||||
|
||||
static struct ti_cpufreq_soc_data am4x_soc_data = {
|
||||
.efuse_xlate = amx3_efuse_xlate,
|
||||
.efuse_fallback = AM43XX_600M_ARM_MPU_MAX_FREQ,
|
||||
.efuse_offset = 0x0610,
|
||||
.efuse_mask = 0x3f,
|
||||
.rev_offset = 0x600,
|
||||
};
|
||||
|
||||
static struct ti_cpufreq_soc_data dra7_soc_data = {
|
||||
.efuse_xlate = dra7_efuse_xlate,
|
||||
.efuse_offset = 0x020c,
|
||||
.efuse_mask = 0xf80000,
|
||||
.efuse_shift = 19,
|
||||
.rev_offset = 0x204,
|
||||
};
|
||||
|
||||
/**
|
||||
* ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC
|
||||
* @opp_data: pointer to ti_cpufreq_data context
|
||||
* @efuse_value: Set to the value parsed from efuse
|
||||
*
|
||||
* Returns error code if efuse not read properly.
|
||||
*/
|
||||
static int ti_cpufreq_get_efuse(struct ti_cpufreq_data *opp_data,
|
||||
u32 *efuse_value)
|
||||
{
|
||||
struct device *dev = opp_data->cpu_dev;
|
||||
u32 efuse;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset,
|
||||
&efuse);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to read the efuse value from syscon: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
efuse = (efuse & opp_data->soc_data->efuse_mask);
|
||||
efuse >>= opp_data->soc_data->efuse_shift;
|
||||
|
||||
*efuse_value = opp_data->soc_data->efuse_xlate(opp_data, efuse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_cpufreq_get_rev() - Parse and return rev value present on SoC
|
||||
* @opp_data: pointer to ti_cpufreq_data context
|
||||
* @revision_value: Set to the value parsed from revision register
|
||||
*
|
||||
* Returns error code if revision not read properly.
|
||||
*/
|
||||
static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data,
|
||||
u32 *revision_value)
|
||||
{
|
||||
struct device *dev = opp_data->cpu_dev;
|
||||
u32 revision;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset,
|
||||
&revision);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to read the revision number from syscon: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*revision_value = BIT((revision >> REVISION_SHIFT) & REVISION_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data *opp_data)
|
||||
{
|
||||
struct device *dev = opp_data->cpu_dev;
|
||||
struct device_node *np = opp_data->opp_node;
|
||||
|
||||
opp_data->syscon = syscon_regmap_lookup_by_phandle(np,
|
||||
"syscon");
|
||||
if (IS_ERR(opp_data->syscon)) {
|
||||
dev_err(dev,
|
||||
"\"syscon\" is missing, cannot use OPPv2 table.\n");
|
||||
return PTR_ERR(opp_data->syscon);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_cpufreq_of_match[] = {
|
||||
{ .compatible = "ti,am33xx", .data = &am3x_soc_data, },
|
||||
{ .compatible = "ti,am4372", .data = &am4x_soc_data, },
|
||||
{ .compatible = "ti,dra7", .data = &dra7_soc_data },
|
||||
{},
|
||||
};
|
||||
|
||||
static int ti_cpufreq_init(void)
|
||||
{
|
||||
u32 version[VERSION_COUNT];
|
||||
struct device_node *np;
|
||||
const struct of_device_id *match;
|
||||
struct ti_cpufreq_data *opp_data;
|
||||
int ret;
|
||||
|
||||
np = of_find_node_by_path("/");
|
||||
match = of_match_node(ti_cpufreq_of_match, np);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
opp_data = kzalloc(sizeof(*opp_data), GFP_KERNEL);
|
||||
if (!opp_data)
|
||||
return -ENOMEM;
|
||||
|
||||
opp_data->soc_data = match->data;
|
||||
|
||||
opp_data->cpu_dev = get_cpu_device(0);
|
||||
if (!opp_data->cpu_dev) {
|
||||
pr_err("%s: Failed to get device for CPU0\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
opp_data->opp_node = dev_pm_opp_of_get_opp_desc_node(opp_data->cpu_dev);
|
||||
if (!opp_data->opp_node) {
|
||||
dev_info(opp_data->cpu_dev,
|
||||
"OPP-v2 not supported, cpufreq-dt will attempt to use legacy tables.\n");
|
||||
goto register_cpufreq_dt;
|
||||
}
|
||||
|
||||
ret = ti_cpufreq_setup_syscon_register(opp_data);
|
||||
if (ret)
|
||||
goto fail_put_node;
|
||||
|
||||
/*
|
||||
* OPPs determine whether or not they are supported based on
|
||||
* two metrics:
|
||||
* 0 - SoC Revision
|
||||
* 1 - eFuse value
|
||||
*/
|
||||
ret = ti_cpufreq_get_rev(opp_data, &version[0]);
|
||||
if (ret)
|
||||
goto fail_put_node;
|
||||
|
||||
ret = ti_cpufreq_get_efuse(opp_data, &version[1]);
|
||||
if (ret)
|
||||
goto fail_put_node;
|
||||
|
||||
of_node_put(opp_data->opp_node);
|
||||
|
||||
ret = PTR_ERR_OR_ZERO(dev_pm_opp_set_supported_hw(opp_data->cpu_dev,
|
||||
version, VERSION_COUNT));
|
||||
if (ret) {
|
||||
dev_err(opp_data->cpu_dev,
|
||||
"Failed to set supported hardware\n");
|
||||
goto fail_put_node;
|
||||
}
|
||||
|
||||
register_cpufreq_dt:
|
||||
platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_put_node:
|
||||
of_node_put(opp_data->opp_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
device_initcall(ti_cpufreq_init);
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
#define CPUFREQ_ETERNAL (-1)
|
||||
#define CPUFREQ_NAME_LEN 16
|
||||
/* Print length for names. Extra 1 space for accomodating '\n' in prints */
|
||||
/* Print length for names. Extra 1 space for accommodating '\n' in prints */
|
||||
#define CPUFREQ_NAME_PLEN (CPUFREQ_NAME_LEN + 1)
|
||||
|
||||
struct cpufreq_governor;
|
||||
|
@ -115,7 +115,7 @@ struct cpufreq_policy {
|
|||
* guarantee that frequency can be changed on any CPU sharing the
|
||||
* policy and that the change will affect all of the policy CPUs then.
|
||||
* - fast_switch_enabled is to be set by governors that support fast
|
||||
* freqnency switching with the help of cpufreq_enable_fast_switch().
|
||||
* frequency switching with the help of cpufreq_enable_fast_switch().
|
||||
*/
|
||||
bool fast_switch_possible;
|
||||
bool fast_switch_enabled;
|
||||
|
@ -415,9 +415,6 @@ static inline void cpufreq_resume(void) {}
|
|||
/* Policy Notifiers */
|
||||
#define CPUFREQ_ADJUST (0)
|
||||
#define CPUFREQ_NOTIFY (1)
|
||||
#define CPUFREQ_START (2)
|
||||
#define CPUFREQ_CREATE_POLICY (3)
|
||||
#define CPUFREQ_REMOVE_POLICY (4)
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list);
|
||||
|
|
Loading…
Reference in New Issue