Commit b4ec8054 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull power management updates from Rafael Wysocki:
 "These update cpufreq (core and drivers), cpuidle (polling state
  implementation and the PSCI driver), the OPP (operating performance
  points) framework, devfreq (core and drivers), the power capping RAPL
  (Running Average Power Limit) driver, the Energy Model support, the
  generic power domains (genpd) framework, the ACPI device power
  management, the core system-wide suspend code and power management
  utilities.

  Specifics:

   - Use local_clock() instead of jiffies in the cpufreq statistics to
     improve accuracy (Viresh Kumar).

   - Fix up OPP usage in the cpufreq-dt and qcom-cpufreq-nvmem cpufreq
     drivers (Viresh Kumar).

   - Clean up the cpufreq core, the intel_pstate driver and the
     schedutil cpufreq governor (Rafael Wysocki).

   - Fix up error code paths in the sti-cpufreq and mediatek cpufreq
     drivers (Yangtao Li, Qinglang Miao).

   - Fix cpufreq_online() to return error codes instead of success (0)
     in all cases when it fails (Wang ShaoBo).

   - Add mt8167 support to the mediatek cpufreq driver and blacklist
     mt8516 in the cpufreq-dt-platdev driver (Fabien Parent).

   - Modify the tegra194 cpufreq driver to always return values from the
     frequency table as the current frequency and clean up that driver
     (Sumit Gupta, Jon Hunter).

   - Modify the arm_scmi cpufreq driver to allow it to discover the
     power scale present in the performance protocol and provide this
     information to the Energy Model (Lukasz Luba).

   - Add missing MODULE_DEVICE_TABLE to several cpufreq drivers (Pali
     Rohár).

   - Clean up the CPPC cpufreq driver (Ionela Voinescu).

   - Fix NVMEM_IMX_OCOTP dependency in the imx cpufreq driver (Arnd
     Bergmann).

   - Rework the poling interval selection for the polling state in
     cpuidle (Mel Gorman).

   - Enable suspend-to-idle for PSCI OSI mode in the PSCI cpuidle driver
     (Ulf Hansson).

   - Modify the OPP framework to support empty (node-less) OPP tables in
     DT for passing dependency information (Nicola Mazzucato).

   - Fix potential lockdep issue in the OPP core and clean up the OPP
     core (Viresh Kumar).

   - Modify dev_pm_opp_put_regulators() to accept a NULL argument and
     update its users accordingly (Viresh Kumar).

   - Add frequency changes tracepoint to devfreq (Matthias Kaehlcke).

   - Add support for governor feature flags to devfreq, make devfreq
     sysfs file permissions depend on the governor and clean up the
     devfreq core (Chanwoo Choi).

   - Clean up the tegra20 devfreq driver and deprecate it to allow
     another driver based on EMC_STAT to be used instead of it (Dmitry
     Osipenko).

   - Add interconnect support to the tegra30 devfreq driver, allow it to
     take the interconnect and OPP information from DT and clean it up
     (Dmitry Osipenko).

   - Add interconnect support to the exynos-bus devfreq driver along
     with interconnect properties documentation (Sylwester Nawrocki).

   - Add suport for AMD Fam17h and Fam19h processors to the RAPL power
     capping driver (Victor Ding, Kim Phillips).

   - Fix handling of overly long constraint names in the powercap
     framework (Lukasz Luba).

   - Fix the wakeup configuration handling for bridges in the ACPI
     device power management core (Rafael Wysocki).

   - Add support for using an abstract scale for power units in the
     Energy Model (EM) and document it (Lukasz Luba).

   - Add em_cpu_energy() micro-optimization to the EM (Pavankumar
     Kondeti).

   - Modify the generic power domains (genpd) framwework to support
     suspend-to-idle (Ulf Hansson).

   - Fix creation of debugfs nodes in genpd (Thierry Strudel).

   - Clean up genpd (Lina Iyer).

   - Clean up the core system-wide suspend code and make it print driver
     flags for devices with debug enabled (Alex Shi, Patrice Chotard,
     Chen Yu).

   - Modify the ACPI system reboot code to make it prepare for system
     power off to avoid confusing the platform firmware (Kai-Heng Feng).

   - Update the pm-graph (multiple changes, mostly usability-related)
     and cpupower (online and offline CPU information support) PM
     utilities (Todd Brandt, Brahadambal Srinivasan)"

* tag 'pm-5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (86 commits)
  cpufreq: Fix cpufreq_online() return value on errors
  cpufreq: Fix up several kerneldoc comments
  cpufreq: stats: Use local_clock() instead of jiffies
  cpufreq: schedutil: Simplify sugov_update_next_freq()
  cpufreq: intel_pstate: Simplify intel_cpufreq_update_pstate()
  PM: domains: create debugfs nodes when adding power domains
  opp: of: Allow empty opp-table with opp-shared
  dt-bindings: opp: Allow empty OPP tables
  media: venus: dev_pm_opp_put_*() accepts NULL argument
  drm/panfrost: dev_pm_opp_put_*() accepts NULL argument
  drm/lima: dev_pm_opp_put_*() accepts NULL argument
  PM / devfreq: exynos: dev_pm_opp_put_*() accepts NULL argument
  cpufreq: qcom-cpufreq-nvmem: dev_pm_opp_put_*() accepts NULL argument
  cpufreq: dt: dev_pm_opp_put_regulators() accepts NULL argument
  opp: Allow dev_pm_opp_put_*() APIs to accept NULL opp_table
  opp: Don't create an OPP table from dev_pm_opp_get_opp_table()
  cpufreq: dt: Don't (ab)use dev_pm_opp_get_opp_table() to create OPP table
  opp: Reduce the size of critical section in _opp_kref_release()
  PM / EM: Micro optimization in em_cpu_energy
  cpufreq: arm_scmi: Discover the power scale in performance protocol
  ...
