提交 1e2af254 编写于 作者: L Linus Torvalds

Merge tag 'pm-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management updates from Rafael Wysocki:
 "These add sysadmin documentation for cpuidle, extend the cpuidle
  subsystem somewhat, improve the handling of performance states in the
  generic power domains (genpd) and operating performance points (OPP)
  frameworks, add a new cpufreq driver for Qualcomm SoCs, update some
  other cpufreq drivers, switch over the runtime PM framework to using
  high-res timers for device autosuspend, fix a problem with
  suspend-to-idle on ACPI-based platforms, add system-wide suspend and
  resume handling to the devfreq framework, do some janitorial cleanups
  all over and update some utilities.

  Specifics:

   - Add sysadmin documentation for cpuidle (Rafael Wysocki).

   - Make it possible to specify a cpuidle governor from kernel command
     line, add new cpuidle state sysfs attributes for governor
     evaluation, and improve the "polling" idle state handling (Rafael
     Wysocki).

   - Fix the handling of the "required-opps" DT property in the
     operating performance points (OPP) framework, improve the
     integration of it with the generic power domains (genpd) framework,
     improve the handling of performance states in them and clean up the
     idle states vs performance states separation in genpd (Viresh
     Kumar, Ulf Hansson).

   - Add a cpufreq driver called "qcom-hw" for Qualcomm SoCs using a
     hardware engine to control CPU frequency transitions along with DT
     bindings for it (Taniya Das).

   - Fix an intel_pstate driver issue related to CPU offline and update
     the documentation of it (Srinivas Pandruvada).

   - Clean up the imx6q cpufreq driver (Anson Huang).

   - Add SPDX license IDs to cpufreq schedutil governor files (Daniel
     Lezcano).

   - Switch over the runtime PM framework to using high-res timers for
     device autosuspend to allow the control of it to be more precise
     (Vincent Guittot).

   - Disable non-wakeup ACPI GPEs during suspend-to-idle so that they
     don't prevent the system from reaching the target low-power state
     and simplify the suspend-to-idle handling on ACPI platforms without
     full Low-Power S0 Idle (LPS0) support (Rafael Wysocki).

   - Add system-wide suspend and resume support to the devfreq framework
     (Lukasz Luba).

   - Clean up the SmartReflex adaptive voltage scaling (AVS) driver and
     add an SPDX license ID to it (Nishanth Menon, Uwe Kleine-König,
     Thomas Meyer).

   - Get rid of code duplication by using the DEFINE_SHOW_ATTRIBUTE
     macro in some places, fix some DT node refcount leaks, and do some
     other janitorial cleanups (Yangtao Li).

   - Update the cpupower, intel_pstate_tracer and turbosat utilities
     (Abhishek Goel, Doug Smythies, Len Brown)"

* tag 'pm-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (54 commits)
  PM / Domains: remove define_genpd_open_function() and define_genpd_debugfs_fops()
  PM-runtime: Switch autosuspend over to using hrtimers
  cpufreq: qcom-hw: Add support for QCOM cpufreq HW driver
  dt-bindings: cpufreq: Introduce QCOM cpufreq firmware bindings
  ACPI: PM: Loop in full LPS0 mode only
  ACPI: EC / PM: Disable non-wakeup GPEs for suspend-to-idle
  tools/power/x86/intel_pstate_tracer: Fix non root execution for post processing a trace file
  tools/power turbostat: consolidate duplicate model numbers
  tools/power turbostat: fix goldmont C-state limit decoding
  PM / Domains: Propagate performance state updates
  PM / Domains: Factorize dev_pm_genpd_set_performance_state()
  PM / Domains: Save OPP table pointer in genpd
  OPP: Don't return 0 on error from of_get_required_opp_performance_state()
  OPP: Add dev_pm_opp_xlate_performance_state() helper
  OPP: Improve _find_table_of_opp_np()
  PM / Domains: Make genpd performance states orthogonal to the idlestates
  PM / sleep: convert to DEFINE_SHOW_ATTRIBUTE
  cpuidle: Add 'above' and 'below' idle state metrics
  PM / AVS: SmartReflex: Switch to SPDX Licence ID
  PM / AVS: SmartReflex: NULL check before some freeing functions is not needed
  ...
