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>
|
Dominik Brodowski <linux@brodo.de>
|
||||||
David Kimdon <dwhedon@debian.org>
|
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
|
kernel "constant" loops_per_jiffy is updated on frequency changes
|
||||||
here.
|
here.
|
||||||
|
|
||||||
Reference counting is done by cpufreq_get_cpu and cpufreq_put_cpu,
|
Reference counting of the cpufreq policies is done by cpufreq_cpu_get
|
||||||
which make sure that the cpufreq processor driver is correctly
|
and cpufreq_cpu_put, which make sure that the cpufreq driver is
|
||||||
registered with the core, and will not be unloaded until
|
correctly registered with the core, and will not be unloaded until
|
||||||
cpufreq_put_cpu is called.
|
cpufreq_put_cpu is called. That also ensures that the respective cpufreq
|
||||||
|
policy doesn't get freed while being used.
|
||||||
|
|
||||||
2. CPUFreq notifiers
|
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 phase is specified in the second argument to the notifier.
|
||||||
|
|
||||||
The third argument, a void *pointer, points to a struct cpufreq_policy
|
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
|
consisting of several values, including min, max (the lower and upper
|
||||||
and max are the lower and upper frequencies (in kHz) of the new
|
frequencies (in kHz) of the new policy).
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
2.2 CPUFreq transition notifiers
|
2.2 CPUFreq transition notifiers
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
These are notified twice when the CPUfreq driver switches the CPU core
|
These are notified twice for each online CPU in the policy, when the
|
||||||
frequency and this change has any external implications.
|
CPUfreq driver switches the CPU core frequency and this change has no
|
||||||
|
any external implications.
|
||||||
|
|
||||||
The second argument specifies the phase - CPUFREQ_PRECHANGE or
|
The second argument specifies the phase - CPUFREQ_PRECHANGE or
|
||||||
CPUFREQ_POSTCHANGE.
|
CPUFREQ_POSTCHANGE.
|
||||||
@ -90,6 +91,7 @@ values:
|
|||||||
cpu - number of the affected CPU
|
cpu - number of the affected CPU
|
||||||
old - old frequency
|
old - old frequency
|
||||||
new - new frequency
|
new - new frequency
|
||||||
|
flags - flags of the cpufreq driver
|
||||||
|
|
||||||
3. CPUFreq Table Generation with Operating Performance Point (OPP)
|
3. CPUFreq Table Generation with Operating Performance Point (OPP)
|
||||||
==================================================================
|
==================================================================
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
|
|
||||||
Dominik Brodowski <linux@brodo.de>
|
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?
|
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
|
.init - A pointer to the per-policy initialization function.
|
||||||
function.
|
|
||||||
|
|
||||||
cpufreq_driver.verify - A pointer to a "verification" function.
|
.verify - A pointer to a "verification" function.
|
||||||
|
|
||||||
cpufreq_driver.setpolicy _or_
|
.setpolicy _or_ .fast_switch _or_ .target _or_ .target_index - See
|
||||||
cpufreq_driver.target/
|
below on the differences.
|
||||||
target_index - See below on the differences.
|
|
||||||
|
|
||||||
And optionally
|
And optionally
|
||||||
|
|
||||||
cpufreq_driver.exit - A pointer to a per-CPU cleanup
|
.flags - Hints for the cpufreq core.
|
||||||
function called during CPU_POST_DEAD
|
|
||||||
phase of cpu hotplug process.
|
|
||||||
|
|
||||||
cpufreq_driver.stop_cpu - A pointer to a per-CPU stop function
|
.driver_data - cpufreq driver specific data.
|
||||||
called during CPU_DOWN_PREPARE phase of
|
|
||||||
cpu hotplug process.
|
|
||||||
|
|
||||||
cpufreq_driver.resume - A pointer to a per-CPU resume function
|
.resolve_freq - Returns the most appropriate frequency for a target
|
||||||
which is called with interrupts disabled
|
frequency. Doesn't change the frequency though.
|
||||||
and _before_ the pre-suspend frequency
|
|
||||||
and/or policy is restored by a call to
|
|
||||||
->target/target_index or ->setpolicy.
|
|
||||||
|
|
||||||
cpufreq_driver.attr - A pointer to a NULL-terminated list of
|
.get_intermediate and target_intermediate - Used to switch to stable
|
||||||
"struct freq_attr" which allow to
|
frequency while changing CPU frequency.
|
||||||
export values to sysfs.
|
|
||||||
|
|
||||||
cpufreq_driver.get_intermediate
|
.get - Returns current frequency of the CPU.
|
||||||
and target_intermediate Used to switch to stable frequency while
|
|
||||||
changing CPU frequency.
|
.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
|
1.2 Per-CPU Initialization
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
Whenever a new CPU is registered with the device model, or after the
|
Whenever a new CPU is registered with the device model, or after the
|
||||||
cpufreq driver registers itself, the per-CPU initialization function
|
cpufreq driver registers itself, the per-policy initialization function
|
||||||
cpufreq_driver.init is called. It takes a struct cpufreq_policy
|
cpufreq_driver.init is called if no cpufreq policy existed for the CPU.
|
||||||
*policy as argument. What to do now?
|
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.
|
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.setpolicy or
|
||||||
cpufreq_driver.target/target_index is called
|
cpufreq_driver.target/target_index is called
|
||||||
with these values.
|
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
|
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
|
frequency table helpers might be helpful. See the section 2 for more information
|
||||||
on them.
|
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
|
1.3 verify
|
||||||
------------
|
----------
|
||||||
|
|
||||||
When the user decides a new policy (consisting of
|
When the user decides a new policy (consisting of
|
||||||
"policy,governor,min,max") shall be set, this policy must be validated
|
"policy,governor,min,max") shall be set, this policy must be validated
|
||||||
so that incompatible values can be corrected. For verifying these
|
so that incompatible values can be corrected. For verifying these
|
||||||
values, a frequency table helper and/or the
|
values cpufreq_verify_within_limits(struct cpufreq_policy *policy,
|
||||||
cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned
|
unsigned int min_freq, unsigned int max_freq) function might be helpful.
|
||||||
int min_freq, unsigned int max_freq) function might be helpful. See
|
See section 2 for details on frequency table helpers.
|
||||||
section 2 for details on frequency table helpers.
|
|
||||||
|
|
||||||
You need to make sure that at least one valid frequency (or operating
|
You need to make sure that at least one valid frequency (or operating
|
||||||
range) is within policy->min and policy->max. If necessary, increase
|
range) is within policy->min and policy->max. If necessary, increase
|
||||||
policy->max first, and only if this is no solution, decrease policy->min.
|
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
|
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
|
only allow the CPU frequency to be set to predefined fixed values. For
|
||||||
->target/target_index call.
|
these, you use the ->target(), ->target_index() or ->fast_switch()
|
||||||
|
callbacks.
|
||||||
|
|
||||||
Some cpufreq-capable processors switch the frequency between certain
|
Some cpufreq capable processors switch the frequency between certain
|
||||||
limits on their own. These shall use the ->setpolicy call
|
limits on their own. These shall use the ->setpolicy() callback.
|
||||||
|
|
||||||
|
|
||||||
1.5. target/target_index
|
1.5. target/target_index
|
||||||
-------------
|
------------------------
|
||||||
|
|
||||||
The target_index call has two arguments: struct cpufreq_policy *policy,
|
The target_index call has two arguments: struct cpufreq_policy *policy,
|
||||||
and unsigned int index (into the exposed frequency table).
|
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
|
Here again the frequency table helper might assist you - see section 2
|
||||||
for details.
|
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
|
The setpolicy call only takes a struct cpufreq_policy *policy as
|
||||||
argument. You need to set the lower limit of the in-processor or
|
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
|
powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
|
||||||
the reference implementation in drivers/cpufreq/longrun.c
|
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.
|
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
|
As most cpufreq processors only allow for being set to a few specific
|
||||||
frequencies, a "frequency table" with some functions might assist in
|
frequencies, a "frequency table" with some functions might assist in
|
||||||
some work of the processor driver. Such a "frequency table" consists
|
some work of the processor driver. Such a "frequency table" consists of
|
||||||
of an array of struct cpufreq_frequency_table entries, with any value in
|
an array of struct cpufreq_frequency_table entries, with driver specific
|
||||||
"driver_data" you want to use, and the corresponding frequency in
|
values in "driver_data", the corresponding frequency in "frequency" and
|
||||||
"frequency". At the end of the table, you need to add a
|
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
|
cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END.
|
||||||
if you want to skip one entry in the table, set the frequency to
|
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
|
CPUFREQ_ENTRY_INVALID. The entries don't need to be in sorted in any
|
||||||
order.
|
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,
|
By calling cpufreq_table_validate_and_show(), the cpuinfo.min_freq and
|
||||||
struct cpufreq_frequency_table *table);
|
cpuinfo.max_freq values are detected, and policy->min and policy->max
|
||||||
the cpuinfo.min_freq and cpuinfo.max_freq values are detected, and
|
are set to the same values. This is helpful for the per-CPU
|
||||||
policy->min and policy->max are set to the same values. This is
|
initialization stage.
|
||||||
helpful for the per-CPU initialization stage.
|
|
||||||
|
|
||||||
int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
|
cpufreq_frequency_table_verify() assures that at least one valid
|
||||||
struct cpufreq_frequency_table *table);
|
frequency is within policy->min and policy->max, and all other criteria
|
||||||
assures that at least one valid frequency is within policy->min and
|
are met. This is helpful for the ->verify call.
|
||||||
policy->max, and all other criteria are met. This is helpful for the
|
|
||||||
->verify call.
|
|
||||||
|
|
||||||
int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
|
cpufreq_frequency_table_target() is the corresponding frequency table
|
||||||
unsigned int target_freq,
|
helper for the ->target stage. Just pass the values to this function,
|
||||||
unsigned int relation);
|
and this function returns the of the frequency table entry which
|
||||||
|
contains the frequency the CPU shall be set to.
|
||||||
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.
|
|
||||||
|
|
||||||
The following macros can be used as iterators over cpufreq_frequency_table:
|
The following macros can be used as iterators over cpufreq_frequency_table:
|
||||||
|
|
||||||
cpufreq_for_each_entry(pos, table) - iterates over all entries of frequency
|
cpufreq_for_each_entry(pos, table) - iterates over all entries of frequency
|
||||||
table.
|
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.
|
excluding CPUFREQ_ENTRY_INVALID frequencies.
|
||||||
Use arguments "pos" - a cpufreq_frequency_table * as a loop cursor and
|
Use arguments "pos" - a cpufreq_frequency_table * as a loop cursor and
|
||||||
"table" - the cpufreq_frequency_table * you want to iterate over.
|
"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
|
- total_trans
|
||||||
- trans_table
|
- trans_table
|
||||||
|
|
||||||
All the statistics will be from the time the stats driver has been inserted
|
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
|
(or the time the stats were reset) to the time when a read of a particular
|
||||||
driver will not have any information about the frequency transitions before
|
statistic is done. Obviously, stats driver will not have any information
|
||||||
the stats driver insertion.
|
about the frequency transitions before the stats driver insertion.
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
<mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # ls -l
|
<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 scaling
|
[*] CPU Frequency scaling
|
||||||
[*] CPU frequency translation statistics
|
[*] CPU frequency translation statistics
|
||||||
[*] CPU frequency translation statistics details
|
|
||||||
|
|
||||||
|
|
||||||
"CPU Frequency scaling" (CONFIG_CPU_FREQ) should be enabled to configure
|
"CPU Frequency scaling" (CONFIG_CPU_FREQ) should be enabled to configure
|
||||||
cpufreq-stats.
|
cpufreq-stats.
|
||||||
|
|
||||||
"CPU frequency translation statistics" (CONFIG_CPU_FREQ_STAT) provides the
|
"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)
|
Once this option is enabled and your CPU supports cpufrequency, you
|
||||||
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
|
|
||||||
will be able to see the CPU frequency statistics in /sysfs.
|
will be able to see the CPU frequency statistics in /sysfs.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
Dominik Brodowski <linux@brodo.de>
|
Dominik Brodowski <linux@brodo.de>
|
||||||
some additions and corrections by Nico Golde <nico@ngolde.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.3 Userspace
|
||||||
2.4 Ondemand
|
2.4 Ondemand
|
||||||
2.5 Conservative
|
2.5 Conservative
|
||||||
|
2.6 Schedutil
|
||||||
|
|
||||||
3. The Governor Interface in the CPUfreq Core
|
3. The Governor Interface in the CPUfreq Core
|
||||||
|
|
||||||
|
4. References
|
||||||
|
|
||||||
|
|
||||||
1. What Is A CPUFreq Governor?
|
1. What Is A CPUFreq Governor?
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Most cpufreq drivers (except the intel_pstate and longrun) or even most
|
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
|
cpu frequency scaling algorithms only allow the CPU frequency to be set
|
||||||
frequency. In order to offer dynamic frequency scaling, the cpufreq
|
to predefined fixed values. In order to offer dynamic frequency
|
||||||
core must be able to tell these drivers of a "target frequency". So
|
scaling, the cpufreq core must be able to tell these drivers of a
|
||||||
these specific drivers will be transformed to offer a "->target/target_index"
|
"target frequency". So these specific drivers will be transformed to
|
||||||
call instead of the existing "->setpolicy" call. For "longrun", all
|
offer a "->target/target_index/fast_switch()" call instead of the
|
||||||
stays the same, though.
|
"->setpolicy()" call. For set_policy drivers, all stays the same,
|
||||||
|
though.
|
||||||
|
|
||||||
How to decide what frequency within the CPUfreq policy should be used?
|
How to decide what frequency within the CPUfreq policy should be used?
|
||||||
That's done using "cpufreq governors". Two are already in this patch
|
That's done using "cpufreq governors".
|
||||||
-- 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.
|
|
||||||
|
|
||||||
Basically, it's the following flow graph:
|
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}
|
/ 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
|
the limits and the the frequency closest
|
||||||
"policy" is set. to target_freq is set.
|
"policy" is set. to target_freq is set.
|
||||||
It is assured that it
|
It is assured that it
|
||||||
@ -109,114 +106,159 @@ directory.
|
|||||||
2.4 Ondemand
|
2.4 Ondemand
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The CPUfreq governor "ondemand" sets the CPU depending on the
|
The CPUfreq governor "ondemand" sets the CPU frequency depending on the
|
||||||
current usage. To do this the CPU must have the capability to
|
current system load. Load estimation is triggered by the scheduler
|
||||||
switch the frequency very quickly. There are a number of sysfs file
|
through the update_util_data->func hook; when triggered, cpufreq checks
|
||||||
accessible parameters:
|
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
|
Sysfs files:
|
||||||
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
|
|
||||||
|
|
||||||
sampling_rate_min:
|
* sampling_rate:
|
||||||
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: defines what the average CPU usage between the samplings
|
Measured in uS (10^-6 seconds), this is how often you want the kernel
|
||||||
of 'sampling_rate' needs to be for the kernel to make a decision on
|
to look at the CPU usage and to make decisions on what to do about the
|
||||||
whether it should increase the frequency. For example when it is set
|
frequency. Typically this is set to values of around '10000' or more.
|
||||||
to its default value of '95' it means that between the checking
|
It's default value is (cmp. with users-guide.txt): transition_latency
|
||||||
intervals the CPU needs to be on average more than 95% in use to then
|
* 1000. Be aware that transition latency is in ns and sampling_rate
|
||||||
decide that the CPU frequency needs to be increased.
|
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
|
$ echo `$(($(cat cpuinfo_transition_latency) * 750 / 1000)) > ondemand/sampling_rate
|
||||||
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
|
* sampling_rate_min:
|
||||||
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
|
The sampling rate is limited by the HW transition latency:
|
||||||
defines the percentage (times 10) value of the target frequency that
|
transition_latency * 100
|
||||||
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
|
Or by kernel restrictions:
|
||||||
1000 MHz - (10% of 1000 MHz) = 900 MHz instead. This is set to 0
|
- If CONFIG_NO_HZ_COMMON is set, the limit is 10ms fixed.
|
||||||
(disabled) by default.
|
- If CONFIG_NO_HZ_COMMON is not set or nohz=off boot parameter is
|
||||||
When AMD frequency sensitivity powersave bias driver --
|
used, the limits depend on the CONFIG_HZ option:
|
||||||
drivers/cpufreq/amd_freq_sensitivity.c is loaded, this parameter
|
HZ=1000: min=20000us (20ms)
|
||||||
defines the workload frequency sensitivity threshold in which a lower
|
HZ=250: min=80000us (80ms)
|
||||||
frequency is chosen instead of ondemand governor's original target.
|
HZ=100: min=200000us (200ms)
|
||||||
The frequency sensitivity is a hardware reported (on AMD Family 16h
|
|
||||||
Processors and above) value between 0 to 100% that tells software how
|
The highest value of kernel and HW latency restrictions is shown and
|
||||||
the performance of the workload running on a CPU will change when
|
used as the minimum sampling rate.
|
||||||
frequency changes. A workload with sensitivity of 0% (memory/IO-bound)
|
|
||||||
will not perform any better on higher core frequency, whereas a
|
* up_threshold:
|
||||||
workload with sensitivity of 100% (CPU-bound) will perform better
|
|
||||||
higher the frequency. When the driver is loaded, this is set to 400
|
This defines what the average CPU usage between the samplings of
|
||||||
by default -- for CPUs running workloads with sensitivity value below
|
'sampling_rate' needs to be for the kernel to make a decision on
|
||||||
40%, a lower frequency is chosen. Unloading the driver or writing 0
|
whether it should increase the frequency. For example when it is set
|
||||||
will disable this feature.
|
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
|
2.5 Conservative
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The CPUfreq governor "conservative", much like the "ondemand"
|
The CPUfreq governor "conservative", much like the "ondemand"
|
||||||
governor, sets the CPU depending on the current usage. It differs in
|
governor, sets the CPU frequency depending on the current usage. It
|
||||||
behaviour in that it gracefully increases and decreases the CPU speed
|
differs in behaviour in that it gracefully increases and decreases the
|
||||||
rather than jumping to max speed the moment there is any load on the
|
CPU speed rather than jumping to max speed the moment there is any load
|
||||||
CPU. This behaviour more suitable in a battery powered environment.
|
on the CPU. This behaviour is more suitable in a battery powered
|
||||||
The governor is tweaked in the same manner as the "ondemand" governor
|
environment. The governor is tweaked in the same manner as the
|
||||||
through sysfs with the addition of:
|
"ondemand" governor through sysfs with the addition of:
|
||||||
|
|
||||||
freq_step: this describes what percentage steps the cpu freq should be
|
* freq_step:
|
||||||
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"
|
This describes what percentage steps the cpu freq should be increased
|
||||||
governor but for the opposite direction. For example when set to its
|
and decreased smoothly by. By default the cpu frequency will increase
|
||||||
default value of '20' it means that if the CPU usage needs to be below
|
in 5% chunks of your maximum cpu frequency. You can change this value
|
||||||
20% between samples to have the frequency decreased.
|
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
|
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
|
"cpufreq_register_governor". The struct cpufreq_governor, which has to
|
||||||
be passed to that function, must contain the following values:
|
be passed to that function, must contain the following values:
|
||||||
|
|
||||||
governor->name - A unique name for this governor
|
governor->name - A unique name for this governor.
|
||||||
governor->governor - The governor callback function
|
governor->owner - .THIS_MODULE for the governor module (if appropriate).
|
||||||
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.
|
|
||||||
|
|
||||||
|
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
|
The CPUfreq governor may call the CPU processor driver using one of
|
||||||
these two functions:
|
these two functions:
|
||||||
@ -258,12 +284,18 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
|
|||||||
unsigned int relation);
|
unsigned int relation);
|
||||||
|
|
||||||
target_freq must be within policy->min and policy->max, of course.
|
target_freq must be within policy->min and policy->max, of course.
|
||||||
What's the difference between these two functions? When your governor
|
What's the difference between these two functions? When your governor is
|
||||||
still is in a direct code path of a call to governor->governor, the
|
in a direct code path of a call to governor callbacks, like
|
||||||
per-CPU cpufreq lock is still held in the cpufreq core, and there's
|
governor->start(), the policy->rwsem is still held in the cpufreq core,
|
||||||
no need to lock it again (in fact, this would cause a deadlock). So
|
and there's no need to lock it again (in fact, this would cause a
|
||||||
use __cpufreq_driver_target only in these cases. In all other cases
|
deadlock). So use __cpufreq_driver_target only in these cases. In all
|
||||||
(for example, when there's a "daemonized" function that wakes up
|
other cases (for example, when there's a "daemonized" function that
|
||||||
every second), use cpufreq_driver_target to lock the cpufreq per-CPU
|
wakes up every second), use cpufreq_driver_target to take policy->rwsem
|
||||||
lock before the command is passed to the cpufreq processor driver.
|
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:
|
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
|
governors.txt - What are cpufreq governors and how to
|
||||||
implement them?
|
implement them?
|
||||||
|
|
||||||
index.txt - File index, Mailing list and Links (this document)
|
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
|
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
|
There is a CPU frequency changing CVS commit and general list where
|
||||||
you can report bugs, problems or submit patches. To post a message,
|
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
|
send an email to linux-pm@vger.kernel.org.
|
||||||
http://vger.kernel.org/vger-lists.html#linux-pm and follow the
|
|
||||||
instructions there.
|
|
||||||
|
|
||||||
Links
|
Links
|
||||||
-----
|
-----
|
||||||
@ -48,7 +59,7 @@ how to access the CVS repository:
|
|||||||
* http://cvs.arm.linux.org.uk/
|
* http://cvs.arm.linux.org.uk/
|
||||||
|
|
||||||
the CPUFreq Mailing list:
|
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:
|
Clock and voltage scaling for the SA-1100:
|
||||||
* http://www.lartmaker.nl/projects/scaling
|
* 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
|
Refer to "Intel® 64 and IA-32 Architectures Software Developer’s Manual
|
||||||
Volume 3: System Programming Guide" to understand ratios.
|
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
|
cpufreq sysfs for Intel P-State
|
||||||
|
|
||||||
Since this driver registers with cpufreq, cpufreq sysfs is also presented.
|
Since this driver registers with cpufreq, cpufreq sysfs is also presented.
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
Contents:
|
Contents:
|
||||||
---------
|
---------
|
||||||
1. Supported Architectures and Processors
|
1. Supported Architectures and Processors
|
||||||
1.1 ARM
|
1.1 ARM and ARM64
|
||||||
1.2 x86
|
1.2 x86
|
||||||
1.3 sparc64
|
1.3 sparc64
|
||||||
1.4 ppc
|
1.4 ppc
|
||||||
@ -37,16 +37,10 @@ Contents:
|
|||||||
1. Supported Architectures and Processors
|
1. Supported Architectures and Processors
|
||||||
=========================================
|
=========================================
|
||||||
|
|
||||||
1.1 ARM
|
1.1 ARM and ARM64
|
||||||
-------
|
-----------------
|
||||||
|
|
||||||
The following ARM processors are supported by cpufreq:
|
|
||||||
|
|
||||||
ARM Integrator
|
|
||||||
ARM-SA1100
|
|
||||||
ARM-SA1110
|
|
||||||
Intel PXA
|
|
||||||
|
|
||||||
|
Almost all ARM and ARM64 platforms support CPU frequency scaling.
|
||||||
|
|
||||||
1.2 x86
|
1.2 x86
|
||||||
-------
|
-------
|
||||||
@ -69,6 +63,7 @@ Transmeta Crusoe
|
|||||||
Transmeta Efficeon
|
Transmeta Efficeon
|
||||||
VIA Cyrix 3 / C3
|
VIA Cyrix 3 / C3
|
||||||
various processors on some ACPI 2.0-compatible systems [*]
|
various processors on some ACPI 2.0-compatible systems [*]
|
||||||
|
And many more
|
||||||
|
|
||||||
[*] Only if "ACPI Processor Performance States" are available
|
[*] Only if "ACPI Processor Performance States" are available
|
||||||
to the ACPI<->BIOS interface.
|
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
|
"cpufreq" within the cpu-device directory
|
||||||
(e.g. /sys/devices/system/cpu/cpu0/cpufreq/ for the first CPU).
|
(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
|
cpuinfo_min_freq : this file shows the minimum operating
|
||||||
frequency the processor can run at(in kHz)
|
frequency the processor can run at(in kHz)
|
||||||
|
|
||||||
cpuinfo_max_freq : this file shows the maximum operating
|
cpuinfo_max_freq : this file shows the maximum operating
|
||||||
frequency the processor can run at(in kHz)
|
frequency the processor can run at(in kHz)
|
||||||
|
|
||||||
cpuinfo_transition_latency The time it takes on this CPU to
|
cpuinfo_transition_latency The time it takes on this CPU to
|
||||||
switch between two frequencies in nano
|
switch between two frequencies in nano
|
||||||
seconds. If unknown or known to be
|
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
|
userspace daemon. Make sure to not
|
||||||
switch the frequency too often
|
switch the frequency too often
|
||||||
resulting in performance loss.
|
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
|
scaling_available_governors : this file shows the CPUfreq governors
|
||||||
available in this kernel. You can see the
|
available in this kernel. You can see the
|
||||||
currently activated governor in
|
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
|
scaling_governor, and by "echoing" the name of another
|
||||||
governor you can change it. Please note
|
governor you can change it. Please note
|
||||||
that some governors won't load - they only
|
that some governors won't load - they only
|
||||||
work on some specific architectures or
|
work on some specific architectures or
|
||||||
processors.
|
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_min_freq and
|
||||||
scaling_max_freq show the current "policy limits" (in
|
scaling_max_freq show the current "policy limits" (in
|
||||||
kHz). By echoing new values into these
|
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
|
first set scaling_max_freq, then
|
||||||
scaling_min_freq.
|
scaling_min_freq.
|
||||||
|
|
||||||
affected_cpus : List of Online CPUs that require software
|
scaling_setspeed This can be read to get the currently programmed
|
||||||
coordination of frequency.
|
value by the governor. This can be written to
|
||||||
|
change the current frequency for a group of
|
||||||
related_cpus : List of Online + Offline CPUs that need software
|
CPUs, represented by a policy. This is supported
|
||||||
coordination of frequency.
|
currently only by the userspace governor.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
bios_limit : If the BIOS tells the OS to limit a CPU to
|
bios_limit : If the BIOS tells the OS to limit a CPU to
|
||||||
lower frequencies, the user can read out the
|
lower frequencies, the user can read out the
|
||||||
|
128
Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt
Normal file
128
Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt
Normal file
@ -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_nvram.h
|
||||||
F: include/linux/bcm963xx_tag.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
|
BROADCOM TG3 GIGABIT ETHERNET DRIVER
|
||||||
M: Siva Reddy Kallam <siva.kallam@broadcom.com>
|
M: Siva Reddy Kallam <siva.kallam@broadcom.com>
|
||||||
M: Prashant Sreedharan <prashant@broadcom.com>
|
M: Prashant Sreedharan <prashant@broadcom.com>
|
||||||
|
@ -24,7 +24,7 @@ CONFIG_ARM_APPENDED_DTB=y
|
|||||||
CONFIG_ARM_ATAG_DTB_COMPAT=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_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc mem=256M"
|
||||||
CONFIG_CPU_FREQ=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_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
||||||
|
@ -58,7 +58,7 @@ CONFIG_ZBOOT_ROM_BSS=0x0
|
|||||||
CONFIG_ARM_APPENDED_DTB=y
|
CONFIG_ARM_APPENDED_DTB=y
|
||||||
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
||||||
CONFIG_CPU_FREQ=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_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_IDLE=y
|
CONFIG_CPU_IDLE=y
|
||||||
CONFIG_ARM_KIRKWOOD_CPUIDLE=y
|
CONFIG_ARM_KIRKWOOD_CPUIDLE=y
|
||||||
|
@ -132,7 +132,7 @@ CONFIG_ARM_ATAG_DTB_COMPAT=y
|
|||||||
CONFIG_KEXEC=y
|
CONFIG_KEXEC=y
|
||||||
CONFIG_EFI=y
|
CONFIG_EFI=y
|
||||||
CONFIG_CPU_FREQ=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_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
||||||
|
@ -44,7 +44,7 @@ CONFIG_ZBOOT_ROM_BSS=0x0
|
|||||||
CONFIG_ARM_APPENDED_DTB=y
|
CONFIG_ARM_APPENDED_DTB=y
|
||||||
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
||||||
CONFIG_CPU_FREQ=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_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_IDLE=y
|
CONFIG_CPU_IDLE=y
|
||||||
CONFIG_ARM_KIRKWOOD_CPUIDLE=y
|
CONFIG_ARM_KIRKWOOD_CPUIDLE=y
|
||||||
|
@ -97,7 +97,7 @@ CONFIG_ZBOOT_ROM_BSS=0x0
|
|||||||
CONFIG_CMDLINE="root=/dev/ram0 ro"
|
CONFIG_CMDLINE="root=/dev/ram0 ro"
|
||||||
CONFIG_KEXEC=y
|
CONFIG_KEXEC=y
|
||||||
CONFIG_CPU_FREQ=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_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
||||||
|
@ -38,7 +38,7 @@ CONFIG_ZBOOT_ROM_BSS=0x0
|
|||||||
CONFIG_ARM_APPENDED_DTB=y
|
CONFIG_ARM_APPENDED_DTB=y
|
||||||
CONFIG_KEXEC=y
|
CONFIG_KEXEC=y
|
||||||
CONFIG_CPU_FREQ=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_POWERSAVE=y
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
||||||
|
@ -1703,6 +1703,8 @@ config CPU_BMIPS
|
|||||||
select WEAK_ORDERING
|
select WEAK_ORDERING
|
||||||
select CPU_SUPPORTS_HIGHMEM
|
select CPU_SUPPORTS_HIGHMEM
|
||||||
select CPU_HAS_PREFETCH
|
select CPU_HAS_PREFETCH
|
||||||
|
select CPU_SUPPORTS_CPUFREQ
|
||||||
|
select MIPS_EXTERNAL_TIMER
|
||||||
help
|
help
|
||||||
Support for BMIPS32/3300/4350/4380 and BMIPS5000 processors.
|
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_SWAP is not set
|
||||||
CONFIG_NO_HZ=y
|
CONFIG_NO_HZ=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
CONFIG_RD_GZIP=y
|
|
||||||
CONFIG_EXPERT=y
|
CONFIG_EXPERT=y
|
||||||
# CONFIG_VM_EVENT_COUNTERS is not set
|
# CONFIG_VM_EVENT_COUNTERS is not set
|
||||||
# CONFIG_SLUB_DEBUG is not set
|
# CONFIG_SLUB_DEBUG is not set
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
# CONFIG_IOSCHED_DEADLINE is not set
|
# CONFIG_IOSCHED_DEADLINE is not set
|
||||||
# CONFIG_IOSCHED_CFQ 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_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_PACKET_DIAG=y
|
CONFIG_PACKET_DIAG=y
|
||||||
@ -24,7 +31,6 @@ CONFIG_INET=y
|
|||||||
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
|
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
|
||||||
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
|
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
|
||||||
# CONFIG_INET_XFRM_MODE_BEET is not set
|
# CONFIG_INET_XFRM_MODE_BEET is not set
|
||||||
# CONFIG_INET_LRO is not set
|
|
||||||
# CONFIG_INET_DIAG is not set
|
# CONFIG_INET_DIAG is not set
|
||||||
CONFIG_CFG80211=y
|
CONFIG_CFG80211=y
|
||||||
CONFIG_NL80211_TESTMODE=y
|
CONFIG_NL80211_TESTMODE=y
|
||||||
@ -34,8 +40,6 @@ CONFIG_DEVTMPFS=y
|
|||||||
CONFIG_DEVTMPFS_MOUNT=y
|
CONFIG_DEVTMPFS_MOUNT=y
|
||||||
# CONFIG_STANDALONE is not set
|
# CONFIG_STANDALONE is not set
|
||||||
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
|
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
|
||||||
CONFIG_PRINTK_TIME=y
|
|
||||||
CONFIG_BRCMSTB_GISB_ARB=y
|
|
||||||
CONFIG_MTD=y
|
CONFIG_MTD=y
|
||||||
CONFIG_MTD_CFI=y
|
CONFIG_MTD_CFI=y
|
||||||
CONFIG_MTD_CFI_INTELEXT=y
|
CONFIG_MTD_CFI_INTELEXT=y
|
||||||
@ -51,16 +55,15 @@ CONFIG_USB_USBNET=y
|
|||||||
# CONFIG_INPUT is not set
|
# CONFIG_INPUT is not set
|
||||||
# CONFIG_SERIO is not set
|
# CONFIG_SERIO is not set
|
||||||
# CONFIG_VT is not set
|
# CONFIG_VT is not set
|
||||||
# CONFIG_DEVKMEM is not set
|
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
# CONFIG_HW_RANDOM is not set
|
# CONFIG_HW_RANDOM is not set
|
||||||
CONFIG_POWER_SUPPLY=y
|
|
||||||
CONFIG_POWER_RESET=y
|
CONFIG_POWER_RESET=y
|
||||||
CONFIG_POWER_RESET_BRCMSTB=y
|
CONFIG_POWER_RESET_BRCMSTB=y
|
||||||
CONFIG_POWER_RESET_SYSCON=y
|
CONFIG_POWER_RESET_SYSCON=y
|
||||||
|
CONFIG_POWER_SUPPLY=y
|
||||||
# CONFIG_HWMON is not set
|
# CONFIG_HWMON is not set
|
||||||
CONFIG_USB=y
|
CONFIG_USB=y
|
||||||
CONFIG_USB_EHCI_HCD=y
|
CONFIG_USB_EHCI_HCD=y
|
||||||
@ -82,6 +85,7 @@ CONFIG_CIFS=y
|
|||||||
CONFIG_NLS_CODEPAGE_437=y
|
CONFIG_NLS_CODEPAGE_437=y
|
||||||
CONFIG_NLS_ASCII=y
|
CONFIG_NLS_ASCII=y
|
||||||
CONFIG_NLS_ISO8859_1=y
|
CONFIG_NLS_ISO8859_1=y
|
||||||
|
CONFIG_PRINTK_TIME=y
|
||||||
CONFIG_DEBUG_FS=y
|
CONFIG_DEBUG_FS=y
|
||||||
CONFIG_MAGIC_SYSRQ=y
|
CONFIG_MAGIC_SYSRQ=y
|
||||||
CONFIG_CMDLINE_BOOL=y
|
CONFIG_CMDLINE_BOOL=y
|
||||||
|
@ -40,7 +40,6 @@ CONFIG_PM_STD_PARTITION="/dev/hda3"
|
|||||||
CONFIG_CPU_FREQ=y
|
CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_DEBUG=y
|
CONFIG_CPU_FREQ_DEBUG=y
|
||||||
CONFIG_CPU_FREQ_STAT=m
|
CONFIG_CPU_FREQ_STAT=m
|
||||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
||||||
|
@ -62,7 +62,6 @@ CONFIG_MPC8610_HPCD=y
|
|||||||
CONFIG_GEF_SBC610=y
|
CONFIG_GEF_SBC610=y
|
||||||
CONFIG_CPU_FREQ=y
|
CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_STAT=m
|
CONFIG_CPU_FREQ_STAT=m
|
||||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||||
|
@ -25,7 +25,7 @@ CONFIG_SH_SH7785LCR=y
|
|||||||
CONFIG_NO_HZ=y
|
CONFIG_NO_HZ=y
|
||||||
CONFIG_HIGH_RES_TIMERS=y
|
CONFIG_HIGH_RES_TIMERS=y
|
||||||
CONFIG_CPU_FREQ=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_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_SH_CPU_FREQ=y
|
CONFIG_SH_CPU_FREQ=y
|
||||||
CONFIG_HEARTBEAT=y
|
CONFIG_HEARTBEAT=y
|
||||||
|
@ -75,10 +75,8 @@ static int acpi_processor_ppc_notifier(struct notifier_block *nb,
|
|||||||
struct acpi_processor *pr;
|
struct acpi_processor *pr;
|
||||||
unsigned int ppc = 0;
|
unsigned int ppc = 0;
|
||||||
|
|
||||||
if (event == CPUFREQ_START && ignore_ppc <= 0) {
|
if (ignore_ppc < 0)
|
||||||
ignore_ppc = 0;
|
ignore_ppc = 0;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignore_ppc)
|
if (ignore_ppc)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -37,14 +37,6 @@ config CPU_FREQ_STAT
|
|||||||
|
|
||||||
If in doubt, say N.
|
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
|
choice
|
||||||
prompt "Default CPUFreq governor"
|
prompt "Default CPUFreq governor"
|
||||||
default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ
|
default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ
|
||||||
@ -271,6 +263,16 @@ config IA64_ACPI_CPUFREQ
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if MIPS
|
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
|
config LOONGSON2_CPUFREQ
|
||||||
tristate "Loongson2 CPUFreq Driver"
|
tristate "Loongson2 CPUFreq Driver"
|
||||||
help
|
help
|
||||||
@ -332,7 +334,7 @@ endif
|
|||||||
|
|
||||||
config QORIQ_CPUFREQ
|
config QORIQ_CPUFREQ
|
||||||
tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
|
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
|
depends on !CPU_THERMAL || THERMAL
|
||||||
select CLK_QORIQ
|
select CLK_QORIQ
|
||||||
help
|
help
|
||||||
|
@ -247,6 +247,17 @@ config ARM_TEGRA124_CPUFREQ
|
|||||||
help
|
help
|
||||||
This adds the CPUFreq driver support for Tegra124 SOCs.
|
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
|
config ARM_PXA2xx_CPUFREQ
|
||||||
tristate "Intel PXA2xx CPUfreq driver"
|
tristate "Intel PXA2xx CPUfreq driver"
|
||||||
depends on PXA27x || PXA25x
|
depends on PXA27x || PXA25x
|
||||||
@ -257,7 +268,7 @@ config ARM_PXA2xx_CPUFREQ
|
|||||||
|
|
||||||
config ACPI_CPPC_CPUFREQ
|
config ACPI_CPPC_CPUFREQ
|
||||||
tristate "CPUFreq driver based on the ACPI CPPC spec"
|
tristate "CPUFreq driver based on the ACPI CPPC spec"
|
||||||
depends on ACPI
|
depends on ACPI_PROCESSOR
|
||||||
select ACPI_CPPC_LIB
|
select ACPI_CPPC_LIB
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
|
@ -77,6 +77,7 @@ obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
|
|||||||
obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o
|
obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o
|
||||||
obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
|
obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
|
||||||
obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-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_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
|
||||||
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
|
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
|
||||||
obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
|
obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
|
||||||
@ -98,6 +99,7 @@ obj-$(CONFIG_POWERNV_CPUFREQ) += powernv-cpufreq.o
|
|||||||
# Other platform drivers
|
# Other platform drivers
|
||||||
obj-$(CONFIG_AVR32_AT32AP_CPUFREQ) += at32ap-cpufreq.o
|
obj-$(CONFIG_AVR32_AT32AP_CPUFREQ) += at32ap-cpufreq.o
|
||||||
obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-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_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o
|
||||||
obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o
|
obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o
|
||||||
obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o
|
obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o
|
||||||
|
188
drivers/cpufreq/bmips-cpufreq.c
Normal file
188
drivers/cpufreq/bmips-cpufreq.c
Normal file
@ -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);
|
iounmap(priv->avs_intr_base);
|
||||||
unmap_base:
|
unmap_base:
|
||||||
iounmap(priv->base);
|
iounmap(priv->base);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1042,7 +1041,6 @@ static int brcm_avs_cpufreq_remove(struct platform_device *pdev)
|
|||||||
priv = platform_get_drvdata(pdev);
|
priv = platform_get_drvdata(pdev);
|
||||||
iounmap(priv->base);
|
iounmap(priv->base);
|
||||||
iounmap(priv->avs_intr_base);
|
iounmap(priv->avs_intr_base);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,6 @@ static const struct of_device_id machines[] __initconst = {
|
|||||||
{ .compatible = "socionext,uniphier-ld11", },
|
{ .compatible = "socionext,uniphier-ld11", },
|
||||||
{ .compatible = "socionext,uniphier-ld20", },
|
{ .compatible = "socionext,uniphier-ld20", },
|
||||||
|
|
||||||
{ .compatible = "ti,am33xx", },
|
|
||||||
{ .compatible = "ti,dra7", },
|
|
||||||
{ .compatible = "ti,omap2", },
|
{ .compatible = "ti,omap2", },
|
||||||
{ .compatible = "ti,omap3", },
|
{ .compatible = "ti,omap3", },
|
||||||
{ .compatible = "ti,omap4", },
|
{ .compatible = "ti,omap4", },
|
||||||
|
@ -1078,15 +1078,11 @@ err_free_policy:
|
|||||||
return NULL;
|
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 kobject *kobj;
|
||||||
struct completion *cmp;
|
struct completion *cmp;
|
||||||
|
|
||||||
if (notify)
|
|
||||||
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
|
||||||
CPUFREQ_REMOVE_POLICY, policy);
|
|
||||||
|
|
||||||
down_write(&policy->rwsem);
|
down_write(&policy->rwsem);
|
||||||
cpufreq_stats_free_table(policy);
|
cpufreq_stats_free_table(policy);
|
||||||
kobj = &policy->kobj;
|
kobj = &policy->kobj;
|
||||||
@ -1104,7 +1100,7 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify)
|
|||||||
pr_debug("wait complete\n");
|
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;
|
unsigned long flags;
|
||||||
int cpu;
|
int cpu;
|
||||||
@ -1117,7 +1113,7 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy, bool notify)
|
|||||||
per_cpu(cpufreq_cpu_data, cpu) = NULL;
|
per_cpu(cpufreq_cpu_data, cpu) = NULL;
|
||||||
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
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->real_cpus);
|
||||||
free_cpumask_var(policy->related_cpus);
|
free_cpumask_var(policy->related_cpus);
|
||||||
free_cpumask_var(policy->cpus);
|
free_cpumask_var(policy->cpus);
|
||||||
@ -1170,8 +1166,6 @@ static int cpufreq_online(unsigned int cpu)
|
|||||||
if (new_policy) {
|
if (new_policy) {
|
||||||
/* related_cpus should at least include policy->cpus. */
|
/* related_cpus should at least include policy->cpus. */
|
||||||
cpumask_copy(policy->related_cpus, 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;
|
goto out_exit_policy;
|
||||||
|
|
||||||
cpufreq_stats_create_table(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);
|
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
||||||
list_add(&policy->policy_list, &cpufreq_policy_list);
|
list_add(&policy->policy_list, &cpufreq_policy_list);
|
||||||
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
|
||||||
CPUFREQ_START, policy);
|
|
||||||
|
|
||||||
ret = cpufreq_init_policy(policy);
|
ret = cpufreq_init_policy(policy);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("%s: Failed to initialize policy for cpu: %d (%d)\n",
|
pr_err("%s: Failed to initialize policy for cpu: %d (%d)\n",
|
||||||
@ -1282,7 +1271,7 @@ out_exit_policy:
|
|||||||
if (cpufreq_driver->exit)
|
if (cpufreq_driver->exit)
|
||||||
cpufreq_driver->exit(policy);
|
cpufreq_driver->exit(policy);
|
||||||
out_free_policy:
|
out_free_policy:
|
||||||
cpufreq_policy_free(policy, !new_policy);
|
cpufreq_policy_free(policy);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1403,7 +1392,7 @@ static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
|
|||||||
remove_cpu_dev_symlink(policy, dev);
|
remove_cpu_dev_symlink(policy, dev);
|
||||||
|
|
||||||
if (cpumask_empty(policy->real_cpus))
|
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;
|
unsigned int last_index;
|
||||||
u64 *time_in_state;
|
u64 *time_in_state;
|
||||||
unsigned int *freq_table;
|
unsigned int *freq_table;
|
||||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
|
||||||
unsigned int *trans_table;
|
unsigned int *trans_table;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int cpufreq_stats_update(struct cpufreq_stats *stats)
|
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;
|
unsigned int count = stats->max_state;
|
||||||
|
|
||||||
memset(stats->time_in_state, 0, count * sizeof(u64));
|
memset(stats->time_in_state, 0, count * sizeof(u64));
|
||||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
|
||||||
memset(stats->trans_table, 0, count * count * sizeof(int));
|
memset(stats->trans_table, 0, count * count * sizeof(int));
|
||||||
#endif
|
|
||||||
stats->last_time = get_jiffies_64();
|
stats->last_time = get_jiffies_64();
|
||||||
stats->total_trans = 0;
|
stats->total_trans = 0;
|
||||||
}
|
}
|
||||||
@ -84,7 +80,6 @@ static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
|
||||||
static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
|
static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
|
||||||
{
|
{
|
||||||
struct cpufreq_stats *stats = policy->stats;
|
struct cpufreq_stats *stats = policy->stats;
|
||||||
@ -129,7 +124,6 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
cpufreq_freq_attr_ro(trans_table);
|
cpufreq_freq_attr_ro(trans_table);
|
||||||
#endif
|
|
||||||
|
|
||||||
cpufreq_freq_attr_ro(total_trans);
|
cpufreq_freq_attr_ro(total_trans);
|
||||||
cpufreq_freq_attr_ro(time_in_state);
|
cpufreq_freq_attr_ro(time_in_state);
|
||||||
@ -139,9 +133,7 @@ static struct attribute *default_attrs[] = {
|
|||||||
&total_trans.attr,
|
&total_trans.attr,
|
||||||
&time_in_state.attr,
|
&time_in_state.attr,
|
||||||
&reset.attr,
|
&reset.attr,
|
||||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
|
||||||
&trans_table.attr,
|
&trans_table.attr,
|
||||||
#endif
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
static struct attribute_group stats_attr_group = {
|
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);
|
alloc_size = count * sizeof(int) + count * sizeof(u64);
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
|
||||||
alloc_size += count * count * sizeof(int);
|
alloc_size += count * count * sizeof(int);
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Allocate memory for time_in_state/freq_table/trans_table in one go */
|
/* Allocate memory for time_in_state/freq_table/trans_table in one go */
|
||||||
stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
|
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);
|
stats->freq_table = (unsigned int *)(stats->time_in_state + count);
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
|
||||||
stats->trans_table = stats->freq_table + count;
|
stats->trans_table = stats->freq_table + count;
|
||||||
#endif
|
|
||||||
|
|
||||||
stats->max_state = count;
|
stats->max_state = count;
|
||||||
|
|
||||||
@ -259,8 +247,6 @@ void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
|
|||||||
cpufreq_stats_update(stats);
|
cpufreq_stats_update(stats);
|
||||||
|
|
||||||
stats->last_index = new_index;
|
stats->last_index = new_index;
|
||||||
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
|
||||||
stats->trans_table[old_index * stats->max_state + new_index]++;
|
stats->trans_table[old_index * stats->max_state + new_index]++;
|
||||||
#endif
|
|
||||||
stats->total_trans++;
|
stats->total_trans++;
|
||||||
}
|
}
|
||||||
|
@ -358,6 +358,8 @@ static struct pstate_funcs pstate_funcs __read_mostly;
|
|||||||
static int hwp_active __read_mostly;
|
static int hwp_active __read_mostly;
|
||||||
static bool per_cpu_limits __read_mostly;
|
static bool per_cpu_limits __read_mostly;
|
||||||
|
|
||||||
|
static bool driver_registered __read_mostly;
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
static bool acpi_ppc;
|
static bool acpi_ppc;
|
||||||
#endif
|
#endif
|
||||||
@ -394,6 +396,7 @@ static struct perf_limits *limits = &performance_limits;
|
|||||||
static struct perf_limits *limits = &powersave_limits;
|
static struct perf_limits *limits = &powersave_limits;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(intel_pstate_driver_lock);
|
||||||
static DEFINE_MUTEX(intel_pstate_limits_lock);
|
static DEFINE_MUTEX(intel_pstate_limits_lock);
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
@ -538,7 +541,6 @@ static void intel_pstate_exit_perf_limits(struct cpufreq_policy *policy)
|
|||||||
|
|
||||||
acpi_processor_unregister_performance(policy->cpu);
|
acpi_processor_unregister_performance(policy->cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static inline void intel_pstate_init_acpi_perf_limits(struct cpufreq_policy *policy)
|
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);
|
rdmsrl_on_cpu(cpu, MSR_HWP_CAPABILITIES, &cap);
|
||||||
hw_min = HWP_LOWEST_PERF(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;
|
range = hw_max - hw_min;
|
||||||
|
|
||||||
max_perf_pct = perf_limits->max_perf_pct;
|
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;
|
adj_range = max_perf_pct * range / 100;
|
||||||
max = hw_min + adj_range;
|
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(~0L);
|
||||||
value |= HWP_MAX_PERF(max);
|
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");
|
DEFINE_SIMPLE_ATTRIBUTE(fops_pid_param, pid_param_get, pid_param_set, "%llu\n");
|
||||||
|
|
||||||
|
static struct dentry *debugfs_parent;
|
||||||
|
|
||||||
struct pid_param {
|
struct pid_param {
|
||||||
char *name;
|
char *name;
|
||||||
void *value;
|
void *value;
|
||||||
|
struct dentry *dentry;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct pid_param pid_files[] = {
|
static struct pid_param pid_files[] = {
|
||||||
{"sample_rate_ms", &pid_params.sample_rate_ms},
|
{"sample_rate_ms", &pid_params.sample_rate_ms, },
|
||||||
{"d_gain_pct", &pid_params.d_gain_pct},
|
{"d_gain_pct", &pid_params.d_gain_pct, },
|
||||||
{"i_gain_pct", &pid_params.i_gain_pct},
|
{"i_gain_pct", &pid_params.i_gain_pct, },
|
||||||
{"deadband", &pid_params.deadband},
|
{"deadband", &pid_params.deadband, },
|
||||||
{"setpoint", &pid_params.setpoint},
|
{"setpoint", &pid_params.setpoint, },
|
||||||
{"p_gain_pct", &pid_params.p_gain_pct},
|
{"p_gain_pct", &pid_params.p_gain_pct, },
|
||||||
{NULL, NULL}
|
{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;
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
debugfs_parent = debugfs_create_dir("pstate_snb", NULL);
|
debugfs_parent = debugfs_create_dir("pstate_snb", NULL);
|
||||||
if (IS_ERR_OR_NULL(debugfs_parent))
|
if (IS_ERR_OR_NULL(debugfs_parent))
|
||||||
return;
|
return;
|
||||||
while (pid_files[i].name) {
|
|
||||||
debugfs_create_file(pid_files[i].name, 0660,
|
for (i = 0; pid_files[i].name; i++) {
|
||||||
debugfs_parent, pid_files[i].value,
|
struct dentry *dentry;
|
||||||
&fops_pid_param);
|
|
||||||
i++;
|
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 ************************/
|
/************************** debugfs end ************************/
|
||||||
|
|
||||||
/************************** sysfs begin ************************/
|
/************************** sysfs begin ************************/
|
||||||
@ -1048,6 +1070,34 @@ static void __init intel_pstate_debug_expose_params(void)
|
|||||||
return sprintf(buf, "%u\n", limits->object); \
|
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,
|
static ssize_t show_turbo_pct(struct kobject *kobj,
|
||||||
struct attribute *attr, char *buf)
|
struct attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
@ -1055,12 +1105,22 @@ static ssize_t show_turbo_pct(struct kobject *kobj,
|
|||||||
int total, no_turbo, turbo_pct;
|
int total, no_turbo, turbo_pct;
|
||||||
uint32_t turbo_fp;
|
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];
|
cpu = all_cpu_data[0];
|
||||||
|
|
||||||
total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1;
|
total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1;
|
||||||
no_turbo = cpu->pstate.max_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_fp = div_fp(no_turbo, total);
|
||||||
turbo_pct = 100 - fp_toint(mul_fp(turbo_fp, int_tofp(100)));
|
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);
|
return sprintf(buf, "%u\n", turbo_pct);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1070,8 +1130,18 @@ static ssize_t show_num_pstates(struct kobject *kobj,
|
|||||||
struct cpudata *cpu;
|
struct cpudata *cpu;
|
||||||
int total;
|
int total;
|
||||||
|
|
||||||
|
mutex_lock(&intel_pstate_driver_lock);
|
||||||
|
|
||||||
|
if (!driver_registered) {
|
||||||
|
mutex_unlock(&intel_pstate_driver_lock);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
cpu = all_cpu_data[0];
|
cpu = all_cpu_data[0];
|
||||||
total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1;
|
total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1;
|
||||||
|
|
||||||
|
mutex_unlock(&intel_pstate_driver_lock);
|
||||||
|
|
||||||
return sprintf(buf, "%u\n", total);
|
return sprintf(buf, "%u\n", total);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1080,12 +1150,21 @@ static ssize_t show_no_turbo(struct kobject *kobj,
|
|||||||
{
|
{
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
|
mutex_lock(&intel_pstate_driver_lock);
|
||||||
|
|
||||||
|
if (!driver_registered) {
|
||||||
|
mutex_unlock(&intel_pstate_driver_lock);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
update_turbo_state();
|
update_turbo_state();
|
||||||
if (limits->turbo_disabled)
|
if (limits->turbo_disabled)
|
||||||
ret = sprintf(buf, "%u\n", limits->turbo_disabled);
|
ret = sprintf(buf, "%u\n", limits->turbo_disabled);
|
||||||
else
|
else
|
||||||
ret = sprintf(buf, "%u\n", limits->no_turbo);
|
ret = sprintf(buf, "%u\n", limits->no_turbo);
|
||||||
|
|
||||||
|
mutex_unlock(&intel_pstate_driver_lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1099,12 +1178,20 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b,
|
|||||||
if (ret != 1)
|
if (ret != 1)
|
||||||
return -EINVAL;
|
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);
|
mutex_lock(&intel_pstate_limits_lock);
|
||||||
|
|
||||||
update_turbo_state();
|
update_turbo_state();
|
||||||
if (limits->turbo_disabled) {
|
if (limits->turbo_disabled) {
|
||||||
pr_warn("Turbo disabled by BIOS or unavailable on processor\n");
|
pr_warn("Turbo disabled by BIOS or unavailable on processor\n");
|
||||||
mutex_unlock(&intel_pstate_limits_lock);
|
mutex_unlock(&intel_pstate_limits_lock);
|
||||||
|
mutex_unlock(&intel_pstate_driver_lock);
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1114,6 +1201,8 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b,
|
|||||||
|
|
||||||
intel_pstate_update_policies();
|
intel_pstate_update_policies();
|
||||||
|
|
||||||
|
mutex_unlock(&intel_pstate_driver_lock);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1127,6 +1216,13 @@ static ssize_t store_max_perf_pct(struct kobject *a, struct attribute *b,
|
|||||||
if (ret != 1)
|
if (ret != 1)
|
||||||
return -EINVAL;
|
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);
|
mutex_lock(&intel_pstate_limits_lock);
|
||||||
|
|
||||||
limits->max_sysfs_pct = clamp_t(int, input, 0 , 100);
|
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();
|
intel_pstate_update_policies();
|
||||||
|
|
||||||
|
mutex_unlock(&intel_pstate_driver_lock);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1155,6 +1253,13 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct attribute *b,
|
|||||||
if (ret != 1)
|
if (ret != 1)
|
||||||
return -EINVAL;
|
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);
|
mutex_lock(&intel_pstate_limits_lock);
|
||||||
|
|
||||||
limits->min_sysfs_pct = clamp_t(int, input, 0 , 100);
|
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();
|
intel_pstate_update_policies();
|
||||||
|
|
||||||
|
mutex_unlock(&intel_pstate_driver_lock);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
show_one(max_perf_pct, max_perf_pct);
|
show_one(max_perf_pct, max_perf_pct);
|
||||||
show_one(min_perf_pct, min_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(no_turbo);
|
||||||
define_one_global_rw(max_perf_pct);
|
define_one_global_rw(max_perf_pct);
|
||||||
define_one_global_rw(min_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);
|
define_one_global_ro(num_pstates);
|
||||||
|
|
||||||
static struct attribute *intel_pstate_attributes[] = {
|
static struct attribute *intel_pstate_attributes[] = {
|
||||||
|
&status.attr,
|
||||||
&no_turbo.attr,
|
&no_turbo.attr,
|
||||||
&turbo_pct.attr,
|
&turbo_pct.attr,
|
||||||
&num_pstates.attr,
|
&num_pstates.attr,
|
||||||
@ -1364,48 +1473,71 @@ static int core_get_max_pstate_physical(void)
|
|||||||
return (value >> 8) & 0xFF;
|
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)
|
static int core_get_max_pstate(void)
|
||||||
{
|
{
|
||||||
u64 tar;
|
u64 tar;
|
||||||
u64 plat_info;
|
u64 plat_info;
|
||||||
int max_pstate;
|
int max_pstate;
|
||||||
|
int tdp_ratio;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
rdmsrl(MSR_PLATFORM_INFO, plat_info);
|
rdmsrl(MSR_PLATFORM_INFO, plat_info);
|
||||||
max_pstate = (plat_info >> 8) & 0xFF;
|
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);
|
err = rdmsrl_safe(MSR_TURBO_ACTIVATION_RATIO, &tar);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
|
int tar_levels;
|
||||||
|
|
||||||
/* Do some sanity checking for safety */
|
/* Do some sanity checking for safety */
|
||||||
if (plat_info & 0x600000000) {
|
tar_levels = tar & 0xff;
|
||||||
u64 tdp_ctrl;
|
if (tdp_ratio - 1 == tar_levels) {
|
||||||
u64 tdp_ratio;
|
max_pstate = tar_levels;
|
||||||
int tdp_msr;
|
pr_debug("max_pstate=TAC %x\n", max_pstate);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_tar:
|
|
||||||
return max_pstate;
|
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)
|
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);
|
cpufreq_verify_within_cpu_limits(policy);
|
||||||
|
|
||||||
if (policy->policy != CPUFREQ_POLICY_POWERSAVE &&
|
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 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_load __initdata;
|
||||||
static int no_hwp __initdata;
|
static int no_hwp __initdata;
|
||||||
static int hwp_only __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)
|
static int __init intel_pstate_init(void)
|
||||||
{
|
{
|
||||||
int cpu, rc = 0;
|
|
||||||
const struct x86_cpu_id *id;
|
const struct x86_cpu_id *id;
|
||||||
struct cpu_defaults *cpu_def;
|
struct cpu_defaults *cpu_def;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
if (no_load)
|
if (no_load)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@ -2520,45 +2771,29 @@ hwp_cpu_matched:
|
|||||||
if (intel_pstate_platform_pwr_mgmt_exists())
|
if (intel_pstate_platform_pwr_mgmt_exists())
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (!hwp_active && hwp_only)
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
pr_info("Intel P-state driver initializing\n");
|
pr_info("Intel P-state driver initializing\n");
|
||||||
|
|
||||||
all_cpu_data = vzalloc(sizeof(void *) * num_possible_cpus());
|
all_cpu_data = vzalloc(sizeof(void *) * num_possible_cpus());
|
||||||
if (!all_cpu_data)
|
if (!all_cpu_data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (!hwp_active && hwp_only)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
intel_pstate_request_control_from_smm();
|
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();
|
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)
|
if (hwp_active)
|
||||||
pr_info("HWP enabled\n");
|
pr_info("HWP enabled\n");
|
||||||
|
|
||||||
return rc;
|
return 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
device_initcall(intel_pstate_init);
|
device_initcall(intel_pstate_init);
|
||||||
|
|
||||||
|
@ -144,6 +144,7 @@ static struct powernv_pstate_info {
|
|||||||
unsigned int max;
|
unsigned int max;
|
||||||
unsigned int nominal;
|
unsigned int nominal;
|
||||||
unsigned int nr_pstates;
|
unsigned int nr_pstates;
|
||||||
|
bool wof_enabled;
|
||||||
} powernv_pstate_info;
|
} powernv_pstate_info;
|
||||||
|
|
||||||
/* Use following macros for conversions between pstate_id and index */
|
/* 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;
|
const __be32 *pstate_ids, *pstate_freqs;
|
||||||
u32 len_ids, len_freqs;
|
u32 len_ids, len_freqs;
|
||||||
u32 pstate_min, pstate_max, pstate_nominal;
|
u32 pstate_min, pstate_max, pstate_nominal;
|
||||||
|
u32 pstate_turbo, pstate_ultra_turbo;
|
||||||
|
|
||||||
power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
|
power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
|
||||||
if (!power_mgt) {
|
if (!power_mgt) {
|
||||||
@ -225,8 +227,29 @@ static int init_powernv_pstates(void)
|
|||||||
pr_warn("ibm,pstate-nominal not found\n");
|
pr_warn("ibm,pstate-nominal not found\n");
|
||||||
return -ENODEV;
|
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,
|
pr_info("cpufreq pstate min %d nominal %d max %d\n", pstate_min,
|
||||||
pstate_nominal, pstate_max);
|
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);
|
pstate_ids = of_get_property(power_mgt, "ibm,pstate-ids", &len_ids);
|
||||||
if (!pstate_ids) {
|
if (!pstate_ids) {
|
||||||
@ -268,6 +291,13 @@ static int init_powernv_pstates(void)
|
|||||||
powernv_pstate_info.nominal = i;
|
powernv_pstate_info.nominal = i;
|
||||||
else if (id == pstate_min)
|
else if (id == pstate_min)
|
||||||
powernv_pstate_info.min = i;
|
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 */
|
/* 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 =
|
struct freq_attr cpufreq_freq_attr_cpuinfo_nominal_freq =
|
||||||
__ATTR_RO(cpuinfo_nominal_freq);
|
__ATTR_RO(cpuinfo_nominal_freq);
|
||||||
|
|
||||||
|
#define SCALING_BOOST_FREQS_ATTR_INDEX 2
|
||||||
|
|
||||||
static struct freq_attr *powernv_cpu_freq_attr[] = {
|
static struct freq_attr *powernv_cpu_freq_attr[] = {
|
||||||
&cpufreq_freq_attr_scaling_available_freqs,
|
&cpufreq_freq_attr_scaling_available_freqs,
|
||||||
&cpufreq_freq_attr_cpuinfo_nominal_freq,
|
&cpufreq_freq_attr_cpuinfo_nominal_freq,
|
||||||
|
&cpufreq_freq_attr_scaling_boost_freqs,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1013,11 +1046,22 @@ static int __init powernv_cpufreq_init(void)
|
|||||||
register_reboot_notifier(&powernv_cpufreq_reboot_nb);
|
register_reboot_notifier(&powernv_cpufreq_reboot_nb);
|
||||||
opal_message_notifier_register(OPAL_MSG_OCC, &powernv_cpufreq_opal_nb);
|
opal_message_notifier_register(OPAL_MSG_OCC, &powernv_cpufreq_opal_nb);
|
||||||
|
|
||||||
rc = cpufreq_register_driver(&powernv_cpufreq_driver);
|
if (powernv_pstate_info.wof_enabled)
|
||||||
if (!rc)
|
powernv_cpufreq_driver.boost_enabled = true;
|
||||||
return 0;
|
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();
|
unregister_all_notifiers();
|
||||||
clean_chip_info();
|
clean_chip_info();
|
||||||
out:
|
out:
|
||||||
|
@ -100,9 +100,6 @@ static int pmi_notifier(struct notifier_block *nb,
|
|||||||
/* Should this really be called for CPUFREQ_ADJUST and CPUFREQ_NOTIFY
|
/* Should this really be called for CPUFREQ_ADJUST and CPUFREQ_NOTIFY
|
||||||
* policy events?)
|
* policy events?)
|
||||||
*/
|
*/
|
||||||
if (event == CPUFREQ_START)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
node = cbe_cpu_to_node(policy->cpu);
|
node = cbe_cpu_to_node(policy->cpu);
|
||||||
|
|
||||||
pr_debug("got notified, event=%lu, node=%u\n", event, node);
|
pr_debug("got notified, event=%lu, node=%u\n", event, node);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/cpufreq.h>
|
#include <linux/cpufreq.h>
|
||||||
#include <linux/cpu_cooling.h>
|
#include <linux/cpu_cooling.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
@ -37,53 +38,20 @@ struct cpu_data {
|
|||||||
struct thermal_cooling_device *cdev;
|
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
|
* struct soc_data - SoC specific data
|
||||||
* @freq_mask: mask the disallowed frequencies
|
* @flags: SOC_xxx
|
||||||
* @flag: unique flags
|
|
||||||
*/
|
*/
|
||||||
struct soc_data {
|
struct soc_data {
|
||||||
u32 freq_mask[4];
|
u32 flags;
|
||||||
u32 flag;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#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)
|
static u32 get_bus_freq(void)
|
||||||
{
|
{
|
||||||
struct device_node *soc;
|
struct device_node *soc;
|
||||||
@ -101,9 +69,10 @@ static u32 get_bus_freq(void)
|
|||||||
return sysfreq;
|
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))
|
if (!cpu_present(cpu))
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -112,37 +81,28 @@ static struct device_node *cpu_to_clk_node(int cpu)
|
|||||||
if (!np)
|
if (!np)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
clk_np = of_parse_phandle(np, "clocks", 0);
|
clk = of_clk_get(np, 0);
|
||||||
if (!clk_np)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
of_node_put(np);
|
of_node_put(np);
|
||||||
|
return clk;
|
||||||
return clk_np;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* traverse cpu nodes to get cpu mask of sharing clock wire */
|
/* traverse cpu nodes to get cpu mask of sharing clock wire */
|
||||||
static void set_affected_cpus(struct cpufreq_policy *policy)
|
static void set_affected_cpus(struct cpufreq_policy *policy)
|
||||||
{
|
{
|
||||||
struct device_node *np, *clk_np;
|
|
||||||
struct cpumask *dstp = policy->cpus;
|
struct cpumask *dstp = policy->cpus;
|
||||||
|
struct clk *clk;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
np = cpu_to_clk_node(policy->cpu);
|
|
||||||
if (!np)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for_each_present_cpu(i) {
|
for_each_present_cpu(i) {
|
||||||
clk_np = cpu_to_clk_node(i);
|
clk = cpu_to_clk(i);
|
||||||
if (!clk_np)
|
if (IS_ERR(clk)) {
|
||||||
|
pr_err("%s: no clock for cpu %d\n", __func__, i);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (clk_np == np)
|
if (clk_is_match(policy->clk, clk))
|
||||||
cpumask_set_cpu(i, dstp);
|
cpumask_set_cpu(i, dstp);
|
||||||
|
|
||||||
of_node_put(clk_np);
|
|
||||||
}
|
}
|
||||||
of_node_put(np);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reduce the duplicated frequencies in frequency table */
|
/* 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)
|
static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||||
{
|
{
|
||||||
struct device_node *np, *pnode;
|
struct device_node *np;
|
||||||
int i, count, ret;
|
int i, count, ret;
|
||||||
u32 freq, mask;
|
u32 freq;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
const struct clk_hw *hwclk;
|
||||||
struct cpufreq_frequency_table *table;
|
struct cpufreq_frequency_table *table;
|
||||||
struct cpu_data *data;
|
struct cpu_data *data;
|
||||||
unsigned int cpu = policy->cpu;
|
unsigned int cpu = policy->cpu;
|
||||||
@ -221,17 +182,13 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|||||||
goto err_nomem2;
|
goto err_nomem2;
|
||||||
}
|
}
|
||||||
|
|
||||||
pnode = of_parse_phandle(np, "clocks", 0);
|
hwclk = __clk_get_hw(policy->clk);
|
||||||
if (!pnode) {
|
count = clk_hw_get_num_parents(hwclk);
|
||||||
pr_err("%s: could not get clock information\n", __func__);
|
|
||||||
goto err_nomem2;
|
|
||||||
}
|
|
||||||
|
|
||||||
count = of_property_count_strings(pnode, "clock-names");
|
|
||||||
data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
|
data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
|
||||||
if (!data->pclk) {
|
if (!data->pclk) {
|
||||||
pr_err("%s: no memory\n", __func__);
|
pr_err("%s: no memory\n", __func__);
|
||||||
goto err_node;
|
goto err_nomem2;
|
||||||
}
|
}
|
||||||
|
|
||||||
table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
|
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;
|
goto err_pclk;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fmask)
|
|
||||||
mask = fmask[get_cpu_physical_id(cpu)];
|
|
||||||
else
|
|
||||||
mask = 0x0;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
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;
|
data->pclk[i] = clk;
|
||||||
freq = clk_get_rate(clk);
|
freq = clk_get_rate(clk);
|
||||||
/*
|
table[i].frequency = freq / 1000;
|
||||||
* 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].driver_data = i;
|
table[i].driver_data = i;
|
||||||
}
|
}
|
||||||
freq_table_redup(table, count);
|
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;
|
policy->cpuinfo.transition_latency = u64temp + 1;
|
||||||
|
|
||||||
of_node_put(np);
|
of_node_put(np);
|
||||||
of_node_put(pnode);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -290,10 +234,7 @@ err_nomem1:
|
|||||||
kfree(table);
|
kfree(table);
|
||||||
err_pclk:
|
err_pclk:
|
||||||
kfree(data->pclk);
|
kfree(data->pclk);
|
||||||
err_node:
|
|
||||||
of_node_put(pnode);
|
|
||||||
err_nomem2:
|
err_nomem2:
|
||||||
policy->driver_data = NULL;
|
|
||||||
kfree(data);
|
kfree(data);
|
||||||
err_np:
|
err_np:
|
||||||
of_node_put(np);
|
of_node_put(np);
|
||||||
@ -357,12 +298,25 @@ static struct cpufreq_driver qoriq_cpufreq_driver = {
|
|||||||
.attr = cpufreq_generic_attr,
|
.attr = cpufreq_generic_attr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct soc_data blacklist = {
|
||||||
|
.flags = SOC_BLACKLIST,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id node_matches[] __initconst = {
|
static const struct of_device_id node_matches[] __initconst = {
|
||||||
{ .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
|
/* e6500 cannot use cpufreq due to erratum A-008083 */
|
||||||
{ .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
|
{ .compatible = "fsl,b4420-clockgen", &blacklist },
|
||||||
{ .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
|
{ .compatible = "fsl,b4860-clockgen", &blacklist },
|
||||||
{ .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
|
{ .compatible = "fsl,t2080-clockgen", &blacklist },
|
||||||
{ .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
|
{ .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", },
|
{ .compatible = "fsl,qoriq-clockgen-2.0", },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@ -380,16 +334,12 @@ static int __init qoriq_cpufreq_init(void)
|
|||||||
|
|
||||||
match = of_match_node(node_matches, np);
|
match = of_match_node(node_matches, np);
|
||||||
data = match->data;
|
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);
|
of_node_put(np);
|
||||||
|
|
||||||
|
if (data && data->flags & SOC_BLACKLIST)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
|
ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
pr_info("Freescale QorIQ CPU frequency scaling driver\n");
|
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);
|
rate = clk_get_rate(s3c_freq->hclk);
|
||||||
if (rate < 133 * 1000 * 1000) {
|
if (rate < 133 * 1000 * 1000) {
|
||||||
pr_err("cpufreq: HCLK not at 133MHz\n");
|
pr_err("cpufreq: HCLK not at 133MHz\n");
|
||||||
clk_put(s3c_freq->hclk);
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto err_armclk;
|
goto err_armclk;
|
||||||
}
|
}
|
||||||
|
268
drivers/cpufreq/ti-cpufreq.c
Normal file
268
drivers/cpufreq/ti-cpufreq.c
Normal file
@ -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_ETERNAL (-1)
|
||||||
#define CPUFREQ_NAME_LEN 16
|
#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)
|
#define CPUFREQ_NAME_PLEN (CPUFREQ_NAME_LEN + 1)
|
||||||
|
|
||||||
struct cpufreq_governor;
|
struct cpufreq_governor;
|
||||||
@ -115,7 +115,7 @@ struct cpufreq_policy {
|
|||||||
* guarantee that frequency can be changed on any CPU sharing the
|
* guarantee that frequency can be changed on any CPU sharing the
|
||||||
* policy and that the change will affect all of the policy CPUs then.
|
* 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
|
* - 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_possible;
|
||||||
bool fast_switch_enabled;
|
bool fast_switch_enabled;
|
||||||
@ -415,9 +415,6 @@ static inline void cpufreq_resume(void) {}
|
|||||||
/* Policy Notifiers */
|
/* Policy Notifiers */
|
||||||
#define CPUFREQ_ADJUST (0)
|
#define CPUFREQ_ADJUST (0)
|
||||||
#define CPUFREQ_NOTIFY (1)
|
#define CPUFREQ_NOTIFY (1)
|
||||||
#define CPUFREQ_START (2)
|
|
||||||
#define CPUFREQ_CREATE_POLICY (3)
|
|
||||||
#define CPUFREQ_REMOVE_POLICY (4)
|
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_FREQ
|
#ifdef CONFIG_CPU_FREQ
|
||||||
int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list);
|
int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list);
|
||||||
|
Loading…
Reference in New Issue
Block a user