parents b109bc72 b3fac817
......@@ -37,20 +37,6 @@ Description:
The /sys/class/devfreq/.../target_freq shows the next governor
predicted target frequency of the corresponding devfreq object.
What: /sys/class/devfreq/.../polling_interval
Date: September 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/devfreq/.../polling_interval shows and sets
the requested polling interval of the corresponding devfreq
object. The values are represented in ms. If the value is
less than 1 jiffy, it is considered to be 0, which means
no polling. This value is meaningless if the governor is
not polling; thus. If the governor is not using
devfreq-provided central polling
(/sys/class/devfreq/.../central_polling is 0), this value
may be useless.
What: /sys/class/devfreq/.../trans_stat
Date: October 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
......@@ -66,14 +52,6 @@ Description:
echo 0 > /sys/class/devfreq/.../trans_stat
What: /sys/class/devfreq/.../userspace/set_freq
Date: September 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/devfreq/.../userspace/set_freq shows and
sets the requested frequency for the devfreq object if
userspace governor is in effect.
What: /sys/class/devfreq/.../available_frequencies
Date: October 2012
Contact: Nishanth Menon <nm@ti.com>
......@@ -110,6 +88,35 @@ Description:
The max_freq overrides min_freq because max_freq may be
used to throttle devices to avoid overheating.
What: /sys/class/devfreq/.../polling_interval
Date: September 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/devfreq/.../polling_interval shows and sets
the requested polling interval of the corresponding devfreq
object. The values are represented in ms. If the value is
less than 1 jiffy, it is considered to be 0, which means
no polling. This value is meaningless if the governor is
not polling; thus. If the governor is not using
devfreq-provided central polling
(/sys/class/devfreq/.../central_polling is 0), this value
may be useless.
A list of governors that support the node:
- simple_ondmenad
- tegra_actmon
What: /sys/class/devfreq/.../userspace/set_freq
Date: September 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/devfreq/.../userspace/set_freq shows and
sets the requested frequency for the devfreq object if
userspace governor is in effect.
A list of governors that support the node:
- userspace
What: /sys/class/devfreq/.../timer
Date: July 2020
Contact: Chanwoo Choi <cw00.choi@samsung.com>
......@@ -122,3 +129,6 @@ Description:
echo deferrable > /sys/class/devfreq/.../timer
echo delayed > /sys/class/devfreq/.../timer
A list of governors that support the node:
- simple_ondemand
......@@ -51,6 +51,19 @@ Optional properties only for parent bus device:
- exynos,saturation-ratio: the percentage value which is used to calibrate
the performance count against total cycle count.
Optional properties for the interconnect functionality (QoS frequency
constraints):
- #interconnect-cells: should be 0.
- interconnects: as documented in ../interconnect.txt, describes a path at the
higher level interconnects used by this interconnect provider.
If this interconnect provider is directly linked to a top level interconnect
provider the property contains only one phandle. The provider extends
the interconnect graph by linking its node to a node registered by provider
pointed to by first phandle in the 'interconnects' property.
- samsung,data-clock-ratio: ratio of the data throughput in B/s to minimum data
clock frequency in Hz, default value is 8 when this property is missing.
Detailed correlation between sub-blocks and power line according to Exynos SoC:
- In case of Exynos3250, there are two power line as following:
VDD_MIF |--- DMC
......@@ -135,7 +148,7 @@ Detailed correlation between sub-blocks and power line according to Exynos SoC:
|--- PERIC (Fixed clock rate)
|--- FSYS (Fixed clock rate)
Example1:
Example 1:
Show the AXI buses of Exynos3250 SoC. Exynos3250 divides the buses to
power line (regulator). The MIF (Memory Interface) AXI bus is used to
transfer data between DRAM and CPU and uses the VDD_MIF regulator.
......@@ -184,7 +197,7 @@ Example1:
|L5 |200000 |200000 |400000 |300000 | ||1000000 |
----------------------------------------------------------
Example2 :
Example 2:
The bus of DMC (Dynamic Memory Controller) block in exynos3250.dtsi
is listed below:
......@@ -419,3 +432,57 @@ Example2 :
devfreq = <&bus_leftbus>;
status = "okay";
};
Example 3:
An interconnect path "bus_display -- bus_leftbus -- bus_dmc" on
Exynos4412 SoC with video mixer as an interconnect consumer device.
soc {
bus_dmc: bus_dmc {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_DIV_DMC>;
clock-names = "bus";
operating-points-v2 = <&bus_dmc_opp_table>;
samsung,data-clock-ratio = <4>;
#interconnect-cells = <0>;
};
bus_leftbus: bus_leftbus {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_DIV_GDL>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
#interconnect-cells = <0>;
interconnects = <&bus_dmc>;
};
bus_display: bus_display {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_ACLK160>;
clock-names = "bus";
operating-points-v2 = <&bus_display_opp_table>;
#interconnect-cells = <0>;
interconnects = <&bus_leftbus &bus_dmc>;
};
bus_dmc_opp_table: opp_table1 {
compatible = "operating-points-v2";
/* ... */
}
bus_leftbus_opp_table: opp_table3 {
compatible = "operating-points-v2";
/* ... */
};
bus_display_opp_table: opp_table4 {
compatible = "operating-points-v2";
/* .. */
};
&mixer {
compatible = "samsung,exynos4212-mixer";
interconnects = <&bus_display &bus_dmc>;
/* ... */
};
};
......@@ -65,7 +65,9 @@ Required properties:
- OPP nodes: One or more OPP nodes describing voltage-current-frequency
combinations. Their name isn't significant but their phandle can be used to
reference an OPP.
reference an OPP. These are mandatory except for the case where the OPP table
is present only to indicate dependency between devices using the opp-shared
property.
Optional properties:
- opp-shared: Indicates that device nodes using this OPP Table Node's phandle
......@@ -568,3 +570,53 @@ Example 6: opp-microvolt-<name>, opp-microamp-<name>:
};
};
};
Example 7: Single cluster Quad-core ARM cortex A53, OPP points from firmware,
distinct clock controls but two sets of clock/voltage/current lines.
/ {
cpus {
#address-cells = <2>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a53";
reg = <0x0 0x100>;
next-level-cache = <&A53_L2>;
clocks = <&dvfs_controller 0>;
operating-points-v2 = <&cpu_opp0_table>;
};
cpu@1 {
compatible = "arm,cortex-a53";
reg = <0x0 0x101>;
next-level-cache = <&A53_L2>;
clocks = <&dvfs_controller 1>;
operating-points-v2 = <&cpu_opp0_table>;
};
cpu@2 {
compatible = "arm,cortex-a53";
reg = <0x0 0x102>;
next-level-cache = <&A53_L2>;
clocks = <&dvfs_controller 2>;
operating-points-v2 = <&cpu_opp1_table>;
};
cpu@3 {
compatible = "arm,cortex-a53";
reg = <0x0 0x103>;
next-level-cache = <&A53_L2>;
clocks = <&dvfs_controller 3>;
operating-points-v2 = <&cpu_opp1_table>;
};
};
cpu_opp0_table: opp0_table {
compatible = "operating-points-v2";
opp-shared;
};
cpu_opp1_table: opp1_table {
compatible = "operating-points-v2";
opp-shared;
};
};
......@@ -71,7 +71,9 @@ to the speed-grade of the silicon. `sustainable_power` is therefore
simply an estimate, and may be tuned to affect the aggressiveness of
the thermal ramp. For reference, the sustainable power of a 4" phone
is typically 2000mW, while on a 10" tablet is around 4500mW (may vary
depending on screen size).
depending on screen size). It is possible to have the power value
expressed in an abstract scale. The sustained power should be aligned
to the scale used by the related cooling devices.
If you are using device tree, do add it as a property of the
thermal-zone. For example::
......@@ -269,3 +271,11 @@ won't be very good. Note that this is not particular to this
governor, step-wise will also misbehave if you call its throttle()
faster than the normal thermal framework tick (due to interrupts for
example) as it will overreact.
Energy Model requirements
=========================
Another important thing is the consistent scale of the power values
provided by the cooling devices. All of the cooling devices in a single
thermal zone should have power values reported either in milli-Watts
or scaled to the same 'abstract scale'.
......@@ -20,6 +20,21 @@ possible source of information on its own, the EM framework intervenes as an
abstraction layer which standardizes the format of power cost tables in the
kernel, hence enabling to avoid redundant work.
The power values might be expressed in milli-Watts or in an 'abstract scale'.
Multiple subsystems might use the EM and it is up to the system integrator to
check that the requirements for the power value scale types are met. An example
can be found in the Energy-Aware Scheduler documentation
Documentation/scheduler/sched-energy.rst. For some subsystems like thermal or
powercap power values expressed in an 'abstract scale' might cause issues.
These subsystems are more interested in estimation of power used in the past,
thus the real milli-Watts might be needed. An example of these requirements can
be found in the Intelligent Power Allocation in
Documentation/driver-api/thermal/power_allocator.rst.
Kernel subsystems might implement automatic detection to check whether EM
registered devices have inconsistent scale (based on EM internal flag).
Important thing to keep in mind is that when the power values are expressed in
an 'abstract scale' deriving real energy in milli-Joules would not be possible.
The figure below depicts an example of drivers (Arm-specific here, but the
approach is applicable to any architecture) providing power costs to the EM
framework, and interested clients reading the data from it::
......@@ -73,7 +88,7 @@ Drivers are expected to register performance domains into the EM framework by
calling the following API::
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *cpus);
struct em_data_callback *cb, cpumask_t *cpus, bool milliwatts);
Drivers must provide a callback function returning <frequency, power> tuples
for each performance state. The callback function provided by the driver is free
......@@ -81,6 +96,10 @@ to fetch data from any relevant location (DT, firmware, ...), and by any mean
deemed necessary. Only for CPU devices, drivers must specify the CPUs of the
performance domains using cpumask. For other devices than CPUs the last
argument must be set to NULL.
The last argument 'milliwatts' is important to set with correct value. Kernel
subsystems which use EM might rely on this flag to check if all EM devices use
the same scale. If there are different scales, these subsystems might decide
to: return warning/error, stop working or panic.
See Section 3. for an example of driver implementing this
callback, and kernel/power/energy_model.c for further documentation on this
API.
......@@ -156,7 +175,8 @@ EM framework::
37 nr_opp = foo_get_nr_opp(policy);
38
39 /* And register the new performance domain */
40 em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus);
41
42 return 0;
43 }
40 em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus,
41 true);
42
43 return 0;
44 }
......@@ -350,6 +350,11 @@ independent EM framework in Documentation/power/energy-model.rst.
Please also note that the scheduling domains need to be re-built after the
EM has been registered in order to start EAS.
EAS uses the EM to make a forecasting decision on energy usage and thus it is
more focused on the difference when checking possible options for task
placement. For EAS it doesn't matter whether the EM power values are expressed
in milli-Watts or in an 'abstract scale'.
6.3 - Energy Model complexity
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
......
......@@ -11438,7 +11438,6 @@ L: linux-pm@vger.kernel.org
L: linux-tegra@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
S: Maintained
F: drivers/devfreq/tegra20-devfreq.c
F: drivers/devfreq/tegra30-devfreq.c
MEMORY MANAGEMENT
......
......@@ -327,8 +327,9 @@
#define MSR_PP1_ENERGY_STATUS 0x00000641
#define MSR_PP1_POLICY 0x00000642
#define MSR_AMD_PKG_ENERGY_STATUS 0xc001029b
#define MSR_AMD_RAPL_POWER_UNIT 0xc0010299
#define MSR_AMD_CORE_ENERGY_STATUS 0xc001029a
#define MSR_AMD_PKG_ENERGY_STATUS 0xc001029b
/* Config TDP MSRs */
#define MSR_CONFIG_TDP_NOMINAL 0x00000648
......
......@@ -749,7 +749,7 @@ static void acpi_pm_notify_work_func(struct acpi_device_wakeup_context *context)
static DEFINE_MUTEX(acpi_wakeup_lock);
static int __acpi_device_wakeup_enable(struct acpi_device *adev,
u32 target_state, int max_count)
u32 target_state)
{
struct acpi_device_wakeup *wakeup = &adev->wakeup;
acpi_status status;
......@@ -757,15 +757,26 @@ static int __acpi_device_wakeup_enable(struct acpi_device *adev,
mutex_lock(&acpi_wakeup_lock);
if (wakeup->enable_count >= max_count)
goto out;
/*
* If the device wakeup power is already enabled, disable it and enable
* it again in case it depends on the configuration of subordinate
* devices and the conditions have changed since it was enabled last
* time.
*/
if (wakeup->enable_count > 0)
goto inc;
acpi_disable_wakeup_device_power(adev);
error = acpi_enable_wakeup_device_power(adev, target_state);
if (error)
if (error) {
if (wakeup->enable_count > 0) {
acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
wakeup->enable_count = 0;
}
goto out;
}
if (wakeup->enable_count > 0)
goto inc;
status = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
if (ACPI_FAILURE(status)) {
......@@ -778,7 +789,10 @@ static int __acpi_device_wakeup_enable(struct acpi_device *adev,
(unsigned int)wakeup->gpe_number);
inc:
wakeup->enable_count++;
if (wakeup->enable_count < INT_MAX)
wakeup->enable_count++;
else
acpi_handle_info(adev->handle, "Wakeup enable count out of bounds!\n");
out:
mutex_unlock(&acpi_wakeup_lock);
......@@ -799,7 +813,7 @@ static int __acpi_device_wakeup_enable(struct acpi_device *adev,
*/
static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
{
return __acpi_device_wakeup_enable(adev, target_state, 1);
return __acpi_device_wakeup_enable(adev, target_state);
}
/**
......@@ -829,8 +843,12 @@ static void acpi_device_wakeup_disable(struct acpi_device *adev)
mutex_unlock(&acpi_wakeup_lock);
}
static int __acpi_pm_set_device_wakeup(struct device *dev, bool enable,
int max_count)
/**
* acpi_pm_set_device_wakeup - Enable/disable remote wakeup for given device.
* @dev: Device to enable/disable to generate wakeup events.
* @enable: Whether to enable or disable the wakeup functionality.
*/
int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
{
struct acpi_device *adev;
int error;
......@@ -850,36 +868,14 @@ static int __acpi_pm_set_device_wakeup(struct device *dev, bool enable,
return 0;
}
error = __acpi_device_wakeup_enable(adev, acpi_target_system_state(),
max_count);
error = __acpi_device_wakeup_enable(adev, acpi_target_system_state());
if (!error)
dev_dbg(dev, "Wakeup enabled by ACPI\n");
return error;
}
/**
* acpi_pm_set_device_wakeup - Enable/disable remote wakeup for given device.
* @dev: Device to enable/disable to generate wakeup events.
* @enable: Whether to enable or disable the wakeup functionality.
*/
int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
{
return __acpi_pm_set_device_wakeup(dev, enable, 1);
}
EXPORT_SYMBOL_GPL(acpi_pm_set_device_wakeup);
/**
* acpi_pm_set_bridge_wakeup - Enable/disable remote wakeup for given bridge.
* @dev: Bridge device to enable/disable to generate wakeup events.
* @enable: Whether to enable or disable the wakeup functionality.
*/
int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable)
{
return __acpi_pm_set_device_wakeup(dev, enable, INT_MAX);
}
EXPORT_SYMBOL_GPL(acpi_pm_set_bridge_wakeup);
/**
* acpi_dev_pm_low_power - Put ACPI device into a low-power state.
* @dev: Device to put into a low-power state.
......
......@@ -21,6 +21,7 @@
#include <linux/suspend.h>
#include <linux/export.h>
#include <linux/cpu.h>
#include <linux/debugfs.h>
#include "power.h"
......@@ -210,6 +211,18 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
}
#ifdef CONFIG_DEBUG_FS
static struct dentry *genpd_debugfs_dir;
static void genpd_debug_add(struct generic_pm_domain *genpd);
static void genpd_debug_remove(struct generic_pm_domain *genpd)
{
struct dentry *d;
d = debugfs_lookup(genpd->name, genpd_debugfs_dir);
debugfs_remove(d);
}
static void genpd_update_accounting(struct generic_pm_domain *genpd)
{
ktime_t delta, now;
......@@ -234,6 +247,8 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)
genpd->accounting_time = now;
}
#else
static inline void genpd_debug_add(struct generic_pm_domain *genpd) {}
static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {}
static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
#endif
......@@ -1142,7 +1157,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
if (ret)
return ret;
if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
return 0;
if (genpd->dev_ops.stop && genpd->dev_ops.start &&
......@@ -1196,7 +1211,7 @@ static int genpd_resume_noirq(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;
if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
return pm_generic_resume_noirq(dev);
genpd_lock(genpd);
......@@ -1363,41 +1378,60 @@ static void genpd_complete(struct device *dev)
genpd_unlock(genpd);
}
/**
* genpd_syscore_switch - Switch power during system core suspend or resume.
* @dev: Device that normally is marked as "always on" to switch power for.
*
* This routine may only be called during the system core (syscore) suspend or
* resume phase for devices whose "always on" flags are set.
*/
static void genpd_syscore_switch(struct device *dev, bool suspend)
static void genpd_switch_state(struct device *dev, bool suspend)
{
struct generic_pm_domain *genpd;
bool use_lock;
genpd = dev_to_genpd_safe(dev);
if (!genpd)
return;
use_lock = genpd_is_irq_safe(genpd);
if (use_lock)
genpd_lock(genpd);
if (suspend) {
genpd->suspended_count++;
genpd_sync_power_off(genpd, false, 0);
genpd_sync_power_off(genpd, use_lock, 0);
} else {
genpd_sync_power_on(genpd, false, 0);
genpd_sync_power_on(genpd, use_lock, 0);
genpd->suspended_count--;
}
if (use_lock)
genpd_unlock(genpd);
}
void pm_genpd_syscore_poweroff(struct device *dev)
/**
* dev_pm_genpd_suspend - Synchronously try to suspend the genpd for @dev
* @dev: The device that is attached to the genpd, that can be suspended.
*
* This routine should typically be called for a device that needs to be
* suspended during the syscore suspend phase. It may also be called during
* suspend-to-idle to suspend a corresponding CPU device that is attached to a
* genpd.
*/
void dev_pm_genpd_suspend(struct device *dev)
{
genpd_syscore_switch(dev, true);
genpd_switch_state(dev, true);
}
EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff);
EXPORT_SYMBOL_GPL(dev_pm_genpd_suspend);
void pm_genpd_syscore_poweron(struct device *dev)
/**
* dev_pm_genpd_resume - Synchronously try to resume the genpd for @dev
* @dev: The device that is attached to the genpd, which needs to be resumed.
*
* This routine should typically be called for a device that needs to be resumed
* during the syscore resume phase. It may also be called during suspend-to-idle
* to resume a corresponding CPU device that is attached to a genpd.
*/
void dev_pm_genpd_resume(struct device *dev)
{
genpd_syscore_switch(dev, false);
genpd_switch_state(dev, false);
}
EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron);
EXPORT_SYMBOL_GPL(dev_pm_genpd_resume);
#else /* !CONFIG_PM_SLEEP */
......@@ -1954,6 +1988,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
mutex_lock(&gpd_list_lock);
list_add(&genpd->gpd_list_node, &gpd_list);
genpd_debug_add(genpd);
mutex_unlock(&gpd_list_lock);
return 0;
......@@ -1987,6 +2022,7 @@ static int genpd_remove(struct generic_pm_domain *genpd)
kfree(link);
}
genpd_debug_remove(genpd);
list_del(&genpd->gpd_list_node);
genpd_unlock(genpd);
cancel_work_sync(&genpd->power_off_work);
......@@ -2249,7 +2285,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
* Save table for faster processing while setting
* performance state.
*/
genpd->opp_table = dev_pm_opp_get_opp_table_indexed(&genpd->dev, i);
genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
WARN_ON(IS_ERR(genpd->opp_table));
}
......@@ -2893,14 +2929,6 @@ core_initcall(genpd_bus_init);
/*** debugfs support ***/
#ifdef CONFIG_DEBUG_FS
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/kobject.h>
static struct dentry *genpd_debugfs_dir;
/*
* TODO: This function is a slightly modified version of rtpm_status_show
* from sysfs.c, so generalize it.
......@@ -3177,9 +3205,34 @@ DEFINE_SHOW_ATTRIBUTE(total_idle_time);
DEFINE_SHOW_ATTRIBUTE(devices);
DEFINE_SHOW_ATTRIBUTE(perf_state);
static int __init genpd_debug_init(void)
static void genpd_debug_add(struct generic_pm_domain *genpd)
{
struct dentry *d;
if (!genpd_debugfs_dir)
return;
d = debugfs_create_dir(genpd->name, genpd_debugfs_dir);
debugfs_create_file("current_state", 0444,
d, genpd, &status_fops);
debugfs_create_file("sub_domains", 0444,
d, genpd, &sub_domains_fops);
debugfs_create_file("idle_states", 0444,
d, genpd, &idle_states_fops);
debugfs_create_file("active_time", 0444,
d, genpd, &active_time_fops);
debugfs_create_file("total_idle_time", 0444,
d, genpd, &total_idle_time_fops);
debugfs_create_file("devices", 0444,
d, genpd, &devices_fops);
if (genpd->set_performance_state)
debugfs_create_file("perf_state", 0444,
d, genpd, &perf_state_fops);
}
static int __init genpd_debug_init(void)
{
struct generic_pm_domain *genpd;
genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL);
......@@ -3187,25 +3240,8 @@ static int __init genpd_debug_init(void)
debugfs_create_file("pm_genpd_summary", S_IRUGO, genpd_debugfs_dir,
NULL, &summary_fops);
list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
d = debugfs_create_dir(genpd->name, genpd_debugfs_dir);
debugfs_create_file("current_state", 0444,
d, genpd, &status_fops);
debugfs_create_file("sub_domains", 0444,
d, genpd, &sub_domains_fops);
debugfs_create_file("idle_states", 0444,
d, genpd, &idle_states_fops);
debugfs_create_file("active_time", 0444,
d, genpd, &active_time_fops);
debugfs_create_file("total_idle_time", 0444,
d, genpd, &total_idle_time_fops);
debugfs_create_file("devices", 0444,
d, genpd, &devices_fops);
if (genpd->set_performance_state)
debugfs_create_file("perf_state", 0444,
d, genpd, &perf_state_fops);
}
list_for_each_entry(genpd, &gpd_list, gpd_list_node)
genpd_debug_add(genpd);
return 0;
}
......
......@@ -441,9 +441,9 @@ static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t stat
static void pm_dev_dbg(struct device *dev, pm_message_t state, const char *info)
{
dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event),
dev_dbg(dev, "%s%s%s driver flags: %x\n", info, pm_verb(state.event),
((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ?
", may wakeup" : "");
", may wakeup" : "", dev->power.driver_flags);
}
static void pm_dev_err(struct device *dev, pm_message_t state, const char *info,
......@@ -1359,7 +1359,7 @@ static void dpm_propagate_wakeup_to_parent(struct device *dev)
spin_lock_irq(&parent->power.lock);
if (dev->power.wakeup_path && !parent->power.ignore_children)
if (device_wakeup_path(dev) && !parent->power.ignore_children)
parent->power.wakeup_path = true;
spin_unlock_irq(&parent->power.lock);
......@@ -1627,7 +1627,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
goto Complete;
/* Avoid direct_complete to let wakeup_path propagate. */
if (device_may_wakeup(dev) || dev->power.wakeup_path)
if (device_may_wakeup(dev) || device_wakeup_path(dev))
dev->power.direct_complete = false;
if (dev->power.direct_complete) {
......
......@@ -13,6 +13,7 @@
#include <linux/clk-provider.h>
#include <linux/clk/tegra.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
......@@ -235,6 +236,7 @@ void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
emc->cb_arg = cb_arg;
}
}
EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback);
bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
{
......@@ -291,3 +293,4 @@ int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
return 0;
}
EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);
......@@ -668,7 +668,7 @@ static void sh_cmt_clocksource_suspend(struct clocksource *cs)
return;
sh_cmt_stop(ch, FLAG_CLOCKSOURCE);
pm_genpd_syscore_poweroff(&ch->cmt->pdev->dev);
dev_pm_genpd_suspend(&ch->cmt->pdev->dev);
}
static void sh_cmt_clocksource_resume(struct clocksource *cs)
......@@ -678,7 +678,7 @@ static void sh_cmt_clocksource_resume(struct clocksource *cs)
if (!ch->cs_enabled)
return;
pm_genpd_syscore_poweron(&ch->cmt->pdev->dev);
dev_pm_genpd_resume(&ch->cmt->pdev->dev);
sh_cmt_start(ch, FLAG_CLOCKSOURCE);
}
......@@ -770,7 +770,7 @@ static void sh_cmt_clock_event_suspend(struct clock_event_device *ced)
{
struct sh_cmt_channel *ch = ced_to_sh_cmt(ced);
pm_genpd_syscore_poweroff(&ch->cmt->pdev->dev);
dev_pm_genpd_suspend(&ch->cmt->pdev->dev);
clk_unprepare(ch->cmt->clk);
}
......@@ -779,7 +779,7 @@ static void sh_cmt_clock_event_resume(struct clock_event_device *ced)
struct sh_cmt_channel *ch = ced_to_sh_cmt(ced);
clk_prepare(ch->cmt->clk);
pm_genpd_syscore_poweron(&ch->cmt->pdev->dev);
dev_pm_genpd_resume(&ch->cmt->pdev->dev);
}
static int sh_cmt_register_clockevent(struct sh_cmt_channel *ch,
......
......@@ -297,12 +297,12 @@ static int sh_mtu2_clock_event_set_periodic(struct clock_event_device *ced)
static void sh_mtu2_clock_event_suspend(struct clock_event_device *ced)
{
pm_genpd_syscore_poweroff(&ced_to_sh_mtu2(ced)->mtu->pdev->dev);
dev_pm_genpd_suspend(&ced_to_sh_mtu2(ced)->mtu->pdev->dev);
}
static void sh_mtu2_clock_event_resume(struct clock_event_device *ced)
{
pm_genpd_syscore_poweron(&ced_to_sh_mtu2(ced)->mtu->pdev->dev);
dev_pm_genpd_resume(&ced_to_sh_mtu2(ced)->mtu->pdev->dev);
}
static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
......
......@@ -292,7 +292,7 @@ static void sh_tmu_clocksource_suspend(struct clocksource *cs)
if (--ch->enable_count == 0) {
__sh_tmu_disable(ch);
pm_genpd_syscore_poweroff(&ch->tmu->pdev->dev);
dev_pm_genpd_suspend(&ch->tmu->pdev->dev);
}
}
......@@ -304,7 +304,7 @@ static void sh_tmu_clocksource_resume(struct clocksource *cs)
return;
if (ch->enable_count++ == 0) {
pm_genpd_syscore_poweron(&ch->tmu->pdev->dev);
dev_pm_genpd_resume(&ch->tmu->pdev->dev);
__sh_tmu_enable(ch);
}
}
......@@ -394,12 +394,12 @@ static int sh_tmu_clock_event_next(unsigned long delta,
static void sh_tmu_clock_event_suspend(struct clock_event_device *ced)
{
pm_genpd_syscore_poweroff(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
dev_pm_genpd_suspend(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
}
static void sh_tmu_clock_event_resume(struct clock_event_device *ced)
{
pm_genpd_syscore_poweron(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
dev_pm_genpd_resume(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
}
static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
......
......@@ -94,7 +94,7 @@ config ARM_IMX6Q_CPUFREQ
tristate "Freescale i.MX6 cpufreq support"
depends on ARCH_MXC
depends on REGULATOR_ANATOP
select NVMEM_IMX_OCOTP
depends on NVMEM_IMX_OCOTP || COMPILE_TEST
select PM_OPP
help
This adds cpufreq driver support for Freescale i.MX6 series SoCs.
......
......@@ -204,6 +204,12 @@ static void __exit armada_8k_cpufreq_exit(void)
}
module_exit(armada_8k_cpufreq_exit);
static const struct of_device_id __maybe_unused armada_8k_cpufreq_of_match[] = {
{ .compatible = "marvell,ap806-cpu-clock" },
{ },
};
MODULE_DEVICE_TABLE(of, armada_8k_cpufreq_of_match);
MODULE_AUTHOR("Gregory Clement <gregory.clement@bootlin.com>");
MODULE_DESCRIPTION("Armada 8K cpufreq driver");
MODULE_LICENSE("GPL");
This diff is collapsed.
......@@ -119,10 +119,12 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "mediatek,mt2712", },
{ .compatible = "mediatek,mt7622", },
{ .compatible = "mediatek,mt7623", },
{ .compatible = "mediatek,mt8167", },
{ .compatible = "mediatek,mt817x", },
{ .compatible = "mediatek,mt8173", },
{ .compatible = "mediatek,mt8176", },
{ .compatible = "mediatek,mt8183", },
{ .compatible = "mediatek,mt8516", },
{ .compatible = "nvidia,tegra20", },
{ .compatible = "nvidia,tegra30", },
......
......@@ -30,7 +30,7 @@ struct private_data {
cpumask_var_t cpus;
struct device *cpu_dev;
struct opp_table *opp_table;
struct opp_table *reg_opp_table;
struct cpufreq_frequency_table *freq_table;
bool have_static_opps;
};
......@@ -102,7 +102,6 @@ static const char *find_supply_name(struct device *dev)
static int cpufreq_init(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *freq_table;
struct private_data *priv;
struct device *cpu_dev;
struct clk *cpu_clk;
......@@ -114,9 +113,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
pr_err("failed to find data for cpu%d\n", policy->cpu);
return -ENODEV;
}
cpu_dev = priv->cpu_dev;
cpumask_copy(policy->cpus, priv->cpus);
cpu_clk = clk_get(cpu_dev, NULL);
if (IS_ERR(cpu_clk)) {
......@@ -125,67 +122,32 @@ static int cpufreq_init(struct cpufreq_policy *policy)
return ret;
}
/*
* Initialize OPP tables for all policy->cpus. They will be shared by
* all CPUs which have marked their CPUs shared with OPP bindings.
*
* For platforms not using operating-points-v2 bindings, we do this
* before updating policy->cpus. Otherwise, we will end up creating
* duplicate OPPs for policy->cpus.
*
* OPPs might be populated at runtime, don't check for error here
*/
if (!dev_pm_opp_of_cpumask_add_table(policy->cpus))
priv->have_static_opps = true;
/*
* But we need OPP table to function so if it is not there let's
* give platform code chance to provide it for us.
*/
ret = dev_pm_opp_get_opp_count(cpu_dev);
if (ret <= 0) {
dev_err(cpu_dev, "OPP table can't be empty\n");
ret = -ENODEV;
goto out_free_opp;
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
goto out_free_opp;
}
transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
if (!transition_latency)
transition_latency = CPUFREQ_ETERNAL;
cpumask_copy(policy->cpus, priv->cpus);
policy->driver_data = priv;
policy->clk = cpu_clk;
policy->freq_table = freq_table;
policy->freq_table = priv->freq_table;
policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
policy->cpuinfo.transition_latency = transition_latency;
policy->dvfs_possible_from_any_cpu = true;
/* Support turbo/boost mode */
if (policy_has_boost_freq(policy)) {
/* This gets disabled by core on driver unregister */
ret = cpufreq_enable_boost_support();
if (ret)
goto out_free_cpufreq_table;
goto out_clk_put;
cpufreq_dt_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs;
}
transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
if (!transition_latency)
transition_latency = CPUFREQ_ETERNAL;
policy->cpuinfo.transition_latency = transition_latency;
policy->dvfs_possible_from_any_cpu = true;
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
return 0;
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_opp:
if (priv->have_static_opps)
dev_pm_opp_of_cpumask_remove_table(policy->cpus);
out_clk_put:
clk_put(cpu_clk);
return ret;
......@@ -208,11 +170,6 @@ static int cpufreq_offline(struct cpufreq_policy *policy)
static int cpufreq_exit(struct cpufreq_policy *policy)
{
struct private_data *priv = policy->driver_data;
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
if (priv->have_static_opps)
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
clk_put(policy->clk);
return 0;
}
......@@ -236,6 +193,7 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
{
struct private_data *priv;
struct device *cpu_dev;
bool fallback = false;
const char *reg_name;
int ret;
......@@ -254,68 +212,86 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
if (!alloc_cpumask_var(&priv->cpus, GFP_KERNEL))
return -ENOMEM;
cpumask_set_cpu(cpu, priv->cpus);
priv->cpu_dev = cpu_dev;
/* Try to get OPP table early to ensure resources are available */
priv->opp_table = dev_pm_opp_get_opp_table(cpu_dev);
if (IS_ERR(priv->opp_table)) {
ret = PTR_ERR(priv->opp_table);
if (ret != -EPROBE_DEFER)
dev_err(cpu_dev, "failed to get OPP table: %d\n", ret);
goto free_cpumask;
}
/*
* OPP layer will be taking care of regulators now, but it needs to know
* the name of the regulator first.
*/
reg_name = find_supply_name(cpu_dev);
if (reg_name) {
priv->reg_opp_table = dev_pm_opp_set_regulators(cpu_dev,
&reg_name, 1);
if (IS_ERR(priv->reg_opp_table)) {
ret = PTR_ERR(priv->reg_opp_table);
priv->opp_table = dev_pm_opp_set_regulators(cpu_dev, &reg_name,
1);
if (IS_ERR(priv->opp_table)) {
ret = PTR_ERR(priv->opp_table);
if (ret != -EPROBE_DEFER)
dev_err(cpu_dev, "failed to set regulators: %d\n",
ret);
goto put_table;
goto free_cpumask;
}
}
/* Find OPP sharing information so we can fill pri->cpus here */
/* Get OPP-sharing information from "operating-points-v2" bindings */
ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus);
if (ret) {
if (ret != -ENOENT)
goto put_reg;
goto out;
/*
* operating-points-v2 not supported, fallback to all CPUs share
* OPP for backward compatibility if the platform hasn't set
* sharing CPUs.
*/
if (dev_pm_opp_get_sharing_cpus(cpu_dev, priv->cpus)) {
cpumask_setall(priv->cpus);
/*
* OPP tables are initialized only for cpu, do it for
* others as well.
*/
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, priv->cpus);
if (ret)
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
__func__, ret);
}
if (dev_pm_opp_get_sharing_cpus(cpu_dev, priv->cpus))
fallback = true;
}
/*
* Initialize OPP tables for all priv->cpus. They will be shared by
* all CPUs which have marked their CPUs shared with OPP bindings.
*
* For platforms not using operating-points-v2 bindings, we do this
* before updating priv->cpus. Otherwise, we will end up creating
* duplicate OPPs for the CPUs.
*
* OPPs might be populated at runtime, don't check for error here.
*/
if (!dev_pm_opp_of_cpumask_add_table(priv->cpus))
priv->have_static_opps = true;
/*
* The OPP table must be initialized, statically or dynamically, by this
* point.
*/
ret = dev_pm_opp_get_opp_count(cpu_dev);
if (ret <= 0) {
dev_err(cpu_dev, "OPP table can't be empty\n");
ret = -ENODEV;
goto out;
}
if (fallback) {
cpumask_setall(priv->cpus);
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, priv->cpus);
if (ret)
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
__func__, ret);
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
goto out;
}
list_add(&priv->node, &priv_list);
return 0;
put_reg:
if (priv->reg_opp_table)
dev_pm_opp_put_regulators(priv->reg_opp_table);
put_table:
dev_pm_opp_put_opp_table(priv->opp_table);
out:
if (priv->have_static_opps)
dev_pm_opp_of_cpumask_remove_table(priv->cpus);
dev_pm_opp_put_regulators(priv->opp_table);
free_cpumask:
free_cpumask_var(priv->cpus);
return ret;
......@@ -326,9 +302,10 @@ static void dt_cpufreq_release(void)
struct private_data *priv, *tmp;
list_for_each_entry_safe(priv, tmp, &priv_list, node) {
if (priv->reg_opp_table)
dev_pm_opp_put_regulators(priv->reg_opp_table);
dev_pm_opp_put_opp_table(priv->opp_table);
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table);
if (priv->have_static_opps)
dev_pm_opp_of_cpumask_remove_table(priv->cpus);
dev_pm_opp_put_regulators(priv->opp_table);
free_cpumask_var(priv->cpus);
list_del(&priv->node);
}
......
......@@ -298,8 +298,10 @@ struct cpufreq_policy *cpufreq_cpu_acquire(unsigned int cpu)
* EXTERNALLY AFFECTING FREQUENCY CHANGES *
*********************************************************************/
/*
* adjust_jiffies - adjust the system "loops_per_jiffy"
/**
* adjust_jiffies - Adjust the system "loops_per_jiffy".
* @val: CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE.
* @ci: Frequency change information.
*
* This function alters the system "loops_per_jiffy" for the clock
* speed change. Note that loops_per_jiffy cannot be updated on SMP
......@@ -331,14 +333,14 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
}
/**
* cpufreq_notify_transition - Notify frequency transition and adjust_jiffies.
* cpufreq_notify_transition - Notify frequency transition and adjust jiffies.
* @policy: cpufreq policy to enable fast frequency switching for.
* @freqs: contain details of the frequency update.
* @state: set to CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE.
*
* This function calls the transition notifiers and the "adjust_jiffies"
* function. It is called twice on all CPU frequency changes that have
* external effects.
* This function calls the transition notifiers and adjust_jiffies().
*
* It is called twice on all CPU frequency changes that have external effects.
*/
static void cpufreq_notify_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs,
......@@ -1391,8 +1393,10 @@ static int cpufreq_online(unsigned int cpu)
policy->min_freq_req = kzalloc(2 * sizeof(*policy->min_freq_req),
GFP_KERNEL);
if (!policy->min_freq_req)
if (!policy->min_freq_req) {
ret = -ENOMEM;
goto out_destroy_policy;
}
ret = freq_qos_add_request(&policy->constraints,
policy->min_freq_req, FREQ_QOS_MIN,
......@@ -1429,6 +1433,7 @@ static int cpufreq_online(unsigned int cpu)
if (cpufreq_driver->get && has_target()) {
policy->cur = cpufreq_driver->get(policy->cpu);
if (!policy->cur) {
ret = -EIO;
pr_err("%s: ->get() failed\n", __func__);
goto out_destroy_policy;
}
......@@ -1646,13 +1651,12 @@ static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
}
/**
* cpufreq_out_of_sync - If actual and saved CPU frequency differs, we're
* in deep trouble.
* @policy: policy managing CPUs
* @new_freq: CPU frequency the CPU actually runs at
* cpufreq_out_of_sync - Fix up actual and saved CPU frequency difference.
* @policy: Policy managing CPUs.
* @new_freq: New CPU frequency.
*
* We adjust to current frequency first, and need to clean up later.
* So either call to cpufreq_update_policy() or schedule handle_update()).
* Adjust to the current frequency first and clean up later by either calling
* cpufreq_update_policy(), or scheduling handle_update().
*/
static void cpufreq_out_of_sync(struct cpufreq_policy *policy,
unsigned int new_freq)
......@@ -1832,7 +1836,7 @@ int cpufreq_generic_suspend(struct cpufreq_policy *policy)
EXPORT_SYMBOL(cpufreq_generic_suspend);
/**
* cpufreq_suspend() - Suspend CPUFreq governors
* cpufreq_suspend() - Suspend CPUFreq governors.
*
* Called during system wide Suspend/Hibernate cycles for suspending governors
* as some platforms can't change frequency after this point in suspend cycle.
......@@ -1868,7 +1872,7 @@ void cpufreq_suspend(void)
}
/**
* cpufreq_resume() - Resume CPUFreq governors
* cpufreq_resume() - Resume CPUFreq governors.
*
* Called during system wide Suspend/Hibernate cycle for resuming governors that
* are suspended with cpufreq_suspend().
......@@ -1920,10 +1924,10 @@ bool cpufreq_driver_test_flags(u16 flags)
}
/**
* cpufreq_get_current_driver - return current driver's name
* cpufreq_get_current_driver - Return the current driver's name.
*
* Return the name string of the currently loaded cpufreq driver
* or NULL, if none.
* Return the name string of the currently registered cpufreq driver or NULL if
* none.
*/
const char *cpufreq_get_current_driver(void)
{
......@@ -1935,10 +1939,10 @@ const char *cpufreq_get_current_driver(void)
EXPORT_SYMBOL_GPL(cpufreq_get_current_driver);
/**
* cpufreq_get_driver_data - return current driver data
* cpufreq_get_driver_data - Return current driver data.
*
* Return the private data of the currently loaded cpufreq
* driver, or NULL if no cpufreq driver is loaded.
* Return the private data of the currently registered cpufreq driver, or NULL
* if no cpufreq driver has been registered.
*/
void *cpufreq_get_driver_data(void)
{
......@@ -1954,17 +1958,16 @@ EXPORT_SYMBOL_GPL(cpufreq_get_driver_data);
*********************************************************************/
/**
* cpufreq_register_notifier - register a driver with cpufreq
* @nb: notifier function to register
* @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
* cpufreq_register_notifier - Register a notifier with cpufreq.
* @nb: notifier function to register.
* @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER.
*
* Add a driver to one of two lists: either a list of drivers that
* are notified about clock rate changes (once before and once after
* the transition), or a list of drivers that are notified about
* changes in cpufreq policy.
* Add a notifier to one of two lists: either a list of notifiers that run on
* clock rate changes (once before and once after every transition), or a list
* of notifiers that ron on cpufreq policy changes.
*
* This function may sleep, and has the same return conditions as
* blocking_notifier_chain_register.
* This function may sleep and it has the same return values as
* blocking_notifier_chain_register().
*/
int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
{
......@@ -2001,14 +2004,14 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
EXPORT_SYMBOL(cpufreq_register_notifier);
/**
* cpufreq_unregister_notifier - unregister a driver with cpufreq
* @nb: notifier block to be unregistered
* @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
* cpufreq_unregister_notifier - Unregister a notifier from cpufreq.
* @nb: notifier block to be unregistered.
* @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER.
*
* Remove a driver from the CPU frequency notifier list.
* Remove a notifier from one of the cpufreq notifier lists.
*
* This function may sleep, and has the same return conditions as
* blocking_notifier_chain_unregister.
* This function may sleep and it has the same return values as
* blocking_notifier_chain_unregister().
*/
int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
{
......@@ -2123,7 +2126,7 @@ static int __target_intermediate(struct cpufreq_policy *policy,
static int __target_index(struct cpufreq_policy *policy, int index)
{
struct cpufreq_freqs freqs = {.old = policy->cur, .flags = 0};
unsigned int intermediate_freq = 0;
unsigned int restore_freq, intermediate_freq = 0;
unsigned int newfreq = policy->freq_table[index].frequency;
int retval = -EINVAL;
bool notify;
......@@ -2131,6 +2134,9 @@ static int __target_index(struct cpufreq_policy *policy, int index)
if (newfreq == policy->cur)
return 0;
/* Save last value to restore later on errors */
restore_freq = policy->cur;
notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION);
if (notify) {
/* Handle switching to intermediate frequency */
......@@ -2168,7 +2174,7 @@ static int __target_index(struct cpufreq_policy *policy, int index)
*/
if (unlikely(retval && intermediate_freq)) {
freqs.old = intermediate_freq;
freqs.new = policy->restore_freq;
freqs.new = restore_freq;
cpufreq_freq_transition_begin(policy, &freqs);
cpufreq_freq_transition_end(policy, &freqs, 0);
}
......@@ -2203,9 +2209,6 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
!(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS))
return 0;
/* Save last value to restore later on errors */
policy->restore_freq = policy->cur;
if (cpufreq_driver->target)
return cpufreq_driver->target(policy, target_freq, relation);
......
......@@ -9,9 +9,9 @@
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/module.h>
#include <linux/sched/clock.h>
#include <linux/slab.h>
struct cpufreq_stats {
unsigned int total_trans;
unsigned long long last_time;
......@@ -30,7 +30,7 @@ struct cpufreq_stats {
static void cpufreq_stats_update(struct cpufreq_stats *stats,
unsigned long long time)
{
unsigned long long cur_time = get_jiffies_64();
unsigned long long cur_time = local_clock();
stats->time_in_state[stats->last_index] += cur_time - time;
stats->last_time = cur_time;
......@@ -42,7 +42,7 @@ static void cpufreq_stats_reset_table(struct cpufreq_stats *stats)
memset(stats->time_in_state, 0, count * sizeof(u64));
memset(stats->trans_table, 0, count * count * sizeof(int));
stats->last_time = get_jiffies_64();
stats->last_time = local_clock();
stats->total_trans = 0;
/* Adjust for the time elapsed since reset was requested */
......@@ -82,18 +82,18 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
* before the reset_pending read above.
*/
smp_rmb();
time = get_jiffies_64() - READ_ONCE(stats->reset_time);
time = local_clock() - READ_ONCE(stats->reset_time);
} else {
time = 0;
}
} else {
time = stats->time_in_state[i];
if (i == stats->last_index)
time += get_jiffies_64() - stats->last_time;
time += local_clock() - stats->last_time;
}
len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
jiffies_64_to_clock_t(time));
nsec_to_clock_t(time));
}
return len;
}
......@@ -109,7 +109,7 @@ static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
* Defer resetting of stats to cpufreq_stats_record_transition() to
* avoid races.
*/
WRITE_ONCE(stats->reset_time, get_jiffies_64());
WRITE_ONCE(stats->reset_time, local_clock());
/*
* The memory barrier below is to prevent the readers of reset_time from
* seeing a stale or partially updated value.
......@@ -249,7 +249,7 @@ void cpufreq_stats_create_table(struct cpufreq_policy *policy)
stats->freq_table[i++] = pos->frequency;
stats->state_num = i;
stats->last_time = get_jiffies_64();
stats->last_time = local_clock();
stats->last_index = freq_table_get_index(stats, policy->cur);
policy->stats = stats;
......
......@@ -101,6 +101,13 @@ static int hb_cpufreq_driver_init(void)
}
module_init(hb_cpufreq_driver_init);
static const struct of_device_id __maybe_unused hb_cpufreq_of_match[] = {
{ .compatible = "calxeda,highbank" },
{ .compatible = "calxeda,ecx-2000" },
{ },
};
MODULE_DEVICE_TABLE(of, hb_cpufreq_of_match);
MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>");
MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver");
MODULE_LICENSE("GPL");
......@@ -2569,14 +2569,13 @@ static int intel_cpufreq_update_pstate(struct cpufreq_policy *policy,
int old_pstate = cpu->pstate.current_pstate;
target_pstate = intel_pstate_prepare_request(cpu, target_pstate);
if (hwp_active) {
if (hwp_active)
intel_cpufreq_adjust_hwp(cpu, target_pstate,
policy->strict_target, fast_switch);
cpu->pstate.current_pstate = target_pstate;
} else if (target_pstate != old_pstate) {
else if (target_pstate != old_pstate)
intel_cpufreq_adjust_perf_ctl(cpu, target_pstate, fast_switch);
cpu->pstate.current_pstate = target_pstate;
}
cpu->pstate.current_pstate = target_pstate;
intel_cpufreq_trace(cpu, fast_switch ? INTEL_PSTATE_TRACE_FAST_SWITCH :
INTEL_PSTATE_TRACE_TARGET, old_pstate);
......
......@@ -216,6 +216,7 @@ static struct platform_driver ls1x_cpufreq_platdrv = {
module_platform_driver(ls1x_cpufreq_platdrv);
MODULE_ALIAS("platform:ls1x-cpufreq");
MODULE_AUTHOR("Kelvin Cheung <keguang.zhang@gmail.com>");
MODULE_DESCRIPTION("Loongson1 CPUFreq driver");
MODULE_LICENSE("GPL");
......@@ -532,6 +532,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
{ .compatible = "mediatek,mt2712", },
{ .compatible = "mediatek,mt7622", },
{ .compatible = "mediatek,mt7623", },
{ .compatible = "mediatek,mt8167", },
{ .compatible = "mediatek,mt817x", },
{ .compatible = "mediatek,mt8173", },
{ .compatible = "mediatek,mt8176", },
......@@ -540,6 +541,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
{ }
};
MODULE_DEVICE_TABLE(of, mtk_cpufreq_machines);
static int __init mtk_cpufreq_driver_init(void)
{
......@@ -572,6 +574,7 @@ static int __init mtk_cpufreq_driver_init(void)
pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0);
if (IS_ERR(pdev)) {
pr_err("failed to register mtk-cpufreq platform device\n");
platform_driver_unregister(&mtk_cpufreq_platdrv);
return PTR_ERR(pdev);
}
......
......@@ -397,19 +397,19 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
free_genpd_opp:
for_each_possible_cpu(cpu) {
if (IS_ERR_OR_NULL(drv->genpd_opp_tables[cpu]))
if (IS_ERR(drv->genpd_opp_tables[cpu]))
break;
dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
}
kfree(drv->genpd_opp_tables);
free_opp:
for_each_possible_cpu(cpu) {
if (IS_ERR_OR_NULL(drv->names_opp_tables[cpu]))
if (IS_ERR(drv->names_opp_tables[cpu]))
break;
dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]);
}
for_each_possible_cpu(cpu) {
if (IS_ERR_OR_NULL(drv->hw_opp_tables[cpu]))
if (IS_ERR(drv->hw_opp_tables[cpu]))
break;
dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
}
......@@ -430,12 +430,9 @@ static int qcom_cpufreq_remove(struct platform_device *pdev)
platform_device_unregister(cpufreq_dt_pdev);
for_each_possible_cpu(cpu) {
if (drv->names_opp_tables[cpu])
dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]);
if (drv->hw_opp_tables[cpu])
dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
if (drv->genpd_opp_tables[cpu])
dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]);
dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
}
kfree(drv->names_opp_tables);
......@@ -464,6 +461,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
{ .compatible = "qcom,msm8960", .data = &match_data_krait },
{},
};
MODULE_DEVICE_TABLE(of, qcom_cpufreq_match_list);
/*
* Since the driver depends on smem and nvmem drivers, which may
......
......@@ -126,6 +126,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
struct scmi_data *priv;
struct cpufreq_frequency_table *freq_table;
struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
bool power_scale_mw;
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
......@@ -189,7 +190,9 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
policy->fast_switch_possible =
handle->perf_ops->fast_switch_possible(handle, cpu_dev);
em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus);
power_scale_mw = handle->perf_ops->power_scale_mw_get(handle);
em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus,
power_scale_mw);
return 0;
......
......@@ -233,6 +233,7 @@ static struct platform_driver scpi_cpufreq_platdrv = {
};
module_platform_driver(scpi_cpufreq_platdrv);
MODULE_ALIAS("platform:scpi-cpufreq");
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
MODULE_LICENSE("GPL v2");
......@@ -223,7 +223,8 @@ static int sti_cpufreq_set_opp_info(void)
opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
if (IS_ERR(opp_table)) {
dev_err(dev, "Failed to set supported hardware\n");
return PTR_ERR(opp_table);
ret = PTR_ERR(opp_table);
goto err_put_prop_name;
}
dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
......@@ -232,6 +233,10 @@ static int sti_cpufreq_set_opp_info(void)
version[0], version[1], version[2]);
return 0;
err_put_prop_name:
dev_pm_opp_put_prop_name(opp_table);
return ret;
}
static int sti_cpufreq_fetch_syscon_registers(void)
......@@ -292,6 +297,13 @@ static int sti_cpufreq_init(void)
}
module_init(sti_cpufreq_init);
static const struct of_device_id __maybe_unused sti_cpufreq_of_match[] = {
{ .compatible = "st,stih407" },
{ .compatible = "st,stih410" },
{ },
};
MODULE_DEVICE_TABLE(of, sti_cpufreq_of_match);
MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
......
......@@ -167,6 +167,7 @@ static const struct of_device_id sun50i_cpufreq_match_list[] = {
{ .compatible = "allwinner,sun50i-h6" },
{}
};
MODULE_DEVICE_TABLE(of, sun50i_cpufreq_match_list);
static const struct of_device_id *sun50i_cpufreq_match_node(void)
{
......
......@@ -12,35 +12,52 @@
#include <soc/tegra/bpmp.h>
#include <soc/tegra/bpmp-abi.h>
#define EDVD_CORE_VOLT_FREQ(core) (0x20 + (core) * 0x4)
#define EDVD_CORE_VOLT_FREQ_F_SHIFT 0
#define EDVD_CORE_VOLT_FREQ_F_MASK 0xffff
#define EDVD_CORE_VOLT_FREQ_V_SHIFT 16
struct tegra186_cpufreq_cluster_info {
unsigned long offset;
int cpus[4];
#define TEGRA186_NUM_CLUSTERS 2
#define EDVD_OFFSET_A57(core) ((SZ_64K * 6) + (0x20 + (core) * 0x4))
#define EDVD_OFFSET_DENVER(core) ((SZ_64K * 7) + (0x20 + (core) * 0x4))
#define EDVD_CORE_VOLT_FREQ_F_SHIFT 0
#define EDVD_CORE_VOLT_FREQ_F_MASK 0xffff
#define EDVD_CORE_VOLT_FREQ_V_SHIFT 16
struct tegra186_cpufreq_cpu {
unsigned int bpmp_cluster_id;
unsigned int edvd_offset;
};
#define NO_CPU -1
static const struct tegra186_cpufreq_cluster_info tegra186_clusters[] = {
/* Denver cluster */
static const struct tegra186_cpufreq_cpu tegra186_cpus[] = {
/* CPU0 - A57 Cluster */
{
.bpmp_cluster_id = 1,
.edvd_offset = EDVD_OFFSET_A57(0)
},
/* CPU1 - Denver Cluster */
{
.offset = SZ_64K * 7,
.cpus = { 1, 2, NO_CPU, NO_CPU },
.bpmp_cluster_id = 0,
.edvd_offset = EDVD_OFFSET_DENVER(0)
},
/* CPU2 - Denver Cluster */
{
.bpmp_cluster_id = 0,
.edvd_offset = EDVD_OFFSET_DENVER(1)
},
/* CPU3 - A57 Cluster */
{
.bpmp_cluster_id = 1,
.edvd_offset = EDVD_OFFSET_A57(1)
},
/* A57 cluster */
/* CPU4 - A57 Cluster */
{
.offset = SZ_64K * 6,
.cpus = { 0, 3, 4, 5 },
.bpmp_cluster_id = 1,
.edvd_offset = EDVD_OFFSET_A57(2)
},
/* CPU5 - A57 Cluster */
{
.bpmp_cluster_id = 1,
.edvd_offset = EDVD_OFFSET_A57(3)
},
};
struct tegra186_cpufreq_cluster {
const struct tegra186_cpufreq_cluster_info *info;
struct cpufreq_frequency_table *table;
u32 ref_clk_khz;
u32 div;
......@@ -48,36 +65,18 @@ struct tegra186_cpufreq_cluster {
struct tegra186_cpufreq_data {
void __iomem *regs;
size_t num_clusters;
struct tegra186_cpufreq_cluster *clusters;
const struct tegra186_cpufreq_cpu *cpus;
};
static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
{
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
unsigned int i;
for (i = 0; i < data->num_clusters; i++) {
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
const struct tegra186_cpufreq_cluster_info *info =
cluster->info;
int core;
for (core = 0; core < ARRAY_SIZE(info->cpus); core++) {
if (info->cpus[core] == policy->cpu)
break;
}
if (core == ARRAY_SIZE(info->cpus))
continue;
policy->driver_data =
data->regs + info->offset + EDVD_CORE_VOLT_FREQ(core);
policy->freq_table = cluster->table;
break;
}
unsigned int cluster = data->cpus[policy->cpu].bpmp_cluster_id;
policy->freq_table = data->clusters[cluster].table;
policy->cpuinfo.transition_latency = 300 * 1000;
policy->driver_data = NULL;
return 0;
}
......@@ -85,11 +84,12 @@ static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int index)
{
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
struct cpufreq_frequency_table *tbl = policy->freq_table + index;
void __iomem *edvd_reg = policy->driver_data;
unsigned int edvd_offset = data->cpus[policy->cpu].edvd_offset;
u32 edvd_val = tbl->driver_data;
writel(edvd_val, edvd_reg);
writel(edvd_val, data->regs + edvd_offset);
return 0;
}
......@@ -97,35 +97,22 @@ static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
static unsigned int tegra186_cpufreq_get(unsigned int cpu)
{
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
struct tegra186_cpufreq_cluster *cluster;
struct cpufreq_policy *policy;
void __iomem *edvd_reg;
unsigned int i, freq = 0;
unsigned int edvd_offset, cluster_id;
u32 ndiv;
policy = cpufreq_cpu_get(cpu);
if (!policy)
return 0;
edvd_reg = policy->driver_data;
ndiv = readl(edvd_reg) & EDVD_CORE_VOLT_FREQ_F_MASK;
for (i = 0; i < data->num_clusters; i++) {
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
int core;
for (core = 0; core < ARRAY_SIZE(cluster->info->cpus); core++) {
if (cluster->info->cpus[core] != policy->cpu)
continue;
freq = (cluster->ref_clk_khz * ndiv) / cluster->div;
goto out;
}
}
out:
edvd_offset = data->cpus[policy->cpu].edvd_offset;
ndiv = readl(data->regs + edvd_offset) & EDVD_CORE_VOLT_FREQ_F_MASK;
cluster_id = data->cpus[policy->cpu].bpmp_cluster_id;
cluster = &data->clusters[cluster_id];
cpufreq_cpu_put(policy);
return freq;
return (cluster->ref_clk_khz * ndiv) / cluster->div;
}
static struct cpufreq_driver tegra186_cpufreq_driver = {
......@@ -141,7 +128,7 @@ static struct cpufreq_driver tegra186_cpufreq_driver = {
static struct cpufreq_frequency_table *init_vhint_table(
struct platform_device *pdev, struct tegra_bpmp *bpmp,
struct tegra186_cpufreq_cluster *cluster)
struct tegra186_cpufreq_cluster *cluster, unsigned int cluster_id)
{
struct cpufreq_frequency_table *table;
struct mrq_cpu_vhint_request req;
......@@ -160,7 +147,7 @@ static struct cpufreq_frequency_table *init_vhint_table(
memset(&req, 0, sizeof(req));
req.addr = phys;
req.cluster_id = cluster->info->bpmp_cluster_id;
req.cluster_id = cluster_id;
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_CPU_VHINT;
......@@ -234,12 +221,12 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
data->clusters = devm_kcalloc(&pdev->dev, ARRAY_SIZE(tegra186_clusters),
data->clusters = devm_kcalloc(&pdev->dev, TEGRA186_NUM_CLUSTERS,
sizeof(*data->clusters), GFP_KERNEL);
if (!data->clusters)
return -ENOMEM;
data->num_clusters = ARRAY_SIZE(tegra186_clusters);
data->cpus = tegra186_cpus;
bpmp = tegra_bpmp_get(&pdev->dev);
if (IS_ERR(bpmp))
......@@ -251,11 +238,10 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
goto put_bpmp;
}
for (i = 0; i < data->num_clusters; i++) {
for (i = 0; i < TEGRA186_NUM_CLUSTERS; i++) {
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
cluster->info = &tegra186_clusters[i];
cluster->table = init_vhint_table(pdev, bpmp, cluster);
cluster->table = init_vhint_table(pdev, bpmp, cluster, i);
if (IS_ERR(cluster->table)) {
err = PTR_ERR(cluster->table);
goto put_bpmp;
......
......@@ -21,7 +21,6 @@
#define KHZ 1000
#define REF_CLK_MHZ 408 /* 408 MHz */
#define US_DELAY 500
#define US_DELAY_MIN 2
#define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ)
#define MAX_CNT ~0U
......@@ -44,7 +43,6 @@ struct tegra194_cpufreq_data {
struct tegra_cpu_ctr {
u32 cpu;
u32 delay;
u32 coreclk_cnt, last_coreclk_cnt;
u32 refclk_cnt, last_refclk_cnt;
};
......@@ -112,7 +110,7 @@ static void tegra_read_counters(struct work_struct *work)
val = read_freq_feedback();
c->last_refclk_cnt = lower_32_bits(val);
c->last_coreclk_cnt = upper_32_bits(val);
udelay(c->delay);
udelay(US_DELAY);
val = read_freq_feedback();
c->refclk_cnt = lower_32_bits(val);
c->coreclk_cnt = upper_32_bits(val);
......@@ -139,7 +137,7 @@ static void tegra_read_counters(struct work_struct *work)
* @cpu - logical cpu whose freq to be updated
* Returns freq in KHz on success, 0 if cpu is offline
*/
static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
static unsigned int tegra194_calculate_speed(u32 cpu)
{
struct read_counters_work read_counters_work;
struct tegra_cpu_ctr c;
......@@ -153,7 +151,6 @@ static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
* interrupts enabled.
*/
read_counters_work.c.cpu = cpu;
read_counters_work.c.delay = delay;
INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters);
queue_work_on(cpu, read_counters_wq, &read_counters_work.work);
flush_work(&read_counters_work.work);
......@@ -180,9 +177,61 @@ static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
return (rate_mhz * KHZ); /* in KHz */
}
static void get_cpu_ndiv(void *ndiv)
{
u64 ndiv_val;
asm volatile("mrs %0, s3_0_c15_c0_4" : "=r" (ndiv_val) : );
*(u64 *)ndiv = ndiv_val;
}
static void set_cpu_ndiv(void *data)
{
struct cpufreq_frequency_table *tbl = data;
u64 ndiv_val = (u64)tbl->driver_data;
asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val));
}
static unsigned int tegra194_get_speed(u32 cpu)
{
return tegra194_get_speed_common(cpu, US_DELAY);
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
struct cpufreq_frequency_table *pos;
unsigned int rate;
u64 ndiv;
int ret;
u32 cl;
smp_call_function_single(cpu, get_cpu_cluster, &cl, true);
/* reconstruct actual cpu freq using counters */
rate = tegra194_calculate_speed(cpu);
/* get last written ndiv value */
ret = smp_call_function_single(cpu, get_cpu_ndiv, &ndiv, true);
if (WARN_ON_ONCE(ret))
return rate;
/*
* If the reconstructed frequency has acceptable delta from
* the last written value, then return freq corresponding
* to the last written ndiv value from freq_table. This is
* done to return consistent value.
*/
cpufreq_for_each_valid_entry(pos, data->tables[cl]) {
if (pos->driver_data != ndiv)
continue;
if (abs(pos->frequency - rate) > 115200) {
pr_warn("cpufreq: cpu%d,cur:%u,set:%u,set ndiv:%llu\n",
cpu, rate, pos->frequency, ndiv);
} else {
rate = pos->frequency;
}
break;
}
return rate;
}
static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
......@@ -196,9 +245,6 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
if (cl >= data->num_clusters)
return -EINVAL;
/* boot freq */
policy->cur = tegra194_get_speed_common(policy->cpu, US_DELAY_MIN);
/* set same policy for all cpus in a cluster */
for (cpu = (cl * 2); cpu < ((cl + 1) * 2); cpu++)
cpumask_set_cpu(cpu, policy->cpus);
......@@ -209,14 +255,6 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
return 0;
}
static void set_cpu_ndiv(void *data)
{
struct cpufreq_frequency_table *tbl = data;
u64 ndiv_val = (u64)tbl->driver_data;
asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val));
}
static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int index)
{
......
......@@ -591,6 +591,7 @@ static struct platform_driver ve_spc_cpufreq_platdrv = {
};
module_platform_driver(ve_spc_cpufreq_platdrv);
MODULE_ALIAS("platform:vexpress-spc-cpufreq");
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("Vexpress SPC ARM big LITTLE cpufreq driver");
......
......@@ -327,6 +327,8 @@ struct device *psci_dt_attach_cpu(int cpu)
if (cpu_online(cpu))
pm_runtime_get_sync(dev);
dev_pm_syscore_device(dev, true);
return dev;
}
......
......@@ -19,6 +19,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/psci.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/string.h>
......@@ -52,8 +53,9 @@ static inline int psci_enter_state(int idx, u32 state)
return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter, idx, state);
}
static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
static int __psci_enter_domain_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx,
bool s2idle)
{
struct psci_cpuidle_data *data = this_cpu_ptr(&psci_cpuidle_data);
u32 *states = data->psci_states;
......@@ -66,7 +68,12 @@ static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
return -1;
/* Do runtime PM to manage a hierarchical CPU toplogy. */
RCU_NONIDLE(pm_runtime_put_sync_suspend(pd_dev));
rcu_irq_enter_irqson();
if (s2idle)
dev_pm_genpd_suspend(pd_dev);
else
pm_runtime_put_sync_suspend(pd_dev);
rcu_irq_exit_irqson();
state = psci_get_domain_state();
if (!state)
......@@ -74,7 +81,12 @@ static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
ret = psci_cpu_suspend_enter(state) ? -1 : idx;
RCU_NONIDLE(pm_runtime_get_sync(pd_dev));
rcu_irq_enter_irqson();
if (s2idle)
dev_pm_genpd_resume(pd_dev);
else
pm_runtime_get_sync(pd_dev);
rcu_irq_exit_irqson();
cpu_pm_exit();
......@@ -83,6 +95,19 @@ static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
return ret;
}
static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
return __psci_enter_domain_idle_state(dev, drv, idx, false);
}
static int psci_enter_s2idle_domain_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int idx)
{
return __psci_enter_domain_idle_state(dev, drv, idx, true);
}
static int psci_idle_cpuhp_up(unsigned int cpu)
{
struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev);
......@@ -170,6 +195,7 @@ static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv,
* deeper states.
*/
drv->states[state_count - 1].enter = psci_enter_domain_idle_state;
drv->states[state_count - 1].enter_s2idle = psci_enter_s2idle_domain_idle_state;
psci_cpuidle_use_cpuhp = true;
return 0;
......
......@@ -368,6 +368,19 @@ void cpuidle_reflect(struct cpuidle_device *dev, int index)
cpuidle_curr_governor->reflect(dev, index);
}
/*
* Min polling interval of 10usec is a guess. It is assuming that
* for most users, the time for a single ping-pong workload like
* perf bench pipe would generally complete within 10usec but
* this is hardware dependant. Actual time can be estimated with
*
* perf bench sched pipe -l 10000
*
* Run multiple times to avoid cpufreq effects.
*/
#define CPUIDLE_POLL_MIN 10000
#define CPUIDLE_POLL_MAX (TICK_NSEC / 16)
/**
* cpuidle_poll_time - return amount of time to poll for,
* governors can override dev->poll_limit_ns if necessary
......@@ -382,15 +395,23 @@ u64 cpuidle_poll_time(struct cpuidle_driver *drv,
int i;
u64 limit_ns;
BUILD_BUG_ON(CPUIDLE_POLL_MIN > CPUIDLE_POLL_MAX);
if (dev->poll_limit_ns)
return dev->poll_limit_ns;
limit_ns = TICK_NSEC;
limit_ns = CPUIDLE_POLL_MAX;
for (i = 1; i < drv->state_count; i++) {
u64 state_limit;
if (dev->states_usage[i].disable)
continue;
limit_ns = drv->states[i].target_residency_ns;
state_limit = drv->states[i].target_residency_ns;
if (state_limit < CPUIDLE_POLL_MIN)
continue;
limit_ns = min_t(u64, state_limit, CPUIDLE_POLL_MAX);
break;
}
......
......@@ -121,16 +121,6 @@ config ARM_TEGRA_DEVFREQ
It reads ACTMON counters of memory controllers and adjusts the
operating frequencies and voltages with OPP support.
config ARM_TEGRA20_DEVFREQ
tristate "NVIDIA Tegra20 DEVFREQ Driver"
depends on (TEGRA_MC && TEGRA20_EMC) || COMPILE_TEST
depends on COMMON_CLK
select DEVFREQ_GOV_SIMPLE_ONDEMAND
help
This adds the DEVFREQ driver for the Tegra20 family of SoCs.
It reads Memory Controller counters and adjusts the operating
frequencies and voltages with OPP support.
config ARM_RK3399_DMC_DEVFREQ
tristate "ARM RK3399 DMC DEVFREQ Driver"
depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \
......
......@@ -13,7 +13,6 @@ obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o
obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o
obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o
# DEVFREQ Event Drivers
obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
This diff is collapsed.
......@@ -24,6 +24,7 @@
struct exynos_bus {
struct device *dev;
struct platform_device *icc_pdev;
struct devfreq *devfreq;
struct devfreq_event_dev **edev;
......@@ -156,18 +157,20 @@ static void exynos_bus_exit(struct device *dev)
if (ret < 0)
dev_warn(dev, "failed to disable the devfreq-event devices\n");
platform_device_unregister(bus->icc_pdev);
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
if (bus->opp_table) {
dev_pm_opp_put_regulators(bus->opp_table);
bus->opp_table = NULL;
}
dev_pm_opp_put_regulators(bus->opp_table);
bus->opp_table = NULL;
}
static void exynos_bus_passive_exit(struct device *dev)
{
struct exynos_bus *bus = dev_get_drvdata(dev);
platform_device_unregister(bus->icc_pdev);
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
}
......@@ -432,6 +435,18 @@ static int exynos_bus_probe(struct platform_device *pdev)
if (ret < 0)
goto err;
/* Create child platform device for the interconnect provider */
if (of_get_property(dev->of_node, "#interconnect-cells", NULL)) {
bus->icc_pdev = platform_device_register_data(
dev, "exynos-generic-icc",
PLATFORM_DEVID_AUTO, NULL, 0);
if (IS_ERR(bus->icc_pdev)) {
ret = PTR_ERR(bus->icc_pdev);
goto err;
}
}
max_state = bus->devfreq->profile->max_state;
min_freq = (bus->devfreq->profile->freq_table[0] / 1000);
max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000);
......@@ -444,10 +459,8 @@ static int exynos_bus_probe(struct platform_device *pdev)
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
err_reg:
if (!passive) {
dev_pm_opp_put_regulators(bus->opp_table);
bus->opp_table = NULL;
}
dev_pm_opp_put_regulators(bus->opp_table);
bus->opp_table = NULL;
return ret;
}
......
......@@ -13,6 +13,8 @@
#include <linux/devfreq.h>
#define DEVFREQ_NAME_LEN 16
#define to_devfreq(DEV) container_of((DEV), struct devfreq, dev)
/* Devfreq events */
......@@ -25,14 +27,32 @@
#define DEVFREQ_MIN_FREQ 0
#define DEVFREQ_MAX_FREQ ULONG_MAX
/*
* Definition of the governor feature flags
* - DEVFREQ_GOV_FLAG_IMMUTABLE
* : This governor is never changeable to other governors.
* - DEVFREQ_GOV_FLAG_IRQ_DRIVEN
* : The devfreq won't schedule the work for this governor.
*/
#define DEVFREQ_GOV_FLAG_IMMUTABLE BIT(0)
#define DEVFREQ_GOV_FLAG_IRQ_DRIVEN BIT(1)
/*
* Definition of governor attribute flags except for common sysfs attributes
* - DEVFREQ_GOV_ATTR_POLLING_INTERVAL
* : Indicate polling_interal sysfs attribute
* - DEVFREQ_GOV_ATTR_TIMER
* : Indicate timer sysfs attribute
*/
#define DEVFREQ_GOV_ATTR_POLLING_INTERVAL BIT(0)
#define DEVFREQ_GOV_ATTR_TIMER BIT(1)
/**
* struct devfreq_governor - Devfreq policy governor
* @node: list node - contains registered devfreq governors
* @name: Governor's name
* @immutable: Immutable flag for governor. If the value is 1,
* this governor is never changeable to other governor.
* @interrupt_driven: Devfreq core won't schedule polling work for this
* governor if value is set to 1.
* @attrs: Governor's sysfs attribute flags
* @flags: Governor's feature flags
* @get_target_freq: Returns desired operating frequency for the device.
* Basically, get_target_freq will run
* devfreq_dev_profile.get_dev_status() to get the
......@@ -50,8 +70,8 @@ struct devfreq_governor {
struct list_head node;
const char name[DEVFREQ_NAME_LEN];
const unsigned int immutable;
const unsigned int interrupt_driven;
const u64 attrs;
const u64 flags;
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
int (*event_handler)(struct devfreq *devfreq,
unsigned int event, void *data);
......@@ -67,6 +87,7 @@ int devfreq_add_governor(struct devfreq_governor *governor);
int devfreq_remove_governor(struct devfreq_governor *governor);
int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
int devfreq_update_target(struct devfreq *devfreq, unsigned long freq);
static inline int devfreq_update_stats(struct devfreq *df)
{
......
......@@ -92,36 +92,6 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
return ret;
}
static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
{
int ret;
if (!devfreq->governor)
return -EINVAL;
mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
ret = devfreq->governor->get_target_freq(devfreq, &freq);
if (ret < 0)
goto out;
ret = devfreq->profile->target(devfreq->dev.parent, &freq, 0);
if (ret < 0)
goto out;
if (devfreq->profile->freq_table
&& (devfreq_update_status(devfreq, freq)))
dev_err(&devfreq->dev,
"Couldn't update frequency transition information.\n");
devfreq->previous_freq = freq;
out:
mutex_unlock(&devfreq->lock);
return 0;
}
static int devfreq_passive_notifier_call(struct notifier_block *nb,
unsigned long event, void *ptr)
{
......@@ -131,17 +101,25 @@ static int devfreq_passive_notifier_call(struct notifier_block *nb,
struct devfreq *parent = (struct devfreq *)data->parent;
struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr;
unsigned long freq = freqs->new;
int ret = 0;
mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
switch (event) {
case DEVFREQ_PRECHANGE:
if (parent->previous_freq > freq)
update_devfreq_passive(devfreq, freq);
ret = devfreq_update_target(devfreq, freq);
break;
case DEVFREQ_POSTCHANGE:
if (parent->previous_freq < freq)
update_devfreq_passive(devfreq, freq);
ret = devfreq_update_target(devfreq, freq);
break;
}
mutex_unlock(&devfreq->lock);
if (ret < 0)
dev_warn(&devfreq->dev,
"failed to update devfreq using passive governor\n");
return NOTIFY_DONE;
}
......@@ -180,7 +158,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
static struct devfreq_governor devfreq_passive = {
.name = DEVFREQ_GOV_PASSIVE,
.immutable = 1,
.flags = DEVFREQ_GOV_FLAG_IMMUTABLE,
.get_target_freq = devfreq_passive_get_target_freq,
.event_handler = devfreq_passive_event_handler,
};
......
......@@ -117,6 +117,8 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
static struct devfreq_governor devfreq_simple_ondemand = {
.name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL
| DEVFREQ_GOV_ATTR_TIMER,
.get_target_freq = devfreq_simple_ondemand_func,
.event_handler = devfreq_simple_ondemand_handler,
};
......
// SPDX-License-Identifier: GPL-2.0
/*
* NVIDIA Tegra20 devfreq driver
*
* Copyright (C) 2019 GRATE-DRIVER project
*/
#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <soc/tegra/mc.h>
#include "governor.h"
#define MC_STAT_CONTROL 0x90
#define MC_STAT_EMC_CLOCK_LIMIT 0xa0
#define MC_STAT_EMC_CLOCKS 0xa4
#define MC_STAT_EMC_CONTROL 0xa8
#define MC_STAT_EMC_COUNT 0xb8
#define EMC_GATHER_CLEAR (1 << 8)
#define EMC_GATHER_ENABLE (3 << 8)
struct tegra_devfreq {
struct devfreq *devfreq;
struct clk *emc_clock;
void __iomem *regs;
};
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct devfreq *devfreq = tegra->devfreq;
struct dev_pm_opp *opp;
unsigned long rate;
int err;
opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp))
return PTR_ERR(opp);
rate = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(opp);
err = clk_set_min_rate(tegra->emc_clock, rate);
if (err)
return err;
err = clk_set_rate(tegra->emc_clock, 0);
if (err)
goto restore_min_rate;
return 0;
restore_min_rate:
clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
return err;
}
static int tegra_devfreq_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
/*
* EMC_COUNT returns number of memory events, that number is lower
* than the number of clocks. Conversion ratio of 1/8 results in a
* bit higher bandwidth than actually needed, it is good enough for
* the time being because drivers don't support requesting minimum
* needed memory bandwidth yet.
*
* TODO: adjust the ratio value once relevant drivers will support
* memory bandwidth management.
*/
stat->busy_time = readl_relaxed(tegra->regs + MC_STAT_EMC_COUNT);
stat->total_time = readl_relaxed(tegra->regs + MC_STAT_EMC_CLOCKS) / 8;
stat->current_frequency = clk_get_rate(tegra->emc_clock);
writel_relaxed(EMC_GATHER_CLEAR, tegra->regs + MC_STAT_CONTROL);
writel_relaxed(EMC_GATHER_ENABLE, tegra->regs + MC_STAT_CONTROL);
return 0;
}
static struct devfreq_dev_profile tegra_devfreq_profile = {
.polling_ms = 500,
.target = tegra_devfreq_target,
.get_dev_status = tegra_devfreq_get_dev_status,
};
static struct tegra_mc *tegra_get_memory_controller(void)
{
struct platform_device *pdev;
struct device_node *np;
struct tegra_mc *mc;
np = of_find_compatible_node(NULL, NULL, "nvidia,tegra20-mc-gart");
if (!np)
return ERR_PTR(-ENOENT);
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev)
return ERR_PTR(-ENODEV);
mc = platform_get_drvdata(pdev);
if (!mc)
return ERR_PTR(-EPROBE_DEFER);
return mc;
}
static int tegra_devfreq_probe(struct platform_device *pdev)
{
struct tegra_devfreq *tegra;
struct tegra_mc *mc;
unsigned long max_rate;
unsigned long rate;
int err;
mc = tegra_get_memory_controller();
if (IS_ERR(mc)) {
err = PTR_ERR(mc);
dev_err(&pdev->dev, "failed to get memory controller: %d\n",
err);
return err;
}
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
/* EMC is a system-critical clock that is always enabled */
tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clock)) {
err = PTR_ERR(tegra->emc_clock);
dev_err(&pdev->dev, "failed to get emc clock: %d\n", err);
return err;
}
tegra->regs = mc->regs;
max_rate = clk_round_rate(tegra->emc_clock, ULONG_MAX);
for (rate = 0; rate <= max_rate; rate++) {
rate = clk_round_rate(tegra->emc_clock, rate);
err = dev_pm_opp_add(&pdev->dev, rate, 0);
if (err) {
dev_err(&pdev->dev, "failed to add opp: %d\n", err);
goto remove_opps;
}
}
/*
* Reset statistic gathers state, select global bandwidth for the
* statistics collection mode and set clocks counter saturation
* limit to maximum.
*/
writel_relaxed(0x00000000, tegra->regs + MC_STAT_CONTROL);
writel_relaxed(0x00000000, tegra->regs + MC_STAT_EMC_CONTROL);
writel_relaxed(0xffffffff, tegra->regs + MC_STAT_EMC_CLOCK_LIMIT);
platform_set_drvdata(pdev, tegra);
tegra->devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
if (IS_ERR(tegra->devfreq)) {
err = PTR_ERR(tegra->devfreq);
goto remove_opps;
}
return 0;
remove_opps:
dev_pm_opp_remove_all_dynamic(&pdev->dev);
return err;
}
static int tegra_devfreq_remove(struct platform_device *pdev)
{
struct tegra_devfreq *tegra = platform_get_drvdata(pdev);
devfreq_remove_device(tegra->devfreq);
dev_pm_opp_remove_all_dynamic(&pdev->dev);
return 0;
}
static struct platform_driver tegra_devfreq_driver = {
.probe = tegra_devfreq_probe,
.remove = tegra_devfreq_remove,
.driver = {
.name = "tegra20-devfreq",
},
};
module_platform_driver(tegra_devfreq_driver);
MODULE_ALIAS("platform:tegra20-devfreq");
MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
MODULE_DESCRIPTION("NVIDIA Tegra20 devfreq driver");
MODULE_LICENSE("GPL v2");
......@@ -19,6 +19,8 @@
#include <linux/reset.h>
#include <linux/workqueue.h>
#include <soc/tegra/fuse.h>
#include "governor.h"
#define ACTMON_GLB_STATUS 0x0
......@@ -55,13 +57,6 @@
#define ACTMON_BELOW_WMARK_WINDOW 3
#define ACTMON_BOOST_FREQ_STEP 16000
/*
* Activity counter is incremented every 256 memory transactions, and each
* transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is
* 4 * 256 = 1024.
*/
#define ACTMON_COUNT_WEIGHT 0x400
/*
* ACTMON_AVERAGE_WINDOW_LOG2: default value for @DEV_CTRL_K_VAL, which
* translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128
......@@ -109,7 +104,7 @@ enum tegra_actmon_device {
MCCPU,
};
static const struct tegra_devfreq_device_config actmon_device_configs[] = {
static const struct tegra_devfreq_device_config tegra124_device_configs[] = {
{
/* MCALL: All memory accesses (including from the CPUs) */
.offset = 0x1c0,
......@@ -131,6 +126,28 @@ static const struct tegra_devfreq_device_config actmon_device_configs[] = {
},
};
static const struct tegra_devfreq_device_config tegra30_device_configs[] = {
{
/* MCALL: All memory accesses (including from the CPUs) */
.offset = 0x1c0,
.irq_mask = 1 << 26,
.boost_up_coeff = 200,
.boost_down_coeff = 50,
.boost_up_threshold = 20,
.boost_down_threshold = 10,
},
{
/* MCCPU: memory accesses from the CPUs */
.offset = 0x200,
.irq_mask = 1 << 25,
.boost_up_coeff = 800,
.boost_down_coeff = 40,
.boost_up_threshold = 27,
.boost_down_threshold = 10,
.avg_dependency_threshold = 16000, /* 16MHz in kHz units */
},
};
/**
* struct tegra_devfreq_device - state specific to an ACTMON device
*
......@@ -153,8 +170,15 @@ struct tegra_devfreq_device {
unsigned long target_freq;
};
struct tegra_devfreq_soc_data {
const struct tegra_devfreq_device_config *configs;
/* Weight value for count measurements */
unsigned int count_weight;
};
struct tegra_devfreq {
struct devfreq *devfreq;
struct opp_table *opp_table;
struct reset_control *reset;
struct clk *clock;
......@@ -168,11 +192,13 @@ struct tegra_devfreq {
struct delayed_work cpufreq_update_work;
struct notifier_block cpu_rate_change_nb;
struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
struct tegra_devfreq_device devices[2];
unsigned int irq;
bool started;
const struct tegra_devfreq_soc_data *soc;
};
struct tegra_actmon_emc_ratio {
......@@ -485,7 +511,7 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
tegra_devfreq_update_avg_wmark(tegra, dev);
tegra_devfreq_update_wmark(tegra, dev);
device_writel(dev, ACTMON_COUNT_WEIGHT, ACTMON_DEV_COUNT_WEIGHT);
device_writel(dev, tegra->soc->count_weight, ACTMON_DEV_COUNT_WEIGHT);
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
val |= ACTMON_DEV_CTRL_ENB_PERIODIC;
......@@ -612,34 +638,19 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct devfreq *devfreq = tegra->devfreq;
struct dev_pm_opp *opp;
unsigned long rate;
int err;
int ret;
opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp)) {
dev_err(dev, "Failed to find opp for %lu Hz\n", *freq);
return PTR_ERR(opp);
}
rate = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(opp);
err = clk_set_min_rate(tegra->emc_clock, rate * KHZ);
if (err)
return err;
err = clk_set_rate(tegra->emc_clock, 0);
if (err)
goto restore_min_rate;
return 0;
restore_min_rate:
clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
ret = dev_pm_opp_set_bw(dev, opp);
dev_pm_opp_put(opp);
return err;
return ret;
}
static int tegra_devfreq_get_dev_status(struct device *dev,
......@@ -655,7 +666,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
stat->private_data = tegra;
/* The below are to be used by the other governors */
stat->current_frequency = cur_freq;
stat->current_frequency = cur_freq * KHZ;
actmon_dev = &tegra->devices[MCALL];
......@@ -705,7 +716,12 @@ static int tegra_governor_get_target(struct devfreq *devfreq,
target_freq = max(target_freq, dev->target_freq);
}
*freq = target_freq;
/*
* tegra-devfreq driver operates with KHz units, while OPP table
* entries use Hz units. Hence we need to convert the units for the
* devfreq core.
*/
*freq = target_freq * KHZ;
return 0;
}
......@@ -765,14 +781,16 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
static struct devfreq_governor tegra_devfreq_governor = {
.name = "tegra_actmon",
.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL,
.flags = DEVFREQ_GOV_FLAG_IMMUTABLE
| DEVFREQ_GOV_FLAG_IRQ_DRIVEN,
.get_target_freq = tegra_governor_get_target,
.event_handler = tegra_governor_event_handler,
.immutable = true,
.interrupt_driven = true,
};
static int tegra_devfreq_probe(struct platform_device *pdev)
{
u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
struct tegra_devfreq_device *dev;
struct tegra_devfreq *tegra;
struct devfreq *devfreq;
......@@ -784,6 +802,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
if (!tegra)
return -ENOMEM;
tegra->soc = of_device_get_match_data(&pdev->dev);
tegra->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tegra->regs))
return PTR_ERR(tegra->regs);
......@@ -801,10 +821,9 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
}
tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clock)) {
dev_err(&pdev->dev, "Failed to get emc clock\n");
return PTR_ERR(tegra->emc_clock);
}
if (IS_ERR(tegra->emc_clock))
return dev_err_probe(&pdev->dev, PTR_ERR(tegra->emc_clock),
"Failed to get emc clock\n");
err = platform_get_irq(pdev, 0);
if (err < 0)
......@@ -822,11 +841,25 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return err;
}
tegra->opp_table = dev_pm_opp_set_supported_hw(&pdev->dev,
&hw_version, 1);
err = PTR_ERR_OR_ZERO(tegra->opp_table);
if (err) {
dev_err(&pdev->dev, "Failed to set supported HW: %d\n", err);
return err;
}
err = dev_pm_opp_of_add_table(&pdev->dev);
if (err) {
dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err);
goto put_hw;
}
err = clk_prepare_enable(tegra->clock);
if (err) {
dev_err(&pdev->dev,
"Failed to prepare and enable ACTMON clock\n");
return err;
goto remove_table;
}
err = reset_control_reset(tegra->reset);
......@@ -844,29 +877,12 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
tegra->max_freq = rate / KHZ;
for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
dev = tegra->devices + i;
dev->config = actmon_device_configs + i;
dev->config = tegra->soc->configs + i;
dev->regs = tegra->regs + dev->config->offset;
}
for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
rate = clk_round_rate(tegra->emc_clock, rate);
if (rate < 0) {
dev_err(&pdev->dev,
"Failed to round clock rate: %ld\n", rate);
err = rate;
goto remove_opps;
}
err = dev_pm_opp_add(&pdev->dev, rate / KHZ, 0);
if (err) {
dev_err(&pdev->dev, "Failed to add OPP: %d\n", err);
goto remove_opps;
}
}
platform_set_drvdata(pdev, tegra);
tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_notify_cb;
......@@ -882,7 +898,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
}
tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
tegra_devfreq_profile.initial_freq /= KHZ;
devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
"tegra_actmon", NULL);
......@@ -902,6 +917,10 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
reset_control_reset(tegra->reset);
disable_clk:
clk_disable_unprepare(tegra->clock);
remove_table:
dev_pm_opp_of_remove_table(&pdev->dev);
put_hw:
dev_pm_opp_put_supported_hw(tegra->opp_table);
return err;
}
......@@ -913,17 +932,33 @@ static int tegra_devfreq_remove(struct platform_device *pdev)
devfreq_remove_device(tegra->devfreq);
devfreq_remove_governor(&tegra_devfreq_governor);
dev_pm_opp_remove_all_dynamic(&pdev->dev);
reset_control_reset(tegra->reset);
clk_disable_unprepare(tegra->clock);
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_supported_hw(tegra->opp_table);
return 0;
}
static const struct tegra_devfreq_soc_data tegra124_soc = {
.configs = tegra124_device_configs,
/*
* Activity counter is incremented every 256 memory transactions,
* and each transaction takes 4 EMC clocks.
*/
.count_weight = 4 * 256,
};
static const struct tegra_devfreq_soc_data tegra30_soc = {
.configs = tegra30_device_configs,
.count_weight = 2 * 256,
};
static const struct of_device_id tegra_devfreq_of_match[] = {
{ .compatible = "nvidia,tegra30-actmon" },
{ .compatible = "nvidia,tegra124-actmon" },
{ .compatible = "nvidia,tegra30-actmon", .data = &tegra30_soc, },
{ .compatible = "nvidia,tegra124-actmon", .data = &tegra124_soc, },
{ },
};
......
......@@ -750,6 +750,13 @@ static bool scmi_fast_switch_possible(const struct scmi_handle *handle,
return dom->fc_info && dom->fc_info->level_set_addr;
}
static bool scmi_power_scale_mw_get(const struct scmi_handle *handle)
{
struct scmi_perf_info *pi = handle->perf_priv;
return pi->power_scale_mw;
}
static const struct scmi_perf_ops perf_ops = {
.limits_set = scmi_perf_limits_set,
.limits_get = scmi_perf_limits_get,
......@@ -762,6 +769,7 @@ static const struct scmi_perf_ops perf_ops = {
.freq_get = scmi_dvfs_freq_get,
.est_power_get = scmi_dvfs_est_power_get,
.fast_switch_possible = scmi_fast_switch_possible,
.power_scale_mw_get = scmi_power_scale_mw_get,
};
static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle,
......
......@@ -102,15 +102,10 @@ void lima_devfreq_fini(struct lima_device *ldev)
dev_pm_opp_of_remove_table(ldev->dev);
if (devfreq->regulators_opp_table) {
dev_pm_opp_put_regulators(devfreq->regulators_opp_table);
devfreq->regulators_opp_table = NULL;
}
if (devfreq->clkname_opp_table) {
dev_pm_opp_put_clkname(devfreq->clkname_opp_table);
devfreq->clkname_opp_table = NULL;
}
dev_pm_opp_put_regulators(devfreq->regulators_opp_table);
dev_pm_opp_put_clkname(devfreq->clkname_opp_table);
devfreq->regulators_opp_table = NULL;
devfreq->clkname_opp_table = NULL;
}
int lima_devfreq_init(struct lima_device *ldev)
......
......@@ -165,10 +165,8 @@ void panfrost_devfreq_fini(struct panfrost_device *pfdev)
pfdevfreq->opp_of_table_added = false;
}
if (pfdevfreq->regulators_opp_table) {
dev_pm_opp_put_regulators(pfdevfreq->regulators_opp_table);
pfdevfreq->regulators_opp_table = NULL;
}
dev_pm_opp_put_regulators(pfdevfreq->regulators_opp_table);
pfdevfreq->regulators_opp_table = NULL;
}
void panfrost_devfreq_resume(struct panfrost_device *pfdev)
......
......@@ -2322,7 +2322,7 @@ static int stm32f7_i2c_suspend(struct device *dev)
i2c_mark_adapter_suspended(&i2c_dev->adap);
if (!device_may_wakeup(dev) && !dev->power.wakeup_path) {
if (!device_may_wakeup(dev) && !device_wakeup_path(dev)) {
ret = stm32f7_i2c_regs_backup(i2c_dev);
if (ret < 0) {
i2c_mark_adapter_resumed(&i2c_dev->adap);
......@@ -2341,7 +2341,7 @@ static int stm32f7_i2c_resume(struct device *dev)
struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
int ret;
if (!device_may_wakeup(dev) && !dev->power.wakeup_path) {
if (!device_may_wakeup(dev) && !device_wakeup_path(dev)) {
ret = pm_runtime_force_resume(dev);
if (ret < 0)
return ret;
......
......@@ -908,8 +908,7 @@ static void core_put_v4(struct device *dev)
if (core->has_opp_table)
dev_pm_opp_of_remove_table(dev);
if (core->opp_table)
dev_pm_opp_put_clkname(core->opp_table);
dev_pm_opp_put_clkname(core->opp_table);
}
......
This diff is collapsed.
......@@ -112,8 +112,6 @@ 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;
......@@ -121,12 +119,15 @@ static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np)
/* It is safe to put the node now as all we need now is its address */
of_node_put(opp_table_np);
mutex_lock(&opp_table_lock);
list_for_each_entry(opp_table, &opp_tables, node) {
if (opp_table_np == opp_table->np) {
_get_opp_table_kref(opp_table);
mutex_unlock(&opp_table_lock);
return opp_table;
}
}
mutex_unlock(&opp_table_lock);
err:
return ERR_PTR(-ENODEV);
......@@ -169,7 +170,8 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
/* 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");
dev_warn(dev, "Empty OPP table\n");
return;
}
......@@ -377,7 +379,9 @@ int dev_pm_opp_of_find_icc_paths(struct device *dev,
struct icc_path **paths;
ret = _bandwidth_supported(dev, opp_table);
if (ret <= 0)
if (ret == -EINVAL)
return 0; /* Empty OPP table is a valid corner-case, let's not fail */
else if (ret <= 0)
return ret;
ret = 0;
......@@ -974,7 +978,7 @@ int dev_pm_opp_of_add_table(struct device *dev)
struct opp_table *opp_table;
int ret;
opp_table = dev_pm_opp_get_opp_table_indexed(dev, 0);
opp_table = _add_opp_table_indexed(dev, 0);
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
......@@ -1029,7 +1033,7 @@ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
index = 0;
}
opp_table = dev_pm_opp_get_opp_table_indexed(dev, index);
opp_table = _add_opp_table_indexed(dev, index);
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
......@@ -1335,7 +1339,7 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
goto failed;
}
ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus);
ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true);
if (ret)
goto failed;
......
......@@ -224,6 +224,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *o
int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic);
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu);
struct opp_table *_add_opp_table(struct device *dev);
struct opp_table *_add_opp_table_indexed(struct device *dev, int index);
void _put_opp_list_kref(struct opp_table *opp_table);
#ifdef CONFIG_OF
......
......@@ -1060,7 +1060,7 @@ static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable)
{
while (bus->parent) {
if (acpi_pm_device_can_wakeup(&bus->self->dev))
return acpi_pm_set_bridge_wakeup(&bus->self->dev, enable);
return acpi_pm_set_device_wakeup(&bus->self->dev, enable);
bus = bus->parent;
}
......@@ -1068,7 +1068,7 @@ static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable)
/* We have reached the root bus. */
if (bus->bridge) {
if (acpi_pm_device_can_wakeup(bus->bridge))
return acpi_pm_set_bridge_wakeup(bus->bridge, enable);
return acpi_pm_set_device_wakeup(bus->bridge, enable);
}
return 0;
}
......
......@@ -1011,6 +1011,10 @@ static const struct rapl_defaults rapl_defaults_cht = {
.compute_time_window = rapl_compute_time_window_atom,
};
static const struct rapl_defaults rapl_defaults_amd = {
.check_unit = rapl_check_unit_core,
};
static const struct x86_cpu_id rapl_ids[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(SANDYBRIDGE, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(SANDYBRIDGE_X, &rapl_defaults_core),
......@@ -1061,6 +1065,9 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNL, &rapl_defaults_hsw_server),
X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNM, &rapl_defaults_hsw_server),
X86_MATCH_VENDOR_FAM(AMD, 0x17, &rapl_defaults_amd),
X86_MATCH_VENDOR_FAM(AMD, 0x19, &rapl_defaults_amd),
{}
};
MODULE_DEVICE_TABLE(x86cpu, rapl_ids);
......
......@@ -31,7 +31,9 @@
#define MSR_VR_CURRENT_CONFIG 0x00000601
/* private data for RAPL MSR Interface */
static struct rapl_if_priv rapl_msr_priv = {
static struct rapl_if_priv *rapl_msr_priv;
static struct rapl_if_priv rapl_msr_priv_intel = {
.reg_unit = MSR_RAPL_POWER_UNIT,
.regs[RAPL_DOMAIN_PACKAGE] = {
MSR_PKG_POWER_LIMIT, MSR_PKG_ENERGY_STATUS, MSR_PKG_PERF_STATUS, 0, MSR_PKG_POWER_INFO },
......@@ -47,6 +49,14 @@ static struct rapl_if_priv rapl_msr_priv = {
.limits[RAPL_DOMAIN_PLATFORM] = 2,
};
static struct rapl_if_priv rapl_msr_priv_amd = {
.reg_unit = MSR_AMD_RAPL_POWER_UNIT,
.regs[RAPL_DOMAIN_PACKAGE] = {
0, MSR_AMD_PKG_ENERGY_STATUS, 0, 0, 0 },
.regs[RAPL_DOMAIN_PP0] = {
0, MSR_AMD_CORE_ENERGY_STATUS, 0, 0, 0 },
};
/* Handles CPU hotplug on multi-socket systems.
* If a CPU goes online as the first CPU of the physical package
* we add the RAPL package to the system. Similarly, when the last
......@@ -58,9 +68,9 @@ static int rapl_cpu_online(unsigned int cpu)
{
struct rapl_package *rp;
rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
rp = rapl_find_package_domain(cpu, rapl_msr_priv);
if (!rp) {
rp = rapl_add_package(cpu, &rapl_msr_priv);
rp = rapl_add_package(cpu, rapl_msr_priv);
if (IS_ERR(rp))
return PTR_ERR(rp);
}
......@@ -73,7 +83,7 @@ static int rapl_cpu_down_prep(unsigned int cpu)
struct rapl_package *rp;
int lead_cpu;
rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
rp = rapl_find_package_domain(cpu, rapl_msr_priv);
if (!rp)
return 0;
......@@ -136,40 +146,51 @@ static int rapl_msr_probe(struct platform_device *pdev)
const struct x86_cpu_id *id = x86_match_cpu(pl4_support_ids);
int ret;
rapl_msr_priv.read_raw = rapl_msr_read_raw;
rapl_msr_priv.write_raw = rapl_msr_write_raw;
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_INTEL:
rapl_msr_priv = &rapl_msr_priv_intel;
break;
case X86_VENDOR_AMD:
rapl_msr_priv = &rapl_msr_priv_amd;
break;
default:
pr_err("intel-rapl does not support CPU vendor %d\n", boot_cpu_data.x86_vendor);
return -ENODEV;
}
rapl_msr_priv->read_raw = rapl_msr_read_raw;
rapl_msr_priv->write_raw = rapl_msr_write_raw;
if (id) {
rapl_msr_priv.limits[RAPL_DOMAIN_PACKAGE] = 3;
rapl_msr_priv.regs[RAPL_DOMAIN_PACKAGE][RAPL_DOMAIN_REG_PL4] =
rapl_msr_priv->limits[RAPL_DOMAIN_PACKAGE] = 3;
rapl_msr_priv->regs[RAPL_DOMAIN_PACKAGE][RAPL_DOMAIN_REG_PL4] =
MSR_VR_CURRENT_CONFIG;
pr_info("PL4 support detected.\n");
}
rapl_msr_priv.control_type = powercap_register_control_type(NULL, "intel-rapl", NULL);
if (IS_ERR(rapl_msr_priv.control_type)) {
rapl_msr_priv->control_type = powercap_register_control_type(NULL, "intel-rapl", NULL);
if (IS_ERR(rapl_msr_priv->control_type)) {
pr_debug("failed to register powercap control_type.\n");
return PTR_ERR(rapl_msr_priv.control_type);
return PTR_ERR(rapl_msr_priv->control_type);
}
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online",
rapl_cpu_online, rapl_cpu_down_prep);
if (ret < 0)
goto out;
rapl_msr_priv.pcap_rapl_online = ret;
rapl_msr_priv->pcap_rapl_online = ret;
return 0;
out:
if (ret)
powercap_unregister_control_type(rapl_msr_priv.control_type);
powercap_unregister_control_type(rapl_msr_priv->control_type);
return ret;
}
static int rapl_msr_remove(struct platform_device *pdev)
{
cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
powercap_unregister_control_type(rapl_msr_priv.control_type);
cpuhp_remove_state(rapl_msr_priv->pcap_rapl_online);
powercap_unregister_control_type(rapl_msr_priv->control_type);
return 0;
}
......
......@@ -170,9 +170,8 @@ static ssize_t show_constraint_name(struct device *dev,
if (pconst && pconst->ops && pconst->ops->get_name) {
name = pconst->ops->get_name(power_zone, id);
if (name) {
snprintf(buf, POWERCAP_CONSTRAINT_NAME_LEN,
"%s\n", name);
buf[POWERCAP_CONSTRAINT_NAME_LEN] = '\0';
sprintf(buf, "%.*s\n", POWERCAP_CONSTRAINT_NAME_LEN - 1,
name);
len = strlen(buf);
}
}
......
......@@ -3,6 +3,7 @@
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
......@@ -90,6 +91,7 @@ u32 tegra_read_ram_code(void)
return straps >> PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT;
}
EXPORT_SYMBOL_GPL(tegra_read_ram_code);
static const struct of_device_id apbmisc_match[] __initconst = {
{ .compatible = "nvidia,tegra20-apbmisc", },
......
......@@ -620,7 +620,6 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev);
bool acpi_pm_device_can_wakeup(struct device *dev);
int acpi_pm_device_sleep_state(struct device *, int *, int);
int acpi_pm_set_device_wakeup(struct device *dev, bool enable);
int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable);
#else
static inline void acpi_pm_wakeup_event(struct device *dev)
{
......@@ -651,10 +650,6 @@ static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
{
return -ENODEV;
}
static inline int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable)
{
return -ENODEV;
}
#endif
#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT
......
......@@ -65,7 +65,6 @@ struct cpufreq_policy {
unsigned int max; /* in kHz */
unsigned int cur; /* in kHz, only needed if cpufreq
* governors are used */
unsigned int restore_freq; /* = policy->cur before transition */
unsigned int suspend_freq; /* freq to set during suspend */
unsigned int policy; /* see above */
......@@ -314,10 +313,6 @@ struct cpufreq_driver {
/* define one out of two */
int (*setpolicy)(struct cpufreq_policy *policy);
/*
* On failure, should always restore frequency to policy->restore_freq
* (i.e. old freq).
*/
int (*target)(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation); /* Deprecated */
......
......@@ -15,8 +15,6 @@
#include <linux/pm_opp.h>
#include <linux/pm_qos.h>
#define DEVFREQ_NAME_LEN 16
/* DEVFREQ governor name */
#define DEVFREQ_GOV_SIMPLE_ONDEMAND "simple_ondemand"
#define DEVFREQ_GOV_PERFORMANCE "performance"
......@@ -139,7 +137,6 @@ struct devfreq_stats {
* using devfreq.
* @profile: device-specific devfreq profile
* @governor: method how to choose frequency based on the usage.
* @governor_name: devfreq governor name for use with this devfreq
* @nb: notifier block used to notify devfreq object that it should
* reevaluate operable frequencies. Devfreq users may use
* devfreq.nb to the corresponding register notifier call chain.
......@@ -176,7 +173,6 @@ struct devfreq {
struct device dev;
struct devfreq_dev_profile *profile;
const struct devfreq_governor *governor;
char governor_name[DEVFREQ_NAME_LEN];
struct notifier_block nb;
struct delayed_work work;
......
......@@ -13,9 +13,8 @@
/**
* em_perf_state - Performance state of a performance domain
* @frequency: The frequency in KHz, for consistency with CPUFreq
* @power: The power consumed at this level, in milli-watts (by 1 CPU or
by a registered device). It can be a total power: static and
dynamic.
* @power: The power consumed at this level (by 1 CPU or by a registered
* device). It can be a total power: static and dynamic.
* @cost: The cost coefficient associated with this level, used during
* energy calculation. Equal to: power * max_frequency / frequency
*/
......@@ -29,6 +28,8 @@ struct em_perf_state {
* em_perf_domain - Performance domain
* @table: List of performance states, in ascending order
* @nr_perf_states: Number of performance states
* @milliwatts: Flag indicating the power values are in milli-Watts
* or some other scale.
* @cpus: Cpumask covering the CPUs of the domain. It's here
* for performance reasons to avoid potential cache
* misses during energy calculations in the scheduler
......@@ -43,6 +44,7 @@ struct em_perf_state {
struct em_perf_domain {
struct em_perf_state *table;
int nr_perf_states;
int milliwatts;
unsigned long cpus[];
};
......@@ -55,7 +57,7 @@ struct em_data_callback {
/**
* active_power() - Provide power at the next performance state of
* a device
* @power : Active power at the performance state in mW
* @power : Active power at the performance state
* (modified)
* @freq : Frequency at the performance state in kHz
* (modified)
......@@ -66,8 +68,8 @@ struct em_data_callback {
* and frequency.
*
* In case of CPUs, the power is the one of a single CPU in the domain,
* expressed in milli-watts. It is expected to fit in the
* [0, EM_MAX_POWER] range.
* expressed in milli-Watts or an abstract scale. It is expected to
* fit in the [0, EM_MAX_POWER] range.
*
* Return 0 on success.
*/
......@@ -79,7 +81,8 @@ struct em_data_callback {
struct em_perf_domain *em_cpu_get(int cpu);
struct em_perf_domain *em_pd_get(struct device *dev);
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *span);
struct em_data_callback *cb, cpumask_t *span,
bool milliwatts);
void em_dev_unregister_perf_domain(struct device *dev);
/**
......@@ -103,6 +106,9 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
struct em_perf_state *ps;
int i, cpu;
if (!sum_util)
return 0;
/*
* In order to predict the performance state, map the utilization of
* the most utilized CPU of the performance domain to a requested
......@@ -186,7 +192,8 @@ struct em_data_callback {};
static inline
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *span)
struct em_data_callback *cb, cpumask_t *span,
bool milliwatts)
{
return -EINVAL;
}
......
......@@ -255,24 +255,24 @@ static inline int pm_genpd_init(struct generic_pm_domain *genpd,
}
static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
{
return -ENOTSUPP;
return -EOPNOTSUPP;
}
static inline int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state)
{
return -ENOTSUPP;
return -EOPNOTSUPP;
}
static inline int dev_pm_genpd_add_notifier(struct device *dev,
struct notifier_block *nb)
{
return -ENOTSUPP;
return -EOPNOTSUPP;
}
static inline int dev_pm_genpd_remove_notifier(struct device *dev)
{
return -ENOTSUPP;
return -EOPNOTSUPP;
}
#define simple_qos_governor (*(struct dev_power_governor *)(NULL))
......@@ -280,11 +280,11 @@ static inline int dev_pm_genpd_remove_notifier(struct device *dev)
#endif
#ifdef CONFIG_PM_GENERIC_DOMAINS_SLEEP
void pm_genpd_syscore_poweroff(struct device *dev);
void pm_genpd_syscore_poweron(struct device *dev);
void dev_pm_genpd_suspend(struct device *dev);
void dev_pm_genpd_resume(struct device *dev);
#else
static inline void pm_genpd_syscore_poweroff(struct device *dev) {}
static inline void pm_genpd_syscore_poweron(struct device *dev) {}
static inline void dev_pm_genpd_suspend(struct device *dev) {}
static inline void dev_pm_genpd_resume(struct device *dev) {}
#endif
/* OF PM domain providers */
......@@ -325,13 +325,13 @@ struct device *genpd_dev_pm_attach_by_name(struct device *dev,
static inline int of_genpd_add_provider_simple(struct device_node *np,
struct generic_pm_domain *genpd)
{
return -ENOTSUPP;
return -EOPNOTSUPP;
}
static inline int of_genpd_add_provider_onecell(struct device_node *np,
struct genpd_onecell_data *data)
{
return -ENOTSUPP;
return -EOPNOTSUPP;
}
static inline void of_genpd_del_provider(struct device_node *np) {}
......@@ -387,7 +387,7 @@ static inline struct device *genpd_dev_pm_attach_by_name(struct device *dev,
static inline
struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
{
return ERR_PTR(-ENOTSUPP);
return ERR_PTR(-EOPNOTSUPP);
}
#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
......
......@@ -90,7 +90,6 @@ struct dev_pm_set_opp_data {
#if defined(CONFIG_PM_OPP)
struct opp_table *dev_pm_opp_get_opp_table(struct device *dev);
struct opp_table *dev_pm_opp_get_opp_table_indexed(struct device *dev, int index);
void dev_pm_opp_put_opp_table(struct opp_table *opp_table);
unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
......
......@@ -84,6 +84,11 @@ static inline bool device_may_wakeup(struct device *dev)
return dev->power.can_wakeup && !!dev->power.wakeup;
}
static inline bool device_wakeup_path(struct device *dev)
{
return dev->power.wakeup_path;
}
static inline void device_set_wakeup_path(struct device *dev)
{
dev->power.wakeup_path = true;
......@@ -174,6 +179,11 @@ static inline bool device_may_wakeup(struct device *dev)
return dev->power.can_wakeup && dev->power.should_wakeup;
}
static inline bool device_wakeup_path(struct device *dev)
{
return false;
}
static inline void device_set_wakeup_path(struct device *dev) {}
static inline void __pm_stay_awake(struct wakeup_source *ws) {}
......
......@@ -121,6 +121,7 @@ struct scmi_perf_ops {
unsigned long *rate, unsigned long *power);
bool (*fast_switch_possible)(const struct scmi_handle *handle,
struct device *dev);
bool (*power_scale_mw_get)(const struct scmi_handle *handle);
};
/**
......
......@@ -56,7 +56,11 @@ u32 tegra_read_straps(void);
u32 tegra_read_ram_code(void);
int tegra_fuse_readl(unsigned long offset, u32 *value);
#ifdef CONFIG_ARCH_TEGRA
extern struct tegra_sku_info tegra_sku_info;
#else
static struct tegra_sku_info tegra_sku_info __maybe_unused;
#endif
struct device *tegra_soc_device_register(void);
......
......@@ -8,6 +8,34 @@
#include <linux/devfreq.h>
#include <linux/tracepoint.h>
TRACE_EVENT(devfreq_frequency,
TP_PROTO(struct devfreq *devfreq, unsigned long freq,
unsigned long prev_freq),
TP_ARGS(devfreq, freq, prev_freq),
TP_STRUCT__entry(
__string(dev_name, dev_name(&devfreq->dev))
__field(unsigned long, freq)
__field(unsigned long, prev_freq)
__field(unsigned long, busy_time)
__field(unsigned long, total_time)
),
TP_fast_assign(
__assign_str(dev_name, dev_name(&devfreq->dev));
__entry->freq = freq;
__entry->prev_freq = prev_freq;
__entry->busy_time = devfreq->last_status.busy_time;
__entry->total_time = devfreq->last_status.total_time;
),
TP_printk("dev_name=%-30s freq=%-12lu prev_freq=%-12lu load=%-2lu",
__get_str(dev_name), __entry->freq, __entry->prev_freq,
__entry->total_time == 0 ? 0 :
(100 * __entry->busy_time) / __entry->total_time)
);
TRACE_EVENT(devfreq_monitor,
TP_PROTO(struct devfreq *devfreq),
......@@ -29,7 +57,7 @@ TRACE_EVENT(devfreq_monitor,
__assign_str(dev_name, dev_name(&devfreq->dev));
),
TP_printk("dev_name=%s freq=%lu polling_ms=%u load=%lu",
TP_printk("dev_name=%-30s freq=%-12lu polling_ms=%-3u load=%-2lu",
__get_str(dev_name), __entry->freq, __entry->polling_ms,
__entry->total_time == 0 ? 0 :
(100 * __entry->busy_time) / __entry->total_time)
......
......@@ -52,6 +52,17 @@ static int em_debug_cpus_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(em_debug_cpus);
static int em_debug_units_show(struct seq_file *s, void *unused)
{
struct em_perf_domain *pd = s->private;
char *units = pd->milliwatts ? "milliWatts" : "bogoWatts";
seq_printf(s, "%s\n", units);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(em_debug_units);
static void em_debug_create_pd(struct device *dev)
{
struct dentry *d;
......@@ -64,6 +75,8 @@ static void em_debug_create_pd(struct device *dev)
debugfs_create_file("cpus", 0444, d, dev->em_pd->cpus,
&em_debug_cpus_fops);
debugfs_create_file("units", 0444, d, dev->em_pd, &em_debug_units_fops);
/* Create a sub-directory for each performance state */
for (i = 0; i < dev->em_pd->nr_perf_states; i++)
em_debug_create_ps(&dev->em_pd->table[i], d);
......@@ -130,7 +143,7 @@ static int em_create_perf_table(struct device *dev, struct em_perf_domain *pd,
/*
* The power returned by active_state() is expected to be
* positive, in milli-watts and to fit into 16 bits.
* positive and to fit into 16 bits.
*/
if (!power || power > EM_MAX_POWER) {
dev_err(dev, "EM: invalid power: %lu\n",
......@@ -250,17 +263,24 @@ EXPORT_SYMBOL_GPL(em_cpu_get);
* @cpus : Pointer to cpumask_t, which in case of a CPU device is
* obligatory. It can be taken from i.e. 'policy->cpus'. For other
* type of devices this should be set to NULL.
* @milliwatts : Flag indicating that the power values are in milliWatts or
* in some other scale. It must be set properly.
*
* Create Energy Model tables for a performance domain using the callbacks
* defined in cb.
*
* The @milliwatts is important to set with correct value. Some kernel
* sub-systems might rely on this flag and check if all devices in the EM are
* using the same scale.
*
* If multiple clients register the same performance domain, all but the first
* registration will be ignored.
*
* Return 0 on success
*/
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *cpus)
struct em_data_callback *cb, cpumask_t *cpus,
bool milliwatts)
{
unsigned long cap, prev_cap = 0;
int cpu, ret;
......@@ -313,6 +333,8 @@ int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
if (ret)
goto unlock;
dev->em_pd->milliwatts = milliwatts;
em_debug_create_pd(dev);
dev_info(dev, "EM: created perf domain\n");
......
......@@ -224,6 +224,7 @@ EXPORT_SYMBOL_GPL(suspend_set_ops);
/**
* suspend_valid_only_mem - Generic memory-only valid callback.
* @state: Target system sleep state.
*
* Platform drivers that implement mem suspend only and only need to check for
* that in their .valid() callback can use this instead of rolling their own
......@@ -335,6 +336,7 @@ static int suspend_test(int level)
/**
* suspend_prepare - Prepare for entering system sleep state.
* @state: Target system sleep state.
*
* Common code run for every system sleep state that can be entered (except for
* hibernation). Run suspend notifiers, allocate the "suspend" console and
......
......@@ -244,6 +244,8 @@ void migrate_to_reboot_cpu(void)
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd);
if (pm_power_off_prepare)
pm_power_off_prepare();
migrate_to_reboot_cpu();
syscore_shutdown();
if (!cmd)
......
......@@ -102,12 +102,10 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
static bool sugov_update_next_freq(struct sugov_policy *sg_policy, u64 time,
unsigned int next_freq)
{
if (!sg_policy->need_freq_update) {
if (sg_policy->next_freq == next_freq)
return false;
} else {
if (sg_policy->need_freq_update)
sg_policy->need_freq_update = cpufreq_driver_test_flags(CPUFREQ_NEED_UPDATE_LIMITS);
}
else if (sg_policy->next_freq == next_freq)
return false;
sg_policy->next_freq = next_freq;
sg_policy->last_freq_update_time = time;
......
......@@ -315,6 +315,7 @@ int cmd_freq_set(int argc, char **argv)
}
}
get_cpustate();
/* loop over CPUs */
for (cpu = bitmask_first(cpus_chosen);
......@@ -332,5 +333,7 @@ int cmd_freq_set(int argc, char **argv)
}
}
print_offline_cpus();
return 0;
}
......@@ -95,6 +95,8 @@ int cmd_idle_set(int argc, char **argv)
exit(EXIT_FAILURE);
}
get_cpustate();
/* Default is: set all CPUs */
if (bitmask_isallclear(cpus_chosen))
bitmask_setall(cpus_chosen);
......@@ -181,5 +183,7 @@ int cmd_idle_set(int argc, char **argv)
break;
}
}
print_offline_cpus();
return EXIT_SUCCESS;
}
......@@ -34,6 +34,8 @@ int run_as_root;
int base_cpu;
/* Affected cpus chosen by -c/--cpu param */
struct bitmask *cpus_chosen;
struct bitmask *online_cpus;
struct bitmask *offline_cpus;
#ifdef DEBUG
int be_verbose;
......@@ -178,6 +180,8 @@ int main(int argc, const char *argv[])
char pathname[32];
cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
online_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
offline_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
argc--;
argv += 1;
......@@ -230,6 +234,10 @@ int main(int argc, const char *argv[])
ret = p->main(argc, argv);
if (cpus_chosen)
bitmask_free(cpus_chosen);
if (online_cpus)
bitmask_free(online_cpus);
if (offline_cpus)
bitmask_free(offline_cpus);
return ret;
}
print_help();
......
......@@ -94,6 +94,8 @@ struct cpupower_cpu_info {
*/
extern int get_cpu_info(struct cpupower_cpu_info *cpu_info);
extern struct cpupower_cpu_info cpupower_cpu_info;
/* cpuid and cpuinfo helpers **************************/
/* X86 ONLY ****************************************/
......@@ -171,4 +173,14 @@ static inline unsigned int cpuid_ecx(unsigned int op) { return 0; };
static inline unsigned int cpuid_edx(unsigned int op) { return 0; };
#endif /* defined(__i386__) || defined(__x86_64__) */
/*
* CPU State related functions
*/
extern struct bitmask *online_cpus;
extern struct bitmask *offline_cpus;
void get_cpustate(void);
void print_online_cpus(void);
void print_offline_cpus(void);
#endif /* __CPUPOWERUTILS_HELPERS__ */
......@@ -4,11 +4,11 @@
#include <errno.h>
#include <stdlib.h>
#if defined(__i386__) || defined(__x86_64__)
#include "helpers/helpers.h"
#include "helpers/sysfs.h"
#if defined(__i386__) || defined(__x86_64__)
#include "cpupower_intern.h"
#define MSR_AMD_HWCR 0xc0010015
......@@ -89,3 +89,63 @@ int cpupower_intel_set_perf_bias(unsigned int cpu, unsigned int val)
}
#endif /* #if defined(__i386__) || defined(__x86_64__) */
/* get_cpustate
*
* Gather the information of all online CPUs into bitmask struct
*/
void get_cpustate(void)
{
unsigned int cpu = 0;
bitmask_clearall(online_cpus);
bitmask_clearall(offline_cpus);
for (cpu = bitmask_first(cpus_chosen);
cpu <= bitmask_last(cpus_chosen); cpu++) {
if (cpupower_is_cpu_online(cpu) == 1)
bitmask_setbit(online_cpus, cpu);
else
bitmask_setbit(offline_cpus, cpu);
continue;
}
}
/* print_online_cpus
*
* Print the CPU numbers of all CPUs that are online currently
*/
void print_online_cpus(void)
{
int str_len = 0;
char *online_cpus_str = NULL;
str_len = online_cpus->size * 5;
online_cpus_str = (void *)malloc(sizeof(char) * str_len);
if (!bitmask_isallclear(online_cpus)) {
bitmask_displaylist(online_cpus_str, str_len, online_cpus);
printf(_("Following CPUs are online:\n%s\n"), online_cpus_str);
}
}
/* print_offline_cpus
*
* Print the CPU numbers of all CPUs that are offline currently
*/
void print_offline_cpus(void)
{
int str_len = 0;
char *offline_cpus_str = NULL;
str_len = offline_cpus->size * 5;
offline_cpus_str = (void *)malloc(sizeof(char) * str_len);
if (!bitmask_isallclear(offline_cpus)) {
bitmask_displaylist(offline_cpus_str, str_len, offline_cpus);
printf(_("Following CPUs are offline:\n%s\n"), offline_cpus_str);
printf(_("cpupower set operation was not performed on them\n"));
}
}
......@@ -6,7 +6,7 @@
|_| |___/ |_|
pm-graph: suspend/resume/boot timing analysis tools
Version: 5.7
Version: 5.8
Author: Todd Brandt <todd.e.brandt@intel.com>
Home Page: https://01.org/pm-graph
......@@ -61,7 +61,7 @@
- runs with python2 or python3, choice is made by /usr/bin/python link
- python
- python-configparser (for python2 sleepgraph)
- python-requests (for googlesheet.py)
- python-requests (for stresstester.py)
- linux-tools-common (for turbostat usage in sleepgraph)
Ubuntu:
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment