Commit 7f3fdd40 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull power management updates from Rafael Wysocki:
 "This includes some infrastructure changes in the PM core, mostly
  related to integration between runtime PM and system-wide suspend and
  hibernation, plus some driver changes depending on them and fixes for
  issues in that area which have become quite apparent recently.

  Also included are changes making more x86-based systems use the Low
  Power Sleep S0 _DSM interface by default, which turned out to be
  necessary to handle power button wakeups from suspend-to-idle on
  Surface Pro3.

  On the cpufreq front we have fixes and cleanups in the core, some new
  hardware support, driver updates and the removal of some unused code
  from the CPU cooling thermal driver.

  Apart from this, the Operating Performance Points (OPP) framework is
  prepared to be used with power domains in the future and there is a
  usual bunch of assorted fixes and cleanups.

  Specifics:

   - Define a PM driver flag allowing drivers to request that their
     devices be left in suspend after system-wide transitions to the
     working state if possible and add support for it to the PCI bus
     type and the ACPI PM domain (Rafael Wysocki).

   - Make the PM core carry out optimizations for devices with driver PM
     flags set in some cases and make a few drivers set those flags
     (Rafael Wysocki).

   - Fix and clean up wrapper routines allowing runtime PM device
     callbacks to be re-used for system-wide PM, change the generic
     power domains (genpd) framework to stop using those routines
     incorrectly and fix up a driver depending on that behavior of genpd
     (Rafael Wysocki, Ulf Hansson, Geert Uytterhoeven).

   - Fix and clean up the PM core's device wakeup framework and
     re-factor system-wide PM core code related to device wakeup
     (Rafael Wysocki, Ulf Hansson, Brian Norris).

   - Make more x86-based systems use the Low Power Sleep S0 _DSM
     interface by default (to fix power button wakeup from
     suspend-to-idle on Surface Pro3) and add a kernel command line
     switch to tell it to ignore the system sleep blacklist in the ACPI
     core (Rafael Wysocki).

   - Fix a race condition related to cpufreq governor module removal and
     clean up the governor management code in the cpufreq core (Rafael
     Wysocki).

   - Drop the unused generic code related to the handling of the static
     power energy usage model in the CPU cooling thermal driver along
     with the corresponding documentation (Viresh Kumar).

   - Add mt2712 support to the Mediatek cpufreq driver (Andrew-sh
     Cheng).

   - Add a new operating point to the imx6ul and imx6q cpufreq drivers
     and switch the latter to using clk_bulk_get() (Anson Huang, Dong
     Aisheng).

   - Add support for multiple regulators to the TI cpufreq driver along
     with a new DT binding related to that and clean up that driver
     somewhat (Dave Gerlach).

   - Fix a powernv cpufreq driver regression leading to incorrect CPU
     frequency reporting, fix that driver to deal with non-continguous
     P-states correctly and clean it up (Gautham Shenoy, Shilpasri
     Bhat).

   - Add support for frequency scaling on Armada 37xx SoCs through the
     generic DT cpufreq driver (Gregory CLEMENT).

   - Fix error code paths in the mvebu cpufreq driver (Gregory CLEMENT).

   - Fix a transition delay setting regression in the longhaul cpufreq
     driver (Viresh Kumar).

   - Add Skylake X (server) support to the intel_pstate cpufreq driver
     and clean up that driver somewhat (Srinivas Pandruvada).

   - Clean up the cpufreq statistics collection code (Viresh Kumar).

   - Drop cluster terminology and dependency on physical_package_id from
     the PSCI driver and drop dependency on arm_big_little from the SCPI
     cpufreq driver (Sudeep Holla).

   - Add support for system-wide suspend and resume to the RAPL power
     capping driver and drop a redundant semicolon from it (Zhen Han,
     Luis de Bethencourt).

   - Make SPI domain validation (in the SCSI SPI transport driver) and
     system-wide suspend mutually exclusive as they rely on the same
     underlying mechanism and cannot be carried out at the same time
     (Bart Van Assche).

   - Fix the computation of the amount of memory to preallocate in the
     hibernation core and clean up one function in there (Rainer Fiebig,
     Kyungsik Lee).

   - Prepare the Operating Performance Points (OPP) framework for being
     used with power domains and clean up one function in it (Viresh
     Kumar, Wei Yongjun).

   - Clean up the generic sysfs interface for device PM (Andy
     Shevchenko).

   - Fix several minor issues in power management frameworks and clean
     them up a bit (Arvind Yadav, Bjorn Andersson, Geert Uytterhoeven,
     Gustavo Silva, Julia Lawall, Luis de Bethencourt, Paul Gortmaker,
     Sergey Senozhatsky, gaurav jindal).

   - Make it easier to disable PM via Kconfig (Mark Brown).

   - Clean up the cpupower and intel_pstate_tracer utilities (Doug
     Smythies, Laura Abbott)"

* tag 'pm-4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (89 commits)
  PCI / PM: Remove spurious semicolon
  cpufreq: scpi: remove arm_big_little dependency
  drivers: psci: remove cluster terminology and dependency on physical_package_id
  powercap: intel_rapl: Fix trailing semicolon
  dmaengine: rcar-dmac: Make DMAC reinit during system resume explicit
  PM / runtime: Allow no callbacks in pm_runtime_force_suspend|resume()
  PM / hibernate: Drop unused parameter of enough_swap
  PM / runtime: Check ignore_children in pm_runtime_need_not_resume()
  PM / runtime: Rework pm_runtime_force_suspend/resume()
  PM / genpd: Stop/start devices without pm_runtime_force_suspend/resume()
  cpufreq: powernv: Dont assume distinct pstate values for nominal and pmin
  cpufreq: intel_pstate: Add Skylake servers support
  cpufreq: intel_pstate: Replace bxt_funcs with core_funcs
  platform/x86: surfacepro3: Support for wakeup from suspend-to-idle
  ACPI / PM: Use Low Power S0 Idle on more systems
  PM / wakeup: Print warn if device gets enabled as wakeup source during sleep
  PM / domains: Don't skip driver's ->suspend|resume_noirq() callbacks
  PM / core: Propagate wakeup_path status flag in __device_suspend_late()
  PM / core: Re-structure code for clearing the direct_complete flag
  powercap: add suspend and resume mechanism for SOC power limit
  ...