...@@ -145,6 +145,8 @@ What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/name ...@@ -145,6 +145,8 @@ What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/name
/sys/devices/system/cpu/cpuX/cpuidle/stateN/power /sys/devices/system/cpu/cpuX/cpuidle/stateN/power
/sys/devices/system/cpu/cpuX/cpuidle/stateN/time /sys/devices/system/cpu/cpuX/cpuidle/stateN/time
/sys/devices/system/cpu/cpuX/cpuidle/stateN/usage /sys/devices/system/cpu/cpuX/cpuidle/stateN/usage
/sys/devices/system/cpu/cpuX/cpuidle/stateN/above
/sys/devices/system/cpu/cpuX/cpuidle/stateN/below
Date: September 2007 Date: September 2007
KernelVersion: v2.6.24 KernelVersion: v2.6.24
Contact: Linux power management list <linux-pm@vger.kernel.org> Contact: Linux power management list <linux-pm@vger.kernel.org>
...@@ -166,6 +168,11 @@ Description: ...@@ -166,6 +168,11 @@ Description:
usage: (RO) Number of times this state was entered (a count). usage: (RO) Number of times this state was entered (a count).
above: (RO) Number of times this state was entered, but the
observed CPU idle duration was too short for it (a count).
below: (RO) Number of times this state was entered, but the
observed CPU idle duration was too long for it (a count).
What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/desc What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/desc
Date: February 2008 Date: February 2008
......
...@@ -674,6 +674,9 @@ ...@@ -674,6 +674,9 @@
cpuidle.off=1 [CPU_IDLE] cpuidle.off=1 [CPU_IDLE]
disable the cpuidle sub-system disable the cpuidle sub-system
cpuidle.governor=
[CPU_IDLE] Name of the cpuidle governor to use.
cpufreq.off=1 [CPU_FREQ] cpufreq.off=1 [CPU_FREQ]
disable the cpufreq sub-system disable the cpufreq sub-system
......
此差异已折叠。
...@@ -495,7 +495,15 @@ on the following rules, regardless of the current operation mode of the driver: ...@@ -495,7 +495,15 @@ on the following rules, regardless of the current operation mode of the driver:
2. Each individual CPU is affected by its own per-policy limits (that is, it 2. Each individual CPU is affected by its own per-policy limits (that is, it
cannot be requested to run faster than its own per-policy maximum and it cannot be requested to run faster than its own per-policy maximum and it
cannot be requested to run slower than its own per-policy minimum). cannot be requested to run slower than its own per-policy minimum). The
effective performance depends on whether the platform supports per core
P-states, hyper-threading is enabled and on current performance requests
from other CPUs. When platform doesn't support per core P-states, the
effective performance can be more than the policy limits set on a CPU, if
other CPUs are requesting higher performance at that moment. Even with per
core P-states support, when hyper-threading is enabled, if the sibling CPU
is requesting higher performance, the other siblings will get higher
performance than their policy limits.
3. The global and per-policy limits can be set independently. 3. The global and per-policy limits can be set independently.
......
...@@ -5,5 +5,6 @@ Working-State Power Management ...@@ -5,5 +5,6 @@ Working-State Power Management
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
cpuidle
cpufreq cpufreq
intel_pstate intel_pstate
Supporting multiple CPU idle levels in kernel
cpuidle
General Information:
Various CPUs today support multiple idle levels that are differentiated
by varying exit latencies and power consumption during idle.
cpuidle is a generic in-kernel infrastructure that separates
idle policy (governor) from idle mechanism (driver) and provides a
standardized infrastructure to support independent development of
governors and drivers.
cpuidle resides under drivers/cpuidle.
Boot options:
"cpuidle_sysfs_switch"
enables current_governor interface in /sys/devices/system/cpu/cpuidle/,
which can be used to switch governors at run time. This boot option
is meant for developer testing only. In normal usage, kernel picks the
best governor based on governor ratings.
SEE ALSO: sysfs.txt in this directory.
Supporting multiple CPU idle levels in kernel
cpuidle sysfs
System global cpuidle related information and tunables are under
/sys/devices/system/cpu/cpuidle
The current interfaces in this directory has self-explanatory names:
* current_driver
* current_governor_ro
With cpuidle_sysfs_switch boot option (meant for developer testing)
following objects are visible instead.
* current_driver
* available_governors
* current_governor
In this case users can switch the governor at run time by writing
to current_governor.
Per logical CPU specific cpuidle information are under
/sys/devices/system/cpu/cpuX/cpuidle
for each online cpu X
--------------------------------------------------------------------------------
# ls -lR /sys/devices/system/cpu/cpu0/cpuidle/
/sys/devices/system/cpu/cpu0/cpuidle/:
total 0
drwxr-xr-x 2 root root 0 Feb 8 10:42 state0
drwxr-xr-x 2 root root 0 Feb 8 10:42 state1
drwxr-xr-x 2 root root 0 Feb 8 10:42 state2
drwxr-xr-x 2 root root 0 Feb 8 10:42 state3
/sys/devices/system/cpu/cpu0/cpuidle/state0:
total 0
-r--r--r-- 1 root root 4096 Feb 8 10:42 desc
-rw-r--r-- 1 root root 4096 Feb 8 10:42 disable
-r--r--r-- 1 root root 4096 Feb 8 10:42 latency
-r--r--r-- 1 root root 4096 Feb 8 10:42 name
-r--r--r-- 1 root root 4096 Feb 8 10:42 power
-r--r--r-- 1 root root 4096 Feb 8 10:42 residency
-r--r--r-- 1 root root 4096 Feb 8 10:42 time
-r--r--r-- 1 root root 4096 Feb 8 10:42 usage
/sys/devices/system/cpu/cpu0/cpuidle/state1:
total 0
-r--r--r-- 1 root root 4096 Feb 8 10:42 desc
-rw-r--r-- 1 root root 4096 Feb 8 10:42 disable
-r--r--r-- 1 root root 4096 Feb 8 10:42 latency
-r--r--r-- 1 root root 4096 Feb 8 10:42 name
-r--r--r-- 1 root root 4096 Feb 8 10:42 power
-r--r--r-- 1 root root 4096 Feb 8 10:42 residency
-r--r--r-- 1 root root 4096 Feb 8 10:42 time
-r--r--r-- 1 root root 4096 Feb 8 10:42 usage
/sys/devices/system/cpu/cpu0/cpuidle/state2:
total 0
-r--r--r-- 1 root root 4096 Feb 8 10:42 desc
-rw-r--r-- 1 root root 4096 Feb 8 10:42 disable
-r--r--r-- 1 root root 4096 Feb 8 10:42 latency
-r--r--r-- 1 root root 4096 Feb 8 10:42 name
-r--r--r-- 1 root root 4096 Feb 8 10:42 power
-r--r--r-- 1 root root 4096 Feb 8 10:42 residency
-r--r--r-- 1 root root 4096 Feb 8 10:42 time
-r--r--r-- 1 root root 4096 Feb 8 10:42 usage
/sys/devices/system/cpu/cpu0/cpuidle/state3:
total 0
-r--r--r-- 1 root root 4096 Feb 8 10:42 desc
-rw-r--r-- 1 root root 4096 Feb 8 10:42 disable
-r--r--r-- 1 root root 4096 Feb 8 10:42 latency
-r--r--r-- 1 root root 4096 Feb 8 10:42 name
-r--r--r-- 1 root root 4096 Feb 8 10:42 power
-r--r--r-- 1 root root 4096 Feb 8 10:42 residency
-r--r--r-- 1 root root 4096 Feb 8 10:42 time
-r--r--r-- 1 root root 4096 Feb 8 10:42 usage
--------------------------------------------------------------------------------
* desc : Small description about the idle state (string)
* disable : Option to disable this idle state (bool) -> see note below
* latency : Latency to exit out of this idle state (in microseconds)
* residency : Time after which a state becomes more effecient than any
shallower state (in microseconds)
* name : Name of the idle state (string)
* power : Power consumed while in this idle state (in milliwatts)
* time : Total time spent in this idle state (in microseconds)
* usage : Number of times this state was entered (count)
Note:
The behavior and the effect of the disable variable depends on the
implementation of a particular governor. In the ladder governor, for
example, it is not coherent, i.e. if one is disabling a light state,
then all deeper states are disabled as well, but the disable variable
does not reflect it. Likewise, if one enables a deep state but a lighter
state still is disabled, then this has no effect.
Qualcomm Technologies, Inc. CPUFREQ Bindings
CPUFREQ HW is a hardware engine used by some Qualcomm Technologies, Inc. (QTI)
SoCs to manage frequency in hardware. It is capable of controlling frequency
for multiple clusters.
Properties:
- compatible
Usage: required
Value type: <string>
Definition: must be "qcom,cpufreq-hw".
- clocks
Usage: required
Value type: <phandle> From common clock binding.
Definition: clock handle for XO clock and GPLL0 clock.
- clock-names
Usage: required
Value type: <string> From common clock binding.
Definition: must be "xo", "alternate".
- reg
Usage: required
Value type: <prop-encoded-array>
Definition: Addresses and sizes for the memory of the HW bases in
each frequency domain.
- reg-names
Usage: Optional
Value type: <string>
Definition: Frequency domain name i.e.
"freq-domain0", "freq-domain1".
- #freq-domain-cells:
Usage: required.
Definition: Number of cells in a freqency domain specifier.
* Property qcom,freq-domain
Devices supporting freq-domain must set their "qcom,freq-domain" property with
phandle to a cpufreq_hw followed by the Domain ID(0/1) in the CPU DT node.
Example:
Example 1: Dual-cluster, Quad-core per cluster. CPUs within a cluster switch
DCVS state together.
/ {
cpus {
#address-cells = <2>;
#size-cells = <0>;
CPU0: cpu@0 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x0>;
enable-method = "psci";
next-level-cache = <&L2_0>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_0: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
L3_0: l3-cache {
compatible = "cache";
};
};
};
CPU1: cpu@100 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x100>;
enable-method = "psci";
next-level-cache = <&L2_100>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_100: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU2: cpu@200 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x200>;
enable-method = "psci";
next-level-cache = <&L2_200>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_200: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU3: cpu@300 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x300>;
enable-method = "psci";
next-level-cache = <&L2_300>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_300: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU4: cpu@400 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x400>;
enable-method = "psci";
next-level-cache = <&L2_400>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_400: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU5: cpu@500 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x500>;
enable-method = "psci";
next-level-cache = <&L2_500>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_500: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU6: cpu@600 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x600>;
enable-method = "psci";
next-level-cache = <&L2_600>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_600: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU7: cpu@700 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x700>;
enable-method = "psci";
next-level-cache = <&L2_700>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_700: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
};
soc {
cpufreq_hw: cpufreq@17d43000 {
compatible = "qcom,cpufreq-hw";
reg = <0x17d43000 0x1400>, <0x17d45800 0x1400>;
reg-names = "freq-domain0", "freq-domain1";
clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GPLL0>;
clock-names = "xo", "alternate";
#freq-domain-cells = <1>;
};
}
...@@ -1034,6 +1034,18 @@ void acpi_ec_unblock_transactions(void) ...@@ -1034,6 +1034,18 @@ void acpi_ec_unblock_transactions(void)
acpi_ec_start(first_ec, true); acpi_ec_start(first_ec, true);
} }
void acpi_ec_mark_gpe_for_wake(void)
{
if (first_ec && !ec_no_wakeup)
acpi_mark_gpe_for_wake(NULL, first_ec->gpe);
}
void acpi_ec_set_gpe_wake_mask(u8 action)
{
if (first_ec && !ec_no_wakeup)
acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action);
}
void acpi_ec_dispatch_gpe(void) void acpi_ec_dispatch_gpe(void)
{ {
if (first_ec) if (first_ec)
......
...@@ -188,6 +188,8 @@ int acpi_ec_ecdt_probe(void); ...@@ -188,6 +188,8 @@ int acpi_ec_ecdt_probe(void);
int acpi_ec_dsdt_probe(void); int acpi_ec_dsdt_probe(void);
void acpi_ec_block_transactions(void); void acpi_ec_block_transactions(void);
void acpi_ec_unblock_transactions(void); void acpi_ec_unblock_transactions(void);
void acpi_ec_mark_gpe_for_wake(void);
void acpi_ec_set_gpe_wake_mask(u8 action);
void acpi_ec_dispatch_gpe(void); void acpi_ec_dispatch_gpe(void);
int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
acpi_handle handle, acpi_ec_query_func func, acpi_handle handle, acpi_ec_query_func func,
......
...@@ -940,6 +940,8 @@ static int lps0_device_attach(struct acpi_device *adev, ...@@ -940,6 +940,8 @@ static int lps0_device_attach(struct acpi_device *adev,
acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n", acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n",
bitmask); bitmask);
acpi_ec_mark_gpe_for_wake();
} else { } else {
acpi_handle_debug(adev->handle, acpi_handle_debug(adev->handle,
"_DSM function 0 evaluation failed\n"); "_DSM function 0 evaluation failed\n");
...@@ -968,16 +970,23 @@ static int acpi_s2idle_prepare(void) ...@@ -968,16 +970,23 @@ static int acpi_s2idle_prepare(void)
if (lps0_device_handle) { if (lps0_device_handle) {
acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF); acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF);
acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY); acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY);
acpi_ec_set_gpe_wake_mask(ACPI_GPE_ENABLE);
} }
if (acpi_sci_irq_valid()) if (acpi_sci_irq_valid())
enable_irq_wake(acpi_sci_irq); enable_irq_wake(acpi_sci_irq);
/* Change the configuration of GPEs to avoid spurious wakeup. */
acpi_enable_all_wakeup_gpes();
acpi_os_wait_events_complete();
return 0; return 0;
} }
static void acpi_s2idle_wake(void) static void acpi_s2idle_wake(void)
{ {
if (!lps0_device_handle)
return;
if (pm_debug_messages_on) if (pm_debug_messages_on)
lpi_check_constraints(); lpi_check_constraints();
...@@ -996,8 +1005,7 @@ static void acpi_s2idle_wake(void) ...@@ -996,8 +1005,7 @@ static void acpi_s2idle_wake(void)
* takes too much time for EC wakeup events to survive, so look * takes too much time for EC wakeup events to survive, so look
* for them now. * for them now.
*/ */
if (lps0_device_handle) acpi_ec_dispatch_gpe();
acpi_ec_dispatch_gpe();
} }
} }
...@@ -1017,10 +1025,14 @@ static void acpi_s2idle_sync(void) ...@@ -1017,10 +1025,14 @@ static void acpi_s2idle_sync(void)
static void acpi_s2idle_restore(void) static void acpi_s2idle_restore(void)
{ {
acpi_enable_all_runtime_gpes();
if (acpi_sci_irq_valid()) if (acpi_sci_irq_valid())
disable_irq_wake(acpi_sci_irq); disable_irq_wake(acpi_sci_irq);
if (lps0_device_handle) { if (lps0_device_handle) {
acpi_ec_set_gpe_wake_mask(ACPI_GPE_DISABLE);
acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT); acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT);
acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON); acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON);
} }
......
...@@ -239,6 +239,127 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd) ...@@ -239,6 +239,127 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)
static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
#endif #endif
static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd,
unsigned int state)
{
struct generic_pm_domain_data *pd_data;
struct pm_domain_data *pdd;
struct gpd_link *link;
/* New requested state is same as Max requested state */
if (state == genpd->performance_state)
return state;
/* New requested state is higher than Max requested state */
if (state > genpd->performance_state)
return state;
/* Traverse all devices within the domain */
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
pd_data = to_gpd_data(pdd);
if (pd_data->performance_state > state)
state = pd_data->performance_state;
}
/*
* Traverse all sub-domains within the domain. This can be
* done without any additional locking as the link->performance_state
* field is protected by the master genpd->lock, which is already taken.
*
* Also note that link->performance_state (subdomain's performance state
* requirement to master domain) is different from
* link->slave->performance_state (current performance state requirement
* of the devices/sub-domains of the subdomain) and so can have a
* different value.
*
* Note that we also take vote from powered-off sub-domains into account
* as the same is done for devices right now.
*/
list_for_each_entry(link, &genpd->master_links, master_node) {
if (link->performance_state > state)
state = link->performance_state;
}
return state;
}
static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
unsigned int state, int depth)
{
struct generic_pm_domain *master;
struct gpd_link *link;
int master_state, ret;
if (state == genpd->performance_state)
return 0;
/* Propagate to masters of genpd */
list_for_each_entry(link, &genpd->slave_links, slave_node) {
master = link->master;
if (!master->set_performance_state)
continue;
/* Find master's performance state */
ret = dev_pm_opp_xlate_performance_state(genpd->opp_table,
master->opp_table,
state);
if (unlikely(ret < 0))
goto err;
master_state = ret;
genpd_lock_nested(master, depth + 1);
link->prev_performance_state = link->performance_state;
link->performance_state = master_state;
master_state = _genpd_reeval_performance_state(master,
master_state);
ret = _genpd_set_performance_state(master, master_state, depth + 1);
if (ret)
link->performance_state = link->prev_performance_state;
genpd_unlock(master);
if (ret)
goto err;
}
ret = genpd->set_performance_state(genpd, state);
if (ret)
goto err;
genpd->performance_state = state;
return 0;
err:
/* Encountered an error, lets rollback */
list_for_each_entry_continue_reverse(link, &genpd->slave_links,
slave_node) {
master = link->master;
if (!master->set_performance_state)
continue;
genpd_lock_nested(master, depth + 1);
master_state = link->prev_performance_state;
link->performance_state = master_state;
master_state = _genpd_reeval_performance_state(master,
master_state);
if (_genpd_set_performance_state(master, master_state, depth + 1)) {
pr_err("%s: Failed to roll back to %d performance state\n",
master->name, master_state);
}
genpd_unlock(master);
}
return ret;
}
/** /**
* dev_pm_genpd_set_performance_state- Set performance state of device's power * dev_pm_genpd_set_performance_state- Set performance state of device's power
* domain. * domain.
...@@ -257,10 +378,9 @@ static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} ...@@ -257,10 +378,9 @@ static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
struct generic_pm_domain_data *gpd_data, *pd_data; struct generic_pm_domain_data *gpd_data;
struct pm_domain_data *pdd;
unsigned int prev; unsigned int prev;
int ret = 0; int ret;
genpd = dev_to_genpd(dev); genpd = dev_to_genpd(dev);
if (IS_ERR(genpd)) if (IS_ERR(genpd))
...@@ -281,47 +401,11 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) ...@@ -281,47 +401,11 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
prev = gpd_data->performance_state; prev = gpd_data->performance_state;
gpd_data->performance_state = state; gpd_data->performance_state = state;
/* New requested state is same as Max requested state */ state = _genpd_reeval_performance_state(genpd, state);
if (state == genpd->performance_state) ret = _genpd_set_performance_state(genpd, state, 0);
goto unlock; if (ret)
gpd_data->performance_state = prev;
/* New requested state is higher than Max requested state */
if (state > genpd->performance_state)
goto update_state;
/* Traverse all devices within the domain */
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
pd_data = to_gpd_data(pdd);
if (pd_data->performance_state > state)
state = pd_data->performance_state;
}
if (state == genpd->performance_state)
goto unlock;
/*
* We aren't propagating performance state changes of a subdomain to its
* masters as we don't have hardware that needs it. Over that, the
* performance states of subdomain and its masters may not have
* one-to-one mapping and would require additional information. We can
* get back to this once we have hardware that needs it. For that
* reason, we don't have to consider performance state of the subdomains
* of genpd here.
*/
update_state:
if (genpd_status_on(genpd)) {
ret = genpd->set_performance_state(genpd, state);
if (ret) {
gpd_data->performance_state = prev;
goto unlock;
}
}
genpd->performance_state = state;
unlock:
genpd_unlock(genpd); genpd_unlock(genpd);
return ret; return ret;
...@@ -347,15 +431,6 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) ...@@ -347,15 +431,6 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
return ret; return ret;
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (unlikely(genpd->set_performance_state)) {
ret = genpd->set_performance_state(genpd, genpd->performance_state);
if (ret) {
pr_warn("%s: Failed to set performance state %d (%d)\n",
genpd->name, genpd->performance_state, ret);
}
}
if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
return ret; return ret;
...@@ -1907,12 +1982,21 @@ int of_genpd_add_provider_simple(struct device_node *np, ...@@ -1907,12 +1982,21 @@ int of_genpd_add_provider_simple(struct device_node *np,
ret); ret);
goto unlock; goto unlock;
} }
/*
* Save table for faster processing while setting performance
* state.
*/
genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
WARN_ON(!genpd->opp_table);
} }
ret = genpd_add_provider(np, genpd_xlate_simple, genpd); ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
if (ret) { if (ret) {
if (genpd->set_performance_state) if (genpd->set_performance_state) {
dev_pm_opp_put_opp_table(genpd->opp_table);
dev_pm_opp_of_remove_table(&genpd->dev); dev_pm_opp_of_remove_table(&genpd->dev);
}
goto unlock; goto unlock;
} }
...@@ -1965,6 +2049,13 @@ int of_genpd_add_provider_onecell(struct device_node *np, ...@@ -1965,6 +2049,13 @@ int of_genpd_add_provider_onecell(struct device_node *np,
i, ret); i, ret);
goto error; goto error;
} }
/*
* Save table for faster processing while setting
* performance state.
*/
genpd->opp_table = dev_pm_opp_get_opp_table_indexed(&genpd->dev, i);
WARN_ON(!genpd->opp_table);
} }
genpd->provider = &np->fwnode; genpd->provider = &np->fwnode;
...@@ -1989,8 +2080,10 @@ int of_genpd_add_provider_onecell(struct device_node *np, ...@@ -1989,8 +2080,10 @@ int of_genpd_add_provider_onecell(struct device_node *np,
genpd->provider = NULL; genpd->provider = NULL;
genpd->has_provider = false; genpd->has_provider = false;
if (genpd->set_performance_state) if (genpd->set_performance_state) {
dev_pm_opp_put_opp_table(genpd->opp_table);
dev_pm_opp_of_remove_table(&genpd->dev); dev_pm_opp_of_remove_table(&genpd->dev);
}
} }
mutex_unlock(&gpd_list_lock); mutex_unlock(&gpd_list_lock);
...@@ -2024,6 +2117,7 @@ void of_genpd_del_provider(struct device_node *np) ...@@ -2024,6 +2117,7 @@ void of_genpd_del_provider(struct device_node *np)
if (!gpd->set_performance_state) if (!gpd->set_performance_state)
continue; continue;
dev_pm_opp_put_opp_table(gpd->opp_table);
dev_pm_opp_of_remove_table(&gpd->dev); dev_pm_opp_of_remove_table(&gpd->dev);
} }
} }
...@@ -2338,7 +2432,7 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); ...@@ -2338,7 +2432,7 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
struct device *genpd_dev_pm_attach_by_id(struct device *dev, struct device *genpd_dev_pm_attach_by_id(struct device *dev,
unsigned int index) unsigned int index)
{ {
struct device *genpd_dev; struct device *virt_dev;
int num_domains; int num_domains;
int ret; int ret;
...@@ -2352,31 +2446,31 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev, ...@@ -2352,31 +2446,31 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev,
return NULL; return NULL;
/* Allocate and register device on the genpd bus. */ /* Allocate and register device on the genpd bus. */
genpd_dev = kzalloc(sizeof(*genpd_dev), GFP_KERNEL); virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
if (!genpd_dev) if (!virt_dev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dev_set_name(genpd_dev, "genpd:%u:%s", index, dev_name(dev)); dev_set_name(virt_dev, "genpd:%u:%s", index, dev_name(dev));
genpd_dev->bus = &genpd_bus_type; virt_dev->bus = &genpd_bus_type;
genpd_dev->release = genpd_release_dev; virt_dev->release = genpd_release_dev;
ret = device_register(genpd_dev); ret = device_register(virt_dev);
if (ret) { if (ret) {
kfree(genpd_dev); kfree(virt_dev);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
/* Try to attach the device to the PM domain at the specified index. */ /* Try to attach the device to the PM domain at the specified index. */
ret = __genpd_dev_pm_attach(genpd_dev, dev->of_node, index, false); ret = __genpd_dev_pm_attach(virt_dev, dev->of_node, index, false);
if (ret < 1) { if (ret < 1) {
device_unregister(genpd_dev); device_unregister(virt_dev);
return ret ? ERR_PTR(ret) : NULL; return ret ? ERR_PTR(ret) : NULL;
} }
pm_runtime_enable(genpd_dev); pm_runtime_enable(virt_dev);
genpd_queue_power_off_work(dev_to_genpd(genpd_dev)); genpd_queue_power_off_work(dev_to_genpd(virt_dev));
return genpd_dev; return virt_dev;
} }
EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id); EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id);
...@@ -2521,52 +2615,36 @@ int of_genpd_parse_idle_states(struct device_node *dn, ...@@ -2521,52 +2615,36 @@ int of_genpd_parse_idle_states(struct device_node *dn,
EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states);
/** /**
* of_genpd_opp_to_performance_state- Gets performance state of device's * pm_genpd_opp_to_performance_state - Gets performance state of the genpd from its OPP node.
* power domain corresponding to a DT node's "required-opps" property.
* *
* @dev: Device for which the performance-state needs to be found. * @genpd_dev: Genpd's device for which the performance-state needs to be found.
* @np: DT node where the "required-opps" property is present. This can be * @opp: struct dev_pm_opp of the OPP for which we need to find performance
* the device node itself (if it doesn't have an OPP table) or a node * state.
* within the OPP table of a device (if device has an OPP table).
* *
* Returns performance state corresponding to the "required-opps" property of * Returns performance state encoded in the OPP of the genpd. This calls
* a DT node. This calls platform specific genpd->opp_to_performance_state() * platform specific genpd->opp_to_performance_state() callback to translate
* callback to translate power domain OPP to performance state. * power domain OPP to performance state.
* *
* Returns performance state on success and 0 on failure. * Returns performance state on success and 0 on failure.
*/ */
unsigned int of_genpd_opp_to_performance_state(struct device *dev, unsigned int pm_genpd_opp_to_performance_state(struct device *genpd_dev,
struct device_node *np) struct dev_pm_opp *opp)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd = NULL;
struct dev_pm_opp *opp; int state;
int state = 0;
genpd = dev_to_genpd(dev); genpd = container_of(genpd_dev, struct generic_pm_domain, dev);
if (IS_ERR(genpd))
return 0;
if (unlikely(!genpd->set_performance_state)) if (unlikely(!genpd->opp_to_performance_state))
return 0; return 0;
genpd_lock(genpd); genpd_lock(genpd);
opp = of_dev_pm_opp_find_required_opp(&genpd->dev, np);
if (IS_ERR(opp)) {
dev_err(dev, "Failed to find required OPP: %ld\n",
PTR_ERR(opp));
goto unlock;
}
state = genpd->opp_to_performance_state(genpd, opp); state = genpd->opp_to_performance_state(genpd, opp);
dev_pm_opp_put(opp);
unlock:
genpd_unlock(genpd); genpd_unlock(genpd);
return state; return state;
} }
EXPORT_SYMBOL_GPL(of_genpd_opp_to_performance_state); EXPORT_SYMBOL_GPL(pm_genpd_opp_to_performance_state);
static int __init genpd_bus_init(void) static int __init genpd_bus_init(void)
{ {
...@@ -2671,7 +2749,7 @@ static int genpd_summary_one(struct seq_file *s, ...@@ -2671,7 +2749,7 @@ static int genpd_summary_one(struct seq_file *s,
return 0; return 0;
} }
static int genpd_summary_show(struct seq_file *s, void *data) static int summary_show(struct seq_file *s, void *data)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
int ret = 0; int ret = 0;
...@@ -2694,7 +2772,7 @@ static int genpd_summary_show(struct seq_file *s, void *data) ...@@ -2694,7 +2772,7 @@ static int genpd_summary_show(struct seq_file *s, void *data)
return ret; return ret;
} }
static int genpd_status_show(struct seq_file *s, void *data) static int status_show(struct seq_file *s, void *data)
{ {
static const char * const status_lookup[] = { static const char * const status_lookup[] = {
[GPD_STATE_ACTIVE] = "on", [GPD_STATE_ACTIVE] = "on",
...@@ -2721,7 +2799,7 @@ static int genpd_status_show(struct seq_file *s, void *data) ...@@ -2721,7 +2799,7 @@ static int genpd_status_show(struct seq_file *s, void *data)
return ret; return ret;
} }
static int genpd_sub_domains_show(struct seq_file *s, void *data) static int sub_domains_show(struct seq_file *s, void *data)
{ {
struct generic_pm_domain *genpd = s->private; struct generic_pm_domain *genpd = s->private;
struct gpd_link *link; struct gpd_link *link;
...@@ -2738,7 +2816,7 @@ static int genpd_sub_domains_show(struct seq_file *s, void *data) ...@@ -2738,7 +2816,7 @@ static int genpd_sub_domains_show(struct seq_file *s, void *data)
return ret; return ret;
} }
static int genpd_idle_states_show(struct seq_file *s, void *data) static int idle_states_show(struct seq_file *s, void *data)
{ {
struct generic_pm_domain *genpd = s->private; struct generic_pm_domain *genpd = s->private;
unsigned int i; unsigned int i;
...@@ -2767,7 +2845,7 @@ static int genpd_idle_states_show(struct seq_file *s, void *data) ...@@ -2767,7 +2845,7 @@ static int genpd_idle_states_show(struct seq_file *s, void *data)
return ret; return ret;
} }
static int genpd_active_time_show(struct seq_file *s, void *data) static int active_time_show(struct seq_file *s, void *data)
{ {
struct generic_pm_domain *genpd = s->private; struct generic_pm_domain *genpd = s->private;
ktime_t delta = 0; ktime_t delta = 0;
...@@ -2787,7 +2865,7 @@ static int genpd_active_time_show(struct seq_file *s, void *data) ...@@ -2787,7 +2865,7 @@ static int genpd_active_time_show(struct seq_file *s, void *data)
return ret; return ret;
} }
static int genpd_total_idle_time_show(struct seq_file *s, void *data) static int total_idle_time_show(struct seq_file *s, void *data)
{ {
struct generic_pm_domain *genpd = s->private; struct generic_pm_domain *genpd = s->private;
ktime_t delta = 0, total = 0; ktime_t delta = 0, total = 0;
...@@ -2815,7 +2893,7 @@ static int genpd_total_idle_time_show(struct seq_file *s, void *data) ...@@ -2815,7 +2893,7 @@ static int genpd_total_idle_time_show(struct seq_file *s, void *data)
} }
static int genpd_devices_show(struct seq_file *s, void *data) static int devices_show(struct seq_file *s, void *data)
{ {
struct generic_pm_domain *genpd = s->private; struct generic_pm_domain *genpd = s->private;
struct pm_domain_data *pm_data; struct pm_domain_data *pm_data;
...@@ -2841,7 +2919,7 @@ static int genpd_devices_show(struct seq_file *s, void *data) ...@@ -2841,7 +2919,7 @@ static int genpd_devices_show(struct seq_file *s, void *data)
return ret; return ret;
} }
static int genpd_perf_state_show(struct seq_file *s, void *data) static int perf_state_show(struct seq_file *s, void *data)
{ {
struct generic_pm_domain *genpd = s->private; struct generic_pm_domain *genpd = s->private;
...@@ -2854,37 +2932,14 @@ static int genpd_perf_state_show(struct seq_file *s, void *data) ...@@ -2854,37 +2932,14 @@ static int genpd_perf_state_show(struct seq_file *s, void *data)
return 0; return 0;
} }
#define define_genpd_open_function(name) \ DEFINE_SHOW_ATTRIBUTE(summary);
static int genpd_##name##_open(struct inode *inode, struct file *file) \ DEFINE_SHOW_ATTRIBUTE(status);
{ \ DEFINE_SHOW_ATTRIBUTE(sub_domains);
return single_open(file, genpd_##name##_show, inode->i_private); \ DEFINE_SHOW_ATTRIBUTE(idle_states);
} DEFINE_SHOW_ATTRIBUTE(active_time);
DEFINE_SHOW_ATTRIBUTE(total_idle_time);
define_genpd_open_function(summary); DEFINE_SHOW_ATTRIBUTE(devices);
define_genpd_open_function(status); DEFINE_SHOW_ATTRIBUTE(perf_state);
define_genpd_open_function(sub_domains);
define_genpd_open_function(idle_states);
define_genpd_open_function(active_time);
define_genpd_open_function(total_idle_time);
define_genpd_open_function(devices);
define_genpd_open_function(perf_state);
#define define_genpd_debugfs_fops(name) \
static const struct file_operations genpd_##name##_fops = { \
.open = genpd_##name##_open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}
define_genpd_debugfs_fops(summary);
define_genpd_debugfs_fops(status);
define_genpd_debugfs_fops(sub_domains);
define_genpd_debugfs_fops(idle_states);
define_genpd_debugfs_fops(active_time);
define_genpd_debugfs_fops(total_idle_time);
define_genpd_debugfs_fops(devices);
define_genpd_debugfs_fops(perf_state);
static int __init genpd_debug_init(void) static int __init genpd_debug_init(void)
{ {
...@@ -2897,7 +2952,7 @@ static int __init genpd_debug_init(void) ...@@ -2897,7 +2952,7 @@ static int __init genpd_debug_init(void)
return -ENOMEM; return -ENOMEM;
d = debugfs_create_file("pm_genpd_summary", S_IRUGO, d = debugfs_create_file("pm_genpd_summary", S_IRUGO,
genpd_debugfs_dir, NULL, &genpd_summary_fops); genpd_debugfs_dir, NULL, &summary_fops);
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
...@@ -2907,20 +2962,20 @@ static int __init genpd_debug_init(void) ...@@ -2907,20 +2962,20 @@ static int __init genpd_debug_init(void)
return -ENOMEM; return -ENOMEM;
debugfs_create_file("current_state", 0444, debugfs_create_file("current_state", 0444,
d, genpd, &genpd_status_fops); d, genpd, &status_fops);
debugfs_create_file("sub_domains", 0444, debugfs_create_file("sub_domains", 0444,
d, genpd, &genpd_sub_domains_fops); d, genpd, &sub_domains_fops);
debugfs_create_file("idle_states", 0444, debugfs_create_file("idle_states", 0444,
d, genpd, &genpd_idle_states_fops); d, genpd, &idle_states_fops);
debugfs_create_file("active_time", 0444, debugfs_create_file("active_time", 0444,
d, genpd, &genpd_active_time_fops); d, genpd, &active_time_fops);
debugfs_create_file("total_idle_time", 0444, debugfs_create_file("total_idle_time", 0444,
d, genpd, &genpd_total_idle_time_fops); d, genpd, &total_idle_time_fops);
debugfs_create_file("devices", 0444, debugfs_create_file("devices", 0444,
d, genpd, &genpd_devices_fops); d, genpd, &devices_fops);
if (genpd->set_performance_state) if (genpd->set_performance_state)
debugfs_create_file("perf_state", 0444, debugfs_create_file("perf_state", 0444,
d, genpd, &genpd_perf_state_fops); d, genpd, &perf_state_fops);
} }
return 0; return 0;
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
*/ */
#include <linux/sched/mm.h> #include <linux/sched/mm.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h> #include <linux/pm_wakeirq.h>
...@@ -93,7 +95,7 @@ static void __update_runtime_status(struct device *dev, enum rpm_status status) ...@@ -93,7 +95,7 @@ static void __update_runtime_status(struct device *dev, enum rpm_status status)
static void pm_runtime_deactivate_timer(struct device *dev) static void pm_runtime_deactivate_timer(struct device *dev)
{ {
if (dev->power.timer_expires > 0) { if (dev->power.timer_expires > 0) {
del_timer(&dev->power.suspend_timer); hrtimer_cancel(&dev->power.suspend_timer);
dev->power.timer_expires = 0; dev->power.timer_expires = 0;
} }
} }
...@@ -124,12 +126,11 @@ static void pm_runtime_cancel_pending(struct device *dev) ...@@ -124,12 +126,11 @@ static void pm_runtime_cancel_pending(struct device *dev)
* This function may be called either with or without dev->power.lock held. * This function may be called either with or without dev->power.lock held.
* Either way it can be racy, since power.last_busy may be updated at any time. * Either way it can be racy, since power.last_busy may be updated at any time.
*/ */
unsigned long pm_runtime_autosuspend_expiration(struct device *dev) u64 pm_runtime_autosuspend_expiration(struct device *dev)
{ {
int autosuspend_delay; int autosuspend_delay;
long elapsed; u64 last_busy, expires = 0;
unsigned long last_busy; u64 now = ktime_to_ns(ktime_get());
unsigned long expires = 0;
if (!dev->power.use_autosuspend) if (!dev->power.use_autosuspend)
goto out; goto out;
...@@ -139,19 +140,9 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev) ...@@ -139,19 +140,9 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev)
goto out; goto out;
last_busy = READ_ONCE(dev->power.last_busy); last_busy = READ_ONCE(dev->power.last_busy);
elapsed = jiffies - last_busy;
if (elapsed < 0)
goto out; /* jiffies has wrapped around. */
/* expires = last_busy + autosuspend_delay * NSEC_PER_MSEC;
* If the autosuspend_delay is >= 1 second, align the timer by rounding if (expires <= now)
* up to the nearest second.
*/
expires = last_busy + msecs_to_jiffies(autosuspend_delay);
if (autosuspend_delay >= 1000)
expires = round_jiffies(expires);
expires += !expires;
if (elapsed >= expires - last_busy)
expires = 0; /* Already expired. */ expires = 0; /* Already expired. */
out: out:
...@@ -515,7 +506,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) ...@@ -515,7 +506,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
/* If the autosuspend_delay time hasn't expired yet, reschedule. */ /* If the autosuspend_delay time hasn't expired yet, reschedule. */
if ((rpmflags & RPM_AUTO) if ((rpmflags & RPM_AUTO)
&& dev->power.runtime_status != RPM_SUSPENDING) { && dev->power.runtime_status != RPM_SUSPENDING) {
unsigned long expires = pm_runtime_autosuspend_expiration(dev); u64 expires = pm_runtime_autosuspend_expiration(dev);
if (expires != 0) { if (expires != 0) {
/* Pending requests need to be canceled. */ /* Pending requests need to be canceled. */
...@@ -528,10 +519,20 @@ static int rpm_suspend(struct device *dev, int rpmflags) ...@@ -528,10 +519,20 @@ static int rpm_suspend(struct device *dev, int rpmflags)
* expire; pm_suspend_timer_fn() will take care of the * expire; pm_suspend_timer_fn() will take care of the
* rest. * rest.
*/ */
if (!(dev->power.timer_expires && time_before_eq( if (!(dev->power.timer_expires &&
dev->power.timer_expires, expires))) { dev->power.timer_expires <= expires)) {
/*
* We add a slack of 25% to gather wakeups
* without sacrificing the granularity.
*/
u64 slack = READ_ONCE(dev->power.autosuspend_delay) *
(NSEC_PER_MSEC >> 2);
dev->power.timer_expires = expires; dev->power.timer_expires = expires;
mod_timer(&dev->power.suspend_timer, expires); hrtimer_start_range_ns(&dev->power.suspend_timer,
ns_to_ktime(expires),
slack,
HRTIMER_MODE_ABS);
} }
dev->power.timer_autosuspends = 1; dev->power.timer_autosuspends = 1;
goto out; goto out;
...@@ -895,23 +896,25 @@ static void pm_runtime_work(struct work_struct *work) ...@@ -895,23 +896,25 @@ static void pm_runtime_work(struct work_struct *work)
* *
* Check if the time is right and queue a suspend request. * Check if the time is right and queue a suspend request.
*/ */
static void pm_suspend_timer_fn(struct timer_list *t) static enum hrtimer_restart pm_suspend_timer_fn(struct hrtimer *timer)
{ {
struct device *dev = from_timer(dev, t, power.suspend_timer); struct device *dev = container_of(timer, struct device, power.suspend_timer);
unsigned long flags; unsigned long flags;
unsigned long expires; u64 expires;
spin_lock_irqsave(&dev->power.lock, flags); spin_lock_irqsave(&dev->power.lock, flags);
expires = dev->power.timer_expires; expires = dev->power.timer_expires;
/* If 'expire' is after 'jiffies' we've been called too early. */ /* If 'expire' is after 'jiffies' we've been called too early. */
if (expires > 0 && !time_after(expires, jiffies)) { if (expires > 0 && expires < ktime_to_ns(ktime_get())) {
dev->power.timer_expires = 0; dev->power.timer_expires = 0;
rpm_suspend(dev, dev->power.timer_autosuspends ? rpm_suspend(dev, dev->power.timer_autosuspends ?
(RPM_ASYNC | RPM_AUTO) : RPM_ASYNC); (RPM_ASYNC | RPM_AUTO) : RPM_ASYNC);
} }
spin_unlock_irqrestore(&dev->power.lock, flags); spin_unlock_irqrestore(&dev->power.lock, flags);
return HRTIMER_NORESTART;
} }
/** /**
...@@ -922,6 +925,7 @@ static void pm_suspend_timer_fn(struct timer_list *t) ...@@ -922,6 +925,7 @@ static void pm_suspend_timer_fn(struct timer_list *t)
int pm_schedule_suspend(struct device *dev, unsigned int delay) int pm_schedule_suspend(struct device *dev, unsigned int delay)
{ {
unsigned long flags; unsigned long flags;
ktime_t expires;
int retval; int retval;
spin_lock_irqsave(&dev->power.lock, flags); spin_lock_irqsave(&dev->power.lock, flags);
...@@ -938,10 +942,10 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay) ...@@ -938,10 +942,10 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay)
/* Other scheduled or pending requests need to be canceled. */ /* Other scheduled or pending requests need to be canceled. */
pm_runtime_cancel_pending(dev); pm_runtime_cancel_pending(dev);
dev->power.timer_expires = jiffies + msecs_to_jiffies(delay); expires = ktime_add(ktime_get(), ms_to_ktime(delay));
dev->power.timer_expires += !dev->power.timer_expires; dev->power.timer_expires = ktime_to_ns(expires);
dev->power.timer_autosuspends = 0; dev->power.timer_autosuspends = 0;
mod_timer(&dev->power.suspend_timer, dev->power.timer_expires); hrtimer_start(&dev->power.suspend_timer, expires, HRTIMER_MODE_ABS);
out: out:
spin_unlock_irqrestore(&dev->power.lock, flags); spin_unlock_irqrestore(&dev->power.lock, flags);
...@@ -1491,7 +1495,8 @@ void pm_runtime_init(struct device *dev) ...@@ -1491,7 +1495,8 @@ void pm_runtime_init(struct device *dev)
INIT_WORK(&dev->power.work, pm_runtime_work); INIT_WORK(&dev->power.work, pm_runtime_work);
dev->power.timer_expires = 0; dev->power.timer_expires = 0;
timer_setup(&dev->power.suspend_timer, pm_suspend_timer_fn, 0); hrtimer_init(&dev->power.suspend_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
dev->power.suspend_timer.function = pm_suspend_timer_fn;
init_waitqueue_head(&dev->power.wait_queue); init_waitqueue_head(&dev->power.wait_queue);
} }
......
...@@ -114,6 +114,17 @@ config ARM_QCOM_CPUFREQ_KRYO ...@@ -114,6 +114,17 @@ config ARM_QCOM_CPUFREQ_KRYO
If in doubt, say N. If in doubt, say N.
config ARM_QCOM_CPUFREQ_HW
tristate "QCOM CPUFreq HW driver"
depends on ARCH_QCOM || COMPILE_TEST
help
Support for the CPUFreq HW driver.
Some QCOM chipsets have a HW engine to offload the steps
necessary for changing the frequency of the CPUs. Firmware loaded
in this engine exposes a programming interface to the OS.
The driver implements the cpufreq interface for this HW engine.
Say Y if you want to support CPUFreq HW.
config ARM_S3C_CPUFREQ config ARM_S3C_CPUFREQ
bool bool
help help
......
...@@ -61,6 +61,7 @@ obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o ...@@ -61,6 +61,7 @@ obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-kryo.o obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-kryo.o
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
......
...@@ -123,8 +123,6 @@ static void nforce2_write_pll(int pll) ...@@ -123,8 +123,6 @@ static void nforce2_write_pll(int pll)
/* Now write the value in all 64 registers */ /* Now write the value in all 64 registers */
for (temp = 0; temp <= 0x3f; temp++) for (temp = 0; temp <= 0x3f; temp++)
pci_write_config_dword(nforce2_dev, NFORCE2_PLLREG, pll); pci_write_config_dword(nforce2_dev, NFORCE2_PLLREG, pll);
return;
} }
/** /**
...@@ -438,4 +436,3 @@ static void __exit nforce2_exit(void) ...@@ -438,4 +436,3 @@ static void __exit nforce2_exit(void)
module_init(nforce2_init); module_init(nforce2_init);
module_exit(nforce2_exit); module_exit(nforce2_exit);
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/io.h> #include <asm/io.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/pal.h> #include <asm/pal.h>
...@@ -28,7 +27,6 @@ MODULE_AUTHOR("Venkatesh Pallipadi"); ...@@ -28,7 +27,6 @@ MODULE_AUTHOR("Venkatesh Pallipadi");
MODULE_DESCRIPTION("ACPI Processor P-States Driver"); MODULE_DESCRIPTION("ACPI Processor P-States Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
struct cpufreq_acpi_io { struct cpufreq_acpi_io {
struct acpi_processor_performance acpi_data; struct acpi_processor_performance acpi_data;
unsigned int resume; unsigned int resume;
...@@ -348,10 +346,7 @@ acpi_cpufreq_exit (void) ...@@ -348,10 +346,7 @@ acpi_cpufreq_exit (void)
pr_debug("acpi_cpufreq_exit\n"); pr_debug("acpi_cpufreq_exit\n");
cpufreq_unregister_driver(&acpi_cpufreq_driver); cpufreq_unregister_driver(&acpi_cpufreq_driver);
return;
} }
late_initcall(acpi_cpufreq_init); late_initcall(acpi_cpufreq_init);
module_exit(acpi_cpufreq_exit); module_exit(acpi_cpufreq_exit);
...@@ -177,22 +177,16 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) ...@@ -177,22 +177,16 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
/* scaling down? scale voltage after frequency */ /* scaling down? scale voltage after frequency */
if (new_freq < old_freq) { if (new_freq < old_freq) {
ret = regulator_set_voltage_tol(arm_reg, volt, 0); ret = regulator_set_voltage_tol(arm_reg, volt, 0);
if (ret) { if (ret)
dev_warn(cpu_dev, dev_warn(cpu_dev,
"failed to scale vddarm down: %d\n", ret); "failed to scale vddarm down: %d\n", ret);
ret = 0;
}
ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0);
if (ret) { if (ret)
dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret); dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret);
ret = 0;
}
if (!IS_ERR(pu_reg)) { if (!IS_ERR(pu_reg)) {
ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0); ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0);
if (ret) { if (ret)
dev_warn(cpu_dev, "failed to scale vddpu down: %d\n", ret); dev_warn(cpu_dev, "failed to scale vddpu down: %d\n", ret);
ret = 0;
}
} }
} }
...@@ -411,9 +405,10 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ...@@ -411,9 +405,10 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
if (of_machine_is_compatible("fsl,imx6ul") || if (of_machine_is_compatible("fsl,imx6ul") ||
of_machine_is_compatible("fsl,imx6ull")) { of_machine_is_compatible("fsl,imx6ull")) {
ret = imx6ul_opp_check_speed_grading(cpu_dev); ret = imx6ul_opp_check_speed_grading(cpu_dev);
if (ret == -EPROBE_DEFER)
return ret;
if (ret) { if (ret) {
if (ret == -EPROBE_DEFER)
return ret;
dev_err(cpu_dev, "failed to read ocotp: %d\n", dev_err(cpu_dev, "failed to read ocotp: %d\n",
ret); ret);
return ret; return ret;
......
...@@ -830,6 +830,28 @@ static void intel_pstate_hwp_set(unsigned int cpu) ...@@ -830,6 +830,28 @@ static void intel_pstate_hwp_set(unsigned int cpu)
wrmsrl_on_cpu(cpu, MSR_HWP_REQUEST, value); wrmsrl_on_cpu(cpu, MSR_HWP_REQUEST, value);
} }
static void intel_pstate_hwp_force_min_perf(int cpu)
{
u64 value;
int min_perf;
value = all_cpu_data[cpu]->hwp_req_cached;
value &= ~GENMASK_ULL(31, 0);
min_perf = HWP_LOWEST_PERF(all_cpu_data[cpu]->hwp_cap_cached);
/* Set hwp_max = hwp_min */
value |= HWP_MAX_PERF(min_perf);
value |= HWP_MIN_PERF(min_perf);
/* Set EPP/EPB to min */
if (static_cpu_has(X86_FEATURE_HWP_EPP))
value |= HWP_ENERGY_PERF_PREFERENCE(HWP_EPP_POWERSAVE);
else
intel_pstate_set_epb(cpu, HWP_EPP_BALANCE_POWERSAVE);
wrmsrl_on_cpu(cpu, MSR_HWP_REQUEST, value);
}
static int intel_pstate_hwp_save_state(struct cpufreq_policy *policy) static int intel_pstate_hwp_save_state(struct cpufreq_policy *policy)
{ {
struct cpudata *cpu_data = all_cpu_data[policy->cpu]; struct cpudata *cpu_data = all_cpu_data[policy->cpu];
...@@ -2084,10 +2106,12 @@ static void intel_pstate_stop_cpu(struct cpufreq_policy *policy) ...@@ -2084,10 +2106,12 @@ static void intel_pstate_stop_cpu(struct cpufreq_policy *policy)
pr_debug("CPU %d exiting\n", policy->cpu); pr_debug("CPU %d exiting\n", policy->cpu);
intel_pstate_clear_update_util_hook(policy->cpu); intel_pstate_clear_update_util_hook(policy->cpu);
if (hwp_active) if (hwp_active) {
intel_pstate_hwp_save_state(policy); intel_pstate_hwp_save_state(policy);
else intel_pstate_hwp_force_min_perf(policy->cpu);
} else {
intel_cpufreq_stop_cpu(policy); intel_cpufreq_stop_cpu(policy);
}
} }
static int intel_pstate_cpu_exit(struct cpufreq_policy *policy) static int intel_pstate_cpu_exit(struct cpufreq_policy *policy)
......
...@@ -411,6 +411,7 @@ static int __init g5_neo2_cpufreq_init(struct device_node *cpunode) ...@@ -411,6 +411,7 @@ static int __init g5_neo2_cpufreq_init(struct device_node *cpunode)
pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0"); pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0");
pfunc_vdnap0_complete = pfunc_vdnap0_complete =
pmf_find_function(root, "slewing-done"); pmf_find_function(root, "slewing-done");
of_node_put(root);
if (pfunc_set_vdnap0 == NULL || if (pfunc_set_vdnap0 == NULL ||
pfunc_vdnap0_complete == NULL) { pfunc_vdnap0_complete == NULL) {
pr_err("Can't find required platform function\n"); pr_err("Can't find required platform function\n");
......
...@@ -253,18 +253,18 @@ static int init_powernv_pstates(void) ...@@ -253,18 +253,18 @@ static int init_powernv_pstates(void)
if (of_property_read_u32(power_mgt, "ibm,pstate-min", &pstate_min)) { if (of_property_read_u32(power_mgt, "ibm,pstate-min", &pstate_min)) {
pr_warn("ibm,pstate-min node not found\n"); pr_warn("ibm,pstate-min node not found\n");
return -ENODEV; goto out;
} }
if (of_property_read_u32(power_mgt, "ibm,pstate-max", &pstate_max)) { if (of_property_read_u32(power_mgt, "ibm,pstate-max", &pstate_max)) {
pr_warn("ibm,pstate-max node not found\n"); pr_warn("ibm,pstate-max node not found\n");
return -ENODEV; goto out;
} }
if (of_property_read_u32(power_mgt, "ibm,pstate-nominal", if (of_property_read_u32(power_mgt, "ibm,pstate-nominal",
&pstate_nominal)) { &pstate_nominal)) {
pr_warn("ibm,pstate-nominal not found\n"); pr_warn("ibm,pstate-nominal not found\n");
return -ENODEV; goto out;
} }
if (of_property_read_u32(power_mgt, "ibm,pstate-ultra-turbo", if (of_property_read_u32(power_mgt, "ibm,pstate-ultra-turbo",
...@@ -293,14 +293,14 @@ static int init_powernv_pstates(void) ...@@ -293,14 +293,14 @@ static int init_powernv_pstates(void)
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) {
pr_warn("ibm,pstate-ids not found\n"); pr_warn("ibm,pstate-ids not found\n");
return -ENODEV; goto out;
} }
pstate_freqs = of_get_property(power_mgt, "ibm,pstate-frequencies-mhz", pstate_freqs = of_get_property(power_mgt, "ibm,pstate-frequencies-mhz",
&len_freqs); &len_freqs);
if (!pstate_freqs) { if (!pstate_freqs) {
pr_warn("ibm,pstate-frequencies-mhz not found\n"); pr_warn("ibm,pstate-frequencies-mhz not found\n");
return -ENODEV; goto out;
} }
if (len_ids != len_freqs) { if (len_ids != len_freqs) {
...@@ -311,7 +311,7 @@ static int init_powernv_pstates(void) ...@@ -311,7 +311,7 @@ static int init_powernv_pstates(void)
nr_pstates = min(len_ids, len_freqs) / sizeof(u32); nr_pstates = min(len_ids, len_freqs) / sizeof(u32);
if (!nr_pstates) { if (!nr_pstates) {
pr_warn("No PStates found\n"); pr_warn("No PStates found\n");
return -ENODEV; goto out;
} }
powernv_pstate_info.nr_pstates = nr_pstates; powernv_pstate_info.nr_pstates = nr_pstates;
...@@ -352,7 +352,12 @@ static int init_powernv_pstates(void) ...@@ -352,7 +352,12 @@ static int init_powernv_pstates(void)
/* End of list marker entry */ /* End of list marker entry */
powernv_freqs[i].frequency = CPUFREQ_TABLE_END; powernv_freqs[i].frequency = CPUFREQ_TABLE_END;
of_node_put(power_mgt);
return 0; return 0;
out:
of_node_put(power_mgt);
return -ENODEV;
} }
/* Returns the CPU frequency corresponding to the pstate_id. */ /* Returns the CPU frequency corresponding to the pstate_id. */
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#define LUT_MAX_ENTRIES 40U
#define LUT_SRC GENMASK(31, 30)
#define LUT_L_VAL GENMASK(7, 0)
#define LUT_CORE_COUNT GENMASK(18, 16)
#define LUT_ROW_SIZE 32
#define CLK_HW_DIV 2
/* Register offsets */
#define REG_ENABLE 0x0
#define REG_LUT_TABLE 0x110
#define REG_PERF_STATE 0x920
static unsigned long cpu_hw_rate, xo_rate;
static struct platform_device *global_pdev;
static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy,
unsigned int index)
{
void __iomem *perf_state_reg = policy->driver_data;
writel_relaxed(index, perf_state_reg);
return 0;
}
static unsigned int qcom_cpufreq_hw_get(unsigned int cpu)
{
void __iomem *perf_state_reg;
struct cpufreq_policy *policy;
unsigned int index;
policy = cpufreq_cpu_get_raw(cpu);
if (!policy)
return 0;
perf_state_reg = policy->driver_data;
index = readl_relaxed(perf_state_reg);
index = min(index, LUT_MAX_ENTRIES - 1);
return policy->freq_table[index].frequency;
}
static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
unsigned int target_freq)
{
void __iomem *perf_state_reg = policy->driver_data;
int index;
index = policy->cached_resolved_idx;
if (index < 0)
return 0;
writel_relaxed(index, perf_state_reg);
return policy->freq_table[index].frequency;
}
static int qcom_cpufreq_hw_read_lut(struct device *dev,
struct cpufreq_policy *policy,
void __iomem *base)
{
u32 data, src, lval, i, core_count, prev_cc = 0, prev_freq = 0, freq;
unsigned int max_cores = cpumask_weight(policy->cpus);
struct cpufreq_frequency_table *table;
table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
for (i = 0; i < LUT_MAX_ENTRIES; i++) {
data = readl_relaxed(base + REG_LUT_TABLE + i * LUT_ROW_SIZE);
src = FIELD_GET(LUT_SRC, data);
lval = FIELD_GET(LUT_L_VAL, data);
core_count = FIELD_GET(LUT_CORE_COUNT, data);
if (src)
freq = xo_rate * lval / 1000;
else
freq = cpu_hw_rate / 1000;
/* Ignore boosts in the middle of the table */
if (core_count != max_cores) {
table[i].frequency = CPUFREQ_ENTRY_INVALID;
} else {
table[i].frequency = freq;
dev_dbg(dev, "index=%d freq=%d, core_count %d\n", i,
freq, core_count);
}
/*
* Two of the same frequencies with the same core counts means
* end of table
*/
if (i > 0 && prev_freq == freq && prev_cc == core_count) {
struct cpufreq_frequency_table *prev = &table[i - 1];
/*
* Only treat the last frequency that might be a boost
* as the boost frequency
*/
if (prev_cc != max_cores) {
prev->frequency = prev_freq;
prev->flags = CPUFREQ_BOOST_FREQ;
}
break;
}
prev_cc = core_count;
prev_freq = freq;
}
table[i].frequency = CPUFREQ_TABLE_END;
policy->freq_table = table;
return 0;
}
static void qcom_get_related_cpus(int index, struct cpumask *m)
{
struct device_node *cpu_np;
struct of_phandle_args args;
int cpu, ret;
for_each_possible_cpu(cpu) {
cpu_np = of_cpu_device_node_get(cpu);
if (!cpu_np)
continue;
ret = of_parse_phandle_with_args(cpu_np, "qcom,freq-domain",
"#freq-domain-cells", 0,
&args);
of_node_put(cpu_np);
if (ret < 0)
continue;
if (index == args.args[0])
cpumask_set_cpu(cpu, m);
}
}
static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
{
struct device *dev = &global_pdev->dev;
struct of_phandle_args args;
struct device_node *cpu_np;
struct resource *res;
void __iomem *base;
int ret, index;
cpu_np = of_cpu_device_node_get(policy->cpu);
if (!cpu_np)
return -EINVAL;
ret = of_parse_phandle_with_args(cpu_np, "qcom,freq-domain",
"#freq-domain-cells", 0, &args);
of_node_put(cpu_np);
if (ret)
return ret;
index = args.args[0];
res = platform_get_resource(global_pdev, IORESOURCE_MEM, index);
if (!res)
return -ENODEV;
base = devm_ioremap(dev, res->start, resource_size(res));
if (!base)
return -ENOMEM;
/* HW should be in enabled state to proceed */
if (!(readl_relaxed(base + REG_ENABLE) & 0x1)) {
dev_err(dev, "Domain-%d cpufreq hardware not enabled\n", index);
ret = -ENODEV;
goto error;
}
qcom_get_related_cpus(index, policy->cpus);
if (!cpumask_weight(policy->cpus)) {
dev_err(dev, "Domain-%d failed to get related CPUs\n", index);
ret = -ENOENT;
goto error;
}
policy->driver_data = base + REG_PERF_STATE;
ret = qcom_cpufreq_hw_read_lut(dev, policy, base);
if (ret) {
dev_err(dev, "Domain-%d failed to read LUT\n", index);
goto error;
}
policy->fast_switch_possible = true;
return 0;
error:
devm_iounmap(dev, base);
return ret;
}
static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
{
void __iomem *base = policy->driver_data - REG_PERF_STATE;
kfree(policy->freq_table);
devm_iounmap(&global_pdev->dev, base);
return 0;
}
static struct freq_attr *qcom_cpufreq_hw_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
&cpufreq_freq_attr_scaling_boost_freqs,
NULL
};
static struct cpufreq_driver cpufreq_qcom_hw_driver = {
.flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
CPUFREQ_HAVE_GOVERNOR_PER_POLICY,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = qcom_cpufreq_hw_target_index,
.get = qcom_cpufreq_hw_get,
.init = qcom_cpufreq_hw_cpu_init,
.exit = qcom_cpufreq_hw_cpu_exit,
.fast_switch = qcom_cpufreq_hw_fast_switch,
.name = "qcom-cpufreq-hw",
.attr = qcom_cpufreq_hw_attr,
};
static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
{
struct clk *clk;
int ret;
clk = clk_get(&pdev->dev, "xo");
if (IS_ERR(clk))
return PTR_ERR(clk);
xo_rate = clk_get_rate(clk);
clk_put(clk);
clk = clk_get(&pdev->dev, "alternate");
if (IS_ERR(clk))
return PTR_ERR(clk);
cpu_hw_rate = clk_get_rate(clk) / CLK_HW_DIV;
clk_put(clk);
global_pdev = pdev;
ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver);
if (ret)
dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n");
else
dev_dbg(&pdev->dev, "QCOM CPUFreq HW driver initialized\n");
return ret;
}
static int qcom_cpufreq_hw_driver_remove(struct platform_device *pdev)
{
return cpufreq_unregister_driver(&cpufreq_qcom_hw_driver);
}
static const struct of_device_id qcom_cpufreq_hw_match[] = {
{ .compatible = "qcom,cpufreq-hw" },
{}
};
MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match);
static struct platform_driver qcom_cpufreq_hw_driver = {
.probe = qcom_cpufreq_hw_driver_probe,
.remove = qcom_cpufreq_hw_driver_remove,
.driver = {
.name = "qcom-cpufreq-hw",
.of_match_table = qcom_cpufreq_hw_match,
},
};
static int __init qcom_cpufreq_hw_init(void)
{
return platform_driver_register(&qcom_cpufreq_hw_driver);
}
subsys_initcall(qcom_cpufreq_hw_init);
static void __exit qcom_cpufreq_hw_exit(void)
{
platform_driver_unregister(&qcom_cpufreq_hw_driver);
}
module_exit(qcom_cpufreq_hw_exit);
MODULE_DESCRIPTION("QCOM CPUFREQ HW Driver");
MODULE_LICENSE("GPL v2");
...@@ -63,18 +63,7 @@ static int board_show(struct seq_file *seq, void *p) ...@@ -63,18 +63,7 @@ static int board_show(struct seq_file *seq, void *p)
return 0; return 0;
} }
static int fops_board_open(struct inode *inode, struct file *file) DEFINE_SHOW_ATTRIBUTE(board);
{
return single_open(file, board_show, NULL);
}
static const struct file_operations fops_board = {
.open = fops_board_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int info_show(struct seq_file *seq, void *p) static int info_show(struct seq_file *seq, void *p)
{ {
...@@ -105,18 +94,7 @@ static int info_show(struct seq_file *seq, void *p) ...@@ -105,18 +94,7 @@ static int info_show(struct seq_file *seq, void *p)
return 0; return 0;
} }
static int fops_info_open(struct inode *inode, struct file *file) DEFINE_SHOW_ATTRIBUTE(info);
{
return single_open(file, info_show, NULL);
}
static const struct file_operations fops_info = {
.open = fops_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int io_show(struct seq_file *seq, void *p) static int io_show(struct seq_file *seq, void *p)
{ {
...@@ -162,19 +140,7 @@ static int io_show(struct seq_file *seq, void *p) ...@@ -162,19 +140,7 @@ static int io_show(struct seq_file *seq, void *p)
return 0; return 0;
} }
static int fops_io_open(struct inode *inode, struct file *file) DEFINE_SHOW_ATTRIBUTE(io);
{
return single_open(file, io_show, NULL);
}
static const struct file_operations fops_io = {
.open = fops_io_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int __init s3c_freq_debugfs_init(void) static int __init s3c_freq_debugfs_init(void)
{ {
...@@ -185,13 +151,13 @@ static int __init s3c_freq_debugfs_init(void) ...@@ -185,13 +151,13 @@ static int __init s3c_freq_debugfs_init(void)
} }
dbgfs_file_io = debugfs_create_file("io-timing", S_IRUGO, dbgfs_root, dbgfs_file_io = debugfs_create_file("io-timing", S_IRUGO, dbgfs_root,
NULL, &fops_io); NULL, &io_fops);
dbgfs_file_info = debugfs_create_file("info", S_IRUGO, dbgfs_root, dbgfs_file_info = debugfs_create_file("info", S_IRUGO, dbgfs_root,
NULL, &fops_info); NULL, &info_fops);
dbgfs_file_board = debugfs_create_file("board", S_IRUGO, dbgfs_root, dbgfs_file_board = debugfs_create_file("board", S_IRUGO, dbgfs_root,
NULL, &fops_board); NULL, &board_fops);
return 0; return 0;
} }
......
...@@ -167,6 +167,7 @@ static int __init bl_idle_init(void) ...@@ -167,6 +167,7 @@ static int __init bl_idle_init(void)
{ {
int ret; int ret;
struct device_node *root = of_find_node_by_path("/"); struct device_node *root = of_find_node_by_path("/");
const struct of_device_id *match_id;
if (!root) if (!root)
return -ENODEV; return -ENODEV;
...@@ -174,7 +175,11 @@ static int __init bl_idle_init(void) ...@@ -174,7 +175,11 @@ static int __init bl_idle_init(void)
/* /*
* Initialize the driver just for a compliant set of machines * Initialize the driver just for a compliant set of machines
*/ */
if (!of_match_node(compatible_machine_match, root)) match_id = of_match_node(compatible_machine_match, root);
of_node_put(root);
if (!match_id)
return -ENODEV; return -ENODEV;
if (!mcpm_is_available()) if (!mcpm_is_available())
......
...@@ -202,7 +202,6 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, ...@@ -202,7 +202,6 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
struct cpuidle_state *target_state = &drv->states[index]; struct cpuidle_state *target_state = &drv->states[index];
bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP); bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
ktime_t time_start, time_end; ktime_t time_start, time_end;
s64 diff;
/* /*
* Tell the time framework to switch to a broadcast timer because our * Tell the time framework to switch to a broadcast timer because our
...@@ -248,6 +247,9 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, ...@@ -248,6 +247,9 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
local_irq_enable(); local_irq_enable();
if (entered_state >= 0) { if (entered_state >= 0) {
s64 diff, delay = drv->states[entered_state].exit_latency;
int i;
/* /*
* Update cpuidle counters * Update cpuidle counters
* This can be moved to within driver enter routine, * This can be moved to within driver enter routine,
...@@ -260,6 +262,33 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, ...@@ -260,6 +262,33 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
dev->last_residency = (int)diff; dev->last_residency = (int)diff;
dev->states_usage[entered_state].time += dev->last_residency; dev->states_usage[entered_state].time += dev->last_residency;
dev->states_usage[entered_state].usage++; dev->states_usage[entered_state].usage++;
if (diff < drv->states[entered_state].target_residency) {
for (i = entered_state - 1; i >= 0; i--) {
if (drv->states[i].disabled ||
dev->states_usage[i].disable)
continue;
/* Shallower states are enabled, so update. */
dev->states_usage[entered_state].above++;
break;
}
} else if (diff > delay) {
for (i = entered_state + 1; i < drv->state_count; i++) {
if (drv->states[i].disabled ||
dev->states_usage[i].disable)
continue;
/*
* Update if a deeper state would have been a
* better match for the observed idle duration.
*/
if (diff - delay >= drv->states[i].target_residency)
dev->states_usage[entered_state].below++;
break;
}
}
} else { } else {
dev->last_residency = 0; dev->last_residency = 0;
} }
...@@ -702,4 +731,5 @@ static int __init cpuidle_init(void) ...@@ -702,4 +731,5 @@ static int __init cpuidle_init(void)
} }
module_param(off, int, 0444); module_param(off, int, 0444);
module_param_string(governor, param_governor, CPUIDLE_NAME_LEN, 0444);
core_initcall(cpuidle_init); core_initcall(cpuidle_init);
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#define __DRIVER_CPUIDLE_H #define __DRIVER_CPUIDLE_H
/* For internal use only */ /* For internal use only */
extern char param_governor[];
extern struct cpuidle_governor *cpuidle_curr_governor; extern struct cpuidle_governor *cpuidle_curr_governor;
extern struct list_head cpuidle_governors; extern struct list_head cpuidle_governors;
extern struct list_head cpuidle_detected_devices; extern struct list_head cpuidle_detected_devices;
......
...@@ -11,10 +11,13 @@ ...@@ -11,10 +11,13 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/module.h>
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
#include "cpuidle.h" #include "cpuidle.h"
char param_governor[CPUIDLE_NAME_LEN];
LIST_HEAD(cpuidle_governors); LIST_HEAD(cpuidle_governors);
struct cpuidle_governor *cpuidle_curr_governor; struct cpuidle_governor *cpuidle_curr_governor;
...@@ -86,9 +89,11 @@ int cpuidle_register_governor(struct cpuidle_governor *gov) ...@@ -86,9 +89,11 @@ int cpuidle_register_governor(struct cpuidle_governor *gov)
mutex_lock(&cpuidle_lock); mutex_lock(&cpuidle_lock);
if (__cpuidle_find_governor(gov->name) == NULL) { if (__cpuidle_find_governor(gov->name) == NULL) {
ret = 0; ret = 0;
list_add_tail(&gov->governor_list, &cpuidle_governors);
if (!cpuidle_curr_governor || if (!cpuidle_curr_governor ||
cpuidle_curr_governor->rating < gov->rating) !strncasecmp(param_governor, gov->name, CPUIDLE_NAME_LEN) ||
(cpuidle_curr_governor->rating < gov->rating &&
strncasecmp(param_governor, cpuidle_curr_governor->name,
CPUIDLE_NAME_LEN)))
cpuidle_switch_governor(gov); cpuidle_switch_governor(gov);
} }
mutex_unlock(&cpuidle_lock); mutex_unlock(&cpuidle_lock);
......
...@@ -20,8 +20,17 @@ static int __cpuidle poll_idle(struct cpuidle_device *dev, ...@@ -20,8 +20,17 @@ static int __cpuidle poll_idle(struct cpuidle_device *dev,
local_irq_enable(); local_irq_enable();
if (!current_set_polling_and_test()) { if (!current_set_polling_and_test()) {
u64 limit = (u64)drv->states[1].target_residency * NSEC_PER_USEC;
unsigned int loop_count = 0; unsigned int loop_count = 0;
u64 limit = TICK_USEC;
int i;
for (i = 1; i < drv->state_count; i++) {
if (drv->states[i].disabled || dev->states_usage[i].disable)
continue;
limit = (u64)drv->states[i].target_residency * NSEC_PER_USEC;
break;
}
while (!need_resched()) { while (!need_resched()) {
cpu_relax(); cpu_relax();
......
...@@ -301,6 +301,8 @@ define_show_state_str_function(name) ...@@ -301,6 +301,8 @@ define_show_state_str_function(name)
define_show_state_str_function(desc) define_show_state_str_function(desc)
define_show_state_ull_function(disable) define_show_state_ull_function(disable)
define_store_state_ull_function(disable) define_store_state_ull_function(disable)
define_show_state_ull_function(above)
define_show_state_ull_function(below)
define_one_state_ro(name, show_state_name); define_one_state_ro(name, show_state_name);
define_one_state_ro(desc, show_state_desc); define_one_state_ro(desc, show_state_desc);
...@@ -310,6 +312,8 @@ define_one_state_ro(power, show_state_power_usage); ...@@ -310,6 +312,8 @@ define_one_state_ro(power, show_state_power_usage);
define_one_state_ro(usage, show_state_usage); define_one_state_ro(usage, show_state_usage);
define_one_state_ro(time, show_state_time); define_one_state_ro(time, show_state_time);
define_one_state_rw(disable, show_state_disable, store_state_disable); define_one_state_rw(disable, show_state_disable, store_state_disable);
define_one_state_ro(above, show_state_above);
define_one_state_ro(below, show_state_below);
static struct attribute *cpuidle_state_default_attrs[] = { static struct attribute *cpuidle_state_default_attrs[] = {
&attr_name.attr, &attr_name.attr,
...@@ -320,6 +324,8 @@ static struct attribute *cpuidle_state_default_attrs[] = { ...@@ -320,6 +324,8 @@ static struct attribute *cpuidle_state_default_attrs[] = {
&attr_usage.attr, &attr_usage.attr,
&attr_time.attr, &attr_time.attr,
&attr_disable.attr, &attr_disable.attr,
&attr_above.attr,
&attr_below.attr,
NULL NULL
}; };
......
...@@ -285,6 +285,44 @@ static int devfreq_notify_transition(struct devfreq *devfreq, ...@@ -285,6 +285,44 @@ static int devfreq_notify_transition(struct devfreq *devfreq,
return 0; return 0;
} }
static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
u32 flags)
{
struct devfreq_freqs freqs;
unsigned long cur_freq;
int err = 0;
if (devfreq->profile->get_cur_freq)
devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq);
else
cur_freq = devfreq->previous_freq;
freqs.old = cur_freq;
freqs.new = new_freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE);
err = devfreq->profile->target(devfreq->dev.parent, &new_freq, flags);
if (err) {
freqs.new = cur_freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
return err;
}
freqs.new = new_freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
if (devfreq_update_status(devfreq, new_freq))
dev_err(&devfreq->dev,
"Couldn't update frequency transition information.\n");
devfreq->previous_freq = new_freq;
if (devfreq->suspend_freq)
devfreq->resume_freq = cur_freq;
return err;
}
/* Load monitoring helper functions for governors use */ /* Load monitoring helper functions for governors use */
/** /**
...@@ -296,8 +334,7 @@ static int devfreq_notify_transition(struct devfreq *devfreq, ...@@ -296,8 +334,7 @@ static int devfreq_notify_transition(struct devfreq *devfreq,
*/ */
int update_devfreq(struct devfreq *devfreq) int update_devfreq(struct devfreq *devfreq)
{ {
struct devfreq_freqs freqs; unsigned long freq, min_freq, max_freq;
unsigned long freq, cur_freq, min_freq, max_freq;
int err = 0; int err = 0;
u32 flags = 0; u32 flags = 0;
...@@ -333,31 +370,8 @@ int update_devfreq(struct devfreq *devfreq) ...@@ -333,31 +370,8 @@ int update_devfreq(struct devfreq *devfreq)
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
} }
if (devfreq->profile->get_cur_freq) return devfreq_set_target(devfreq, freq, flags);
devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq);
else
cur_freq = devfreq->previous_freq;
freqs.old = cur_freq;
freqs.new = freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE);
err = devfreq->profile->target(devfreq->dev.parent, &freq, flags);
if (err) {
freqs.new = cur_freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
return err;
}
freqs.new = freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
if (devfreq_update_status(devfreq, freq))
dev_err(&devfreq->dev,
"Couldn't update frequency transition information.\n");
devfreq->previous_freq = freq;
return err;
} }
EXPORT_SYMBOL(update_devfreq); EXPORT_SYMBOL(update_devfreq);
...@@ -657,6 +671,9 @@ struct devfreq *devfreq_add_device(struct device *dev, ...@@ -657,6 +671,9 @@ struct devfreq *devfreq_add_device(struct device *dev,
} }
devfreq->max_freq = devfreq->scaling_max_freq; devfreq->max_freq = devfreq->scaling_max_freq;
devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev);
atomic_set(&devfreq->suspend_count, 0);
dev_set_name(&devfreq->dev, "devfreq%d", dev_set_name(&devfreq->dev, "devfreq%d",
atomic_inc_return(&devfreq_no)); atomic_inc_return(&devfreq_no));
err = device_register(&devfreq->dev); err = device_register(&devfreq->dev);
...@@ -857,14 +874,28 @@ EXPORT_SYMBOL(devm_devfreq_remove_device); ...@@ -857,14 +874,28 @@ EXPORT_SYMBOL(devm_devfreq_remove_device);
*/ */
int devfreq_suspend_device(struct devfreq *devfreq) int devfreq_suspend_device(struct devfreq *devfreq)
{ {
int ret;
if (!devfreq) if (!devfreq)
return -EINVAL; return -EINVAL;
if (!devfreq->governor) if (atomic_inc_return(&devfreq->suspend_count) > 1)
return 0; return 0;
return devfreq->governor->event_handler(devfreq, if (devfreq->governor) {
DEVFREQ_GOV_SUSPEND, NULL); ret = devfreq->governor->event_handler(devfreq,
DEVFREQ_GOV_SUSPEND, NULL);
if (ret)
return ret;
}
if (devfreq->suspend_freq) {
ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0);
if (ret)
return ret;
}
return 0;
} }
EXPORT_SYMBOL(devfreq_suspend_device); EXPORT_SYMBOL(devfreq_suspend_device);
...@@ -878,17 +909,75 @@ EXPORT_SYMBOL(devfreq_suspend_device); ...@@ -878,17 +909,75 @@ EXPORT_SYMBOL(devfreq_suspend_device);
*/ */
int devfreq_resume_device(struct devfreq *devfreq) int devfreq_resume_device(struct devfreq *devfreq)
{ {
int ret;
if (!devfreq) if (!devfreq)
return -EINVAL; return -EINVAL;
if (!devfreq->governor) if (atomic_dec_return(&devfreq->suspend_count) >= 1)
return 0; return 0;
return devfreq->governor->event_handler(devfreq, if (devfreq->resume_freq) {
DEVFREQ_GOV_RESUME, NULL); ret = devfreq_set_target(devfreq, devfreq->resume_freq, 0);
if (ret)
return ret;
}
if (devfreq->governor) {
ret = devfreq->governor->event_handler(devfreq,
DEVFREQ_GOV_RESUME, NULL);
if (ret)
return ret;
}
return 0;
} }
EXPORT_SYMBOL(devfreq_resume_device); EXPORT_SYMBOL(devfreq_resume_device);
/**
* devfreq_suspend() - Suspend devfreq governors and devices
*
* Called during system wide Suspend/Hibernate cycles for suspending governors
* and devices preserving the state for resume. On some platforms the devfreq
* device must have precise state (frequency) after resume in order to provide
* fully operating setup.
*/
void devfreq_suspend(void)
{
struct devfreq *devfreq;
int ret;
mutex_lock(&devfreq_list_lock);
list_for_each_entry(devfreq, &devfreq_list, node) {
ret = devfreq_suspend_device(devfreq);
if (ret)
dev_err(&devfreq->dev,
"failed to suspend devfreq device\n");
}
mutex_unlock(&devfreq_list_lock);
}
/**
* devfreq_resume() - Resume devfreq governors and devices
*
* Called during system wide Suspend/Hibernate cycle for resuming governors and
* devices that are suspended with devfreq_suspend().
*/
void devfreq_resume(void)
{
struct devfreq *devfreq;
int ret;
mutex_lock(&devfreq_list_lock);
list_for_each_entry(devfreq, &devfreq_list, node) {
ret = devfreq_resume_device(devfreq);
if (ret)
dev_warn(&devfreq->dev,
"failed to resume devfreq device\n");
}
mutex_unlock(&devfreq_list_lock);
}
/** /**
* devfreq_add_governor() - Add devfreq governor * devfreq_add_governor() - Add devfreq governor
* @governor: the devfreq governor to be added * @governor: the devfreq governor to be added
......
...@@ -196,12 +196,12 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) ...@@ -196,12 +196,12 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
if (IS_ERR(opp_table)) if (IS_ERR(opp_table))
return 0; return 0;
count = opp_table->regulator_count;
/* Regulator may not be required for the device */ /* Regulator may not be required for the device */
if (!count) if (!opp_table->regulators)
goto put_opp_table; goto put_opp_table;
count = opp_table->regulator_count;
uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL); uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL);
if (!uV) if (!uV)
goto put_opp_table; goto put_opp_table;
...@@ -548,44 +548,6 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk, ...@@ -548,44 +548,6 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk,
return ret; return ret;
} }
static inline int
_generic_set_opp_domain(struct device *dev, struct clk *clk,
unsigned long old_freq, unsigned long freq,
unsigned int old_pstate, unsigned int new_pstate)
{
int ret;
/* Scaling up? Scale domain performance state before frequency */
if (freq > old_freq) {
ret = dev_pm_genpd_set_performance_state(dev, new_pstate);
if (ret)
return ret;
}
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
if (ret)
goto restore_domain_state;
/* Scaling down? Scale domain performance state after frequency */
if (freq < old_freq) {
ret = dev_pm_genpd_set_performance_state(dev, new_pstate);
if (ret)
goto restore_freq;
}
return 0;
restore_freq:
if (_generic_set_opp_clk_only(dev, clk, freq, old_freq))
dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
__func__, old_freq);
restore_domain_state:
if (freq > old_freq)
dev_pm_genpd_set_performance_state(dev, old_pstate);
return ret;
}
static int _generic_set_opp_regulator(const struct opp_table *opp_table, static int _generic_set_opp_regulator(const struct opp_table *opp_table,
struct device *dev, struct device *dev,
unsigned long old_freq, unsigned long old_freq,
...@@ -635,6 +597,84 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table, ...@@ -635,6 +597,84 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table,
return ret; return ret;
} }
static int _set_opp_custom(const struct opp_table *opp_table,
struct device *dev, unsigned long old_freq,
unsigned long freq,
struct dev_pm_opp_supply *old_supply,
struct dev_pm_opp_supply *new_supply)
{
struct dev_pm_set_opp_data *data;
int size;
data = opp_table->set_opp_data;
data->regulators = opp_table->regulators;
data->regulator_count = opp_table->regulator_count;
data->clk = opp_table->clk;
data->dev = dev;
data->old_opp.rate = old_freq;
size = sizeof(*old_supply) * opp_table->regulator_count;
if (IS_ERR(old_supply))
memset(data->old_opp.supplies, 0, size);
else
memcpy(data->old_opp.supplies, old_supply, size);
data->new_opp.rate = freq;
memcpy(data->new_opp.supplies, new_supply, size);
return opp_table->set_opp(data);
}
/* This is only called for PM domain for now */
static int _set_required_opps(struct device *dev,
struct opp_table *opp_table,
struct dev_pm_opp *opp)
{
struct opp_table **required_opp_tables = opp_table->required_opp_tables;
struct device **genpd_virt_devs = opp_table->genpd_virt_devs;
unsigned int pstate;
int i, ret = 0;
if (!required_opp_tables)
return 0;
/* Single genpd case */
if (!genpd_virt_devs) {
pstate = opp->required_opps[0]->pstate;
ret = dev_pm_genpd_set_performance_state(dev, pstate);
if (ret) {
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
dev_name(dev), pstate, ret);
}
return ret;
}
/* Multiple genpd case */
/*
* Acquire genpd_virt_dev_lock to make sure we don't use a genpd_dev
* after it is freed from another thread.
*/
mutex_lock(&opp_table->genpd_virt_dev_lock);
for (i = 0; i < opp_table->required_opp_count; i++) {
pstate = opp->required_opps[i]->pstate;
if (!genpd_virt_devs[i])
continue;
ret = dev_pm_genpd_set_performance_state(genpd_virt_devs[i], pstate);
if (ret) {
dev_err(dev, "Failed to set performance rate of %s: %d (%d)\n",
dev_name(genpd_virt_devs[i]), pstate, ret);
break;
}
}
mutex_unlock(&opp_table->genpd_virt_dev_lock);
return ret;
}
/** /**
* dev_pm_opp_set_rate() - Configure new OPP based on frequency * dev_pm_opp_set_rate() - Configure new OPP based on frequency
* @dev: device for which we do this operation * @dev: device for which we do this operation
...@@ -649,7 +689,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) ...@@ -649,7 +689,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
unsigned long freq, old_freq; unsigned long freq, old_freq;
struct dev_pm_opp *old_opp, *opp; struct dev_pm_opp *old_opp, *opp;
struct clk *clk; struct clk *clk;
int ret, size; int ret;
if (unlikely(!target_freq)) { if (unlikely(!target_freq)) {
dev_err(dev, "%s: Invalid target frequency %lu\n", __func__, dev_err(dev, "%s: Invalid target frequency %lu\n", __func__,
...@@ -702,44 +742,34 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) ...@@ -702,44 +742,34 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__, dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__,
old_freq, freq); old_freq, freq);
/* Only frequency scaling */ /* Scaling up? Configure required OPPs before frequency */
if (!opp_table->regulators) { if (freq > old_freq) {
/* ret = _set_required_opps(dev, opp_table, opp);
* We don't support devices with both regulator and if (ret)
* domain performance-state for now. goto put_opp;
*/ }
if (opp_table->genpd_performance_state)
ret = _generic_set_opp_domain(dev, clk, old_freq, freq, if (opp_table->set_opp) {
IS_ERR(old_opp) ? 0 : old_opp->pstate, ret = _set_opp_custom(opp_table, dev, old_freq, freq,
opp->pstate); IS_ERR(old_opp) ? NULL : old_opp->supplies,
else opp->supplies);
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); } else if (opp_table->regulators) {
} else if (!opp_table->set_opp) {
ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq, ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq,
IS_ERR(old_opp) ? NULL : old_opp->supplies, IS_ERR(old_opp) ? NULL : old_opp->supplies,
opp->supplies); opp->supplies);
} else { } else {
struct dev_pm_set_opp_data *data; /* Only frequency scaling */
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
data = opp_table->set_opp_data; }
data->regulators = opp_table->regulators;
data->regulator_count = opp_table->regulator_count;
data->clk = clk;
data->dev = dev;
data->old_opp.rate = old_freq;
size = sizeof(*opp->supplies) * opp_table->regulator_count;
if (IS_ERR(old_opp))
memset(data->old_opp.supplies, 0, size);
else
memcpy(data->old_opp.supplies, old_opp->supplies, size);
data->new_opp.rate = freq;
memcpy(data->new_opp.supplies, opp->supplies, size);
ret = opp_table->set_opp(data); /* Scaling down? Configure required OPPs after frequency */
if (!ret && freq < old_freq) {
ret = _set_required_opps(dev, opp_table, opp);
if (ret)
dev_err(dev, "Failed to set required opps: %d\n", ret);
} }
put_opp:
dev_pm_opp_put(opp); dev_pm_opp_put(opp);
put_old_opp: put_old_opp:
if (!IS_ERR(old_opp)) if (!IS_ERR(old_opp))
...@@ -810,8 +840,12 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index) ...@@ -810,8 +840,12 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
return NULL; return NULL;
mutex_init(&opp_table->lock); mutex_init(&opp_table->lock);
mutex_init(&opp_table->genpd_virt_dev_lock);
INIT_LIST_HEAD(&opp_table->dev_list); INIT_LIST_HEAD(&opp_table->dev_list);
/* Mark regulator count uninitialized */
opp_table->regulator_count = -1;
opp_dev = _add_opp_dev(dev, opp_table); opp_dev = _add_opp_dev(dev, opp_table);
if (!opp_dev) { if (!opp_dev) {
kfree(opp_table); kfree(opp_table);
...@@ -888,6 +922,8 @@ static void _opp_table_kref_release(struct kref *kref) ...@@ -888,6 +922,8 @@ static void _opp_table_kref_release(struct kref *kref)
struct opp_table *opp_table = container_of(kref, struct opp_table, kref); struct opp_table *opp_table = container_of(kref, struct opp_table, kref);
struct opp_device *opp_dev, *temp; struct opp_device *opp_dev, *temp;
_of_clear_opp_table(opp_table);
/* Release clk */ /* Release clk */
if (!IS_ERR(opp_table->clk)) if (!IS_ERR(opp_table->clk))
clk_put(opp_table->clk); clk_put(opp_table->clk);
...@@ -905,6 +941,7 @@ static void _opp_table_kref_release(struct kref *kref) ...@@ -905,6 +941,7 @@ static void _opp_table_kref_release(struct kref *kref)
_remove_opp_dev(opp_dev, opp_table); _remove_opp_dev(opp_dev, opp_table);
} }
mutex_destroy(&opp_table->genpd_virt_dev_lock);
mutex_destroy(&opp_table->lock); mutex_destroy(&opp_table->lock);
list_del(&opp_table->node); list_del(&opp_table->node);
kfree(opp_table); kfree(opp_table);
...@@ -961,6 +998,7 @@ static void _opp_kref_release(struct kref *kref) ...@@ -961,6 +998,7 @@ static void _opp_kref_release(struct kref *kref)
* frequency/voltage list. * frequency/voltage list.
*/ */
blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_REMOVE, opp); blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_REMOVE, opp);
_of_opp_free_required_opps(opp_table, opp);
opp_debug_remove_one(opp); opp_debug_remove_one(opp);
list_del(&opp->node); list_del(&opp->node);
kfree(opp); kfree(opp);
...@@ -1028,7 +1066,7 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *table) ...@@ -1028,7 +1066,7 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *table)
int count, supply_size; int count, supply_size;
/* Allocate space for at least one supply */ /* Allocate space for at least one supply */
count = table->regulator_count ? table->regulator_count : 1; count = table->regulator_count > 0 ? table->regulator_count : 1;
supply_size = sizeof(*opp->supplies) * count; supply_size = sizeof(*opp->supplies) * count;
/* allocate new OPP node and supplies structures */ /* allocate new OPP node and supplies structures */
...@@ -1049,6 +1087,9 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp, ...@@ -1049,6 +1087,9 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
struct regulator *reg; struct regulator *reg;
int i; int i;
if (!opp_table->regulators)
return true;
for (i = 0; i < opp_table->regulator_count; i++) { for (i = 0; i < opp_table->regulator_count; i++) {
reg = opp_table->regulators[i]; reg = opp_table->regulators[i];
...@@ -1333,7 +1374,7 @@ static int _allocate_set_opp_data(struct opp_table *opp_table) ...@@ -1333,7 +1374,7 @@ static int _allocate_set_opp_data(struct opp_table *opp_table)
struct dev_pm_set_opp_data *data; struct dev_pm_set_opp_data *data;
int len, count = opp_table->regulator_count; int len, count = opp_table->regulator_count;
if (WARN_ON(!count)) if (WARN_ON(!opp_table->regulators))
return -EINVAL; return -EINVAL;
/* space for set_opp_data */ /* space for set_opp_data */
...@@ -1430,7 +1471,7 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, ...@@ -1430,7 +1471,7 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev,
kfree(opp_table->regulators); kfree(opp_table->regulators);
opp_table->regulators = NULL; opp_table->regulators = NULL;
opp_table->regulator_count = 0; opp_table->regulator_count = -1;
err: err:
dev_pm_opp_put_opp_table(opp_table); dev_pm_opp_put_opp_table(opp_table);
...@@ -1459,7 +1500,7 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table) ...@@ -1459,7 +1500,7 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table)
kfree(opp_table->regulators); kfree(opp_table->regulators);
opp_table->regulators = NULL; opp_table->regulators = NULL;
opp_table->regulator_count = 0; opp_table->regulator_count = -1;
put_opp_table: put_opp_table:
dev_pm_opp_put_opp_table(opp_table); dev_pm_opp_put_opp_table(opp_table);
...@@ -1586,6 +1627,155 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) ...@@ -1586,6 +1627,155 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper); EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);
/**
* dev_pm_opp_set_genpd_virt_dev - Set virtual genpd device for an index
* @dev: Consumer device for which the genpd device is getting set.
* @virt_dev: virtual genpd device.
* @index: index.
*
* Multiple generic power domains for a device are supported with the help of
* virtual genpd devices, which are created for each consumer device - genpd
* pair. These are the device structures which are attached to the power domain
* and are required by the OPP core to set the performance state of the genpd.
*
* This helper will normally be called by the consumer driver of the device
* "dev", as only that has details of the genpd devices.
*
* This helper needs to be called once for each of those virtual devices, but
* only if multiple domains are available for a device. Otherwise the original
* device structure will be used instead by the OPP core.
*/
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev,
struct device *virt_dev,
int index)
{
struct opp_table *opp_table;
opp_table = dev_pm_opp_get_opp_table(dev);
if (!opp_table)
return ERR_PTR(-ENOMEM);
mutex_lock(&opp_table->genpd_virt_dev_lock);
if (unlikely(!opp_table->genpd_virt_devs ||
index >= opp_table->required_opp_count ||
opp_table->genpd_virt_devs[index])) {
dev_err(dev, "Invalid request to set required device\n");
dev_pm_opp_put_opp_table(opp_table);
mutex_unlock(&opp_table->genpd_virt_dev_lock);
return ERR_PTR(-EINVAL);
}
opp_table->genpd_virt_devs[index] = virt_dev;
mutex_unlock(&opp_table->genpd_virt_dev_lock);
return opp_table;
}
/**
* dev_pm_opp_put_genpd_virt_dev() - Releases resources blocked for genpd device.
* @opp_table: OPP table returned by dev_pm_opp_set_genpd_virt_dev().
* @virt_dev: virtual genpd device.
*
* This releases the resource previously acquired with a call to
* dev_pm_opp_set_genpd_virt_dev(). The consumer driver shall call this helper
* if it doesn't want OPP core to update performance state of a power domain
* anymore.
*/
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
struct device *virt_dev)
{
int i;
/*
* Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting
* used in parallel.
*/
mutex_lock(&opp_table->genpd_virt_dev_lock);
for (i = 0; i < opp_table->required_opp_count; i++) {
if (opp_table->genpd_virt_devs[i] != virt_dev)
continue;
opp_table->genpd_virt_devs[i] = NULL;
dev_pm_opp_put_opp_table(opp_table);
/* Drop the vote */
dev_pm_genpd_set_performance_state(virt_dev, 0);
break;
}
mutex_unlock(&opp_table->genpd_virt_dev_lock);
if (unlikely(i == opp_table->required_opp_count))
dev_err(virt_dev, "Failed to find required device entry\n");
}
/**
* dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
* @src_table: OPP table which has dst_table as one of its required OPP table.
* @dst_table: Required OPP table of the src_table.
* @pstate: Current performance state of the src_table.
*
* This Returns pstate of the OPP (present in @dst_table) pointed out by the
* "required-opps" property of the OPP (present in @src_table) which has
* performance state set to @pstate.
*
* Return: Zero or positive performance state on success, otherwise negative
* value on errors.
*/
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table,
struct opp_table *dst_table,
unsigned int pstate)
{
struct dev_pm_opp *opp;
int dest_pstate = -EINVAL;
int i;
if (!pstate)
return 0;
/*
* Normally the src_table will have the "required_opps" property set to
* point to one of the OPPs in the dst_table, but in some cases the
* genpd and its master have one to one mapping of performance states
* and so none of them have the "required-opps" property set. Return the
* pstate of the src_table as it is in such cases.
*/
if (!src_table->required_opp_count)
return pstate;
for (i = 0; i < src_table->required_opp_count; i++) {
if (src_table->required_opp_tables[i]->np == dst_table->np)
break;
}
if (unlikely(i == src_table->required_opp_count)) {
pr_err("%s: Couldn't find matching OPP table (%p: %p)\n",
__func__, src_table, dst_table);
return -EINVAL;
}
mutex_lock(&src_table->lock);
list_for_each_entry(opp, &src_table->opp_list, node) {
if (opp->pstate == pstate) {
dest_pstate = opp->required_opps[i]->pstate;
goto unlock;
}
}
pr_err("%s: Couldn't find matching OPP (%p: %p)\n", __func__, src_table,
dst_table);
unlock:
mutex_unlock(&src_table->lock);
return dest_pstate;
}
/** /**
* dev_pm_opp_add() - Add an OPP table from a table definitions * dev_pm_opp_add() - Add an OPP table from a table definitions
* @dev: device for which we do this operation * @dev: device for which we do this operation
...@@ -1612,6 +1802,9 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) ...@@ -1612,6 +1802,9 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
if (!opp_table) if (!opp_table)
return -ENOMEM; return -ENOMEM;
/* Fix regulator count for dynamic OPPs */
opp_table->regulator_count = 1;
ret = _opp_add_v1(opp_table, dev, freq, u_volt, true); ret = _opp_add_v1(opp_table, dev, freq, u_volt, true);
if (ret) if (ret)
dev_pm_opp_put_opp_table(opp_table); dev_pm_opp_put_opp_table(opp_table);
......
...@@ -73,6 +73,167 @@ struct opp_table *_managed_opp(struct device *dev, int index) ...@@ -73,6 +73,167 @@ struct opp_table *_managed_opp(struct device *dev, int index)
return managed_table; return managed_table;
} }
/* The caller must call dev_pm_opp_put() after the OPP is used */
static struct dev_pm_opp *_find_opp_of_np(struct opp_table *opp_table,
struct device_node *opp_np)
{
struct dev_pm_opp *opp;
lockdep_assert_held(&opp_table_lock);
mutex_lock(&opp_table->lock);
list_for_each_entry(opp, &opp_table->opp_list, node) {
if (opp->np == opp_np) {
dev_pm_opp_get(opp);
mutex_unlock(&opp_table->lock);
return opp;
}
}
mutex_unlock(&opp_table->lock);
return NULL;
}
static struct device_node *of_parse_required_opp(struct device_node *np,
int index)
{
struct device_node *required_np;
required_np = of_parse_phandle(np, "required-opps", index);
if (unlikely(!required_np)) {
pr_err("%s: Unable to parse required-opps: %pOF, index: %d\n",
__func__, np, index);
}
return required_np;
}
/* The caller must call dev_pm_opp_put_opp_table() after the table is used */
static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np)
{
struct opp_table *opp_table;
struct device_node *opp_table_np;
lockdep_assert_held(&opp_table_lock);
opp_table_np = of_get_parent(opp_np);
if (!opp_table_np)
goto err;
/* It is safe to put the node now as all we need now is its address */
of_node_put(opp_table_np);
list_for_each_entry(opp_table, &opp_tables, node) {
if (opp_table_np == opp_table->np) {
_get_opp_table_kref(opp_table);
return opp_table;
}
}
err:
return ERR_PTR(-ENODEV);
}
/* Free resources previously acquired by _opp_table_alloc_required_tables() */
static void _opp_table_free_required_tables(struct opp_table *opp_table)
{
struct opp_table **required_opp_tables = opp_table->required_opp_tables;
struct device **genpd_virt_devs = opp_table->genpd_virt_devs;
int i;
if (!required_opp_tables)
return;
for (i = 0; i < opp_table->required_opp_count; i++) {
if (IS_ERR_OR_NULL(required_opp_tables[i]))
break;
dev_pm_opp_put_opp_table(required_opp_tables[i]);
}
kfree(required_opp_tables);
kfree(genpd_virt_devs);
opp_table->required_opp_count = 0;
opp_table->genpd_virt_devs = NULL;
opp_table->required_opp_tables = NULL;
}
/*
* Populate all devices and opp tables which are part of "required-opps" list.
* Checking only the first OPP node should be enough.
*/
static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
struct device *dev,
struct device_node *opp_np)
{
struct opp_table **required_opp_tables;
struct device **genpd_virt_devs = NULL;
struct device_node *required_np, *np;
int count, i;
/* Traversing the first OPP node is all we need */
np = of_get_next_available_child(opp_np, NULL);
if (!np) {
dev_err(dev, "Empty OPP table\n");
return;
}
count = of_count_phandle_with_args(np, "required-opps", NULL);
if (!count)
goto put_np;
if (count > 1) {
genpd_virt_devs = kcalloc(count, sizeof(*genpd_virt_devs),
GFP_KERNEL);
if (!genpd_virt_devs)
goto put_np;
}
required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
GFP_KERNEL);
if (!required_opp_tables) {
kfree(genpd_virt_devs);
goto put_np;
}
opp_table->genpd_virt_devs = genpd_virt_devs;
opp_table->required_opp_tables = required_opp_tables;
opp_table->required_opp_count = count;
for (i = 0; i < count; i++) {
required_np = of_parse_required_opp(np, i);
if (!required_np)
goto free_required_tables;
required_opp_tables[i] = _find_table_of_opp_np(required_np);
of_node_put(required_np);
if (IS_ERR(required_opp_tables[i]))
goto free_required_tables;
/*
* We only support genpd's OPPs in the "required-opps" for now,
* as we don't know how much about other cases. Error out if the
* required OPP doesn't belong to a genpd.
*/
if (!required_opp_tables[i]->is_genpd) {
dev_err(dev, "required-opp doesn't belong to genpd: %pOF\n",
required_np);
goto free_required_tables;
}
}
goto put_np;
free_required_tables:
_opp_table_free_required_tables(opp_table);
put_np:
of_node_put(np);
}
void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
int index) int index)
{ {
...@@ -92,6 +253,9 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, ...@@ -92,6 +253,9 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
of_property_read_u32(np, "voltage-tolerance", of_property_read_u32(np, "voltage-tolerance",
&opp_table->voltage_tolerance_v1); &opp_table->voltage_tolerance_v1);
if (of_find_property(np, "#power-domain-cells", NULL))
opp_table->is_genpd = true;
/* Get OPP table node */ /* Get OPP table node */
opp_np = _opp_of_get_opp_desc_node(np, index); opp_np = _opp_of_get_opp_desc_node(np, index);
of_node_put(np); of_node_put(np);
...@@ -106,9 +270,86 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, ...@@ -106,9 +270,86 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
opp_table->np = opp_np; opp_table->np = opp_np;
_opp_table_alloc_required_tables(opp_table, dev, opp_np);
of_node_put(opp_np); of_node_put(opp_np);
} }
void _of_clear_opp_table(struct opp_table *opp_table)
{
_opp_table_free_required_tables(opp_table);
}
/*
* Release all resources previously acquired with a call to
* _of_opp_alloc_required_opps().
*/
void _of_opp_free_required_opps(struct opp_table *opp_table,
struct dev_pm_opp *opp)
{
struct dev_pm_opp **required_opps = opp->required_opps;
int i;
if (!required_opps)
return;
for (i = 0; i < opp_table->required_opp_count; i++) {
if (!required_opps[i])
break;
/* Put the reference back */
dev_pm_opp_put(required_opps[i]);
}
kfree(required_opps);
opp->required_opps = NULL;
}
/* Populate all required OPPs which are part of "required-opps" list */
static int _of_opp_alloc_required_opps(struct opp_table *opp_table,
struct dev_pm_opp *opp)
{
struct dev_pm_opp **required_opps;
struct opp_table *required_table;
struct device_node *np;
int i, ret, count = opp_table->required_opp_count;
if (!count)
return 0;
required_opps = kcalloc(count, sizeof(*required_opps), GFP_KERNEL);
if (!required_opps)
return -ENOMEM;
opp->required_opps = required_opps;
for (i = 0; i < count; i++) {
required_table = opp_table->required_opp_tables[i];
np = of_parse_required_opp(opp->np, i);
if (unlikely(!np)) {
ret = -ENODEV;
goto free_required_opps;
}
required_opps[i] = _find_opp_of_np(required_table, np);
of_node_put(np);
if (!required_opps[i]) {
pr_err("%s: Unable to find required OPP node: %pOF (%d)\n",
__func__, opp->np, i);
ret = -ENODEV;
goto free_required_opps;
}
}
return 0;
free_required_opps:
_of_opp_free_required_opps(opp_table, opp);
return ret;
}
static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table, static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
struct device_node *np) struct device_node *np)
{ {
...@@ -150,12 +391,10 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -150,12 +391,10 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
struct opp_table *opp_table) struct opp_table *opp_table)
{ {
u32 *microvolt, *microamp = NULL; u32 *microvolt, *microamp = NULL;
int supplies, vcount, icount, ret, i, j; int supplies = opp_table->regulator_count, vcount, icount, ret, i, j;
struct property *prop = NULL; struct property *prop = NULL;
char name[NAME_MAX]; char name[NAME_MAX];
supplies = opp_table->regulator_count ? opp_table->regulator_count : 1;
/* Search for "opp-microvolt-<name>" */ /* Search for "opp-microvolt-<name>" */
if (opp_table->prop_name) { if (opp_table->prop_name) {
snprintf(name, sizeof(name), "opp-microvolt-%s", snprintf(name, sizeof(name), "opp-microvolt-%s",
...@@ -170,7 +409,13 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -170,7 +409,13 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
/* Missing property isn't a problem, but an invalid entry is */ /* Missing property isn't a problem, but an invalid entry is */
if (!prop) { if (!prop) {
if (!opp_table->regulator_count) if (unlikely(supplies == -1)) {
/* Initialize regulator_count */
opp_table->regulator_count = 0;
return 0;
}
if (!supplies)
return 0; return 0;
dev_err(dev, "%s: opp-microvolt missing although OPP managing regulators\n", dev_err(dev, "%s: opp-microvolt missing although OPP managing regulators\n",
...@@ -179,6 +424,14 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -179,6 +424,14 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
} }
} }
if (unlikely(supplies == -1)) {
/* Initialize regulator_count */
supplies = opp_table->regulator_count = 1;
} else if (unlikely(!supplies)) {
dev_err(dev, "%s: opp-microvolt wasn't expected\n", __func__);
return -EINVAL;
}
vcount = of_property_count_u32_elems(opp->np, name); vcount = of_property_count_u32_elems(opp->np, name);
if (vcount < 0) { if (vcount < 0) {
dev_err(dev, "%s: Invalid %s property (%d)\n", dev_err(dev, "%s: Invalid %s property (%d)\n",
...@@ -326,8 +579,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, ...@@ -326,8 +579,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
ret = of_property_read_u64(np, "opp-hz", &rate); ret = of_property_read_u64(np, "opp-hz", &rate);
if (ret < 0) { if (ret < 0) {
/* "opp-hz" is optional for devices like power domains. */ /* "opp-hz" is optional for devices like power domains. */
if (!of_find_property(dev->of_node, "#power-domain-cells", if (!opp_table->is_genpd) {
NULL)) {
dev_err(dev, "%s: opp-hz not found\n", __func__); dev_err(dev, "%s: opp-hz not found\n", __func__);
goto free_opp; goto free_opp;
} }
...@@ -354,21 +606,26 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, ...@@ -354,21 +606,26 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
new_opp->dynamic = false; new_opp->dynamic = false;
new_opp->available = true; new_opp->available = true;
ret = _of_opp_alloc_required_opps(opp_table, new_opp);
if (ret)
goto free_opp;
if (!of_property_read_u32(np, "clock-latency-ns", &val)) if (!of_property_read_u32(np, "clock-latency-ns", &val))
new_opp->clock_latency_ns = val; new_opp->clock_latency_ns = val;
new_opp->pstate = of_genpd_opp_to_performance_state(dev, np);
ret = opp_parse_supplies(new_opp, dev, opp_table); ret = opp_parse_supplies(new_opp, dev, opp_table);
if (ret) if (ret)
goto free_opp; goto free_required_opps;
if (opp_table->is_genpd)
new_opp->pstate = pm_genpd_opp_to_performance_state(dev, new_opp);
ret = _opp_add(dev, new_opp, opp_table, rate_not_available); ret = _opp_add(dev, new_opp, opp_table, rate_not_available);
if (ret) { if (ret) {
/* Don't return error for duplicate OPPs */ /* Don't return error for duplicate OPPs */
if (ret == -EBUSY) if (ret == -EBUSY)
ret = 0; ret = 0;
goto free_opp; goto free_required_opps;
} }
/* OPP to select on device suspend */ /* OPP to select on device suspend */
...@@ -398,6 +655,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, ...@@ -398,6 +655,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp); blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp);
return new_opp; return new_opp;
free_required_opps:
_of_opp_free_required_opps(opp_table, new_opp);
free_opp: free_opp:
_opp_free(new_opp); _opp_free(new_opp);
...@@ -727,58 +986,48 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, ...@@ -727,58 +986,48 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus);
/** /**
* of_dev_pm_opp_find_required_opp() - Search for required OPP. * of_get_required_opp_performance_state() - Search for required OPP and return its performance state.
* @dev: The device whose OPP node is referenced by the 'np' DT node.
* @np: Node that contains the "required-opps" property. * @np: Node that contains the "required-opps" property.
* @index: Index of the phandle to parse.
* *
* Returns the OPP of the device 'dev', whose phandle is present in the "np" * Returns the performance state of the OPP pointed out by the "required-opps"
* node. Although the "required-opps" property supports having multiple * property at @index in @np.
* phandles, this helper routine only parses the very first phandle in the list.
*
* Return: Matching opp, else returns ERR_PTR in case of error and should be
* handled using IS_ERR.
* *
* The callers are required to call dev_pm_opp_put() for the returned OPP after * Return: Zero or positive performance state on success, otherwise negative
* use. * value on errors.
*/ */
struct dev_pm_opp *of_dev_pm_opp_find_required_opp(struct device *dev, int of_get_required_opp_performance_state(struct device_node *np, int index)
struct device_node *np)
{ {
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ENODEV); struct dev_pm_opp *opp;
struct device_node *required_np; struct device_node *required_np;
struct opp_table *opp_table; struct opp_table *opp_table;
int pstate = -EINVAL;
opp_table = _find_opp_table(dev); required_np = of_parse_required_opp(np, index);
if (IS_ERR(opp_table)) if (!required_np)
return ERR_CAST(opp_table); return -EINVAL;
required_np = of_parse_phandle(np, "required-opps", 0); opp_table = _find_table_of_opp_np(required_np);
if (unlikely(!required_np)) { if (IS_ERR(opp_table)) {
dev_err(dev, "Unable to parse required-opps\n"); pr_err("%s: Failed to find required OPP table %pOF: %ld\n",
goto put_opp_table; __func__, np, PTR_ERR(opp_table));
goto put_required_np;
} }
mutex_lock(&opp_table->lock); opp = _find_opp_of_np(opp_table, required_np);
if (opp) {
list_for_each_entry(temp_opp, &opp_table->opp_list, node) { pstate = opp->pstate;
if (temp_opp->available && temp_opp->np == required_np) { dev_pm_opp_put(opp);
opp = temp_opp;
/* Increment the reference count of OPP */
dev_pm_opp_get(opp);
break;
}
} }
mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table);
put_required_np:
of_node_put(required_np); of_node_put(required_np);
put_opp_table:
dev_pm_opp_put_opp_table(opp_table);
return opp; return pstate;
} }
EXPORT_SYMBOL_GPL(of_dev_pm_opp_find_required_opp); EXPORT_SYMBOL_GPL(of_get_required_opp_performance_state);
/** /**
* dev_pm_opp_get_of_node() - Gets the DT node corresponding to an opp * dev_pm_opp_get_of_node() - Gets the DT node corresponding to an opp
......
...@@ -63,6 +63,7 @@ extern struct list_head opp_tables; ...@@ -63,6 +63,7 @@ extern struct list_head opp_tables;
* @supplies: Power supplies voltage/current values * @supplies: Power supplies voltage/current values
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
* frequency from any other OPP's frequency. * frequency from any other OPP's frequency.
* @required_opps: List of OPPs that are required by this OPP.
* @opp_table: points back to the opp_table struct this opp belongs to * @opp_table: points back to the opp_table struct this opp belongs to
* @np: OPP's device node. * @np: OPP's device node.
* @dentry: debugfs dentry pointer (per opp) * @dentry: debugfs dentry pointer (per opp)
...@@ -84,6 +85,7 @@ struct dev_pm_opp { ...@@ -84,6 +85,7 @@ struct dev_pm_opp {
unsigned long clock_latency_ns; unsigned long clock_latency_ns;
struct dev_pm_opp **required_opps;
struct opp_table *opp_table; struct opp_table *opp_table;
struct device_node *np; struct device_node *np;
...@@ -133,13 +135,21 @@ enum opp_table_access { ...@@ -133,13 +135,21 @@ enum opp_table_access {
* @parsed_static_opps: True if OPPs are initialized from DT. * @parsed_static_opps: True if OPPs are initialized from DT.
* @shared_opp: OPP is shared between multiple devices. * @shared_opp: OPP is shared between multiple devices.
* @suspend_opp: Pointer to OPP to be used during device suspend. * @suspend_opp: Pointer to OPP to be used during device suspend.
* @genpd_virt_dev_lock: Mutex protecting the genpd virtual device pointers.
* @genpd_virt_devs: List of virtual devices for multiple genpd support.
* @required_opp_tables: List of device OPP tables that are required by OPPs in
* this table.
* @required_opp_count: Number of required devices.
* @supported_hw: Array of version number to support. * @supported_hw: Array of version number to support.
* @supported_hw_count: Number of elements in supported_hw array. * @supported_hw_count: Number of elements in supported_hw array.
* @prop_name: A name to postfix to many DT properties, while parsing them. * @prop_name: A name to postfix to many DT properties, while parsing them.
* @clk: Device's clock handle * @clk: Device's clock handle
* @regulators: Supply regulators * @regulators: Supply regulators
* @regulator_count: Number of power supply regulators * @regulator_count: Number of power supply regulators. Its value can be -1
* (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt
* property).
* @genpd_performance_state: Device's power domain support performance state. * @genpd_performance_state: Device's power domain support performance state.
* @is_genpd: Marks if the OPP table belongs to a genpd.
* @set_opp: Platform specific set_opp callback * @set_opp: Platform specific set_opp callback
* @set_opp_data: Data to be passed to set_opp callback * @set_opp_data: Data to be passed to set_opp callback
* @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry: debugfs dentry pointer of the real device directory (not links).
...@@ -171,13 +181,19 @@ struct opp_table { ...@@ -171,13 +181,19 @@ struct opp_table {
enum opp_table_access shared_opp; enum opp_table_access shared_opp;
struct dev_pm_opp *suspend_opp; struct dev_pm_opp *suspend_opp;
struct mutex genpd_virt_dev_lock;
struct device **genpd_virt_devs;
struct opp_table **required_opp_tables;
unsigned int required_opp_count;
unsigned int *supported_hw; unsigned int *supported_hw;
unsigned int supported_hw_count; unsigned int supported_hw_count;
const char *prop_name; const char *prop_name;
struct clk *clk; struct clk *clk;
struct regulator **regulators; struct regulator **regulators;
unsigned int regulator_count; int regulator_count;
bool genpd_performance_state; bool genpd_performance_state;
bool is_genpd;
int (*set_opp)(struct dev_pm_set_opp_data *data); int (*set_opp)(struct dev_pm_set_opp_data *data);
struct dev_pm_set_opp_data *set_opp_data; struct dev_pm_set_opp_data *set_opp_data;
...@@ -206,10 +222,16 @@ void _put_opp_list_kref(struct opp_table *opp_table); ...@@ -206,10 +222,16 @@ void _put_opp_list_kref(struct opp_table *opp_table);
#ifdef CONFIG_OF #ifdef CONFIG_OF
void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index); void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index);
void _of_clear_opp_table(struct opp_table *opp_table);
struct opp_table *_managed_opp(struct device *dev, int index); struct opp_table *_managed_opp(struct device *dev, int index);
void _of_opp_free_required_opps(struct opp_table *opp_table,
struct dev_pm_opp *opp);
#else #else
static inline void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index) {} static inline void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index) {}
static inline void _of_clear_opp_table(struct opp_table *opp_table) {}
static inline struct opp_table *_managed_opp(struct device *dev, int index) { return NULL; } static inline struct opp_table *_managed_opp(struct device *dev, int index) { return NULL; }
static inline void _of_opp_free_required_opps(struct opp_table *opp_table,
struct dev_pm_opp *opp) {}
#endif #endif
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* OMAP SmartReflex Voltage Control * OMAP SmartReflex Voltage Control
* *
...@@ -11,10 +12,6 @@ ...@@ -11,10 +12,6 @@
* *
* Copyright (C) 2007 Texas Instruments, Inc. * Copyright (C) 2007 Texas Instruments, Inc.
* Lesly A M <x0080970@ti.com> * Lesly A M <x0080970@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.
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -37,7 +34,6 @@ ...@@ -37,7 +34,6 @@
static LIST_HEAD(sr_list); static LIST_HEAD(sr_list);
static struct omap_sr_class_data *sr_class; static struct omap_sr_class_data *sr_class;
static struct omap_sr_pmic_data *sr_pmic_data;
static struct dentry *sr_dbg_dir; static struct dentry *sr_dbg_dir;
static inline void sr_write_reg(struct omap_sr *sr, unsigned offset, u32 value) static inline void sr_write_reg(struct omap_sr *sr, unsigned offset, u32 value)
...@@ -780,25 +776,6 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm) ...@@ -780,25 +776,6 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm)
sr_class->disable(sr, 1); sr_class->disable(sr, 1);
} }
/**
* omap_sr_register_pmic() - API to register pmic specific info.
* @pmic_data: The structure containing pmic specific data.
*
* This API is to be called from the PMIC specific code to register with
* smartreflex driver pmic specific info. Currently the only info required
* is the smartreflex init on the PMIC side.
*/
void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data)
{
if (!pmic_data) {
pr_warn("%s: Trying to register NULL PMIC data structure with smartreflex\n",
__func__);
return;
}
sr_pmic_data = pmic_data;
}
/* PM Debug FS entries to enable and disable smartreflex. */ /* PM Debug FS entries to enable and disable smartreflex. */
static int omap_sr_autocomp_show(void *data, u64 *val) static int omap_sr_autocomp_show(void *data, u64 *val)
{ {
...@@ -1010,8 +987,7 @@ static int omap_sr_remove(struct platform_device *pdev) ...@@ -1010,8 +987,7 @@ static int omap_sr_remove(struct platform_device *pdev)
if (sr_info->autocomp_active) if (sr_info->autocomp_active)
sr_stop_vddautocomp(sr_info); sr_stop_vddautocomp(sr_info);
if (sr_info->dbg_dir) debugfs_remove_recursive(sr_info->dbg_dir);
debugfs_remove_recursive(sr_info->dbg_dir);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
list_del(&sr_info->node); list_del(&sr_info->node);
...@@ -1065,17 +1041,6 @@ static int __init sr_init(void) ...@@ -1065,17 +1041,6 @@ static int __init sr_init(void)
{ {
int ret = 0; int ret = 0;
/*
* sr_init is a late init. If by then a pmic specific API is not
* registered either there is no need for anything to be done on
* the PMIC side or somebody has forgotten to register a PMIC
* handler. Warn for the second condition.
*/
if (sr_pmic_data && sr_pmic_data->sr_pmic_init)
sr_pmic_data->sr_pmic_init();
else
pr_warn("%s: No PMIC hook to init smartreflex\n", __func__);
ret = platform_driver_register(&smartreflex_driver); ret = platform_driver_register(&smartreflex_driver);
if (ret) { if (ret) {
pr_err("%s: platform driver register failed for SR\n", pr_err("%s: platform driver register failed for SR\n",
......
...@@ -33,6 +33,8 @@ struct cpuidle_state_usage { ...@@ -33,6 +33,8 @@ struct cpuidle_state_usage {
unsigned long long disable; unsigned long long disable;
unsigned long long usage; unsigned long long usage;
unsigned long long time; /* in US */ unsigned long long time; /* in US */
unsigned long long above; /* Number of times it's been too deep */
unsigned long long below; /* Number of times it's been too shallow */
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
unsigned long long s2idle_usage; unsigned long long s2idle_usage;
unsigned long long s2idle_time; /* in US */ unsigned long long s2idle_time; /* in US */
......
...@@ -131,6 +131,9 @@ struct devfreq_dev_profile { ...@@ -131,6 +131,9 @@ struct devfreq_dev_profile {
* @scaling_min_freq: Limit minimum frequency requested by OPP interface * @scaling_min_freq: Limit minimum frequency requested by OPP interface
* @scaling_max_freq: Limit maximum frequency requested by OPP interface * @scaling_max_freq: Limit maximum frequency requested by OPP interface
* @stop_polling: devfreq polling status of a device. * @stop_polling: devfreq polling status of a device.
* @suspend_freq: frequency of a device set during suspend phase.
* @resume_freq: frequency of a device set in resume phase.
* @suspend_count: suspend requests counter for a device.
* @total_trans: Number of devfreq transitions * @total_trans: Number of devfreq transitions
* @trans_table: Statistics of devfreq transitions * @trans_table: Statistics of devfreq transitions
* @time_in_state: Statistics of devfreq states * @time_in_state: Statistics of devfreq states
...@@ -167,6 +170,10 @@ struct devfreq { ...@@ -167,6 +170,10 @@ struct devfreq {
unsigned long scaling_max_freq; unsigned long scaling_max_freq;
bool stop_polling; bool stop_polling;
unsigned long suspend_freq;
unsigned long resume_freq;
atomic_t suspend_count;
/* information for device frequency transition */ /* information for device frequency transition */
unsigned int total_trans; unsigned int total_trans;
unsigned int *trans_table; unsigned int *trans_table;
...@@ -198,6 +205,9 @@ extern void devm_devfreq_remove_device(struct device *dev, ...@@ -198,6 +205,9 @@ extern void devm_devfreq_remove_device(struct device *dev,
extern int devfreq_suspend_device(struct devfreq *devfreq); extern int devfreq_suspend_device(struct devfreq *devfreq);
extern int devfreq_resume_device(struct devfreq *devfreq); extern int devfreq_resume_device(struct devfreq *devfreq);
extern void devfreq_suspend(void);
extern void devfreq_resume(void);
/** /**
* update_devfreq() - Reevaluate the device and configure frequency * update_devfreq() - Reevaluate the device and configure frequency
* @devfreq: the devfreq device * @devfreq: the devfreq device
...@@ -324,6 +334,9 @@ static inline int devfreq_resume_device(struct devfreq *devfreq) ...@@ -324,6 +334,9 @@ static inline int devfreq_resume_device(struct devfreq *devfreq)
return 0; return 0;
} }
static inline void devfreq_suspend(void) {}
static inline void devfreq_resume(void) {}
static inline struct dev_pm_opp *devfreq_recommended_opp(struct device *dev, static inline struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
unsigned long *freq, u32 flags) unsigned long *freq, u32 flags)
{ {
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/hrtimer.h>
#include <linux/completion.h> #include <linux/completion.h>
/* /*
...@@ -608,7 +609,7 @@ struct dev_pm_info { ...@@ -608,7 +609,7 @@ struct dev_pm_info {
unsigned int should_wakeup:1; unsigned int should_wakeup:1;
#endif #endif
#ifdef CONFIG_PM #ifdef CONFIG_PM
struct timer_list suspend_timer; struct hrtimer suspend_timer;
unsigned long timer_expires; unsigned long timer_expires;
struct work_struct work; struct work_struct work;
wait_queue_head_t wait_queue; wait_queue_head_t wait_queue;
...@@ -631,7 +632,7 @@ struct dev_pm_info { ...@@ -631,7 +632,7 @@ struct dev_pm_info {
enum rpm_status runtime_status; enum rpm_status runtime_status;
int runtime_error; int runtime_error;
int autosuspend_delay; int autosuspend_delay;
unsigned long last_busy; u64 last_busy;
unsigned long active_jiffies; unsigned long active_jiffies;
unsigned long suspended_jiffies; unsigned long suspended_jiffies;
unsigned long accounting_timestamp; unsigned long accounting_timestamp;
......
...@@ -73,6 +73,7 @@ struct genpd_power_state { ...@@ -73,6 +73,7 @@ struct genpd_power_state {
struct genpd_lock_ops; struct genpd_lock_ops;
struct dev_pm_opp; struct dev_pm_opp;
struct opp_table;
struct generic_pm_domain { struct generic_pm_domain {
struct device dev; struct device dev;
...@@ -94,6 +95,7 @@ struct generic_pm_domain { ...@@ -94,6 +95,7 @@ struct generic_pm_domain {
unsigned int performance_state; /* Aggregated max performance state */ unsigned int performance_state; /* Aggregated max performance state */
int (*power_off)(struct generic_pm_domain *domain); int (*power_off)(struct generic_pm_domain *domain);
int (*power_on)(struct generic_pm_domain *domain); int (*power_on)(struct generic_pm_domain *domain);
struct opp_table *opp_table; /* OPP table of the genpd */
unsigned int (*opp_to_performance_state)(struct generic_pm_domain *genpd, unsigned int (*opp_to_performance_state)(struct generic_pm_domain *genpd,
struct dev_pm_opp *opp); struct dev_pm_opp *opp);
int (*set_performance_state)(struct generic_pm_domain *genpd, int (*set_performance_state)(struct generic_pm_domain *genpd,
...@@ -134,6 +136,10 @@ struct gpd_link { ...@@ -134,6 +136,10 @@ struct gpd_link {
struct list_head master_node; struct list_head master_node;
struct generic_pm_domain *slave; struct generic_pm_domain *slave;
struct list_head slave_node; struct list_head slave_node;
/* Sub-domain's per-master domain performance state */
unsigned int performance_state;
unsigned int prev_performance_state;
}; };
struct gpd_timing_data { struct gpd_timing_data {
...@@ -258,8 +264,8 @@ int of_genpd_add_subdomain(struct of_phandle_args *parent, ...@@ -258,8 +264,8 @@ int of_genpd_add_subdomain(struct of_phandle_args *parent,
struct generic_pm_domain *of_genpd_remove_last(struct device_node *np); struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
int of_genpd_parse_idle_states(struct device_node *dn, int of_genpd_parse_idle_states(struct device_node *dn,
struct genpd_power_state **states, int *n); struct genpd_power_state **states, int *n);
unsigned int of_genpd_opp_to_performance_state(struct device *dev, unsigned int pm_genpd_opp_to_performance_state(struct device *genpd_dev,
struct device_node *np); struct dev_pm_opp *opp);
int genpd_dev_pm_attach(struct device *dev); int genpd_dev_pm_attach(struct device *dev);
struct device *genpd_dev_pm_attach_by_id(struct device *dev, struct device *genpd_dev_pm_attach_by_id(struct device *dev,
...@@ -300,8 +306,8 @@ static inline int of_genpd_parse_idle_states(struct device_node *dn, ...@@ -300,8 +306,8 @@ static inline int of_genpd_parse_idle_states(struct device_node *dn,
} }
static inline unsigned int static inline unsigned int
of_genpd_opp_to_performance_state(struct device *dev, pm_genpd_opp_to_performance_state(struct device *genpd_dev,
struct device_node *np) struct dev_pm_opp *opp)
{ {
return 0; return 0;
} }
......
...@@ -126,6 +126,9 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name); ...@@ -126,6 +126,9 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
void dev_pm_opp_put_clkname(struct opp_table *opp_table); void dev_pm_opp_put_clkname(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index);
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev);
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
...@@ -272,6 +275,18 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const ...@@ -272,6 +275,18 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const
static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {} static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}
static inline struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index)
{
return ERR_PTR(-ENOTSUPP);
}
static inline void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev) {}
static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate)
{
return -ENOTSUPP;
}
static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{ {
return -ENOTSUPP; return -ENOTSUPP;
...@@ -305,8 +320,8 @@ int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask); ...@@ -305,8 +320,8 @@ int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask);
void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask); void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask);
int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev); struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev);
struct dev_pm_opp *of_dev_pm_opp_find_required_opp(struct device *dev, struct device_node *np);
struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp); struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp);
int of_get_required_opp_performance_state(struct device_node *np, int index);
#else #else
static inline int dev_pm_opp_of_add_table(struct device *dev) static inline int dev_pm_opp_of_add_table(struct device *dev)
{ {
...@@ -341,13 +356,13 @@ static inline struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device ...@@ -341,13 +356,13 @@ static inline struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device
return NULL; return NULL;
} }
static inline struct dev_pm_opp *of_dev_pm_opp_find_required_opp(struct device *dev, struct device_node *np) static inline struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp)
{ {
return NULL; return NULL;
} }
static inline struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) static inline int of_get_required_opp_performance_state(struct device_node *np, int index)
{ {
return NULL; return -ENOTSUPP;
} }
#endif #endif
......
...@@ -51,7 +51,7 @@ extern void pm_runtime_no_callbacks(struct device *dev); ...@@ -51,7 +51,7 @@ extern void pm_runtime_no_callbacks(struct device *dev);
extern void pm_runtime_irq_safe(struct device *dev); extern void pm_runtime_irq_safe(struct device *dev);
extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); extern void __pm_runtime_use_autosuspend(struct device *dev, bool use);
extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); extern u64 pm_runtime_autosuspend_expiration(struct device *dev);
extern void pm_runtime_update_max_time_suspended(struct device *dev, extern void pm_runtime_update_max_time_suspended(struct device *dev,
s64 delta_ns); s64 delta_ns);
extern void pm_runtime_set_memalloc_noio(struct device *dev, bool enable); extern void pm_runtime_set_memalloc_noio(struct device *dev, bool enable);
...@@ -105,7 +105,7 @@ static inline bool pm_runtime_callbacks_present(struct device *dev) ...@@ -105,7 +105,7 @@ static inline bool pm_runtime_callbacks_present(struct device *dev)
static inline void pm_runtime_mark_last_busy(struct device *dev) static inline void pm_runtime_mark_last_busy(struct device *dev)
{ {
WRITE_ONCE(dev->power.last_busy, jiffies); WRITE_ONCE(dev->power.last_busy, ktime_to_ns(ktime_get()));
} }
static inline bool pm_runtime_is_irq_safe(struct device *dev) static inline bool pm_runtime_is_irq_safe(struct device *dev)
...@@ -168,7 +168,7 @@ static inline void __pm_runtime_use_autosuspend(struct device *dev, ...@@ -168,7 +168,7 @@ static inline void __pm_runtime_use_autosuspend(struct device *dev,
bool use) {} bool use) {}
static inline void pm_runtime_set_autosuspend_delay(struct device *dev, static inline void pm_runtime_set_autosuspend_delay(struct device *dev,
int delay) {} int delay) {}
static inline unsigned long pm_runtime_autosuspend_expiration( static inline u64 pm_runtime_autosuspend_expiration(
struct device *dev) { return 0; } struct device *dev) { return 0; }
static inline void pm_runtime_set_memalloc_noio(struct device *dev, static inline void pm_runtime_set_memalloc_noio(struct device *dev,
bool enable){} bool enable){}
......
/* SPDX-License-Identifier: GPL-2.0 */
/* /*
* OMAP Smartreflex Defines and Routines * OMAP Smartreflex Defines and Routines
* *
...@@ -11,10 +12,6 @@ ...@@ -11,10 +12,6 @@
* *
* Copyright (C) 2007 Texas Instruments, Inc. * Copyright (C) 2007 Texas Instruments, Inc.
* Lesly A M <x0080970@ti.com> * Lesly A M <x0080970@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.
*/ */
#ifndef __POWER_SMARTREFLEX_H #ifndef __POWER_SMARTREFLEX_H
...@@ -303,9 +300,6 @@ void omap_sr_enable(struct voltagedomain *voltdm); ...@@ -303,9 +300,6 @@ void omap_sr_enable(struct voltagedomain *voltdm);
void omap_sr_disable(struct voltagedomain *voltdm); void omap_sr_disable(struct voltagedomain *voltdm);
void omap_sr_disable_reset_volt(struct voltagedomain *voltdm); void omap_sr_disable_reset_volt(struct voltagedomain *voltdm);
/* API to register the pmic specific data with the smartreflex driver. */
void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data);
/* Smartreflex driver hooks to be called from Smartreflex class driver */ /* Smartreflex driver hooks to be called from Smartreflex class driver */
int sr_enable(struct omap_sr *sr, unsigned long volt); int sr_enable(struct omap_sr *sr, unsigned long volt);
void sr_disable(struct omap_sr *sr); void sr_disable(struct omap_sr *sr);
...@@ -320,7 +314,5 @@ static inline void omap_sr_enable(struct voltagedomain *voltdm) {} ...@@ -320,7 +314,5 @@ static inline void omap_sr_enable(struct voltagedomain *voltdm) {}
static inline void omap_sr_disable(struct voltagedomain *voltdm) {} static inline void omap_sr_disable(struct voltagedomain *voltdm) {}
static inline void omap_sr_disable_reset_volt( static inline void omap_sr_disable_reset_volt(
struct voltagedomain *voltdm) {} struct voltagedomain *voltdm) {}
static inline void omap_sr_register_pmic(
struct omap_sr_pmic_data *pmic_data) {}
#endif #endif
#endif #endif
...@@ -318,23 +318,12 @@ static int suspend_stats_show(struct seq_file *s, void *unused) ...@@ -318,23 +318,12 @@ static int suspend_stats_show(struct seq_file *s, void *unused)
return 0; return 0;
} }
DEFINE_SHOW_ATTRIBUTE(suspend_stats);
static int suspend_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, suspend_stats_show, NULL);
}
static const struct file_operations suspend_stats_operations = {
.open = suspend_stats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init pm_debugfs_init(void) static int __init pm_debugfs_init(void)
{ {
debugfs_create_file("suspend_stats", S_IFREG | S_IRUGO, debugfs_create_file("suspend_stats", S_IFREG | S_IRUGO,
NULL, NULL, &suspend_stats_operations); NULL, NULL, &suspend_stats_fops);
return 0; return 0;
} }
......
...@@ -184,7 +184,7 @@ static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value) ...@@ -184,7 +184,7 @@ static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value)
c->target_value = value; c->target_value = value;
} }
static int pm_qos_dbg_show_requests(struct seq_file *s, void *unused) static int pm_qos_debug_show(struct seq_file *s, void *unused)
{ {
struct pm_qos_object *qos = (struct pm_qos_object *)s->private; struct pm_qos_object *qos = (struct pm_qos_object *)s->private;
struct pm_qos_constraints *c; struct pm_qos_constraints *c;
...@@ -245,18 +245,7 @@ static int pm_qos_dbg_show_requests(struct seq_file *s, void *unused) ...@@ -245,18 +245,7 @@ static int pm_qos_dbg_show_requests(struct seq_file *s, void *unused)
return 0; return 0;
} }
static int pm_qos_dbg_open(struct inode *inode, struct file *file) DEFINE_SHOW_ATTRIBUTE(pm_qos_debug);
{
return single_open(file, pm_qos_dbg_show_requests,
inode->i_private);
}
static const struct file_operations pm_qos_debug_fops = {
.open = pm_qos_dbg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/** /**
* pm_qos_update_target - manages the constraints list and calls the notifiers * pm_qos_update_target - manages the constraints list and calls the notifiers
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* Scheduler code and data structures related to cpufreq. * Scheduler code and data structures related to cpufreq.
* *
* Copyright (C) 2016, Intel Corporation * Copyright (C) 2016, Intel Corporation
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.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.
*/ */
#include "sched.h" #include "sched.h"
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* CPUFreq governor based on scheduler-provided CPU utilization data. * CPUFreq governor based on scheduler-provided CPU utilization data.
* *
* Copyright (C) 2016, Intel Corporation * Copyright (C) 2016, Intel Corporation
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.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.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
......
...@@ -89,6 +89,7 @@ endif ...@@ -89,6 +89,7 @@ endif
localedir ?= /usr/share/locale localedir ?= /usr/share/locale
docdir ?= /usr/share/doc/packages/cpupower docdir ?= /usr/share/doc/packages/cpupower
confdir ?= /etc/ confdir ?= /etc/
bash_completion_dir ?= /usr/share/bash-completion/completions
# Toolchain: what tools do we use, and what options do they need: # Toolchain: what tools do we use, and what options do they need:
...@@ -96,7 +97,8 @@ CP = cp -fpR ...@@ -96,7 +97,8 @@ CP = cp -fpR
INSTALL = /usr/bin/install -c INSTALL = /usr/bin/install -c
INSTALL_PROGRAM = ${INSTALL} INSTALL_PROGRAM = ${INSTALL}
INSTALL_DATA = ${INSTALL} -m 644 INSTALL_DATA = ${INSTALL} -m 644
INSTALL_SCRIPT = ${INSTALL_PROGRAM} #bash completion scripts get sourced and so they should be rw only.
INSTALL_SCRIPT = ${INSTALL} -m 644
# If you are running a cross compiler, you may want to set this # If you are running a cross compiler, you may want to set this
# to something more interesting, like "arm-linux-". If you want # to something more interesting, like "arm-linux-". If you want
...@@ -288,6 +290,8 @@ install-lib: ...@@ -288,6 +290,8 @@ install-lib:
install-tools: install-tools:
$(INSTALL) -d $(DESTDIR)${bindir} $(INSTALL) -d $(DESTDIR)${bindir}
$(INSTALL_PROGRAM) $(OUTPUT)cpupower $(DESTDIR)${bindir} $(INSTALL_PROGRAM) $(OUTPUT)cpupower $(DESTDIR)${bindir}
$(INSTALL) -d $(DESTDIR)${bash_completion_dir}
$(INSTALL_SCRIPT) cpupower-completion.sh '$(DESTDIR)${bash_completion_dir}/cpupower'
install-man: install-man:
$(INSTALL_DATA) -D man/cpupower.1 $(DESTDIR)${mandir}/man1/cpupower.1 $(INSTALL_DATA) -D man/cpupower.1 $(DESTDIR)${mandir}/man1/cpupower.1
......
# -*- shell-script -*-
# bash completion script for cpupower
# Taken from git.git's completion script.
_cpupower_commands="frequency-info frequency-set idle-info idle-set set info monitor"
_frequency_info ()
{
local flags="-f -w -l -d -p -g -a -s -y -o -m -n --freq --hwfreq --hwlimits --driver --policy --governors --related-cpus --affected-cpus --stats --latency --proc --human --no-rounding"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$prev" in
frequency-info) COMPREPLY=($(compgen -W "$flags" -- "$cur")) ;;
esac
}
_frequency_set ()
{
local flags="-f -g --freq --governor -d --min -u --max -r --related"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$prev" in
-f| --freq | -d | --min | -u | --max)
if [ -d /sys/devices/system/cpu/cpufreq/ ] ; then
COMPREPLY=($(compgen -W '$(cat $(ls -d /sys/devices/system/cpu/cpufreq/policy* | head -1)/scaling_available_frequencies)' -- "$cur"))
fi ;;
-g| --governor)
if [ -d /sys/devices/system/cpu/cpufreq/ ] ; then
COMPREPLY=($(compgen -W '$(cat $(ls -d /sys/devices/system/cpu/cpufreq/policy* | head -1)/scaling_available_governors)' -- "$cur"))
fi;;
frequency-set) COMPREPLY=($(compgen -W "$flags" -- "$cur")) ;;
esac
}
_idle_info()
{
local flags="-f --silent"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$prev" in
idle-info) COMPREPLY=($(compgen -W "$flags" -- "$cur")) ;;
esac
}
_idle_set()
{
local flags="-d --disable -e --enable -D --disable-by-latency -E --enable-all"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$prev" in
idle-set) COMPREPLY=($(compgen -W "$flags" -- "$cur")) ;;
esac
}
_set()
{
local flags="--perf-bias, -b"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$prev" in
set) COMPREPLY=($(compgen -W "$flags" -- "$cur")) ;;
esac
}
_monitor()
{
local flags="-l -m -i -c -v"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$prev" in
monitor) COMPREPLY=($(compgen -W "$flags" -- "$cur")) ;;
esac
}
_taskset()
{
local prev_to_prev="${COMP_WORDS[COMP_CWORD-2]}"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$prev_to_prev" in
-c|--cpu) COMPREPLY=($(compgen -W "$_cpupower_commands" -- "$cur")) ;;
esac
case "$prev" in
frequency-info) _frequency_info ;;
frequency-set) _frequency_set ;;
idle-info) _idle_info ;;
idle-set) _idle_set ;;
set) _set ;;
monitor) _monitor ;;
esac
}
_cpupower ()
{
local i
local c=1
local command
while test $c -lt $COMP_CWORD; do
if test $c == 1; then
command="${COMP_WORDS[c]}"
fi
c=$((++c))
done
# Complete name of subcommand if the user has not finished typing it yet.
if test $c -eq $COMP_CWORD -a -z "$command"; then
COMPREPLY=($(compgen -W "help -v --version -c --cpu $_cpupower_commands" -- "${COMP_WORDS[COMP_CWORD]}"))
return
fi
# Complete arguments to subcommands.
case "$command" in
-v|--version) return ;;
-c|--cpu) _taskset ;;
help) COMPREPLY=($(compgen -W "$_cpupower_commands" -- "${COMP_WORDS[COMP_CWORD]}")) ;;
frequency-info) _frequency_info ;;
frequency-set) _frequency_set ;;
idle-info) _idle_info ;;
idle-set) _idle_set ;;
set) _set ;;
monitor) _monitor ;;
esac
}
complete -o bashdefault -o default -F _cpupower cpupower 2>/dev/null \
|| complete -o default -F _cpupower cpupower
...@@ -585,9 +585,9 @@ current_max_cpu = 0 ...@@ -585,9 +585,9 @@ current_max_cpu = 0
read_trace_data(filename) read_trace_data(filename)
clear_trace_file()
# Free the memory
if interval: if interval:
clear_trace_file()
# Free the memory
free_trace_buffer() free_trace_buffer()
if graph_data_present == False: if graph_data_present == False:
......
/* /*
* turbostat -- show CPU frequency and C-state residency * turbostat -- show CPU frequency and C-state residency
* on modern Intel turbo-capable processors. * on modern Intel and AMD processors.
* *
* Copyright (c) 2013 Intel Corporation. * Copyright (c) 2013 Intel Corporation.
* Len Brown <len.brown@intel.com> * Len Brown <len.brown@intel.com>
...@@ -71,6 +71,8 @@ unsigned int do_irtl_snb; ...@@ -71,6 +71,8 @@ unsigned int do_irtl_snb;
unsigned int do_irtl_hsw; unsigned int do_irtl_hsw;
unsigned int units = 1000000; /* MHz etc */ unsigned int units = 1000000; /* MHz etc */
unsigned int genuine_intel; unsigned int genuine_intel;
unsigned int authentic_amd;
unsigned int max_level, max_extended_level;
unsigned int has_invariant_tsc; unsigned int has_invariant_tsc;
unsigned int do_nhm_platform_info; unsigned int do_nhm_platform_info;
unsigned int no_MSR_MISC_PWR_MGMT; unsigned int no_MSR_MISC_PWR_MGMT;
...@@ -1667,30 +1669,51 @@ int get_mp(int cpu, struct msr_counter *mp, unsigned long long *counterp) ...@@ -1667,30 +1669,51 @@ int get_mp(int cpu, struct msr_counter *mp, unsigned long long *counterp)
void get_apic_id(struct thread_data *t) void get_apic_id(struct thread_data *t)
{ {
unsigned int eax, ebx, ecx, edx, max_level; unsigned int eax, ebx, ecx, edx;
eax = ebx = ecx = edx = 0; if (DO_BIC(BIC_APIC)) {
eax = ebx = ecx = edx = 0;
__cpuid(1, eax, ebx, ecx, edx);
if (!genuine_intel) t->apic_id = (ebx >> 24) & 0xff;
}
if (!DO_BIC(BIC_X2APIC))
return; return;
__cpuid(0, max_level, ebx, ecx, edx); if (authentic_amd) {
unsigned int topology_extensions;
__cpuid(1, eax, ebx, ecx, edx); if (max_extended_level < 0x8000001e)
t->apic_id = (ebx >> 24) & 0xf; return;
if (max_level < 0xb) eax = ebx = ecx = edx = 0;
__cpuid(0x80000001, eax, ebx, ecx, edx);
topology_extensions = ecx & (1 << 22);
if (topology_extensions == 0)
return;
eax = ebx = ecx = edx = 0;
__cpuid(0x8000001e, eax, ebx, ecx, edx);
t->x2apic_id = eax;
return; return;
}
if (!DO_BIC(BIC_X2APIC)) if (!genuine_intel)
return;
if (max_level < 0xb)
return; return;
ecx = 0; ecx = 0;
__cpuid(0xb, eax, ebx, ecx, edx); __cpuid(0xb, eax, ebx, ecx, edx);
t->x2apic_id = edx; t->x2apic_id = edx;
if (debug && (t->apic_id != t->x2apic_id)) if (debug && (t->apic_id != (t->x2apic_id & 0xff)))
fprintf(outf, "cpu%d: apic 0x%x x2apic 0x%x\n", t->cpu_id, t->apic_id, t->x2apic_id); fprintf(outf, "cpu%d: BIOS BUG: apic 0x%x x2apic 0x%x\n",
t->cpu_id, t->apic_id, t->x2apic_id);
} }
/* /*
...@@ -1953,11 +1976,12 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) ...@@ -1953,11 +1976,12 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
#define PCL_7S 11 /* PC7 Shrink */ #define PCL_7S 11 /* PC7 Shrink */
#define PCL__8 12 /* PC8 */ #define PCL__8 12 /* PC8 */
#define PCL__9 13 /* PC9 */ #define PCL__9 13 /* PC9 */
#define PCLUNL 14 /* Unlimited */ #define PCL_10 14 /* PC10 */
#define PCLUNL 15 /* Unlimited */
int pkg_cstate_limit = PCLUKN; int pkg_cstate_limit = PCLUKN;
char *pkg_cstate_limit_strings[] = { "reserved", "unknown", "pc0", "pc1", "pc2", char *pkg_cstate_limit_strings[] = { "reserved", "unknown", "pc0", "pc1", "pc2",
"pc3", "pc4", "pc6", "pc6n", "pc6r", "pc7", "pc7s", "pc8", "pc9", "unlimited"}; "pc3", "pc4", "pc6", "pc6n", "pc6r", "pc7", "pc7s", "pc8", "pc9", "pc10", "unlimited"};
int nhm_pkg_cstate_limits[16] = {PCL__0, PCL__1, PCL__3, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV}; int nhm_pkg_cstate_limits[16] = {PCL__0, PCL__1, PCL__3, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
int snb_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCL__7, PCL_7S, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV}; int snb_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCL__7, PCL_7S, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
...@@ -1965,7 +1989,7 @@ int hsw_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL__3, PCL__6, PCL__7, PCL_7S, ...@@ -1965,7 +1989,7 @@ int hsw_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL__3, PCL__6, PCL__7, PCL_7S,
int slv_pkg_cstate_limits[16] = {PCL__0, PCL__1, PCLRSV, PCLRSV, PCL__4, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7}; int slv_pkg_cstate_limits[16] = {PCL__0, PCL__1, PCLRSV, PCLRSV, PCL__4, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7};
int amt_pkg_cstate_limits[16] = {PCLUNL, PCL__1, PCL__2, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV}; int amt_pkg_cstate_limits[16] = {PCLUNL, PCL__1, PCL__2, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
int phi_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV}; int phi_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
int bxt_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV}; int glm_pkg_cstate_limits[16] = {PCLUNL, PCL__1, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCL_10, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
int skx_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV}; int skx_pkg_cstate_limits[16] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV};
...@@ -3113,13 +3137,8 @@ int probe_nhm_msrs(unsigned int family, unsigned int model) ...@@ -3113,13 +3137,8 @@ int probe_nhm_msrs(unsigned int family, unsigned int model)
bclk = discover_bclk(family, model); bclk = discover_bclk(family, model);
switch (model) { switch (model) {
case INTEL_FAM6_NEHALEM_EP: /* Core i7, Xeon 5500 series - Bloomfield, Gainstown NHM-EP */
case INTEL_FAM6_NEHALEM: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper Forest */ case INTEL_FAM6_NEHALEM: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper Forest */
case 0x1F: /* Core i7 and i5 Processor - Nehalem */
case INTEL_FAM6_WESTMERE: /* Westmere Client - Clarkdale, Arrandale */
case INTEL_FAM6_WESTMERE_EP: /* Westmere EP - Gulftown */
case INTEL_FAM6_NEHALEM_EX: /* Nehalem-EX Xeon - Beckton */ case INTEL_FAM6_NEHALEM_EX: /* Nehalem-EX Xeon - Beckton */
case INTEL_FAM6_WESTMERE_EX: /* Westmere-EX Xeon - Eagleton */
pkg_cstate_limits = nhm_pkg_cstate_limits; pkg_cstate_limits = nhm_pkg_cstate_limits;
break; break;
case INTEL_FAM6_SANDYBRIDGE: /* SNB */ case INTEL_FAM6_SANDYBRIDGE: /* SNB */
...@@ -3131,16 +3150,11 @@ int probe_nhm_msrs(unsigned int family, unsigned int model) ...@@ -3131,16 +3150,11 @@ int probe_nhm_msrs(unsigned int family, unsigned int model)
break; break;
case INTEL_FAM6_HASWELL_CORE: /* HSW */ case INTEL_FAM6_HASWELL_CORE: /* HSW */
case INTEL_FAM6_HASWELL_X: /* HSX */ case INTEL_FAM6_HASWELL_X: /* HSX */
case INTEL_FAM6_HASWELL_ULT: /* HSW */
case INTEL_FAM6_HASWELL_GT3E: /* HSW */ case INTEL_FAM6_HASWELL_GT3E: /* HSW */
case INTEL_FAM6_BROADWELL_CORE: /* BDW */ case INTEL_FAM6_BROADWELL_CORE: /* BDW */
case INTEL_FAM6_BROADWELL_GT3E: /* BDW */ case INTEL_FAM6_BROADWELL_GT3E: /* BDW */
case INTEL_FAM6_BROADWELL_X: /* BDX */ case INTEL_FAM6_BROADWELL_X: /* BDX */
case INTEL_FAM6_BROADWELL_XEON_D: /* BDX-DE */
case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */ case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
case INTEL_FAM6_SKYLAKE_DESKTOP: /* SKL */
case INTEL_FAM6_KABYLAKE_MOBILE: /* KBL */
case INTEL_FAM6_KABYLAKE_DESKTOP: /* KBL */
case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */ case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
pkg_cstate_limits = hsw_pkg_cstate_limits; pkg_cstate_limits = hsw_pkg_cstate_limits;
has_misc_feature_control = 1; has_misc_feature_control = 1;
...@@ -3159,13 +3173,12 @@ int probe_nhm_msrs(unsigned int family, unsigned int model) ...@@ -3159,13 +3173,12 @@ int probe_nhm_msrs(unsigned int family, unsigned int model)
no_MSR_MISC_PWR_MGMT = 1; no_MSR_MISC_PWR_MGMT = 1;
break; break;
case INTEL_FAM6_XEON_PHI_KNL: /* PHI */ case INTEL_FAM6_XEON_PHI_KNL: /* PHI */
case INTEL_FAM6_XEON_PHI_KNM:
pkg_cstate_limits = phi_pkg_cstate_limits; pkg_cstate_limits = phi_pkg_cstate_limits;
break; break;
case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */ case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */
case INTEL_FAM6_ATOM_GOLDMONT_PLUS: case INTEL_FAM6_ATOM_GOLDMONT_PLUS:
case INTEL_FAM6_ATOM_GOLDMONT_X: /* DNV */ case INTEL_FAM6_ATOM_GOLDMONT_X: /* DNV */
pkg_cstate_limits = bxt_pkg_cstate_limits; pkg_cstate_limits = glm_pkg_cstate_limits;
break; break;
default: default:
return 0; return 0;
...@@ -3220,7 +3233,6 @@ int is_bdx(unsigned int family, unsigned int model) ...@@ -3220,7 +3233,6 @@ int is_bdx(unsigned int family, unsigned int model)
switch (model) { switch (model) {
case INTEL_FAM6_BROADWELL_X: case INTEL_FAM6_BROADWELL_X:
case INTEL_FAM6_BROADWELL_XEON_D:
return 1; return 1;
} }
return 0; return 0;
...@@ -3246,9 +3258,7 @@ int has_turbo_ratio_limit(unsigned int family, unsigned int model) ...@@ -3246,9 +3258,7 @@ int has_turbo_ratio_limit(unsigned int family, unsigned int model)
switch (model) { switch (model) {
/* Nehalem compatible, but do not include turbo-ratio limit support */ /* Nehalem compatible, but do not include turbo-ratio limit support */
case INTEL_FAM6_NEHALEM_EX: /* Nehalem-EX Xeon - Beckton */ case INTEL_FAM6_NEHALEM_EX: /* Nehalem-EX Xeon - Beckton */
case INTEL_FAM6_WESTMERE_EX: /* Westmere-EX Xeon - Eagleton */
case INTEL_FAM6_XEON_PHI_KNL: /* PHI - Knights Landing (different MSR definition) */ case INTEL_FAM6_XEON_PHI_KNL: /* PHI - Knights Landing (different MSR definition) */
case INTEL_FAM6_XEON_PHI_KNM:
return 0; return 0;
default: default:
return 1; return 1;
...@@ -3303,7 +3313,6 @@ int has_knl_turbo_ratio_limit(unsigned int family, unsigned int model) ...@@ -3303,7 +3313,6 @@ int has_knl_turbo_ratio_limit(unsigned int family, unsigned int model)
switch (model) { switch (model) {
case INTEL_FAM6_XEON_PHI_KNL: /* Knights Landing */ case INTEL_FAM6_XEON_PHI_KNL: /* Knights Landing */
case INTEL_FAM6_XEON_PHI_KNM:
return 1; return 1;
default: default:
return 0; return 0;
...@@ -3337,21 +3346,15 @@ int has_config_tdp(unsigned int family, unsigned int model) ...@@ -3337,21 +3346,15 @@ int has_config_tdp(unsigned int family, unsigned int model)
case INTEL_FAM6_IVYBRIDGE: /* IVB */ case INTEL_FAM6_IVYBRIDGE: /* IVB */
case INTEL_FAM6_HASWELL_CORE: /* HSW */ case INTEL_FAM6_HASWELL_CORE: /* HSW */
case INTEL_FAM6_HASWELL_X: /* HSX */ case INTEL_FAM6_HASWELL_X: /* HSX */
case INTEL_FAM6_HASWELL_ULT: /* HSW */
case INTEL_FAM6_HASWELL_GT3E: /* HSW */ case INTEL_FAM6_HASWELL_GT3E: /* HSW */
case INTEL_FAM6_BROADWELL_CORE: /* BDW */ case INTEL_FAM6_BROADWELL_CORE: /* BDW */
case INTEL_FAM6_BROADWELL_GT3E: /* BDW */ case INTEL_FAM6_BROADWELL_GT3E: /* BDW */
case INTEL_FAM6_BROADWELL_X: /* BDX */ case INTEL_FAM6_BROADWELL_X: /* BDX */
case INTEL_FAM6_BROADWELL_XEON_D: /* BDX-DE */
case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */ case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
case INTEL_FAM6_SKYLAKE_DESKTOP: /* SKL */
case INTEL_FAM6_KABYLAKE_MOBILE: /* KBL */
case INTEL_FAM6_KABYLAKE_DESKTOP: /* KBL */
case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */ case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
case INTEL_FAM6_SKYLAKE_X: /* SKX */ case INTEL_FAM6_SKYLAKE_X: /* SKX */
case INTEL_FAM6_XEON_PHI_KNL: /* Knights Landing */ case INTEL_FAM6_XEON_PHI_KNL: /* Knights Landing */
case INTEL_FAM6_XEON_PHI_KNM:
return 1; return 1;
default: default:
return 0; return 0;
...@@ -3744,9 +3747,7 @@ rapl_dram_energy_units_probe(int model, double rapl_energy_units) ...@@ -3744,9 +3747,7 @@ rapl_dram_energy_units_probe(int model, double rapl_energy_units)
switch (model) { switch (model) {
case INTEL_FAM6_HASWELL_X: /* HSX */ case INTEL_FAM6_HASWELL_X: /* HSX */
case INTEL_FAM6_BROADWELL_X: /* BDX */ case INTEL_FAM6_BROADWELL_X: /* BDX */
case INTEL_FAM6_BROADWELL_XEON_D: /* BDX-DE */
case INTEL_FAM6_XEON_PHI_KNL: /* KNL */ case INTEL_FAM6_XEON_PHI_KNL: /* KNL */
case INTEL_FAM6_XEON_PHI_KNM:
return (rapl_dram_energy_units = 15.3 / 1000000); return (rapl_dram_energy_units = 15.3 / 1000000);
default: default:
return (rapl_energy_units); return (rapl_energy_units);
...@@ -3775,7 +3776,6 @@ void rapl_probe(unsigned int family, unsigned int model) ...@@ -3775,7 +3776,6 @@ void rapl_probe(unsigned int family, unsigned int model)
case INTEL_FAM6_SANDYBRIDGE: case INTEL_FAM6_SANDYBRIDGE:
case INTEL_FAM6_IVYBRIDGE: case INTEL_FAM6_IVYBRIDGE:
case INTEL_FAM6_HASWELL_CORE: /* HSW */ case INTEL_FAM6_HASWELL_CORE: /* HSW */
case INTEL_FAM6_HASWELL_ULT: /* HSW */
case INTEL_FAM6_HASWELL_GT3E: /* HSW */ case INTEL_FAM6_HASWELL_GT3E: /* HSW */
case INTEL_FAM6_BROADWELL_CORE: /* BDW */ case INTEL_FAM6_BROADWELL_CORE: /* BDW */
case INTEL_FAM6_BROADWELL_GT3E: /* BDW */ case INTEL_FAM6_BROADWELL_GT3E: /* BDW */
...@@ -3799,9 +3799,6 @@ void rapl_probe(unsigned int family, unsigned int model) ...@@ -3799,9 +3799,6 @@ void rapl_probe(unsigned int family, unsigned int model)
BIC_PRESENT(BIC_PkgWatt); BIC_PRESENT(BIC_PkgWatt);
break; break;
case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */ case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
case INTEL_FAM6_SKYLAKE_DESKTOP: /* SKL */
case INTEL_FAM6_KABYLAKE_MOBILE: /* KBL */
case INTEL_FAM6_KABYLAKE_DESKTOP: /* KBL */
case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */ case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
do_rapl = RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS | RAPL_GFX | RAPL_PKG_POWER_INFO; do_rapl = RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS | RAPL_GFX | RAPL_PKG_POWER_INFO;
BIC_PRESENT(BIC_PKG__); BIC_PRESENT(BIC_PKG__);
...@@ -3820,10 +3817,8 @@ void rapl_probe(unsigned int family, unsigned int model) ...@@ -3820,10 +3817,8 @@ void rapl_probe(unsigned int family, unsigned int model)
break; break;
case INTEL_FAM6_HASWELL_X: /* HSX */ case INTEL_FAM6_HASWELL_X: /* HSX */
case INTEL_FAM6_BROADWELL_X: /* BDX */ case INTEL_FAM6_BROADWELL_X: /* BDX */
case INTEL_FAM6_BROADWELL_XEON_D: /* BDX-DE */
case INTEL_FAM6_SKYLAKE_X: /* SKX */ case INTEL_FAM6_SKYLAKE_X: /* SKX */
case INTEL_FAM6_XEON_PHI_KNL: /* KNL */ case INTEL_FAM6_XEON_PHI_KNL: /* KNL */
case INTEL_FAM6_XEON_PHI_KNM:
do_rapl = RAPL_PKG | RAPL_DRAM | RAPL_DRAM_POWER_INFO | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS | RAPL_PKG_POWER_INFO; do_rapl = RAPL_PKG | RAPL_DRAM | RAPL_DRAM_POWER_INFO | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS | RAPL_PKG_POWER_INFO;
BIC_PRESENT(BIC_PKG__); BIC_PRESENT(BIC_PKG__);
BIC_PRESENT(BIC_RAM__); BIC_PRESENT(BIC_RAM__);
...@@ -3916,7 +3911,6 @@ void perf_limit_reasons_probe(unsigned int family, unsigned int model) ...@@ -3916,7 +3911,6 @@ void perf_limit_reasons_probe(unsigned int family, unsigned int model)
switch (model) { switch (model) {
case INTEL_FAM6_HASWELL_CORE: /* HSW */ case INTEL_FAM6_HASWELL_CORE: /* HSW */
case INTEL_FAM6_HASWELL_ULT: /* HSW */
case INTEL_FAM6_HASWELL_GT3E: /* HSW */ case INTEL_FAM6_HASWELL_GT3E: /* HSW */
do_gfx_perf_limit_reasons = 1; do_gfx_perf_limit_reasons = 1;
case INTEL_FAM6_HASWELL_X: /* HSX */ case INTEL_FAM6_HASWELL_X: /* HSX */
...@@ -4128,16 +4122,11 @@ int has_snb_msrs(unsigned int family, unsigned int model) ...@@ -4128,16 +4122,11 @@ int has_snb_msrs(unsigned int family, unsigned int model)
case INTEL_FAM6_IVYBRIDGE_X: /* IVB Xeon */ case INTEL_FAM6_IVYBRIDGE_X: /* IVB Xeon */
case INTEL_FAM6_HASWELL_CORE: /* HSW */ case INTEL_FAM6_HASWELL_CORE: /* HSW */
case INTEL_FAM6_HASWELL_X: /* HSW */ case INTEL_FAM6_HASWELL_X: /* HSW */
case INTEL_FAM6_HASWELL_ULT: /* HSW */
case INTEL_FAM6_HASWELL_GT3E: /* HSW */ case INTEL_FAM6_HASWELL_GT3E: /* HSW */
case INTEL_FAM6_BROADWELL_CORE: /* BDW */ case INTEL_FAM6_BROADWELL_CORE: /* BDW */
case INTEL_FAM6_BROADWELL_GT3E: /* BDW */ case INTEL_FAM6_BROADWELL_GT3E: /* BDW */
case INTEL_FAM6_BROADWELL_X: /* BDX */ case INTEL_FAM6_BROADWELL_X: /* BDX */
case INTEL_FAM6_BROADWELL_XEON_D: /* BDX-DE */
case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */ case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
case INTEL_FAM6_SKYLAKE_DESKTOP: /* SKL */
case INTEL_FAM6_KABYLAKE_MOBILE: /* KBL */
case INTEL_FAM6_KABYLAKE_DESKTOP: /* KBL */
case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */ case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
case INTEL_FAM6_SKYLAKE_X: /* SKX */ case INTEL_FAM6_SKYLAKE_X: /* SKX */
case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */ case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */
...@@ -4166,12 +4155,9 @@ int has_hsw_msrs(unsigned int family, unsigned int model) ...@@ -4166,12 +4155,9 @@ int has_hsw_msrs(unsigned int family, unsigned int model)
return 0; return 0;
switch (model) { switch (model) {
case INTEL_FAM6_HASWELL_ULT: /* HSW */ case INTEL_FAM6_HASWELL_CORE:
case INTEL_FAM6_BROADWELL_CORE: /* BDW */ case INTEL_FAM6_BROADWELL_CORE: /* BDW */
case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */ case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
case INTEL_FAM6_SKYLAKE_DESKTOP: /* SKL */
case INTEL_FAM6_KABYLAKE_MOBILE: /* KBL */
case INTEL_FAM6_KABYLAKE_DESKTOP: /* KBL */
case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */ case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */ case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */
case INTEL_FAM6_ATOM_GOLDMONT_PLUS: case INTEL_FAM6_ATOM_GOLDMONT_PLUS:
...@@ -4195,9 +4181,6 @@ int has_skl_msrs(unsigned int family, unsigned int model) ...@@ -4195,9 +4181,6 @@ int has_skl_msrs(unsigned int family, unsigned int model)
switch (model) { switch (model) {
case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */ case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
case INTEL_FAM6_SKYLAKE_DESKTOP: /* SKL */
case INTEL_FAM6_KABYLAKE_MOBILE: /* KBL */
case INTEL_FAM6_KABYLAKE_DESKTOP: /* KBL */
case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */ case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
return 1; return 1;
} }
...@@ -4222,7 +4205,6 @@ int is_knl(unsigned int family, unsigned int model) ...@@ -4222,7 +4205,6 @@ int is_knl(unsigned int family, unsigned int model)
return 0; return 0;
switch (model) { switch (model) {
case INTEL_FAM6_XEON_PHI_KNL: /* KNL */ case INTEL_FAM6_XEON_PHI_KNL: /* KNL */
case INTEL_FAM6_XEON_PHI_KNM:
return 1; return 1;
} }
return 0; return 0;
...@@ -4436,18 +4418,56 @@ void decode_c6_demotion_policy_msr(void) ...@@ -4436,18 +4418,56 @@ void decode_c6_demotion_policy_msr(void)
base_cpu, msr, msr & (1 << 0) ? "EN" : "DIS"); base_cpu, msr, msr & (1 << 0) ? "EN" : "DIS");
} }
/*
* When models are the same, for the purpose of turbostat, reuse
*/
unsigned int intel_model_duplicates(unsigned int model)
{
switch(model) {
case INTEL_FAM6_NEHALEM_EP: /* Core i7, Xeon 5500 series - Bloomfield, Gainstown NHM-EP */
case INTEL_FAM6_NEHALEM: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper Forest */
case 0x1F: /* Core i7 and i5 Processor - Nehalem */
case INTEL_FAM6_WESTMERE: /* Westmere Client - Clarkdale, Arrandale */
case INTEL_FAM6_WESTMERE_EP: /* Westmere EP - Gulftown */
return INTEL_FAM6_NEHALEM;
case INTEL_FAM6_NEHALEM_EX: /* Nehalem-EX Xeon - Beckton */
case INTEL_FAM6_WESTMERE_EX: /* Westmere-EX Xeon - Eagleton */
return INTEL_FAM6_NEHALEM_EX;
case INTEL_FAM6_XEON_PHI_KNM:
return INTEL_FAM6_XEON_PHI_KNL;
case INTEL_FAM6_HASWELL_ULT:
return INTEL_FAM6_HASWELL_CORE;
case INTEL_FAM6_BROADWELL_X:
case INTEL_FAM6_BROADWELL_XEON_D: /* BDX-DE */
return INTEL_FAM6_BROADWELL_X;
case INTEL_FAM6_SKYLAKE_MOBILE:
case INTEL_FAM6_SKYLAKE_DESKTOP:
case INTEL_FAM6_KABYLAKE_MOBILE:
case INTEL_FAM6_KABYLAKE_DESKTOP:
return INTEL_FAM6_SKYLAKE_MOBILE;
}
return model;
}
void process_cpuid() void process_cpuid()
{ {
unsigned int eax, ebx, ecx, edx, max_level, max_extended_level; unsigned int eax, ebx, ecx, edx;
unsigned int fms, family, model, stepping; unsigned int fms, family, model, stepping, ecx_flags, edx_flags;
unsigned int has_turbo; unsigned int has_turbo;
eax = ebx = ecx = edx = 0; eax = ebx = ecx = edx = 0;
__cpuid(0, max_level, ebx, ecx, edx); __cpuid(0, max_level, ebx, ecx, edx);
if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e) if (ebx == 0x756e6547 && ecx == 0x6c65746e && edx == 0x49656e69)
genuine_intel = 1; genuine_intel = 1;
else if (ebx == 0x68747541 && ecx == 0x444d4163 && edx == 0x69746e65)
authentic_amd = 1;
if (!quiet) if (!quiet)
fprintf(outf, "CPUID(0): %.4s%.4s%.4s ", fprintf(outf, "CPUID(0): %.4s%.4s%.4s ",
...@@ -4461,25 +4481,8 @@ void process_cpuid() ...@@ -4461,25 +4481,8 @@ void process_cpuid()
family += (fms >> 20) & 0xff; family += (fms >> 20) & 0xff;
if (family >= 6) if (family >= 6)
model += ((fms >> 16) & 0xf) << 4; model += ((fms >> 16) & 0xf) << 4;
ecx_flags = ecx;
if (!quiet) { edx_flags = edx;
fprintf(outf, "%d CPUID levels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n",
max_level, family, model, stepping, family, model, stepping);
fprintf(outf, "CPUID(1): %s %s %s %s %s %s %s %s %s %s\n",
ecx & (1 << 0) ? "SSE3" : "-",
ecx & (1 << 3) ? "MONITOR" : "-",
ecx & (1 << 6) ? "SMX" : "-",
ecx & (1 << 7) ? "EIST" : "-",
ecx & (1 << 8) ? "TM2" : "-",
edx & (1 << 4) ? "TSC" : "-",
edx & (1 << 5) ? "MSR" : "-",
edx & (1 << 22) ? "ACPI-TM" : "-",
edx & (1 << 28) ? "HT" : "-",
edx & (1 << 29) ? "TM" : "-");
}
if (!(edx & (1 << 5)))
errx(1, "CPUID: no MSR");
/* /*
* check max extended function levels of CPUID. * check max extended function levels of CPUID.
...@@ -4489,6 +4492,27 @@ void process_cpuid() ...@@ -4489,6 +4492,27 @@ void process_cpuid()
ebx = ecx = edx = 0; ebx = ecx = edx = 0;
__cpuid(0x80000000, max_extended_level, ebx, ecx, edx); __cpuid(0x80000000, max_extended_level, ebx, ecx, edx);
if (!quiet) {
fprintf(outf, "0x%x CPUID levels; 0x%x xlevels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n",
max_level, max_extended_level, family, model, stepping, family, model, stepping);
fprintf(outf, "CPUID(1): %s %s %s %s %s %s %s %s %s %s\n",
ecx_flags & (1 << 0) ? "SSE3" : "-",
ecx_flags & (1 << 3) ? "MONITOR" : "-",
ecx_flags & (1 << 6) ? "SMX" : "-",
ecx_flags & (1 << 7) ? "EIST" : "-",
ecx_flags & (1 << 8) ? "TM2" : "-",
edx_flags & (1 << 4) ? "TSC" : "-",
edx_flags & (1 << 5) ? "MSR" : "-",
edx_flags & (1 << 22) ? "ACPI-TM" : "-",
edx_flags & (1 << 28) ? "HT" : "-",
edx_flags & (1 << 29) ? "TM" : "-");
}
if (genuine_intel)
model = intel_model_duplicates(model);
if (!(edx_flags & (1 << 5)))
errx(1, "CPUID: no MSR");
if (max_extended_level >= 0x80000007) { if (max_extended_level >= 0x80000007) {
/* /*
...@@ -4576,9 +4600,6 @@ void process_cpuid() ...@@ -4576,9 +4600,6 @@ void process_cpuid()
if (crystal_hz == 0) if (crystal_hz == 0)
switch(model) { switch(model) {
case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */ case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
case INTEL_FAM6_SKYLAKE_DESKTOP: /* SKL */
case INTEL_FAM6_KABYLAKE_MOBILE: /* KBL */
case INTEL_FAM6_KABYLAKE_DESKTOP: /* KBL */
crystal_hz = 24000000; /* 24.0 MHz */ crystal_hz = 24000000; /* 24.0 MHz */
break; break;
case INTEL_FAM6_ATOM_GOLDMONT_X: /* DNV */ case INTEL_FAM6_ATOM_GOLDMONT_X: /* DNV */
...@@ -4860,6 +4881,8 @@ void topology_probe() ...@@ -4860,6 +4881,8 @@ void topology_probe()
return; return;
for (i = 0; i <= topo.max_cpu_num; ++i) { for (i = 0; i <= topo.max_cpu_num; ++i) {
if (cpu_is_not_present(i))
continue;
fprintf(outf, fprintf(outf,
"cpu %d pkg %d node %d lnode %d core %d thread %d\n", "cpu %d pkg %d node %d lnode %d core %d thread %d\n",
i, cpus[i].physical_package_id, i, cpus[i].physical_package_id,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册