Commit 8f5e823f authored by Linus Torvalds's avatar Linus Torvalds

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

Pull power management updates from Rafael Wysocki:
 "These fix the (Intel-specific) Performance and Energy Bias Hint (EPB)
  handling and expose it to user space via sysfs, fix and clean up
  several cpufreq drivers, add support for two new chips to the qoriq
  cpufreq driver, fix, simplify and clean up the cpufreq core and the
  schedutil governor, add support for "CPU" domains to the generic power
  domains (genpd) framework and provide low-level PSCI firmware support
  for that feature, fix the exynos cpuidle driver and fix a couple of
  issues in the devfreq subsystem and clean it up.

  Specifics:

   - Fix the handling of Performance and Energy Bias Hint (EPB) on Intel
     processors and expose it to user space via sysfs to avoid having to
     access it through the generic MSR I/F (Rafael Wysocki).

   - Improve the handling of global turbo changes made by the platform
     firmware in the intel_pstate driver (Rafael Wysocki).

   - Convert some slow-path static_cpu_has() callers to boot_cpu_has()
     in cpufreq (Borislav Petkov).

   - Fix the frequency calculation loop in the armada-37xx cpufreq
     driver (Gregory CLEMENT).

   - Fix possible object reference leaks in multuple cpufreq drivers
     (Wen Yang).

   - Fix kerneldoc comment in the centrino cpufreq driver (dongjian).

   - Clean up the ACPI and maple cpufreq drivers (Viresh Kumar, Mohan
     Kumar).

   - Add support for lx2160a and ls1028a to the qoriq cpufreq driver
     (Vabhav Sharma, Yuantian Tang).

   - Fix kobject memory leak in the cpufreq core (Viresh Kumar).

   - Simplify the IOwait boosting in the schedutil cpufreq governor and
     rework the TSC cpufreq notifier on x86 (Rafael Wysocki).

   - Clean up the cpufreq core and statistics code (Yue Hu, Kyle Lin).

   - Improve the cpufreq documentation, add SPDX license tags to some PM
     documentation files and unify copyright notices in them (Rafael
     Wysocki).

   - Add support for "CPU" domains to the generic power domains (genpd)
     framework and provide low-level PSCI firmware support for that
     feature (Ulf Hansson).

   - Rearrange the PSCI firmware support code and add support for
     SYSTEM_RESET2 to it (Ulf Hansson, Sudeep Holla).

   - Improve genpd support for devices in multiple power domains (Ulf
     Hansson).

   - Unify target residency for the AFTR and coupled AFTR states in the
     exynos cpuidle driver (Marek Szyprowski).

   - Introduce new helper routine in the operating performance points
     (OPP) framework (Andrew-sh.Cheng).

   - Add support for passing on-die termination (ODT) and auto power
     down parameters from the kernel to Trusted Firmware-A (TF-A) to the
     rk3399_dmc devfreq driver (Enric Balletbo i Serra).

   - Add tracing to devfreq (Lukasz Luba).

   - Make the exynos-bus devfreq driver suspend all devices on system
     shutdown (Marek Szyprowski).

   - Fix a few minor issues in the devfreq subsystem and clean it up
     somewhat (Enric Balletbo i Serra, MyungJoo Ham, Rob Herring,
     Saravana Kannan, Yangtao Li).

   - Improve system wakeup diagnostics (Stephen Boyd).

   - Rework filesystem sync messages emitted during system suspend and
     hibernation (Harry Pan)"

* tag 'pm-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (72 commits)
  cpufreq: Fix kobject memleak
  cpufreq: armada-37xx: fix frequency calculation for opp
  cpufreq: centrino: Fix centrino_setpolicy() kerneldoc comment
  cpufreq: qoriq: add support for lx2160a
  x86: tsc: Rework time_cpufreq_notifier()
  PM / Domains: Allow to attach a CPU via genpd_dev_pm_attach_by_id|name()
  PM / Domains: Search for the CPU device outside the genpd lock
  PM / Domains: Drop unused in-parameter to some genpd functions
  PM / Domains: Use the base device for driver_deferred_probe_check_state()
  cpufreq: qoriq: Add ls1028a chip support
  PM / Domains: Enable genpd_dev_pm_attach_by_id|name() for single PM domain
  PM / Domains: Allow OF lookup for multi PM domain case from ->attach_dev()
  PM / Domains: Don't kfree() the virtual device in the error path
  cpufreq: Move ->get callback check outside of __cpufreq_get()
  PM / Domains: remove unnecessary unlikely()
  cpufreq: Remove needless bios_limit check in show_bios_limit()
  drivers/cpufreq/acpi-cpufreq.c: This fixes the following checkpatch warning
  firmware/psci: add support for SYSTEM_RESET2
  PM / devfreq: add tracing for scheduling work
  trace: events: add devfreq trace event file
  ...