parents 1c1f395b ee43730d
...@@ -223,7 +223,7 @@ ...@@ -223,7 +223,7 @@
acpi_sleep= [HW,ACPI] Sleep options acpi_sleep= [HW,ACPI] Sleep options
Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
old_ordering, nonvs, sci_force_enable } old_ordering, nonvs, sci_force_enable, nobl }
See Documentation/power/video.txt for information on See Documentation/power/video.txt for information on
s3_bios and s3_mode. s3_bios and s3_mode.
s3_beep is for debugging; it makes the PC's speaker beep s3_beep is for debugging; it makes the PC's speaker beep
...@@ -239,6 +239,9 @@ ...@@ -239,6 +239,9 @@
sci_force_enable causes the kernel to set SCI_EN directly sci_force_enable causes the kernel to set SCI_EN directly
on resume from S1/S3 (which is against the ACPI spec, on resume from S1/S3 (which is against the ACPI spec,
but some broken systems don't work without it). but some broken systems don't work without it).
nobl causes the internal blacklist of systems known to
behave incorrectly in some ways with respect to system
suspend and resume to be ignored (use wisely).
acpi_use_timer_override [HW,ACPI] acpi_use_timer_override [HW,ACPI]
Use timer override. For some broken Nvidia NF5 boards Use timer override. For some broken Nvidia NF5 boards
......
...@@ -14,3 +14,22 @@ following property before the previous one: ...@@ -14,3 +14,22 @@ following property before the previous one:
Example: Example:
compatible = "marvell,armada-3720-db", "marvell,armada3720", "marvell,armada3710"; compatible = "marvell,armada-3720-db", "marvell,armada3720", "marvell,armada3710";
Power management
----------------
For power management (particularly DVFS and AVS), the North Bridge
Power Management component is needed:
Required properties:
- compatible : should contain "marvell,armada-3700-nb-pm", "syscon";
- reg : the register start and length for the North Bridge
Power Management
Example:
nb_pm: syscon@14000 {
compatible = "marvell,armada-3700-nb-pm", "syscon";
reg = <0x14000 0x60>;
}
...@@ -45,6 +45,11 @@ Devices supporting OPPs must set their "operating-points-v2" property with ...@@ -45,6 +45,11 @@ Devices supporting OPPs must set their "operating-points-v2" property with
phandle to a OPP table in their DT node. The OPP core will use this phandle to phandle to a OPP table in their DT node. The OPP core will use this phandle to
find the operating points for the device. find the operating points for the device.
This can contain more than one phandle for power domain providers that provide
multiple power domains. That is, one phandle for each power domain. If only one
phandle is available, then the same OPP table will be used for all power domains
provided by the power domain provider.
If required, this can be extended for SoC vendor specific bindings. Such bindings If required, this can be extended for SoC vendor specific bindings. Such bindings
should be documented as Documentation/devicetree/bindings/power/<vendor>-opp.txt should be documented as Documentation/devicetree/bindings/power/<vendor>-opp.txt
and should have a compatible description like: "operating-points-v2-<vendor>". and should have a compatible description like: "operating-points-v2-<vendor>".
...@@ -154,6 +159,14 @@ Optional properties: ...@@ -154,6 +159,14 @@ Optional properties:
- status: Marks the node enabled/disabled. - status: Marks the node enabled/disabled.
- required-opp: This contains phandle to an OPP node in another device's OPP
table. It may contain an array of phandles, where each phandle points to an
OPP of a different device. It should not contain multiple phandles to the OPP
nodes in the same OPP table. This specifies the minimum required OPP of the
device(s), whose OPP's phandle is present in this property, for the
functioning of the current device at the current OPP (where this property is
present).
Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together. Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
/ { / {
......
Texas Instruments OMAP compatible OPP supply description
OMAP5, DRA7, and AM57 family of SoCs have Class0 AVS eFuse registers which
contain data that can be used to adjust voltages programmed for some of their
supplies for more efficient operation. This binding provides the information
needed to read these values and use them to program the main regulator during
an OPP transitions.
Also, some supplies may have an associated vbb-supply which is an Adaptive Body
Bias regulator which much be transitioned in a specific sequence with regards
to the vdd-supply and clk when making an OPP transition. By supplying two
regulators to the device that will undergo OPP transitions we can make use
of the multi regulator binding that is part of the OPP core described here [1]
to describe both regulators needed by the platform.
[1] Documentation/devicetree/bindings/opp/opp.txt
Required Properties for Device Node:
- vdd-supply: phandle to regulator controlling VDD supply
- vbb-supply: phandle to regulator controlling Body Bias supply
(Usually Adaptive Body Bias regulator)
Required Properties for opp-supply node:
- compatible: Should be one of:
"ti,omap-opp-supply" - basic OPP supply controlling VDD and VBB
"ti,omap5-opp-supply" - OMAP5+ optimized voltages in efuse(class0)VDD
along with VBB
"ti,omap5-core-opp-supply" - OMAP5+ optimized voltages in efuse(class0) VDD
but no VBB.
- reg: Address and length of the efuse register set for the device (mandatory
only for "ti,omap5-opp-supply")
- ti,efuse-settings: An array of u32 tuple items providing information about
optimized efuse configuration. Each item consists of the following:
volt: voltage in uV - reference voltage (OPP voltage)
efuse_offseet: efuse offset from reg where the optimized voltage is stored.
- ti,absolute-max-voltage-uv: absolute maximum voltage for the OPP supply.
Example:
/* Device Node (CPU) */
cpus {
cpu0: cpu@0 {
device_type = "cpu";
...
vdd-supply = <&vcc>;
vbb-supply = <&abb_mpu>;
};
};
/* OMAP OPP Supply with Class0 registers */
opp_supply_mpu: opp_supply@4a003b20 {
compatible = "ti,omap5-opp-supply";
reg = <0x4a003b20 0x8>;
ti,efuse-settings = <
/* uV offset */
1060000 0x0
1160000 0x4
1210000 0x8
>;
ti,absolute-max-voltage-uv = <1500000>;
};
...@@ -40,6 +40,12 @@ Optional properties: ...@@ -40,6 +40,12 @@ Optional properties:
domain's idle states. In the absence of this property, the domain would be domain's idle states. In the absence of this property, the domain would be
considered as capable of being powered-on or powered-off. considered as capable of being powered-on or powered-off.
- operating-points-v2 : Phandles to the OPP tables of power domains provided by
a power domain provider. If the provider provides a single power domain only
or all the power domains provided by the provider have identical OPP tables,
then this shall contain a single phandle. Refer to ../opp/opp.txt for more
information.
Example: Example:
power: power-controller@12340000 { power: power-controller@12340000 {
...@@ -120,4 +126,63 @@ The node above defines a typical PM domain consumer device, which is located ...@@ -120,4 +126,63 @@ The node above defines a typical PM domain consumer device, which is located
inside a PM domain with index 0 of a power controller represented by a node inside a PM domain with index 0 of a power controller represented by a node
with the label "power". with the label "power".
Optional properties:
- required-opp: This contains phandle to an OPP node in another device's OPP
table. It may contain an array of phandles, where each phandle points to an
OPP of a different device. It should not contain multiple phandles to the OPP
nodes in the same OPP table. This specifies the minimum required OPP of the
device(s), whose OPP's phandle is present in this property, for the
functioning of the current device at the current OPP (where this property is
present).
Example:
- OPP table for domain provider that provides two domains.
domain0_opp_table: opp-table0 {
compatible = "operating-points-v2";
domain0_opp_0: opp-1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <975000 970000 985000>;
};
domain0_opp_1: opp-1100000000 {
opp-hz = /bits/ 64 <1100000000>;
opp-microvolt = <1000000 980000 1010000>;
};
};
domain1_opp_table: opp-table1 {
compatible = "operating-points-v2";
domain1_opp_0: opp-1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <975000 970000 985000>;
};
domain1_opp_1: opp-1300000000 {
opp-hz = /bits/ 64 <1300000000>;
opp-microvolt = <1000000 980000 1010000>;
};
};
power: power-controller@12340000 {
compatible = "foo,power-controller";
reg = <0x12340000 0x1000>;
#power-domain-cells = <1>;
operating-points-v2 = <&domain0_opp_table>, <&domain1_opp_table>;
};
leaky-device0@12350000 {
compatible = "foo,i-leak-current";
reg = <0x12350000 0x1000>;
power-domains = <&power 0>;
required-opp = <&domain0_opp_0>;
};
leaky-device1@12350000 {
compatible = "foo,i-leak-current";
reg = <0x12350000 0x1000>;
power-domains = <&power 1>;
required-opp = <&domain1_opp_1>;
};
[1]. Documentation/devicetree/bindings/power/domain-idle-state.txt [1]. Documentation/devicetree/bindings/power/domain-idle-state.txt
...@@ -777,17 +777,51 @@ The driver can indicate that by setting ``DPM_FLAG_SMART_SUSPEND`` in ...@@ -777,17 +777,51 @@ The driver can indicate that by setting ``DPM_FLAG_SMART_SUSPEND`` in
runtime suspend at the beginning of the ``suspend_late`` phase of system-wide runtime suspend at the beginning of the ``suspend_late`` phase of system-wide
suspend (or in the ``poweroff_late`` phase of hibernation), when runtime PM suspend (or in the ``poweroff_late`` phase of hibernation), when runtime PM
has been disabled for it, under the assumption that its state should not change has been disabled for it, under the assumption that its state should not change
after that point until the system-wide transition is over. If that happens, the after that point until the system-wide transition is over (the PM core itself
driver's system-wide resume callbacks, if present, may still be invoked during does that for devices whose "noirq", "late" and "early" system-wide PM callbacks
the subsequent system-wide resume transition and the device's runtime power are executed directly by it). If that happens, the driver's system-wide resume
management status may be set to "active" before enabling runtime PM for it, callbacks, if present, may still be invoked during the subsequent system-wide
so the driver must be prepared to cope with the invocation of its system-wide resume transition and the device's runtime power management status may be set
resume callbacks back-to-back with its ``->runtime_suspend`` one (without the to "active" before enabling runtime PM for it, so the driver must be prepared to
intervening ``->runtime_resume`` and so on) and the final state of the device cope with the invocation of its system-wide resume callbacks back-to-back with
must reflect the "active" status for runtime PM in that case. its ``->runtime_suspend`` one (without the intervening ``->runtime_resume`` and
so on) and the final state of the device must reflect the "active" runtime PM
status in that case.
During system-wide resume from a sleep state it's easiest to put devices into During system-wide resume from a sleep state it's easiest to put devices into
the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`. the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`.
Refer to that document for more information regarding this particular issue as [Refer to that document for more information regarding this particular issue as
well as for information on the device runtime power management framework in well as for information on the device runtime power management framework in
general. general.]
However, it often is desirable to leave devices in suspend after system
transitions to the working state, especially if those devices had been in
runtime suspend before the preceding system-wide suspend (or analogous)
transition. Device drivers can use the ``DPM_FLAG_LEAVE_SUSPENDED`` flag to
indicate to the PM core (and middle-layer code) that they prefer the specific
devices handled by them to be left suspended and they have no problems with
skipping their system-wide resume callbacks for this reason. Whether or not the
devices will actually be left in suspend may depend on their state before the
given system suspend-resume cycle and on the type of the system transition under
way. In particular, devices are not left suspended if that transition is a
restore from hibernation, as device states are not guaranteed to be reflected
by the information stored in the hibernation image in that case.
The middle-layer code involved in the handling of the device is expected to
indicate to the PM core if the device may be left in suspend by setting its
:c:member:`power.may_skip_resume` status bit which is checked by the PM core
during the "noirq" phase of the preceding system-wide suspend (or analogous)
transition. The middle layer is then responsible for handling the device as
appropriate in its "noirq" resume callback, which is executed regardless of
whether or not the device is left suspended, but the other resume callbacks
(except for ``->complete``) will be skipped automatically by the PM core if the
device really can be left in suspend.
For devices whose "noirq", "late" and "early" driver callbacks are invoked
directly by the PM core, all of the system-wide resume callbacks are skipped if
``DPM_FLAG_LEAVE_SUSPENDED`` is set and the device is in runtime suspend during
the ``suspend_noirq`` (or analogous) phase or the transition under way is a
proper system suspend (rather than anything related to hibernation) and the
device's wakeup settings are suitable for runtime PM (that is, it cannot
generate wakeup signals at all or it is allowed to wake up the system from
sleep).
...@@ -994,6 +994,17 @@ into D0 going forward), but if it is in runtime suspend in pci_pm_thaw_noirq(), ...@@ -994,6 +994,17 @@ into D0 going forward), but if it is in runtime suspend in pci_pm_thaw_noirq(),
the function will set the power.direct_complete flag for it (to make the PM core the function will set the power.direct_complete flag for it (to make the PM core
skip the subsequent "thaw" callbacks for it) and return. skip the subsequent "thaw" callbacks for it) and return.
Setting the DPM_FLAG_LEAVE_SUSPENDED flag means that the driver prefers the
device to be left in suspend after system-wide transitions to the working state.
This flag is checked by the PM core, but the PCI bus type informs the PM core
which devices may be left in suspend from its perspective (that happens during
the "noirq" phase of system-wide suspend and analogous transitions) and next it
uses the dev_pm_may_skip_resume() helper to decide whether or not to return from
pci_pm_resume_noirq() early, as the PM core will skip the remaining resume
callbacks for the device during the transition under way and will set its
runtime PM status to "suspended" if dev_pm_may_skip_resume() returns "true" for
it.
3.2. Device Runtime Power Management 3.2. Device Runtime Power Management
------------------------------------ ------------------------------------
In addition to providing device power management callbacks PCI device drivers In addition to providing device power management callbacks PCI device drivers
......
...@@ -26,39 +26,16 @@ the user. The registration APIs returns the cooling device pointer. ...@@ -26,39 +26,16 @@ the user. The registration APIs returns the cooling device pointer.
clip_cpus: cpumask of cpus where the frequency constraints will happen. clip_cpus: cpumask of cpus where the frequency constraints will happen.
1.1.2 struct thermal_cooling_device *of_cpufreq_cooling_register( 1.1.2 struct thermal_cooling_device *of_cpufreq_cooling_register(
struct device_node *np, const struct cpumask *clip_cpus) struct cpufreq_policy *policy)
This interface function registers the cpufreq cooling device with This interface function registers the cpufreq cooling device with
the name "thermal-cpufreq-%x" linking it with a device tree node, in the name "thermal-cpufreq-%x" linking it with a device tree node, in
order to bind it via the thermal DT code. This api can support multiple order to bind it via the thermal DT code. This api can support multiple
instances of cpufreq cooling devices. instances of cpufreq cooling devices.
np: pointer to the cooling device device tree node policy: CPUFreq policy.
clip_cpus: cpumask of cpus where the frequency constraints will happen.
1.1.3 struct thermal_cooling_device *cpufreq_power_cooling_register( 1.1.3 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
const struct cpumask *clip_cpus, u32 capacitance,
get_static_t plat_static_func)
Similar to cpufreq_cooling_register, this function registers a cpufreq
cooling device. Using this function, the cooling device will
implement the power extensions by using a simple cpu power model. The
cpus must have registered their OPPs using the OPP library.
The additional parameters are needed for the power model (See 2. Power
models). "capacitance" is the dynamic power coefficient (See 2.1
Dynamic power). "plat_static_func" is a function to calculate the
static power consumed by these cpus (See 2.2 Static power).
1.1.4 struct thermal_cooling_device *of_cpufreq_power_cooling_register(
struct device_node *np, const struct cpumask *clip_cpus, u32 capacitance,
get_static_t plat_static_func)
Similar to cpufreq_power_cooling_register, this function register a
cpufreq cooling device with power extensions using the device tree
information supplied by the np parameter.
1.1.5 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
This interface function unregisters the "thermal-cpufreq-%x" cooling device. This interface function unregisters the "thermal-cpufreq-%x" cooling device.
...@@ -67,20 +44,14 @@ information supplied by the np parameter. ...@@ -67,20 +44,14 @@ information supplied by the np parameter.
2. Power models 2. Power models
The power API registration functions provide a simple power model for The power API registration functions provide a simple power model for
CPUs. The current power is calculated as dynamic + (optionally) CPUs. The current power is calculated as dynamic power (static power isn't
static power. This power model requires that the operating-points of supported currently). This power model requires that the operating-points of
the CPUs are registered using the kernel's opp library and the the CPUs are registered using the kernel's opp library and the
`cpufreq_frequency_table` is assigned to the `struct device` of the `cpufreq_frequency_table` is assigned to the `struct device` of the
cpu. If you are using CONFIG_CPUFREQ_DT then the cpu. If you are using CONFIG_CPUFREQ_DT then the
`cpufreq_frequency_table` should already be assigned to the cpu `cpufreq_frequency_table` should already be assigned to the cpu
device. device.
The `plat_static_func` parameter of `cpufreq_power_cooling_register()`
and `of_cpufreq_power_cooling_register()` is optional. If you don't
provide it, only dynamic power will be considered.
2.1 Dynamic power
The dynamic power consumption of a processor depends on many factors. The dynamic power consumption of a processor depends on many factors.
For a given processor implementation the primary factors are: For a given processor implementation the primary factors are:
...@@ -119,79 +90,3 @@ mW/MHz/uVolt^2. Typical values for mobile CPUs might lie in range ...@@ -119,79 +90,3 @@ mW/MHz/uVolt^2. Typical values for mobile CPUs might lie in range
from 100 to 500. For reference, the approximate values for the SoC in from 100 to 500. For reference, the approximate values for the SoC in
ARM's Juno Development Platform are 530 for the Cortex-A57 cluster and ARM's Juno Development Platform are 530 for the Cortex-A57 cluster and
140 for the Cortex-A53 cluster. 140 for the Cortex-A53 cluster.
2.2 Static power
Static leakage power consumption depends on a number of factors. For a
given circuit implementation the primary factors are:
- Time the circuit spends in each 'power state'
- Temperature
- Operating voltage
- Process grade
The time the circuit spends in each 'power state' for a given
evaluation period at first order means OFF or ON. However,
'retention' states can also be supported that reduce power during
inactive periods without loss of context.
Note: The visibility of state entries to the OS can vary, according to
platform specifics, and this can then impact the accuracy of a model
based on OS state information alone. It might be possible in some
cases to extract more accurate information from system resources.
The temperature, operating voltage and process 'grade' (slow to fast)
of the circuit are all significant factors in static leakage power
consumption. All of these have complex relationships to static power.
Circuit implementation specific factors include the chosen silicon
process as well as the type, number and size of transistors in both
the logic gates and any RAM elements included.
The static power consumption modelling must take into account the
power managed regions that are implemented. Taking the example of an
ARM processor cluster, the modelling would take into account whether
each CPU can be powered OFF separately or if only a single power
region is implemented for the complete cluster.
In one view, there are others, a static power consumption model can
then start from a set of reference values for each power managed
region (e.g. CPU, Cluster/L2) in each state (e.g. ON, OFF) at an
arbitrary process grade, voltage and temperature point. These values
are then scaled for all of the following: the time in each state, the
process grade, the current temperature and the operating voltage.
However, since both implementation specific and complex relationships
dominate the estimate, the appropriate interface to the model from the
cpu cooling device is to provide a function callback that calculates
the static power in this platform. When registering the cpu cooling
device pass a function pointer that follows the `get_static_t`
prototype:
int plat_get_static(cpumask_t *cpumask, int interval,
unsigned long voltage, u32 &power);
`cpumask` is the cpumask of the cpus involved in the calculation.
`voltage` is the voltage at which they are operating. The function
should calculate the average static power for the last `interval`
milliseconds. It returns 0 on success, -E* on error. If it
succeeds, it should store the static power in `power`. Reading the
temperature of the cpus described by `cpumask` is left for
plat_get_static() to do as the platform knows best which thermal
sensor is closest to the cpu.
If `plat_static_func` is NULL, static power is considered to be
negligible for this platform and only dynamic power is considered.
The platform specific callback can then use any combination of tables
and/or equations to permute the estimated value. Process grade
information is not passed to the model since access to such data, from
on-chip measurement capability or manufacture time data, is platform
specific.
Note: the significance of static power for CPUs in comparison to
dynamic power is highly dependent on implementation. Given the
potential complexity in implementation, the importance and accuracy of
its inclusion when using cpu cooling devices should be assessed on a
case by case basis.
...@@ -1591,6 +1591,7 @@ F: arch/arm/boot/dts/kirkwood* ...@@ -1591,6 +1591,7 @@ F: arch/arm/boot/dts/kirkwood*
F: arch/arm/configs/mvebu_*_defconfig F: arch/arm/configs/mvebu_*_defconfig
F: arch/arm/mach-mvebu/ F: arch/arm/mach-mvebu/
F: arch/arm64/boot/dts/marvell/armada* F: arch/arm64/boot/dts/marvell/armada*
F: drivers/cpufreq/armada-37xx-cpufreq.c
F: drivers/cpufreq/mvebu-cpufreq.c F: drivers/cpufreq/mvebu-cpufreq.c
F: drivers/irqchip/irq-armada-370-xp.c F: drivers/irqchip/irq-armada-370-xp.c
F: drivers/irqchip/irq-mvebu-* F: drivers/irqchip/irq-mvebu-*
...@@ -10889,6 +10890,7 @@ F: include/linux/pm.h ...@@ -10889,6 +10890,7 @@ F: include/linux/pm.h
F: include/linux/pm_* F: include/linux/pm_*
F: include/linux/powercap.h F: include/linux/powercap.h
F: drivers/powercap/ F: drivers/powercap/
F: kernel/configs/nopm.config
POWER STATE COORDINATION INTERFACE (PSCI) POWER STATE COORDINATION INTERFACE (PSCI)
M: Mark Rutland <mark.rutland@arm.com> M: Mark Rutland <mark.rutland@arm.com>
......
...@@ -68,12 +68,14 @@ cpu0: cpu@0 { ...@@ -68,12 +68,14 @@ cpu0: cpu@0 {
clock-latency = <61036>; /* two CLK32 periods */ clock-latency = <61036>; /* two CLK32 periods */
operating-points = < operating-points = <
/* kHz uV */ /* kHz uV */
696000 1275000
528000 1175000 528000 1175000
396000 1025000 396000 1025000
198000 950000 198000 950000
>; >;
fsl,soc-operating-points = < fsl,soc-operating-points = <
/* KHz uV */ /* KHz uV */
696000 1275000
528000 1175000 528000 1175000
396000 1175000 396000 1175000
198000 1175000 198000 1175000
......
...@@ -138,6 +138,8 @@ static int __init acpi_sleep_setup(char *str) ...@@ -138,6 +138,8 @@ static int __init acpi_sleep_setup(char *str)
acpi_nvs_nosave_s3(); acpi_nvs_nosave_s3();
if (strncmp(str, "old_ordering", 12) == 0) if (strncmp(str, "old_ordering", 12) == 0)
acpi_old_suspend_ordering(); acpi_old_suspend_ordering();
if (strncmp(str, "nobl", 4) == 0)
acpi_sleep_no_blacklist();
str = strchr(str, ','); str = strchr(str, ',');
if (str != NULL) if (str != NULL)
str += strspn(str, ", \t"); str += strspn(str, ", \t");
......
...@@ -990,7 +990,7 @@ void acpi_subsys_complete(struct device *dev) ...@@ -990,7 +990,7 @@ void acpi_subsys_complete(struct device *dev)
* the sleep state it is going out of and it has never been resumed till * the sleep state it is going out of and it has never been resumed till
* now, resume it in case the firmware powered it up. * now, resume it in case the firmware powered it up.
*/ */
if (dev->power.direct_complete && pm_resume_via_firmware()) if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
pm_request_resume(dev); pm_request_resume(dev);
} }
EXPORT_SYMBOL_GPL(acpi_subsys_complete); EXPORT_SYMBOL_GPL(acpi_subsys_complete);
...@@ -1039,10 +1039,28 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late); ...@@ -1039,10 +1039,28 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
*/ */
int acpi_subsys_suspend_noirq(struct device *dev) int acpi_subsys_suspend_noirq(struct device *dev)
{ {
if (dev_pm_smart_suspend_and_suspended(dev)) int ret;
if (dev_pm_smart_suspend_and_suspended(dev)) {
dev->power.may_skip_resume = true;
return 0; return 0;
}
ret = pm_generic_suspend_noirq(dev);
if (ret)
return ret;
/*
* If the target system sleep state is suspend-to-idle, it is sufficient
* to check whether or not the device's wakeup settings are good for
* runtime PM. Otherwise, the pm_resume_via_firmware() check will cause
* acpi_subsys_complete() to take care of fixing up the device's state
* anyway, if need be.
*/
dev->power.may_skip_resume = device_may_wakeup(dev) ||
!device_can_wakeup(dev);
return pm_generic_suspend_noirq(dev); return 0;
} }
EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq); EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq);
...@@ -1052,6 +1070,9 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq); ...@@ -1052,6 +1070,9 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq);
*/ */
int acpi_subsys_resume_noirq(struct device *dev) int acpi_subsys_resume_noirq(struct device *dev)
{ {
if (dev_pm_may_skip_resume(dev))
return 0;
/* /*
* Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
* during system suspend, so update their runtime PM status to "active" * during system suspend, so update their runtime PM status to "active"
......
...@@ -367,10 +367,20 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = { ...@@ -367,10 +367,20 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = {
{}, {},
}; };
static bool ignore_blacklist;
void __init acpi_sleep_no_blacklist(void)
{
ignore_blacklist = true;
}
static void __init acpi_sleep_dmi_check(void) static void __init acpi_sleep_dmi_check(void)
{ {
int year; int year;
if (ignore_blacklist)
return;
if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year >= 2012) if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year >= 2012)
acpi_nvs_nosave_s3(); acpi_nvs_nosave_s3();
...@@ -697,7 +707,8 @@ static const struct acpi_device_id lps0_device_ids[] = { ...@@ -697,7 +707,8 @@ static const struct acpi_device_id lps0_device_ids[] = {
#define ACPI_LPS0_ENTRY 5 #define ACPI_LPS0_ENTRY 5
#define ACPI_LPS0_EXIT 6 #define ACPI_LPS0_EXIT 6
#define ACPI_S2IDLE_FUNC_MASK ((1 << ACPI_LPS0_ENTRY) | (1 << ACPI_LPS0_EXIT)) #define ACPI_LPS0_SCREEN_MASK ((1 << ACPI_LPS0_SCREEN_OFF) | (1 << ACPI_LPS0_SCREEN_ON))
#define ACPI_LPS0_PLATFORM_MASK ((1 << ACPI_LPS0_ENTRY) | (1 << ACPI_LPS0_EXIT))
static acpi_handle lps0_device_handle; static acpi_handle lps0_device_handle;
static guid_t lps0_dsm_guid; static guid_t lps0_dsm_guid;
...@@ -900,7 +911,8 @@ static int lps0_device_attach(struct acpi_device *adev, ...@@ -900,7 +911,8 @@ static int lps0_device_attach(struct acpi_device *adev,
if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) { if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) {
char bitmask = *(char *)out_obj->buffer.pointer; char bitmask = *(char *)out_obj->buffer.pointer;
if ((bitmask & ACPI_S2IDLE_FUNC_MASK) == ACPI_S2IDLE_FUNC_MASK) { if ((bitmask & ACPI_LPS0_PLATFORM_MASK) == ACPI_LPS0_PLATFORM_MASK ||
(bitmask & ACPI_LPS0_SCREEN_MASK) == ACPI_LPS0_SCREEN_MASK) {
lps0_dsm_func_mask = bitmask; lps0_dsm_func_mask = bitmask;
lps0_device_handle = adev->handle; lps0_device_handle = adev->handle;
/* /*
......
...@@ -1032,15 +1032,12 @@ static int genpd_prepare(struct device *dev) ...@@ -1032,15 +1032,12 @@ static int genpd_prepare(struct device *dev)
static int genpd_finish_suspend(struct device *dev, bool poweroff) static int genpd_finish_suspend(struct device *dev, bool poweroff)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
int ret; int ret = 0;
genpd = dev_to_genpd(dev); genpd = dev_to_genpd(dev);
if (IS_ERR(genpd)) if (IS_ERR(genpd))
return -EINVAL; return -EINVAL;
if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
return 0;
if (poweroff) if (poweroff)
ret = pm_generic_poweroff_noirq(dev); ret = pm_generic_poweroff_noirq(dev);
else else
...@@ -1048,10 +1045,19 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff) ...@@ -1048,10 +1045,19 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
if (ret) if (ret)
return ret; return ret;
if (genpd->dev_ops.stop && genpd->dev_ops.start) { if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
ret = pm_runtime_force_suspend(dev); return 0;
if (ret)
if (genpd->dev_ops.stop && genpd->dev_ops.start &&
!pm_runtime_status_suspended(dev)) {
ret = genpd_stop_dev(genpd, dev);
if (ret) {
if (poweroff)
pm_generic_restore_noirq(dev);
else
pm_generic_resume_noirq(dev);
return ret; return ret;
}
} }
genpd_lock(genpd); genpd_lock(genpd);
...@@ -1085,7 +1091,7 @@ static int genpd_suspend_noirq(struct device *dev) ...@@ -1085,7 +1091,7 @@ static int genpd_suspend_noirq(struct device *dev)
static int genpd_resume_noirq(struct device *dev) static int genpd_resume_noirq(struct device *dev)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
int ret = 0; int ret;
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
...@@ -1094,21 +1100,21 @@ static int genpd_resume_noirq(struct device *dev) ...@@ -1094,21 +1100,21 @@ static int genpd_resume_noirq(struct device *dev)
return -EINVAL; return -EINVAL;
if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd)) if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
return 0; return pm_generic_resume_noirq(dev);
genpd_lock(genpd); genpd_lock(genpd);
genpd_sync_power_on(genpd, true, 0); genpd_sync_power_on(genpd, true, 0);
genpd->suspended_count--; genpd->suspended_count--;
genpd_unlock(genpd); genpd_unlock(genpd);
if (genpd->dev_ops.stop && genpd->dev_ops.start) if (genpd->dev_ops.stop && genpd->dev_ops.start &&
ret = pm_runtime_force_resume(dev); !pm_runtime_status_suspended(dev)) {
ret = genpd_start_dev(genpd, dev);
ret = pm_generic_resume_noirq(dev); if (ret)
if (ret) return ret;
return ret; }
return ret; return pm_generic_resume_noirq(dev);
} }
/** /**
...@@ -1135,8 +1141,9 @@ static int genpd_freeze_noirq(struct device *dev) ...@@ -1135,8 +1141,9 @@ static int genpd_freeze_noirq(struct device *dev)
if (ret) if (ret)
return ret; return ret;
if (genpd->dev_ops.stop && genpd->dev_ops.start) if (genpd->dev_ops.stop && genpd->dev_ops.start &&
ret = pm_runtime_force_suspend(dev); !pm_runtime_status_suspended(dev))
ret = genpd_stop_dev(genpd, dev);
return ret; return ret;
} }
...@@ -1159,8 +1166,9 @@ static int genpd_thaw_noirq(struct device *dev) ...@@ -1159,8 +1166,9 @@ static int genpd_thaw_noirq(struct device *dev)
if (IS_ERR(genpd)) if (IS_ERR(genpd))
return -EINVAL; return -EINVAL;
if (genpd->dev_ops.stop && genpd->dev_ops.start) { if (genpd->dev_ops.stop && genpd->dev_ops.start &&
ret = pm_runtime_force_resume(dev); !pm_runtime_status_suspended(dev)) {
ret = genpd_start_dev(genpd, dev);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -1217,8 +1225,9 @@ static int genpd_restore_noirq(struct device *dev) ...@@ -1217,8 +1225,9 @@ static int genpd_restore_noirq(struct device *dev)
genpd_sync_power_on(genpd, true, 0); genpd_sync_power_on(genpd, true, 0);
genpd_unlock(genpd); genpd_unlock(genpd);
if (genpd->dev_ops.stop && genpd->dev_ops.start) { if (genpd->dev_ops.stop && genpd->dev_ops.start &&
ret = pm_runtime_force_resume(dev); !pm_runtime_status_suspended(dev)) {
ret = genpd_start_dev(genpd, dev);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -2199,20 +2208,8 @@ int genpd_dev_pm_attach(struct device *dev) ...@@ -2199,20 +2208,8 @@ int genpd_dev_pm_attach(struct device *dev)
ret = of_parse_phandle_with_args(dev->of_node, "power-domains", ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells", 0, &pd_args); "#power-domain-cells", 0, &pd_args);
if (ret < 0) { if (ret < 0)
if (ret != -ENOENT) return ret;
return ret;
/*
* Try legacy Samsung-specific bindings
* (for backwards compatibility of DT ABI)
*/
pd_args.args_count = 0;
pd_args.np = of_parse_phandle(dev->of_node,
"samsung,power-domain", 0);
if (!pd_args.np)
return -ENOENT;
}
mutex_lock(&gpd_list_lock); mutex_lock(&gpd_list_lock);
pd = genpd_get_from_provider(&pd_args); pd = genpd_get_from_provider(&pd_args);
......
This diff is collapsed.
...@@ -41,20 +41,15 @@ extern void dev_pm_disable_wake_irq_check(struct device *dev); ...@@ -41,20 +41,15 @@ extern void dev_pm_disable_wake_irq_check(struct device *dev);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
extern int device_wakeup_attach_irq(struct device *dev, extern void device_wakeup_attach_irq(struct device *dev, struct wake_irq *wakeirq);
struct wake_irq *wakeirq);
extern void device_wakeup_detach_irq(struct device *dev); extern void device_wakeup_detach_irq(struct device *dev);
extern void device_wakeup_arm_wake_irqs(void); extern void device_wakeup_arm_wake_irqs(void);
extern void device_wakeup_disarm_wake_irqs(void); extern void device_wakeup_disarm_wake_irqs(void);
#else #else
static inline int static inline void device_wakeup_attach_irq(struct device *dev,
device_wakeup_attach_irq(struct device *dev, struct wake_irq *wakeirq) {}
struct wake_irq *wakeirq)
{
return 0;
}
static inline void device_wakeup_detach_irq(struct device *dev) static inline void device_wakeup_detach_irq(struct device *dev)
{ {
......
...@@ -1613,22 +1613,34 @@ void pm_runtime_drop_link(struct device *dev) ...@@ -1613,22 +1613,34 @@ void pm_runtime_drop_link(struct device *dev)
spin_unlock_irq(&dev->power.lock); spin_unlock_irq(&dev->power.lock);
} }
static bool pm_runtime_need_not_resume(struct device *dev)
{
return atomic_read(&dev->power.usage_count) <= 1 &&
(atomic_read(&dev->power.child_count) == 0 ||
dev->power.ignore_children);
}
/** /**
* pm_runtime_force_suspend - Force a device into suspend state if needed. * pm_runtime_force_suspend - Force a device into suspend state if needed.
* @dev: Device to suspend. * @dev: Device to suspend.
* *
* Disable runtime PM so we safely can check the device's runtime PM status and * Disable runtime PM so we safely can check the device's runtime PM status and
* if it is active, invoke it's .runtime_suspend callback to bring it into * if it is active, invoke its ->runtime_suspend callback to suspend it and
* suspend state. Keep runtime PM disabled to preserve the state unless we * change its runtime PM status field to RPM_SUSPENDED. Also, if the device's
* encounter errors. * usage and children counters don't indicate that the device was in use before
* the system-wide transition under way, decrement its parent's children counter
* (if there is a parent). Keep runtime PM disabled to preserve the state
* unless we encounter errors.
* *
* Typically this function may be invoked from a system suspend callback to make * Typically this function may be invoked from a system suspend callback to make
* sure the device is put into low power state. * sure the device is put into low power state and it should only be used during
* system-wide PM transitions to sleep states. It assumes that the analogous
* pm_runtime_force_resume() will be used to resume the device.
*/ */
int pm_runtime_force_suspend(struct device *dev) int pm_runtime_force_suspend(struct device *dev)
{ {
int (*callback)(struct device *); int (*callback)(struct device *);
int ret = 0; int ret;
pm_runtime_disable(dev); pm_runtime_disable(dev);
if (pm_runtime_status_suspended(dev)) if (pm_runtime_status_suspended(dev))
...@@ -1636,27 +1648,23 @@ int pm_runtime_force_suspend(struct device *dev) ...@@ -1636,27 +1648,23 @@ int pm_runtime_force_suspend(struct device *dev)
callback = RPM_GET_CALLBACK(dev, runtime_suspend); callback = RPM_GET_CALLBACK(dev, runtime_suspend);
if (!callback) { ret = callback ? callback(dev) : 0;
ret = -ENOSYS;
goto err;
}
ret = callback(dev);
if (ret) if (ret)
goto err; goto err;
/* /*
* Increase the runtime PM usage count for the device's parent, in case * If the device can stay in suspend after the system-wide transition
* when we find the device being used when system suspend was invoked. * to the working state that will follow, drop the children counter of
* This informs pm_runtime_force_resume() to resume the parent * its parent, but set its status to RPM_SUSPENDED anyway in case this
* immediately, which is needed to be able to resume its children, * function will be called again for it in the meantime.
* when not deferring the resume to be managed via runtime PM.
*/ */
if (dev->parent && atomic_read(&dev->power.usage_count) > 1) if (pm_runtime_need_not_resume(dev))
pm_runtime_get_noresume(dev->parent); pm_runtime_set_suspended(dev);
else
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_set_suspended(dev);
return 0; return 0;
err: err:
pm_runtime_enable(dev); pm_runtime_enable(dev);
return ret; return ret;
...@@ -1669,13 +1677,9 @@ EXPORT_SYMBOL_GPL(pm_runtime_force_suspend); ...@@ -1669,13 +1677,9 @@ EXPORT_SYMBOL_GPL(pm_runtime_force_suspend);
* *
* Prior invoking this function we expect the user to have brought the device * Prior invoking this function we expect the user to have brought the device
* into low power state by a call to pm_runtime_force_suspend(). Here we reverse * into low power state by a call to pm_runtime_force_suspend(). Here we reverse
* those actions and brings the device into full power, if it is expected to be * those actions and bring the device into full power, if it is expected to be
* used on system resume. To distinguish that, we check whether the runtime PM * used on system resume. In the other case, we defer the resume to be managed
* usage count is greater than 1 (the PM core increases the usage count in the * via runtime PM.
* system PM prepare phase), as that indicates a real user (such as a subsystem,
* driver, userspace, etc.) is using it. If that is the case, the device is
* expected to be used on system resume as well, so then we resume it. In the
* other case, we defer the resume to be managed via runtime PM.
* *
* Typically this function may be invoked from a system resume callback. * Typically this function may be invoked from a system resume callback.
*/ */
...@@ -1684,32 +1688,18 @@ int pm_runtime_force_resume(struct device *dev) ...@@ -1684,32 +1688,18 @@ int pm_runtime_force_resume(struct device *dev)
int (*callback)(struct device *); int (*callback)(struct device *);
int ret = 0; int ret = 0;
callback = RPM_GET_CALLBACK(dev, runtime_resume); if (!pm_runtime_status_suspended(dev) || pm_runtime_need_not_resume(dev))
if (!callback) {
ret = -ENOSYS;
goto out;
}
if (!pm_runtime_status_suspended(dev))
goto out; goto out;
/* /*
* Decrease the parent's runtime PM usage count, if we increased it * The value of the parent's children counter is correct already, so
* during system suspend in pm_runtime_force_suspend(). * just update the status of the device.
*/ */
if (atomic_read(&dev->power.usage_count) > 1) { __update_runtime_status(dev, RPM_ACTIVE);
if (dev->parent)
pm_runtime_put_noidle(dev->parent);
} else {
goto out;
}
ret = pm_runtime_set_active(dev); callback = RPM_GET_CALLBACK(dev, runtime_resume);
if (ret)
goto out;
ret = callback(dev); ret = callback ? callback(dev) : 0;
if (ret) { if (ret) {
pm_runtime_set_suspended(dev); pm_runtime_set_suspended(dev);
goto out; goto out;
......
This diff is collapsed.
...@@ -33,7 +33,6 @@ static int dev_pm_attach_wake_irq(struct device *dev, int irq, ...@@ -33,7 +33,6 @@ static int dev_pm_attach_wake_irq(struct device *dev, int irq,
struct wake_irq *wirq) struct wake_irq *wirq)
{ {
unsigned long flags; unsigned long flags;
int err;
if (!dev || !wirq) if (!dev || !wirq)
return -EINVAL; return -EINVAL;
...@@ -45,12 +44,11 @@ static int dev_pm_attach_wake_irq(struct device *dev, int irq, ...@@ -45,12 +44,11 @@ static int dev_pm_attach_wake_irq(struct device *dev, int irq,
return -EEXIST; return -EEXIST;
} }
err = device_wakeup_attach_irq(dev, wirq); dev->power.wakeirq = wirq;
if (!err) device_wakeup_attach_irq(dev, wirq);
dev->power.wakeirq = wirq;
spin_unlock_irqrestore(&dev->power.lock, flags); spin_unlock_irqrestore(&dev->power.lock, flags);
return err; return 0;
} }
/** /**
......
...@@ -19,6 +19,11 @@ ...@@ -19,6 +19,11 @@
#include "power.h" #include "power.h"
#ifndef CONFIG_SUSPEND
suspend_state_t pm_suspend_target_state;
#define pm_suspend_target_state (PM_SUSPEND_ON)
#endif
/* /*
* If set, the suspend/hibernate code will abort transitions to a sleep state * If set, the suspend/hibernate code will abort transitions to a sleep state
* if wakeup events are registered during or immediately before the transition. * if wakeup events are registered during or immediately before the transition.
...@@ -268,6 +273,9 @@ int device_wakeup_enable(struct device *dev) ...@@ -268,6 +273,9 @@ int device_wakeup_enable(struct device *dev)
if (!dev || !dev->power.can_wakeup) if (!dev || !dev->power.can_wakeup)
return -EINVAL; return -EINVAL;
if (pm_suspend_target_state != PM_SUSPEND_ON)
dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__);
ws = wakeup_source_register(dev_name(dev)); ws = wakeup_source_register(dev_name(dev));
if (!ws) if (!ws)
return -ENOMEM; return -ENOMEM;
...@@ -291,22 +299,19 @@ EXPORT_SYMBOL_GPL(device_wakeup_enable); ...@@ -291,22 +299,19 @@ EXPORT_SYMBOL_GPL(device_wakeup_enable);
* *
* Call under the device's power.lock lock. * Call under the device's power.lock lock.
*/ */
int device_wakeup_attach_irq(struct device *dev, void device_wakeup_attach_irq(struct device *dev,
struct wake_irq *wakeirq) struct wake_irq *wakeirq)
{ {
struct wakeup_source *ws; struct wakeup_source *ws;
ws = dev->power.wakeup; ws = dev->power.wakeup;
if (!ws) { if (!ws)
dev_err(dev, "forgot to call call device_init_wakeup?\n"); return;
return -EINVAL;
}
if (ws->wakeirq) if (ws->wakeirq)
return -EEXIST; dev_err(dev, "Leftover wakeup IRQ found, overriding\n");
ws->wakeirq = wakeirq; ws->wakeirq = wakeirq;
return 0;
} }
/** /**
...@@ -448,9 +453,7 @@ int device_init_wakeup(struct device *dev, bool enable) ...@@ -448,9 +453,7 @@ int device_init_wakeup(struct device *dev, bool enable)
device_set_wakeup_capable(dev, true); device_set_wakeup_capable(dev, true);
ret = device_wakeup_enable(dev); ret = device_wakeup_enable(dev);
} else { } else {
if (dev->power.can_wakeup) device_wakeup_disable(dev);
device_wakeup_disable(dev);
device_set_wakeup_capable(dev, false); device_set_wakeup_capable(dev, false);
} }
...@@ -464,9 +467,6 @@ EXPORT_SYMBOL_GPL(device_init_wakeup); ...@@ -464,9 +467,6 @@ EXPORT_SYMBOL_GPL(device_init_wakeup);
*/ */
int device_set_wakeup_enable(struct device *dev, bool enable) int device_set_wakeup_enable(struct device *dev, bool enable)
{ {
if (!dev || !dev->power.can_wakeup)
return -EINVAL;
return enable ? device_wakeup_enable(dev) : device_wakeup_disable(dev); return enable ? device_wakeup_enable(dev) : device_wakeup_disable(dev);
} }
EXPORT_SYMBOL_GPL(device_set_wakeup_enable); EXPORT_SYMBOL_GPL(device_set_wakeup_enable);
......
...@@ -120,7 +120,7 @@ config QCOM_EBI2 ...@@ -120,7 +120,7 @@ config QCOM_EBI2
SRAM, ethernet adapters, FPGAs and LCD displays. SRAM, ethernet adapters, FPGAs and LCD displays.
config SIMPLE_PM_BUS config SIMPLE_PM_BUS
bool "Simple Power-Managed Bus Driver" tristate "Simple Power-Managed Bus Driver"
depends on OF && PM depends on OF && PM
help help
Driver for transparent busses that don't need a real driver, but Driver for transparent busses that don't need a real driver, but
......
...@@ -2,6 +2,29 @@ ...@@ -2,6 +2,29 @@
# ARM CPU Frequency scaling drivers # ARM CPU Frequency scaling drivers
# #
config ACPI_CPPC_CPUFREQ
tristate "CPUFreq driver based on the ACPI CPPC spec"
depends on ACPI_PROCESSOR
select ACPI_CPPC_LIB
help
This adds a CPUFreq driver which uses CPPC methods
as described in the ACPIv5.1 spec. CPPC stands for
Collaborative Processor Performance Controls. It
is based on an abstract continuous scale of CPU
performance values which allows the remote power
processor to flexibly optimize for power and
performance. CPPC relies on power management firmware
support for its operation.
If in doubt, say N.
config ARM_ARMADA_37XX_CPUFREQ
tristate "Armada 37xx CPUFreq support"
depends on ARCH_MVEBU
help
This adds the CPUFreq driver support for Marvell Armada 37xx SoCs.
The Armada 37xx PMU supports 4 frequency and VDD levels.
# big LITTLE core layer and glue drivers # big LITTLE core layer and glue drivers
config ARM_BIG_LITTLE_CPUFREQ config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver" tristate "Generic ARM big LITTLE CPUfreq driver"
...@@ -12,6 +35,30 @@ config ARM_BIG_LITTLE_CPUFREQ ...@@ -12,6 +35,30 @@ config ARM_BIG_LITTLE_CPUFREQ
help help
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
config ARM_DT_BL_CPUFREQ
tristate "Generic probing via DT for ARM big LITTLE CPUfreq driver"
depends on ARM_BIG_LITTLE_CPUFREQ && OF
help
This enables probing via DT for Generic CPUfreq driver for ARM
big.LITTLE platform. This gets frequency tables from DT.
config ARM_SCPI_CPUFREQ
tristate "SCPI based CPUfreq driver"
depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI
help
This adds the CPUfreq driver support for ARM big.LITTLE platforms
using SCPI protocol for CPU power management.
This driver uses SCPI Message Protocol driver to interact with the
firmware providing the CPU DVFS functionality.
config ARM_VEXPRESS_SPC_CPUFREQ
tristate "Versatile Express SPC based CPUfreq driver"
depends on ARM_BIG_LITTLE_CPUFREQ && ARCH_VEXPRESS_SPC
help
This add the CPUfreq driver support for Versatile Express
big.LITTLE platforms using SPC for power management.
config ARM_BRCMSTB_AVS_CPUFREQ config ARM_BRCMSTB_AVS_CPUFREQ
tristate "Broadcom STB AVS CPUfreq driver" tristate "Broadcom STB AVS CPUfreq driver"
depends on ARCH_BRCMSTB || COMPILE_TEST depends on ARCH_BRCMSTB || COMPILE_TEST
...@@ -33,20 +80,6 @@ config ARM_BRCMSTB_AVS_CPUFREQ_DEBUG ...@@ -33,20 +80,6 @@ config ARM_BRCMSTB_AVS_CPUFREQ_DEBUG
If in doubt, say N. If in doubt, say N.
config ARM_DT_BL_CPUFREQ
tristate "Generic probing via DT for ARM big LITTLE CPUfreq driver"
depends on ARM_BIG_LITTLE_CPUFREQ && OF
help
This enables probing via DT for Generic CPUfreq driver for ARM
big.LITTLE platform. This gets frequency tables from DT.
config ARM_VEXPRESS_SPC_CPUFREQ
tristate "Versatile Express SPC based CPUfreq driver"
depends on ARM_BIG_LITTLE_CPUFREQ && ARCH_VEXPRESS_SPC
help
This add the CPUfreq driver support for Versatile Express
big.LITTLE platforms using SPC for power management.
config ARM_EXYNOS5440_CPUFREQ config ARM_EXYNOS5440_CPUFREQ
tristate "SAMSUNG EXYNOS5440" tristate "SAMSUNG EXYNOS5440"
depends on SOC_EXYNOS5440 depends on SOC_EXYNOS5440
...@@ -205,16 +238,6 @@ config ARM_SA1100_CPUFREQ ...@@ -205,16 +238,6 @@ config ARM_SA1100_CPUFREQ
config ARM_SA1110_CPUFREQ config ARM_SA1110_CPUFREQ
bool bool
config ARM_SCPI_CPUFREQ
tristate "SCPI based CPUfreq driver"
depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI
help
This adds the CPUfreq driver support for ARM big.LITTLE platforms
using SCPI protocol for CPU power management.
This driver uses SCPI Message Protocol driver to interact with the
firmware providing the CPU DVFS functionality.
config ARM_SPEAR_CPUFREQ config ARM_SPEAR_CPUFREQ
bool "SPEAr CPUFreq support" bool "SPEAr CPUFreq support"
depends on PLAT_SPEAR depends on PLAT_SPEAR
...@@ -275,20 +298,3 @@ config ARM_PXA2xx_CPUFREQ ...@@ -275,20 +298,3 @@ config ARM_PXA2xx_CPUFREQ
This add the CPUFreq driver support for Intel PXA2xx SOCs. This add the CPUFreq driver support for Intel PXA2xx SOCs.
If in doubt, say N. If in doubt, say N.
config ACPI_CPPC_CPUFREQ
tristate "CPUFreq driver based on the ACPI CPPC spec"
depends on ACPI_PROCESSOR
select ACPI_CPPC_LIB
default n
help
This adds a CPUFreq driver which uses CPPC methods
as described in the ACPIv5.1 spec. CPPC stands for
Collaborative Processor Performance Controls. It
is based on an abstract continuous scale of CPU
performance values which allows the remote power
processor to flexibly optimize for power and
performance. CPPC relies on power management firmware
support for its operation.
If in doubt, say N.
...@@ -52,23 +52,26 @@ obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o ...@@ -52,23 +52,26 @@ obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
# LITTLE drivers, so that it is probed last. # LITTLE drivers, so that it is probed last.
obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o
obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o
obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o
obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o
obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o
obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o
obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
obj-$(CONFIG_ARM_S3C2440_CPUFREQ) += s3c2440-cpufreq.o obj-$(CONFIG_ARM_S3C2440_CPUFREQ) += s3c2440-cpufreq.o
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
...@@ -81,8 +84,6 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o ...@@ -81,8 +84,6 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o
obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
################################################################################## ##################################################################################
......
...@@ -526,34 +526,13 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy) ...@@ -526,34 +526,13 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy)
static void bL_cpufreq_ready(struct cpufreq_policy *policy) static void bL_cpufreq_ready(struct cpufreq_policy *policy)
{ {
struct device *cpu_dev = get_cpu_device(policy->cpu);
int cur_cluster = cpu_to_cluster(policy->cpu); int cur_cluster = cpu_to_cluster(policy->cpu);
struct device_node *np;
/* Do not register a cpu_cooling device if we are in IKS mode */ /* Do not register a cpu_cooling device if we are in IKS mode */
if (cur_cluster >= MAX_CLUSTERS) if (cur_cluster >= MAX_CLUSTERS)
return; return;
np = of_node_get(cpu_dev->of_node); cdev[cur_cluster] = of_cpufreq_cooling_register(policy);
if (WARN_ON(!np))
return;
if (of_find_property(np, "#cooling-cells", NULL)) {
u32 power_coefficient = 0;
of_property_read_u32(np, "dynamic-power-coefficient",
&power_coefficient);
cdev[cur_cluster] = of_cpufreq_power_cooling_register(np,
policy, power_coefficient, NULL);
if (IS_ERR(cdev[cur_cluster])) {
dev_err(cpu_dev,
"running cpufreq without cooling device: %ld\n",
PTR_ERR(cdev[cur_cluster]));
cdev[cur_cluster] = NULL;
}
}
of_node_put(np);
} }
static struct cpufreq_driver bL_cpufreq_driver = { static struct cpufreq_driver bL_cpufreq_driver = {
......
// SPDX-License-Identifier: GPL-2.0+
/*
* CPU frequency scaling support for Armada 37xx platform.
*
* Copyright (C) 2017 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*/
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/regmap.h>
#include <linux/slab.h>
/* Power management in North Bridge register set */
#define ARMADA_37XX_NB_L0L1 0x18
#define ARMADA_37XX_NB_L2L3 0x1C
#define ARMADA_37XX_NB_TBG_DIV_OFF 13
#define ARMADA_37XX_NB_TBG_DIV_MASK 0x7
#define ARMADA_37XX_NB_CLK_SEL_OFF 11
#define ARMADA_37XX_NB_CLK_SEL_MASK 0x1
#define ARMADA_37XX_NB_CLK_SEL_TBG 0x1
#define ARMADA_37XX_NB_TBG_SEL_OFF 9
#define ARMADA_37XX_NB_TBG_SEL_MASK 0x3
#define ARMADA_37XX_NB_VDD_SEL_OFF 6
#define ARMADA_37XX_NB_VDD_SEL_MASK 0x3
#define ARMADA_37XX_NB_CONFIG_SHIFT 16
#define ARMADA_37XX_NB_DYN_MOD 0x24
#define ARMADA_37XX_NB_CLK_SEL_EN BIT(26)
#define ARMADA_37XX_NB_TBG_EN BIT(28)
#define ARMADA_37XX_NB_DIV_EN BIT(29)
#define ARMADA_37XX_NB_VDD_EN BIT(30)
#define ARMADA_37XX_NB_DFS_EN BIT(31)
#define ARMADA_37XX_NB_CPU_LOAD 0x30
#define ARMADA_37XX_NB_CPU_LOAD_MASK 0x3
#define ARMADA_37XX_DVFS_LOAD_0 0
#define ARMADA_37XX_DVFS_LOAD_1 1
#define ARMADA_37XX_DVFS_LOAD_2 2
#define ARMADA_37XX_DVFS_LOAD_3 3
/*
* On Armada 37xx the Power management manages 4 level of CPU load,
* each level can be associated with a CPU clock source, a CPU
* divider, a VDD level, etc...
*/
#define LOAD_LEVEL_NR 4
struct armada_37xx_dvfs {
u32 cpu_freq_max;
u8 divider[LOAD_LEVEL_NR];
};
static struct armada_37xx_dvfs armada_37xx_dvfs[] = {
{.cpu_freq_max = 1200*1000*1000, .divider = {1, 2, 4, 6} },
{.cpu_freq_max = 1000*1000*1000, .divider = {1, 2, 4, 5} },
{.cpu_freq_max = 800*1000*1000, .divider = {1, 2, 3, 4} },
{.cpu_freq_max = 600*1000*1000, .divider = {2, 4, 5, 6} },
};
static struct armada_37xx_dvfs *armada_37xx_cpu_freq_info_get(u32 freq)
{
int i;
for (i = 0; i < ARRAY_SIZE(armada_37xx_dvfs); i++) {
if (freq == armada_37xx_dvfs[i].cpu_freq_max)
return &armada_37xx_dvfs[i];
}
pr_err("Unsupported CPU frequency %d MHz\n", freq/1000000);
return NULL;
}
/*
* Setup the four level managed by the hardware. Once the four level
* will be configured then the DVFS will be enabled.
*/
static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base,
struct clk *clk, u8 *divider)
{
int load_lvl;
struct clk *parent;
for (load_lvl = 0; load_lvl < LOAD_LEVEL_NR; load_lvl++) {
unsigned int reg, mask, val, offset = 0;
if (load_lvl <= ARMADA_37XX_DVFS_LOAD_1)
reg = ARMADA_37XX_NB_L0L1;
else
reg = ARMADA_37XX_NB_L2L3;
if (load_lvl == ARMADA_37XX_DVFS_LOAD_0 ||
load_lvl == ARMADA_37XX_DVFS_LOAD_2)
offset += ARMADA_37XX_NB_CONFIG_SHIFT;
/* Set cpu clock source, for all the level we use TBG */
val = ARMADA_37XX_NB_CLK_SEL_TBG << ARMADA_37XX_NB_CLK_SEL_OFF;
mask = (ARMADA_37XX_NB_CLK_SEL_MASK
<< ARMADA_37XX_NB_CLK_SEL_OFF);
/*
* Set cpu divider based on the pre-computed array in
* order to have balanced step.
*/
val |= divider[load_lvl] << ARMADA_37XX_NB_TBG_DIV_OFF;
mask |= (ARMADA_37XX_NB_TBG_DIV_MASK
<< ARMADA_37XX_NB_TBG_DIV_OFF);
/* Set VDD divider which is actually the load level. */
val |= load_lvl << ARMADA_37XX_NB_VDD_SEL_OFF;
mask |= (ARMADA_37XX_NB_VDD_SEL_MASK
<< ARMADA_37XX_NB_VDD_SEL_OFF);
val <<= offset;
mask <<= offset;
regmap_update_bits(base, reg, mask, val);
}
/*
* Set cpu clock source, for all the level we keep the same
* clock source that the one already configured. For this one
* we need to use the clock framework
*/
parent = clk_get_parent(clk);
clk_set_parent(clk, parent);
}
static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base)
{
unsigned int reg = ARMADA_37XX_NB_DYN_MOD,
mask = ARMADA_37XX_NB_DFS_EN;
regmap_update_bits(base, reg, mask, 0);
}
static void __init armada37xx_cpufreq_enable_dvfs(struct regmap *base)
{
unsigned int val, reg = ARMADA_37XX_NB_CPU_LOAD,
mask = ARMADA_37XX_NB_CPU_LOAD_MASK;
/* Start with the highest load (0) */
val = ARMADA_37XX_DVFS_LOAD_0;
regmap_update_bits(base, reg, mask, val);
/* Now enable DVFS for the CPUs */
reg = ARMADA_37XX_NB_DYN_MOD;
mask = ARMADA_37XX_NB_CLK_SEL_EN | ARMADA_37XX_NB_TBG_EN |
ARMADA_37XX_NB_DIV_EN | ARMADA_37XX_NB_VDD_EN |
ARMADA_37XX_NB_DFS_EN;
regmap_update_bits(base, reg, mask, mask);
}
static int __init armada37xx_cpufreq_driver_init(void)
{
struct armada_37xx_dvfs *dvfs;
struct platform_device *pdev;
unsigned int cur_frequency;
struct regmap *nb_pm_base;
struct device *cpu_dev;
int load_lvl, ret;
struct clk *clk;
nb_pm_base =
syscon_regmap_lookup_by_compatible("marvell,armada-3700-nb-pm");
if (IS_ERR(nb_pm_base))
return -ENODEV;
/* Before doing any configuration on the DVFS first, disable it */
armada37xx_cpufreq_disable_dvfs(nb_pm_base);
/*
* On CPU 0 register the operating points supported (which are
* the nominal CPU frequency and full integer divisions of
* it).
*/
cpu_dev = get_cpu_device(0);
if (!cpu_dev) {
dev_err(cpu_dev, "Cannot get CPU\n");
return -ENODEV;
}
clk = clk_get(cpu_dev, 0);
if (IS_ERR(clk)) {
dev_err(cpu_dev, "Cannot get clock for CPU0\n");
return PTR_ERR(clk);
}
/* Get nominal (current) CPU frequency */
cur_frequency = clk_get_rate(clk);
if (!cur_frequency) {
dev_err(cpu_dev, "Failed to get clock rate for CPU\n");
return -EINVAL;
}
dvfs = armada_37xx_cpu_freq_info_get(cur_frequency);
if (!dvfs)
return -EINVAL;
armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider);
for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR;
load_lvl++) {
unsigned long freq = cur_frequency / dvfs->divider[load_lvl];
ret = dev_pm_opp_add(cpu_dev, freq, 0);
if (ret) {
/* clean-up the already added opp before leaving */
while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) {
freq = cur_frequency / dvfs->divider[load_lvl];
dev_pm_opp_remove(cpu_dev, freq);
}
return ret;
}
}
/* Now that everything is setup, enable the DVFS at hardware level */
armada37xx_cpufreq_enable_dvfs(nb_pm_base);
pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
return PTR_ERR_OR_ZERO(pdev);
}
/* late_initcall, to guarantee the driver is loaded after A37xx clock driver */
late_initcall(armada37xx_cpufreq_driver_init);
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
MODULE_DESCRIPTION("Armada 37xx cpufreq driver");
MODULE_LICENSE("GPL");
...@@ -108,6 +108,14 @@ static const struct of_device_id blacklist[] __initconst = { ...@@ -108,6 +108,14 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "marvell,armadaxp", }, { .compatible = "marvell,armadaxp", },
{ .compatible = "mediatek,mt2701", },
{ .compatible = "mediatek,mt2712", },
{ .compatible = "mediatek,mt7622", },
{ .compatible = "mediatek,mt7623", },
{ .compatible = "mediatek,mt817x", },
{ .compatible = "mediatek,mt8173", },
{ .compatible = "mediatek,mt8176", },
{ .compatible = "nvidia,tegra124", }, { .compatible = "nvidia,tegra124", },
{ .compatible = "st,stih407", }, { .compatible = "st,stih407", },
......
...@@ -319,33 +319,8 @@ static int cpufreq_exit(struct cpufreq_policy *policy) ...@@ -319,33 +319,8 @@ static int cpufreq_exit(struct cpufreq_policy *policy)
static void cpufreq_ready(struct cpufreq_policy *policy) static void cpufreq_ready(struct cpufreq_policy *policy)
{ {
struct private_data *priv = policy->driver_data; struct private_data *priv = policy->driver_data;
struct device_node *np = of_node_get(priv->cpu_dev->of_node);
if (WARN_ON(!np)) priv->cdev = of_cpufreq_cooling_register(policy);
return;
/*
* For now, just loading the cooling device;
* thermal DT code takes care of matching them.
*/
if (of_find_property(np, "#cooling-cells", NULL)) {
u32 power_coefficient = 0;
of_property_read_u32(np, "dynamic-power-coefficient",
&power_coefficient);
priv->cdev = of_cpufreq_power_cooling_register(np,
policy, power_coefficient, NULL);
if (IS_ERR(priv->cdev)) {
dev_err(priv->cpu_dev,
"running cpufreq without cooling device: %ld\n",
PTR_ERR(priv->cdev));
priv->cdev = NULL;
}
}
of_node_put(np);
} }
static struct cpufreq_driver dt_cpufreq_driver = { static struct cpufreq_driver dt_cpufreq_driver = {
......
...@@ -601,19 +601,18 @@ static struct cpufreq_governor *find_governor(const char *str_governor) ...@@ -601,19 +601,18 @@ static struct cpufreq_governor *find_governor(const char *str_governor)
/** /**
* cpufreq_parse_governor - parse a governor string * cpufreq_parse_governor - parse a governor string
*/ */
static int cpufreq_parse_governor(char *str_governor, unsigned int *policy, static int cpufreq_parse_governor(char *str_governor,
struct cpufreq_governor **governor) struct cpufreq_policy *policy)
{ {
int err = -EINVAL;
if (cpufreq_driver->setpolicy) { if (cpufreq_driver->setpolicy) {
if (!strncasecmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { if (!strncasecmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {
*policy = CPUFREQ_POLICY_PERFORMANCE; policy->policy = CPUFREQ_POLICY_PERFORMANCE;
err = 0; return 0;
} else if (!strncasecmp(str_governor, "powersave", }
CPUFREQ_NAME_LEN)) {
*policy = CPUFREQ_POLICY_POWERSAVE; if (!strncasecmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) {
err = 0; policy->policy = CPUFREQ_POLICY_POWERSAVE;
return 0;
} }
} else { } else {
struct cpufreq_governor *t; struct cpufreq_governor *t;
...@@ -621,26 +620,31 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy, ...@@ -621,26 +620,31 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy,
mutex_lock(&cpufreq_governor_mutex); mutex_lock(&cpufreq_governor_mutex);
t = find_governor(str_governor); t = find_governor(str_governor);
if (!t) {
if (t == NULL) {
int ret; int ret;
mutex_unlock(&cpufreq_governor_mutex); mutex_unlock(&cpufreq_governor_mutex);
ret = request_module("cpufreq_%s", str_governor); ret = request_module("cpufreq_%s", str_governor);
mutex_lock(&cpufreq_governor_mutex); if (ret)
return -EINVAL;
if (ret == 0) mutex_lock(&cpufreq_governor_mutex);
t = find_governor(str_governor);
}
if (t != NULL) { t = find_governor(str_governor);
*governor = t;
err = 0;
} }
if (t && !try_module_get(t->owner))
t = NULL;
mutex_unlock(&cpufreq_governor_mutex); mutex_unlock(&cpufreq_governor_mutex);
if (t) {
policy->governor = t;
return 0;
}
} }
return err;
return -EINVAL;
} }
/** /**
...@@ -760,11 +764,14 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy, ...@@ -760,11 +764,14 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy,
if (ret != 1) if (ret != 1)
return -EINVAL; return -EINVAL;
if (cpufreq_parse_governor(str_governor, &new_policy.policy, if (cpufreq_parse_governor(str_governor, &new_policy))
&new_policy.governor))
return -EINVAL; return -EINVAL;
ret = cpufreq_set_policy(policy, &new_policy); ret = cpufreq_set_policy(policy, &new_policy);
if (new_policy.governor)
module_put(new_policy.governor->owner);
return ret ? ret : count; return ret ? ret : count;
} }
...@@ -1044,8 +1051,7 @@ static int cpufreq_init_policy(struct cpufreq_policy *policy) ...@@ -1044,8 +1051,7 @@ static int cpufreq_init_policy(struct cpufreq_policy *policy)
if (policy->last_policy) if (policy->last_policy)
new_policy.policy = policy->last_policy; new_policy.policy = policy->last_policy;
else else
cpufreq_parse_governor(gov->name, &new_policy.policy, cpufreq_parse_governor(gov->name, &new_policy);
NULL);
} }
/* set default policy */ /* set default policy */
return cpufreq_set_policy(policy, &new_policy); return cpufreq_set_policy(policy, &new_policy);
...@@ -2160,7 +2166,6 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) ...@@ -2160,7 +2166,6 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
mutex_lock(&cpufreq_governor_mutex); mutex_lock(&cpufreq_governor_mutex);
list_del(&governor->governor_list); list_del(&governor->governor_list);
mutex_unlock(&cpufreq_governor_mutex); mutex_unlock(&cpufreq_governor_mutex);
return;
} }
EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); EXPORT_SYMBOL_GPL(cpufreq_unregister_governor);
......
...@@ -27,7 +27,7 @@ struct cpufreq_stats { ...@@ -27,7 +27,7 @@ struct cpufreq_stats {
unsigned int *trans_table; unsigned int *trans_table;
}; };
static int cpufreq_stats_update(struct cpufreq_stats *stats) static void cpufreq_stats_update(struct cpufreq_stats *stats)
{ {
unsigned long long cur_time = get_jiffies_64(); unsigned long long cur_time = get_jiffies_64();
...@@ -35,7 +35,6 @@ static int cpufreq_stats_update(struct cpufreq_stats *stats) ...@@ -35,7 +35,6 @@ static int cpufreq_stats_update(struct cpufreq_stats *stats)
stats->time_in_state[stats->last_index] += cur_time - stats->last_time; stats->time_in_state[stats->last_index] += cur_time - stats->last_time;
stats->last_time = cur_time; stats->last_time = cur_time;
spin_unlock(&cpufreq_stats_lock); spin_unlock(&cpufreq_stats_lock);
return 0;
} }
static void cpufreq_stats_clear_table(struct cpufreq_stats *stats) static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
......
...@@ -25,15 +25,29 @@ static struct regulator *arm_reg; ...@@ -25,15 +25,29 @@ static struct regulator *arm_reg;
static struct regulator *pu_reg; static struct regulator *pu_reg;
static struct regulator *soc_reg; static struct regulator *soc_reg;
static struct clk *arm_clk; enum IMX6_CPUFREQ_CLKS {
static struct clk *pll1_sys_clk; ARM,
static struct clk *pll1_sw_clk; PLL1_SYS,
static struct clk *step_clk; STEP,
static struct clk *pll2_pfd2_396m_clk; PLL1_SW,
PLL2_PFD2_396M,
/* clk used by i.MX6UL */ /* MX6UL requires two more clks */
static struct clk *pll2_bus_clk; PLL2_BUS,
static struct clk *secondary_sel_clk; SECONDARY_SEL,
};
#define IMX6Q_CPUFREQ_CLK_NUM 5
#define IMX6UL_CPUFREQ_CLK_NUM 7
static int num_clks;
static struct clk_bulk_data clks[] = {
{ .id = "arm" },
{ .id = "pll1_sys" },
{ .id = "step" },
{ .id = "pll1_sw" },
{ .id = "pll2_pfd2_396m" },
{ .id = "pll2_bus" },
{ .id = "secondary_sel" },
};
static struct device *cpu_dev; static struct device *cpu_dev;
static bool free_opp; static bool free_opp;
...@@ -53,7 +67,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) ...@@ -53,7 +67,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
new_freq = freq_table[index].frequency; new_freq = freq_table[index].frequency;
freq_hz = new_freq * 1000; freq_hz = new_freq * 1000;
old_freq = clk_get_rate(arm_clk) / 1000; old_freq = clk_get_rate(clks[ARM].clk) / 1000;
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz); opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
if (IS_ERR(opp)) { if (IS_ERR(opp)) {
...@@ -112,29 +126,35 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) ...@@ -112,29 +126,35 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
* voltage of 528MHz, so lower the CPU frequency to one * voltage of 528MHz, so lower the CPU frequency to one
* half before changing CPU frequency. * half before changing CPU frequency.
*/ */
clk_set_rate(arm_clk, (old_freq >> 1) * 1000); clk_set_rate(clks[ARM].clk, (old_freq >> 1) * 1000);
clk_set_parent(pll1_sw_clk, pll1_sys_clk); clk_set_parent(clks[PLL1_SW].clk, clks[PLL1_SYS].clk);
if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) if (freq_hz > clk_get_rate(clks[PLL2_PFD2_396M].clk))
clk_set_parent(secondary_sel_clk, pll2_bus_clk); clk_set_parent(clks[SECONDARY_SEL].clk,
clks[PLL2_BUS].clk);
else else
clk_set_parent(secondary_sel_clk, pll2_pfd2_396m_clk); clk_set_parent(clks[SECONDARY_SEL].clk,
clk_set_parent(step_clk, secondary_sel_clk); clks[PLL2_PFD2_396M].clk);
clk_set_parent(pll1_sw_clk, step_clk); clk_set_parent(clks[STEP].clk, clks[SECONDARY_SEL].clk);
clk_set_parent(clks[PLL1_SW].clk, clks[STEP].clk);
if (freq_hz > clk_get_rate(clks[PLL2_BUS].clk)) {
clk_set_rate(clks[PLL1_SYS].clk, new_freq * 1000);
clk_set_parent(clks[PLL1_SW].clk, clks[PLL1_SYS].clk);
}
} else { } else {
clk_set_parent(step_clk, pll2_pfd2_396m_clk); clk_set_parent(clks[STEP].clk, clks[PLL2_PFD2_396M].clk);
clk_set_parent(pll1_sw_clk, step_clk); clk_set_parent(clks[PLL1_SW].clk, clks[STEP].clk);
if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) { if (freq_hz > clk_get_rate(clks[PLL2_PFD2_396M].clk)) {
clk_set_rate(pll1_sys_clk, new_freq * 1000); clk_set_rate(clks[PLL1_SYS].clk, new_freq * 1000);
clk_set_parent(pll1_sw_clk, pll1_sys_clk); clk_set_parent(clks[PLL1_SW].clk, clks[PLL1_SYS].clk);
} else { } else {
/* pll1_sys needs to be enabled for divider rate change to work. */ /* pll1_sys needs to be enabled for divider rate change to work. */
pll1_sys_temp_enabled = true; pll1_sys_temp_enabled = true;
clk_prepare_enable(pll1_sys_clk); clk_prepare_enable(clks[PLL1_SYS].clk);
} }
} }
/* Ensure the arm clock divider is what we expect */ /* Ensure the arm clock divider is what we expect */
ret = clk_set_rate(arm_clk, new_freq * 1000); ret = clk_set_rate(clks[ARM].clk, new_freq * 1000);
if (ret) { if (ret) {
dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); dev_err(cpu_dev, "failed to set clock rate: %d\n", ret);
regulator_set_voltage_tol(arm_reg, volt_old, 0); regulator_set_voltage_tol(arm_reg, volt_old, 0);
...@@ -143,7 +163,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) ...@@ -143,7 +163,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
/* PLL1 is only needed until after ARM-PODF is set. */ /* PLL1 is only needed until after ARM-PODF is set. */
if (pll1_sys_temp_enabled) if (pll1_sys_temp_enabled)
clk_disable_unprepare(pll1_sys_clk); clk_disable_unprepare(clks[PLL1_SYS].clk);
/* scaling down? scale voltage after frequency */ /* scaling down? scale voltage after frequency */
if (new_freq < old_freq) { if (new_freq < old_freq) {
...@@ -174,7 +194,7 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy) ...@@ -174,7 +194,7 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy)
{ {
int ret; int ret;
policy->clk = arm_clk; policy->clk = clks[ARM].clk;
ret = cpufreq_generic_init(policy, freq_table, transition_latency); ret = cpufreq_generic_init(policy, freq_table, transition_latency);
policy->suspend_freq = policy->max; policy->suspend_freq = policy->max;
...@@ -244,6 +264,43 @@ static void imx6q_opp_check_speed_grading(struct device *dev) ...@@ -244,6 +264,43 @@ static void imx6q_opp_check_speed_grading(struct device *dev)
of_node_put(np); of_node_put(np);
} }
#define OCOTP_CFG3_6UL_SPEED_696MHZ 0x2
static void imx6ul_opp_check_speed_grading(struct device *dev)
{
struct device_node *np;
void __iomem *base;
u32 val;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-ocotp");
if (!np)
return;
base = of_iomap(np, 0);
if (!base) {
dev_err(dev, "failed to map ocotp\n");
goto put_node;
}
/*
* Speed GRADING[1:0] defines the max speed of ARM:
* 2b'00: Reserved;
* 2b'01: 528000000Hz;
* 2b'10: 696000000Hz;
* 2b'11: Reserved;
* We need to set the max speed of ARM according to fuse map.
*/
val = readl_relaxed(base + OCOTP_CFG3);
val >>= OCOTP_CFG3_SPEED_SHIFT;
val &= 0x3;
if (val != OCOTP_CFG3_6UL_SPEED_696MHZ)
if (dev_pm_opp_disable(dev, 696000000))
dev_warn(dev, "failed to disable 696MHz OPP\n");
iounmap(base);
put_node:
of_node_put(np);
}
static int imx6q_cpufreq_probe(struct platform_device *pdev) static int imx6q_cpufreq_probe(struct platform_device *pdev)
{ {
struct device_node *np; struct device_node *np;
...@@ -266,28 +323,15 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ...@@ -266,28 +323,15 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
return -ENOENT; return -ENOENT;
} }
arm_clk = clk_get(cpu_dev, "arm");
pll1_sys_clk = clk_get(cpu_dev, "pll1_sys");
pll1_sw_clk = clk_get(cpu_dev, "pll1_sw");
step_clk = clk_get(cpu_dev, "step");
pll2_pfd2_396m_clk = clk_get(cpu_dev, "pll2_pfd2_396m");
if (IS_ERR(arm_clk) || IS_ERR(pll1_sys_clk) || IS_ERR(pll1_sw_clk) ||
IS_ERR(step_clk) || IS_ERR(pll2_pfd2_396m_clk)) {
dev_err(cpu_dev, "failed to get clocks\n");
ret = -ENOENT;
goto put_clk;
}
if (of_machine_is_compatible("fsl,imx6ul") || if (of_machine_is_compatible("fsl,imx6ul") ||
of_machine_is_compatible("fsl,imx6ull")) { of_machine_is_compatible("fsl,imx6ull"))
pll2_bus_clk = clk_get(cpu_dev, "pll2_bus"); num_clks = IMX6UL_CPUFREQ_CLK_NUM;
secondary_sel_clk = clk_get(cpu_dev, "secondary_sel"); else
if (IS_ERR(pll2_bus_clk) || IS_ERR(secondary_sel_clk)) { num_clks = IMX6Q_CPUFREQ_CLK_NUM;
dev_err(cpu_dev, "failed to get clocks specific to imx6ul\n");
ret = -ENOENT; ret = clk_bulk_get(cpu_dev, num_clks, clks);
goto put_clk; if (ret)
} goto put_node;
}
arm_reg = regulator_get(cpu_dev, "arm"); arm_reg = regulator_get(cpu_dev, "arm");
pu_reg = regulator_get_optional(cpu_dev, "pu"); pu_reg = regulator_get_optional(cpu_dev, "pu");
...@@ -311,7 +355,10 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ...@@ -311,7 +355,10 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
goto put_reg; goto put_reg;
} }
imx6q_opp_check_speed_grading(cpu_dev); if (of_machine_is_compatible("fsl,imx6ul"))
imx6ul_opp_check_speed_grading(cpu_dev);
else
imx6q_opp_check_speed_grading(cpu_dev);
/* Because we have added the OPPs here, we must free them */ /* Because we have added the OPPs here, we must free them */
free_opp = true; free_opp = true;
...@@ -424,22 +471,11 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ...@@ -424,22 +471,11 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
regulator_put(pu_reg); regulator_put(pu_reg);
if (!IS_ERR(soc_reg)) if (!IS_ERR(soc_reg))
regulator_put(soc_reg); regulator_put(soc_reg);
put_clk:
if (!IS_ERR(arm_clk)) clk_bulk_put(num_clks, clks);
clk_put(arm_clk); put_node:
if (!IS_ERR(pll1_sys_clk))
clk_put(pll1_sys_clk);
if (!IS_ERR(pll1_sw_clk))
clk_put(pll1_sw_clk);
if (!IS_ERR(step_clk))
clk_put(step_clk);
if (!IS_ERR(pll2_pfd2_396m_clk))
clk_put(pll2_pfd2_396m_clk);
if (!IS_ERR(pll2_bus_clk))
clk_put(pll2_bus_clk);
if (!IS_ERR(secondary_sel_clk))
clk_put(secondary_sel_clk);
of_node_put(np); of_node_put(np);
return ret; return ret;
} }
...@@ -453,13 +489,8 @@ static int imx6q_cpufreq_remove(struct platform_device *pdev) ...@@ -453,13 +489,8 @@ static int imx6q_cpufreq_remove(struct platform_device *pdev)
if (!IS_ERR(pu_reg)) if (!IS_ERR(pu_reg))
regulator_put(pu_reg); regulator_put(pu_reg);
regulator_put(soc_reg); regulator_put(soc_reg);
clk_put(arm_clk);
clk_put(pll1_sys_clk); clk_bulk_put(num_clks, clks);
clk_put(pll1_sw_clk);
clk_put(step_clk);
clk_put(pll2_pfd2_396m_clk);
clk_put(pll2_bus_clk);
clk_put(secondary_sel_clk);
return 0; return 0;
} }
......
...@@ -1595,15 +1595,6 @@ static const struct pstate_funcs knl_funcs = { ...@@ -1595,15 +1595,6 @@ static const struct pstate_funcs knl_funcs = {
.get_val = core_get_val, .get_val = core_get_val,
}; };
static const struct pstate_funcs bxt_funcs = {
.get_max = core_get_max_pstate,
.get_max_physical = core_get_max_pstate_physical,
.get_min = core_get_min_pstate,
.get_turbo = core_get_turbo_pstate,
.get_scaling = core_get_scaling,
.get_val = core_get_val,
};
#define ICPU(model, policy) \ #define ICPU(model, policy) \
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_APERFMPERF,\ { X86_VENDOR_INTEL, 6, model, X86_FEATURE_APERFMPERF,\
(unsigned long)&policy } (unsigned long)&policy }
...@@ -1627,8 +1618,9 @@ static const struct x86_cpu_id intel_pstate_cpu_ids[] = { ...@@ -1627,8 +1618,9 @@ static const struct x86_cpu_id intel_pstate_cpu_ids[] = {
ICPU(INTEL_FAM6_BROADWELL_XEON_D, core_funcs), ICPU(INTEL_FAM6_BROADWELL_XEON_D, core_funcs),
ICPU(INTEL_FAM6_XEON_PHI_KNL, knl_funcs), ICPU(INTEL_FAM6_XEON_PHI_KNL, knl_funcs),
ICPU(INTEL_FAM6_XEON_PHI_KNM, knl_funcs), ICPU(INTEL_FAM6_XEON_PHI_KNM, knl_funcs),
ICPU(INTEL_FAM6_ATOM_GOLDMONT, bxt_funcs), ICPU(INTEL_FAM6_ATOM_GOLDMONT, core_funcs),
ICPU(INTEL_FAM6_ATOM_GEMINI_LAKE, bxt_funcs), ICPU(INTEL_FAM6_ATOM_GEMINI_LAKE, core_funcs),
ICPU(INTEL_FAM6_SKYLAKE_X, core_funcs),
{} {}
}; };
MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids); MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids);
......
...@@ -894,7 +894,7 @@ static int longhaul_cpu_init(struct cpufreq_policy *policy) ...@@ -894,7 +894,7 @@ static int longhaul_cpu_init(struct cpufreq_policy *policy)
if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0)) if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0))
longhaul_setup_voltagescaling(); longhaul_setup_voltagescaling();
policy->cpuinfo.transition_latency = 200000; /* nsec */ policy->transition_delay_us = 200000; /* usec */
return cpufreq_table_validate_and_show(policy, longhaul_table); return cpufreq_table_validate_and_show(policy, longhaul_table);
} }
......
...@@ -310,28 +310,8 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, ...@@ -310,28 +310,8 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
static void mtk_cpufreq_ready(struct cpufreq_policy *policy) static void mtk_cpufreq_ready(struct cpufreq_policy *policy)
{ {
struct mtk_cpu_dvfs_info *info = policy->driver_data; struct mtk_cpu_dvfs_info *info = policy->driver_data;
struct device_node *np = of_node_get(info->cpu_dev->of_node);
u32 capacitance = 0;
if (WARN_ON(!np)) info->cdev = of_cpufreq_cooling_register(policy);
return;
if (of_find_property(np, "#cooling-cells", NULL)) {
of_property_read_u32(np, DYNAMIC_POWER, &capacitance);
info->cdev = of_cpufreq_power_cooling_register(np,
policy, capacitance, NULL);
if (IS_ERR(info->cdev)) {
dev_err(info->cpu_dev,
"running cpufreq without cooling device: %ld\n",
PTR_ERR(info->cdev));
info->cdev = NULL;
}
}
of_node_put(np);
} }
static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
...@@ -574,6 +554,7 @@ static struct platform_driver mtk_cpufreq_platdrv = { ...@@ -574,6 +554,7 @@ static struct platform_driver mtk_cpufreq_platdrv = {
/* List of machines supported by this driver */ /* List of machines supported by this driver */
static const struct of_device_id mtk_cpufreq_machines[] __initconst = { static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
{ .compatible = "mediatek,mt2701", }, { .compatible = "mediatek,mt2701", },
{ .compatible = "mediatek,mt2712", },
{ .compatible = "mediatek,mt7622", }, { .compatible = "mediatek,mt7622", },
{ .compatible = "mediatek,mt7623", }, { .compatible = "mediatek,mt7623", },
{ .compatible = "mediatek,mt817x", }, { .compatible = "mediatek,mt817x", },
......
...@@ -76,12 +76,6 @@ static int __init armada_xp_pmsu_cpufreq_init(void) ...@@ -76,12 +76,6 @@ static int __init armada_xp_pmsu_cpufreq_init(void)
return PTR_ERR(clk); return PTR_ERR(clk);
} }
/*
* In case of a failure of dev_pm_opp_add(), we don't
* bother with cleaning up the registered OPP (there's
* no function to do so), and simply cancel the
* registration of the cpufreq device.
*/
ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk), 0); ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk), 0);
if (ret) { if (ret) {
clk_put(clk); clk_put(clk);
...@@ -91,7 +85,8 @@ static int __init armada_xp_pmsu_cpufreq_init(void) ...@@ -91,7 +85,8 @@ static int __init armada_xp_pmsu_cpufreq_init(void)
ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk) / 2, 0); ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk) / 2, 0);
if (ret) { if (ret) {
clk_put(clk); clk_put(clk);
return ret; dev_err(cpu_dev, "Failed to register OPPs\n");
goto opp_register_failed;
} }
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, ret = dev_pm_opp_set_sharing_cpus(cpu_dev,
...@@ -99,9 +94,16 @@ static int __init armada_xp_pmsu_cpufreq_init(void) ...@@ -99,9 +94,16 @@ static int __init armada_xp_pmsu_cpufreq_init(void)
if (ret) if (ret)
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
__func__, ret); __func__, ret);
clk_put(clk);
} }
platform_device_register_simple("cpufreq-dt", -1, NULL, 0); platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
return 0; return 0;
opp_register_failed:
/* As registering has failed remove all the opp for all cpus */
dev_pm_opp_cpumask_remove_table(cpu_possible_mask);
return ret;
} }
device_initcall(armada_xp_pmsu_cpufreq_init); device_initcall(armada_xp_pmsu_cpufreq_init);
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/hashtable.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#include <asm/cputhreads.h> #include <asm/cputhreads.h>
...@@ -38,14 +39,13 @@ ...@@ -38,14 +39,13 @@
#include <asm/opal.h> #include <asm/opal.h>
#include <linux/timer.h> #include <linux/timer.h>
#define POWERNV_MAX_PSTATES 256 #define POWERNV_MAX_PSTATES_ORDER 8
#define POWERNV_MAX_PSTATES (1UL << (POWERNV_MAX_PSTATES_ORDER))
#define PMSR_PSAFE_ENABLE (1UL << 30) #define PMSR_PSAFE_ENABLE (1UL << 30)
#define PMSR_SPR_EM_DISABLE (1UL << 31) #define PMSR_SPR_EM_DISABLE (1UL << 31)
#define PMSR_MAX(x) ((x >> 32) & 0xFF) #define MAX_PSTATE_SHIFT 32
#define LPSTATE_SHIFT 48 #define LPSTATE_SHIFT 48
#define GPSTATE_SHIFT 56 #define GPSTATE_SHIFT 56
#define GET_LPSTATE(x) (((x) >> LPSTATE_SHIFT) & 0xFF)
#define GET_GPSTATE(x) (((x) >> GPSTATE_SHIFT) & 0xFF)
#define MAX_RAMP_DOWN_TIME 5120 #define MAX_RAMP_DOWN_TIME 5120
/* /*
...@@ -94,6 +94,27 @@ struct global_pstate_info { ...@@ -94,6 +94,27 @@ struct global_pstate_info {
}; };
static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1];
DEFINE_HASHTABLE(pstate_revmap, POWERNV_MAX_PSTATES_ORDER);
/**
* struct pstate_idx_revmap_data: Entry in the hashmap pstate_revmap
* indexed by a function of pstate id.
*
* @pstate_id: pstate id for this entry.
*
* @cpufreq_table_idx: Index into the powernv_freqs
* cpufreq_frequency_table for frequency
* corresponding to pstate_id.
*
* @hentry: hlist_node that hooks this entry into the pstate_revmap
* hashtable
*/
struct pstate_idx_revmap_data {
u8 pstate_id;
unsigned int cpufreq_table_idx;
struct hlist_node hentry;
};
static bool rebooting, throttled, occ_reset; static bool rebooting, throttled, occ_reset;
static const char * const throttle_reason[] = { static const char * const throttle_reason[] = {
...@@ -148,39 +169,56 @@ static struct powernv_pstate_info { ...@@ -148,39 +169,56 @@ static struct powernv_pstate_info {
bool wof_enabled; bool wof_enabled;
} powernv_pstate_info; } powernv_pstate_info;
/* Use following macros for conversions between pstate_id and index */ static inline u8 extract_pstate(u64 pmsr_val, unsigned int shift)
static inline int idx_to_pstate(unsigned int i) {
return ((pmsr_val >> shift) & 0xFF);
}
#define extract_local_pstate(x) extract_pstate(x, LPSTATE_SHIFT)
#define extract_global_pstate(x) extract_pstate(x, GPSTATE_SHIFT)
#define extract_max_pstate(x) extract_pstate(x, MAX_PSTATE_SHIFT)
/* Use following functions for conversions between pstate_id and index */
/**
* idx_to_pstate : Returns the pstate id corresponding to the
* frequency in the cpufreq frequency table
* powernv_freqs indexed by @i.
*
* If @i is out of bound, this will return the pstate
* corresponding to the nominal frequency.
*/
static inline u8 idx_to_pstate(unsigned int i)
{ {
if (unlikely(i >= powernv_pstate_info.nr_pstates)) { if (unlikely(i >= powernv_pstate_info.nr_pstates)) {
pr_warn_once("index %u is out of bound\n", i); pr_warn_once("idx_to_pstate: index %u is out of bound\n", i);
return powernv_freqs[powernv_pstate_info.nominal].driver_data; return powernv_freqs[powernv_pstate_info.nominal].driver_data;
} }
return powernv_freqs[i].driver_data; return powernv_freqs[i].driver_data;
} }
static inline unsigned int pstate_to_idx(int pstate) /**
* pstate_to_idx : Returns the index in the cpufreq frequencytable
* powernv_freqs for the frequency whose corresponding
* pstate id is @pstate.
*
* If no frequency corresponding to @pstate is found,
* this will return the index of the nominal
* frequency.
*/
static unsigned int pstate_to_idx(u8 pstate)
{ {
int min = powernv_freqs[powernv_pstate_info.min].driver_data; unsigned int key = pstate % POWERNV_MAX_PSTATES;
int max = powernv_freqs[powernv_pstate_info.max].driver_data; struct pstate_idx_revmap_data *revmap_data;
if (min > 0) { hash_for_each_possible(pstate_revmap, revmap_data, hentry, key) {
if (unlikely((pstate < max) || (pstate > min))) { if (revmap_data->pstate_id == pstate)
pr_warn_once("pstate %d is out of bound\n", pstate); return revmap_data->cpufreq_table_idx;
return powernv_pstate_info.nominal;
}
} else {
if (unlikely((pstate > max) || (pstate < min))) {
pr_warn_once("pstate %d is out of bound\n", pstate);
return powernv_pstate_info.nominal;
}
} }
/*
* abs() is deliberately used so that is works with pr_warn_once("pstate_to_idx: pstate 0x%x not found\n", pstate);
* both monotonically increasing and decreasing return powernv_pstate_info.nominal;
* pstate values
*/
return abs(pstate - idx_to_pstate(powernv_pstate_info.max));
} }
static inline void reset_gpstates(struct cpufreq_policy *policy) static inline void reset_gpstates(struct cpufreq_policy *policy)
...@@ -247,7 +285,7 @@ static int init_powernv_pstates(void) ...@@ -247,7 +285,7 @@ static int init_powernv_pstates(void)
powernv_pstate_info.wof_enabled = true; powernv_pstate_info.wof_enabled = true;
next: next:
pr_info("cpufreq pstate min %d nominal %d max %d\n", pstate_min, pr_info("cpufreq pstate min 0x%x nominal 0x%x max 0x%x\n", pstate_min,
pstate_nominal, pstate_max); pstate_nominal, pstate_max);
pr_info("Workload Optimized Frequency is %s in the platform\n", pr_info("Workload Optimized Frequency is %s in the platform\n",
(powernv_pstate_info.wof_enabled) ? "enabled" : "disabled"); (powernv_pstate_info.wof_enabled) ? "enabled" : "disabled");
...@@ -278,19 +316,30 @@ static int init_powernv_pstates(void) ...@@ -278,19 +316,30 @@ static int init_powernv_pstates(void)
powernv_pstate_info.nr_pstates = nr_pstates; powernv_pstate_info.nr_pstates = nr_pstates;
pr_debug("NR PStates %d\n", nr_pstates); pr_debug("NR PStates %d\n", nr_pstates);
for (i = 0; i < nr_pstates; i++) { for (i = 0; i < nr_pstates; i++) {
u32 id = be32_to_cpu(pstate_ids[i]); u32 id = be32_to_cpu(pstate_ids[i]);
u32 freq = be32_to_cpu(pstate_freqs[i]); u32 freq = be32_to_cpu(pstate_freqs[i]);
struct pstate_idx_revmap_data *revmap_data;
unsigned int key;
pr_debug("PState id %d freq %d MHz\n", id, freq); pr_debug("PState id %d freq %d MHz\n", id, freq);
powernv_freqs[i].frequency = freq * 1000; /* kHz */ powernv_freqs[i].frequency = freq * 1000; /* kHz */
powernv_freqs[i].driver_data = id; powernv_freqs[i].driver_data = id & 0xFF;
revmap_data = (struct pstate_idx_revmap_data *)
kmalloc(sizeof(*revmap_data), GFP_KERNEL);
revmap_data->pstate_id = id & 0xFF;
revmap_data->cpufreq_table_idx = i;
key = (revmap_data->pstate_id) % POWERNV_MAX_PSTATES;
hash_add(pstate_revmap, &revmap_data->hentry, key);
if (id == pstate_max) if (id == pstate_max)
powernv_pstate_info.max = i; powernv_pstate_info.max = i;
else if (id == pstate_nominal) if (id == pstate_nominal)
powernv_pstate_info.nominal = i; powernv_pstate_info.nominal = i;
else if (id == pstate_min) if (id == pstate_min)
powernv_pstate_info.min = i; powernv_pstate_info.min = i;
if (powernv_pstate_info.wof_enabled && id == pstate_turbo) { if (powernv_pstate_info.wof_enabled && id == pstate_turbo) {
...@@ -307,14 +356,13 @@ static int init_powernv_pstates(void) ...@@ -307,14 +356,13 @@ static int init_powernv_pstates(void)
} }
/* Returns the CPU frequency corresponding to the pstate_id. */ /* Returns the CPU frequency corresponding to the pstate_id. */
static unsigned int pstate_id_to_freq(int pstate_id) static unsigned int pstate_id_to_freq(u8 pstate_id)
{ {
int i; int i;
i = pstate_to_idx(pstate_id); i = pstate_to_idx(pstate_id);
if (i >= powernv_pstate_info.nr_pstates || i < 0) { if (i >= powernv_pstate_info.nr_pstates || i < 0) {
pr_warn("PState id %d outside of PState table, " pr_warn("PState id 0x%x outside of PState table, reporting nominal id 0x%x instead\n",
"reporting nominal id %d instead\n",
pstate_id, idx_to_pstate(powernv_pstate_info.nominal)); pstate_id, idx_to_pstate(powernv_pstate_info.nominal));
i = powernv_pstate_info.nominal; i = powernv_pstate_info.nominal;
} }
...@@ -420,8 +468,8 @@ static inline void set_pmspr(unsigned long sprn, unsigned long val) ...@@ -420,8 +468,8 @@ static inline void set_pmspr(unsigned long sprn, unsigned long val)
*/ */
struct powernv_smp_call_data { struct powernv_smp_call_data {
unsigned int freq; unsigned int freq;
int pstate_id; u8 pstate_id;
int gpstate_id; u8 gpstate_id;
}; };
/* /*
...@@ -438,22 +486,15 @@ struct powernv_smp_call_data { ...@@ -438,22 +486,15 @@ struct powernv_smp_call_data {
static void powernv_read_cpu_freq(void *arg) static void powernv_read_cpu_freq(void *arg)
{ {
unsigned long pmspr_val; unsigned long pmspr_val;
s8 local_pstate_id;
struct powernv_smp_call_data *freq_data = arg; struct powernv_smp_call_data *freq_data = arg;
pmspr_val = get_pmspr(SPRN_PMSR); pmspr_val = get_pmspr(SPRN_PMSR);
freq_data->pstate_id = extract_local_pstate(pmspr_val);
/*
* The local pstate id corresponds bits 48..55 in the PMSR.
* Note: Watch out for the sign!
*/
local_pstate_id = (pmspr_val >> 48) & 0xFF;
freq_data->pstate_id = local_pstate_id;
freq_data->freq = pstate_id_to_freq(freq_data->pstate_id); freq_data->freq = pstate_id_to_freq(freq_data->pstate_id);
pr_debug("cpu %d pmsr %016lX pstate_id %d frequency %d kHz\n", pr_debug("cpu %d pmsr %016lX pstate_id 0x%x frequency %d kHz\n",
raw_smp_processor_id(), pmspr_val, freq_data->pstate_id, raw_smp_processor_id(), pmspr_val, freq_data->pstate_id,
freq_data->freq); freq_data->freq);
} }
/* /*
...@@ -515,21 +556,21 @@ static void powernv_cpufreq_throttle_check(void *data) ...@@ -515,21 +556,21 @@ static void powernv_cpufreq_throttle_check(void *data)
struct chip *chip; struct chip *chip;
unsigned int cpu = smp_processor_id(); unsigned int cpu = smp_processor_id();
unsigned long pmsr; unsigned long pmsr;
int pmsr_pmax; u8 pmsr_pmax;
unsigned int pmsr_pmax_idx; unsigned int pmsr_pmax_idx;
pmsr = get_pmspr(SPRN_PMSR); pmsr = get_pmspr(SPRN_PMSR);
chip = this_cpu_read(chip_info); chip = this_cpu_read(chip_info);
/* Check for Pmax Capping */ /* Check for Pmax Capping */
pmsr_pmax = (s8)PMSR_MAX(pmsr); pmsr_pmax = extract_max_pstate(pmsr);
pmsr_pmax_idx = pstate_to_idx(pmsr_pmax); pmsr_pmax_idx = pstate_to_idx(pmsr_pmax);
if (pmsr_pmax_idx != powernv_pstate_info.max) { if (pmsr_pmax_idx != powernv_pstate_info.max) {
if (chip->throttled) if (chip->throttled)
goto next; goto next;
chip->throttled = true; chip->throttled = true;
if (pmsr_pmax_idx > powernv_pstate_info.nominal) { if (pmsr_pmax_idx > powernv_pstate_info.nominal) {
pr_warn_once("CPU %d on Chip %u has Pmax(%d) reduced below nominal frequency(%d)\n", pr_warn_once("CPU %d on Chip %u has Pmax(0x%x) reduced below that of nominal frequency(0x%x)\n",
cpu, chip->id, pmsr_pmax, cpu, chip->id, pmsr_pmax,
idx_to_pstate(powernv_pstate_info.nominal)); idx_to_pstate(powernv_pstate_info.nominal));
chip->throttle_sub_turbo++; chip->throttle_sub_turbo++;
...@@ -645,8 +686,8 @@ void gpstate_timer_handler(struct timer_list *t) ...@@ -645,8 +686,8 @@ void gpstate_timer_handler(struct timer_list *t)
* value. Hence, read from PMCR to get correct data. * value. Hence, read from PMCR to get correct data.
*/ */
val = get_pmspr(SPRN_PMCR); val = get_pmspr(SPRN_PMCR);
freq_data.gpstate_id = (s8)GET_GPSTATE(val); freq_data.gpstate_id = extract_global_pstate(val);
freq_data.pstate_id = (s8)GET_LPSTATE(val); freq_data.pstate_id = extract_local_pstate(val);
if (freq_data.gpstate_id == freq_data.pstate_id) { if (freq_data.gpstate_id == freq_data.pstate_id) {
reset_gpstates(policy); reset_gpstates(policy);
spin_unlock(&gpstates->gpstate_lock); spin_unlock(&gpstates->gpstate_lock);
......
...@@ -275,20 +275,8 @@ static int qoriq_cpufreq_target(struct cpufreq_policy *policy, ...@@ -275,20 +275,8 @@ static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
static void qoriq_cpufreq_ready(struct cpufreq_policy *policy) static void qoriq_cpufreq_ready(struct cpufreq_policy *policy)
{ {
struct cpu_data *cpud = policy->driver_data; struct cpu_data *cpud = policy->driver_data;
struct device_node *np = of_get_cpu_node(policy->cpu, NULL);
if (of_find_property(np, "#cooling-cells", NULL)) { cpud->cdev = of_cpufreq_cooling_register(policy);
cpud->cdev = of_cpufreq_cooling_register(np, policy);
if (IS_ERR(cpud->cdev) && PTR_ERR(cpud->cdev) != -ENOSYS) {
pr_err("cpu%d is not running as cooling device: %ld\n",
policy->cpu, PTR_ERR(cpud->cdev));
cpud->cdev = NULL;
}
}
of_node_put(np);
} }
static struct cpufreq_driver qoriq_cpufreq_driver = { static struct cpufreq_driver qoriq_cpufreq_driver = {
......
...@@ -18,27 +18,89 @@ ...@@ -18,27 +18,89 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/cpu_cooling.h>
#include <linux/export.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/of_platform.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/scpi_protocol.h> #include <linux/scpi_protocol.h>
#include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include "arm_big_little.h" struct scpi_data {
struct clk *clk;
struct device *cpu_dev;
struct thermal_cooling_device *cdev;
};
static struct scpi_ops *scpi_ops; static struct scpi_ops *scpi_ops;
static int scpi_get_transition_latency(struct device *cpu_dev) static unsigned int scpi_cpufreq_get_rate(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
struct scpi_data *priv = policy->driver_data;
unsigned long rate = clk_get_rate(priv->clk);
return rate / 1000;
}
static int
scpi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
{
struct scpi_data *priv = policy->driver_data;
u64 rate = policy->freq_table[index].frequency * 1000;
int ret;
ret = clk_set_rate(priv->clk, rate);
if (!ret && (clk_get_rate(priv->clk) != rate))
ret = -EIO;
return ret;
}
static int
scpi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
{ {
return scpi_ops->get_transition_latency(cpu_dev); int cpu, domain, tdomain;
struct device *tcpu_dev;
domain = scpi_ops->device_domain_id(cpu_dev);
if (domain < 0)
return domain;
for_each_possible_cpu(cpu) {
if (cpu == cpu_dev->id)
continue;
tcpu_dev = get_cpu_device(cpu);
if (!tcpu_dev)
continue;
tdomain = scpi_ops->device_domain_id(tcpu_dev);
if (tdomain == domain)
cpumask_set_cpu(cpu, cpumask);
}
return 0;
} }
static int scpi_init_opp_table(const struct cpumask *cpumask) static int scpi_cpufreq_init(struct cpufreq_policy *policy)
{ {
int ret; int ret;
struct device *cpu_dev = get_cpu_device(cpumask_first(cpumask)); unsigned int latency;
struct device *cpu_dev;
struct scpi_data *priv;
struct cpufreq_frequency_table *freq_table;
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
pr_err("failed to get cpu%d device\n", policy->cpu);
return -ENODEV;
}
ret = scpi_ops->add_opps_to_device(cpu_dev); ret = scpi_ops->add_opps_to_device(cpu_dev);
if (ret) { if (ret) {
...@@ -46,32 +108,133 @@ static int scpi_init_opp_table(const struct cpumask *cpumask) ...@@ -46,32 +108,133 @@ static int scpi_init_opp_table(const struct cpumask *cpumask)
return ret; return ret;
} }
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, cpumask); ret = scpi_get_sharing_cpus(cpu_dev, policy->cpus);
if (ret) if (ret) {
dev_warn(cpu_dev, "failed to get sharing cpumask\n");
return ret;
}
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
if (ret) {
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
__func__, ret); __func__, ret);
return ret;
}
ret = dev_pm_opp_get_opp_count(cpu_dev);
if (ret <= 0) {
dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
ret = -EPROBE_DEFER;
goto out_free_opp;
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto out_free_opp;
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
goto out_free_priv;
}
priv->cpu_dev = cpu_dev;
priv->clk = clk_get(cpu_dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d\n",
__func__, cpu_dev->id);
goto out_free_cpufreq_table;
}
policy->driver_data = priv;
ret = cpufreq_table_validate_and_show(policy, freq_table);
if (ret) {
dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__,
ret);
goto out_put_clk;
}
/* scpi allows DVFS request for any domain from any CPU */
policy->dvfs_possible_from_any_cpu = true;
latency = scpi_ops->get_transition_latency(cpu_dev);
if (!latency)
latency = CPUFREQ_ETERNAL;
policy->cpuinfo.transition_latency = latency;
policy->fast_switch_possible = false;
return 0;
out_put_clk:
clk_put(priv->clk);
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_priv:
kfree(priv);
out_free_opp:
dev_pm_opp_cpumask_remove_table(policy->cpus);
return ret; return ret;
} }
static const struct cpufreq_arm_bL_ops scpi_cpufreq_ops = { static int scpi_cpufreq_exit(struct cpufreq_policy *policy)
.name = "scpi", {
.get_transition_latency = scpi_get_transition_latency, struct scpi_data *priv = policy->driver_data;
.init_opp_table = scpi_init_opp_table,
.free_opp_table = dev_pm_opp_cpumask_remove_table, cpufreq_cooling_unregister(priv->cdev);
clk_put(priv->clk);
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
kfree(priv);
dev_pm_opp_cpumask_remove_table(policy->related_cpus);
return 0;
}
static void scpi_cpufreq_ready(struct cpufreq_policy *policy)
{
struct scpi_data *priv = policy->driver_data;
struct thermal_cooling_device *cdev;
cdev = of_cpufreq_cooling_register(policy);
if (!IS_ERR(cdev))
priv->cdev = cdev;
}
static struct cpufreq_driver scpi_cpufreq_driver = {
.name = "scpi-cpufreq",
.flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify,
.attr = cpufreq_generic_attr,
.get = scpi_cpufreq_get_rate,
.init = scpi_cpufreq_init,
.exit = scpi_cpufreq_exit,
.ready = scpi_cpufreq_ready,
.target_index = scpi_cpufreq_set_target,
}; };
static int scpi_cpufreq_probe(struct platform_device *pdev) static int scpi_cpufreq_probe(struct platform_device *pdev)
{ {
int ret;
scpi_ops = get_scpi_ops(); scpi_ops = get_scpi_ops();
if (!scpi_ops) if (!scpi_ops)
return -EIO; return -EIO;
return bL_cpufreq_register(&scpi_cpufreq_ops); ret = cpufreq_register_driver(&scpi_cpufreq_driver);
if (ret)
dev_err(&pdev->dev, "%s: registering cpufreq failed, err: %d\n",
__func__, ret);
return ret;
} }
static int scpi_cpufreq_remove(struct platform_device *pdev) static int scpi_cpufreq_remove(struct platform_device *pdev)
{ {
bL_cpufreq_unregister(&scpi_cpufreq_ops); cpufreq_unregister_driver(&scpi_cpufreq_driver);
scpi_ops = NULL; scpi_ops = NULL;
return 0; return 0;
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
...@@ -50,6 +51,7 @@ struct ti_cpufreq_soc_data { ...@@ -50,6 +51,7 @@ struct ti_cpufreq_soc_data {
unsigned long efuse_mask; unsigned long efuse_mask;
unsigned long efuse_shift; unsigned long efuse_shift;
unsigned long rev_offset; unsigned long rev_offset;
bool multi_regulator;
}; };
struct ti_cpufreq_data { struct ti_cpufreq_data {
...@@ -57,6 +59,7 @@ struct ti_cpufreq_data { ...@@ -57,6 +59,7 @@ struct ti_cpufreq_data {
struct device_node *opp_node; struct device_node *opp_node;
struct regmap *syscon; struct regmap *syscon;
const struct ti_cpufreq_soc_data *soc_data; const struct ti_cpufreq_soc_data *soc_data;
struct opp_table *opp_table;
}; };
static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data, static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data,
...@@ -95,6 +98,7 @@ static struct ti_cpufreq_soc_data am3x_soc_data = { ...@@ -95,6 +98,7 @@ static struct ti_cpufreq_soc_data am3x_soc_data = {
.efuse_offset = 0x07fc, .efuse_offset = 0x07fc,
.efuse_mask = 0x1fff, .efuse_mask = 0x1fff,
.rev_offset = 0x600, .rev_offset = 0x600,
.multi_regulator = false,
}; };
static struct ti_cpufreq_soc_data am4x_soc_data = { static struct ti_cpufreq_soc_data am4x_soc_data = {
...@@ -103,6 +107,7 @@ static struct ti_cpufreq_soc_data am4x_soc_data = { ...@@ -103,6 +107,7 @@ static struct ti_cpufreq_soc_data am4x_soc_data = {
.efuse_offset = 0x0610, .efuse_offset = 0x0610,
.efuse_mask = 0x3f, .efuse_mask = 0x3f,
.rev_offset = 0x600, .rev_offset = 0x600,
.multi_regulator = false,
}; };
static struct ti_cpufreq_soc_data dra7_soc_data = { static struct ti_cpufreq_soc_data dra7_soc_data = {
...@@ -111,6 +116,7 @@ static struct ti_cpufreq_soc_data dra7_soc_data = { ...@@ -111,6 +116,7 @@ static struct ti_cpufreq_soc_data dra7_soc_data = {
.efuse_mask = 0xf80000, .efuse_mask = 0xf80000,
.efuse_shift = 19, .efuse_shift = 19,
.rev_offset = 0x204, .rev_offset = 0x204,
.multi_regulator = true,
}; };
/** /**
...@@ -195,12 +201,14 @@ static const struct of_device_id ti_cpufreq_of_match[] = { ...@@ -195,12 +201,14 @@ static const struct of_device_id ti_cpufreq_of_match[] = {
{}, {},
}; };
static int ti_cpufreq_init(void) static int ti_cpufreq_probe(struct platform_device *pdev)
{ {
u32 version[VERSION_COUNT]; u32 version[VERSION_COUNT];
struct device_node *np; struct device_node *np;
const struct of_device_id *match; const struct of_device_id *match;
struct opp_table *ti_opp_table;
struct ti_cpufreq_data *opp_data; struct ti_cpufreq_data *opp_data;
const char * const reg_names[] = {"vdd", "vbb"};
int ret; int ret;
np = of_find_node_by_path("/"); np = of_find_node_by_path("/");
...@@ -247,16 +255,29 @@ static int ti_cpufreq_init(void) ...@@ -247,16 +255,29 @@ static int ti_cpufreq_init(void)
if (ret) if (ret)
goto fail_put_node; goto fail_put_node;
ret = PTR_ERR_OR_ZERO(dev_pm_opp_set_supported_hw(opp_data->cpu_dev, ti_opp_table = dev_pm_opp_set_supported_hw(opp_data->cpu_dev,
version, VERSION_COUNT)); version, VERSION_COUNT);
if (ret) { if (IS_ERR(ti_opp_table)) {
dev_err(opp_data->cpu_dev, dev_err(opp_data->cpu_dev,
"Failed to set supported hardware\n"); "Failed to set supported hardware\n");
ret = PTR_ERR(ti_opp_table);
goto fail_put_node; goto fail_put_node;
} }
of_node_put(opp_data->opp_node); opp_data->opp_table = ti_opp_table;
if (opp_data->soc_data->multi_regulator) {
ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev,
reg_names,
ARRAY_SIZE(reg_names));
if (IS_ERR(ti_opp_table)) {
dev_pm_opp_put_supported_hw(opp_data->opp_table);
ret = PTR_ERR(ti_opp_table);
goto fail_put_node;
}
}
of_node_put(opp_data->opp_node);
register_cpufreq_dt: register_cpufreq_dt:
platform_device_register_simple("cpufreq-dt", -1, NULL, 0); platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
...@@ -269,4 +290,22 @@ static int ti_cpufreq_init(void) ...@@ -269,4 +290,22 @@ static int ti_cpufreq_init(void)
return ret; return ret;
} }
device_initcall(ti_cpufreq_init);
static int ti_cpufreq_init(void)
{
platform_device_register_simple("ti-cpufreq", -1, NULL, 0);
return 0;
}
module_init(ti_cpufreq_init);
static struct platform_driver ti_cpufreq_driver = {
.probe = ti_cpufreq_probe,
.driver = {
.name = "ti-cpufreq",
},
};
module_platform_driver(ti_cpufreq_driver);
MODULE_DESCRIPTION("TI CPUFreq/OPP hw-supported driver");
MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
MODULE_LICENSE("GPL v2");
...@@ -36,14 +36,15 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str) ...@@ -36,14 +36,15 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
/** /**
* cpuidle_switch_governor - changes the governor * cpuidle_switch_governor - changes the governor
* @gov: the new target governor * @gov: the new target governor
*
* NOTE: "gov" can be NULL to specify disabled
* Must be called with cpuidle_lock acquired. * Must be called with cpuidle_lock acquired.
*/ */
int cpuidle_switch_governor(struct cpuidle_governor *gov) int cpuidle_switch_governor(struct cpuidle_governor *gov)
{ {
struct cpuidle_device *dev; struct cpuidle_device *dev;
if (!gov)
return -EINVAL;
if (gov == cpuidle_curr_governor) if (gov == cpuidle_curr_governor)
return 0; return 0;
......
...@@ -737,7 +737,7 @@ struct devfreq *devm_devfreq_add_device(struct device *dev, ...@@ -737,7 +737,7 @@ struct devfreq *devm_devfreq_add_device(struct device *dev,
devfreq = devfreq_add_device(dev, profile, governor_name, data); devfreq = devfreq_add_device(dev, profile, governor_name, data);
if (IS_ERR(devfreq)) { if (IS_ERR(devfreq)) {
devres_free(ptr); devres_free(ptr);
return ERR_PTR(-ENOMEM); return devfreq;
} }
*ptr = devfreq; *ptr = devfreq;
...@@ -996,7 +996,8 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, ...@@ -996,7 +996,8 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
if (df->governor == governor) { if (df->governor == governor) {
ret = 0; ret = 0;
goto out; goto out;
} else if (df->governor->immutable || governor->immutable) { } else if ((df->governor && df->governor->immutable) ||
governor->immutable) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
......
...@@ -1615,22 +1615,6 @@ static struct dma_chan *rcar_dmac_of_xlate(struct of_phandle_args *dma_spec, ...@@ -1615,22 +1615,6 @@ static struct dma_chan *rcar_dmac_of_xlate(struct of_phandle_args *dma_spec,
* Power management * Power management
*/ */
#ifdef CONFIG_PM_SLEEP
static int rcar_dmac_sleep_suspend(struct device *dev)
{
/*
* TODO: Wait for the current transfer to complete and stop the device.
*/
return 0;
}
static int rcar_dmac_sleep_resume(struct device *dev)
{
/* TODO: Resume transfers, if any. */
return 0;
}
#endif
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int rcar_dmac_runtime_suspend(struct device *dev) static int rcar_dmac_runtime_suspend(struct device *dev)
{ {
...@@ -1646,7 +1630,13 @@ static int rcar_dmac_runtime_resume(struct device *dev) ...@@ -1646,7 +1630,13 @@ static int rcar_dmac_runtime_resume(struct device *dev)
#endif #endif
static const struct dev_pm_ops rcar_dmac_pm = { static const struct dev_pm_ops rcar_dmac_pm = {
SET_SYSTEM_SLEEP_PM_OPS(rcar_dmac_sleep_suspend, rcar_dmac_sleep_resume) /*
* TODO for system sleep/resume:
* - Wait for the current transfer to complete and stop the device,
* - Resume transfers, if any.
*/
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(rcar_dmac_runtime_suspend, rcar_dmac_runtime_resume, SET_RUNTIME_PM_OPS(rcar_dmac_runtime_suspend, rcar_dmac_runtime_resume,
NULL) NULL)
}; };
......
...@@ -77,8 +77,8 @@ static int psci_ops_check(void) ...@@ -77,8 +77,8 @@ static int psci_ops_check(void)
return 0; return 0;
} }
static int find_clusters(const struct cpumask *cpus, static int find_cpu_groups(const struct cpumask *cpus,
const struct cpumask **clusters) const struct cpumask **cpu_groups)
{ {
unsigned int nb = 0; unsigned int nb = 0;
cpumask_var_t tmp; cpumask_var_t tmp;
...@@ -88,11 +88,11 @@ static int find_clusters(const struct cpumask *cpus, ...@@ -88,11 +88,11 @@ static int find_clusters(const struct cpumask *cpus,
cpumask_copy(tmp, cpus); cpumask_copy(tmp, cpus);
while (!cpumask_empty(tmp)) { while (!cpumask_empty(tmp)) {
const struct cpumask *cluster = const struct cpumask *cpu_group =
topology_core_cpumask(cpumask_any(tmp)); topology_core_cpumask(cpumask_any(tmp));
clusters[nb++] = cluster; cpu_groups[nb++] = cpu_group;
cpumask_andnot(tmp, tmp, cluster); cpumask_andnot(tmp, tmp, cpu_group);
} }
free_cpumask_var(tmp); free_cpumask_var(tmp);
...@@ -170,24 +170,24 @@ static int hotplug_tests(void) ...@@ -170,24 +170,24 @@ static int hotplug_tests(void)
{ {
int err; int err;
cpumask_var_t offlined_cpus; cpumask_var_t offlined_cpus;
int i, nb_cluster; int i, nb_cpu_group;
const struct cpumask **clusters; const struct cpumask **cpu_groups;
char *page_buf; char *page_buf;
err = -ENOMEM; err = -ENOMEM;
if (!alloc_cpumask_var(&offlined_cpus, GFP_KERNEL)) if (!alloc_cpumask_var(&offlined_cpus, GFP_KERNEL))
return err; return err;
/* We may have up to nb_available_cpus clusters. */ /* We may have up to nb_available_cpus cpu_groups. */
clusters = kmalloc_array(nb_available_cpus, sizeof(*clusters), cpu_groups = kmalloc_array(nb_available_cpus, sizeof(*cpu_groups),
GFP_KERNEL); GFP_KERNEL);
if (!clusters) if (!cpu_groups)
goto out_free_cpus; goto out_free_cpus;
page_buf = (char *)__get_free_page(GFP_KERNEL); page_buf = (char *)__get_free_page(GFP_KERNEL);
if (!page_buf) if (!page_buf)
goto out_free_clusters; goto out_free_cpu_groups;
err = 0; err = 0;
nb_cluster = find_clusters(cpu_online_mask, clusters); nb_cpu_group = find_cpu_groups(cpu_online_mask, cpu_groups);
/* /*
* Of course the last CPU cannot be powered down and cpu_down() should * Of course the last CPU cannot be powered down and cpu_down() should
...@@ -197,24 +197,22 @@ static int hotplug_tests(void) ...@@ -197,24 +197,22 @@ static int hotplug_tests(void)
err += down_and_up_cpus(cpu_online_mask, offlined_cpus); err += down_and_up_cpus(cpu_online_mask, offlined_cpus);
/* /*
* Take down CPUs by cluster this time. When the last CPU is turned * Take down CPUs by cpu group this time. When the last CPU is turned
* off, the cluster itself should shut down. * off, the cpu group itself should shut down.
*/ */
for (i = 0; i < nb_cluster; ++i) { for (i = 0; i < nb_cpu_group; ++i) {
int cluster_id =
topology_physical_package_id(cpumask_any(clusters[i]));
ssize_t len = cpumap_print_to_pagebuf(true, page_buf, ssize_t len = cpumap_print_to_pagebuf(true, page_buf,
clusters[i]); cpu_groups[i]);
/* Remove trailing newline. */ /* Remove trailing newline. */
page_buf[len - 1] = '\0'; page_buf[len - 1] = '\0';
pr_info("Trying to turn off and on again cluster %d " pr_info("Trying to turn off and on again group %d (CPUs %s)\n",
"(CPUs %s)\n", cluster_id, page_buf); i, page_buf);
err += down_and_up_cpus(clusters[i], offlined_cpus); err += down_and_up_cpus(cpu_groups[i], offlined_cpus);
} }
free_page((unsigned long)page_buf); free_page((unsigned long)page_buf);
out_free_clusters: out_free_cpu_groups:
kfree(clusters); kfree(cpu_groups);
out_free_cpus: out_free_cpus:
free_cpumask_var(offlined_cpus); free_cpumask_var(offlined_cpus);
return err; return err;
......
...@@ -280,8 +280,6 @@ struct dw_i2c_dev { ...@@ -280,8 +280,6 @@ struct dw_i2c_dev {
int (*acquire_lock)(struct dw_i2c_dev *dev); int (*acquire_lock)(struct dw_i2c_dev *dev);
void (*release_lock)(struct dw_i2c_dev *dev); void (*release_lock)(struct dw_i2c_dev *dev);
bool pm_disabled; bool pm_disabled;
bool suspended;
bool skip_resume;
void (*disable)(struct dw_i2c_dev *dev); void (*disable)(struct dw_i2c_dev *dev);
void (*disable_int)(struct dw_i2c_dev *dev); void (*disable_int)(struct dw_i2c_dev *dev);
int (*init)(struct dw_i2c_dev *dev); int (*init)(struct dw_i2c_dev *dev);
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/suspend.h>
#include "i2c-designware-core.h" #include "i2c-designware-core.h"
...@@ -372,6 +373,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) ...@@ -372,6 +373,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
adap->dev.of_node = pdev->dev.of_node; adap->dev.of_node = pdev->dev.of_node;
dev_pm_set_driver_flags(&pdev->dev,
DPM_FLAG_SMART_PREPARE |
DPM_FLAG_SMART_SUSPEND |
DPM_FLAG_LEAVE_SUSPENDED);
/* The code below assumes runtime PM to be disabled. */ /* The code below assumes runtime PM to be disabled. */
WARN_ON(pm_runtime_enabled(&pdev->dev)); WARN_ON(pm_runtime_enabled(&pdev->dev));
...@@ -435,12 +441,24 @@ MODULE_DEVICE_TABLE(of, dw_i2c_of_match); ...@@ -435,12 +441,24 @@ MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int dw_i2c_plat_prepare(struct device *dev) static int dw_i2c_plat_prepare(struct device *dev)
{ {
return pm_runtime_suspended(dev); /*
* If the ACPI companion device object is present for this device, it
* may be accessed during suspend and resume of other devices via I2C
* operation regions, so tell the PM core and middle layers to avoid
* skipping system suspend/resume callbacks for it in that case.
*/
return !has_acpi_companion(dev);
} }
static void dw_i2c_plat_complete(struct device *dev) static void dw_i2c_plat_complete(struct device *dev)
{ {
if (dev->power.direct_complete) /*
* The device can only be in runtime suspend at this point if it has not
* been resumed throughout the ending system suspend/resume cycle, so if
* the platform firmware might mess up with it, request the runtime PM
* framework to resume it.
*/
if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
pm_request_resume(dev); pm_request_resume(dev);
} }
#else #else
...@@ -453,16 +471,9 @@ static int dw_i2c_plat_suspend(struct device *dev) ...@@ -453,16 +471,9 @@ static int dw_i2c_plat_suspend(struct device *dev)
{ {
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
if (i_dev->suspended) {
i_dev->skip_resume = true;
return 0;
}
i_dev->disable(i_dev); i_dev->disable(i_dev);
i2c_dw_plat_prepare_clk(i_dev, false); i2c_dw_plat_prepare_clk(i_dev, false);
i_dev->suspended = true;
return 0; return 0;
} }
...@@ -470,19 +481,9 @@ static int dw_i2c_plat_resume(struct device *dev) ...@@ -470,19 +481,9 @@ static int dw_i2c_plat_resume(struct device *dev)
{ {
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
if (!i_dev->suspended)
return 0;
if (i_dev->skip_resume) {
i_dev->skip_resume = false;
return 0;
}
i2c_dw_plat_prepare_clk(i_dev, true); i2c_dw_plat_prepare_clk(i_dev, true);
i_dev->init(i_dev); i_dev->init(i_dev);
i_dev->suspended = false;
return 0; return 0;
} }
......
...@@ -450,6 +450,8 @@ int intel_lpss_probe(struct device *dev, ...@@ -450,6 +450,8 @@ int intel_lpss_probe(struct device *dev,
if (ret) if (ret)
goto err_remove_ltr; goto err_remove_ltr;
dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND);
return 0; return 0;
err_remove_ltr: err_remove_ltr:
...@@ -478,7 +480,9 @@ EXPORT_SYMBOL_GPL(intel_lpss_remove); ...@@ -478,7 +480,9 @@ EXPORT_SYMBOL_GPL(intel_lpss_remove);
static int resume_lpss_device(struct device *dev, void *data) static int resume_lpss_device(struct device *dev, void *data)
{ {
pm_runtime_resume(dev); if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
pm_runtime_resume(dev);
return 0; return 0;
} }
......
...@@ -2,3 +2,4 @@ ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG ...@@ -2,3 +2,4 @@ ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
obj-y += core.o cpu.o obj-y += core.o cpu.o
obj-$(CONFIG_OF) += of.o obj-$(CONFIG_OF) += of.o
obj-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-opp-supply.o
This diff is collapsed.
...@@ -699,7 +699,7 @@ static void pci_pm_complete(struct device *dev) ...@@ -699,7 +699,7 @@ static void pci_pm_complete(struct device *dev)
pm_generic_complete(dev); pm_generic_complete(dev);
/* Resume device if platform firmware has put it in reset-power-on */ /* Resume device if platform firmware has put it in reset-power-on */
if (dev->power.direct_complete && pm_resume_via_firmware()) { if (pm_runtime_suspended(dev) && pm_resume_via_firmware()) {
pci_power_t pre_sleep_state = pci_dev->current_state; pci_power_t pre_sleep_state = pci_dev->current_state;
pci_update_current_state(pci_dev, pci_dev->current_state); pci_update_current_state(pci_dev, pci_dev->current_state);
...@@ -783,8 +783,10 @@ static int pci_pm_suspend_noirq(struct device *dev) ...@@ -783,8 +783,10 @@ static int pci_pm_suspend_noirq(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
if (dev_pm_smart_suspend_and_suspended(dev)) if (dev_pm_smart_suspend_and_suspended(dev)) {
dev->power.may_skip_resume = true;
return 0; return 0;
}
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend_late(dev, PMSG_SUSPEND); return pci_legacy_suspend_late(dev, PMSG_SUSPEND);
...@@ -838,6 +840,16 @@ static int pci_pm_suspend_noirq(struct device *dev) ...@@ -838,6 +840,16 @@ static int pci_pm_suspend_noirq(struct device *dev)
Fixup: Fixup:
pci_fixup_device(pci_fixup_suspend_late, pci_dev); pci_fixup_device(pci_fixup_suspend_late, pci_dev);
/*
* If the target system sleep state is suspend-to-idle, it is sufficient
* to check whether or not the device's wakeup settings are good for
* runtime PM. Otherwise, the pm_resume_via_firmware() check will cause
* pci_pm_complete() to take care of fixing up the device's state
* anyway, if need be.
*/
dev->power.may_skip_resume = device_may_wakeup(dev) ||
!device_can_wakeup(dev);
return 0; return 0;
} }
...@@ -847,6 +859,9 @@ static int pci_pm_resume_noirq(struct device *dev) ...@@ -847,6 +859,9 @@ static int pci_pm_resume_noirq(struct device *dev)
struct device_driver *drv = dev->driver; struct device_driver *drv = dev->driver;
int error = 0; int error = 0;
if (dev_pm_may_skip_resume(dev))
return 0;
/* /*
* Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
* during system suspend, so update their runtime PM status to "active" * during system suspend, so update their runtime PM status to "active"
...@@ -953,7 +968,7 @@ static int pci_pm_freeze_late(struct device *dev) ...@@ -953,7 +968,7 @@ static int pci_pm_freeze_late(struct device *dev)
if (dev_pm_smart_suspend_and_suspended(dev)) if (dev_pm_smart_suspend_and_suspended(dev))
return 0; return 0;
return pm_generic_freeze_late(dev);; return pm_generic_freeze_late(dev);
} }
static int pci_pm_freeze_noirq(struct device *dev) static int pci_pm_freeze_noirq(struct device *dev)
......
...@@ -150,6 +150,9 @@ static int pcie_portdrv_probe(struct pci_dev *dev, ...@@ -150,6 +150,9 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
pci_save_state(dev); pci_save_state(dev);
dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_SMART_SUSPEND |
DPM_FLAG_LEAVE_SUSPENDED);
if (pci_bridge_d3_possible(dev)) { if (pci_bridge_d3_possible(dev)) {
/* /*
* Keep the port resumed 100ms to make sure things like * Keep the port resumed 100ms to make sure things like
......
...@@ -119,7 +119,7 @@ static void surface_button_notify(struct acpi_device *device, u32 event) ...@@ -119,7 +119,7 @@ static void surface_button_notify(struct acpi_device *device, u32 event)
if (key_code == KEY_RESERVED) if (key_code == KEY_RESERVED)
return; return;
if (pressed) if (pressed)
pm_wakeup_event(&device->dev, 0); pm_wakeup_dev_event(&device->dev, 0, button->suspended);
if (button->suspended) if (button->suspended)
return; return;
input_report_key(input, key_code, pressed?1:0); input_report_key(input, key_code, pressed?1:0);
...@@ -185,6 +185,8 @@ static int surface_button_add(struct acpi_device *device) ...@@ -185,6 +185,8 @@ static int surface_button_add(struct acpi_device *device)
error = input_register_device(input); error = input_register_device(input);
if (error) if (error)
goto err_free_input; goto err_free_input;
device_init_wakeup(&device->dev, true);
dev_info(&device->dev, dev_info(&device->dev,
"%s [%s]\n", name, acpi_device_bid(device)); "%s [%s]\n", name, acpi_device_bid(device));
return 0; return 0;
......
...@@ -76,7 +76,7 @@ struct rockchip_iodomain_supply { ...@@ -76,7 +76,7 @@ struct rockchip_iodomain_supply {
struct rockchip_iodomain { struct rockchip_iodomain {
struct device *dev; struct device *dev;
struct regmap *grf; struct regmap *grf;
struct rockchip_iodomain_soc_data *soc_data; const struct rockchip_iodomain_soc_data *soc_data;
struct rockchip_iodomain_supply supplies[MAX_SUPPLIES]; struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
}; };
...@@ -382,43 +382,43 @@ static const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = { ...@@ -382,43 +382,43 @@ static const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = {
static const struct of_device_id rockchip_iodomain_match[] = { static const struct of_device_id rockchip_iodomain_match[] = {
{ {
.compatible = "rockchip,rk3188-io-voltage-domain", .compatible = "rockchip,rk3188-io-voltage-domain",
.data = (void *)&soc_data_rk3188 .data = &soc_data_rk3188
}, },
{ {
.compatible = "rockchip,rk3228-io-voltage-domain", .compatible = "rockchip,rk3228-io-voltage-domain",
.data = (void *)&soc_data_rk3228 .data = &soc_data_rk3228
}, },
{ {
.compatible = "rockchip,rk3288-io-voltage-domain", .compatible = "rockchip,rk3288-io-voltage-domain",
.data = (void *)&soc_data_rk3288 .data = &soc_data_rk3288
}, },
{ {
.compatible = "rockchip,rk3328-io-voltage-domain", .compatible = "rockchip,rk3328-io-voltage-domain",
.data = (void *)&soc_data_rk3328 .data = &soc_data_rk3328
}, },
{ {
.compatible = "rockchip,rk3368-io-voltage-domain", .compatible = "rockchip,rk3368-io-voltage-domain",
.data = (void *)&soc_data_rk3368 .data = &soc_data_rk3368
}, },
{ {
.compatible = "rockchip,rk3368-pmu-io-voltage-domain", .compatible = "rockchip,rk3368-pmu-io-voltage-domain",
.data = (void *)&soc_data_rk3368_pmu .data = &soc_data_rk3368_pmu
}, },
{ {
.compatible = "rockchip,rk3399-io-voltage-domain", .compatible = "rockchip,rk3399-io-voltage-domain",
.data = (void *)&soc_data_rk3399 .data = &soc_data_rk3399
}, },
{ {
.compatible = "rockchip,rk3399-pmu-io-voltage-domain", .compatible = "rockchip,rk3399-pmu-io-voltage-domain",
.data = (void *)&soc_data_rk3399_pmu .data = &soc_data_rk3399_pmu
}, },
{ {
.compatible = "rockchip,rv1108-io-voltage-domain", .compatible = "rockchip,rv1108-io-voltage-domain",
.data = (void *)&soc_data_rv1108 .data = &soc_data_rv1108
}, },
{ {
.compatible = "rockchip,rv1108-pmu-io-voltage-domain", .compatible = "rockchip,rv1108-pmu-io-voltage-domain",
.data = (void *)&soc_data_rv1108_pmu .data = &soc_data_rv1108_pmu
}, },
{ /* sentinel */ }, { /* sentinel */ },
}; };
...@@ -443,7 +443,7 @@ static int rockchip_iodomain_probe(struct platform_device *pdev) ...@@ -443,7 +443,7 @@ static int rockchip_iodomain_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, iod); platform_set_drvdata(pdev, iod);
match = of_match_node(rockchip_iodomain_match, np); match = of_match_node(rockchip_iodomain_match, np);
iod->soc_data = (struct rockchip_iodomain_soc_data *)match->data; iod->soc_data = match->data;
parent = pdev->dev.parent; parent = pdev->dev.parent;
if (parent && parent->of_node) { if (parent && parent->of_node) {
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/powercap.h> #include <linux/powercap.h>
#include <linux/suspend.h>
#include <asm/iosf_mbi.h> #include <asm/iosf_mbi.h>
#include <asm/processor.h> #include <asm/processor.h>
...@@ -155,6 +156,7 @@ struct rapl_power_limit { ...@@ -155,6 +156,7 @@ struct rapl_power_limit {
int prim_id; /* primitive ID used to enable */ int prim_id; /* primitive ID used to enable */
struct rapl_domain *domain; struct rapl_domain *domain;
const char *name; const char *name;
u64 last_power_limit;
}; };
static const char pl1_name[] = "long_term"; static const char pl1_name[] = "long_term";
...@@ -1209,7 +1211,7 @@ static int rapl_package_register_powercap(struct rapl_package *rp) ...@@ -1209,7 +1211,7 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
struct rapl_domain *rd; struct rapl_domain *rd;
char dev_name[17]; /* max domain name = 7 + 1 + 8 for int + 1 for null*/ char dev_name[17]; /* max domain name = 7 + 1 + 8 for int + 1 for null*/
struct powercap_zone *power_zone = NULL; struct powercap_zone *power_zone = NULL;
int nr_pl, ret;; int nr_pl, ret;
/* Update the domain data of the new package */ /* Update the domain data of the new package */
rapl_update_domain_data(rp); rapl_update_domain_data(rp);
...@@ -1533,6 +1535,92 @@ static int rapl_cpu_down_prep(unsigned int cpu) ...@@ -1533,6 +1535,92 @@ static int rapl_cpu_down_prep(unsigned int cpu)
static enum cpuhp_state pcap_rapl_online; static enum cpuhp_state pcap_rapl_online;
static void power_limit_state_save(void)
{
struct rapl_package *rp;
struct rapl_domain *rd;
int nr_pl, ret, i;
get_online_cpus();
list_for_each_entry(rp, &rapl_packages, plist) {
if (!rp->power_zone)
continue;
rd = power_zone_to_rapl_domain(rp->power_zone);
nr_pl = find_nr_power_limit(rd);
for (i = 0; i < nr_pl; i++) {
switch (rd->rpl[i].prim_id) {
case PL1_ENABLE:
ret = rapl_read_data_raw(rd,
POWER_LIMIT1,
true,
&rd->rpl[i].last_power_limit);
if (ret)
rd->rpl[i].last_power_limit = 0;
break;
case PL2_ENABLE:
ret = rapl_read_data_raw(rd,
POWER_LIMIT2,
true,
&rd->rpl[i].last_power_limit);
if (ret)
rd->rpl[i].last_power_limit = 0;
break;
}
}
}
put_online_cpus();
}
static void power_limit_state_restore(void)
{
struct rapl_package *rp;
struct rapl_domain *rd;
int nr_pl, i;
get_online_cpus();
list_for_each_entry(rp, &rapl_packages, plist) {
if (!rp->power_zone)
continue;
rd = power_zone_to_rapl_domain(rp->power_zone);
nr_pl = find_nr_power_limit(rd);
for (i = 0; i < nr_pl; i++) {
switch (rd->rpl[i].prim_id) {
case PL1_ENABLE:
if (rd->rpl[i].last_power_limit)
rapl_write_data_raw(rd,
POWER_LIMIT1,
rd->rpl[i].last_power_limit);
break;
case PL2_ENABLE:
if (rd->rpl[i].last_power_limit)
rapl_write_data_raw(rd,
POWER_LIMIT2,
rd->rpl[i].last_power_limit);
break;
}
}
}
put_online_cpus();
}
static int rapl_pm_callback(struct notifier_block *nb,
unsigned long mode, void *_unused)
{
switch (mode) {
case PM_SUSPEND_PREPARE:
power_limit_state_save();
break;
case PM_POST_SUSPEND:
power_limit_state_restore();
break;
}
return NOTIFY_OK;
}
static struct notifier_block rapl_pm_notifier = {
.notifier_call = rapl_pm_callback,
};
static int __init rapl_init(void) static int __init rapl_init(void)
{ {
const struct x86_cpu_id *id; const struct x86_cpu_id *id;
...@@ -1560,8 +1648,16 @@ static int __init rapl_init(void) ...@@ -1560,8 +1648,16 @@ static int __init rapl_init(void)
/* Don't bail out if PSys is not supported */ /* Don't bail out if PSys is not supported */
rapl_register_psys(); rapl_register_psys();
ret = register_pm_notifier(&rapl_pm_notifier);
if (ret)
goto err_unreg_all;
return 0; return 0;
err_unreg_all:
cpuhp_remove_state(pcap_rapl_online);
err_unreg: err_unreg:
rapl_unregister_powercap(); rapl_unregister_powercap();
return ret; return ret;
...@@ -1569,6 +1665,7 @@ static int __init rapl_init(void) ...@@ -1569,6 +1665,7 @@ static int __init rapl_init(void)
static void __exit rapl_exit(void) static void __exit rapl_exit(void)
{ {
unregister_pm_notifier(&rapl_pm_notifier);
cpuhp_remove_state(pcap_rapl_online); cpuhp_remove_state(pcap_rapl_online);
rapl_unregister_powercap(); rapl_unregister_powercap();
} }
......
...@@ -673,15 +673,13 @@ EXPORT_SYMBOL_GPL(powercap_unregister_control_type); ...@@ -673,15 +673,13 @@ EXPORT_SYMBOL_GPL(powercap_unregister_control_type);
static int __init powercap_init(void) static int __init powercap_init(void)
{ {
int result = 0; int result;
result = seed_constraint_attributes(); result = seed_constraint_attributes();
if (result) if (result)
return result; return result;
result = class_register(&powercap_class); return class_register(&powercap_class);
return result;
} }
device_initcall(powercap_init); device_initcall(powercap_init);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/suspend.h>
#include <scsi/scsi.h> #include <scsi/scsi.h>
#include "scsi_priv.h" #include "scsi_priv.h"
#include <scsi/scsi_device.h> #include <scsi/scsi_device.h>
...@@ -1009,11 +1010,20 @@ spi_dv_device(struct scsi_device *sdev) ...@@ -1009,11 +1010,20 @@ spi_dv_device(struct scsi_device *sdev)
u8 *buffer; u8 *buffer;
const int len = SPI_MAX_ECHO_BUFFER_SIZE*2; const int len = SPI_MAX_ECHO_BUFFER_SIZE*2;
/*
* Because this function and the power management code both call
* scsi_device_quiesce(), it is not safe to perform domain validation
* while suspend or resume is in progress. Hence the
* lock/unlock_system_sleep() calls.
*/
lock_system_sleep();
if (unlikely(spi_dv_in_progress(starget))) if (unlikely(spi_dv_in_progress(starget)))
return; goto unlock;
if (unlikely(scsi_device_get(sdev))) if (unlikely(scsi_device_get(sdev)))
return; goto unlock;
spi_dv_in_progress(starget) = 1; spi_dv_in_progress(starget) = 1;
buffer = kzalloc(len, GFP_KERNEL); buffer = kzalloc(len, GFP_KERNEL);
...@@ -1049,6 +1059,8 @@ spi_dv_device(struct scsi_device *sdev) ...@@ -1049,6 +1059,8 @@ spi_dv_device(struct scsi_device *sdev)
out_put: out_put:
spi_dv_in_progress(starget) = 0; spi_dv_in_progress(starget) = 0;
scsi_device_put(sdev); scsi_device_put(sdev);
unlock:
unlock_system_sleep();
} }
EXPORT_SYMBOL(spi_dv_device); EXPORT_SYMBOL(spi_dv_device);
......
This diff is collapsed.
...@@ -451,6 +451,7 @@ void __init acpi_no_s4_hw_signature(void); ...@@ -451,6 +451,7 @@ void __init acpi_no_s4_hw_signature(void);
void __init acpi_old_suspend_ordering(void); void __init acpi_old_suspend_ordering(void);
void __init acpi_nvs_nosave(void); void __init acpi_nvs_nosave(void);
void __init acpi_nvs_nosave_s3(void); void __init acpi_nvs_nosave_s3(void);
void __init acpi_sleep_no_blacklist(void);
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
struct acpi_osc_context { struct acpi_osc_context {
......
...@@ -30,9 +30,6 @@ ...@@ -30,9 +30,6 @@
struct cpufreq_policy; struct cpufreq_policy;
typedef int (*get_static_t)(cpumask_t *cpumask, int interval,
unsigned long voltage, u32 *power);
#ifdef CONFIG_CPU_THERMAL #ifdef CONFIG_CPU_THERMAL
/** /**
* cpufreq_cooling_register - function to create cpufreq cooling device. * cpufreq_cooling_register - function to create cpufreq cooling device.
...@@ -41,43 +38,6 @@ typedef int (*get_static_t)(cpumask_t *cpumask, int interval, ...@@ -41,43 +38,6 @@ typedef int (*get_static_t)(cpumask_t *cpumask, int interval,
struct thermal_cooling_device * struct thermal_cooling_device *
cpufreq_cooling_register(struct cpufreq_policy *policy); cpufreq_cooling_register(struct cpufreq_policy *policy);
struct thermal_cooling_device *
cpufreq_power_cooling_register(struct cpufreq_policy *policy,
u32 capacitance, get_static_t plat_static_func);
/**
* of_cpufreq_cooling_register - create cpufreq cooling device based on DT.
* @np: a valid struct device_node to the cooling device device tree node.
* @policy: cpufreq policy.
*/
#ifdef CONFIG_THERMAL_OF
struct thermal_cooling_device *
of_cpufreq_cooling_register(struct device_node *np,
struct cpufreq_policy *policy);
struct thermal_cooling_device *
of_cpufreq_power_cooling_register(struct device_node *np,
struct cpufreq_policy *policy,
u32 capacitance,
get_static_t plat_static_func);
#else
static inline struct thermal_cooling_device *
of_cpufreq_cooling_register(struct device_node *np,
struct cpufreq_policy *policy)
{
return ERR_PTR(-ENOSYS);
}
static inline struct thermal_cooling_device *
of_cpufreq_power_cooling_register(struct device_node *np,
struct cpufreq_policy *policy,
u32 capacitance,
get_static_t plat_static_func)
{
return NULL;
}
#endif
/** /**
* cpufreq_cooling_unregister - function to remove cpufreq cooling device. * cpufreq_cooling_unregister - function to remove cpufreq cooling device.
* @cdev: thermal cooling device pointer. * @cdev: thermal cooling device pointer.
...@@ -90,34 +50,27 @@ cpufreq_cooling_register(struct cpufreq_policy *policy) ...@@ -90,34 +50,27 @@ cpufreq_cooling_register(struct cpufreq_policy *policy)
{ {
return ERR_PTR(-ENOSYS); return ERR_PTR(-ENOSYS);
} }
static inline struct thermal_cooling_device *
cpufreq_power_cooling_register(struct cpufreq_policy *policy,
u32 capacitance, get_static_t plat_static_func)
{
return NULL;
}
static inline struct thermal_cooling_device * static inline
of_cpufreq_cooling_register(struct device_node *np, void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
struct cpufreq_policy *policy)
{ {
return ERR_PTR(-ENOSYS); return;
} }
#endif /* CONFIG_CPU_THERMAL */
#if defined(CONFIG_THERMAL_OF) && defined(CONFIG_CPU_THERMAL)
/**
* of_cpufreq_cooling_register - create cpufreq cooling device based on DT.
* @policy: cpufreq policy.
*/
struct thermal_cooling_device *
of_cpufreq_cooling_register(struct cpufreq_policy *policy);
#else
static inline struct thermal_cooling_device * static inline struct thermal_cooling_device *
of_cpufreq_power_cooling_register(struct device_node *np, of_cpufreq_cooling_register(struct cpufreq_policy *policy)
struct cpufreq_policy *policy,
u32 capacitance,
get_static_t plat_static_func)
{ {
return NULL; return NULL;
} }
#endif /* defined(CONFIG_THERMAL_OF) && defined(CONFIG_CPU_THERMAL) */
static inline
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
{
return;
}
#endif /* CONFIG_CPU_THERMAL */
#endif /* __CPU_COOLING_H__ */ #endif /* __CPU_COOLING_H__ */
...@@ -556,9 +556,10 @@ struct pm_subsys_data { ...@@ -556,9 +556,10 @@ struct pm_subsys_data {
* These flags can be set by device drivers at the probe time. They need not be * These flags can be set by device drivers at the probe time. They need not be
* cleared by the drivers as the driver core will take care of that. * cleared by the drivers as the driver core will take care of that.
* *
* NEVER_SKIP: Do not skip system suspend/resume callbacks for the device. * NEVER_SKIP: Do not skip all system suspend/resume callbacks for the device.
* SMART_PREPARE: Check the return value of the driver's ->prepare callback. * SMART_PREPARE: Check the return value of the driver's ->prepare callback.
* SMART_SUSPEND: No need to resume the device from runtime suspend. * SMART_SUSPEND: No need to resume the device from runtime suspend.
* LEAVE_SUSPENDED: Avoid resuming the device during system resume if possible.
* *
* Setting SMART_PREPARE instructs bus types and PM domains which may want * Setting SMART_PREPARE instructs bus types and PM domains which may want
* system suspend/resume callbacks to be skipped for the device to return 0 from * system suspend/resume callbacks to be skipped for the device to return 0 from
...@@ -572,10 +573,14 @@ struct pm_subsys_data { ...@@ -572,10 +573,14 @@ struct pm_subsys_data {
* necessary from the driver's perspective. It also may cause them to skip * necessary from the driver's perspective. It also may cause them to skip
* invocations of the ->suspend_late and ->suspend_noirq callbacks provided by * invocations of the ->suspend_late and ->suspend_noirq callbacks provided by
* the driver if they decide to leave the device in runtime suspend. * the driver if they decide to leave the device in runtime suspend.
*
* Setting LEAVE_SUSPENDED informs the PM core and middle-layer code that the
* driver prefers the device to be left in suspend after system resume.
*/ */
#define DPM_FLAG_NEVER_SKIP BIT(0) #define DPM_FLAG_NEVER_SKIP BIT(0)
#define DPM_FLAG_SMART_PREPARE BIT(1) #define DPM_FLAG_SMART_PREPARE BIT(1)
#define DPM_FLAG_SMART_SUSPEND BIT(2) #define DPM_FLAG_SMART_SUSPEND BIT(2)
#define DPM_FLAG_LEAVE_SUSPENDED BIT(3)
struct dev_pm_info { struct dev_pm_info {
pm_message_t power_state; pm_message_t power_state;
...@@ -597,6 +602,8 @@ struct dev_pm_info { ...@@ -597,6 +602,8 @@ struct dev_pm_info {
bool wakeup_path:1; bool wakeup_path:1;
bool syscore:1; bool syscore:1;
bool no_pm_callbacks:1; /* Owned by the PM core */ bool no_pm_callbacks:1; /* Owned by the PM core */
unsigned int must_resume:1; /* Owned by the PM core */
unsigned int may_skip_resume:1; /* Set by subsystems */
#else #else
unsigned int should_wakeup:1; unsigned int should_wakeup:1;
#endif #endif
...@@ -766,6 +773,7 @@ extern int pm_generic_poweroff(struct device *dev); ...@@ -766,6 +773,7 @@ extern int pm_generic_poweroff(struct device *dev);
extern void pm_generic_complete(struct device *dev); extern void pm_generic_complete(struct device *dev);
extern void dev_pm_skip_next_resume_phases(struct device *dev); extern void dev_pm_skip_next_resume_phases(struct device *dev);
extern bool dev_pm_may_skip_resume(struct device *dev);
extern bool dev_pm_smart_suspend_and_suspended(struct device *dev); extern bool dev_pm_smart_suspend_and_suspended(struct device *dev);
#else /* !CONFIG_PM_SLEEP */ #else /* !CONFIG_PM_SLEEP */
......
...@@ -88,6 +88,11 @@ static inline bool device_may_wakeup(struct device *dev) ...@@ -88,6 +88,11 @@ static inline bool device_may_wakeup(struct device *dev)
return dev->power.can_wakeup && !!dev->power.wakeup; return dev->power.can_wakeup && !!dev->power.wakeup;
} }
static inline void device_set_wakeup_path(struct device *dev)
{
dev->power.wakeup_path = true;
}
/* drivers/base/power/wakeup.c */ /* drivers/base/power/wakeup.c */
extern void wakeup_source_prepare(struct wakeup_source *ws, const char *name); extern void wakeup_source_prepare(struct wakeup_source *ws, const char *name);
extern struct wakeup_source *wakeup_source_create(const char *name); extern struct wakeup_source *wakeup_source_create(const char *name);
...@@ -174,6 +179,8 @@ static inline bool device_may_wakeup(struct device *dev) ...@@ -174,6 +179,8 @@ static inline bool device_may_wakeup(struct device *dev)
return dev->power.can_wakeup && dev->power.should_wakeup; return dev->power.can_wakeup && dev->power.should_wakeup;
} }
static inline void device_set_wakeup_path(struct device *dev) {}
static inline void __pm_stay_awake(struct wakeup_source *ws) {} static inline void __pm_stay_awake(struct wakeup_source *ws) {}
static inline void pm_stay_awake(struct device *dev) {} static inline void pm_stay_awake(struct device *dev) {}
......
...@@ -443,32 +443,8 @@ extern bool pm_save_wakeup_count(unsigned int count); ...@@ -443,32 +443,8 @@ extern bool pm_save_wakeup_count(unsigned int count);
extern void pm_wakep_autosleep_enabled(bool set); extern void pm_wakep_autosleep_enabled(bool set);
extern void pm_print_active_wakeup_sources(void); extern void pm_print_active_wakeup_sources(void);
static inline void lock_system_sleep(void) extern void lock_system_sleep(void);
{ extern void unlock_system_sleep(void);
current->flags |= PF_FREEZER_SKIP;
mutex_lock(&pm_mutex);
}
static inline void unlock_system_sleep(void)
{
/*
* Don't use freezer_count() because we don't want the call to
* try_to_freeze() here.
*
* Reason:
* Fundamentally, we just don't need it, because freezing condition
* doesn't come into effect until we release the pm_mutex lock,
* since the freezer always works with pm_mutex held.
*
* More importantly, in the case of hibernation,
* unlock_system_sleep() gets called in snapshot_read() and
* snapshot_write() when the freezing condition is still in effect.
* Which means, if we use try_to_freeze() here, it would make them
* enter the refrigerator, thus causing hibernation to lockup.
*/
current->flags &= ~PF_FREEZER_SKIP;
mutex_unlock(&pm_mutex);
}
#else /* !CONFIG_PM_SLEEP */ #else /* !CONFIG_PM_SLEEP */
......
This diff is collapsed.
CONFIG_PM=n
CONFIG_SUSPEND=n
CONFIG_HIBERNATION=n
# Triggers PM on OMAP
CONFIG_CPU_IDLE=n
# Triggers enablement via hibernate callbacks
CONFIG_XEN=n
# ARM/ARM64 architectures that select PM unconditionally
CONFIG_ARCH_OMAP2PLUS_TYPICAL=n
CONFIG_ARCH_RENESAS=n
CONFIG_ARCH_TEGRA=n
CONFIG_ARCH_VEXPRESS=n
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment