mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'pm-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki:
"These are dominated by cpufreq updates which in turn are dominated by
updates related to boost support in the core and drivers and
amd-pstate driver optimizations.
Apart from the above, there are some cpuidle updates including a
rework of the most recent idle intervals handling in the venerable
menu governor that leads to significant improvements in some
performance benchmarks, as the governor is now more likely to predict
a shorter idle duration in some cases, and there are updates of the
core device power management code, mostly related to system suspend
and resume, that should help to avoid potential issues arising when
the drivers of devices depending on one another want to use different
optimizations.
There is also a usual collection of assorted fixes and cleanups,
including removal of some unused code.
Specifics:
- Manage sysfs attributes and boost frequencies efficiently from
cpufreq core to reduce boilerplate code in drivers (Viresh Kumar)
- Minor cleanups to cpufreq drivers (Aaron Kling, Benjamin Schneider,
Dhananjay Ugwekar, Imran Shaik, zuoqian)
- Migrate some cpufreq drivers to using for_each_present_cpu() (Jacky
Bai)
- cpufreq-qcom-hw DT binding fixes (Krzysztof Kozlowski)
- Use str_enable_disable() helper in cpufreq_online() (Lifeng Zheng)
- Optimize the amd-pstate driver to avoid cases where call paths end
up calling the same writes multiple times and needlessly caching
variables through code reorganization, locking overhaul and tracing
adjustments (Mario Limonciello, Dhananjay Ugwekar)
- Make it possible to avoid enabling capacity-aware scheduling (CAS)
in the intel_pstate driver and relocate a check for out-of-band
(OOB) platform handling in it to make it detect OOB before checking
HWP availability (Rafael Wysocki)
- Fix dbs_update() to avoid inadvertent conversions of negative
integer values to unsigned int which causes CPU frequency selection
to be inaccurate in some cases when the "conservative" cpufreq
governor is in use (Jie Zhan)
- Update the handling of the most recent idle intervals in the menu
cpuidle governor to prevent useful information from being discarded
by it in some cases and improve the prediction accuracy (Rafael
Wysocki)
- Make it possible to tell the intel_idle driver to ignore its
built-in table of idle states for the given processor, clean up the
handling of auto-demotion disabling on Baytrail and Cherrytrail
chips in it, and update its MAINTAINERS entry (David Arcari, Artem
Bityutskiy, Rafael Wysocki)
- Make some cpuidle drivers use for_each_present_cpu() instead of
for_each_possible_cpu() during initialization to avoid issues
occurring when nosmp or maxcpus=0 are used (Jacky Bai)
- Clean up the Energy Model handling code somewhat (Rafael Wysocki)
- Use kfree_rcu() to simplify the handling of runtime Energy Model
updates (Li RongQing)
- Add an entry for the Energy Model framework to MAINTAINERS as
properly maintained (Lukasz Luba)
- Address RCU-related sparse warnings in the Energy Model code
(Rafael Wysocki)
- Remove ENERGY_MODEL dependency on SMP and allow it to be selected
when DEVFREQ is set without CPUFREQ so it can be used on a wider
range of systems (Jeson Gao)
- Unify error handling during runtime suspend and runtime resume in
the core to help drivers to implement more consistent runtime PM
error handling (Rafael Wysocki)
- Drop a redundant check from pm_runtime_force_resume() and rearrange
documentation related to __pm_runtime_disable() (Rafael Wysocki)
- Rework the handling of the "smart suspend" driver flag in the PM
core to avoid issues hat may occur when drivers using it depend on
some other drivers and clean up the related PM core code (Rafael
Wysocki, Colin Ian King)
- Fix the handling of devices with the power.direct_complete flag set
if device_suspend() returns an error for at least one device to
avoid situations in which some of them may not be resumed (Rafael
Wysocki)
- Use mutex_trylock() in hibernate_compressor_param_set() to avoid a
possible deadlock that may occur if the "compressor" hibernation
module parameter is accessed during the registration of a new
ieee80211 device (Lizhi Xu)
- Suppress sleeping parent warning in device_pm_add() in the case
when new children are added under a device with the
power.direct_complete set after it has been processed by
device_resume() (Xu Yang)
- Remove needless return in three void functions related to system
wakeup (Zijun Hu)
- Replace deprecated kmap_atomic() with kmap_local_page() in the
hibernation core code (David Reaver)
- Remove unused helper functions related to system sleep (David Alan
Gilbert)
- Clean up s2idle_enter() so it does not lock and unlock CPU offline
in vain and update comments in it (Ulf Hansson)
- Clean up broken white space in dpm_wait_for_children() (Geert
Uytterhoeven)
- Update the cpupower utility to fix lib version-ing in it and memory
leaks in error legs, remove hard-coded values, and implement CPU
physical core querying (Thomas Renninger, John B. Wyatt IV, Shuah
Khan, Yiwei Lin, Zhongqiu Han)"
* tag 'pm-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (139 commits)
PM: sleep: Fix bit masking operation
dt-bindings: cpufreq: cpufreq-qcom-hw: Narrow properties on SDX75, SA8775p and SM8650
dt-bindings: cpufreq: cpufreq-qcom-hw: Drop redundant minItems:1
dt-bindings: cpufreq: cpufreq-qcom-hw: Add missing constraint for interrupt-names
dt-bindings: cpufreq: cpufreq-qcom-hw: Add QCS8300 compatible
cpufreq: Init cpufreq only for present CPUs
PM: sleep: Fix handling devices with direct_complete set on errors
cpuidle: Init cpuidle only for present CPUs
PM: clk: Remove unused pm_clk_remove()
PM: sleep: core: Fix indentation in dpm_wait_for_children()
PM: s2idle: Extend comment in s2idle_enter()
PM: s2idle: Drop redundant locks when entering s2idle
PM: sleep: Remove unused pm_generic_ wrappers
cpufreq: tegra186: Share policy per cluster
cpupower: Make lib versioning scheme more obvious and fix version link
PM: EM: Rework the depends on for CONFIG_ENERGY_MODEL
PM: EM: Address RCU-related sparse warnings
cpupower: Implement CPU physical core querying
pm: cpupower: remove hard-coded topology depth values
pm: cpupower: Fix cmd_monitor() error legs to free cpu_topology
...
This commit is contained in:
@@ -2314,6 +2314,9 @@
|
||||
per_cpu_perf_limits
|
||||
Allow per-logical-CPU P-State performance control limits using
|
||||
cpufreq sysfs interface
|
||||
no_cas
|
||||
Do not enable capacity-aware scheduling (CAS) on
|
||||
hybrid systems
|
||||
|
||||
intremap= [X86-64,Intel-IOMMU,EARLY]
|
||||
on enable Interrupt Remapping (default)
|
||||
|
||||
@@ -275,20 +275,25 @@ values and, when predicting the idle duration next time, it computes the average
|
||||
and variance of them. If the variance is small (smaller than 400 square
|
||||
milliseconds) or it is small relative to the average (the average is greater
|
||||
that 6 times the standard deviation), the average is regarded as the "typical
|
||||
interval" value. Otherwise, the longest of the saved observed idle duration
|
||||
interval" value. Otherwise, either the longest or the shortest (depending on
|
||||
which one is farther from the average) of the saved observed idle duration
|
||||
values is discarded and the computation is repeated for the remaining ones.
|
||||
|
||||
Again, if the variance of them is small (in the above sense), the average is
|
||||
taken as the "typical interval" value and so on, until either the "typical
|
||||
interval" is determined or too many data points are disregarded, in which case
|
||||
the "typical interval" is assumed to equal "infinity" (the maximum unsigned
|
||||
integer value).
|
||||
interval" is determined or too many data points are disregarded. In the latter
|
||||
case, if the size of the set of data points still under consideration is
|
||||
sufficiently large, the next idle duration is not likely to be above the largest
|
||||
idle duration value still in that set, so that value is taken as the predicted
|
||||
next idle duration. Finally, if the set of data points still under
|
||||
consideration is too small, no prediction is made.
|
||||
|
||||
If the "typical interval" computed this way is long enough, the governor obtains
|
||||
the time until the closest timer event with the assumption that the scheduler
|
||||
tick will be stopped. That time, referred to as the *sleep length* in what follows,
|
||||
is the upper bound on the time before the next CPU wakeup. It is used to determine
|
||||
the sleep length range, which in turn is needed to get the sleep length correction
|
||||
factor.
|
||||
If the preliminary prediction of the next idle duration computed this way is
|
||||
long enough, the governor obtains the time until the closest timer event with
|
||||
the assumption that the scheduler tick will be stopped. That time, referred to
|
||||
as the *sleep length* in what follows, is the upper bound on the time before the
|
||||
next CPU wakeup. It is used to determine the sleep length range, which in turn
|
||||
is needed to get the sleep length correction factor.
|
||||
|
||||
The ``menu`` governor maintains an array containing several correction factor
|
||||
values that correspond to different sleep length ranges organized so that each
|
||||
@@ -302,7 +307,7 @@ to 1 the correction factor becomes (it must fall between 0 and 1 inclusive).
|
||||
The sleep length is multiplied by the correction factor for the range that it
|
||||
falls into to obtain an approximation of the predicted idle duration that is
|
||||
compared to the "typical interval" determined previously and the minimum of
|
||||
the two is taken as the idle duration prediction.
|
||||
the two is taken as the final idle duration prediction.
|
||||
|
||||
If the "typical interval" value is small, which means that the CPU is likely
|
||||
to be woken up soon enough, the sleep length computation is skipped as it may
|
||||
|
||||
@@ -192,11 +192,19 @@ even if they have been enumerated (see :ref:`cpu-pm-qos` in
|
||||
Documentation/admin-guide/pm/cpuidle.rst).
|
||||
Setting ``max_cstate`` to 0 causes the ``intel_idle`` initialization to fail.
|
||||
|
||||
The ``no_acpi`` and ``use_acpi`` module parameters (recognized by ``intel_idle``
|
||||
if the kernel has been configured with ACPI support) can be set to make the
|
||||
driver ignore the system's ACPI tables entirely or use them for all of the
|
||||
recognized processor models, respectively (they both are unset by default and
|
||||
``use_acpi`` has no effect if ``no_acpi`` is set).
|
||||
The ``no_acpi``, ``use_acpi`` and ``no_native`` module parameters are
|
||||
recognized by ``intel_idle`` if the kernel has been configured with ACPI
|
||||
support. In the case that ACPI is not configured these flags have no impact
|
||||
on functionality.
|
||||
|
||||
``no_acpi`` - Do not use ACPI at all. Only native mode is available, no
|
||||
ACPI mode.
|
||||
|
||||
``use_acpi`` - No-op in ACPI mode, the driver will consult ACPI tables for
|
||||
C-states on/off status in native mode.
|
||||
|
||||
``no_native`` - Work only in ACPI mode, no native mode available (ignore
|
||||
all custom tables).
|
||||
|
||||
The value of the ``states_off`` module parameter (0 by default) represents a
|
||||
list of idle states to be disabled by default in the form of a bitmask.
|
||||
|
||||
@@ -696,6 +696,9 @@ of them have to be prepended with the ``intel_pstate=`` prefix.
|
||||
Use per-logical-CPU P-State limits (see `Coordination of P-state
|
||||
Limits`_ for details).
|
||||
|
||||
``no_cas``
|
||||
Do not enable capacity-aware scheduling (CAS) which is enabled by
|
||||
default on hybrid systems.
|
||||
|
||||
Diagnostics and Tuning
|
||||
======================
|
||||
|
||||
@@ -34,6 +34,7 @@ properties:
|
||||
- description: v2 of CPUFREQ HW (EPSS)
|
||||
items:
|
||||
- enum:
|
||||
- qcom,qcs8300-cpufreq-epss
|
||||
- qcom,qdu1000-cpufreq-epss
|
||||
- qcom,sa8255p-cpufreq-epss
|
||||
- qcom,sa8775p-cpufreq-epss
|
||||
@@ -111,22 +112,20 @@ allOf:
|
||||
enum:
|
||||
- qcom,qcm2290-cpufreq-hw
|
||||
- qcom,sar2130p-cpufreq-epss
|
||||
- qcom,sdx75-cpufreq-epss
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
|
||||
reg-names:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
|
||||
- if:
|
||||
properties:
|
||||
@@ -135,6 +134,7 @@ allOf:
|
||||
enum:
|
||||
- qcom,qdu1000-cpufreq-epss
|
||||
- qcom,sa8255p-cpufreq-epss
|
||||
- qcom,sa8775p-cpufreq-epss
|
||||
- qcom,sc7180-cpufreq-hw
|
||||
- qcom,sc8180x-cpufreq-hw
|
||||
- qcom,sc8280xp-cpufreq-epss
|
||||
@@ -160,12 +160,14 @@ allOf:
|
||||
|
||||
interrupt-names:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,qcs8300-cpufreq-epss
|
||||
- qcom,sc7280-cpufreq-epss
|
||||
- qcom,sm8250-cpufreq-epss
|
||||
- qcom,sm8350-cpufreq-epss
|
||||
@@ -187,6 +189,7 @@ allOf:
|
||||
|
||||
interrupt-names:
|
||||
minItems: 3
|
||||
maxItems: 3
|
||||
|
||||
- if:
|
||||
properties:
|
||||
@@ -211,7 +214,31 @@ allOf:
|
||||
|
||||
interrupt-names:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sm8650-cpufreq-epss
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
|
||||
reg-names:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
|
||||
interrupts:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
|
||||
interrupt-names:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
||||
+14
-3
@@ -8541,6 +8541,15 @@ M: Maxim Levitsky <maximlevitsky@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/media/rc/ene_ir.*
|
||||
|
||||
ENERGY MODEL
|
||||
M: Lukasz Luba <lukasz.luba@arm.com>
|
||||
M: "Rafael J. Wysocki" <rafael@kernel.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: kernel/power/energy_model.c
|
||||
F: include/linux/energy_model.h
|
||||
F: Documentation/power/energy-model.rst
|
||||
|
||||
EPAPR HYPERVISOR BYTE CHANNEL DEVICE DRIVER
|
||||
M: Laurentiu Tudor <laurentiu.tudor@nxp.com>
|
||||
L: linuxppc-dev@lists.ozlabs.org
|
||||
@@ -11690,12 +11699,14 @@ F: Documentation/driver-api/crypto/iaa/iaa-crypto.rst
|
||||
F: drivers/crypto/intel/iaa/*
|
||||
|
||||
INTEL IDLE DRIVER
|
||||
M: Jacob Pan <jacob.jun.pan@linux.intel.com>
|
||||
M: Len Brown <lenb@kernel.org>
|
||||
M: Rafael J. Wysocki <rafael@kernel.org>
|
||||
M: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
|
||||
M: Artem Bityutskiy <dedekind1@gmail.com>
|
||||
R: Len Brown <lenb@kernel.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Supported
|
||||
B: https://bugzilla.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
|
||||
F: drivers/idle/intel_idle.c
|
||||
|
||||
INTEL IDXD DRIVER
|
||||
|
||||
@@ -703,15 +703,17 @@
|
||||
#define MSR_AMD_CPPC_REQ 0xc00102b3
|
||||
#define MSR_AMD_CPPC_STATUS 0xc00102b4
|
||||
|
||||
#define AMD_CPPC_LOWEST_PERF(x) (((x) >> 0) & 0xff)
|
||||
#define AMD_CPPC_LOWNONLIN_PERF(x) (((x) >> 8) & 0xff)
|
||||
#define AMD_CPPC_NOMINAL_PERF(x) (((x) >> 16) & 0xff)
|
||||
#define AMD_CPPC_HIGHEST_PERF(x) (((x) >> 24) & 0xff)
|
||||
/* Masks for use with MSR_AMD_CPPC_CAP1 */
|
||||
#define AMD_CPPC_LOWEST_PERF_MASK GENMASK(7, 0)
|
||||
#define AMD_CPPC_LOWNONLIN_PERF_MASK GENMASK(15, 8)
|
||||
#define AMD_CPPC_NOMINAL_PERF_MASK GENMASK(23, 16)
|
||||
#define AMD_CPPC_HIGHEST_PERF_MASK GENMASK(31, 24)
|
||||
|
||||
#define AMD_CPPC_MAX_PERF(x) (((x) & 0xff) << 0)
|
||||
#define AMD_CPPC_MIN_PERF(x) (((x) & 0xff) << 8)
|
||||
#define AMD_CPPC_DES_PERF(x) (((x) & 0xff) << 16)
|
||||
#define AMD_CPPC_ENERGY_PERF_PREF(x) (((x) & 0xff) << 24)
|
||||
/* Masks for use with MSR_AMD_CPPC_REQ */
|
||||
#define AMD_CPPC_MAX_PERF_MASK GENMASK(7, 0)
|
||||
#define AMD_CPPC_MIN_PERF_MASK GENMASK(15, 8)
|
||||
#define AMD_CPPC_DES_PERF_MASK GENMASK(23, 16)
|
||||
#define AMD_CPPC_EPP_PERF_MASK GENMASK(31, 24)
|
||||
|
||||
/* AMD Performance Counter Global Status and Control MSRs */
|
||||
#define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS 0xc0000300
|
||||
|
||||
@@ -151,7 +151,7 @@ int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
val = AMD_CPPC_HIGHEST_PERF(val);
|
||||
val = FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, val);
|
||||
} else {
|
||||
ret = cppc_get_highest_perf(cpu, &val);
|
||||
if (ret)
|
||||
|
||||
@@ -1161,7 +1161,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_complete);
|
||||
*/
|
||||
int acpi_subsys_suspend(struct device *dev)
|
||||
{
|
||||
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
|
||||
if (!dev_pm_smart_suspend(dev) ||
|
||||
acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
@@ -1320,7 +1320,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_restore_early);
|
||||
*/
|
||||
int acpi_subsys_poweroff(struct device *dev)
|
||||
{
|
||||
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
|
||||
if (!dev_pm_smart_suspend(dev) ||
|
||||
acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
|
||||
@@ -259,39 +259,6 @@ int pm_clk_add_clk(struct device *dev, struct clk *clk)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_clk_add_clk);
|
||||
|
||||
|
||||
/**
|
||||
* of_pm_clk_add_clk - Start using a device clock for power management.
|
||||
* @dev: Device whose clock is going to be used for power management.
|
||||
* @name: Name of clock that is going to be used for power management.
|
||||
*
|
||||
* Add the clock described in the 'clocks' device-tree node that matches
|
||||
* with the 'name' provided, to the list of clocks used for the power
|
||||
* management of @dev. On success, returns 0. Returns a negative error
|
||||
* code if the clock is not found or cannot be added.
|
||||
*/
|
||||
int of_pm_clk_add_clk(struct device *dev, const char *name)
|
||||
{
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
if (!dev || !dev->of_node || !name)
|
||||
return -EINVAL;
|
||||
|
||||
clk = of_clk_get_by_name(dev->of_node, name);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = pm_clk_add_clk(dev, clk);
|
||||
if (ret) {
|
||||
clk_put(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_pm_clk_add_clk);
|
||||
|
||||
/**
|
||||
* of_pm_clk_add_clks - Start using device clock(s) for power management.
|
||||
* @dev: Device whose clock(s) is going to be used for power management.
|
||||
@@ -376,46 +343,6 @@ static void __pm_clk_remove(struct pm_clock_entry *ce)
|
||||
kfree(ce);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_clk_remove - Stop using a device clock for power management.
|
||||
* @dev: Device whose clock should not be used for PM any more.
|
||||
* @con_id: Connection ID of the clock.
|
||||
*
|
||||
* Remove the clock represented by @con_id from the list of clocks used for
|
||||
* the power management of @dev.
|
||||
*/
|
||||
void pm_clk_remove(struct device *dev, const char *con_id)
|
||||
{
|
||||
struct pm_subsys_data *psd = dev_to_psd(dev);
|
||||
struct pm_clock_entry *ce;
|
||||
|
||||
if (!psd)
|
||||
return;
|
||||
|
||||
pm_clk_list_lock(psd);
|
||||
|
||||
list_for_each_entry(ce, &psd->clock_list, node) {
|
||||
if (!con_id && !ce->con_id)
|
||||
goto remove;
|
||||
else if (!con_id || !ce->con_id)
|
||||
continue;
|
||||
else if (!strcmp(con_id, ce->con_id))
|
||||
goto remove;
|
||||
}
|
||||
|
||||
pm_clk_list_unlock(psd);
|
||||
return;
|
||||
|
||||
remove:
|
||||
list_del(&ce->node);
|
||||
if (ce->enabled_when_prepared)
|
||||
psd->clock_op_might_sleep--;
|
||||
pm_clk_list_unlock(psd);
|
||||
|
||||
__pm_clk_remove(ce);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_clk_remove);
|
||||
|
||||
/**
|
||||
* pm_clk_remove_clk - Stop using a device clock for power management.
|
||||
* @dev: Device whose clock should not be used for PM any more.
|
||||
|
||||
@@ -114,18 +114,6 @@ int pm_generic_freeze_noirq(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
|
||||
|
||||
/**
|
||||
* pm_generic_freeze_late - Generic freeze_late callback for subsystems.
|
||||
* @dev: Device to freeze.
|
||||
*/
|
||||
int pm_generic_freeze_late(struct device *dev)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->freeze_late ? pm->freeze_late(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_freeze_late);
|
||||
|
||||
/**
|
||||
* pm_generic_freeze - Generic freeze callback for subsystems.
|
||||
* @dev: Device to freeze.
|
||||
@@ -186,18 +174,6 @@ int pm_generic_thaw_noirq(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
|
||||
|
||||
/**
|
||||
* pm_generic_thaw_early - Generic thaw_early callback for subsystems.
|
||||
* @dev: Device to thaw.
|
||||
*/
|
||||
int pm_generic_thaw_early(struct device *dev)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->thaw_early ? pm->thaw_early(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_thaw_early);
|
||||
|
||||
/**
|
||||
* pm_generic_thaw - Generic thaw callback for subsystems.
|
||||
* @dev: Device to thaw.
|
||||
|
||||
+120
-45
@@ -249,7 +249,7 @@ static int dpm_wait_fn(struct device *dev, void *async_ptr)
|
||||
|
||||
static void dpm_wait_for_children(struct device *dev, bool async)
|
||||
{
|
||||
device_for_each_child(dev, &async, dpm_wait_fn);
|
||||
device_for_each_child(dev, &async, dpm_wait_fn);
|
||||
}
|
||||
|
||||
static void dpm_wait_for_suppliers(struct device *dev, bool async)
|
||||
@@ -599,27 +599,34 @@ static bool is_async(struct device *dev)
|
||||
|
||||
static bool dpm_async_fn(struct device *dev, async_func_t func)
|
||||
{
|
||||
reinit_completion(&dev->power.completion);
|
||||
if (!is_async(dev))
|
||||
return false;
|
||||
|
||||
if (is_async(dev)) {
|
||||
dev->power.async_in_progress = true;
|
||||
dev->power.work_in_progress = true;
|
||||
|
||||
get_device(dev);
|
||||
get_device(dev);
|
||||
|
||||
if (async_schedule_dev_nocall(func, dev))
|
||||
return true;
|
||||
if (async_schedule_dev_nocall(func, dev))
|
||||
return true;
|
||||
|
||||
put_device(dev);
|
||||
|
||||
put_device(dev);
|
||||
}
|
||||
/*
|
||||
* Because async_schedule_dev_nocall() above has returned false or it
|
||||
* has not been called at all, func() is not running and it is safe to
|
||||
* update the async_in_progress flag without extra synchronization.
|
||||
* async_schedule_dev_nocall() above has returned false, so func() is
|
||||
* not running and it is safe to update power.work_in_progress without
|
||||
* extra synchronization.
|
||||
*/
|
||||
dev->power.async_in_progress = false;
|
||||
dev->power.work_in_progress = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dpm_clear_async_state(struct device *dev)
|
||||
{
|
||||
reinit_completion(&dev->power.completion);
|
||||
dev->power.work_in_progress = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_resume_noirq - Execute a "noirq resume" callback for given device.
|
||||
* @dev: Device to handle.
|
||||
@@ -656,15 +663,13 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy
|
||||
* so change its status accordingly.
|
||||
*
|
||||
* Otherwise, the device is going to be resumed, so set its PM-runtime
|
||||
* status to "active" unless its power.set_active flag is clear, in
|
||||
* status to "active" unless its power.smart_suspend flag is clear, in
|
||||
* which case it is not necessary to update its PM-runtime status.
|
||||
*/
|
||||
if (skip_resume) {
|
||||
if (skip_resume)
|
||||
pm_runtime_set_suspended(dev);
|
||||
} else if (dev->power.set_active) {
|
||||
else if (dev_pm_smart_suspend(dev))
|
||||
pm_runtime_set_active(dev);
|
||||
dev->power.set_active = false;
|
||||
}
|
||||
|
||||
if (dev->pm_domain) {
|
||||
info = "noirq power domain ";
|
||||
@@ -731,14 +736,16 @@ static void dpm_noirq_resume_devices(pm_message_t state)
|
||||
* Trigger the resume of "async" devices upfront so they don't have to
|
||||
* wait for the "non-async" ones they don't depend on.
|
||||
*/
|
||||
list_for_each_entry(dev, &dpm_noirq_list, power.entry)
|
||||
list_for_each_entry(dev, &dpm_noirq_list, power.entry) {
|
||||
dpm_clear_async_state(dev);
|
||||
dpm_async_fn(dev, async_resume_noirq);
|
||||
}
|
||||
|
||||
while (!list_empty(&dpm_noirq_list)) {
|
||||
dev = to_device(dpm_noirq_list.next);
|
||||
list_move_tail(&dev->power.entry, &dpm_late_early_list);
|
||||
|
||||
if (!dev->power.async_in_progress) {
|
||||
if (!dev->power.work_in_progress) {
|
||||
get_device(dev);
|
||||
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
@@ -871,14 +878,16 @@ void dpm_resume_early(pm_message_t state)
|
||||
* Trigger the resume of "async" devices upfront so they don't have to
|
||||
* wait for the "non-async" ones they don't depend on.
|
||||
*/
|
||||
list_for_each_entry(dev, &dpm_late_early_list, power.entry)
|
||||
list_for_each_entry(dev, &dpm_late_early_list, power.entry) {
|
||||
dpm_clear_async_state(dev);
|
||||
dpm_async_fn(dev, async_resume_early);
|
||||
}
|
||||
|
||||
while (!list_empty(&dpm_late_early_list)) {
|
||||
dev = to_device(dpm_late_early_list.next);
|
||||
list_move_tail(&dev->power.entry, &dpm_suspended_list);
|
||||
|
||||
if (!dev->power.async_in_progress) {
|
||||
if (!dev->power.work_in_progress) {
|
||||
get_device(dev);
|
||||
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
@@ -929,7 +938,17 @@ static void device_resume(struct device *dev, pm_message_t state, bool async)
|
||||
if (dev->power.syscore)
|
||||
goto Complete;
|
||||
|
||||
if (!dev->power.is_suspended)
|
||||
goto Complete;
|
||||
|
||||
if (dev->power.direct_complete) {
|
||||
/*
|
||||
* Allow new children to be added under the device after this
|
||||
* point if it has no PM callbacks.
|
||||
*/
|
||||
if (dev->power.no_pm_callbacks)
|
||||
dev->power.is_prepared = false;
|
||||
|
||||
/* Match the pm_runtime_disable() in device_suspend(). */
|
||||
pm_runtime_enable(dev);
|
||||
goto Complete;
|
||||
@@ -947,9 +966,6 @@ static void device_resume(struct device *dev, pm_message_t state, bool async)
|
||||
*/
|
||||
dev->power.is_prepared = false;
|
||||
|
||||
if (!dev->power.is_suspended)
|
||||
goto Unlock;
|
||||
|
||||
if (dev->pm_domain) {
|
||||
info = "power domain ";
|
||||
callback = pm_op(&dev->pm_domain->ops, state);
|
||||
@@ -989,7 +1005,6 @@ static void device_resume(struct device *dev, pm_message_t state, bool async)
|
||||
error = dpm_run_callback(callback, dev, state, info);
|
||||
dev->power.is_suspended = false;
|
||||
|
||||
Unlock:
|
||||
device_unlock(dev);
|
||||
dpm_watchdog_clear(&wd);
|
||||
|
||||
@@ -1037,14 +1052,16 @@ void dpm_resume(pm_message_t state)
|
||||
* Trigger the resume of "async" devices upfront so they don't have to
|
||||
* wait for the "non-async" ones they don't depend on.
|
||||
*/
|
||||
list_for_each_entry(dev, &dpm_suspended_list, power.entry)
|
||||
list_for_each_entry(dev, &dpm_suspended_list, power.entry) {
|
||||
dpm_clear_async_state(dev);
|
||||
dpm_async_fn(dev, async_resume);
|
||||
}
|
||||
|
||||
while (!list_empty(&dpm_suspended_list)) {
|
||||
dev = to_device(dpm_suspended_list.next);
|
||||
list_move_tail(&dev->power.entry, &dpm_prepared_list);
|
||||
|
||||
if (!dev->power.async_in_progress) {
|
||||
if (!dev->power.work_in_progress) {
|
||||
get_device(dev);
|
||||
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
@@ -1109,6 +1126,8 @@ static void device_complete(struct device *dev, pm_message_t state)
|
||||
device_unlock(dev);
|
||||
|
||||
out:
|
||||
/* If enabling runtime PM for the device is blocked, unblock it. */
|
||||
pm_runtime_unblock(dev);
|
||||
pm_runtime_put(dev);
|
||||
}
|
||||
|
||||
@@ -1270,24 +1289,17 @@ Skip:
|
||||
dev->power.is_noirq_suspended = true;
|
||||
|
||||
/*
|
||||
* Skipping the resume of devices that were in use right before the
|
||||
* system suspend (as indicated by their PM-runtime usage counters)
|
||||
* would be suboptimal. Also resume them if doing that is not allowed
|
||||
* to be skipped.
|
||||
* Devices must be resumed unless they are explicitly allowed to be left
|
||||
* in suspend, but even in that case skipping the resume of devices that
|
||||
* were in use right before the system suspend (as indicated by their
|
||||
* runtime PM usage counters and child counters) would be suboptimal.
|
||||
*/
|
||||
if (atomic_read(&dev->power.usage_count) > 1 ||
|
||||
!(dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME) &&
|
||||
dev->power.may_skip_resume))
|
||||
if (!(dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME) &&
|
||||
dev->power.may_skip_resume) || !pm_runtime_need_not_resume(dev))
|
||||
dev->power.must_resume = true;
|
||||
|
||||
if (dev->power.must_resume) {
|
||||
if (dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) {
|
||||
dev->power.set_active = true;
|
||||
if (dev->parent && !dev->parent->power.ignore_children)
|
||||
dev->parent->power.set_active = true;
|
||||
}
|
||||
if (dev->power.must_resume)
|
||||
dpm_superior_set_must_resume(dev);
|
||||
}
|
||||
|
||||
Complete:
|
||||
complete_all(&dev->power.completion);
|
||||
@@ -1320,6 +1332,7 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
|
||||
|
||||
list_move(&dev->power.entry, &dpm_noirq_list);
|
||||
|
||||
dpm_clear_async_state(dev);
|
||||
if (dpm_async_fn(dev, async_suspend_noirq))
|
||||
continue;
|
||||
|
||||
@@ -1404,6 +1417,10 @@ static int device_suspend_late(struct device *dev, pm_message_t state, bool asyn
|
||||
TRACE_DEVICE(dev);
|
||||
TRACE_SUSPEND(0);
|
||||
|
||||
/*
|
||||
* Disable runtime PM for the device without checking if there is a
|
||||
* pending resume request for it.
|
||||
*/
|
||||
__pm_runtime_disable(dev, false);
|
||||
|
||||
dpm_wait_for_subordinate(dev, async);
|
||||
@@ -1493,6 +1510,7 @@ int dpm_suspend_late(pm_message_t state)
|
||||
|
||||
list_move(&dev->power.entry, &dpm_late_early_list);
|
||||
|
||||
dpm_clear_async_state(dev);
|
||||
if (dpm_async_fn(dev, async_suspend_late))
|
||||
continue;
|
||||
|
||||
@@ -1650,6 +1668,7 @@ static int device_suspend(struct device *dev, pm_message_t state, bool async)
|
||||
pm_runtime_disable(dev);
|
||||
if (pm_runtime_status_suspended(dev)) {
|
||||
pm_dev_dbg(dev, state, "direct-complete ");
|
||||
dev->power.is_suspended = true;
|
||||
goto Complete;
|
||||
}
|
||||
|
||||
@@ -1760,6 +1779,7 @@ int dpm_suspend(pm_message_t state)
|
||||
|
||||
list_move(&dev->power.entry, &dpm_suspended_list);
|
||||
|
||||
dpm_clear_async_state(dev);
|
||||
if (dpm_async_fn(dev, async_suspend))
|
||||
continue;
|
||||
|
||||
@@ -1791,6 +1811,46 @@ int dpm_suspend(pm_message_t state)
|
||||
return error;
|
||||
}
|
||||
|
||||
static bool device_prepare_smart_suspend(struct device *dev)
|
||||
{
|
||||
struct device_link *link;
|
||||
bool ret = true;
|
||||
int idx;
|
||||
|
||||
/*
|
||||
* The "smart suspend" feature is enabled for devices whose drivers ask
|
||||
* for it and for devices without PM callbacks.
|
||||
*
|
||||
* However, if "smart suspend" is not enabled for the device's parent
|
||||
* or any of its suppliers that take runtime PM into account, it cannot
|
||||
* be enabled for the device either.
|
||||
*/
|
||||
if (!dev->power.no_pm_callbacks &&
|
||||
!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
|
||||
return false;
|
||||
|
||||
if (dev->parent && !dev_pm_smart_suspend(dev->parent) &&
|
||||
!dev->parent->power.ignore_children && !pm_runtime_blocked(dev->parent))
|
||||
return false;
|
||||
|
||||
idx = device_links_read_lock();
|
||||
|
||||
list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) {
|
||||
if (!(link->flags & DL_FLAG_PM_RUNTIME))
|
||||
continue;
|
||||
|
||||
if (!dev_pm_smart_suspend(link->supplier) &&
|
||||
!pm_runtime_blocked(link->supplier)) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
device_links_read_unlock(idx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_prepare - Prepare a device for system power transition.
|
||||
* @dev: Device to handle.
|
||||
@@ -1802,6 +1862,7 @@ int dpm_suspend(pm_message_t state)
|
||||
static int device_prepare(struct device *dev, pm_message_t state)
|
||||
{
|
||||
int (*callback)(struct device *) = NULL;
|
||||
bool smart_suspend;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
@@ -1811,6 +1872,13 @@ static int device_prepare(struct device *dev, pm_message_t state)
|
||||
* it again during the complete phase.
|
||||
*/
|
||||
pm_runtime_get_noresume(dev);
|
||||
/*
|
||||
* If runtime PM is disabled for the device at this point and it has
|
||||
* never been enabled so far, it should not be enabled until this system
|
||||
* suspend-resume cycle is complete, so prepare to trigger a warning on
|
||||
* subsequent attempts to enable it.
|
||||
*/
|
||||
smart_suspend = !pm_runtime_block_if_disabled(dev);
|
||||
|
||||
if (dev->power.syscore)
|
||||
return 0;
|
||||
@@ -1845,6 +1913,13 @@ unlock:
|
||||
pm_runtime_put(dev);
|
||||
return ret;
|
||||
}
|
||||
/* Do not enable "smart suspend" for devices with disabled runtime PM. */
|
||||
if (smart_suspend)
|
||||
smart_suspend = device_prepare_smart_suspend(dev);
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
|
||||
dev->power.smart_suspend = smart_suspend;
|
||||
/*
|
||||
* A positive return value from ->prepare() means "this device appears
|
||||
* to be runtime-suspended and its state is fine, so if it really is
|
||||
@@ -1852,11 +1927,12 @@ unlock:
|
||||
* will do the same thing with all of its descendants". This only
|
||||
* applies to suspend transitions, however.
|
||||
*/
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->power.direct_complete = state.event == PM_EVENT_SUSPEND &&
|
||||
(ret > 0 || dev->power.no_pm_callbacks) &&
|
||||
!dev_pm_test_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
|
||||
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2020,6 +2096,5 @@ void device_pm_check_callbacks(struct device *dev)
|
||||
|
||||
bool dev_pm_skip_suspend(struct device *dev)
|
||||
{
|
||||
return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
|
||||
pm_runtime_status_suspended(dev);
|
||||
return dev_pm_smart_suspend(dev) && pm_runtime_status_suspended(dev);
|
||||
}
|
||||
|
||||
@@ -448,8 +448,19 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
|
||||
retval = __rpm_callback(cb, dev);
|
||||
}
|
||||
|
||||
dev->power.runtime_error = retval;
|
||||
return retval != -EACCES ? retval : -EIO;
|
||||
/*
|
||||
* Since -EACCES means that runtime PM is disabled for the given device,
|
||||
* it should not be returned by runtime PM callbacks. If it is returned
|
||||
* nevertheless, assume it to be a transient error and convert it to
|
||||
* -EAGAIN.
|
||||
*/
|
||||
if (retval == -EACCES)
|
||||
retval = -EAGAIN;
|
||||
|
||||
if (retval != -EAGAIN && retval != -EBUSY)
|
||||
dev->power.runtime_error = retval;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -725,21 +736,18 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||
dev->power.deferred_resume = false;
|
||||
wake_up_all(&dev->power.wait_queue);
|
||||
|
||||
if (retval == -EAGAIN || retval == -EBUSY) {
|
||||
dev->power.runtime_error = 0;
|
||||
/*
|
||||
* On transient errors, if the callback routine failed an autosuspend,
|
||||
* and if the last_busy time has been updated so that there is a new
|
||||
* autosuspend expiration time, automatically reschedule another
|
||||
* autosuspend.
|
||||
*/
|
||||
if (!dev->power.runtime_error && (rpmflags & RPM_AUTO) &&
|
||||
pm_runtime_autosuspend_expiration(dev) != 0)
|
||||
goto repeat;
|
||||
|
||||
pm_runtime_cancel_pending(dev);
|
||||
|
||||
/*
|
||||
* If the callback routine failed an autosuspend, and
|
||||
* if the last_busy time has been updated so that there
|
||||
* is a new autosuspend expiration time, automatically
|
||||
* reschedule another autosuspend.
|
||||
*/
|
||||
if ((rpmflags & RPM_AUTO) &&
|
||||
pm_runtime_autosuspend_expiration(dev) != 0)
|
||||
goto repeat;
|
||||
} else {
|
||||
pm_runtime_cancel_pending(dev);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1460,20 +1468,31 @@ int pm_runtime_barrier(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_runtime_barrier);
|
||||
|
||||
/**
|
||||
* __pm_runtime_disable - Disable runtime PM of a device.
|
||||
* @dev: Device to handle.
|
||||
* @check_resume: If set, check if there's a resume request for the device.
|
||||
*
|
||||
* Increment power.disable_depth for the device and if it was zero previously,
|
||||
* cancel all pending runtime PM requests for the device and wait for all
|
||||
* operations in progress to complete. The device can be either active or
|
||||
* suspended after its runtime PM has been disabled.
|
||||
*
|
||||
* If @check_resume is set and there's a resume request pending when
|
||||
* __pm_runtime_disable() is called and power.disable_depth is zero, the
|
||||
* function will wake up the device before disabling its runtime PM.
|
||||
*/
|
||||
bool pm_runtime_block_if_disabled(struct device *dev)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
|
||||
ret = !pm_runtime_enabled(dev);
|
||||
if (ret && dev->power.last_status == RPM_INVALID)
|
||||
dev->power.last_status = RPM_BLOCKED;
|
||||
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void pm_runtime_unblock(struct device *dev)
|
||||
{
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
|
||||
if (dev->power.last_status == RPM_BLOCKED)
|
||||
dev->power.last_status = RPM_INVALID;
|
||||
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
}
|
||||
|
||||
void __pm_runtime_disable(struct device *dev, bool check_resume)
|
||||
{
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
@@ -1532,6 +1551,10 @@ void pm_runtime_enable(struct device *dev)
|
||||
if (--dev->power.disable_depth > 0)
|
||||
goto out;
|
||||
|
||||
if (dev->power.last_status == RPM_BLOCKED) {
|
||||
dev_warn(dev, "Attempt to enable runtime PM when it is blocked\n");
|
||||
dump_stack();
|
||||
}
|
||||
dev->power.last_status = RPM_INVALID;
|
||||
dev->power.accounting_timestamp = ktime_get_mono_fast_ns();
|
||||
|
||||
@@ -1874,7 +1897,7 @@ void pm_runtime_drop_link(struct device_link *link)
|
||||
pm_request_idle(link->supplier);
|
||||
}
|
||||
|
||||
static bool pm_runtime_need_not_resume(struct device *dev)
|
||||
bool pm_runtime_need_not_resume(struct device *dev)
|
||||
{
|
||||
return atomic_read(&dev->power.usage_count) <= 1 &&
|
||||
(atomic_read(&dev->power.child_count) == 0 ||
|
||||
@@ -1959,7 +1982,7 @@ int pm_runtime_force_resume(struct device *dev)
|
||||
int (*callback)(struct device *);
|
||||
int ret = 0;
|
||||
|
||||
if (!pm_runtime_status_suspended(dev) || !dev->power.needs_force_resume)
|
||||
if (!dev->power.needs_force_resume)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
|
||||
@@ -254,7 +254,7 @@ config ARM_TEGRA186_CPUFREQ
|
||||
|
||||
config ARM_TEGRA194_CPUFREQ
|
||||
tristate "Tegra194 CPUFreq support"
|
||||
depends on ARCH_TEGRA_194_SOC || (64BIT && COMPILE_TEST)
|
||||
depends on ARCH_TEGRA_194_SOC || ARCH_TEGRA_234_SOC || (64BIT && COMPILE_TEST)
|
||||
depends on TEGRA_BPMP
|
||||
default y
|
||||
help
|
||||
|
||||
@@ -909,6 +909,9 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
if (perf->states[0].core_frequency * 1000 != freq_table[0].frequency)
|
||||
pr_warn(FW_WARN "P-state 0 is not max freq\n");
|
||||
|
||||
if (acpi_cpufreq_driver.set_boost)
|
||||
policy->boost_supported = true;
|
||||
|
||||
return result;
|
||||
|
||||
err_unreg:
|
||||
@@ -949,7 +952,6 @@ static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
|
||||
}
|
||||
|
||||
static struct freq_attr *acpi_cpufreq_attr[] = {
|
||||
&cpufreq_freq_attr_scaling_available_freqs,
|
||||
&freqdomain_cpus,
|
||||
#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB
|
||||
&cpb,
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
|
||||
TRACE_EVENT(amd_pstate_perf,
|
||||
|
||||
TP_PROTO(unsigned long min_perf,
|
||||
unsigned long target_perf,
|
||||
unsigned long capacity,
|
||||
TP_PROTO(u8 min_perf,
|
||||
u8 target_perf,
|
||||
u8 capacity,
|
||||
u64 freq,
|
||||
u64 mperf,
|
||||
u64 aperf,
|
||||
@@ -47,9 +47,9 @@ TRACE_EVENT(amd_pstate_perf,
|
||||
),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned long, min_perf)
|
||||
__field(unsigned long, target_perf)
|
||||
__field(unsigned long, capacity)
|
||||
__field(u8, min_perf)
|
||||
__field(u8, target_perf)
|
||||
__field(u8, capacity)
|
||||
__field(unsigned long long, freq)
|
||||
__field(unsigned long long, mperf)
|
||||
__field(unsigned long long, aperf)
|
||||
@@ -70,10 +70,10 @@ TRACE_EVENT(amd_pstate_perf,
|
||||
__entry->fast_switch = fast_switch;
|
||||
),
|
||||
|
||||
TP_printk("amd_min_perf=%lu amd_des_perf=%lu amd_max_perf=%lu freq=%llu mperf=%llu aperf=%llu tsc=%llu cpu_id=%u fast_switch=%s",
|
||||
(unsigned long)__entry->min_perf,
|
||||
(unsigned long)__entry->target_perf,
|
||||
(unsigned long)__entry->capacity,
|
||||
TP_printk("amd_min_perf=%hhu amd_des_perf=%hhu amd_max_perf=%hhu freq=%llu mperf=%llu aperf=%llu tsc=%llu cpu_id=%u fast_switch=%s",
|
||||
(u8)__entry->min_perf,
|
||||
(u8)__entry->target_perf,
|
||||
(u8)__entry->capacity,
|
||||
(unsigned long long)__entry->freq,
|
||||
(unsigned long long)__entry->mperf,
|
||||
(unsigned long long)__entry->aperf,
|
||||
@@ -86,11 +86,12 @@ TRACE_EVENT(amd_pstate_perf,
|
||||
TRACE_EVENT(amd_pstate_epp_perf,
|
||||
|
||||
TP_PROTO(unsigned int cpu_id,
|
||||
unsigned int highest_perf,
|
||||
unsigned int epp,
|
||||
unsigned int min_perf,
|
||||
unsigned int max_perf,
|
||||
bool boost
|
||||
u8 highest_perf,
|
||||
u8 epp,
|
||||
u8 min_perf,
|
||||
u8 max_perf,
|
||||
bool boost,
|
||||
bool changed
|
||||
),
|
||||
|
||||
TP_ARGS(cpu_id,
|
||||
@@ -98,15 +99,17 @@ TRACE_EVENT(amd_pstate_epp_perf,
|
||||
epp,
|
||||
min_perf,
|
||||
max_perf,
|
||||
boost),
|
||||
boost,
|
||||
changed),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, cpu_id)
|
||||
__field(unsigned int, highest_perf)
|
||||
__field(unsigned int, epp)
|
||||
__field(unsigned int, min_perf)
|
||||
__field(unsigned int, max_perf)
|
||||
__field(u8, highest_perf)
|
||||
__field(u8, epp)
|
||||
__field(u8, min_perf)
|
||||
__field(u8, max_perf)
|
||||
__field(bool, boost)
|
||||
__field(bool, changed)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
@@ -116,15 +119,17 @@ TRACE_EVENT(amd_pstate_epp_perf,
|
||||
__entry->min_perf = min_perf;
|
||||
__entry->max_perf = max_perf;
|
||||
__entry->boost = boost;
|
||||
__entry->changed = changed;
|
||||
),
|
||||
|
||||
TP_printk("cpu%u: [%u<->%u]/%u, epp=%u, boost=%u",
|
||||
TP_printk("cpu%u: [%hhu<->%hhu]/%hhu, epp=%hhu, boost=%u, changed=%u",
|
||||
(unsigned int)__entry->cpu_id,
|
||||
(unsigned int)__entry->min_perf,
|
||||
(unsigned int)__entry->max_perf,
|
||||
(unsigned int)__entry->highest_perf,
|
||||
(unsigned int)__entry->epp,
|
||||
(bool)__entry->boost
|
||||
(u8)__entry->min_perf,
|
||||
(u8)__entry->max_perf,
|
||||
(u8)__entry->highest_perf,
|
||||
(u8)__entry->epp,
|
||||
(bool)__entry->boost,
|
||||
(bool)__entry->changed
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
+91
-126
@@ -22,39 +22,31 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/cleanup.h>
|
||||
|
||||
#include <acpi/cppc_acpi.h>
|
||||
|
||||
#include "amd-pstate.h"
|
||||
|
||||
/*
|
||||
* Abbreviations:
|
||||
* amd_pstate_ut: used as a shortform for AMD P-State unit test.
|
||||
* It helps to keep variable names smaller, simpler
|
||||
*/
|
||||
enum amd_pstate_ut_result {
|
||||
AMD_PSTATE_UT_RESULT_PASS,
|
||||
AMD_PSTATE_UT_RESULT_FAIL,
|
||||
};
|
||||
|
||||
struct amd_pstate_ut_struct {
|
||||
const char *name;
|
||||
void (*func)(u32 index);
|
||||
enum amd_pstate_ut_result result;
|
||||
int (*func)(u32 index);
|
||||
};
|
||||
|
||||
/*
|
||||
* Kernel module for testing the AMD P-State unit test
|
||||
*/
|
||||
static void amd_pstate_ut_acpi_cpc_valid(u32 index);
|
||||
static void amd_pstate_ut_check_enabled(u32 index);
|
||||
static void amd_pstate_ut_check_perf(u32 index);
|
||||
static void amd_pstate_ut_check_freq(u32 index);
|
||||
static void amd_pstate_ut_check_driver(u32 index);
|
||||
static int amd_pstate_ut_acpi_cpc_valid(u32 index);
|
||||
static int amd_pstate_ut_check_enabled(u32 index);
|
||||
static int amd_pstate_ut_check_perf(u32 index);
|
||||
static int amd_pstate_ut_check_freq(u32 index);
|
||||
static int amd_pstate_ut_check_driver(u32 index);
|
||||
|
||||
static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
|
||||
{"amd_pstate_ut_acpi_cpc_valid", amd_pstate_ut_acpi_cpc_valid },
|
||||
@@ -77,71 +69,67 @@ static bool get_shared_mem(void)
|
||||
/*
|
||||
* check the _CPC object is present in SBIOS.
|
||||
*/
|
||||
static void amd_pstate_ut_acpi_cpc_valid(u32 index)
|
||||
static int amd_pstate_ut_acpi_cpc_valid(u32 index)
|
||||
{
|
||||
if (acpi_cpc_valid())
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
|
||||
else {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
if (!acpi_cpc_valid()) {
|
||||
pr_err("%s the _CPC object is not present in SBIOS!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void amd_pstate_ut_pstate_enable(u32 index)
|
||||
{
|
||||
int ret = 0;
|
||||
u64 cppc_enable = 0;
|
||||
|
||||
ret = rdmsrl_safe(MSR_AMD_CPPC_ENABLE, &cppc_enable);
|
||||
if (ret) {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
pr_err("%s rdmsrl_safe MSR_AMD_CPPC_ENABLE ret=%d error!\n", __func__, ret);
|
||||
return;
|
||||
}
|
||||
if (cppc_enable)
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
|
||||
else {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
pr_err("%s amd pstate must be enabled!\n", __func__);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if amd pstate is enabled
|
||||
*/
|
||||
static void amd_pstate_ut_check_enabled(u32 index)
|
||||
static int amd_pstate_ut_check_enabled(u32 index)
|
||||
{
|
||||
u64 cppc_enable = 0;
|
||||
int ret;
|
||||
|
||||
if (get_shared_mem())
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
|
||||
else
|
||||
amd_pstate_ut_pstate_enable(index);
|
||||
return 0;
|
||||
|
||||
ret = rdmsrl_safe(MSR_AMD_CPPC_ENABLE, &cppc_enable);
|
||||
if (ret) {
|
||||
pr_err("%s rdmsrl_safe MSR_AMD_CPPC_ENABLE ret=%d error!\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!cppc_enable) {
|
||||
pr_err("%s amd pstate must be enabled!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if performance values are reasonable.
|
||||
* highest_perf >= nominal_perf > lowest_nonlinear_perf > lowest_perf > 0
|
||||
*/
|
||||
static void amd_pstate_ut_check_perf(u32 index)
|
||||
static int amd_pstate_ut_check_perf(u32 index)
|
||||
{
|
||||
int cpu = 0, ret = 0;
|
||||
u32 highest_perf = 0, nominal_perf = 0, lowest_nonlinear_perf = 0, lowest_perf = 0;
|
||||
u64 cap1 = 0;
|
||||
struct cppc_perf_caps cppc_perf;
|
||||
struct cpufreq_policy *policy = NULL;
|
||||
struct amd_cpudata *cpudata = NULL;
|
||||
union perf_cached cur_perf;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
|
||||
struct amd_cpudata *cpudata;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
policy = cpufreq_cpu_get(cpu);
|
||||
if (!policy)
|
||||
break;
|
||||
continue;
|
||||
cpudata = policy->driver_data;
|
||||
|
||||
if (get_shared_mem()) {
|
||||
ret = cppc_get_perf_caps(cpu, &cppc_perf);
|
||||
if (ret) {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
pr_err("%s cppc_get_perf_caps ret=%d error!\n", __func__, ret);
|
||||
goto skip_test;
|
||||
return ret;
|
||||
}
|
||||
|
||||
highest_perf = cppc_perf.highest_perf;
|
||||
@@ -151,50 +139,44 @@ static void amd_pstate_ut_check_perf(u32 index)
|
||||
} else {
|
||||
ret = rdmsrl_safe_on_cpu(cpu, MSR_AMD_CPPC_CAP1, &cap1);
|
||||
if (ret) {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
pr_err("%s read CPPC_CAP1 ret=%d error!\n", __func__, ret);
|
||||
goto skip_test;
|
||||
return ret;
|
||||
}
|
||||
|
||||
highest_perf = AMD_CPPC_HIGHEST_PERF(cap1);
|
||||
nominal_perf = AMD_CPPC_NOMINAL_PERF(cap1);
|
||||
lowest_nonlinear_perf = AMD_CPPC_LOWNONLIN_PERF(cap1);
|
||||
lowest_perf = AMD_CPPC_LOWEST_PERF(cap1);
|
||||
highest_perf = FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1);
|
||||
nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1);
|
||||
lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1);
|
||||
lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
|
||||
}
|
||||
|
||||
if (highest_perf != READ_ONCE(cpudata->highest_perf) && !cpudata->hw_prefcore) {
|
||||
cur_perf = READ_ONCE(cpudata->perf);
|
||||
if (highest_perf != cur_perf.highest_perf && !cpudata->hw_prefcore) {
|
||||
pr_err("%s cpu%d highest=%d %d highest perf doesn't match\n",
|
||||
__func__, cpu, highest_perf, cpudata->highest_perf);
|
||||
goto skip_test;
|
||||
__func__, cpu, highest_perf, cur_perf.highest_perf);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((nominal_perf != READ_ONCE(cpudata->nominal_perf)) ||
|
||||
(lowest_nonlinear_perf != READ_ONCE(cpudata->lowest_nonlinear_perf)) ||
|
||||
(lowest_perf != READ_ONCE(cpudata->lowest_perf))) {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
if (nominal_perf != cur_perf.nominal_perf ||
|
||||
(lowest_nonlinear_perf != cur_perf.lowest_nonlinear_perf) ||
|
||||
(lowest_perf != cur_perf.lowest_perf)) {
|
||||
pr_err("%s cpu%d nominal=%d %d lowest_nonlinear=%d %d lowest=%d %d, they should be equal!\n",
|
||||
__func__, cpu, nominal_perf, cpudata->nominal_perf,
|
||||
lowest_nonlinear_perf, cpudata->lowest_nonlinear_perf,
|
||||
lowest_perf, cpudata->lowest_perf);
|
||||
goto skip_test;
|
||||
__func__, cpu, nominal_perf, cur_perf.nominal_perf,
|
||||
lowest_nonlinear_perf, cur_perf.lowest_nonlinear_perf,
|
||||
lowest_perf, cur_perf.lowest_perf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!((highest_perf >= nominal_perf) &&
|
||||
(nominal_perf > lowest_nonlinear_perf) &&
|
||||
(lowest_nonlinear_perf > lowest_perf) &&
|
||||
(lowest_nonlinear_perf >= lowest_perf) &&
|
||||
(lowest_perf > 0))) {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
pr_err("%s cpu%d highest=%d >= nominal=%d > lowest_nonlinear=%d > lowest=%d > 0, the formula is incorrect!\n",
|
||||
__func__, cpu, highest_perf, nominal_perf,
|
||||
lowest_nonlinear_perf, lowest_perf);
|
||||
goto skip_test;
|
||||
return -EINVAL;
|
||||
}
|
||||
cpufreq_cpu_put(policy);
|
||||
}
|
||||
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
|
||||
return;
|
||||
skip_test:
|
||||
cpufreq_cpu_put(policy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -202,59 +184,50 @@ skip_test:
|
||||
* max_freq >= nominal_freq > lowest_nonlinear_freq > min_freq > 0
|
||||
* check max freq when set support boost mode.
|
||||
*/
|
||||
static void amd_pstate_ut_check_freq(u32 index)
|
||||
static int amd_pstate_ut_check_freq(u32 index)
|
||||
{
|
||||
int cpu = 0;
|
||||
struct cpufreq_policy *policy = NULL;
|
||||
struct amd_cpudata *cpudata = NULL;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
for_each_online_cpu(cpu) {
|
||||
struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
|
||||
struct amd_cpudata *cpudata;
|
||||
|
||||
policy = cpufreq_cpu_get(cpu);
|
||||
if (!policy)
|
||||
break;
|
||||
continue;
|
||||
cpudata = policy->driver_data;
|
||||
|
||||
if (!((cpudata->max_freq >= cpudata->nominal_freq) &&
|
||||
if (!((policy->cpuinfo.max_freq >= cpudata->nominal_freq) &&
|
||||
(cpudata->nominal_freq > cpudata->lowest_nonlinear_freq) &&
|
||||
(cpudata->lowest_nonlinear_freq > cpudata->min_freq) &&
|
||||
(cpudata->min_freq > 0))) {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
(cpudata->lowest_nonlinear_freq >= policy->cpuinfo.min_freq) &&
|
||||
(policy->cpuinfo.min_freq > 0))) {
|
||||
pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n",
|
||||
__func__, cpu, cpudata->max_freq, cpudata->nominal_freq,
|
||||
cpudata->lowest_nonlinear_freq, cpudata->min_freq);
|
||||
goto skip_test;
|
||||
__func__, cpu, policy->cpuinfo.max_freq, cpudata->nominal_freq,
|
||||
cpudata->lowest_nonlinear_freq, policy->cpuinfo.min_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cpudata->lowest_nonlinear_freq != policy->min) {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
pr_err("%s cpu%d cpudata_lowest_nonlinear_freq=%d policy_min=%d, they should be equal!\n",
|
||||
__func__, cpu, cpudata->lowest_nonlinear_freq, policy->min);
|
||||
goto skip_test;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cpudata->boost_supported) {
|
||||
if ((policy->max == cpudata->max_freq) ||
|
||||
(policy->max == cpudata->nominal_freq))
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
|
||||
else {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
if ((policy->max != policy->cpuinfo.max_freq) &&
|
||||
(policy->max != cpudata->nominal_freq)) {
|
||||
pr_err("%s cpu%d policy_max=%d should be equal cpu_max=%d or cpu_nominal=%d !\n",
|
||||
__func__, cpu, policy->max, cpudata->max_freq,
|
||||
__func__, cpu, policy->max, policy->cpuinfo.max_freq,
|
||||
cpudata->nominal_freq);
|
||||
goto skip_test;
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
|
||||
pr_err("%s cpu%d must support boost!\n", __func__, cpu);
|
||||
goto skip_test;
|
||||
return -EINVAL;
|
||||
}
|
||||
cpufreq_cpu_put(policy);
|
||||
}
|
||||
|
||||
amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
|
||||
return;
|
||||
skip_test:
|
||||
cpufreq_cpu_put(policy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pstate_set_mode(enum amd_pstate_mode mode)
|
||||
@@ -266,32 +239,28 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode)
|
||||
return amd_pstate_update_status(mode_str, strlen(mode_str));
|
||||
}
|
||||
|
||||
static void amd_pstate_ut_check_driver(u32 index)
|
||||
static int amd_pstate_ut_check_driver(u32 index)
|
||||
{
|
||||
enum amd_pstate_mode mode1, mode2 = AMD_PSTATE_DISABLE;
|
||||
int ret;
|
||||
|
||||
for (mode1 = AMD_PSTATE_DISABLE; mode1 < AMD_PSTATE_MAX; mode1++) {
|
||||
ret = amd_pstate_set_mode(mode1);
|
||||
int ret = amd_pstate_set_mode(mode1);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
for (mode2 = AMD_PSTATE_DISABLE; mode2 < AMD_PSTATE_MAX; mode2++) {
|
||||
if (mode1 == mode2)
|
||||
continue;
|
||||
ret = amd_pstate_set_mode(mode2);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (ret) {
|
||||
pr_err("%s: failed to update status for %s->%s\n", __func__,
|
||||
amd_pstate_get_mode_string(mode1),
|
||||
amd_pstate_get_mode_string(mode2));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (ret)
|
||||
pr_warn("%s: failed to update status for %s->%s: %d\n", __func__,
|
||||
amd_pstate_get_mode_string(mode1),
|
||||
amd_pstate_get_mode_string(mode2), ret);
|
||||
|
||||
amd_pstate_ut_cases[index].result = ret ?
|
||||
AMD_PSTATE_UT_RESULT_FAIL :
|
||||
AMD_PSTATE_UT_RESULT_PASS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init amd_pstate_ut_init(void)
|
||||
@@ -299,16 +268,12 @@ static int __init amd_pstate_ut_init(void)
|
||||
u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
|
||||
|
||||
for (i = 0; i < arr_size; i++) {
|
||||
amd_pstate_ut_cases[i].func(i);
|
||||
switch (amd_pstate_ut_cases[i].result) {
|
||||
case AMD_PSTATE_UT_RESULT_PASS:
|
||||
int ret = amd_pstate_ut_cases[i].func(i);
|
||||
|
||||
if (ret)
|
||||
pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret);
|
||||
else
|
||||
pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name);
|
||||
break;
|
||||
case AMD_PSTATE_UT_RESULT_FAIL:
|
||||
default:
|
||||
pr_info("%-4d %-20s\t fail!\n", i+1, amd_pstate_ut_cases[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
+291
-379
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,36 @@
|
||||
/*********************************************************************
|
||||
* AMD P-state INTERFACE *
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* union perf_cached - A union to cache performance-related data.
|
||||
* @highest_perf: the maximum performance an individual processor may reach,
|
||||
* assuming ideal conditions
|
||||
* For platforms that support the preferred core feature, the highest_perf value maybe
|
||||
* configured to any value in the range 166-255 by the firmware (because the preferred
|
||||
* core ranking is encoded in the highest_perf value). To maintain consistency across
|
||||
* all platforms, we split the highest_perf and preferred core ranking values into
|
||||
* cpudata->perf.highest_perf and cpudata->prefcore_ranking.
|
||||
* @nominal_perf: the maximum sustained performance level of the processor,
|
||||
* assuming ideal operating conditions
|
||||
* @lowest_nonlinear_perf: the lowest performance level at which nonlinear power
|
||||
* savings are achieved
|
||||
* @lowest_perf: the absolute lowest performance level of the processor
|
||||
* @min_limit_perf: Cached value of the performance corresponding to policy->min
|
||||
* @max_limit_perf: Cached value of the performance corresponding to policy->max
|
||||
*/
|
||||
union perf_cached {
|
||||
struct {
|
||||
u8 highest_perf;
|
||||
u8 nominal_perf;
|
||||
u8 lowest_nonlinear_perf;
|
||||
u8 lowest_perf;
|
||||
u8 min_limit_perf;
|
||||
u8 max_limit_perf;
|
||||
};
|
||||
u64 val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amd_aperf_mperf
|
||||
* @aperf: actual performance frequency clock count
|
||||
@@ -30,24 +60,11 @@ struct amd_aperf_mperf {
|
||||
* @cpu: CPU number
|
||||
* @req: constraint request to apply
|
||||
* @cppc_req_cached: cached performance request hints
|
||||
* @highest_perf: the maximum performance an individual processor may reach,
|
||||
* assuming ideal conditions
|
||||
* For platforms that do not support the preferred core feature, the
|
||||
* highest_pef may be configured with 166 or 255, to avoid max frequency
|
||||
* calculated wrongly. we take the fixed value as the highest_perf.
|
||||
* @nominal_perf: the maximum sustained performance level of the processor,
|
||||
* assuming ideal operating conditions
|
||||
* @lowest_nonlinear_perf: the lowest performance level at which nonlinear power
|
||||
* savings are achieved
|
||||
* @lowest_perf: the absolute lowest performance level of the processor
|
||||
* @perf: cached performance-related data
|
||||
* @prefcore_ranking: the preferred core ranking, the higher value indicates a higher
|
||||
* priority.
|
||||
* @min_limit_perf: Cached value of the performance corresponding to policy->min
|
||||
* @max_limit_perf: Cached value of the performance corresponding to policy->max
|
||||
* @min_limit_freq: Cached value of policy->min (in khz)
|
||||
* @max_limit_freq: Cached value of policy->max (in khz)
|
||||
* @max_freq: the frequency (in khz) that mapped to highest_perf
|
||||
* @min_freq: the frequency (in khz) that mapped to lowest_perf
|
||||
* @nominal_freq: the frequency (in khz) that mapped to nominal_perf
|
||||
* @lowest_nonlinear_freq: the frequency (in khz) that mapped to lowest_nonlinear_perf
|
||||
* @cur: Difference of Aperf/Mperf/tsc count between last and current sample
|
||||
@@ -59,7 +76,6 @@ struct amd_aperf_mperf {
|
||||
* AMD P-State driver supports preferred core featue.
|
||||
* @epp_cached: Cached CPPC energy-performance preference value
|
||||
* @policy: Cpufreq policy value
|
||||
* @cppc_cap1_cached Cached MSR_AMD_CPPC_CAP1 register value
|
||||
*
|
||||
* The amd_cpudata is key private data for each CPU thread in AMD P-State, and
|
||||
* represents all the attributes and goals that AMD P-State requests at runtime.
|
||||
@@ -70,18 +86,11 @@ struct amd_cpudata {
|
||||
struct freq_qos_request req[2];
|
||||
u64 cppc_req_cached;
|
||||
|
||||
u32 highest_perf;
|
||||
u32 nominal_perf;
|
||||
u32 lowest_nonlinear_perf;
|
||||
u32 lowest_perf;
|
||||
u32 prefcore_ranking;
|
||||
u32 min_limit_perf;
|
||||
u32 max_limit_perf;
|
||||
u32 min_limit_freq;
|
||||
u32 max_limit_freq;
|
||||
union perf_cached perf;
|
||||
|
||||
u32 max_freq;
|
||||
u32 min_freq;
|
||||
u8 prefcore_ranking;
|
||||
u32 min_limit_freq;
|
||||
u32 max_limit_freq;
|
||||
u32 nominal_freq;
|
||||
u32 lowest_nonlinear_freq;
|
||||
|
||||
@@ -93,11 +102,9 @@ struct amd_cpudata {
|
||||
bool hw_prefcore;
|
||||
|
||||
/* EPP feature related attributes*/
|
||||
s16 epp_cached;
|
||||
u32 policy;
|
||||
u64 cppc_cap1_cached;
|
||||
bool suspended;
|
||||
s16 epp_default;
|
||||
u8 epp_default;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -229,12 +229,6 @@ static int apple_soc_cpufreq_find_cluster(struct cpufreq_policy *policy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct freq_attr *apple_soc_cpufreq_hw_attr[] = {
|
||||
&cpufreq_freq_attr_scaling_available_freqs,
|
||||
NULL, /* Filled in below if boost is enabled */
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
int ret, i;
|
||||
@@ -316,16 +310,6 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
|
||||
policy->fast_switch_possible = true;
|
||||
policy->suspend_freq = freq_table[0].frequency;
|
||||
|
||||
if (policy_has_boost_freq(policy)) {
|
||||
ret = cpufreq_enable_boost_support();
|
||||
if (ret) {
|
||||
dev_warn(cpu_dev, "failed to enable boost: %d\n", ret);
|
||||
} else {
|
||||
apple_soc_cpufreq_hw_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs;
|
||||
apple_soc_cpufreq_driver.boost_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_cpufreq_table:
|
||||
@@ -360,7 +344,7 @@ static struct cpufreq_driver apple_soc_cpufreq_driver = {
|
||||
.target_index = apple_soc_cpufreq_set_target,
|
||||
.fast_switch = apple_soc_cpufreq_fast_switch,
|
||||
.register_em = cpufreq_register_em_with_opp,
|
||||
.attr = apple_soc_cpufreq_hw_attr,
|
||||
.set_boost = cpufreq_boost_set_sw,
|
||||
.suspend = cpufreq_generic_suspend,
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user