parents 59df1c2b e07095c9
...@@ -520,3 +520,21 @@ Description: Control Symetric Multi Threading (SMT) ...@@ -520,3 +520,21 @@ Description: Control Symetric Multi Threading (SMT)
If control status is "forceoff" or "notsupported" writes If control status is "forceoff" or "notsupported" writes
are rejected. are rejected.
What: /sys/devices/system/cpu/cpu#/power/energy_perf_bias
Date: March 2019
Contact: linux-pm@vger.kernel.org
Description: Intel Energy and Performance Bias Hint (EPB)
EPB for the given CPU in a sliding scale 0 - 15, where a value
of 0 corresponds to a hint preference for highest performance
and a value of 15 corresponds to the maximum energy savings.
In order to change the EPB value for the CPU, write either
a number in the 0 - 15 sliding scale above, or one of the
strings: "performance", "balance-performance", "normal",
"balance-power", "power" (that represent values reflected by
their meaning), to this attribute.
This attribute is present for all online CPUs supporting the
Intel EPB feature.
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
.. |struct cpufreq_policy| replace:: :c:type:`struct cpufreq_policy <cpufreq_policy>` .. |struct cpufreq_policy| replace:: :c:type:`struct cpufreq_policy <cpufreq_policy>`
.. |intel_pstate| replace:: :doc:`intel_pstate <intel_pstate>` .. |intel_pstate| replace:: :doc:`intel_pstate <intel_pstate>`
...@@ -5,9 +8,10 @@ ...@@ -5,9 +8,10 @@
CPU Performance Scaling CPU Performance Scaling
======================= =======================
:: :Copyright: |copy| 2017 Intel Corporation
:Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Copyright (c) 2017 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
The Concept of CPU Performance Scaling The Concept of CPU Performance Scaling
====================================== ======================================
...@@ -396,8 +400,8 @@ RT or deadline scheduling classes, the governor will increase the frequency to ...@@ -396,8 +400,8 @@ RT or deadline scheduling classes, the governor will increase the frequency to
the allowed maximum (that is, the ``scaling_max_freq`` policy limit). In turn, the allowed maximum (that is, the ``scaling_max_freq`` policy limit). In turn,
if it is invoked by the CFS scheduling class, the governor will use the if it is invoked by the CFS scheduling class, the governor will use the
Per-Entity Load Tracking (PELT) metric for the root control group of the Per-Entity Load Tracking (PELT) metric for the root control group of the
given CPU as the CPU utilization estimate (see the `Per-entity load tracking`_ given CPU as the CPU utilization estimate (see the *Per-entity load tracking*
LWN.net article for a description of the PELT mechanism). Then, the new LWN.net article [1]_ for a description of the PELT mechanism). Then, the new
CPU frequency to apply is computed in accordance with the formula CPU frequency to apply is computed in accordance with the formula
f = 1.25 * ``f_0`` * ``util`` / ``max`` f = 1.25 * ``f_0`` * ``util`` / ``max``
...@@ -698,4 +702,8 @@ hardware feature (e.g. all Intel ones), even if the ...@@ -698,4 +702,8 @@ hardware feature (e.g. all Intel ones), even if the
:c:macro:`CONFIG_X86_ACPI_CPUFREQ_CPB` configuration option is set. :c:macro:`CONFIG_X86_ACPI_CPUFREQ_CPB` configuration option is set.
.. _Per-entity load tracking: https://lwn.net/Articles/531853/ References
==========
.. [1] Jonathan Corbet, *Per-entity load tracking*,
https://lwn.net/Articles/531853/
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
.. |struct cpuidle_state| replace:: :c:type:`struct cpuidle_state <cpuidle_state>` .. |struct cpuidle_state| replace:: :c:type:`struct cpuidle_state <cpuidle_state>`
.. |cpufreq| replace:: :doc:`CPU Performance Scaling <cpufreq>` .. |cpufreq| replace:: :doc:`CPU Performance Scaling <cpufreq>`
...@@ -5,9 +8,10 @@ ...@@ -5,9 +8,10 @@
CPU Idle Time Management CPU Idle Time Management
======================== ========================
:: :Copyright: |copy| 2018 Intel Corporation
:Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Copyright (c) 2018 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Concepts Concepts
======== ========
......
.. SPDX-License-Identifier: GPL-2.0
================ ================
Power Management Power Management
================ ================
......
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
======================================
Intel Performance and Energy Bias Hint
======================================
:Copyright: |copy| 2019 Intel Corporation
:Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
.. kernel-doc:: arch/x86/kernel/cpu/intel_epb.c
:doc: overview
Intel Performance and Energy Bias Attribute in ``sysfs``
========================================================
The Intel Performance and Energy Bias Hint (EPB) value for a given (logical) CPU
can be checked or updated through a ``sysfs`` attribute (file) under
:file:`/sys/devices/system/cpu/cpu<N>/power/`, where the CPU number ``<N>``
is allocated at the system initialization time:
``energy_perf_bias``
Shows the current EPB value for the CPU in a sliding scale 0 - 15, where
a value of 0 corresponds to a hint preference for highest performance
and a value of 15 corresponds to the maximum energy savings.
In order to update the EPB value for the CPU, this attribute can be
written to, either with a number in the 0 - 15 sliding scale above, or
with one of the strings: "performance", "balance-performance", "normal",
"balance-power", "power" that represent values reflected by their
meaning.
This attribute is present for all online CPUs supporting the EPB
feature.
Note that while the EPB interface to the processor is defined at the logical CPU
level, the physical register backing it may be shared by multiple CPUs (for
example, SMT siblings or cores in one package). For this reason, updating the
EPB value for one CPU may cause the EPB values for other CPUs to change.
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
=============================================== ===============================================
``intel_pstate`` CPU Performance Scaling Driver ``intel_pstate`` CPU Performance Scaling Driver
=============================================== ===============================================
:: :Copyright: |copy| 2017 Intel Corporation
Copyright (c) 2017 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com> :Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
General Information General Information
...@@ -20,11 +23,10 @@ you have not done that yet.] ...@@ -20,11 +23,10 @@ you have not done that yet.]
For the processors supported by ``intel_pstate``, the P-state concept is broader For the processors supported by ``intel_pstate``, the P-state concept is broader
than just an operating frequency or an operating performance point (see the than just an operating frequency or an operating performance point (see the
`LinuxCon Europe 2015 presentation by Kristen Accardi <LCEU2015_>`_ for more LinuxCon Europe 2015 presentation by Kristen Accardi [1]_ for more
information about that). For this reason, the representation of P-states used information about that). For this reason, the representation of P-states used
by ``intel_pstate`` internally follows the hardware specification (for details by ``intel_pstate`` internally follows the hardware specification (for details
refer to `Intel® 64 and IA-32 Architectures Software Developers Manual refer to Intel Software Developers Manual [2]_). However, the ``CPUFreq`` core
Volume 3: System Programming Guide <SDM_>`_). However, the ``CPUFreq`` core
uses frequencies for identifying operating performance points of CPUs and uses frequencies for identifying operating performance points of CPUs and
frequencies are involved in the user space interface exposed by it, so frequencies are involved in the user space interface exposed by it, so
``intel_pstate`` maps its internal representation of P-states to frequencies too ``intel_pstate`` maps its internal representation of P-states to frequencies too
...@@ -561,9 +563,9 @@ or to pin every task potentially sensitive to them to a specific CPU.] ...@@ -561,9 +563,9 @@ or to pin every task potentially sensitive to them to a specific CPU.]
On the majority of systems supported by ``intel_pstate``, the ACPI tables On the majority of systems supported by ``intel_pstate``, the ACPI tables
provided by the platform firmware contain ``_PSS`` objects returning information provided by the platform firmware contain ``_PSS`` objects returning information
that can be used for CPU performance scaling (refer to the `ACPI specification`_ that can be used for CPU performance scaling (refer to the ACPI specification
for details on the ``_PSS`` objects and the format of the information returned [3]_ for details on the ``_PSS`` objects and the format of the information
by them). returned by them).
The information returned by the ACPI ``_PSS`` objects is used by the The information returned by the ACPI ``_PSS`` objects is used by the
``acpi-cpufreq`` scaling driver. On systems supported by ``intel_pstate`` ``acpi-cpufreq`` scaling driver. On systems supported by ``intel_pstate``
...@@ -728,6 +730,14 @@ P-state is called, the ``ftrace`` filter can be set to to ...@@ -728,6 +730,14 @@ P-state is called, the ``ftrace`` filter can be set to to
<idle>-0 [000] ..s. 2537.654843: intel_pstate_set_pstate <-intel_pstate_timer_func <idle>-0 [000] ..s. 2537.654843: intel_pstate_set_pstate <-intel_pstate_timer_func
.. _LCEU2015: http://events.linuxfoundation.org/sites/events/files/slides/LinuxConEurope_2015.pdf References
.. _SDM: http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html ==========
.. _ACPI specification: http://www.uefi.org/sites/default/files/resources/ACPI_6_1.pdf
.. [1] Kristen Accardi, *Balancing Power and Performance in the Linux Kernel*,
http://events.linuxfoundation.org/sites/events/files/slides/LinuxConEurope_2015.pdf
.. [2] *Intel® 64 and IA-32 Architectures Software Developers Manual Volume 3: System Programming Guide*,
http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html
.. [3] *Advanced Configuration and Power Interface Specification*,
https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
=================== ===================
System Sleep States System Sleep States
=================== ===================
:: :Copyright: |copy| 2017 Intel Corporation
:Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Copyright (c) 2017 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Sleep states are global low-power states of the entire system in which user Sleep states are global low-power states of the entire system in which user
space code cannot be executed and the overall system activity is significantly space code cannot be executed and the overall system activity is significantly
......
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
=========================== ===========================
Power Management Strategies Power Management Strategies
=========================== ===========================
:: :Copyright: |copy| 2017 Intel Corporation
:Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Copyright (c) 2017 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
The Linux kernel supports two major high-level power management strategies. The Linux kernel supports two major high-level power management strategies.
......
.. SPDX-License-Identifier: GPL-2.0
============================ ============================
System-Wide Power Management System-Wide Power Management
============================ ============================
......
.. SPDX-License-Identifier: GPL-2.0
============================== ==============================
Working-State Power Management Working-State Power Management
============================== ==============================
...@@ -8,3 +10,4 @@ Working-State Power Management ...@@ -8,3 +10,4 @@ Working-State Power Management
cpuidle cpuidle
cpufreq cpufreq
intel_pstate intel_pstate
intel_epb
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
.. |struct cpuidle_governor| replace:: :c:type:`struct cpuidle_governor <cpuidle_governor>` .. |struct cpuidle_governor| replace:: :c:type:`struct cpuidle_governor <cpuidle_governor>`
.. |struct cpuidle_device| replace:: :c:type:`struct cpuidle_device <cpuidle_device>` .. |struct cpuidle_device| replace:: :c:type:`struct cpuidle_device <cpuidle_device>`
.. |struct cpuidle_driver| replace:: :c:type:`struct cpuidle_driver <cpuidle_driver>` .. |struct cpuidle_driver| replace:: :c:type:`struct cpuidle_driver <cpuidle_driver>`
...@@ -7,9 +10,9 @@ ...@@ -7,9 +10,9 @@
CPU Idle Time Management CPU Idle Time Management
======================== ========================
:: :Copyright: |copy| 2019 Intel Corporation
Copyright (c) 2019 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com> :Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
CPU Idle Time Management Subsystem CPU Idle Time Management Subsystem
......
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
.. |struct dev_pm_ops| replace:: :c:type:`struct dev_pm_ops <dev_pm_ops>` .. |struct dev_pm_ops| replace:: :c:type:`struct dev_pm_ops <dev_pm_ops>`
.. |struct dev_pm_domain| replace:: :c:type:`struct dev_pm_domain <dev_pm_domain>` .. |struct dev_pm_domain| replace:: :c:type:`struct dev_pm_domain <dev_pm_domain>`
.. |struct bus_type| replace:: :c:type:`struct bus_type <bus_type>` .. |struct bus_type| replace:: :c:type:`struct bus_type <bus_type>`
...@@ -12,11 +15,12 @@ ...@@ -12,11 +15,12 @@
Device Power Management Basics Device Power Management Basics
============================== ==============================
:: :Copyright: |copy| 2010-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
:Copyright: |copy| 2010 Alan Stern <stern@rowland.harvard.edu>
:Copyright: |copy| 2016 Intel Corporation
:Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Copyright (c) 2010-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
Copyright (c) 2010 Alan Stern <stern@rowland.harvard.edu>
Copyright (c) 2016 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Most of the code in Linux is device drivers, so most of the Linux power Most of the code in Linux is device drivers, so most of the Linux power
management (PM) code is also driver-specific. Most drivers will do very management (PM) code is also driver-specific. Most drivers will do very
......
.. SPDX-License-Identifier: GPL-2.0
=============================== ===============================
CPU and Device Power Management CPU and Device Power Management
=============================== ===============================
......
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
============================= =============================
Suspend/Hibernation Notifiers Suspend/Hibernation Notifiers
============================= =============================
:: :Copyright: |copy| 2016 Intel Corporation
:Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Copyright (c) 2016 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
There are some operations that subsystems or drivers may want to carry out There are some operations that subsystems or drivers may want to carry out
before hibernation/suspend or after restore/resume, but they require the system before hibernation/suspend or after restore/resume, but they require the system
......
.. SPDX-License-Identifier: GPL-2.0
================================== ==================================
Device Power Management Data Types Device Power Management Data Types
================================== ==================================
......
...@@ -4553,6 +4553,7 @@ S: Maintained ...@@ -4553,6 +4553,7 @@ S: Maintained
F: drivers/devfreq/ F: drivers/devfreq/
F: include/linux/devfreq.h F: include/linux/devfreq.h
F: Documentation/devicetree/bindings/devfreq/ F: Documentation/devicetree/bindings/devfreq/
F: include/trace/events/devfreq.h
DEVICE FREQUENCY EVENT (DEVFREQ-EVENT) DEVICE FREQUENCY EVENT (DEVFREQ-EVENT)
M: Chanwoo Choi <cw00.choi@samsung.com> M: Chanwoo Choi <cw00.choi@samsung.com>
...@@ -12416,7 +12417,7 @@ M: Mark Rutland <mark.rutland@arm.com> ...@@ -12416,7 +12417,7 @@ M: Mark Rutland <mark.rutland@arm.com>
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
L: linux-arm-kernel@lists.infradead.org L: linux-arm-kernel@lists.infradead.org
S: Maintained S: Maintained
F: drivers/firmware/psci*.c F: drivers/firmware/psci/
F: include/linux/psci.h F: include/linux/psci.h
F: include/uapi/linux/psci.h F: include/uapi/linux/psci.h
......
...@@ -28,7 +28,7 @@ obj-y += cpuid-deps.o ...@@ -28,7 +28,7 @@ obj-y += cpuid-deps.o
obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o
obj-$(CONFIG_CPU_SUP_INTEL) += intel.o intel_pconfig.o obj-$(CONFIG_CPU_SUP_INTEL) += intel.o intel_pconfig.o intel_epb.o
obj-$(CONFIG_CPU_SUP_AMD) += amd.o obj-$(CONFIG_CPU_SUP_AMD) += amd.o
obj-$(CONFIG_CPU_SUP_HYGON) += hygon.o obj-$(CONFIG_CPU_SUP_HYGON) += hygon.o
obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o
......
...@@ -1824,23 +1824,6 @@ void cpu_init(void) ...@@ -1824,23 +1824,6 @@ void cpu_init(void)
} }
#endif #endif
static void bsp_resume(void)
{
if (this_cpu->c_bsp_resume)
this_cpu->c_bsp_resume(&boot_cpu_data);
}
static struct syscore_ops cpu_syscore_ops = {
.resume = bsp_resume,
};
static int __init init_cpu_syscore(void)
{
register_syscore_ops(&cpu_syscore_ops);
return 0;
}
core_initcall(init_cpu_syscore);
/* /*
* The microcode loader calls this upon late microcode load to recheck features, * The microcode loader calls this upon late microcode load to recheck features,
* only when microcode has been updated. Caller holds microcode_mutex and CPU * only when microcode has been updated. Caller holds microcode_mutex and CPU
......
...@@ -14,7 +14,6 @@ struct cpu_dev { ...@@ -14,7 +14,6 @@ struct cpu_dev {
void (*c_init)(struct cpuinfo_x86 *); void (*c_init)(struct cpuinfo_x86 *);
void (*c_identify)(struct cpuinfo_x86 *); void (*c_identify)(struct cpuinfo_x86 *);
void (*c_detect_tlb)(struct cpuinfo_x86 *); void (*c_detect_tlb)(struct cpuinfo_x86 *);
void (*c_bsp_resume)(struct cpuinfo_x86 *);
int c_x86_vendor; int c_x86_vendor;
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
/* Optional vendor specific routine to obtain the cache size. */ /* Optional vendor specific routine to obtain the cache size. */
......
...@@ -596,36 +596,6 @@ static void detect_tme(struct cpuinfo_x86 *c) ...@@ -596,36 +596,6 @@ static void detect_tme(struct cpuinfo_x86 *c)
c->x86_phys_bits -= keyid_bits; c->x86_phys_bits -= keyid_bits;
} }
static void init_intel_energy_perf(struct cpuinfo_x86 *c)
{
u64 epb;
/*
* Initialize MSR_IA32_ENERGY_PERF_BIAS if not already initialized.
* (x86_energy_perf_policy(8) is available to change it at run-time.)
*/
if (!cpu_has(c, X86_FEATURE_EPB))
return;
rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
if ((epb & 0xF) != ENERGY_PERF_BIAS_PERFORMANCE)
return;
pr_info_once("ENERGY_PERF_BIAS: Set to 'normal', was 'performance'\n");
pr_info_once("ENERGY_PERF_BIAS: View and update with x86_energy_perf_policy(8)\n");
epb = (epb & ~0xF) | ENERGY_PERF_BIAS_NORMAL;
wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
}
static void intel_bsp_resume(struct cpuinfo_x86 *c)
{
/*
* MSR_IA32_ENERGY_PERF_BIAS is lost across suspend/resume,
* so reinitialize it properly like during bootup:
*/
init_intel_energy_perf(c);
}
static void init_cpuid_fault(struct cpuinfo_x86 *c) static void init_cpuid_fault(struct cpuinfo_x86 *c)
{ {
u64 msr; u64 msr;
...@@ -763,8 +733,6 @@ static void init_intel(struct cpuinfo_x86 *c) ...@@ -763,8 +733,6 @@ static void init_intel(struct cpuinfo_x86 *c)
if (cpu_has(c, X86_FEATURE_TME)) if (cpu_has(c, X86_FEATURE_TME))
detect_tme(c); detect_tme(c);
init_intel_energy_perf(c);
init_intel_misc_features(c); init_intel_misc_features(c);
} }
...@@ -1023,9 +991,7 @@ static const struct cpu_dev intel_cpu_dev = { ...@@ -1023,9 +991,7 @@ static const struct cpu_dev intel_cpu_dev = {
.c_detect_tlb = intel_detect_tlb, .c_detect_tlb = intel_detect_tlb,
.c_early_init = early_init_intel, .c_early_init = early_init_intel,
.c_init = init_intel, .c_init = init_intel,
.c_bsp_resume = intel_bsp_resume,
.c_x86_vendor = X86_VENDOR_INTEL, .c_x86_vendor = X86_VENDOR_INTEL,
}; };
cpu_dev_register(intel_cpu_dev); cpu_dev_register(intel_cpu_dev);
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Performance and Energy Bias Hint support.
*
* Copyright (C) 2019 Intel Corporation
*
* Author:
* Rafael J. Wysocki <rafael.j.wysocki@intel.com>
*/
#include <linux/cpuhotplug.h>
#include <linux/cpu.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/syscore_ops.h>
#include <linux/pm.h>
#include <asm/cpufeature.h>
#include <asm/msr.h>
/**
* DOC: overview
*
* The Performance and Energy Bias Hint (EPB) allows software to specify its
* preference with respect to the power-performance tradeoffs present in the
* processor. Generally, the EPB is expected to be set by user space (directly
* via sysfs or with the help of the x86_energy_perf_policy tool), but there are
* two reasons for the kernel to update it.
*
* First, there are systems where the platform firmware resets the EPB during
* system-wide transitions from sleep states back into the working state
* effectively causing the previous EPB updates by user space to be lost.
* Thus the kernel needs to save the current EPB values for all CPUs during
* system-wide transitions to sleep states and restore them on the way back to
* the working state. That can be achieved by saving EPB for secondary CPUs
* when they are taken offline during transitions into system sleep states and
* for the boot CPU in a syscore suspend operation, so that it can be restored
* for the boot CPU in a syscore resume operation and for the other CPUs when
* they are brought back online. However, CPUs that are already offline when
* a system-wide PM transition is started are not taken offline again, but their
* EPB values may still be reset by the platform firmware during the transition,
* so in fact it is necessary to save the EPB of any CPU taken offline and to
* restore it when the given CPU goes back online at all times.
*
* Second, on many systems the initial EPB value coming from the platform
* firmware is 0 ('performance') and at least on some of them that is because
* the platform firmware does not initialize EPB at all with the assumption that
* the OS will do that anyway. That sometimes is problematic, as it may cause
* the system battery to drain too fast, for example, so it is better to adjust
* it on CPU bring-up and if the initial EPB value for a given CPU is 0, the
* kernel changes it to 6 ('normal').
*/
static DEFINE_PER_CPU(u8, saved_epb);
#define EPB_MASK 0x0fULL
#define EPB_SAVED 0x10ULL
#define MAX_EPB EPB_MASK
static int intel_epb_save(void)
{
u64 epb;
rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
/*
* Ensure that saved_epb will always be nonzero after this write even if
* the EPB value read from the MSR is 0.
*/
this_cpu_write(saved_epb, (epb & EPB_MASK) | EPB_SAVED);
return 0;
}
static void intel_epb_restore(void)
{
u64 val = this_cpu_read(saved_epb);
u64 epb;
rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
if (val) {
val &= EPB_MASK;
} else {
/*
* Because intel_epb_save() has not run for the current CPU yet,
* it is going online for the first time, so if its EPB value is
* 0 ('performance') at this point, assume that it has not been
* initialized by the platform firmware and set it to 6
* ('normal').
*/
val = epb & EPB_MASK;
if (val == ENERGY_PERF_BIAS_PERFORMANCE) {
val = ENERGY_PERF_BIAS_NORMAL;
pr_warn_once("ENERGY_PERF_BIAS: Set to 'normal', was 'performance'\n");
}
}
wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, (epb & ~EPB_MASK) | val);
}
static struct syscore_ops intel_epb_syscore_ops = {
.suspend = intel_epb_save,
.resume = intel_epb_restore,
};
static const char * const energy_perf_strings[] = {
"performance",
"balance-performance",
"normal",
"balance-power",
"power"
};
static const u8 energ_perf_values[] = {
ENERGY_PERF_BIAS_PERFORMANCE,
ENERGY_PERF_BIAS_BALANCE_PERFORMANCE,
ENERGY_PERF_BIAS_NORMAL,
ENERGY_PERF_BIAS_BALANCE_POWERSAVE,
ENERGY_PERF_BIAS_POWERSAVE
};
static ssize_t energy_perf_bias_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
unsigned int cpu = dev->id;
u64 epb;
int ret;
ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
if (ret < 0)
return ret;
return sprintf(buf, "%llu\n", epb);
}
static ssize_t energy_perf_bias_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int cpu = dev->id;
u64 epb, val;
int ret;
ret = __sysfs_match_string(energy_perf_strings,
ARRAY_SIZE(energy_perf_strings), buf);
if (ret >= 0)
val = energ_perf_values[ret];
else if (kstrtou64(buf, 0, &val) || val > MAX_EPB)
return -EINVAL;
ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
if (ret < 0)
return ret;
ret = wrmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS,
(epb & ~EPB_MASK) | val);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR_RW(energy_perf_bias);
static struct attribute *intel_epb_attrs[] = {
&dev_attr_energy_perf_bias.attr,
NULL
};
static const struct attribute_group intel_epb_attr_group = {
.name = power_group_name,
.attrs = intel_epb_attrs
};
static int intel_epb_online(unsigned int cpu)
{
struct device *cpu_dev = get_cpu_device(cpu);
intel_epb_restore();
if (!cpuhp_tasks_frozen)
sysfs_merge_group(&cpu_dev->kobj, &intel_epb_attr_group);
return 0;
}
static int intel_epb_offline(unsigned int cpu)
{
struct device *cpu_dev = get_cpu_device(cpu);
if (!cpuhp_tasks_frozen)
sysfs_unmerge_group(&cpu_dev->kobj, &intel_epb_attr_group);
intel_epb_save();
return 0;
}
static __init int intel_epb_init(void)
{
int ret;
if (!boot_cpu_has(X86_FEATURE_EPB))
return -ENODEV;
ret = cpuhp_setup_state(CPUHP_AP_X86_INTEL_EPB_ONLINE,
"x86/intel/epb:online", intel_epb_online,
intel_epb_offline);
if (ret < 0)
goto err_out_online;
register_syscore_ops(&intel_epb_syscore_ops);
return 0;
err_out_online:
cpuhp_remove_state(CPUHP_AP_X86_INTEL_EPB_ONLINE);
return ret;
}
subsys_initcall(intel_epb_init);
...@@ -185,8 +185,7 @@ static void __init cyc2ns_init_boot_cpu(void) ...@@ -185,8 +185,7 @@ static void __init cyc2ns_init_boot_cpu(void)
/* /*
* Secondary CPUs do not run through tsc_init(), so set up * Secondary CPUs do not run through tsc_init(), so set up
* all the scale factors for all CPUs, assuming the same * all the scale factors for all CPUs, assuming the same
* speed as the bootup CPU. (cpufreq notifiers will fix this * speed as the bootup CPU.
* up if their speed diverges)
*/ */
static void __init cyc2ns_init_secondary_cpus(void) static void __init cyc2ns_init_secondary_cpus(void)
{ {
...@@ -940,12 +939,12 @@ void tsc_restore_sched_clock_state(void) ...@@ -940,12 +939,12 @@ void tsc_restore_sched_clock_state(void)
} }
#ifdef CONFIG_CPU_FREQ #ifdef CONFIG_CPU_FREQ
/* Frequency scaling support. Adjust the TSC based timer when the cpu frequency /*
* Frequency scaling support. Adjust the TSC based timer when the CPU frequency
* changes. * changes.
* *
* RED-PEN: On SMP we assume all CPUs run with the same frequency. It's * NOTE: On SMP the situation is not fixable in general, so simply mark the TSC
* not that important because current Opteron setups do not support * as unstable and give up in those cases.
* scaling on SMP anyroads.
* *
* Should fix up last_tsc too. Currently gettimeofday in the * Should fix up last_tsc too. Currently gettimeofday in the
* first tick after the change will be slightly wrong. * first tick after the change will be slightly wrong.
...@@ -959,22 +958,22 @@ static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, ...@@ -959,22 +958,22 @@ static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
void *data) void *data)
{ {
struct cpufreq_freqs *freq = data; struct cpufreq_freqs *freq = data;
unsigned long *lpj;
lpj = &boot_cpu_data.loops_per_jiffy; if (num_online_cpus() > 1) {
#ifdef CONFIG_SMP mark_tsc_unstable("cpufreq changes on SMP");
if (!(freq->flags & CPUFREQ_CONST_LOOPS)) return 0;
lpj = &cpu_data(freq->cpu).loops_per_jiffy; }
#endif
if (!ref_freq) { if (!ref_freq) {
ref_freq = freq->old; ref_freq = freq->old;
loops_per_jiffy_ref = *lpj; loops_per_jiffy_ref = boot_cpu_data.loops_per_jiffy;
tsc_khz_ref = tsc_khz; tsc_khz_ref = tsc_khz;
} }
if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) ||
(val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) { (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) {
*lpj = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new); boot_cpu_data.loops_per_jiffy =
cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
tsc_khz = cpufreq_scale(tsc_khz_ref, ref_freq, freq->new); tsc_khz = cpufreq_scale(tsc_khz_ref, ref_freq, freq->new);
if (!(freq->flags & CPUFREQ_CONST_LOOPS)) if (!(freq->flags & CPUFREQ_CONST_LOOPS))
......
...@@ -181,7 +181,7 @@ void acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag) ...@@ -181,7 +181,7 @@ void acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag)
acpi_processor_ppc_ost(pr->handle, 0); acpi_processor_ppc_ost(pr->handle, 0);
} }
if (ret >= 0) if (ret >= 0)
cpufreq_update_policy(pr->id); cpufreq_update_limits(pr->id);
} }
int acpi_processor_get_bios_limit(int cpu, unsigned int *limit) int acpi_processor_get_bios_limit(int cpu, unsigned int *limit)
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/cpu.h>
#include "power.h" #include "power.h"
...@@ -128,6 +129,7 @@ static const struct genpd_lock_ops genpd_spin_ops = { ...@@ -128,6 +129,7 @@ static const struct genpd_lock_ops genpd_spin_ops = {
#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) #define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE)
#define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) #define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON)
#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP) #define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
#define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN)
static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev,
const struct generic_pm_domain *genpd) const struct generic_pm_domain *genpd)
...@@ -391,11 +393,9 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) ...@@ -391,11 +393,9 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
if (unlikely(!genpd->set_performance_state)) if (unlikely(!genpd->set_performance_state))
return -EINVAL; return -EINVAL;
if (unlikely(!dev->power.subsys_data || if (WARN_ON(!dev->power.subsys_data ||
!dev->power.subsys_data->domain_data)) { !dev->power.subsys_data->domain_data))
WARN_ON(1);
return -EINVAL; return -EINVAL;
}
genpd_lock(genpd); genpd_lock(genpd);
...@@ -1396,8 +1396,7 @@ EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); ...@@ -1396,8 +1396,7 @@ EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron);
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev)
struct gpd_timing_data *td)
{ {
struct generic_pm_domain_data *gpd_data; struct generic_pm_domain_data *gpd_data;
int ret; int ret;
...@@ -1412,9 +1411,6 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, ...@@ -1412,9 +1411,6 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev,
goto err_put; goto err_put;
} }
if (td)
gpd_data->td = *td;
gpd_data->base.dev = dev; gpd_data->base.dev = dev;
gpd_data->td.constraint_changed = true; gpd_data->td.constraint_changed = true;
gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS; gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
...@@ -1454,8 +1450,57 @@ static void genpd_free_dev_data(struct device *dev, ...@@ -1454,8 +1450,57 @@ static void genpd_free_dev_data(struct device *dev,
dev_pm_put_subsys_data(dev); dev_pm_put_subsys_data(dev);
} }
static void genpd_update_cpumask(struct generic_pm_domain *genpd,
int cpu, bool set, unsigned int depth)
{
struct gpd_link *link;
if (!genpd_is_cpu_domain(genpd))
return;
list_for_each_entry(link, &genpd->slave_links, slave_node) {
struct generic_pm_domain *master = link->master;
genpd_lock_nested(master, depth + 1);
genpd_update_cpumask(master, cpu, set, depth + 1);
genpd_unlock(master);
}
if (set)
cpumask_set_cpu(cpu, genpd->cpus);
else
cpumask_clear_cpu(cpu, genpd->cpus);
}
static void genpd_set_cpumask(struct generic_pm_domain *genpd, int cpu)
{
if (cpu >= 0)
genpd_update_cpumask(genpd, cpu, true, 0);
}
static void genpd_clear_cpumask(struct generic_pm_domain *genpd, int cpu)
{
if (cpu >= 0)
genpd_update_cpumask(genpd, cpu, false, 0);
}
static int genpd_get_cpu(struct generic_pm_domain *genpd, struct device *dev)
{
int cpu;
if (!genpd_is_cpu_domain(genpd))
return -1;
for_each_possible_cpu(cpu) {
if (get_cpu_device(cpu) == dev)
return cpu;
}
return -1;
}
static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
struct gpd_timing_data *td) struct device *base_dev)
{ {
struct generic_pm_domain_data *gpd_data; struct generic_pm_domain_data *gpd_data;
int ret; int ret;
...@@ -1465,16 +1510,19 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, ...@@ -1465,16 +1510,19 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
return -EINVAL; return -EINVAL;
gpd_data = genpd_alloc_dev_data(dev, td); gpd_data = genpd_alloc_dev_data(dev);
if (IS_ERR(gpd_data)) if (IS_ERR(gpd_data))
return PTR_ERR(gpd_data); return PTR_ERR(gpd_data);
gpd_data->cpu = genpd_get_cpu(genpd, base_dev);
ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0;
if (ret) if (ret)
goto out; goto out;
genpd_lock(genpd); genpd_lock(genpd);
genpd_set_cpumask(genpd, gpd_data->cpu);
dev_pm_domain_set(dev, &genpd->domain); dev_pm_domain_set(dev, &genpd->domain);
genpd->device_count++; genpd->device_count++;
...@@ -1502,7 +1550,7 @@ int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) ...@@ -1502,7 +1550,7 @@ int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
int ret; int ret;
mutex_lock(&gpd_list_lock); mutex_lock(&gpd_list_lock);
ret = genpd_add_device(genpd, dev, NULL); ret = genpd_add_device(genpd, dev, dev);
mutex_unlock(&gpd_list_lock); mutex_unlock(&gpd_list_lock);
return ret; return ret;
...@@ -1532,6 +1580,7 @@ static int genpd_remove_device(struct generic_pm_domain *genpd, ...@@ -1532,6 +1580,7 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
genpd->device_count--; genpd->device_count--;
genpd->max_off_time_changed = true; genpd->max_off_time_changed = true;
genpd_clear_cpumask(genpd, gpd_data->cpu);
dev_pm_domain_set(dev, NULL); dev_pm_domain_set(dev, NULL);
list_del_init(&pdd->list_node); list_del_init(&pdd->list_node);
...@@ -1686,6 +1735,12 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, ...@@ -1686,6 +1735,12 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
} }
EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain);
static void genpd_free_default_power_state(struct genpd_power_state *states,
unsigned int state_count)
{
kfree(states);
}
static int genpd_set_default_power_state(struct generic_pm_domain *genpd) static int genpd_set_default_power_state(struct generic_pm_domain *genpd)
{ {
struct genpd_power_state *state; struct genpd_power_state *state;
...@@ -1696,7 +1751,7 @@ static int genpd_set_default_power_state(struct generic_pm_domain *genpd) ...@@ -1696,7 +1751,7 @@ static int genpd_set_default_power_state(struct generic_pm_domain *genpd)
genpd->states = state; genpd->states = state;
genpd->state_count = 1; genpd->state_count = 1;
genpd->free = state; genpd->free_states = genpd_free_default_power_state;
return 0; return 0;
} }
...@@ -1762,11 +1817,18 @@ int pm_genpd_init(struct generic_pm_domain *genpd, ...@@ -1762,11 +1817,18 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
if (genpd_is_always_on(genpd) && !genpd_status_on(genpd)) if (genpd_is_always_on(genpd) && !genpd_status_on(genpd))
return -EINVAL; return -EINVAL;
if (genpd_is_cpu_domain(genpd) &&
!zalloc_cpumask_var(&genpd->cpus, GFP_KERNEL))
return -ENOMEM;
/* Use only one "off" state if there were no states declared */ /* Use only one "off" state if there were no states declared */
if (genpd->state_count == 0) { if (genpd->state_count == 0) {
ret = genpd_set_default_power_state(genpd); ret = genpd_set_default_power_state(genpd);
if (ret) if (ret) {
if (genpd_is_cpu_domain(genpd))
free_cpumask_var(genpd->cpus);
return ret; return ret;
}
} else if (!gov && genpd->state_count > 1) { } else if (!gov && genpd->state_count > 1) {
pr_warn("%s: no governor for states\n", genpd->name); pr_warn("%s: no governor for states\n", genpd->name);
} }
...@@ -1812,7 +1874,11 @@ static int genpd_remove(struct generic_pm_domain *genpd) ...@@ -1812,7 +1874,11 @@ static int genpd_remove(struct generic_pm_domain *genpd)
list_del(&genpd->gpd_list_node); list_del(&genpd->gpd_list_node);
genpd_unlock(genpd); genpd_unlock(genpd);
cancel_work_sync(&genpd->power_off_work); cancel_work_sync(&genpd->power_off_work);
kfree(genpd->free); if (genpd_is_cpu_domain(genpd))
free_cpumask_var(genpd->cpus);
if (genpd->free_states)
genpd->free_states(genpd->states, genpd->state_count);
pr_debug("%s: removed %s\n", __func__, genpd->name); pr_debug("%s: removed %s\n", __func__, genpd->name);
return 0; return 0;
...@@ -2190,7 +2256,7 @@ int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) ...@@ -2190,7 +2256,7 @@ int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev)
goto out; goto out;
} }
ret = genpd_add_device(genpd, dev, NULL); ret = genpd_add_device(genpd, dev, dev);
out: out:
mutex_unlock(&gpd_list_lock); mutex_unlock(&gpd_list_lock);
...@@ -2274,6 +2340,7 @@ EXPORT_SYMBOL_GPL(of_genpd_remove_last); ...@@ -2274,6 +2340,7 @@ EXPORT_SYMBOL_GPL(of_genpd_remove_last);
static void genpd_release_dev(struct device *dev) static void genpd_release_dev(struct device *dev)
{ {
of_node_put(dev->of_node);
kfree(dev); kfree(dev);
} }
...@@ -2335,14 +2402,14 @@ static void genpd_dev_pm_sync(struct device *dev) ...@@ -2335,14 +2402,14 @@ static void genpd_dev_pm_sync(struct device *dev)
genpd_queue_power_off_work(pd); genpd_queue_power_off_work(pd);
} }
static int __genpd_dev_pm_attach(struct device *dev, struct device_node *np, static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
unsigned int index, bool power_on) unsigned int index, bool power_on)
{ {
struct of_phandle_args pd_args; struct of_phandle_args pd_args;
struct generic_pm_domain *pd; struct generic_pm_domain *pd;
int ret; int ret;
ret = of_parse_phandle_with_args(np, "power-domains", ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells", index, &pd_args); "#power-domain-cells", index, &pd_args);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -2354,12 +2421,12 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device_node *np, ...@@ -2354,12 +2421,12 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device_node *np,
mutex_unlock(&gpd_list_lock); mutex_unlock(&gpd_list_lock);
dev_dbg(dev, "%s() failed to find PM domain: %ld\n", dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
__func__, PTR_ERR(pd)); __func__, PTR_ERR(pd));
return driver_deferred_probe_check_state(dev); return driver_deferred_probe_check_state(base_dev);
} }
dev_dbg(dev, "adding to PM domain %s\n", pd->name); dev_dbg(dev, "adding to PM domain %s\n", pd->name);
ret = genpd_add_device(pd, dev, NULL); ret = genpd_add_device(pd, dev, base_dev);
mutex_unlock(&gpd_list_lock); mutex_unlock(&gpd_list_lock);
if (ret < 0) { if (ret < 0) {
...@@ -2410,7 +2477,7 @@ int genpd_dev_pm_attach(struct device *dev) ...@@ -2410,7 +2477,7 @@ int genpd_dev_pm_attach(struct device *dev)
"#power-domain-cells") != 1) "#power-domain-cells") != 1)
return 0; return 0;
return __genpd_dev_pm_attach(dev, dev->of_node, 0, true); return __genpd_dev_pm_attach(dev, dev, 0, true);
} }
EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
...@@ -2440,10 +2507,10 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev, ...@@ -2440,10 +2507,10 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev,
if (!dev->of_node) if (!dev->of_node)
return NULL; return NULL;
/* Deal only with devices using multiple PM domains. */ /* Verify that the index is within a valid range. */
num_domains = of_count_phandle_with_args(dev->of_node, "power-domains", num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells"); "#power-domain-cells");
if (num_domains < 2 || index >= num_domains) if (index >= num_domains)
return NULL; return NULL;
/* Allocate and register device on the genpd bus. */ /* Allocate and register device on the genpd bus. */
...@@ -2454,15 +2521,16 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev, ...@@ -2454,15 +2521,16 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev,
dev_set_name(virt_dev, "genpd:%u:%s", index, dev_name(dev)); dev_set_name(virt_dev, "genpd:%u:%s", index, dev_name(dev));
virt_dev->bus = &genpd_bus_type; virt_dev->bus = &genpd_bus_type;
virt_dev->release = genpd_release_dev; virt_dev->release = genpd_release_dev;
virt_dev->of_node = of_node_get(dev->of_node);
ret = device_register(virt_dev); ret = device_register(virt_dev);
if (ret) { if (ret) {
kfree(virt_dev); put_device(virt_dev);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
/* Try to attach the device to the PM domain at the specified index. */ /* Try to attach the device to the PM domain at the specified index. */
ret = __genpd_dev_pm_attach(virt_dev, dev->of_node, index, false); ret = __genpd_dev_pm_attach(virt_dev, dev, index, false);
if (ret < 1) { if (ret < 1) {
device_unregister(virt_dev); device_unregister(virt_dev);
return ret ? ERR_PTR(ret) : NULL; return ret ? ERR_PTR(ret) : NULL;
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <linux/cpuidle.h>
#include <linux/cpumask.h>
#include <linux/ktime.h>
static int dev_update_qos_constraint(struct device *dev, void *data) static int dev_update_qos_constraint(struct device *dev, void *data)
{ {
...@@ -210,8 +213,10 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) ...@@ -210,8 +213,10 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
struct generic_pm_domain *genpd = pd_to_genpd(pd); struct generic_pm_domain *genpd = pd_to_genpd(pd);
struct gpd_link *link; struct gpd_link *link;
if (!genpd->max_off_time_changed) if (!genpd->max_off_time_changed) {
genpd->state_idx = genpd->cached_power_down_state_idx;
return genpd->cached_power_down_ok; return genpd->cached_power_down_ok;
}
/* /*
* We have to invalidate the cached results for the masters, so * We have to invalidate the cached results for the masters, so
...@@ -236,6 +241,7 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) ...@@ -236,6 +241,7 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
genpd->state_idx--; genpd->state_idx--;
} }
genpd->cached_power_down_state_idx = genpd->state_idx;
return genpd->cached_power_down_ok; return genpd->cached_power_down_ok;
} }
...@@ -244,6 +250,65 @@ static bool always_on_power_down_ok(struct dev_pm_domain *domain) ...@@ -244,6 +250,65 @@ static bool always_on_power_down_ok(struct dev_pm_domain *domain)
return false; return false;
} }
#ifdef CONFIG_CPU_IDLE
static bool cpu_power_down_ok(struct dev_pm_domain *pd)
{
struct generic_pm_domain *genpd = pd_to_genpd(pd);
struct cpuidle_device *dev;
ktime_t domain_wakeup, next_hrtimer;
s64 idle_duration_ns;
int cpu, i;
/* Validate dev PM QoS constraints. */
if (!default_power_down_ok(pd))
return false;
if (!(genpd->flags & GENPD_FLAG_CPU_DOMAIN))
return true;
/*
* Find the next wakeup for any of the online CPUs within the PM domain
* and its subdomains. Note, we only need the genpd->cpus, as it already
* contains a mask of all CPUs from subdomains.
*/
domain_wakeup = ktime_set(KTIME_SEC_MAX, 0);
for_each_cpu_and(cpu, genpd->cpus, cpu_online_mask) {
dev = per_cpu(cpuidle_devices, cpu);
if (dev) {
next_hrtimer = READ_ONCE(dev->next_hrtimer);
if (ktime_before(next_hrtimer, domain_wakeup))
domain_wakeup = next_hrtimer;
}
}
/* The minimum idle duration is from now - until the next wakeup. */
idle_duration_ns = ktime_to_ns(ktime_sub(domain_wakeup, ktime_get()));
if (idle_duration_ns <= 0)
return false;
/*
* Find the deepest idle state that has its residency value satisfied
* and by also taking into account the power off latency for the state.
* Start at the state picked by the dev PM QoS constraint validation.
*/
i = genpd->state_idx;
do {
if (idle_duration_ns >= (genpd->states[i].residency_ns +
genpd->states[i].power_off_latency_ns)) {
genpd->state_idx = i;
return true;
}
} while (--i >= 0);
return false;
}
struct dev_power_governor pm_domain_cpu_gov = {
.suspend_ok = default_suspend_ok,
.power_down_ok = cpu_power_down_ok,
};
#endif
struct dev_power_governor simple_qos_governor = { struct dev_power_governor simple_qos_governor = {
.suspend_ok = default_suspend_ok, .suspend_ok = default_suspend_ok,
.power_down_ok = default_power_down_ok, .power_down_ok = default_power_down_ok,
......
...@@ -478,7 +478,7 @@ struct dpm_watchdog { ...@@ -478,7 +478,7 @@ struct dpm_watchdog {
/** /**
* dpm_watchdog_handler - Driver suspend / resume watchdog handler. * dpm_watchdog_handler - Driver suspend / resume watchdog handler.
* @data: Watchdog object address. * @t: The timer that PM watchdog depends on.
* *
* Called when a driver has timed out suspending or resuming. * Called when a driver has timed out suspending or resuming.
* There's not much we can do here to recover so panic() to * There's not much we can do here to recover so panic() to
...@@ -706,6 +706,19 @@ static bool is_async(struct device *dev) ...@@ -706,6 +706,19 @@ static bool is_async(struct device *dev)
&& !pm_trace_is_enabled(); && !pm_trace_is_enabled();
} }
static bool dpm_async_fn(struct device *dev, async_func_t func)
{
reinit_completion(&dev->power.completion);
if (is_async(dev)) {
get_device(dev);
async_schedule(func, dev);
return true;
}
return false;
}
static void async_resume_noirq(void *data, async_cookie_t cookie) static void async_resume_noirq(void *data, async_cookie_t cookie)
{ {
struct device *dev = (struct device *)data; struct device *dev = (struct device *)data;
...@@ -732,13 +745,8 @@ void dpm_noirq_resume_devices(pm_message_t state) ...@@ -732,13 +745,8 @@ void dpm_noirq_resume_devices(pm_message_t state)
* in case the starting of async threads is * in case the starting of async threads is
* delayed by non-async resuming devices. * delayed by non-async resuming devices.
*/ */
list_for_each_entry(dev, &dpm_noirq_list, power.entry) { list_for_each_entry(dev, &dpm_noirq_list, power.entry)
reinit_completion(&dev->power.completion); dpm_async_fn(dev, async_resume_noirq);
if (is_async(dev)) {
get_device(dev);
async_schedule_dev(async_resume_noirq, dev);
}
}
while (!list_empty(&dpm_noirq_list)) { while (!list_empty(&dpm_noirq_list)) {
dev = to_device(dpm_noirq_list.next); dev = to_device(dpm_noirq_list.next);
...@@ -889,13 +897,8 @@ void dpm_resume_early(pm_message_t state) ...@@ -889,13 +897,8 @@ void dpm_resume_early(pm_message_t state)
* in case the starting of async threads is * in case the starting of async threads is
* delayed by non-async resuming devices. * delayed by non-async resuming devices.
*/ */
list_for_each_entry(dev, &dpm_late_early_list, power.entry) { list_for_each_entry(dev, &dpm_late_early_list, power.entry)
reinit_completion(&dev->power.completion); dpm_async_fn(dev, async_resume_early);
if (is_async(dev)) {
get_device(dev);
async_schedule_dev(async_resume_early, dev);
}
}
while (!list_empty(&dpm_late_early_list)) { while (!list_empty(&dpm_late_early_list)) {
dev = to_device(dpm_late_early_list.next); dev = to_device(dpm_late_early_list.next);
...@@ -1053,13 +1056,8 @@ void dpm_resume(pm_message_t state) ...@@ -1053,13 +1056,8 @@ void dpm_resume(pm_message_t state)
pm_transition = state; pm_transition = state;
async_error = 0; async_error = 0;
list_for_each_entry(dev, &dpm_suspended_list, power.entry) { list_for_each_entry(dev, &dpm_suspended_list, power.entry)
reinit_completion(&dev->power.completion); dpm_async_fn(dev, async_resume);
if (is_async(dev)) {
get_device(dev);
async_schedule_dev(async_resume, dev);
}
}
while (!list_empty(&dpm_suspended_list)) { while (!list_empty(&dpm_suspended_list)) {
dev = to_device(dpm_suspended_list.next); dev = to_device(dpm_suspended_list.next);
...@@ -1373,13 +1371,9 @@ static void async_suspend_noirq(void *data, async_cookie_t cookie) ...@@ -1373,13 +1371,9 @@ static void async_suspend_noirq(void *data, async_cookie_t cookie)
static int device_suspend_noirq(struct device *dev) static int device_suspend_noirq(struct device *dev)
{ {
reinit_completion(&dev->power.completion); if (dpm_async_fn(dev, async_suspend_noirq))
if (is_async(dev)) {
get_device(dev);
async_schedule_dev(async_suspend_noirq, dev);
return 0; return 0;
}
return __device_suspend_noirq(dev, pm_transition, false); return __device_suspend_noirq(dev, pm_transition, false);
} }
...@@ -1576,13 +1570,8 @@ static void async_suspend_late(void *data, async_cookie_t cookie) ...@@ -1576,13 +1570,8 @@ static void async_suspend_late(void *data, async_cookie_t cookie)
static int device_suspend_late(struct device *dev) static int device_suspend_late(struct device *dev)
{ {
reinit_completion(&dev->power.completion); if (dpm_async_fn(dev, async_suspend_late))
if (is_async(dev)) {
get_device(dev);
async_schedule_dev(async_suspend_late, dev);
return 0; return 0;
}
return __device_suspend_late(dev, pm_transition, false); return __device_suspend_late(dev, pm_transition, false);
} }
...@@ -1747,6 +1736,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) ...@@ -1747,6 +1736,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
if (dev->power.syscore) if (dev->power.syscore)
goto Complete; goto Complete;
/* Avoid direct_complete to let wakeup_path propagate. */
if (device_may_wakeup(dev) || dev->power.wakeup_path)
dev->power.direct_complete = false;
if (dev->power.direct_complete) { if (dev->power.direct_complete) {
if (pm_runtime_status_suspended(dev)) { if (pm_runtime_status_suspended(dev)) {
pm_runtime_disable(dev); pm_runtime_disable(dev);
...@@ -1842,13 +1835,8 @@ static void async_suspend(void *data, async_cookie_t cookie) ...@@ -1842,13 +1835,8 @@ static void async_suspend(void *data, async_cookie_t cookie)
static int device_suspend(struct device *dev) static int device_suspend(struct device *dev)
{ {
reinit_completion(&dev->power.completion); if (dpm_async_fn(dev, async_suspend))
if (is_async(dev)) {
get_device(dev);
async_schedule_dev(async_suspend, dev);
return 0; return 0;
}
return __device_suspend(dev, pm_transition, false); return __device_suspend(dev, pm_transition, false);
} }
...@@ -2069,8 +2057,8 @@ EXPORT_SYMBOL_GPL(__suspend_report_result); ...@@ -2069,8 +2057,8 @@ EXPORT_SYMBOL_GPL(__suspend_report_result);
/** /**
* device_pm_wait_for_dev - Wait for suspend/resume of a device to complete. * device_pm_wait_for_dev - Wait for suspend/resume of a device to complete.
* @dev: Device to wait for.
* @subordinate: Device that needs to wait for @dev. * @subordinate: Device that needs to wait for @dev.
* @dev: Device to wait for.
*/ */
int device_pm_wait_for_dev(struct device *subordinate, struct device *dev) int device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
{ {
......
...@@ -804,7 +804,7 @@ void pm_print_active_wakeup_sources(void) ...@@ -804,7 +804,7 @@ void pm_print_active_wakeup_sources(void)
srcuidx = srcu_read_lock(&wakeup_srcu); srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) { list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
if (ws->active) { if (ws->active) {
pr_debug("active wakeup source: %s\n", ws->name); pm_pr_dbg("active wakeup source: %s\n", ws->name);
active = 1; active = 1;
} else if (!active && } else if (!active &&
(!last_activity_ws || (!last_activity_ws ||
...@@ -815,7 +815,7 @@ void pm_print_active_wakeup_sources(void) ...@@ -815,7 +815,7 @@ void pm_print_active_wakeup_sources(void)
} }
if (!active && last_activity_ws) if (!active && last_activity_ws)
pr_debug("last active wakeup source: %s\n", pm_pr_dbg("last active wakeup source: %s\n",
last_activity_ws->name); last_activity_ws->name);
srcu_read_unlock(&wakeup_srcu, srcuidx); srcu_read_unlock(&wakeup_srcu, srcuidx);
} }
...@@ -845,7 +845,7 @@ bool pm_wakeup_pending(void) ...@@ -845,7 +845,7 @@ bool pm_wakeup_pending(void)
raw_spin_unlock_irqrestore(&events_lock, flags); raw_spin_unlock_irqrestore(&events_lock, flags);
if (ret) { if (ret) {
pr_debug("Wakeup pending, aborting suspend\n"); pm_pr_dbg("Wakeup pending, aborting suspend\n");
pm_print_active_wakeup_sources(); pm_print_active_wakeup_sources();
} }
......
...@@ -26,10 +26,6 @@ config CPU_FREQ_GOV_COMMON ...@@ -26,10 +26,6 @@ config CPU_FREQ_GOV_COMMON
select IRQ_WORK select IRQ_WORK
bool bool
config CPU_FREQ_BOOST_SW
bool
depends on THERMAL
config CPU_FREQ_STAT config CPU_FREQ_STAT
bool "CPU frequency transition statistics" bool "CPU frequency transition statistics"
help help
......
...@@ -366,7 +366,7 @@ static u32 get_cur_val(const struct cpumask *mask, struct acpi_cpufreq_data *dat ...@@ -366,7 +366,7 @@ static u32 get_cur_val(const struct cpumask *mask, struct acpi_cpufreq_data *dat
val = drv_read(data, mask); val = drv_read(data, mask);
pr_debug("get_cur_val = %u\n", val); pr_debug("%s = %u\n", __func__, val);
return val; return val;
} }
...@@ -378,7 +378,7 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) ...@@ -378,7 +378,7 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
unsigned int freq; unsigned int freq;
unsigned int cached_freq; unsigned int cached_freq;
pr_debug("get_cur_freq_on_cpu (%d)\n", cpu); pr_debug("%s (%d)\n", __func__, cpu);
policy = cpufreq_cpu_get_raw(cpu); policy = cpufreq_cpu_get_raw(cpu);
if (unlikely(!policy)) if (unlikely(!policy))
...@@ -458,8 +458,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, ...@@ -458,8 +458,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
if (acpi_pstate_strict) { if (acpi_pstate_strict) {
if (!check_freqs(policy, mask, if (!check_freqs(policy, mask,
policy->freq_table[index].frequency)) { policy->freq_table[index].frequency)) {
pr_debug("acpi_cpufreq_target failed (%d)\n", pr_debug("%s (%d)\n", __func__, policy->cpu);
policy->cpu);
result = -EAGAIN; result = -EAGAIN;
} }
} }
...@@ -573,7 +572,7 @@ static int cpufreq_boost_down_prep(unsigned int cpu) ...@@ -573,7 +572,7 @@ static int cpufreq_boost_down_prep(unsigned int cpu)
static int __init acpi_cpufreq_early_init(void) static int __init acpi_cpufreq_early_init(void)
{ {
unsigned int i; unsigned int i;
pr_debug("acpi_cpufreq_early_init\n"); pr_debug("%s\n", __func__);
acpi_perf_data = alloc_percpu(struct acpi_processor_performance); acpi_perf_data = alloc_percpu(struct acpi_processor_performance);
if (!acpi_perf_data) { if (!acpi_perf_data) {
...@@ -657,7 +656,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -657,7 +656,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
static int blacklisted; static int blacklisted;
#endif #endif
pr_debug("acpi_cpufreq_cpu_init\n"); pr_debug("%s\n", __func__);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
if (blacklisted) if (blacklisted)
...@@ -856,7 +855,7 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) ...@@ -856,7 +855,7 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{ {
struct acpi_cpufreq_data *data = policy->driver_data; struct acpi_cpufreq_data *data = policy->driver_data;
pr_debug("acpi_cpufreq_cpu_exit\n"); pr_debug("%s\n", __func__);
policy->fast_switch_possible = false; policy->fast_switch_possible = false;
policy->driver_data = NULL; policy->driver_data = NULL;
...@@ -881,7 +880,7 @@ static int acpi_cpufreq_resume(struct cpufreq_policy *policy) ...@@ -881,7 +880,7 @@ static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
{ {
struct acpi_cpufreq_data *data = policy->driver_data; struct acpi_cpufreq_data *data = policy->driver_data;
pr_debug("acpi_cpufreq_resume\n"); pr_debug("%s\n", __func__);
data->resume = 1; data->resume = 1;
...@@ -954,7 +953,7 @@ static int __init acpi_cpufreq_init(void) ...@@ -954,7 +953,7 @@ static int __init acpi_cpufreq_init(void)
if (cpufreq_get_current_driver()) if (cpufreq_get_current_driver())
return -EEXIST; return -EEXIST;
pr_debug("acpi_cpufreq_init\n"); pr_debug("%s\n", __func__);
ret = acpi_cpufreq_early_init(); ret = acpi_cpufreq_early_init();
if (ret) if (ret)
...@@ -991,7 +990,7 @@ static int __init acpi_cpufreq_init(void) ...@@ -991,7 +990,7 @@ static int __init acpi_cpufreq_init(void)
static void __exit acpi_cpufreq_exit(void) static void __exit acpi_cpufreq_exit(void)
{ {
pr_debug("acpi_cpufreq_exit\n"); pr_debug("%s\n", __func__);
acpi_cpufreq_boost_exit(); acpi_cpufreq_boost_exit();
......
...@@ -124,7 +124,7 @@ static int __init amd_freq_sensitivity_init(void) ...@@ -124,7 +124,7 @@ static int __init amd_freq_sensitivity_init(void)
PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL); PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL);
if (!pcidev) { if (!pcidev) {
if (!static_cpu_has(X86_FEATURE_PROC_FEEDBACK)) if (!boot_cpu_has(X86_FEATURE_PROC_FEEDBACK))
return -ENODEV; return -ENODEV;
} }
......
...@@ -359,11 +359,11 @@ static int __init armada37xx_cpufreq_driver_init(void) ...@@ -359,11 +359,11 @@ static int __init armada37xx_cpufreq_driver_init(void)
struct armada_37xx_dvfs *dvfs; struct armada_37xx_dvfs *dvfs;
struct platform_device *pdev; struct platform_device *pdev;
unsigned long freq; unsigned long freq;
unsigned int cur_frequency; unsigned int cur_frequency, base_frequency;
struct regmap *nb_pm_base, *avs_base; struct regmap *nb_pm_base, *avs_base;
struct device *cpu_dev; struct device *cpu_dev;
int load_lvl, ret; int load_lvl, ret;
struct clk *clk; struct clk *clk, *parent;
nb_pm_base = nb_pm_base =
syscon_regmap_lookup_by_compatible("marvell,armada-3700-nb-pm"); syscon_regmap_lookup_by_compatible("marvell,armada-3700-nb-pm");
...@@ -399,6 +399,22 @@ static int __init armada37xx_cpufreq_driver_init(void) ...@@ -399,6 +399,22 @@ static int __init armada37xx_cpufreq_driver_init(void)
return PTR_ERR(clk); return PTR_ERR(clk);
} }
parent = clk_get_parent(clk);
if (IS_ERR(parent)) {
dev_err(cpu_dev, "Cannot get parent clock for CPU0\n");
clk_put(clk);
return PTR_ERR(parent);
}
/* Get parent CPU frequency */
base_frequency = clk_get_rate(parent);
if (!base_frequency) {
dev_err(cpu_dev, "Failed to get parent clock rate for CPU\n");
clk_put(clk);
return -EINVAL;
}
/* Get nominal (current) CPU frequency */ /* Get nominal (current) CPU frequency */
cur_frequency = clk_get_rate(clk); cur_frequency = clk_get_rate(clk);
if (!cur_frequency) { if (!cur_frequency) {
...@@ -431,7 +447,7 @@ static int __init armada37xx_cpufreq_driver_init(void) ...@@ -431,7 +447,7 @@ static int __init armada37xx_cpufreq_driver_init(void)
for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR; for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR;
load_lvl++) { load_lvl++) {
unsigned long u_volt = avs_map[dvfs->avs[load_lvl]] * 1000; unsigned long u_volt = avs_map[dvfs->avs[load_lvl]] * 1000;
freq = cur_frequency / dvfs->divider[load_lvl]; freq = base_frequency / dvfs->divider[load_lvl];
ret = dev_pm_opp_add(cpu_dev, freq, u_volt); ret = dev_pm_opp_add(cpu_dev, freq, u_volt);
if (ret) if (ret)
goto remove_opp; goto remove_opp;
......
...@@ -132,6 +132,7 @@ static int __init armada_8k_cpufreq_init(void) ...@@ -132,6 +132,7 @@ static int __init armada_8k_cpufreq_init(void)
of_node_put(node); of_node_put(node);
return -ENODEV; return -ENODEV;
} }
of_node_put(node);
nb_cpus = num_possible_cpus(); nb_cpus = num_possible_cpus();
freq_tables = kcalloc(nb_cpus, sizeof(*freq_tables), GFP_KERNEL); freq_tables = kcalloc(nb_cpus, sizeof(*freq_tables), GFP_KERNEL);
......
...@@ -34,11 +34,6 @@ ...@@ -34,11 +34,6 @@
static LIST_HEAD(cpufreq_policy_list); static LIST_HEAD(cpufreq_policy_list);
static inline bool policy_is_inactive(struct cpufreq_policy *policy)
{
return cpumask_empty(policy->cpus);
}
/* Macros to iterate over CPU policies */ /* Macros to iterate over CPU policies */
#define for_each_suitable_policy(__policy, __active) \ #define for_each_suitable_policy(__policy, __active) \
list_for_each_entry(__policy, &cpufreq_policy_list, policy_list) \ list_for_each_entry(__policy, &cpufreq_policy_list, policy_list) \
...@@ -250,6 +245,51 @@ void cpufreq_cpu_put(struct cpufreq_policy *policy) ...@@ -250,6 +245,51 @@ void cpufreq_cpu_put(struct cpufreq_policy *policy)
} }
EXPORT_SYMBOL_GPL(cpufreq_cpu_put); EXPORT_SYMBOL_GPL(cpufreq_cpu_put);
/**
* cpufreq_cpu_release - Unlock a policy and decrement its usage counter.
* @policy: cpufreq policy returned by cpufreq_cpu_acquire().
*/
void cpufreq_cpu_release(struct cpufreq_policy *policy)
{
if (WARN_ON(!policy))
return;
lockdep_assert_held(&policy->rwsem);
up_write(&policy->rwsem);
cpufreq_cpu_put(policy);
}
/**
* cpufreq_cpu_acquire - Find policy for a CPU, mark it as busy and lock it.
* @cpu: CPU to find the policy for.
*
* Call cpufreq_cpu_get() to get a reference on the cpufreq policy for @cpu and
* if the policy returned by it is not NULL, acquire its rwsem for writing.
* Return the policy if it is active or release it and return NULL otherwise.
*
* The policy returned by this function has to be released with the help of
* cpufreq_cpu_release() in order to release its rwsem and balance its usage
* counter properly.
*/
struct cpufreq_policy *cpufreq_cpu_acquire(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
if (!policy)
return NULL;
down_write(&policy->rwsem);
if (policy_is_inactive(policy)) {
cpufreq_cpu_release(policy);
return NULL;
}
return policy;
}
/********************************************************************* /*********************************************************************
* EXTERNALLY AFFECTING FREQUENCY CHANGES * * EXTERNALLY AFFECTING FREQUENCY CHANGES *
*********************************************************************/ *********************************************************************/
...@@ -669,9 +709,6 @@ static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf) ...@@ -669,9 +709,6 @@ static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf)
return ret; return ret;
} }
static int cpufreq_set_policy(struct cpufreq_policy *policy,
struct cpufreq_policy *new_policy);
/** /**
* cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access * cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access
*/ */
...@@ -857,11 +894,9 @@ static ssize_t show_bios_limit(struct cpufreq_policy *policy, char *buf) ...@@ -857,11 +894,9 @@ static ssize_t show_bios_limit(struct cpufreq_policy *policy, char *buf)
{ {
unsigned int limit; unsigned int limit;
int ret; int ret;
if (cpufreq_driver->bios_limit) { ret = cpufreq_driver->bios_limit(policy->cpu, &limit);
ret = cpufreq_driver->bios_limit(policy->cpu, &limit); if (!ret)
if (!ret) return sprintf(buf, "%u\n", limit);
return sprintf(buf, "%u\n", limit);
}
return sprintf(buf, "%u\n", policy->cpuinfo.max_freq); return sprintf(buf, "%u\n", policy->cpuinfo.max_freq);
} }
...@@ -1098,6 +1133,7 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) ...@@ -1098,6 +1133,7 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
cpufreq_global_kobject, "policy%u", cpu); cpufreq_global_kobject, "policy%u", cpu);
if (ret) { if (ret) {
pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret); pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret);
kobject_put(&policy->kobj);
goto err_free_real_cpus; goto err_free_real_cpus;
} }
...@@ -1550,7 +1586,7 @@ static unsigned int __cpufreq_get(struct cpufreq_policy *policy) ...@@ -1550,7 +1586,7 @@ static unsigned int __cpufreq_get(struct cpufreq_policy *policy)
{ {
unsigned int ret_freq = 0; unsigned int ret_freq = 0;
if (unlikely(policy_is_inactive(policy)) || !cpufreq_driver->get) if (unlikely(policy_is_inactive(policy)))
return ret_freq; return ret_freq;
ret_freq = cpufreq_driver->get(policy->cpu); ret_freq = cpufreq_driver->get(policy->cpu);
...@@ -1588,7 +1624,8 @@ unsigned int cpufreq_get(unsigned int cpu) ...@@ -1588,7 +1624,8 @@ unsigned int cpufreq_get(unsigned int cpu)
if (policy) { if (policy) {
down_read(&policy->rwsem); down_read(&policy->rwsem);
ret_freq = __cpufreq_get(policy); if (cpufreq_driver->get)
ret_freq = __cpufreq_get(policy);
up_read(&policy->rwsem); up_read(&policy->rwsem);
cpufreq_cpu_put(policy); cpufreq_cpu_put(policy);
...@@ -2229,8 +2266,8 @@ EXPORT_SYMBOL(cpufreq_get_policy); ...@@ -2229,8 +2266,8 @@ EXPORT_SYMBOL(cpufreq_get_policy);
* *
* The cpuinfo part of @policy is not updated by this function. * The cpuinfo part of @policy is not updated by this function.
*/ */
static int cpufreq_set_policy(struct cpufreq_policy *policy, int cpufreq_set_policy(struct cpufreq_policy *policy,
struct cpufreq_policy *new_policy) struct cpufreq_policy *new_policy)
{ {
struct cpufreq_governor *old_gov; struct cpufreq_governor *old_gov;
int ret; int ret;
...@@ -2337,17 +2374,12 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, ...@@ -2337,17 +2374,12 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
*/ */
void cpufreq_update_policy(unsigned int cpu) void cpufreq_update_policy(unsigned int cpu)
{ {
struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu);
struct cpufreq_policy new_policy; struct cpufreq_policy new_policy;
if (!policy) if (!policy)
return; return;
down_write(&policy->rwsem);
if (policy_is_inactive(policy))
goto unlock;
/* /*
* BIOS might change freq behind our back * BIOS might change freq behind our back
* -> ask driver for current freq and notify governors about a change * -> ask driver for current freq and notify governors about a change
...@@ -2364,12 +2396,26 @@ void cpufreq_update_policy(unsigned int cpu) ...@@ -2364,12 +2396,26 @@ void cpufreq_update_policy(unsigned int cpu)
cpufreq_set_policy(policy, &new_policy); cpufreq_set_policy(policy, &new_policy);
unlock: unlock:
up_write(&policy->rwsem); cpufreq_cpu_release(policy);
cpufreq_cpu_put(policy);
} }
EXPORT_SYMBOL(cpufreq_update_policy); EXPORT_SYMBOL(cpufreq_update_policy);
/**
* cpufreq_update_limits - Update policy limits for a given CPU.
* @cpu: CPU to update the policy limits for.
*
* Invoke the driver's ->update_limits callback if present or call
* cpufreq_update_policy() for @cpu.
*/
void cpufreq_update_limits(unsigned int cpu)
{
if (cpufreq_driver->update_limits)
cpufreq_driver->update_limits(cpu);
else
cpufreq_update_policy(cpu);
}
EXPORT_SYMBOL_GPL(cpufreq_update_limits);
/********************************************************************* /*********************************************************************
* BOOST * * BOOST *
*********************************************************************/ *********************************************************************/
...@@ -2426,7 +2472,7 @@ int cpufreq_boost_trigger_state(int state) ...@@ -2426,7 +2472,7 @@ int cpufreq_boost_trigger_state(int state)
static bool cpufreq_boost_supported(void) static bool cpufreq_boost_supported(void)
{ {
return likely(cpufreq_driver) && cpufreq_driver->set_boost; return cpufreq_driver->set_boost;
} }
static int create_boost_sysfs_file(void) static int create_boost_sysfs_file(void)
......
...@@ -459,6 +459,8 @@ int cpufreq_dbs_governor_init(struct cpufreq_policy *policy) ...@@ -459,6 +459,8 @@ int cpufreq_dbs_governor_init(struct cpufreq_policy *policy)
/* Failure, so roll back. */ /* Failure, so roll back. */
pr_err("initialization failed (dbs_data kobject init error %d)\n", ret); pr_err("initialization failed (dbs_data kobject init error %d)\n", ret);
kobject_put(&dbs_data->attr_set.kobj);
policy->governor_data = NULL; policy->governor_data = NULL;
if (!have_governor_per_policy()) if (!have_governor_per_policy())
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
static DEFINE_SPINLOCK(cpufreq_stats_lock);
struct cpufreq_stats { struct cpufreq_stats {
unsigned int total_trans; unsigned int total_trans;
...@@ -23,6 +22,7 @@ struct cpufreq_stats { ...@@ -23,6 +22,7 @@ struct cpufreq_stats {
unsigned int state_num; unsigned int state_num;
unsigned int last_index; unsigned int last_index;
u64 *time_in_state; u64 *time_in_state;
spinlock_t lock;
unsigned int *freq_table; unsigned int *freq_table;
unsigned int *trans_table; unsigned int *trans_table;
}; };
...@@ -39,12 +39,12 @@ static void cpufreq_stats_clear_table(struct cpufreq_stats *stats) ...@@ -39,12 +39,12 @@ static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
{ {
unsigned int count = stats->max_state; unsigned int count = stats->max_state;
spin_lock(&cpufreq_stats_lock); spin_lock(&stats->lock);
memset(stats->time_in_state, 0, count * sizeof(u64)); memset(stats->time_in_state, 0, count * sizeof(u64));
memset(stats->trans_table, 0, count * count * sizeof(int)); memset(stats->trans_table, 0, count * count * sizeof(int));
stats->last_time = get_jiffies_64(); stats->last_time = get_jiffies_64();
stats->total_trans = 0; stats->total_trans = 0;
spin_unlock(&cpufreq_stats_lock); spin_unlock(&stats->lock);
} }
static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf) static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
...@@ -62,9 +62,9 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) ...@@ -62,9 +62,9 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
if (policy->fast_switch_enabled) if (policy->fast_switch_enabled)
return 0; return 0;
spin_lock(&cpufreq_stats_lock); spin_lock(&stats->lock);
cpufreq_stats_update(stats); cpufreq_stats_update(stats);
spin_unlock(&cpufreq_stats_lock); spin_unlock(&stats->lock);
for (i = 0; i < stats->state_num; i++) { for (i = 0; i < stats->state_num; i++) {
len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i], len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
...@@ -211,6 +211,7 @@ void cpufreq_stats_create_table(struct cpufreq_policy *policy) ...@@ -211,6 +211,7 @@ void cpufreq_stats_create_table(struct cpufreq_policy *policy)
stats->state_num = i; stats->state_num = i;
stats->last_time = get_jiffies_64(); stats->last_time = get_jiffies_64();
stats->last_index = freq_table_get_index(stats, policy->cur); stats->last_index = freq_table_get_index(stats, policy->cur);
spin_lock_init(&stats->lock);
policy->stats = stats; policy->stats = stats;
ret = sysfs_create_group(&policy->kobj, &stats_attr_group); ret = sysfs_create_group(&policy->kobj, &stats_attr_group);
...@@ -242,11 +243,11 @@ void cpufreq_stats_record_transition(struct cpufreq_policy *policy, ...@@ -242,11 +243,11 @@ void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
if (old_index == -1 || new_index == -1 || old_index == new_index) if (old_index == -1 || new_index == -1 || old_index == new_index)
return; return;
spin_lock(&cpufreq_stats_lock); spin_lock(&stats->lock);
cpufreq_stats_update(stats); cpufreq_stats_update(stats);
stats->last_index = new_index; stats->last_index = new_index;
stats->trans_table[old_index * stats->max_state + new_index]++; stats->trans_table[old_index * stats->max_state + new_index]++;
stats->total_trans++; stats->total_trans++;
spin_unlock(&cpufreq_stats_lock); spin_unlock(&stats->lock);
} }
...@@ -290,9 +290,6 @@ EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs); ...@@ -290,9 +290,6 @@ EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);
struct freq_attr *cpufreq_generic_attr[] = { struct freq_attr *cpufreq_generic_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs, &cpufreq_freq_attr_scaling_available_freqs,
#ifdef CONFIG_CPU_FREQ_BOOST_SW
&cpufreq_freq_attr_scaling_boost_freqs,
#endif
NULL, NULL,
}; };
EXPORT_SYMBOL_GPL(cpufreq_generic_attr); EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
......
...@@ -388,11 +388,11 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ...@@ -388,11 +388,11 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
ret = imx6ul_opp_check_speed_grading(cpu_dev); ret = imx6ul_opp_check_speed_grading(cpu_dev);
if (ret) { if (ret) {
if (ret == -EPROBE_DEFER) if (ret == -EPROBE_DEFER)
return ret; goto put_node;
dev_err(cpu_dev, "failed to read ocotp: %d\n", dev_err(cpu_dev, "failed to read ocotp: %d\n",
ret); ret);
return ret; goto put_node;
} }
} else { } else {
imx6q_opp_check_speed_grading(cpu_dev); imx6q_opp_check_speed_grading(cpu_dev);
......
...@@ -179,6 +179,7 @@ struct vid_data { ...@@ -179,6 +179,7 @@ struct vid_data {
* based on the MSR_IA32_MISC_ENABLE value and whether or * based on the MSR_IA32_MISC_ENABLE value and whether or
* not the maximum reported turbo P-state is different from * not the maximum reported turbo P-state is different from
* the maximum reported non-turbo one. * the maximum reported non-turbo one.
* @turbo_disabled_mf: The @turbo_disabled value reflected by cpuinfo.max_freq.
* @min_perf_pct: Minimum capacity limit in percent of the maximum turbo * @min_perf_pct: Minimum capacity limit in percent of the maximum turbo
* P-state capacity. * P-state capacity.
* @max_perf_pct: Maximum capacity limit in percent of the maximum turbo * @max_perf_pct: Maximum capacity limit in percent of the maximum turbo
...@@ -187,6 +188,7 @@ struct vid_data { ...@@ -187,6 +188,7 @@ struct vid_data {
struct global_params { struct global_params {
bool no_turbo; bool no_turbo;
bool turbo_disabled; bool turbo_disabled;
bool turbo_disabled_mf;
int max_perf_pct; int max_perf_pct;
int min_perf_pct; int min_perf_pct;
}; };
...@@ -525,7 +527,7 @@ static s16 intel_pstate_get_epb(struct cpudata *cpu_data) ...@@ -525,7 +527,7 @@ static s16 intel_pstate_get_epb(struct cpudata *cpu_data)
u64 epb; u64 epb;
int ret; int ret;
if (!static_cpu_has(X86_FEATURE_EPB)) if (!boot_cpu_has(X86_FEATURE_EPB))
return -ENXIO; return -ENXIO;
ret = rdmsrl_on_cpu(cpu_data->cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb); ret = rdmsrl_on_cpu(cpu_data->cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
...@@ -539,7 +541,7 @@ static s16 intel_pstate_get_epp(struct cpudata *cpu_data, u64 hwp_req_data) ...@@ -539,7 +541,7 @@ static s16 intel_pstate_get_epp(struct cpudata *cpu_data, u64 hwp_req_data)
{ {
s16 epp; s16 epp;
if (static_cpu_has(X86_FEATURE_HWP_EPP)) { if (boot_cpu_has(X86_FEATURE_HWP_EPP)) {
/* /*
* When hwp_req_data is 0, means that caller didn't read * When hwp_req_data is 0, means that caller didn't read
* MSR_HWP_REQUEST, so need to read and get EPP. * MSR_HWP_REQUEST, so need to read and get EPP.
...@@ -564,7 +566,7 @@ static int intel_pstate_set_epb(int cpu, s16 pref) ...@@ -564,7 +566,7 @@ static int intel_pstate_set_epb(int cpu, s16 pref)
u64 epb; u64 epb;
int ret; int ret;
if (!static_cpu_has(X86_FEATURE_EPB)) if (!boot_cpu_has(X86_FEATURE_EPB))
return -ENXIO; return -ENXIO;
ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb); ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
...@@ -612,7 +614,7 @@ static int intel_pstate_get_energy_pref_index(struct cpudata *cpu_data) ...@@ -612,7 +614,7 @@ static int intel_pstate_get_energy_pref_index(struct cpudata *cpu_data)
if (epp < 0) if (epp < 0)
return epp; return epp;
if (static_cpu_has(X86_FEATURE_HWP_EPP)) { if (boot_cpu_has(X86_FEATURE_HWP_EPP)) {
if (epp == HWP_EPP_PERFORMANCE) if (epp == HWP_EPP_PERFORMANCE)
return 1; return 1;
if (epp <= HWP_EPP_BALANCE_PERFORMANCE) if (epp <= HWP_EPP_BALANCE_PERFORMANCE)
...@@ -621,7 +623,7 @@ static int intel_pstate_get_energy_pref_index(struct cpudata *cpu_data) ...@@ -621,7 +623,7 @@ static int intel_pstate_get_energy_pref_index(struct cpudata *cpu_data)
return 3; return 3;
else else
return 4; return 4;
} else if (static_cpu_has(X86_FEATURE_EPB)) { } else if (boot_cpu_has(X86_FEATURE_EPB)) {
/* /*
* Range: * Range:
* 0x00-0x03 : Performance * 0x00-0x03 : Performance
...@@ -649,7 +651,7 @@ static int intel_pstate_set_energy_pref_index(struct cpudata *cpu_data, ...@@ -649,7 +651,7 @@ static int intel_pstate_set_energy_pref_index(struct cpudata *cpu_data,
mutex_lock(&intel_pstate_limits_lock); mutex_lock(&intel_pstate_limits_lock);
if (static_cpu_has(X86_FEATURE_HWP_EPP)) { if (boot_cpu_has(X86_FEATURE_HWP_EPP)) {
u64 value; u64 value;
ret = rdmsrl_on_cpu(cpu_data->cpu, MSR_HWP_REQUEST, &value); ret = rdmsrl_on_cpu(cpu_data->cpu, MSR_HWP_REQUEST, &value);
...@@ -824,7 +826,7 @@ static void intel_pstate_hwp_set(unsigned int cpu) ...@@ -824,7 +826,7 @@ static void intel_pstate_hwp_set(unsigned int cpu)
epp = cpu_data->epp_powersave; epp = cpu_data->epp_powersave;
} }
update_epp: update_epp:
if (static_cpu_has(X86_FEATURE_HWP_EPP)) { if (boot_cpu_has(X86_FEATURE_HWP_EPP)) {
value &= ~GENMASK_ULL(31, 24); value &= ~GENMASK_ULL(31, 24);
value |= (u64)epp << 24; value |= (u64)epp << 24;
} else { } else {
...@@ -849,7 +851,7 @@ static void intel_pstate_hwp_force_min_perf(int cpu) ...@@ -849,7 +851,7 @@ static void intel_pstate_hwp_force_min_perf(int cpu)
value |= HWP_MIN_PERF(min_perf); value |= HWP_MIN_PERF(min_perf);
/* Set EPP/EPB to min */ /* Set EPP/EPB to min */
if (static_cpu_has(X86_FEATURE_HWP_EPP)) if (boot_cpu_has(X86_FEATURE_HWP_EPP))
value |= HWP_ENERGY_PERF_PREFERENCE(HWP_EPP_POWERSAVE); value |= HWP_ENERGY_PERF_PREFERENCE(HWP_EPP_POWERSAVE);
else else
intel_pstate_set_epb(cpu, HWP_EPP_BALANCE_POWERSAVE); intel_pstate_set_epb(cpu, HWP_EPP_BALANCE_POWERSAVE);
...@@ -897,6 +899,48 @@ static void intel_pstate_update_policies(void) ...@@ -897,6 +899,48 @@ static void intel_pstate_update_policies(void)
cpufreq_update_policy(cpu); cpufreq_update_policy(cpu);
} }
static void intel_pstate_update_max_freq(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu);
struct cpufreq_policy new_policy;
struct cpudata *cpudata;
if (!policy)
return;
cpudata = all_cpu_data[cpu];
policy->cpuinfo.max_freq = global.turbo_disabled_mf ?
cpudata->pstate.max_freq : cpudata->pstate.turbo_freq;
memcpy(&new_policy, policy, sizeof(*policy));
new_policy.max = min(policy->user_policy.max, policy->cpuinfo.max_freq);
new_policy.min = min(policy->user_policy.min, new_policy.max);
cpufreq_set_policy(policy, &new_policy);
cpufreq_cpu_release(policy);
}
static void intel_pstate_update_limits(unsigned int cpu)
{
mutex_lock(&intel_pstate_driver_lock);
update_turbo_state();
/*
* If turbo has been turned on or off globally, policy limits for
* all CPUs need to be updated to reflect that.
*/
if (global.turbo_disabled_mf != global.turbo_disabled) {
global.turbo_disabled_mf = global.turbo_disabled;
for_each_possible_cpu(cpu)
intel_pstate_update_max_freq(cpu);
} else {
cpufreq_update_policy(cpu);
}
mutex_unlock(&intel_pstate_driver_lock);
}
/************************** sysfs begin ************************/ /************************** sysfs begin ************************/
#define show_one(file_name, object) \ #define show_one(file_name, object) \
static ssize_t show_##file_name \ static ssize_t show_##file_name \
...@@ -1197,7 +1241,7 @@ static void __init intel_pstate_sysfs_expose_params(void) ...@@ -1197,7 +1241,7 @@ static void __init intel_pstate_sysfs_expose_params(void)
static void intel_pstate_hwp_enable(struct cpudata *cpudata) static void intel_pstate_hwp_enable(struct cpudata *cpudata)
{ {
/* First disable HWP notification interrupt as we don't process them */ /* First disable HWP notification interrupt as we don't process them */
if (static_cpu_has(X86_FEATURE_HWP_NOTIFY)) if (boot_cpu_has(X86_FEATURE_HWP_NOTIFY))
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00); wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00);
wrmsrl_on_cpu(cpudata->cpu, MSR_PM_ENABLE, 0x1); wrmsrl_on_cpu(cpudata->cpu, MSR_PM_ENABLE, 0x1);
...@@ -2138,6 +2182,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) ...@@ -2138,6 +2182,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy)
/* cpuinfo and default policy values */ /* cpuinfo and default policy values */
policy->cpuinfo.min_freq = cpu->pstate.min_pstate * cpu->pstate.scaling; policy->cpuinfo.min_freq = cpu->pstate.min_pstate * cpu->pstate.scaling;
update_turbo_state(); update_turbo_state();
global.turbo_disabled_mf = global.turbo_disabled;
policy->cpuinfo.max_freq = global.turbo_disabled ? policy->cpuinfo.max_freq = global.turbo_disabled ?
cpu->pstate.max_pstate : cpu->pstate.turbo_pstate; cpu->pstate.max_pstate : cpu->pstate.turbo_pstate;
policy->cpuinfo.max_freq *= cpu->pstate.scaling; policy->cpuinfo.max_freq *= cpu->pstate.scaling;
...@@ -2182,6 +2227,7 @@ static struct cpufreq_driver intel_pstate = { ...@@ -2182,6 +2227,7 @@ static struct cpufreq_driver intel_pstate = {
.init = intel_pstate_cpu_init, .init = intel_pstate_cpu_init,
.exit = intel_pstate_cpu_exit, .exit = intel_pstate_cpu_exit,
.stop_cpu = intel_pstate_stop_cpu, .stop_cpu = intel_pstate_stop_cpu,
.update_limits = intel_pstate_update_limits,
.name = "intel_pstate", .name = "intel_pstate",
}; };
...@@ -2316,6 +2362,7 @@ static struct cpufreq_driver intel_cpufreq = { ...@@ -2316,6 +2362,7 @@ static struct cpufreq_driver intel_cpufreq = {
.init = intel_cpufreq_cpu_init, .init = intel_cpufreq_cpu_init,
.exit = intel_pstate_cpu_exit, .exit = intel_pstate_cpu_exit,
.stop_cpu = intel_cpufreq_stop_cpu, .stop_cpu = intel_cpufreq_stop_cpu,
.update_limits = intel_pstate_update_limits,
.name = "intel_cpufreq", .name = "intel_cpufreq",
}; };
......
...@@ -124,13 +124,14 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev) ...@@ -124,13 +124,14 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev)
priv.cpu_clk = of_clk_get_by_name(np, "cpu_clk"); priv.cpu_clk = of_clk_get_by_name(np, "cpu_clk");
if (IS_ERR(priv.cpu_clk)) { if (IS_ERR(priv.cpu_clk)) {
dev_err(priv.dev, "Unable to get cpuclk\n"); dev_err(priv.dev, "Unable to get cpuclk\n");
return PTR_ERR(priv.cpu_clk); err = PTR_ERR(priv.cpu_clk);
goto out_node;
} }
err = clk_prepare_enable(priv.cpu_clk); err = clk_prepare_enable(priv.cpu_clk);
if (err) { if (err) {
dev_err(priv.dev, "Unable to prepare cpuclk\n"); dev_err(priv.dev, "Unable to prepare cpuclk\n");
return err; goto out_node;
} }
kirkwood_freq_table[0].frequency = clk_get_rate(priv.cpu_clk) / 1000; kirkwood_freq_table[0].frequency = clk_get_rate(priv.cpu_clk) / 1000;
...@@ -161,20 +162,22 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev) ...@@ -161,20 +162,22 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev)
goto out_ddr; goto out_ddr;
} }
of_node_put(np);
np = NULL;
err = cpufreq_register_driver(&kirkwood_cpufreq_driver); err = cpufreq_register_driver(&kirkwood_cpufreq_driver);
if (!err) if (err) {
return 0; dev_err(priv.dev, "Failed to register cpufreq driver\n");
goto out_powersave;
}
dev_err(priv.dev, "Failed to register cpufreq driver\n"); of_node_put(np);
return 0;
out_powersave:
clk_disable_unprepare(priv.powersave_clk); clk_disable_unprepare(priv.powersave_clk);
out_ddr: out_ddr:
clk_disable_unprepare(priv.ddr_clk); clk_disable_unprepare(priv.ddr_clk);
out_cpu: out_cpu:
clk_disable_unprepare(priv.cpu_clk); clk_disable_unprepare(priv.cpu_clk);
out_node:
of_node_put(np); of_node_put(np);
return err; return err;
......
...@@ -210,7 +210,7 @@ static int __init maple_cpufreq_init(void) ...@@ -210,7 +210,7 @@ static int __init maple_cpufreq_init(void)
*/ */
valp = of_get_property(cpunode, "clock-frequency", NULL); valp = of_get_property(cpunode, "clock-frequency", NULL);
if (!valp) if (!valp)
return -ENODEV; goto bail_noprops;
max_freq = (*valp)/1000; max_freq = (*valp)/1000;
maple_cpu_freqs[0].frequency = max_freq; maple_cpu_freqs[0].frequency = max_freq;
maple_cpu_freqs[1].frequency = max_freq/2; maple_cpu_freqs[1].frequency = max_freq/2;
...@@ -231,10 +231,6 @@ static int __init maple_cpufreq_init(void) ...@@ -231,10 +231,6 @@ static int __init maple_cpufreq_init(void)
rc = cpufreq_register_driver(&maple_cpufreq_driver); rc = cpufreq_register_driver(&maple_cpufreq_driver);
of_node_put(cpunode);
return rc;
bail_noprops: bail_noprops:
of_node_put(cpunode); of_node_put(cpunode);
......
...@@ -146,6 +146,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -146,6 +146,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
cpu = of_get_cpu_node(policy->cpu, NULL); cpu = of_get_cpu_node(policy->cpu, NULL);
of_node_put(cpu);
if (!cpu) if (!cpu)
goto out; goto out;
......
...@@ -552,6 +552,7 @@ static int pmac_cpufreq_init_7447A(struct device_node *cpunode) ...@@ -552,6 +552,7 @@ static int pmac_cpufreq_init_7447A(struct device_node *cpunode)
volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select");
if (volt_gpio_np) if (volt_gpio_np)
voltage_gpio = read_gpio(volt_gpio_np); voltage_gpio = read_gpio(volt_gpio_np);
of_node_put(volt_gpio_np);
if (!voltage_gpio){ if (!voltage_gpio){
pr_err("missing cpu-vcore-select gpio\n"); pr_err("missing cpu-vcore-select gpio\n");
return 1; return 1;
...@@ -588,6 +589,7 @@ static int pmac_cpufreq_init_750FX(struct device_node *cpunode) ...@@ -588,6 +589,7 @@ static int pmac_cpufreq_init_750FX(struct device_node *cpunode)
if (volt_gpio_np) if (volt_gpio_np)
voltage_gpio = read_gpio(volt_gpio_np); voltage_gpio = read_gpio(volt_gpio_np);
of_node_put(volt_gpio_np);
pvr = mfspr(SPRN_PVR); pvr = mfspr(SPRN_PVR);
has_cpu_l2lve = !((pvr & 0xf00) == 0x100); has_cpu_l2lve = !((pvr & 0xf00) == 0x100);
......
...@@ -1178,7 +1178,7 @@ static int powernowk8_init(void) ...@@ -1178,7 +1178,7 @@ static int powernowk8_init(void)
unsigned int i, supported_cpus = 0; unsigned int i, supported_cpus = 0;
int ret; int ret;
if (static_cpu_has(X86_FEATURE_HW_PSTATE)) { if (boot_cpu_has(X86_FEATURE_HW_PSTATE)) {
__request_acpi_cpufreq(); __request_acpi_cpufreq();
return -ENODEV; return -ENODEV;
} }
......
...@@ -86,6 +86,7 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -86,6 +86,7 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
if (!cbe_get_cpu_pmd_regs(policy->cpu) || if (!cbe_get_cpu_pmd_regs(policy->cpu) ||
!cbe_get_cpu_mic_tm_regs(policy->cpu)) { !cbe_get_cpu_mic_tm_regs(policy->cpu)) {
pr_info("invalid CBE regs pointers for cpufreq\n"); pr_info("invalid CBE regs pointers for cpufreq\n");
of_node_put(cpu);
return -EINVAL; return -EINVAL;
} }
......
...@@ -280,10 +280,12 @@ static const struct of_device_id node_matches[] __initconst = { ...@@ -280,10 +280,12 @@ static const struct of_device_id node_matches[] __initconst = {
{ .compatible = "fsl,ls1012a-clockgen", }, { .compatible = "fsl,ls1012a-clockgen", },
{ .compatible = "fsl,ls1021a-clockgen", }, { .compatible = "fsl,ls1021a-clockgen", },
{ .compatible = "fsl,ls1028a-clockgen", },
{ .compatible = "fsl,ls1043a-clockgen", }, { .compatible = "fsl,ls1043a-clockgen", },
{ .compatible = "fsl,ls1046a-clockgen", }, { .compatible = "fsl,ls1046a-clockgen", },
{ .compatible = "fsl,ls1088a-clockgen", }, { .compatible = "fsl,ls1088a-clockgen", },
{ .compatible = "fsl,ls2080a-clockgen", }, { .compatible = "fsl,ls2080a-clockgen", },
{ .compatible = "fsl,lx2160a-clockgen", },
{ .compatible = "fsl,p4080-clockgen", }, { .compatible = "fsl,p4080-clockgen", },
{ .compatible = "fsl,qoriq-clockgen-1.0", }, { .compatible = "fsl,qoriq-clockgen-1.0", },
{ .compatible = "fsl,qoriq-clockgen-2.0", }, { .compatible = "fsl,qoriq-clockgen-2.0", },
......
...@@ -412,7 +412,7 @@ static int centrino_cpu_exit(struct cpufreq_policy *policy) ...@@ -412,7 +412,7 @@ static int centrino_cpu_exit(struct cpufreq_policy *policy)
} }
/** /**
* centrino_setpolicy - set a new CPUFreq policy * centrino_target - set a new CPUFreq policy
* @policy: new policy * @policy: new policy
* @index: index of target frequency * @index: index of target frequency
* *
......
...@@ -84,7 +84,7 @@ static struct cpuidle_driver exynos_idle_driver = { ...@@ -84,7 +84,7 @@ static struct cpuidle_driver exynos_idle_driver = {
[1] = { [1] = {
.enter = exynos_enter_lowpower, .enter = exynos_enter_lowpower,
.exit_latency = 300, .exit_latency = 300,
.target_residency = 100000, .target_residency = 10000,
.name = "C1", .name = "C1",
.desc = "ARM power down", .desc = "ARM power down",
}, },
......
...@@ -328,9 +328,23 @@ int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, ...@@ -328,9 +328,23 @@ int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev, int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
int index) int index)
{ {
int ret = 0;
/*
* Store the next hrtimer, which becomes either next tick or the next
* timer event, whatever expires first. Additionally, to make this data
* useful for consumers outside cpuidle, we rely on that the governor's
* ->select() callback have decided, whether to stop the tick or not.
*/
WRITE_ONCE(dev->next_hrtimer, tick_nohz_get_next_hrtimer());
if (cpuidle_state_is_coupled(drv, index)) if (cpuidle_state_is_coupled(drv, index))
return cpuidle_enter_state_coupled(dev, drv, index); ret = cpuidle_enter_state_coupled(dev, drv, index);
return cpuidle_enter_state(dev, drv, index); else
ret = cpuidle_enter_state(dev, drv, index);
WRITE_ONCE(dev->next_hrtimer, 0);
return ret;
} }
/** /**
...@@ -511,6 +525,7 @@ static void __cpuidle_device_init(struct cpuidle_device *dev) ...@@ -511,6 +525,7 @@ static void __cpuidle_device_init(struct cpuidle_device *dev)
{ {
memset(dev->states_usage, 0, sizeof(dev->states_usage)); memset(dev->states_usage, 0, sizeof(dev->states_usage));
dev->last_residency = 0; dev->last_residency = 0;
dev->next_hrtimer = 0;
} }
/** /**
......
...@@ -240,7 +240,7 @@ struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev, ...@@ -240,7 +240,7 @@ struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev,
} }
list_for_each_entry(edev, &devfreq_event_list, node) { list_for_each_entry(edev, &devfreq_event_list, node) {
if (!strcmp(edev->desc->name, node->name)) if (of_node_name_eq(node, edev->desc->name))
goto out; goto out;
} }
edev = NULL; edev = NULL;
......
...@@ -29,6 +29,9 @@ ...@@ -29,6 +29,9 @@
#include <linux/of.h> #include <linux/of.h>
#include "governor.h" #include "governor.h"
#define CREATE_TRACE_POINTS
#include <trace/events/devfreq.h>
static struct class *devfreq_class; static struct class *devfreq_class;
/* /*
...@@ -228,7 +231,7 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) ...@@ -228,7 +231,7 @@ static struct devfreq_governor *find_devfreq_governor(const char *name)
* if is not found. This can happen when both drivers (the governor driver * if is not found. This can happen when both drivers (the governor driver
* and the driver that call devfreq_add_device) are built as modules. * and the driver that call devfreq_add_device) are built as modules.
* devfreq_list_lock should be held by the caller. Returns the matched * devfreq_list_lock should be held by the caller. Returns the matched
* governor's pointer. * governor's pointer or an error pointer.
*/ */
static struct devfreq_governor *try_then_request_governor(const char *name) static struct devfreq_governor *try_then_request_governor(const char *name)
{ {
...@@ -254,7 +257,7 @@ static struct devfreq_governor *try_then_request_governor(const char *name) ...@@ -254,7 +257,7 @@ static struct devfreq_governor *try_then_request_governor(const char *name)
/* Restore previous state before return */ /* Restore previous state before return */
mutex_lock(&devfreq_list_lock); mutex_lock(&devfreq_list_lock);
if (err) if (err)
return NULL; return ERR_PTR(err);
governor = find_devfreq_governor(name); governor = find_devfreq_governor(name);
} }
...@@ -394,6 +397,8 @@ static void devfreq_monitor(struct work_struct *work) ...@@ -394,6 +397,8 @@ static void devfreq_monitor(struct work_struct *work)
queue_delayed_work(devfreq_wq, &devfreq->work, queue_delayed_work(devfreq_wq, &devfreq->work,
msecs_to_jiffies(devfreq->profile->polling_ms)); msecs_to_jiffies(devfreq->profile->polling_ms));
mutex_unlock(&devfreq->lock); mutex_unlock(&devfreq->lock);
trace_devfreq_monitor(devfreq);
} }
/** /**
...@@ -528,7 +533,7 @@ void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay) ...@@ -528,7 +533,7 @@ void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay)
mutex_lock(&devfreq->lock); mutex_lock(&devfreq->lock);
if (!devfreq->stop_polling) if (!devfreq->stop_polling)
queue_delayed_work(devfreq_wq, &devfreq->work, queue_delayed_work(devfreq_wq, &devfreq->work,
msecs_to_jiffies(devfreq->profile->polling_ms)); msecs_to_jiffies(devfreq->profile->polling_ms));
} }
out: out:
mutex_unlock(&devfreq->lock); mutex_unlock(&devfreq->lock);
...@@ -537,7 +542,7 @@ EXPORT_SYMBOL(devfreq_interval_update); ...@@ -537,7 +542,7 @@ EXPORT_SYMBOL(devfreq_interval_update);
/** /**
* devfreq_notifier_call() - Notify that the device frequency requirements * devfreq_notifier_call() - Notify that the device frequency requirements
* has been changed out of devfreq framework. * has been changed out of devfreq framework.
* @nb: the notifier_block (supposed to be devfreq->nb) * @nb: the notifier_block (supposed to be devfreq->nb)
* @type: not used * @type: not used
* @devp: not used * @devp: not used
...@@ -651,7 +656,7 @@ struct devfreq *devfreq_add_device(struct device *dev, ...@@ -651,7 +656,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_unlock(&devfreq->lock); mutex_unlock(&devfreq->lock);
err = set_freq_table(devfreq); err = set_freq_table(devfreq);
if (err < 0) if (err < 0)
goto err_out; goto err_dev;
mutex_lock(&devfreq->lock); mutex_lock(&devfreq->lock);
} }
...@@ -683,16 +688,27 @@ struct devfreq *devfreq_add_device(struct device *dev, ...@@ -683,16 +688,27 @@ struct devfreq *devfreq_add_device(struct device *dev,
goto err_out; goto err_out;
} }
devfreq->trans_table = devfreq->trans_table = devm_kzalloc(&devfreq->dev,
devm_kzalloc(&devfreq->dev, array3_size(sizeof(unsigned int),
array3_size(sizeof(unsigned int), devfreq->profile->max_state,
devfreq->profile->max_state, devfreq->profile->max_state),
devfreq->profile->max_state), GFP_KERNEL);
GFP_KERNEL); if (!devfreq->trans_table) {
mutex_unlock(&devfreq->lock);
err = -ENOMEM;
goto err_devfreq;
}
devfreq->time_in_state = devm_kcalloc(&devfreq->dev, devfreq->time_in_state = devm_kcalloc(&devfreq->dev,
devfreq->profile->max_state, devfreq->profile->max_state,
sizeof(unsigned long), sizeof(unsigned long),
GFP_KERNEL); GFP_KERNEL);
if (!devfreq->time_in_state) {
mutex_unlock(&devfreq->lock);
err = -ENOMEM;
goto err_devfreq;
}
devfreq->last_stat_updated = jiffies; devfreq->last_stat_updated = jiffies;
srcu_init_notifier_head(&devfreq->transition_notifier_list); srcu_init_notifier_head(&devfreq->transition_notifier_list);
...@@ -726,7 +742,7 @@ struct devfreq *devfreq_add_device(struct device *dev, ...@@ -726,7 +742,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
err_init: err_init:
mutex_unlock(&devfreq_list_lock); mutex_unlock(&devfreq_list_lock);
err_devfreq:
devfreq_remove_device(devfreq); devfreq_remove_device(devfreq);
devfreq = NULL; devfreq = NULL;
err_dev: err_dev:
...@@ -1113,7 +1129,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, ...@@ -1113,7 +1129,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
struct devfreq *df = to_devfreq(dev); struct devfreq *df = to_devfreq(dev);
int ret; int ret;
char str_governor[DEVFREQ_NAME_LEN + 1]; char str_governor[DEVFREQ_NAME_LEN + 1];
struct devfreq_governor *governor; const struct devfreq_governor *governor, *prev_governor;
ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor); ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor);
if (ret != 1) if (ret != 1)
...@@ -1142,12 +1158,24 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, ...@@ -1142,12 +1158,24 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
goto out; goto out;
} }
} }
prev_governor = df->governor;
df->governor = governor; df->governor = governor;
strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN); strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret) if (ret) {
dev_warn(dev, "%s: Governor %s not started(%d)\n", dev_warn(dev, "%s: Governor %s not started(%d)\n",
__func__, df->governor->name, ret); __func__, df->governor->name, ret);
df->governor = prev_governor;
strncpy(df->governor_name, prev_governor->name,
DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret) {
dev_err(dev,
"%s: reverting to Governor %s failed (%d)\n",
__func__, df->governor_name, ret);
df->governor = NULL;
}
}
out: out:
mutex_unlock(&devfreq_list_lock); mutex_unlock(&devfreq_list_lock);
...@@ -1172,7 +1200,7 @@ static ssize_t available_governors_show(struct device *d, ...@@ -1172,7 +1200,7 @@ static ssize_t available_governors_show(struct device *d,
*/ */
if (df->governor->immutable) { if (df->governor->immutable) {
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN, count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
"%s ", df->governor_name); "%s ", df->governor_name);
/* /*
* The devfreq device shows the registered governor except for * The devfreq device shows the registered governor except for
* immutable governors such as passive governor . * immutable governors such as passive governor .
...@@ -1485,8 +1513,8 @@ EXPORT_SYMBOL(devfreq_recommended_opp); ...@@ -1485,8 +1513,8 @@ EXPORT_SYMBOL(devfreq_recommended_opp);
/** /**
* devfreq_register_opp_notifier() - Helper function to get devfreq notified * devfreq_register_opp_notifier() - Helper function to get devfreq notified
* for any changes in the OPP availability * for any changes in the OPP availability
* changes * changes
* @dev: The devfreq user device. (parent of devfreq) * @dev: The devfreq user device. (parent of devfreq)
* @devfreq: The devfreq object. * @devfreq: The devfreq object.
*/ */
...@@ -1498,8 +1526,8 @@ EXPORT_SYMBOL(devfreq_register_opp_notifier); ...@@ -1498,8 +1526,8 @@ EXPORT_SYMBOL(devfreq_register_opp_notifier);
/** /**
* devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq
* notified for any changes in the OPP * notified for any changes in the OPP
* availability changes anymore. * availability changes anymore.
* @dev: The devfreq user device. (parent of devfreq) * @dev: The devfreq user device. (parent of devfreq)
* @devfreq: The devfreq object. * @devfreq: The devfreq object.
* *
...@@ -1518,8 +1546,8 @@ static void devm_devfreq_opp_release(struct device *dev, void *res) ...@@ -1518,8 +1546,8 @@ static void devm_devfreq_opp_release(struct device *dev, void *res)
} }
/** /**
* devm_ devfreq_register_opp_notifier() * devm_devfreq_register_opp_notifier() - Resource-managed
* - Resource-managed devfreq_register_opp_notifier() * devfreq_register_opp_notifier()
* @dev: The devfreq user device. (parent of devfreq) * @dev: The devfreq user device. (parent of devfreq)
* @devfreq: The devfreq object. * @devfreq: The devfreq object.
*/ */
...@@ -1547,8 +1575,8 @@ int devm_devfreq_register_opp_notifier(struct device *dev, ...@@ -1547,8 +1575,8 @@ int devm_devfreq_register_opp_notifier(struct device *dev,
EXPORT_SYMBOL(devm_devfreq_register_opp_notifier); EXPORT_SYMBOL(devm_devfreq_register_opp_notifier);
/** /**
* devm_devfreq_unregister_opp_notifier() * devm_devfreq_unregister_opp_notifier() - Resource-managed
* - Resource-managed devfreq_unregister_opp_notifier() * devfreq_unregister_opp_notifier()
* @dev: The devfreq user device. (parent of devfreq) * @dev: The devfreq user device. (parent of devfreq)
* @devfreq: The devfreq object. * @devfreq: The devfreq object.
*/ */
...@@ -1567,8 +1595,8 @@ EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); ...@@ -1567,8 +1595,8 @@ EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier);
* @list: DEVFREQ_TRANSITION_NOTIFIER. * @list: DEVFREQ_TRANSITION_NOTIFIER.
*/ */
int devfreq_register_notifier(struct devfreq *devfreq, int devfreq_register_notifier(struct devfreq *devfreq,
struct notifier_block *nb, struct notifier_block *nb,
unsigned int list) unsigned int list)
{ {
int ret = 0; int ret = 0;
...@@ -1674,9 +1702,9 @@ EXPORT_SYMBOL(devm_devfreq_register_notifier); ...@@ -1674,9 +1702,9 @@ EXPORT_SYMBOL(devm_devfreq_register_notifier);
* @list: DEVFREQ_TRANSITION_NOTIFIER. * @list: DEVFREQ_TRANSITION_NOTIFIER.
*/ */
void devm_devfreq_unregister_notifier(struct device *dev, void devm_devfreq_unregister_notifier(struct device *dev,
struct devfreq *devfreq, struct devfreq *devfreq,
struct notifier_block *nb, struct notifier_block *nb,
unsigned int list) unsigned int list)
{ {
WARN_ON(devres_release(dev, devm_devfreq_notifier_release, WARN_ON(devres_release(dev, devm_devfreq_notifier_release,
devm_devfreq_dev_match, devfreq)); devm_devfreq_dev_match, devfreq));
......
...@@ -529,7 +529,7 @@ static int of_get_devfreq_events(struct device_node *np, ...@@ -529,7 +529,7 @@ static int of_get_devfreq_events(struct device_node *np,
if (!ppmu_events[i].name) if (!ppmu_events[i].name)
continue; continue;
if (!of_node_cmp(node->name, ppmu_events[i].name)) if (of_node_name_eq(node, ppmu_events[i].name))
break; break;
} }
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/of.h> #include <linux/of.h>
#include <soc/rockchip/rk3399_grf.h>
#define RK3399_DMC_NUM_CH 2 #define RK3399_DMC_NUM_CH 2
/* DDRMON_CTRL */ /* DDRMON_CTRL */
...@@ -43,18 +45,6 @@ ...@@ -43,18 +45,6 @@
#define DDRMON_CH1_COUNT_NUM 0x3c #define DDRMON_CH1_COUNT_NUM 0x3c
#define DDRMON_CH1_DFI_ACCESS_NUM 0x40 #define DDRMON_CH1_DFI_ACCESS_NUM 0x40
/* pmu grf */
#define PMUGRF_OS_REG2 0x308
#define DDRTYPE_SHIFT 13
#define DDRTYPE_MASK 7
enum {
DDR3 = 3,
LPDDR3 = 6,
LPDDR4 = 7,
UNUSED = 0xFF
};
struct dmc_usage { struct dmc_usage {
u32 access; u32 access;
u32 total; u32 total;
...@@ -83,16 +73,17 @@ static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) ...@@ -83,16 +73,17 @@ static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
u32 ddr_type; u32 ddr_type;
/* get ddr type */ /* get ddr type */
regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, &val); regmap_read(info->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val);
ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK; ddr_type = (val >> RK3399_PMUGRF_DDRTYPE_SHIFT) &
RK3399_PMUGRF_DDRTYPE_MASK;
/* clear DDRMON_CTRL setting */ /* clear DDRMON_CTRL setting */
writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL);
/* set ddr type to dfi */ /* set ddr type to dfi */
if (ddr_type == LPDDR3) if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR3)
writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL);
else if (ddr_type == LPDDR4) else if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR4)
writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL);
/* enable count, use software mode */ /* enable count, use software mode */
...@@ -211,7 +202,7 @@ static int rockchip_dfi_probe(struct platform_device *pdev) ...@@ -211,7 +202,7 @@ static int rockchip_dfi_probe(struct platform_device *pdev)
if (IS_ERR(data->clk)) { if (IS_ERR(data->clk)) {
dev_err(dev, "Cannot get the clk dmc_clk\n"); dev_err(dev, "Cannot get the clk dmc_clk\n");
return PTR_ERR(data->clk); return PTR_ERR(data->clk);
}; }
/* try to find the optional reference to the pmu syscon */ /* try to find the optional reference to the pmu syscon */
node = of_parse_phandle(np, "rockchip,pmu", 0); node = of_parse_phandle(np, "rockchip,pmu", 0);
......
...@@ -514,6 +514,13 @@ static int exynos_bus_probe(struct platform_device *pdev) ...@@ -514,6 +514,13 @@ static int exynos_bus_probe(struct platform_device *pdev)
return ret; return ret;
} }
static void exynos_bus_shutdown(struct platform_device *pdev)
{
struct exynos_bus *bus = dev_get_drvdata(&pdev->dev);
devfreq_suspend_device(bus->devfreq);
}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int exynos_bus_resume(struct device *dev) static int exynos_bus_resume(struct device *dev)
{ {
...@@ -556,6 +563,7 @@ MODULE_DEVICE_TABLE(of, exynos_bus_of_match); ...@@ -556,6 +563,7 @@ MODULE_DEVICE_TABLE(of, exynos_bus_of_match);
static struct platform_driver exynos_bus_platdrv = { static struct platform_driver exynos_bus_platdrv = {
.probe = exynos_bus_probe, .probe = exynos_bus_probe,
.shutdown = exynos_bus_shutdown,
.driver = { .driver = {
.name = "exynos-bus", .name = "exynos-bus",
.pm = &exynos_bus_pm, .pm = &exynos_bus_pm,
......
...@@ -18,14 +18,17 @@ ...@@ -18,14 +18,17 @@
#include <linux/devfreq.h> #include <linux/devfreq.h>
#include <linux/devfreq-event.h> #include <linux/devfreq-event.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <soc/rockchip/rk3399_grf.h>
#include <soc/rockchip/rockchip_sip.h> #include <soc/rockchip/rockchip_sip.h>
struct dram_timing { struct dram_timing {
...@@ -69,8 +72,11 @@ struct rk3399_dmcfreq { ...@@ -69,8 +72,11 @@ struct rk3399_dmcfreq {
struct mutex lock; struct mutex lock;
struct dram_timing timing; struct dram_timing timing;
struct regulator *vdd_center; struct regulator *vdd_center;
struct regmap *regmap_pmu;
unsigned long rate, target_rate; unsigned long rate, target_rate;
unsigned long volt, target_volt; unsigned long volt, target_volt;
unsigned int odt_dis_freq;
int odt_pd_arg0, odt_pd_arg1;
}; };
static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
...@@ -80,6 +86,8 @@ static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, ...@@ -80,6 +86,8 @@ static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
unsigned long old_clk_rate = dmcfreq->rate; unsigned long old_clk_rate = dmcfreq->rate;
unsigned long target_volt, target_rate; unsigned long target_volt, target_rate;
struct arm_smccc_res res;
bool odt_enable = false;
int err; int err;
opp = devfreq_recommended_opp(dev, freq, flags); opp = devfreq_recommended_opp(dev, freq, flags);
...@@ -95,6 +103,19 @@ static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, ...@@ -95,6 +103,19 @@ static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
mutex_lock(&dmcfreq->lock); mutex_lock(&dmcfreq->lock);
if (target_rate >= dmcfreq->odt_dis_freq)
odt_enable = true;
/*
* This makes a SMC call to the TF-A to set the DDR PD (power-down)
* timings and to enable or disable the ODT (on-die termination)
* resistors.
*/
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, dmcfreq->odt_pd_arg0,
dmcfreq->odt_pd_arg1,
ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD,
odt_enable, 0, 0, 0, &res);
/* /*
* If frequency scaling from low to high, adjust voltage first. * If frequency scaling from low to high, adjust voltage first.
* If frequency scaling from high to low, adjust frequency first. * If frequency scaling from high to low, adjust frequency first.
...@@ -294,11 +315,13 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) ...@@ -294,11 +315,13 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
{ {
struct arm_smccc_res res; struct arm_smccc_res res;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node, *node;
struct rk3399_dmcfreq *data; struct rk3399_dmcfreq *data;
int ret, index, size; int ret, index, size;
uint32_t *timing; uint32_t *timing;
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
u32 ddr_type;
u32 val;
data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
if (!data) if (!data)
...@@ -322,7 +345,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) ...@@ -322,7 +345,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
dev_err(dev, "Cannot get the clk dmc_clk\n"); dev_err(dev, "Cannot get the clk dmc_clk\n");
return PTR_ERR(data->dmc_clk); return PTR_ERR(data->dmc_clk);
}; }
data->edev = devfreq_event_get_edev_by_phandle(dev, 0); data->edev = devfreq_event_get_edev_by_phandle(dev, 0);
if (IS_ERR(data->edev)) if (IS_ERR(data->edev))
...@@ -354,10 +377,56 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) ...@@ -354,10 +377,56 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
} }
} }
node = of_parse_phandle(np, "rockchip,pmu", 0);
if (node) {
data->regmap_pmu = syscon_node_to_regmap(node);
if (IS_ERR(data->regmap_pmu))
return PTR_ERR(data->regmap_pmu);
}
regmap_read(data->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val);
ddr_type = (val >> RK3399_PMUGRF_DDRTYPE_SHIFT) &
RK3399_PMUGRF_DDRTYPE_MASK;
switch (ddr_type) {
case RK3399_PMUGRF_DDRTYPE_DDR3:
data->odt_dis_freq = data->timing.ddr3_odt_dis_freq;
break;
case RK3399_PMUGRF_DDRTYPE_LPDDR3:
data->odt_dis_freq = data->timing.lpddr3_odt_dis_freq;
break;
case RK3399_PMUGRF_DDRTYPE_LPDDR4:
data->odt_dis_freq = data->timing.lpddr4_odt_dis_freq;
break;
default:
return -EINVAL;
};
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
ROCKCHIP_SIP_CONFIG_DRAM_INIT, ROCKCHIP_SIP_CONFIG_DRAM_INIT,
0, 0, 0, 0, &res); 0, 0, 0, 0, &res);
/*
* In TF-A there is a platform SIP call to set the PD (power-down)
* timings and to enable or disable the ODT (on-die termination).
* This call needs three arguments as follows:
*
* arg0:
* bit[0-7] : sr_idle
* bit[8-15] : sr_mc_gate_idle
* bit[16-31] : standby idle
* arg1:
* bit[0-11] : pd_idle
* bit[16-27] : srpd_lite_idle
* arg2:
* bit[0] : odt enable
*/
data->odt_pd_arg0 = (data->timing.sr_idle & 0xff) |
((data->timing.sr_mc_gate_idle & 0xff) << 8) |
((data->timing.standby_idle & 0xffff) << 16);
data->odt_pd_arg1 = (data->timing.pd_idle & 0xfff) |
((data->timing.srpd_lite_idle & 0xfff) << 16);
/* /*
* We add a devfreq driver to our parent since it has a device tree node * We add a devfreq driver to our parent since it has a device tree node
* with operating points. * with operating points.
......
...@@ -573,10 +573,7 @@ static int tegra_governor_get_target(struct devfreq *devfreq, ...@@ -573,10 +573,7 @@ static int tegra_governor_get_target(struct devfreq *devfreq,
static int tegra_governor_event_handler(struct devfreq *devfreq, static int tegra_governor_event_handler(struct devfreq *devfreq,
unsigned int event, void *data) unsigned int event, void *data)
{ {
struct tegra_devfreq *tegra; struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent);
int ret = 0;
tegra = dev_get_drvdata(devfreq->dev.parent);
switch (event) { switch (event) {
case DEVFREQ_GOV_START: case DEVFREQ_GOV_START:
...@@ -600,7 +597,7 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, ...@@ -600,7 +597,7 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
break; break;
} }
return ret; return 0;
} }
static struct devfreq_governor tegra_devfreq_governor = { static struct devfreq_governor tegra_devfreq_governor = {
......
...@@ -5,20 +5,6 @@ ...@@ -5,20 +5,6 @@
menu "Firmware Drivers" menu "Firmware Drivers"
config ARM_PSCI_FW
bool
config ARM_PSCI_CHECKER
bool "ARM PSCI checker"
depends on ARM_PSCI_FW && HOTPLUG_CPU && CPU_IDLE && !TORTURE_TEST
help
Run the PSCI checker during startup. This checks that hotplug and
suspend operations work correctly when using PSCI.
The torture tests may interfere with the PSCI checker by turning CPUs
on and off through hotplug, so for now torture tests and PSCI checker
are mutually exclusive.
config ARM_SCMI_PROTOCOL config ARM_SCMI_PROTOCOL
bool "ARM System Control and Management Interface (SCMI) Message Protocol" bool "ARM System Control and Management Interface (SCMI) Message Protocol"
depends on ARM || ARM64 || COMPILE_TEST depends on ARM || ARM64 || COMPILE_TEST
...@@ -270,6 +256,7 @@ config TI_SCI_PROTOCOL ...@@ -270,6 +256,7 @@ config TI_SCI_PROTOCOL
config HAVE_ARM_SMCCC config HAVE_ARM_SMCCC
bool bool
source "drivers/firmware/psci/Kconfig"
source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig" source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig" source "drivers/firmware/efi/Kconfig"
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
# #
# Makefile for the linux kernel. # Makefile for the linux kernel.
# #
obj-$(CONFIG_ARM_PSCI_FW) += psci.o
obj-$(CONFIG_ARM_PSCI_CHECKER) += psci_checker.o
obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o
obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o
obj-$(CONFIG_ARM_SDE_INTERFACE) += arm_sdei.o obj-$(CONFIG_ARM_SDE_INTERFACE) += arm_sdei.o
...@@ -25,6 +23,7 @@ CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQU ...@@ -25,6 +23,7 @@ CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQU
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/ obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/
obj-y += psci/
obj-y += broadcom/ obj-y += broadcom/
obj-y += meson/ obj-y += meson/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
......
config ARM_PSCI_FW
bool
config ARM_PSCI_CHECKER
bool "ARM PSCI checker"
depends on ARM_PSCI_FW && HOTPLUG_CPU && CPU_IDLE && !TORTURE_TEST
help
Run the PSCI checker during startup. This checks that hotplug and
suspend operations work correctly when using PSCI.
The torture tests may interfere with the PSCI checker by turning CPUs
on and off through hotplug, so for now torture tests and PSCI checker
are mutually exclusive.
# SPDX-License-Identifier: GPL-2.0
#
obj-$(CONFIG_ARM_PSCI_FW) += psci.o
obj-$(CONFIG_ARM_PSCI_CHECKER) += psci_checker.o
...@@ -88,6 +88,7 @@ static u32 psci_function_id[PSCI_FN_MAX]; ...@@ -88,6 +88,7 @@ static u32 psci_function_id[PSCI_FN_MAX];
PSCI_1_0_EXT_POWER_STATE_TYPE_MASK) PSCI_1_0_EXT_POWER_STATE_TYPE_MASK)
static u32 psci_cpu_suspend_feature; static u32 psci_cpu_suspend_feature;
static bool psci_system_reset2_supported;
static inline bool psci_has_ext_power_state(void) static inline bool psci_has_ext_power_state(void)
{ {
...@@ -95,6 +96,11 @@ static inline bool psci_has_ext_power_state(void) ...@@ -95,6 +96,11 @@ static inline bool psci_has_ext_power_state(void)
PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK; PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK;
} }
static inline bool psci_has_osi_support(void)
{
return psci_cpu_suspend_feature & PSCI_1_0_OS_INITIATED;
}
static inline bool psci_power_state_loses_context(u32 state) static inline bool psci_power_state_loses_context(u32 state)
{ {
const u32 mask = psci_has_ext_power_state() ? const u32 mask = psci_has_ext_power_state() ?
...@@ -253,7 +259,17 @@ static int get_set_conduit_method(struct device_node *np) ...@@ -253,7 +259,17 @@ static int get_set_conduit_method(struct device_node *np)
static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd) static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)
{ {
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) &&
psci_system_reset2_supported) {
/*
* reset_type[31] = 0 (architectural)
* reset_type[30:0] = 0 (SYSTEM_WARM_RESET)
* cookie = 0 (ignored by the implementation)
*/
invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, 0, 0);
} else {
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
}
} }
static void psci_sys_poweroff(void) static void psci_sys_poweroff(void)
...@@ -270,9 +286,26 @@ static int __init psci_features(u32 psci_func_id) ...@@ -270,9 +286,26 @@ static int __init psci_features(u32 psci_func_id)
#ifdef CONFIG_CPU_IDLE #ifdef CONFIG_CPU_IDLE
static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
static int psci_dt_parse_state_node(struct device_node *np, u32 *state)
{
int err = of_property_read_u32(np, "arm,psci-suspend-param", state);
if (err) {
pr_warn("%pOF missing arm,psci-suspend-param property\n", np);
return err;
}
if (!psci_power_state_is_valid(*state)) {
pr_warn("Invalid PSCI power state %#x\n", *state);
return -EINVAL;
}
return 0;
}
static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
{ {
int i, ret, count = 0; int i, ret = 0, count = 0;
u32 *psci_states; u32 *psci_states;
struct device_node *state_node; struct device_node *state_node;
...@@ -291,29 +324,16 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) ...@@ -291,29 +324,16 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
u32 state;
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
ret = psci_dt_parse_state_node(state_node, &psci_states[i]);
of_node_put(state_node);
ret = of_property_read_u32(state_node, if (ret)
"arm,psci-suspend-param",
&state);
if (ret) {
pr_warn(" * %pOF missing arm,psci-suspend-param property\n",
state_node);
of_node_put(state_node);
goto free_mem; goto free_mem;
}
of_node_put(state_node); pr_debug("psci-power-state %#x index %d\n", psci_states[i], i);
pr_debug("psci-power-state %#x index %d\n", state, i);
if (!psci_power_state_is_valid(state)) {
pr_warn("Invalid PSCI power state %#x\n", state);
ret = -EINVAL;
goto free_mem;
}
psci_states[i] = state;
} }
/* Idle states parsed correctly, initialize per-cpu pointer */ /* Idle states parsed correctly, initialize per-cpu pointer */
per_cpu(psci_power_state, cpu) = psci_states; per_cpu(psci_power_state, cpu) = psci_states;
return 0; return 0;
...@@ -451,6 +471,16 @@ static const struct platform_suspend_ops psci_suspend_ops = { ...@@ -451,6 +471,16 @@ static const struct platform_suspend_ops psci_suspend_ops = {
.enter = psci_system_suspend_enter, .enter = psci_system_suspend_enter,
}; };
static void __init psci_init_system_reset2(void)
{
int ret;
ret = psci_features(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2));
if (ret != PSCI_RET_NOT_SUPPORTED)
psci_system_reset2_supported = true;
}
static void __init psci_init_system_suspend(void) static void __init psci_init_system_suspend(void)
{ {
int ret; int ret;
...@@ -588,6 +618,7 @@ static int __init psci_probe(void) ...@@ -588,6 +618,7 @@ static int __init psci_probe(void)
psci_init_smccc(); psci_init_smccc();
psci_init_cpu_suspend(); psci_init_cpu_suspend();
psci_init_system_suspend(); psci_init_system_suspend();
psci_init_system_reset2();
} }
return 0; return 0;
...@@ -605,9 +636,9 @@ static int __init psci_0_2_init(struct device_node *np) ...@@ -605,9 +636,9 @@ static int __init psci_0_2_init(struct device_node *np)
int err; int err;
err = get_set_conduit_method(np); err = get_set_conduit_method(np);
if (err) if (err)
goto out_put_node; return err;
/* /*
* Starting with v0.2, the PSCI specification introduced a call * Starting with v0.2, the PSCI specification introduced a call
* (PSCI_VERSION) that allows probing the firmware version, so * (PSCI_VERSION) that allows probing the firmware version, so
...@@ -615,11 +646,7 @@ static int __init psci_0_2_init(struct device_node *np) ...@@ -615,11 +646,7 @@ static int __init psci_0_2_init(struct device_node *np)
* can be carried out according to the specific version reported * can be carried out according to the specific version reported
* by firmware * by firmware
*/ */
err = psci_probe(); return psci_probe();
out_put_node:
of_node_put(np);
return err;
} }
/* /*
...@@ -631,9 +658,8 @@ static int __init psci_0_1_init(struct device_node *np) ...@@ -631,9 +658,8 @@ static int __init psci_0_1_init(struct device_node *np)
int err; int err;
err = get_set_conduit_method(np); err = get_set_conduit_method(np);
if (err) if (err)
goto out_put_node; return err;
pr_info("Using PSCI v0.1 Function IDs from DT\n"); pr_info("Using PSCI v0.1 Function IDs from DT\n");
...@@ -657,15 +683,27 @@ static int __init psci_0_1_init(struct device_node *np) ...@@ -657,15 +683,27 @@ static int __init psci_0_1_init(struct device_node *np)
psci_ops.migrate = psci_migrate; psci_ops.migrate = psci_migrate;
} }
out_put_node: return 0;
of_node_put(np); }
return err;
static int __init psci_1_0_init(struct device_node *np)
{
int err;
err = psci_0_2_init(np);
if (err)
return err;
if (psci_has_osi_support())
pr_info("OSI mode supported.\n");
return 0;
} }
static const struct of_device_id psci_of_match[] __initconst = { static const struct of_device_id psci_of_match[] __initconst = {
{ .compatible = "arm,psci", .data = psci_0_1_init}, { .compatible = "arm,psci", .data = psci_0_1_init},
{ .compatible = "arm,psci-0.2", .data = psci_0_2_init}, { .compatible = "arm,psci-0.2", .data = psci_0_2_init},
{ .compatible = "arm,psci-1.0", .data = psci_0_2_init}, { .compatible = "arm,psci-1.0", .data = psci_1_0_init},
{}, {},
}; };
...@@ -674,6 +712,7 @@ int __init psci_dt_init(void) ...@@ -674,6 +712,7 @@ int __init psci_dt_init(void)
struct device_node *np; struct device_node *np;
const struct of_device_id *matched_np; const struct of_device_id *matched_np;
psci_initcall_t init_fn; psci_initcall_t init_fn;
int ret;
np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np); np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);
...@@ -681,7 +720,10 @@ int __init psci_dt_init(void) ...@@ -681,7 +720,10 @@ int __init psci_dt_init(void)
return -ENODEV; return -ENODEV;
init_fn = (psci_initcall_t)matched_np->data; init_fn = (psci_initcall_t)matched_np->data;
return init_fn(np); ret = init_fn(np);
of_node_put(np);
return ret;
} }
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
......
...@@ -526,6 +526,60 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, ...@@ -526,6 +526,60 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
/**
* dev_pm_opp_find_freq_ceil_by_volt() - Find OPP with highest frequency for
* target voltage.
* @dev: Device for which we do this operation.
* @u_volt: Target voltage.
*
* Search for OPP with highest (ceil) frequency and has voltage <= u_volt.
*
* Return: matching *opp, else returns ERR_PTR in case of error which should be
* handled using IS_ERR.
*
* Error return values can be:
* EINVAL: bad parameters
*
* The callers are required to call dev_pm_opp_put() for the returned OPP after
* use.
*/
struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev,
unsigned long u_volt)
{
struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
if (!dev || !u_volt) {
dev_err(dev, "%s: Invalid argument volt=%lu\n", __func__,
u_volt);
return ERR_PTR(-EINVAL);
}
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
return ERR_CAST(opp_table);
mutex_lock(&opp_table->lock);
list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available) {
if (temp_opp->supplies[0].u_volt > u_volt)
break;
opp = temp_opp;
}
}
/* Increment the reference count of OPP */
if (!IS_ERR(opp))
dev_pm_opp_get(opp);
mutex_unlock(&opp_table->lock);
dev_pm_opp_put_opp_table(opp_table);
return opp;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil_by_volt);
static int _set_opp_voltage(struct device *dev, struct regulator *reg, static int _set_opp_voltage(struct device *dev, struct regulator *reg,
struct dev_pm_opp_supply *supply) struct dev_pm_opp_supply *supply)
{ {
......
...@@ -178,6 +178,11 @@ static inline struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) ...@@ -178,6 +178,11 @@ static inline struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
static inline void cpufreq_cpu_put(struct cpufreq_policy *policy) { } static inline void cpufreq_cpu_put(struct cpufreq_policy *policy) { }
#endif #endif
static inline bool policy_is_inactive(struct cpufreq_policy *policy)
{
return cpumask_empty(policy->cpus);
}
static inline bool policy_is_shared(struct cpufreq_policy *policy) static inline bool policy_is_shared(struct cpufreq_policy *policy)
{ {
return cpumask_weight(policy->cpus) > 1; return cpumask_weight(policy->cpus) > 1;
...@@ -193,8 +198,14 @@ unsigned int cpufreq_quick_get_max(unsigned int cpu); ...@@ -193,8 +198,14 @@ unsigned int cpufreq_quick_get_max(unsigned int cpu);
void disable_cpufreq(void); void disable_cpufreq(void);
u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy);
struct cpufreq_policy *cpufreq_cpu_acquire(unsigned int cpu);
void cpufreq_cpu_release(struct cpufreq_policy *policy);
int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu); int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu);
int cpufreq_set_policy(struct cpufreq_policy *policy,
struct cpufreq_policy *new_policy);
void cpufreq_update_policy(unsigned int cpu); void cpufreq_update_policy(unsigned int cpu);
void cpufreq_update_limits(unsigned int cpu);
bool have_governor_per_policy(void); bool have_governor_per_policy(void);
struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy); struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy);
void cpufreq_enable_fast_switch(struct cpufreq_policy *policy); void cpufreq_enable_fast_switch(struct cpufreq_policy *policy);
...@@ -322,6 +333,9 @@ struct cpufreq_driver { ...@@ -322,6 +333,9 @@ struct cpufreq_driver {
/* should be defined, if possible */ /* should be defined, if possible */
unsigned int (*get)(unsigned int cpu); unsigned int (*get)(unsigned int cpu);
/* Called to update policy limits on firmware notifications. */
void (*update_limits)(unsigned int cpu);
/* optional */ /* optional */
int (*bios_limit)(int cpu, unsigned int *limit); int (*bios_limit)(int cpu, unsigned int *limit);
......
...@@ -147,6 +147,7 @@ enum cpuhp_state { ...@@ -147,6 +147,7 @@ enum cpuhp_state {
CPUHP_AP_X86_VDSO_VMA_ONLINE, CPUHP_AP_X86_VDSO_VMA_ONLINE,
CPUHP_AP_IRQ_AFFINITY_ONLINE, CPUHP_AP_IRQ_AFFINITY_ONLINE,
CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS, CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS,
CPUHP_AP_X86_INTEL_EPB_ONLINE,
CPUHP_AP_PERF_ONLINE, CPUHP_AP_PERF_ONLINE,
CPUHP_AP_PERF_X86_ONLINE, CPUHP_AP_PERF_X86_ONLINE,
CPUHP_AP_PERF_X86_UNCORE_ONLINE, CPUHP_AP_PERF_X86_UNCORE_ONLINE,
......
...@@ -83,6 +83,7 @@ struct cpuidle_device { ...@@ -83,6 +83,7 @@ struct cpuidle_device {
unsigned int use_deepest_state:1; unsigned int use_deepest_state:1;
unsigned int poll_time_limit:1; unsigned int poll_time_limit:1;
unsigned int cpu; unsigned int cpu;
ktime_t next_hrtimer;
int last_residency; int last_residency;
struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX]; struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX];
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/cpumask.h>
/* /*
* Flags to control the behaviour of a genpd. * Flags to control the behaviour of a genpd.
...@@ -42,11 +43,22 @@ ...@@ -42,11 +43,22 @@
* GENPD_FLAG_ACTIVE_WAKEUP: Instructs genpd to keep the PM domain powered * GENPD_FLAG_ACTIVE_WAKEUP: Instructs genpd to keep the PM domain powered
* on, in case any of its attached devices is used * on, in case any of its attached devices is used
* in the wakeup path to serve system wakeups. * in the wakeup path to serve system wakeups.
*
* GENPD_FLAG_CPU_DOMAIN: Instructs genpd that it should expect to get
* devices attached, which may belong to CPUs or
* possibly have subdomains with CPUs attached.
* This flag enables the genpd backend driver to
* deploy idle power management support for CPUs
* and groups of CPUs. Note that, the backend
* driver must then comply with the so called,
* last-man-standing algorithm, for the CPUs in the
* PM domain.
*/ */
#define GENPD_FLAG_PM_CLK (1U << 0) #define GENPD_FLAG_PM_CLK (1U << 0)
#define GENPD_FLAG_IRQ_SAFE (1U << 1) #define GENPD_FLAG_IRQ_SAFE (1U << 1)
#define GENPD_FLAG_ALWAYS_ON (1U << 2) #define GENPD_FLAG_ALWAYS_ON (1U << 2)
#define GENPD_FLAG_ACTIVE_WAKEUP (1U << 3) #define GENPD_FLAG_ACTIVE_WAKEUP (1U << 3)
#define GENPD_FLAG_CPU_DOMAIN (1U << 4)
enum gpd_status { enum gpd_status {
GPD_STATE_ACTIVE = 0, /* PM domain is active */ GPD_STATE_ACTIVE = 0, /* PM domain is active */
...@@ -69,6 +81,7 @@ struct genpd_power_state { ...@@ -69,6 +81,7 @@ struct genpd_power_state {
s64 residency_ns; s64 residency_ns;
struct fwnode_handle *fwnode; struct fwnode_handle *fwnode;
ktime_t idle_time; ktime_t idle_time;
void *data;
}; };
struct genpd_lock_ops; struct genpd_lock_ops;
...@@ -93,6 +106,7 @@ struct generic_pm_domain { ...@@ -93,6 +106,7 @@ struct generic_pm_domain {
unsigned int suspended_count; /* System suspend device counter */ unsigned int suspended_count; /* System suspend device counter */
unsigned int prepared_count; /* Suspend counter of prepared devices */ unsigned int prepared_count; /* Suspend counter of prepared devices */
unsigned int performance_state; /* Aggregated max performance state */ unsigned int performance_state; /* Aggregated max performance state */
cpumask_var_t cpus; /* A cpumask of the attached CPUs */
int (*power_off)(struct generic_pm_domain *domain); int (*power_off)(struct generic_pm_domain *domain);
int (*power_on)(struct generic_pm_domain *domain); int (*power_on)(struct generic_pm_domain *domain);
struct opp_table *opp_table; /* OPP table of the genpd */ struct opp_table *opp_table; /* OPP table of the genpd */
...@@ -104,15 +118,17 @@ struct generic_pm_domain { ...@@ -104,15 +118,17 @@ struct generic_pm_domain {
s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ s64 max_off_time_ns; /* Maximum allowed "suspended" time. */
bool max_off_time_changed; bool max_off_time_changed;
bool cached_power_down_ok; bool cached_power_down_ok;
bool cached_power_down_state_idx;
int (*attach_dev)(struct generic_pm_domain *domain, int (*attach_dev)(struct generic_pm_domain *domain,
struct device *dev); struct device *dev);
void (*detach_dev)(struct generic_pm_domain *domain, void (*detach_dev)(struct generic_pm_domain *domain,
struct device *dev); struct device *dev);
unsigned int flags; /* Bit field of configs for genpd */ unsigned int flags; /* Bit field of configs for genpd */
struct genpd_power_state *states; struct genpd_power_state *states;
void (*free_states)(struct genpd_power_state *states,
unsigned int state_count);
unsigned int state_count; /* number of states */ unsigned int state_count; /* number of states */
unsigned int state_idx; /* state that genpd will go to when off */ unsigned int state_idx; /* state that genpd will go to when off */
void *free; /* Free the state that was allocated for default */
ktime_t on_time; ktime_t on_time;
ktime_t accounting_time; ktime_t accounting_time;
const struct genpd_lock_ops *lock_ops; const struct genpd_lock_ops *lock_ops;
...@@ -159,6 +175,7 @@ struct generic_pm_domain_data { ...@@ -159,6 +175,7 @@ struct generic_pm_domain_data {
struct pm_domain_data base; struct pm_domain_data base;
struct gpd_timing_data td; struct gpd_timing_data td;
struct notifier_block nb; struct notifier_block nb;
int cpu;
unsigned int performance_state; unsigned int performance_state;
void *data; void *data;
}; };
...@@ -187,6 +204,9 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state); ...@@ -187,6 +204,9 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state);
extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov; extern struct dev_power_governor pm_domain_always_on_gov;
#ifdef CONFIG_CPU_IDLE
extern struct dev_power_governor pm_domain_cpu_gov;
#endif
#else #else
static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
......
...@@ -102,6 +102,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, ...@@ -102,6 +102,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
unsigned long *freq); unsigned long *freq);
struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev,
unsigned long u_volt);
struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
unsigned long *freq); unsigned long *freq);
...@@ -207,6 +209,12 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, ...@@ -207,6 +209,12 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
return ERR_PTR(-ENOTSUPP); return ERR_PTR(-ENOTSUPP);
} }
static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev,
unsigned long u_volt)
{
return ERR_PTR(-ENOTSUPP);
}
static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
unsigned long *freq) unsigned long *freq)
{ {
......
...@@ -425,6 +425,7 @@ void restore_processor_state(void); ...@@ -425,6 +425,7 @@ void restore_processor_state(void);
/* kernel/power/main.c */ /* kernel/power/main.c */
extern int register_pm_notifier(struct notifier_block *nb); extern int register_pm_notifier(struct notifier_block *nb);
extern int unregister_pm_notifier(struct notifier_block *nb); extern int unregister_pm_notifier(struct notifier_block *nb);
extern void ksys_sync_helper(void);
#define pm_notifier(fn, pri) { \ #define pm_notifier(fn, pri) { \
static struct notifier_block fn##_nb = \ static struct notifier_block fn##_nb = \
...@@ -462,6 +463,8 @@ static inline int unregister_pm_notifier(struct notifier_block *nb) ...@@ -462,6 +463,8 @@ static inline int unregister_pm_notifier(struct notifier_block *nb)
return 0; return 0;
} }
static inline void ksys_sync_helper(void) {}
#define pm_notifier(fn, pri) do { (void)(fn); } while (0) #define pm_notifier(fn, pri) do { (void)(fn); } while (0)
static inline bool pm_wakeup_pending(void) { return false; } static inline bool pm_wakeup_pending(void) { return false; }
......
...@@ -128,6 +128,7 @@ extern void tick_nohz_idle_enter(void); ...@@ -128,6 +128,7 @@ extern void tick_nohz_idle_enter(void);
extern void tick_nohz_idle_exit(void); extern void tick_nohz_idle_exit(void);
extern void tick_nohz_irq_exit(void); extern void tick_nohz_irq_exit(void);
extern bool tick_nohz_idle_got_tick(void); extern bool tick_nohz_idle_got_tick(void);
extern ktime_t tick_nohz_get_next_hrtimer(void);
extern ktime_t tick_nohz_get_sleep_length(ktime_t *delta_next); extern ktime_t tick_nohz_get_sleep_length(ktime_t *delta_next);
extern unsigned long tick_nohz_get_idle_calls(void); extern unsigned long tick_nohz_get_idle_calls(void);
extern unsigned long tick_nohz_get_idle_calls_cpu(int cpu); extern unsigned long tick_nohz_get_idle_calls_cpu(int cpu);
...@@ -151,7 +152,11 @@ static inline void tick_nohz_idle_restart_tick(void) { } ...@@ -151,7 +152,11 @@ static inline void tick_nohz_idle_restart_tick(void) { }
static inline void tick_nohz_idle_enter(void) { } static inline void tick_nohz_idle_enter(void) { }
static inline void tick_nohz_idle_exit(void) { } static inline void tick_nohz_idle_exit(void) { }
static inline bool tick_nohz_idle_got_tick(void) { return false; } static inline bool tick_nohz_idle_got_tick(void) { return false; }
static inline ktime_t tick_nohz_get_next_hrtimer(void)
{
/* Next wake up is the tick period, assume it starts now */
return ktime_add(ktime_get(), TICK_NSEC);
}
static inline ktime_t tick_nohz_get_sleep_length(ktime_t *delta_next) static inline ktime_t tick_nohz_get_sleep_length(ktime_t *delta_next)
{ {
*delta_next = TICK_NSEC; *delta_next = TICK_NSEC;
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Rockchip General Register Files definitions
*
* Copyright (c) 2018, Collabora Ltd.
* Author: Enric Balletbo i Serra <enric.balletbo@collabora.com>
*/
#ifndef __SOC_RK3399_GRF_H
#define __SOC_RK3399_GRF_H
/* PMU GRF Registers */
#define RK3399_PMUGRF_OS_REG2 0x308
#define RK3399_PMUGRF_DDRTYPE_SHIFT 13
#define RK3399_PMUGRF_DDRTYPE_MASK 7
#define RK3399_PMUGRF_DDRTYPE_DDR3 3
#define RK3399_PMUGRF_DDRTYPE_LPDDR2 5
#define RK3399_PMUGRF_DDRTYPE_LPDDR3 6
#define RK3399_PMUGRF_DDRTYPE_LPDDR4 7
#endif
...@@ -23,5 +23,6 @@ ...@@ -23,5 +23,6 @@
#define ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE 0x05 #define ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE 0x05
#define ROCKCHIP_SIP_CONFIG_DRAM_CLR_IRQ 0x06 #define ROCKCHIP_SIP_CONFIG_DRAM_CLR_IRQ 0x06
#define ROCKCHIP_SIP_CONFIG_DRAM_SET_PARAM 0x07 #define ROCKCHIP_SIP_CONFIG_DRAM_SET_PARAM 0x07
#define ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD 0x08
#endif #endif
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM devfreq
#if !defined(_TRACE_DEVFREQ_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_DEVFREQ_H
#include <linux/devfreq.h>
#include <linux/tracepoint.h>
TRACE_EVENT(devfreq_monitor,
TP_PROTO(struct devfreq *devfreq),
TP_ARGS(devfreq),
TP_STRUCT__entry(
__field(unsigned long, freq)
__field(unsigned long, busy_time)
__field(unsigned long, total_time)
__field(unsigned int, polling_ms)
__string(dev_name, dev_name(&devfreq->dev))
),
TP_fast_assign(
__entry->freq = devfreq->previous_freq;
__entry->busy_time = devfreq->last_status.busy_time;
__entry->total_time = devfreq->last_status.total_time;
__entry->polling_ms = devfreq->profile->polling_ms;
__assign_str(dev_name, dev_name(&devfreq->dev));
),
TP_printk("dev_name=%s freq=%lu polling_ms=%u load=%lu",
__get_str(dev_name), __entry->freq, __entry->polling_ms,
__entry->total_time == 0 ? 0 :
(100 * __entry->busy_time) / __entry->total_time)
);
#endif /* _TRACE_DEVFREQ_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
...@@ -49,8 +49,11 @@ ...@@ -49,8 +49,11 @@
#define PSCI_1_0_FN_PSCI_FEATURES PSCI_0_2_FN(10) #define PSCI_1_0_FN_PSCI_FEATURES PSCI_0_2_FN(10)
#define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14) #define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14)
#define PSCI_1_0_FN_SET_SUSPEND_MODE PSCI_0_2_FN(15)
#define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18)
#define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14) #define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14)
#define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18)
/* PSCI v0.2 power state encoding for CPU_SUSPEND function */ /* PSCI v0.2 power state encoding for CPU_SUSPEND function */
#define PSCI_0_2_POWER_STATE_ID_MASK 0xffff #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff
...@@ -97,6 +100,10 @@ ...@@ -97,6 +100,10 @@
#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK \ #define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK \
(0x1 << PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT) (0x1 << PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT)
#define PSCI_1_0_OS_INITIATED BIT(0)
#define PSCI_1_0_SUSPEND_MODE_PC 0
#define PSCI_1_0_SUSPEND_MODE_OSI 1
/* PSCI return values (inclusive of all PSCI versions) */ /* PSCI return values (inclusive of all PSCI versions) */
#define PSCI_RET_SUCCESS 0 #define PSCI_RET_SUCCESS 0
#define PSCI_RET_NOT_SUPPORTED -1 #define PSCI_RET_NOT_SUPPORTED -1
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/syscalls.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -709,9 +708,7 @@ int hibernate(void) ...@@ -709,9 +708,7 @@ int hibernate(void)
goto Exit; goto Exit;
} }
pr_info("Syncing filesystems ... \n"); ksys_sync_helper();
ksys_sync();
pr_info("done.\n");
error = freeze_processes(); error = freeze_processes();
if (error) if (error)
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/syscalls.h>
#include "power.h" #include "power.h"
...@@ -51,6 +52,19 @@ void unlock_system_sleep(void) ...@@ -51,6 +52,19 @@ void unlock_system_sleep(void)
} }
EXPORT_SYMBOL_GPL(unlock_system_sleep); EXPORT_SYMBOL_GPL(unlock_system_sleep);
void ksys_sync_helper(void)
{
ktime_t start;
long elapsed_msecs;
start = ktime_get();
ksys_sync();
elapsed_msecs = ktime_to_ms(ktime_sub(ktime_get(), start));
pr_info("Filesystems sync: %ld.%03ld seconds\n",
elapsed_msecs / MSEC_PER_SEC, elapsed_msecs % MSEC_PER_SEC);
}
EXPORT_SYMBOL_GPL(ksys_sync_helper);
/* Routines for PM-transition notifications */ /* Routines for PM-transition notifications */
static BLOCKING_NOTIFIER_HEAD(pm_chain_head); static BLOCKING_NOTIFIER_HEAD(pm_chain_head);
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include <linux/console.h> #include <linux/console.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/syscalls.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -568,13 +567,11 @@ static int enter_state(suspend_state_t state) ...@@ -568,13 +567,11 @@ static int enter_state(suspend_state_t state)
if (state == PM_SUSPEND_TO_IDLE) if (state == PM_SUSPEND_TO_IDLE)
s2idle_begin(); s2idle_begin();
#ifndef CONFIG_SUSPEND_SKIP_SYNC if (!IS_ENABLED(CONFIG_SUSPEND_SKIP_SYNC)) {
trace_suspend_resume(TPS("sync_filesystems"), 0, true); trace_suspend_resume(TPS("sync_filesystems"), 0, true);
pr_info("Syncing filesystems ... "); ksys_sync_helper();
ksys_sync(); trace_suspend_resume(TPS("sync_filesystems"), 0, false);
pr_cont("done.\n"); }
trace_suspend_resume(TPS("sync_filesystems"), 0, false);
#endif
pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]); pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
pm_suspend_clear_flags(); pm_suspend_clear_flags();
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
*/ */
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/syscalls.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -228,9 +227,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, ...@@ -228,9 +227,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
if (data->frozen) if (data->frozen)
break; break;
printk("Syncing filesystems ... "); ksys_sync_helper();
ksys_sync();
printk("done.\n");
error = freeze_processes(); error = freeze_processes();
if (error) if (error)
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include <linux/sched/cpufreq.h> #include <linux/sched/cpufreq.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#define IOWAIT_BOOST_MIN (SCHED_CAPACITY_SCALE / 8)
struct sugov_tunables { struct sugov_tunables {
struct gov_attr_set attr_set; struct gov_attr_set attr_set;
unsigned int rate_limit_us; unsigned int rate_limit_us;
...@@ -51,7 +53,6 @@ struct sugov_cpu { ...@@ -51,7 +53,6 @@ struct sugov_cpu {
u64 last_update; u64 last_update;
unsigned long bw_dl; unsigned long bw_dl;
unsigned long min;
unsigned long max; unsigned long max;
/* The field below is for single-CPU policies only: */ /* The field below is for single-CPU policies only: */
...@@ -291,8 +292,8 @@ static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) ...@@ -291,8 +292,8 @@ static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu)
* *
* The IO wait boost of a task is disabled after a tick since the last update * The IO wait boost of a task is disabled after a tick since the last update
* of a CPU. If a new IO wait boost is requested after more then a tick, then * of a CPU. If a new IO wait boost is requested after more then a tick, then
* we enable the boost starting from the minimum frequency, which improves * we enable the boost starting from IOWAIT_BOOST_MIN, which improves energy
* energy efficiency by ignoring sporadic wakeups from IO. * efficiency by ignoring sporadic wakeups from IO.
*/ */
static bool sugov_iowait_reset(struct sugov_cpu *sg_cpu, u64 time, static bool sugov_iowait_reset(struct sugov_cpu *sg_cpu, u64 time,
bool set_iowait_boost) bool set_iowait_boost)
...@@ -303,7 +304,7 @@ static bool sugov_iowait_reset(struct sugov_cpu *sg_cpu, u64 time, ...@@ -303,7 +304,7 @@ static bool sugov_iowait_reset(struct sugov_cpu *sg_cpu, u64 time,
if (delta_ns <= TICK_NSEC) if (delta_ns <= TICK_NSEC)
return false; return false;
sg_cpu->iowait_boost = set_iowait_boost ? sg_cpu->min : 0; sg_cpu->iowait_boost = set_iowait_boost ? IOWAIT_BOOST_MIN : 0;
sg_cpu->iowait_boost_pending = set_iowait_boost; sg_cpu->iowait_boost_pending = set_iowait_boost;
return true; return true;
...@@ -317,8 +318,9 @@ static bool sugov_iowait_reset(struct sugov_cpu *sg_cpu, u64 time, ...@@ -317,8 +318,9 @@ static bool sugov_iowait_reset(struct sugov_cpu *sg_cpu, u64 time,
* *
* Each time a task wakes up after an IO operation, the CPU utilization can be * Each time a task wakes up after an IO operation, the CPU utilization can be
* boosted to a certain utilization which doubles at each "frequent and * boosted to a certain utilization which doubles at each "frequent and
* successive" wakeup from IO, ranging from the utilization of the minimum * successive" wakeup from IO, ranging from IOWAIT_BOOST_MIN to the utilization
* OPP to the utilization of the maximum OPP. * of the maximum OPP.
*
* To keep doubling, an IO boost has to be requested at least once per tick, * To keep doubling, an IO boost has to be requested at least once per tick,
* otherwise we restart from the utilization of the minimum OPP. * otherwise we restart from the utilization of the minimum OPP.
*/ */
...@@ -349,7 +351,7 @@ static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, u64 time, ...@@ -349,7 +351,7 @@ static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, u64 time,
} }
/* First wakeup after IO: start with minimum boost */ /* First wakeup after IO: start with minimum boost */
sg_cpu->iowait_boost = sg_cpu->min; sg_cpu->iowait_boost = IOWAIT_BOOST_MIN;
} }
/** /**
...@@ -389,7 +391,7 @@ static unsigned long sugov_iowait_apply(struct sugov_cpu *sg_cpu, u64 time, ...@@ -389,7 +391,7 @@ static unsigned long sugov_iowait_apply(struct sugov_cpu *sg_cpu, u64 time,
* No boost pending; reduce the boost value. * No boost pending; reduce the boost value.
*/ */
sg_cpu->iowait_boost >>= 1; sg_cpu->iowait_boost >>= 1;
if (sg_cpu->iowait_boost < sg_cpu->min) { if (sg_cpu->iowait_boost < IOWAIT_BOOST_MIN) {
sg_cpu->iowait_boost = 0; sg_cpu->iowait_boost = 0;
return util; return util;
} }
...@@ -827,9 +829,6 @@ static int sugov_start(struct cpufreq_policy *policy) ...@@ -827,9 +829,6 @@ static int sugov_start(struct cpufreq_policy *policy)
memset(sg_cpu, 0, sizeof(*sg_cpu)); memset(sg_cpu, 0, sizeof(*sg_cpu));
sg_cpu->cpu = cpu; sg_cpu->cpu = cpu;
sg_cpu->sg_policy = sg_policy; sg_cpu->sg_policy = sg_policy;
sg_cpu->min =
(SCHED_CAPACITY_SCALE * policy->cpuinfo.min_freq) /
policy->cpuinfo.max_freq;
} }
for_each_cpu(cpu, policy->cpus) { for_each_cpu(cpu, policy->cpus) {
......
...@@ -1037,6 +1037,18 @@ bool tick_nohz_idle_got_tick(void) ...@@ -1037,6 +1037,18 @@ bool tick_nohz_idle_got_tick(void)
return false; return false;
} }
/**
* tick_nohz_get_next_hrtimer - return the next expiration time for the hrtimer
* or the tick, whatever that expires first. Note that, if the tick has been
* stopped, it returns the next hrtimer.
*
* Called from power state control code with interrupts disabled
*/
ktime_t tick_nohz_get_next_hrtimer(void)
{
return __this_cpu_read(tick_cpu_device.evtdev)->next_event;
}
/** /**
* tick_nohz_get_sleep_length - return the expected length of the current sleep * tick_nohz_get_sleep_length - return the expected length of the current sleep
* @delta_next: duration until the next event if the tick cannot be stopped * @delta_next: duration until the next event if the tick cannot be stopped
......
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