mirror of
https://github.com/torvalds/linux.git
synced 2026-10-23 07:11:03 +02:00
Power management updates for 7.4-rc1
- Fix multiple issues related to input validation, synchronization,
access safety, memory layout and register field validation, and error
handling in the ACPI CPPC library (Christian Loehle and Sumit Gupta)
- Select the frequency-invariance callback per CPU in the CPPC cpufreq
driver and create the FIE worker before enabling PCC callbacks in it
to avoid queueing work through a missing worker and dereferencing a
NULL pointer (Christian Loehle)
- Update the CPPC cpufreq driver entry in MAINTAINERS and add a new
entry for the ACPI CPPC library (Christian Loehle)
- Minor fixes in various cpufreq drivers (Alexander Koskovich, Daniel
Schwierzeck, Frank Zhang, Haotian Zhang, Kathiravan Thirumoorthy,
Sumeet Pawnikar, and Xueqin Luo)
- Fix wrong behaviors on multi-node servers in the Loongson cpufreq
driver (Huacai Chen)
- Reject NULL from cpufreq_cpu_get() in the Rust code (Mehmet Koseoglu)
- Add suspend OPP to mediatek cpufreq driver and DT (Ryan Brue)
- Fixups for a few bugs identified by Sashiko in the amd-pstate cpufreq
driver (Mario Limoncielloi, Wentao Guan)
- Initial support for maximum frequency capacity handling on Zen6
client in amd-pstate (Mario Limonciello)
- Fix for amd-pstate-ut with 'Requested CPU Min frequency' BIOS option
(K Prateek Nayak)
- New Zen6 tuning values for EPP in amd-pstate (Mario Limonciello,
Vishal Badole)
- amd-pstate documentation fix (Karl Mehltretter)
- Restrict deferred downscaling to low load for shared policies in the
conservative cpufreq governor to avoid undesirable CPU frequency
reduction in some cases (Shengming Hu)
- Serialize sugov_start() against rate limit updates in the schedutil
cpufreq governor to avoid using a stale rate limit value (Hui Su)
- Prevent cpuidle governors menu and teo from returning idle state 0
after it has been disabled (Xueqin Luo)
- Replace min_t() with the better alternatives in the intel_idle driver
and make it use 2-argument strscpy() (Andy Shevchenko)
- Fix two repeated phrase instances in cpuidle (Hemanth Selam, Syazwina
Fazrul)
- Add rugged Panther Lake support to the intel_rapl power capping
driver and make it use %pe for printing error pointers (Srinivas
Pandruvada and Sumeet Pawnikar)
- Update and improve runtime PM documentation and add a kunit test for
supplier idle/suspend (Brian Norris)
- Add a pm_runtime_dont_use_autosuspend() call to pm_runtime_reinit()
to ensure autosuspend disabling on driver teardown (Joshua Crofts)
- Ignore page-zero RAM in E820 checksum during hibernation on x86 to
avoid rejecting images created after kexec() (Matthias Goergens)
- Add resume event mapping for PMSG_POWEROFF to the PM core to allow it
to carry out device shutdown in the future (Mario Limonciello)
- Add DPM watchdog to prepare/late/early/noirq/complete phases of
device suspend and resume (Mayank Rungta)
- Update swsusp.txt references to swsusp.rst in hibernation
documentation (Li Jun)
- Fix use-after-free of perf domain in netlink doit handlers for energy
models (Fan Wu)
- Add synchronize_rcu() back to cpu_pm_unregister_notifier() after it
was removed inadvertently in previous development cycles to avoid
use-after-free (Bradley Morgan)
- Unregister CPU PM notifier outside the timer lock in the timer-ti-dm
clocksource driver (Bradley Morgan)
- Fix typo "sucessfully" in a comment in pm_clk_add_clk() kerneldoc
comment and typo "hierachy" in a comment in pm-graph (Hemanth Selam)
- Fix the return value in dev_pm_opp_get_of_node() kernel-doc (Karl
Mehltretter)
-----BEGIN PGP SIGNATURE-----
iQFGBAABCAAwFiEEcM8Aw/RY0dgsiRUR7l+9nS/U47UFAmrWNkISHHJqd0Byand5
c29ja2kubmV0AAoJEO5fvZ0v1OO1M7QH/RyTpxMUHI3eLqhPIczecdsQxoJ9gCZS
wGMQmvBRrNEw/Cp+X+j5Swo8iOJRKQ5Na+h53Ebg4EFJg5Fkh8SDO/VhQDaC0GUp
t9y9MpVfXq/Fc/gOimzJ/MGhRmBXl5HKva586pg5TdFg4adDs4kPcDj9zDFnKMhf
Kb0FHgjRjli1iDT1HSOV7KDsH1bZBWD1P04jMaVQ1zJb6Ngwi1msyBaRlE3A1mqX
W31C3pi7qtfbs1v1JPmrnaVAJEZmv38t7x/G3o5FL35P2bwlgN/KXD0aMNHZ0Rnl
A6Jz4p5ugop+gBLzyQo4W8SNcLk9cRvlp3FUwuEax4MEPbg+rhfjdDs=
=4ccu
-----END PGP SIGNATURE-----
Merge tag 'pm-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki:
"This time, the most visible part are the ACPI CPPC updates from
Christian Loehle, including CPPC cpufreq driver changes, even though
updates of cpufreq drivers proper are still the largest topic group.
However, the updates of cpufreq drivers depend on the ACPI CPPC
updates, so they should really be counted together and that is well
over 1/2 of the total commit count.
Interestingly enough, almost all of the updates included here are
fixes or cleanups, with some new feature support in the amd-pstate
cpufreq driver and IPQ9650 support in the qcom-nvmem cpufreq driver.
The runtime PM documentation improvements from Brian Norris can also
be mostly regarded as fixes, but a new kunit test goes along with
them.
Specifics:
- Fix multiple issues related to input validation, synchronization,
access safety, memory layout and register field validation, and
error handling in the ACPI CPPC library (Christian Loehle and Sumit
Gupta)
- Select the frequency-invariance callback per CPU in the CPPC
cpufreq driver and create the FIE worker before enabling PCC
callbacks in it to avoid queueing work through a missing worker and
dereferencing a NULL pointer (Christian Loehle)
- Update the CPPC cpufreq driver entry in MAINTAINERS and add a new
entry for the ACPI CPPC library (Christian Loehle)
- Minor fixes in various cpufreq drivers (Alexander Koskovich, Daniel
Schwierzeck, Frank Zhang, Haotian Zhang, Kathiravan Thirumoorthy,
Sumeet Pawnikar, and Xueqin Luo)
- Fix wrong behaviors on multi-node servers in the Loongson cpufreq
driver (Huacai Chen)
- Reject NULL from cpufreq_cpu_get() in the Rust code (Mehmet
Koseoglu)
- Add suspend OPP to mediatek cpufreq driver and DT (Ryan Brue)
- Fixups for a few bugs identified by Sashiko in the amd-pstate
cpufreq driver (Mario Limoncielloi, Wentao Guan)
- Initial support for maximum frequency capacity handling on Zen6
client in amd-pstate (Mario Limonciello)
- Fix for amd-pstate-ut with 'Requested CPU Min frequency' BIOS
option (K Prateek Nayak)
- New Zen6 tuning values for EPP in amd-pstate (Mario Limonciello,
Vishal Badole)
- amd-pstate documentation fix (Karl Mehltretter)
- Restrict deferred downscaling to low load for shared policies in
the conservative cpufreq governor to avoid undesirable CPU
frequency reduction in some cases (Shengming Hu)
- Serialize sugov_start() against rate limit updates in the schedutil
cpufreq governor to avoid using a stale rate limit value (Hui Su)
- Prevent cpuidle governors menu and teo from returning idle state 0
after it has been disabled (Xueqin Luo)
- Replace min_t() with the better alternatives in the intel_idle
driver and make it use 2-argument strscpy() (Andy Shevchenko)
- Fix two repeated phrase instances in cpuidle (Hemanth Selam,
Syazwina Fazrul)
- Add rugged Panther Lake support to the intel_rapl power capping
driver and make it use %pe for printing error pointers (Srinivas
Pandruvada and Sumeet Pawnikar)
- Update and improve runtime PM documentation and add a kunit test
for supplier idle/suspend (Brian Norris)
- Add a pm_runtime_dont_use_autosuspend() call to pm_runtime_reinit()
to ensure autosuspend disabling on driver teardown (Joshua Crofts)
- Ignore page-zero RAM in E820 checksum during hibernation on x86 to
avoid rejecting images created after kexec() (Matthias Goergens)
- Add resume event mapping for PMSG_POWEROFF to the PM core to allow
it to carry out device shutdown in the future (Mario Limonciello)
- Add DPM watchdog to prepare/late/early/noirq/complete phases of
device suspend and resume (Mayank Rungta)
- Update swsusp.txt references to swsusp.rst in hibernation
documentation (Li Jun)
- Fix use-after-free of perf domain in netlink doit handlers for
energy models (Fan Wu)
- Add synchronize_rcu() back to cpu_pm_unregister_notifier() after it
was removed inadvertently in previous development cycles to avoid
use-after-free (Bradley Morgan)
- Unregister CPU PM notifier outside the timer lock in the
timer-ti-dm clocksource driver (Bradley Morgan)
- Fix typo "sucessfully" in a comment in pm_clk_add_clk() kerneldoc
comment and typo "hierachy" in a comment in pm-graph (Hemanth
Selam)
- Fix the return value in dev_pm_opp_get_of_node() kernel-doc (Karl
Mehltretter)"
* tag 'pm-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (89 commits)
cpufreq: loongson3: Replace IOCSR read/write with MMIO ones
cpufreq: loongson3: Use global physical CPU ID in get/target callbacks
cpufreq: loongson3: Replace per-package mutex with per-node
cpufreq: loongson3: Adjust the width of id and val in smc_message
cpufreq: loongson3: Make this driver depend on MACH_LOONGSON64
PM: runtime: Expand introduction with core concepts and structure
cpufreq: schedutil: Serialize sugov_start() against rate limit updates
MAINTAINERS: ACPI: Add a separate CPPC entry
MAINTAINERS: cpufreq: Replace CPPC reviewer
arm64: dts: mediatek: mt8173: Mark the lowest CPU OPPs for suspend
cpufreq: mediatek: Enter system sleep at the suspend OPP
cpufreq: qcom-cpufreq-hw: fix device node refcount leak in phandle parsing
cpufreq: qcom-nvmem: Add IPQ9650 support
cpufreq: Add Milos to cpufreq-dt-platdev blocklist
cpufreq: Use %pe to print error pointers symbolically
rust: rcpufreq_dt: add module alias
cpufreq: sti: avoid NULL dereference in dev_err()
cpufreq: tegra194: fix double-pointer error in get_cpu_ndiv
cpufreq: sparc-us2e: fix frequency table index copy-paste error
rust: cpufreq: reject NULL from cpufreq_cpu_get()
...
This commit is contained in:
commit
df37e39242
|
|
@ -335,12 +335,16 @@ Description: Performance Limited
|
|||
Read to check if platform throttling (thermal/power/current
|
||||
limits) caused delivered performance to fall below the
|
||||
requested level. A non-zero value indicates throttling occurred.
|
||||
If firmware provides the register but Linux cannot access it
|
||||
safely, reads return the literal string "<unsupported>".
|
||||
|
||||
Write the bitmask of bits to clear:
|
||||
|
||||
- 0x1 = clear bit 0 (desired performance excursion)
|
||||
- 0x2 = clear bit 1 (minimum performance excursion)
|
||||
- 0x3 = clear both bits
|
||||
Write 0x3 to clear both bits. Selectively clearing one bit is
|
||||
not supported because ACPI does not define the effect of writing
|
||||
one to these status bits and an interlocked read-modify-write is
|
||||
not generally available.
|
||||
Writing 0x3 returns -EOPNOTSUPP when firmware describes the
|
||||
register using an access which Linux cannot clear safely; reads
|
||||
may remain available in that case.
|
||||
|
||||
The platform sets these bits; OSPM can only clear them.
|
||||
|
||||
|
|
|
|||
|
|
@ -501,13 +501,6 @@ Kernel parameters
|
|||
disable
|
||||
Disable amd-pstate preferred core.
|
||||
|
||||
amd_dynamic_epp=
|
||||
[X86]
|
||||
disable
|
||||
Disable amd-pstate dynamic EPP.
|
||||
enable
|
||||
Enable amd-pstate dynamic EPP.
|
||||
|
||||
amijoy.map= [HW,JOY] Amiga joystick support
|
||||
Map of devices attached to JOY0DAT and JOY1DAT
|
||||
Format: <a>,<b>
|
||||
|
|
|
|||
|
|
@ -481,12 +481,6 @@ For systems that support ``amd-pstate`` preferred core, the core rankings will
|
|||
always be advertised by the platform. But OS can choose to ignore that via the
|
||||
kernel parameter ``amd_prefcore=disable``.
|
||||
|
||||
``amd_dynamic_epp``
|
||||
|
||||
When AMD pstate is in auto mode, dynamic EPP will control whether the kernel
|
||||
autonomously changes the EPP mode. The default is disabled. It can be enabled
|
||||
with the kernel parameter ``amd_dynamic_epp=enable``.
|
||||
|
||||
User Space Interface in ``sysfs`` - General
|
||||
===========================================
|
||||
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ interval" value.
|
|||
|
||||
Now, the governor is ready to walk the list of idle states and choose one of
|
||||
them. For this purpose, it compares the target residency of each state with
|
||||
the predicted idle duration and the exit latency of it with the with the latency
|
||||
the predicted idle duration and the exit latency of it with the latency
|
||||
limit coming from the power management quality of service, or `PM QoS <cpu-pm-qos_>`_,
|
||||
framework. It selects the state with the target residency closest to the predicted
|
||||
idle duration, but still below it, and exit latency that does not exceed the
|
||||
|
|
|
|||
|
|
@ -8,34 +8,114 @@ Runtime Power Management Framework for I/O Devices
|
|||
|
||||
(C) 2014 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
|
||||
.. _Section 1:
|
||||
|
||||
1. Introduction
|
||||
===============
|
||||
|
||||
Support for runtime power management (runtime PM) of I/O devices is provided
|
||||
at the power management core (PM core) level by means of:
|
||||
Runtime power management (or runtime PM, sometimes also referred to as RPM)
|
||||
allows individual I/O devices that are not in active use to be put into
|
||||
low-power states, in which they may not be operational or even accessible, and
|
||||
go back to the fully operational state as needed. Power can be reduced this
|
||||
way without waiting for a transition into a system-wide sleep state.
|
||||
|
||||
* The power management workqueue pm_wq in which bus types and device drivers can
|
||||
put their PM-related work items. It is strongly recommended that pm_wq be
|
||||
used for queuing all work items related to runtime PM, because this allows
|
||||
them to be synchronized with system-wide power transitions (suspend to RAM,
|
||||
hibernation and resume from system sleep states). pm_wq is declared in
|
||||
include/linux/pm_runtime.h and defined in kernel/power/main.c.
|
||||
Core Concepts
|
||||
-------------
|
||||
|
||||
* A number of runtime PM fields in the 'power' member of 'struct device' (which
|
||||
is of the type 'struct dev_pm_info', defined in include/linux/pm.h) that can
|
||||
be used for synchronizing runtime PM operations with one another.
|
||||
Runtime PM operates around a few device-level concepts — whether a device is
|
||||
**active** or **suspended**; whether runtime PM is **enabled** or **disabled**;
|
||||
whether runtime PM is **allowed** or **forbidden**; and whether a device is
|
||||
**in use**.
|
||||
|
||||
* **Active**: Runtime PM tracks whether or not devices are in active use, which
|
||||
generally means that they are accessible and operational, and may be depended
|
||||
on by something (for example, other devices or user space). This is
|
||||
represented by the **active** meta-state. By contrast, the **suspended**
|
||||
meta-state represents a promise that the given device will not be accessed.
|
||||
This covers devices in low-power states, but it also may include devices that
|
||||
are not fully operational. There are also two transient meta-states,
|
||||
**suspending** and **resuming**, representing transitions between **active**
|
||||
and **suspended** often referred to as *runtime suspend* and *runtime
|
||||
resume*, or just *suspend* and *resume*, respectively. Every device handled
|
||||
by runtime PM is in one of these four meta-states at any time, and its
|
||||
meta-state is expected to reflect its actual physical configuration (that is,
|
||||
for instance, if the device is not accessible, it must not be **active**).
|
||||
The term "runtime PM status" used in what follows refers to the meta-states
|
||||
described above.
|
||||
|
||||
* **Enabled**: In order to track the runtime PM status and carry out
|
||||
transitions between **active** and **suspended**, runtime PM needs to be
|
||||
**enabled** for the given device. If it is **disabled**, runtime PM
|
||||
generally leaves the device alone. This means in particular that the runtime
|
||||
PM status of a device and its actual physical configuration may get out of
|
||||
sync after disabling runtime PM for it. Additionally, the runtime PM status
|
||||
of a device may need to be explicitly adjusted to reflect its current
|
||||
physical configuration before enabling runtime PM for it (e.g., during device
|
||||
probe). Once runtime PM is **enabled**, it begins managing PM status —
|
||||
performing *runtime suspend* and *runtime resume* transitions based on its
|
||||
understanding of whether a device is in use.
|
||||
|
||||
As a rule, all devices are initialized with runtime PM **disabled**, though
|
||||
some bus types (such as PCI) manage some RPM initialization automatically,
|
||||
and therefore probe devices in an **enabled** state. For other devices,
|
||||
drivers must enable runtime PM on their own to opt in.
|
||||
|
||||
* **Allowed**: Runtime PM may be **forbidden** even though it has been enabled.
|
||||
Doing so causes the given device to transition into the **active** meta-state
|
||||
and prevents it from being suspended. In other words, a device with
|
||||
forbidden runtime PM remains **active** at least until runtime PM is
|
||||
**allowed** for it again. This mechanism can be managed by in-kernel APIs,
|
||||
but more importantly, it is also available to user space via the
|
||||
``/sys/devices/.../power/control`` sysfs attribute of the given device.
|
||||
Namely, writing ``on`` to that attribute forbids runtime PM, and writing
|
||||
``auto`` allows it. User space may modify that attribute at any time, so the
|
||||
ability to transition a device into the **suspended** meta-state via runtime
|
||||
PM must not be relied on for correctness.
|
||||
|
||||
* **In use**: Runtime PM determines whether a device should be transitioned to
|
||||
an **active** state by its understanding of whether a device is in use. The
|
||||
first way a device is considered **in use** is by its own usage count — a
|
||||
reference counter driven by a variety of get()/put() APIs. Additionally, a
|
||||
device may be considered **in use** by having active dependents — either
|
||||
child devices, or device-linked consumers. When a device is no longer in
|
||||
use, runtime PM may choose to transition it to the **suspended** state.
|
||||
|
||||
Thus, when a device moves from **suspended** to **active**, it may also cause
|
||||
its dependencies to resume, unless they are **active** already. Conversely,
|
||||
suspending a device may also cause its otherwise-unused dependencies to
|
||||
suspend.
|
||||
|
||||
* **Autosuspend**: Runtime PM also supports a feature called "autosuspend."
|
||||
This is different than the ``control`` notion of "auto" (i.e., **allowed**).
|
||||
Autosuspend is covered in `Section 9`_.
|
||||
|
||||
Implementation Structure
|
||||
------------------------
|
||||
|
||||
Support for runtime power management is provided at the power management core
|
||||
(PM core) level by means of:
|
||||
|
||||
* Three device runtime PM callbacks in 'struct dev_pm_ops' (defined in
|
||||
include/linux/pm.h).
|
||||
include/linux/pm.h). See `Section 2`_.
|
||||
|
||||
* A number of runtime PM fields in the 'power' member of 'struct device' that
|
||||
can be used for synchronizing runtime PM operations with one another. These
|
||||
are covered in `Section 3`_.
|
||||
|
||||
* A set of helper functions defined in drivers/base/power/runtime.c that can be
|
||||
used for carrying out runtime PM operations in such a way that the
|
||||
synchronization between them is taken care of by the PM core. Bus types and
|
||||
device drivers are encouraged to use these functions.
|
||||
synchronization between them is taken care of by the PM core. Bus types and
|
||||
device drivers are encouraged to use these functions. They are covered in
|
||||
`Section 4`_.
|
||||
|
||||
The runtime PM callbacks present in 'struct dev_pm_ops', the device runtime PM
|
||||
fields of 'struct dev_pm_info' and the core helper functions provided for
|
||||
runtime PM are described below.
|
||||
* The power management workqueue pm_wq in which bus types and device drivers can
|
||||
put their PM-related work items. It is strongly recommended that pm_wq be
|
||||
used for queuing all work items related to runtime PM, because this allows
|
||||
them to be synchronized with system-wide power transitions (suspend to RAM,
|
||||
hibernation and resume from system sleep states). pm_wq is declared in
|
||||
include/linux/pm_runtime.h and defined in kernel/power/main.c.
|
||||
|
||||
.. _Section 2:
|
||||
|
||||
2. Device Runtime PM Callbacks
|
||||
==============================
|
||||
|
|
@ -50,6 +130,14 @@ There are three device runtime PM callbacks defined in 'struct dev_pm_ops'::
|
|||
...
|
||||
};
|
||||
|
||||
Most device drivers only need to implement ->runtime_suspend() and
|
||||
->runtime_resume(). The ->runtime_idle() callback is optional and rarely
|
||||
implemented by peripheral device drivers, as the PM core automatically handles
|
||||
suspension and autosuspend when ->runtime_idle() is omitted (or returns 0).
|
||||
|
||||
Subsystem and Driver Callbacks
|
||||
------------------------------
|
||||
|
||||
The ->runtime_suspend(), ->runtime_resume() and ->runtime_idle() callbacks
|
||||
are executed by the PM core for the device's subsystem that may be either of
|
||||
the following:
|
||||
|
|
@ -80,8 +168,11 @@ the PM core that it is safe to run the ->runtime_suspend(), ->runtime_resume()
|
|||
and ->runtime_idle() callbacks for the given device in atomic context with
|
||||
interrupts disabled. This implies that the callback routines in question must
|
||||
not block or sleep, but it also means that the synchronous helper functions
|
||||
listed at the end of Section 4 may be used for that device within an interrupt
|
||||
handler or generally in an atomic context.
|
||||
listed at the end of `Section 4`_ may be used for that device within an
|
||||
interrupt handler or generally in an atomic context.
|
||||
|
||||
Callback Semantics
|
||||
------------------
|
||||
|
||||
The subsystem-level suspend callback, if present, is _entirely_ _responsible_
|
||||
for handling the suspend of the device as appropriate, which may, but need not
|
||||
|
|
@ -105,9 +196,9 @@ knows what to do to handle the device).
|
|||
|
||||
* If the suspend callback returns an error code different from -EBUSY and
|
||||
-EAGAIN, the PM core regards this as a fatal error and will refuse to run
|
||||
the helper functions described in Section 4 for the device until its status
|
||||
is directly set to either 'active', or 'suspended' (the PM core provides
|
||||
special helper functions for this purpose).
|
||||
the helper functions described in `Section 4`_ for the device until its
|
||||
status is directly set to either 'active', or 'suspended' (the PM core
|
||||
provides special helper functions for this purpose).
|
||||
|
||||
In particular, if the driver requires remote wakeup capability (i.e. hardware
|
||||
mechanism allowing the device to request a change of its power state, such as
|
||||
|
|
@ -132,10 +223,10 @@ what to do to handle the device).
|
|||
'active'.
|
||||
|
||||
* If the resume callback returns an error code, the PM core regards this as a
|
||||
fatal error and will refuse to run the helper functions described in Section
|
||||
4 for the device, until its status is directly set to either 'active', or
|
||||
'suspended' (by means of special helper functions provided by the PM core
|
||||
for this purpose).
|
||||
fatal error and will refuse to run the helper functions described in
|
||||
`Section 4`_ for the device, until its status is directly set to either
|
||||
'active', or 'suspended' (by means of special helper functions provided by
|
||||
the PM core for this purpose).
|
||||
|
||||
The idle callback (a subsystem-level one, if present, or the driver one) is
|
||||
executed by the PM core whenever the device appears to be idle, which is
|
||||
|
|
@ -154,13 +245,21 @@ suspending the device are satisfied) and to queue up a suspend request for the
|
|||
device in that case. If there is no idle callback, or if the callback returns
|
||||
0, then the PM core will attempt to carry out a runtime suspend of the device,
|
||||
also respecting devices configured for autosuspend. In essence this means a
|
||||
call to pm_runtime_autosuspend(). To prevent this (for example, if the callback
|
||||
routine has started a delayed suspend), the routine must return a non-zero
|
||||
value. Negative error return codes are ignored by the PM core.
|
||||
call to pm_runtime_autosuspend().
|
||||
|
||||
The helper functions provided by the PM core, described in Section 4, guarantee
|
||||
that the following constraints are met with respect to runtime PM callbacks for
|
||||
one device:
|
||||
To prevent this suspension (for example, if the callback routine has scheduled
|
||||
a delayed suspend or determined the device cannot be idle), the routine must
|
||||
return a non-zero value (typically -EBUSY or -EAGAIN). Unlike
|
||||
->runtime_suspend() and ->runtime_resume(), the PM core does not treat negative
|
||||
return codes from ->runtime_idle() as a fatal device error; any non-zero value
|
||||
simply stops the PM core from suspending the device.
|
||||
|
||||
Core Guarantees and Synchronization Rules
|
||||
-----------------------------------------
|
||||
|
||||
The helper functions provided by the PM core, described in `Section 4`_,
|
||||
guarantee that the following constraints are met with respect to runtime PM
|
||||
callbacks for one device:
|
||||
|
||||
(1) The callbacks are mutually exclusive (e.g. it is forbidden to execute
|
||||
->runtime_suspend() in parallel with ->runtime_resume() or with another
|
||||
|
|
@ -200,106 +299,19 @@ rules:
|
|||
scheduled requests to execute the other callbacks for the same device,
|
||||
except for scheduled autosuspends.
|
||||
|
||||
.. _Section 3:
|
||||
|
||||
3. Runtime PM Device Fields
|
||||
===========================
|
||||
|
||||
The following device runtime PM fields are present in 'struct dev_pm_info', as
|
||||
defined in include/linux/pm.h:
|
||||
Device PM fields are found in 'struct dev_pm_info', as defined in
|
||||
include/linux/pm.h. Many of those fields track runtime PM configuration and
|
||||
state.
|
||||
|
||||
`struct timer_list suspend_timer;`
|
||||
- timer used for scheduling (delayed) suspend and autosuspend requests
|
||||
.. kernel-doc:: include/linux/pm.h
|
||||
:identifiers: dev_pm_info
|
||||
|
||||
`unsigned long timer_expires;`
|
||||
- timer expiration time, in jiffies (if this is different from zero, the
|
||||
timer is running and will expire at that time, otherwise the timer is not
|
||||
running)
|
||||
|
||||
`struct work_struct work;`
|
||||
- work structure used for queuing up requests (i.e. work items in pm_wq)
|
||||
|
||||
`wait_queue_head_t wait_queue;`
|
||||
- wait queue used if any of the helper functions needs to wait for another
|
||||
one to complete
|
||||
|
||||
`spinlock_t lock;`
|
||||
- lock used for synchronization
|
||||
|
||||
`atomic_t usage_count;`
|
||||
- the usage counter of the device
|
||||
|
||||
`atomic_t child_count;`
|
||||
- the count of 'active' children of the device
|
||||
|
||||
`unsigned int ignore_children;`
|
||||
- if set, the value of child_count is ignored (but still updated)
|
||||
|
||||
`unsigned int disable_depth;`
|
||||
- used for disabling the helper functions (they work normally if this is
|
||||
equal to zero); the initial value of it is 1 (i.e. runtime PM is
|
||||
initially disabled for all devices)
|
||||
|
||||
`int runtime_error;`
|
||||
- if set, there was a fatal error (one of the callbacks returned error code
|
||||
as described in Section 2), so the helper functions will not work until
|
||||
this flag is cleared; this is the error code returned by the failing
|
||||
callback
|
||||
|
||||
`unsigned int idle_notification;`
|
||||
- if set, ->runtime_idle() is being executed
|
||||
|
||||
`unsigned int request_pending;`
|
||||
- if set, there's a pending request (i.e. a work item queued up into pm_wq)
|
||||
|
||||
`enum rpm_request request;`
|
||||
- type of request that's pending (valid if request_pending is set)
|
||||
|
||||
`unsigned int deferred_resume;`
|
||||
- set if ->runtime_resume() is about to be run while ->runtime_suspend() is
|
||||
being executed for that device and it is not practical to wait for the
|
||||
suspend to complete; means "start a resume as soon as you've suspended"
|
||||
|
||||
`enum rpm_status runtime_status;`
|
||||
- the runtime PM status of the device; this field's initial value is
|
||||
RPM_SUSPENDED, which means that each device is initially regarded by the
|
||||
PM core as 'suspended', regardless of its real hardware status
|
||||
|
||||
`enum rpm_status last_status;`
|
||||
- the last runtime PM status of the device captured before disabling runtime
|
||||
PM for it (invalid initially and when disable_depth is 0)
|
||||
|
||||
`unsigned int runtime_auto;`
|
||||
- if set, indicates that the user space has allowed the device driver to
|
||||
power manage the device at run time via the /sys/devices/.../power/control
|
||||
`interface;` it may only be modified with the help of the
|
||||
pm_runtime_allow() and pm_runtime_forbid() helper functions
|
||||
|
||||
`unsigned int no_callbacks;`
|
||||
- indicates that the device does not use the runtime PM callbacks (see
|
||||
Section 8); it may be modified only by the pm_runtime_no_callbacks()
|
||||
helper function
|
||||
|
||||
`unsigned int irq_safe;`
|
||||
- indicates that the ->runtime_suspend() and ->runtime_resume() callbacks
|
||||
will be invoked with the spinlock held and interrupts disabled
|
||||
|
||||
`unsigned int use_autosuspend;`
|
||||
- indicates that the device's driver supports delayed autosuspend (see
|
||||
Section 9); it may be modified only by the
|
||||
pm_runtime{_dont}_use_autosuspend() helper functions
|
||||
|
||||
`unsigned int timer_autosuspends;`
|
||||
- indicates that the PM core should attempt to carry out an autosuspend
|
||||
when the timer expires rather than a normal suspend
|
||||
|
||||
`int autosuspend_delay;`
|
||||
- the delay time (in milliseconds) to be used for autosuspend
|
||||
|
||||
`unsigned long last_busy;`
|
||||
- the time (in jiffies) when the pm_runtime_mark_last_busy() helper
|
||||
function was last called for this device; used in calculating inactivity
|
||||
periods for autosuspend
|
||||
|
||||
All of the above fields are members of the 'power' member of 'struct device'.
|
||||
.. _Section 4:
|
||||
|
||||
4. Runtime PM Device Helper Functions
|
||||
=====================================
|
||||
|
|
@ -307,219 +319,10 @@ All of the above fields are members of the 'power' member of 'struct device'.
|
|||
The following runtime PM helper functions are defined in
|
||||
drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
||||
|
||||
`void pm_runtime_init(struct device *dev);`
|
||||
- initialize the device runtime PM fields in 'struct dev_pm_info'
|
||||
.. kernel-doc:: drivers/base/power/runtime.c
|
||||
:export:
|
||||
|
||||
`void pm_runtime_remove(struct device *dev);`
|
||||
- make sure that the runtime PM of the device will be disabled after
|
||||
removing the device from device hierarchy
|
||||
|
||||
`int pm_runtime_idle(struct device *dev);`
|
||||
- execute the subsystem-level idle callback for the device; returns an
|
||||
error code on failure, where -EINPROGRESS means that ->runtime_idle() is
|
||||
already being executed; if there is no callback or the callback returns 0
|
||||
then run pm_runtime_autosuspend(dev) and return its result
|
||||
|
||||
`int pm_runtime_suspend(struct device *dev);`
|
||||
- execute the subsystem-level suspend callback for the device; returns 0 on
|
||||
success, 1 if the device's runtime PM status was already 'suspended', or
|
||||
error code on failure, where -EAGAIN or -EBUSY means it is safe to attempt
|
||||
to suspend the device again in future and -EACCES means that
|
||||
'power.disable_depth' is different from 0
|
||||
|
||||
`int pm_runtime_autosuspend(struct device *dev);`
|
||||
- same as pm_runtime_suspend() except that a call to
|
||||
pm_runtime_mark_last_busy() is made and an autosuspend is scheduled for
|
||||
the appropriate time and 0 is returned
|
||||
|
||||
`int pm_runtime_resume(struct device *dev);`
|
||||
- execute the subsystem-level resume callback for the device; returns 0 on
|
||||
success, 1 if the device's runtime PM status is already 'active' (also if
|
||||
'power.disable_depth' is nonzero, but the status was 'active' when it was
|
||||
changing from 0 to 1) or error code on failure, where -EAGAIN means it may
|
||||
be safe to attempt to resume the device again in future, but
|
||||
'power.runtime_error' should be checked additionally, and -EACCES means
|
||||
that the callback could not be run, because 'power.disable_depth' was
|
||||
different from 0
|
||||
|
||||
`int pm_runtime_resume_and_get(struct device *dev);`
|
||||
- run pm_runtime_resume(dev) and if successful, increment the device's
|
||||
usage counter; returns 0 on success (whether or not the device's
|
||||
runtime PM status was already 'active') or the error code from
|
||||
pm_runtime_resume() on failure.
|
||||
|
||||
`int pm_request_idle(struct device *dev);`
|
||||
- submit a request to execute the subsystem-level idle callback for the
|
||||
device (the request is represented by a work item in pm_wq); returns 0 on
|
||||
success or error code if the request has not been queued up
|
||||
|
||||
`int pm_request_autosuspend(struct device *dev);`
|
||||
- Call pm_runtime_mark_last_busy() and schedule the execution of the
|
||||
subsystem-level suspend callback for the device when the autosuspend delay
|
||||
expires
|
||||
|
||||
`int pm_schedule_suspend(struct device *dev, unsigned int delay);`
|
||||
- schedule the execution of the subsystem-level suspend callback for the
|
||||
device in future, where 'delay' is the time to wait before queuing up a
|
||||
suspend work item in pm_wq, in milliseconds (if 'delay' is zero, the work
|
||||
item is queued up immediately); returns 0 on success, 1 if the device's PM
|
||||
runtime status was already 'suspended', or error code if the request
|
||||
hasn't been scheduled (or queued up if 'delay' is 0); if the execution of
|
||||
->runtime_suspend() is already scheduled and not yet expired, the new
|
||||
value of 'delay' will be used as the time to wait
|
||||
|
||||
`int pm_request_resume(struct device *dev);`
|
||||
- submit a request to execute the subsystem-level resume callback for the
|
||||
device (the request is represented by a work item in pm_wq); returns 0 on
|
||||
success, 1 if the device's runtime PM status was already 'active', or
|
||||
error code if the request hasn't been queued up
|
||||
|
||||
`void pm_runtime_get_noresume(struct device *dev);`
|
||||
- increment the device's usage counter
|
||||
|
||||
`int pm_runtime_get(struct device *dev);`
|
||||
- increment the device's usage counter, run pm_request_resume(dev) and
|
||||
return its result
|
||||
|
||||
`int pm_runtime_get_sync(struct device *dev);`
|
||||
- increment the device's usage counter, run pm_runtime_resume(dev) and
|
||||
return its result;
|
||||
note that it does not drop the device's usage counter on errors, so
|
||||
consider using pm_runtime_resume_and_get() instead of it, especially
|
||||
if its return value is checked by the caller, as this is likely to
|
||||
result in cleaner code.
|
||||
|
||||
`int pm_runtime_get_if_in_use(struct device *dev);`
|
||||
- return -EINVAL if 'power.disable_depth' is nonzero; otherwise, if the
|
||||
runtime PM status is RPM_ACTIVE and the runtime PM usage counter is
|
||||
nonzero, increment the counter and return 1; otherwise return 0 without
|
||||
changing the counter
|
||||
|
||||
`int pm_runtime_get_if_active(struct device *dev);`
|
||||
- return -EINVAL if 'power.disable_depth' is nonzero; otherwise, if the
|
||||
runtime PM status is RPM_ACTIVE, increment the counter and
|
||||
return 1; otherwise return 0 without changing the counter
|
||||
|
||||
`void pm_runtime_put_noidle(struct device *dev);`
|
||||
- decrement the device's usage counter
|
||||
|
||||
`int pm_runtime_put(struct device *dev);`
|
||||
- decrement the device's usage counter; if the result is 0 then run
|
||||
pm_request_idle(dev) and return its result
|
||||
|
||||
`int pm_runtime_put_autosuspend(struct device *dev);`
|
||||
- set the power.last_busy field to the current time and decrement the
|
||||
device's usage counter; if the result is 0 then run
|
||||
pm_request_autosuspend(dev) and return its result
|
||||
|
||||
`int __pm_runtime_put_autosuspend(struct device *dev);`
|
||||
- decrement the device's usage counter; if the result is 0 then run
|
||||
pm_request_autosuspend(dev) and return its result
|
||||
|
||||
`int pm_runtime_put_sync(struct device *dev);`
|
||||
- decrement the device's usage counter; if the result is 0 then run
|
||||
pm_runtime_idle(dev) and return its result
|
||||
|
||||
`int pm_runtime_put_sync_suspend(struct device *dev);`
|
||||
- decrement the device's usage counter; if the result is 0 then run
|
||||
pm_runtime_suspend(dev) and return its result
|
||||
|
||||
`int pm_runtime_put_sync_autosuspend(struct device *dev);`
|
||||
- set the power.last_busy field to the current time and decrement the
|
||||
device's usage counter; if the result is 0 then run
|
||||
pm_runtime_autosuspend(dev) and return its result
|
||||
|
||||
`void pm_runtime_enable(struct device *dev);`
|
||||
- decrement the device's 'power.disable_depth' field; if that field is equal
|
||||
to zero, the runtime PM helper functions can execute subsystem-level
|
||||
callbacks described in Section 2 for the device
|
||||
|
||||
`int pm_runtime_disable(struct device *dev);`
|
||||
- increment the device's 'power.disable_depth' field (if the value of that
|
||||
field was previously zero, this prevents subsystem-level runtime PM
|
||||
callbacks from being run for the device), make sure that all of the
|
||||
pending runtime PM operations on the device are either completed or
|
||||
canceled; returns 1 if there was a resume request pending and it was
|
||||
necessary to execute the subsystem-level resume callback for the device
|
||||
to satisfy that request, otherwise 0 is returned
|
||||
|
||||
`void pm_runtime_barrier(struct device *dev);`
|
||||
- check if there's a resume request pending for the device and resume it
|
||||
(synchronously) in that case, cancel any other pending runtime PM requests
|
||||
regarding it and wait for all runtime PM operations on it in progress to
|
||||
complete
|
||||
|
||||
`void pm_suspend_ignore_children(struct device *dev, bool enable);`
|
||||
- set/unset the power.ignore_children flag of the device
|
||||
|
||||
`int pm_runtime_set_active(struct device *dev);`
|
||||
- clear the device's 'power.runtime_error' flag, set the device's runtime
|
||||
PM status to 'active' and update its parent's counter of 'active'
|
||||
children as appropriate (it is only valid to use this function if
|
||||
'power.runtime_error' is set or 'power.disable_depth' is greater than
|
||||
zero); it will fail and return error code if the device has a parent
|
||||
which is not active and the 'power.ignore_children' flag of which is unset
|
||||
|
||||
`void pm_runtime_set_suspended(struct device *dev);`
|
||||
- clear the device's 'power.runtime_error' flag, set the device's runtime
|
||||
PM status to 'suspended' and update its parent's counter of 'active'
|
||||
children as appropriate (it is only valid to use this function if
|
||||
'power.runtime_error' is set or 'power.disable_depth' is greater than
|
||||
zero)
|
||||
|
||||
`bool pm_runtime_active(struct device *dev);`
|
||||
- return true if the device's runtime PM status is 'active' or its
|
||||
'power.disable_depth' field is not equal to zero, or false otherwise
|
||||
|
||||
`bool pm_runtime_suspended(struct device *dev);`
|
||||
- return true if the device's runtime PM status is 'suspended' and its
|
||||
'power.disable_depth' field is equal to zero, or false otherwise
|
||||
|
||||
`bool pm_runtime_status_suspended(struct device *dev);`
|
||||
- return true if the device's runtime PM status is 'suspended'
|
||||
|
||||
`void pm_runtime_no_callbacks(struct device *dev);`
|
||||
- set the power.no_callbacks flag for the device and remove the runtime
|
||||
PM attributes from /sys/devices/.../power (or prevent them from being
|
||||
added when the device is registered)
|
||||
|
||||
`void pm_runtime_irq_safe(struct device *dev);`
|
||||
- set the power.irq_safe flag for the device, causing the runtime-PM
|
||||
callbacks to be invoked with interrupts off
|
||||
|
||||
`bool pm_runtime_is_irq_safe(struct device *dev);`
|
||||
- return true if power.irq_safe flag was set for the device, causing
|
||||
the runtime-PM callbacks to be invoked with interrupts off
|
||||
|
||||
`void pm_runtime_mark_last_busy(struct device *dev);`
|
||||
- set the power.last_busy field to the current time
|
||||
|
||||
`void pm_runtime_use_autosuspend(struct device *dev);`
|
||||
- set the power.use_autosuspend flag, enabling autosuspend delays; call
|
||||
pm_runtime_get_sync if the flag was previously cleared and
|
||||
power.autosuspend_delay is negative
|
||||
|
||||
`void pm_runtime_dont_use_autosuspend(struct device *dev);`
|
||||
- clear the power.use_autosuspend flag, disabling autosuspend delays;
|
||||
decrement the device's usage counter if the flag was previously set and
|
||||
power.autosuspend_delay is negative; call pm_runtime_idle
|
||||
|
||||
`void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);`
|
||||
- set the power.autosuspend_delay value to 'delay' (expressed in
|
||||
milliseconds); if 'delay' is negative then runtime suspends are
|
||||
prevented; if power.use_autosuspend is set, pm_runtime_get_sync may be
|
||||
called or the device's usage counter may be decremented and
|
||||
pm_runtime_idle called depending on if power.autosuspend_delay is
|
||||
changed to or from a negative value; if power.use_autosuspend is clear,
|
||||
pm_runtime_idle is called
|
||||
|
||||
`unsigned long pm_runtime_autosuspend_expiration(struct device *dev);`
|
||||
- calculate the time when the current autosuspend delay period will expire,
|
||||
based on power.last_busy and power.autosuspend_delay; if the delay time
|
||||
is 1000 ms or larger then the expiration time is rounded up to the
|
||||
nearest second; returns 0 if the delay period has already expired or
|
||||
power.use_autosuspend isn't set, otherwise returns the expiration time
|
||||
in jiffies
|
||||
.. kernel-doc:: include/linux/pm_runtime.h
|
||||
|
||||
It is safe to execute the following helper functions from interrupt context:
|
||||
|
||||
|
|
@ -529,6 +332,8 @@ It is safe to execute the following helper functions from interrupt context:
|
|||
- pm_request_resume()
|
||||
- pm_runtime_get_noresume()
|
||||
- pm_runtime_get()
|
||||
- pm_runtime_get_if_active()
|
||||
- pm_runtime_get_if_in_use()
|
||||
- pm_runtime_put_noidle()
|
||||
- pm_runtime_put()
|
||||
- pm_runtime_put_autosuspend()
|
||||
|
|
@ -538,6 +343,9 @@ It is safe to execute the following helper functions from interrupt context:
|
|||
- pm_runtime_set_active()
|
||||
- pm_runtime_set_suspended()
|
||||
- pm_runtime_suspended()
|
||||
- pm_runtime_active()
|
||||
- pm_runtime_status_suspended()
|
||||
- pm_runtime_enabled()
|
||||
- pm_runtime_mark_last_busy()
|
||||
- pm_runtime_autosuspend_expiration()
|
||||
|
||||
|
|
@ -549,16 +357,19 @@ functions may also be used in interrupt context:
|
|||
- pm_runtime_autosuspend()
|
||||
- pm_runtime_resume()
|
||||
- pm_runtime_get_sync()
|
||||
- pm_runtime_resume_and_get()
|
||||
- pm_runtime_put_sync()
|
||||
- pm_runtime_put_sync_suspend()
|
||||
- pm_runtime_put_sync_autosuspend()
|
||||
|
||||
.. _Section 5:
|
||||
|
||||
5. Runtime PM Initialization, Device Probing and Removal
|
||||
========================================================
|
||||
|
||||
Initially, the runtime PM is disabled for all devices, which means that the
|
||||
majority of the runtime PM helper functions described in Section 4 will return
|
||||
-EAGAIN until pm_runtime_enable() is called for the device.
|
||||
majority of the runtime PM helper functions described in `Section 4`_ will
|
||||
return -EACCES until pm_runtime_enable() is called for the device.
|
||||
|
||||
In addition to that, the initial runtime PM status of all devices is
|
||||
'suspended', but it need not reflect the actual physical state of the device.
|
||||
|
|
@ -581,13 +392,13 @@ pm_runtime_set_suspended().
|
|||
If the default initial runtime PM status of the device (i.e. 'suspended')
|
||||
reflects the actual state of the device, its bus type's or its driver's
|
||||
->probe() callback will likely need to wake it up using one of the PM core's
|
||||
helper functions described in Section 4. In that case, pm_runtime_resume()
|
||||
helper functions described in `Section 4`_. In that case, pm_runtime_resume()
|
||||
should be used. Of course, for this purpose the device's runtime PM has to be
|
||||
enabled earlier by calling pm_runtime_enable().
|
||||
|
||||
Note, if the device may execute pm_runtime calls during the probe (such as
|
||||
if it is registered with a subsystem that may call back in) then the
|
||||
pm_runtime_get_sync() call paired with a pm_runtime_put() call will be
|
||||
pm_runtime_resume_and_get() call paired with a pm_runtime_put() call will be
|
||||
appropriate to ensure that the device is not put back to sleep during the
|
||||
probe. This can happen with systems such as the network device layer.
|
||||
|
||||
|
|
@ -615,7 +426,10 @@ removal of their drivers.
|
|||
|
||||
Drivers in ->remove() callback should undo the runtime PM changes done
|
||||
in ->probe(). Usually this means calling pm_runtime_disable(),
|
||||
pm_runtime_dont_use_autosuspend() etc.
|
||||
pm_runtime_dont_use_autosuspend() etc. Alternatively, drivers can use
|
||||
devm_pm_runtime_enable() during probe, which automatically takes care of
|
||||
calling pm_runtime_disable() and pm_runtime_dont_use_autosuspend() upon driver
|
||||
detachment.
|
||||
|
||||
The user space can effectively disallow the driver of the device to power manage
|
||||
it at run time by changing the value of its /sys/devices/.../power/control
|
||||
|
|
@ -629,6 +443,8 @@ value of /sys/devices/.../power/control to "auto" to allow the driver to power
|
|||
manage the device at run time, the driver may confuse it by using
|
||||
pm_runtime_forbid() this way.
|
||||
|
||||
.. _Section 6:
|
||||
|
||||
6. Runtime PM and System Sleep
|
||||
==============================
|
||||
|
||||
|
|
@ -721,74 +537,17 @@ out the following operations:
|
|||
callback and right after executing the subsystem-level .complete() callback
|
||||
for it, respectively.
|
||||
|
||||
.. _Section 7:
|
||||
|
||||
7. Generic subsystem callbacks
|
||||
==============================
|
||||
|
||||
Subsystems may wish to conserve code space by using the set of generic power
|
||||
management callbacks provided by the PM core, defined in
|
||||
driver/base/power/generic_ops.c:
|
||||
drivers/base/power/generic_ops.c:
|
||||
|
||||
`int pm_generic_runtime_suspend(struct device *dev);`
|
||||
- invoke the ->runtime_suspend() callback provided by the driver of this
|
||||
device and return its result, or return 0 if not defined
|
||||
|
||||
`int pm_generic_runtime_resume(struct device *dev);`
|
||||
- invoke the ->runtime_resume() callback provided by the driver of this
|
||||
device and return its result, or return 0 if not defined
|
||||
|
||||
`int pm_generic_suspend(struct device *dev);`
|
||||
- if the device has not been suspended at run time, invoke the ->suspend()
|
||||
callback provided by its driver and return its result, or return 0 if not
|
||||
defined
|
||||
|
||||
`int pm_generic_suspend_noirq(struct device *dev);`
|
||||
- if pm_runtime_suspended(dev) returns "false", invoke the ->suspend_noirq()
|
||||
callback provided by the device's driver and return its result, or return
|
||||
0 if not defined
|
||||
|
||||
`int pm_generic_resume(struct device *dev);`
|
||||
- invoke the ->resume() callback provided by the driver of this device and,
|
||||
if successful, change the device's runtime PM status to 'active'
|
||||
|
||||
`int pm_generic_resume_noirq(struct device *dev);`
|
||||
- invoke the ->resume_noirq() callback provided by the driver of this device
|
||||
|
||||
`int pm_generic_freeze(struct device *dev);`
|
||||
- if the device has not been suspended at run time, invoke the ->freeze()
|
||||
callback provided by its driver and return its result, or return 0 if not
|
||||
defined
|
||||
|
||||
`int pm_generic_freeze_noirq(struct device *dev);`
|
||||
- if pm_runtime_suspended(dev) returns "false", invoke the ->freeze_noirq()
|
||||
callback provided by the device's driver and return its result, or return
|
||||
0 if not defined
|
||||
|
||||
`int pm_generic_thaw(struct device *dev);`
|
||||
- if the device has not been suspended at run time, invoke the ->thaw()
|
||||
callback provided by its driver and return its result, or return 0 if not
|
||||
defined
|
||||
|
||||
`int pm_generic_thaw_noirq(struct device *dev);`
|
||||
- if pm_runtime_suspended(dev) returns "false", invoke the ->thaw_noirq()
|
||||
callback provided by the device's driver and return its result, or return
|
||||
0 if not defined
|
||||
|
||||
`int pm_generic_poweroff(struct device *dev);`
|
||||
- if the device has not been suspended at run time, invoke the ->poweroff()
|
||||
callback provided by its driver and return its result, or return 0 if not
|
||||
defined
|
||||
|
||||
`int pm_generic_poweroff_noirq(struct device *dev);`
|
||||
- if pm_runtime_suspended(dev) returns "false", run the ->poweroff_noirq()
|
||||
callback provided by the device's driver and return its result, or return
|
||||
0 if not defined
|
||||
|
||||
`int pm_generic_restore(struct device *dev);`
|
||||
- invoke the ->restore() callback provided by the driver of this device and,
|
||||
if successful, change the device's runtime PM status to 'active'
|
||||
|
||||
`int pm_generic_restore_noirq(struct device *dev);`
|
||||
- invoke the ->restore_noirq() callback provided by the device's driver
|
||||
.. kernel-doc:: drivers/base/power/generic_ops.c
|
||||
:export:
|
||||
|
||||
These functions are the defaults used by the PM core if a subsystem doesn't
|
||||
provide its own callbacks for ->runtime_idle(), ->runtime_suspend(),
|
||||
|
|
@ -800,8 +559,10 @@ subsystem-level dev_pm_ops structure.
|
|||
Device drivers that wish to use the same function as a system suspend, freeze,
|
||||
poweroff and runtime suspend callback, and similarly for system resume, thaw,
|
||||
restore, and runtime resume, can achieve similar behaviour with the help of the
|
||||
DEFINE_RUNTIME_DEV_PM_OPS() defined in include/linux/pm_runtime.h (possibly setting its
|
||||
last argument to NULL).
|
||||
DEFINE_RUNTIME_DEV_PM_OPS() macro defined in include/linux/pm_runtime.h
|
||||
(possibly setting its last argument to NULL).
|
||||
|
||||
.. _Section 8:
|
||||
|
||||
8. "No-Callback" Devices
|
||||
========================
|
||||
|
|
@ -839,6 +600,8 @@ in subsystems/drivers, the PM core allows runtime PM callbacks to be
|
|||
unassigned. More precisely, if a callback pointer is NULL, the PM core will act
|
||||
as though there was a callback and it returned 0.
|
||||
|
||||
.. _Section 9:
|
||||
|
||||
9. Autosuspend, or automatically-delayed suspends
|
||||
=================================================
|
||||
|
||||
|
|
@ -864,7 +627,7 @@ registration the length should be controlled by user space, using the
|
|||
|
||||
In order to use autosuspend, subsystems or drivers must call
|
||||
pm_runtime_use_autosuspend() (preferably before registering the device), and
|
||||
thereafter they should use the various `*_autosuspend()` helper functions
|
||||
thereafter they should use the various \*_autosuspend() helper functions
|
||||
instead of the non-autosuspend counterparts::
|
||||
|
||||
Instead of: pm_runtime_suspend use: pm_runtime_autosuspend;
|
||||
|
|
@ -874,7 +637,7 @@ instead of the non-autosuspend counterparts::
|
|||
|
||||
Drivers may also continue to use the non-autosuspend helper functions; they
|
||||
will behave normally, which means sometimes taking the autosuspend delay into
|
||||
account (see pm_runtime_idle). The autosuspend variants of the functions also
|
||||
account (see pm_runtime_idle()). The autosuspend variants of the functions also
|
||||
call pm_runtime_mark_last_busy().
|
||||
|
||||
Under some circumstances a driver or subsystem may want to prevent a device
|
||||
|
|
@ -917,7 +680,7 @@ Here is a schematic pseudo-code example::
|
|||
|
||||
int foo_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct foo_priv foo = container_of(dev, ...);
|
||||
struct foo_priv *foo = container_of(dev, ...);
|
||||
int ret = 0;
|
||||
|
||||
lock(&foo->private_lock);
|
||||
|
|
@ -933,7 +696,7 @@ Here is a schematic pseudo-code example::
|
|||
|
||||
int foo_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct foo_priv foo = container_of(dev, ...);
|
||||
struct foo_priv *foo = container_of(dev, ...);
|
||||
|
||||
lock(&foo->private_lock);
|
||||
/* ... resume the device ... */
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ Documentation for userland software suspend interface
|
|||
|
||||
(C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
|
||||
|
||||
First, the warnings at the beginning of swsusp.txt still apply.
|
||||
First, the warnings at the beginning of swsusp.rst still apply.
|
||||
|
||||
Second, you should read the FAQ in swsusp.txt _now_ if you have not
|
||||
Second, you should read the FAQ in swsusp.rst _now_ if you have not
|
||||
done it already.
|
||||
|
||||
Now, to use the userland interface for software suspend you need special
|
||||
|
|
|
|||
13
MAINTAINERS
13
MAINTAINERS
|
|
@ -327,6 +327,17 @@ F: drivers/acpi/acpica/
|
|||
F: include/acpi/
|
||||
F: tools/power/acpi/
|
||||
|
||||
ACPI CPPC
|
||||
M: "Rafael J. Wysocki" <rafael@kernel.org>
|
||||
M: Christian Loehle <christian.loehle@arm.com>
|
||||
L: linux-acpi@vger.kernel.org
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
|
||||
F: Documentation/admin-guide/acpi/cppc_sysfs.rst
|
||||
F: drivers/acpi/cppc_acpi.c
|
||||
F: include/acpi/cppc_acpi.h
|
||||
|
||||
ACPI FOR ARM64 (ACPI/arm64)
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
M: Hanjun Guo <guohanjun@huawei.com>
|
||||
|
|
@ -6782,7 +6793,7 @@ M: "Rafael J. Wysocki" <rafael@kernel.org>
|
|||
M: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
R: Jie Zhan <zhanjie9@hisilicon.com>
|
||||
R: Lifeng Zheng <zhenglifeng1@huawei.com>
|
||||
R: Pierre Gondois <pierre.gondois@arm.com>
|
||||
R: Christian Loehle <christian.loehle@arm.com>
|
||||
R: Sumit Gupta <sumitg@nvidia.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ cluster0_opp: opp-table-0 {
|
|||
opp-507000000 {
|
||||
opp-hz = /bits/ 64 <507000000>;
|
||||
opp-microvolt = <859000>;
|
||||
opp-suspend;
|
||||
};
|
||||
opp-702000000 {
|
||||
opp-hz = /bits/ 64 <702000000>;
|
||||
|
|
@ -93,6 +94,7 @@ cluster1_opp: opp-table-1 {
|
|||
opp-507000000 {
|
||||
opp-hz = /bits/ 64 <507000000>;
|
||||
opp-microvolt = <828000>;
|
||||
opp-suspend;
|
||||
};
|
||||
opp-702000000 {
|
||||
opp-hz = /bits/ 64 <702000000>;
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ int counters_read_on_cpu(int cpu, smp_call_func_t func, void *val)
|
|||
return -EPERM;
|
||||
func(val);
|
||||
} else {
|
||||
smp_call_function_single(cpu, func, val, 1);
|
||||
return smp_call_function_single(cpu, func, val, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -470,6 +470,12 @@ static void amu_read_core_const_ctrs(void *val)
|
|||
cpu_read_corecnt(&ctrs->corecnt);
|
||||
}
|
||||
|
||||
static bool cpc_ffh_reg_valid(const struct cpc_reg *reg)
|
||||
{
|
||||
return reg->bit_width && reg->bit_width <= 64 &&
|
||||
reg->bit_offset <= 64 - reg->bit_width;
|
||||
}
|
||||
|
||||
static u64 cpc_ffh_extract_bits(const struct cpc_reg *reg, u64 val)
|
||||
{
|
||||
val &= GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
|
||||
|
|
@ -506,7 +512,8 @@ int cpc_read_ffh_fb_ctrs(int cpu, struct cpc_reg *reg1, u64 *val1,
|
|||
struct amu_ffh_ctrs ctrs;
|
||||
int ret;
|
||||
|
||||
if (!is_amu_ctr_reg(reg1) || !is_amu_ctr_reg(reg2))
|
||||
if (!is_amu_ctr_reg(reg1) || !is_amu_ctr_reg(reg2) ||
|
||||
!cpc_ffh_reg_valid(reg1) || !cpc_ffh_reg_valid(reg2))
|
||||
return -EINVAL;
|
||||
|
||||
ret = counters_read_on_cpu(cpu, amu_read_core_const_ctrs, &ctrs);
|
||||
|
|
@ -530,6 +537,9 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
|
|||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
if (!cpc_ffh_reg_valid(reg))
|
||||
return -EINVAL;
|
||||
|
||||
switch ((u64)reg->address) {
|
||||
case CPC_FFH_CTR_CORE:
|
||||
ret = counters_read_on_cpu(cpu, cpu_read_corecnt, val);
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include <acpi/cppc_acpi.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/topology.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#define CPPC_HIGHEST_PERF_PERFORMANCE 196
|
||||
#define CPPC_HIGHEST_PERF_PREFCORE 166
|
||||
|
|
@ -45,10 +47,20 @@ bool cpc_ffh_supported(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool cpc_ffh_reg_valid(const struct cpc_reg *reg)
|
||||
{
|
||||
return reg->address <= U32_MAX && reg->bit_width &&
|
||||
reg->bit_width <= 64 &&
|
||||
reg->bit_offset <= 64 - reg->bit_width;
|
||||
}
|
||||
|
||||
int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!cpc_ffh_reg_valid(reg))
|
||||
return -EINVAL;
|
||||
|
||||
err = rdmsrq_safe_on_cpu(cpunum, reg->address, val);
|
||||
if (!err) {
|
||||
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
|
||||
|
|
@ -65,6 +77,9 @@ int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
|
|||
u64 rd_val;
|
||||
int err;
|
||||
|
||||
if (!cpc_ffh_reg_valid(reg))
|
||||
return -EINVAL;
|
||||
|
||||
err = rdmsrq_safe_on_cpu(cpunum, reg->address, &rd_val);
|
||||
if (!err) {
|
||||
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
|
||||
|
|
@ -81,31 +96,18 @@ int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
|
|||
|
||||
static void amd_set_max_freq_ratio(void)
|
||||
{
|
||||
struct cppc_perf_caps perf_caps;
|
||||
u64 numerator, nominal_perf;
|
||||
u64 numerator, denominator;
|
||||
u64 perf_ratio;
|
||||
int rc;
|
||||
|
||||
rc = cppc_get_perf_caps(0, &perf_caps);
|
||||
rc = amd_get_boost_ratio(0, &numerator, &denominator);
|
||||
if (rc) {
|
||||
pr_debug("Could not retrieve perf counters (%d)\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = amd_get_boost_ratio_numerator(0, &numerator);
|
||||
if (rc) {
|
||||
pr_debug("Could not retrieve highest performance (%d)\n", rc);
|
||||
return;
|
||||
}
|
||||
nominal_perf = perf_caps.nominal_perf;
|
||||
|
||||
if (!nominal_perf) {
|
||||
pr_debug("Could not retrieve nominal performance\n");
|
||||
pr_debug("Could not retrieve boost ratio (%d)\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* midpoint between max_boost and max_P */
|
||||
perf_ratio = (div_u64(numerator * SCHED_CAPACITY_SCALE, nominal_perf) + SCHED_CAPACITY_SCALE) >> 1;
|
||||
perf_ratio = (div_u64(numerator * SCHED_CAPACITY_SCALE, denominator) + SCHED_CAPACITY_SCALE) >> 1;
|
||||
|
||||
freq_invariance_set_perf_ratio(perf_ratio, false);
|
||||
}
|
||||
|
|
@ -225,35 +227,30 @@ int amd_detect_prefcore(bool *detected)
|
|||
EXPORT_SYMBOL_GPL(amd_detect_prefcore);
|
||||
|
||||
/**
|
||||
* amd_get_boost_ratio_numerator: Get the numerator to use for boost ratio calculation
|
||||
* @cpu: CPU to get numerator for.
|
||||
* @numerator: Output variable for numerator.
|
||||
* amd_get_effective_highest_perf: Get the effective highest performance value
|
||||
* @cpu: CPU to get highest performance for.
|
||||
*
|
||||
* Determine the numerator to use for calculating the boost ratio on
|
||||
* a CPU. On systems that support preferred cores, this will be a hardcoded
|
||||
* value. On other systems this will the highest performance register value.
|
||||
* Get the effective highest performance value for a CPU, accounting for
|
||||
* preferred cores and heterogeneous topologies. On systems with preferred
|
||||
* cores, this may be a hardcoded value. On heterogeneous systems, this
|
||||
* may be a per-CPU value. On other systems, this is the shared highest
|
||||
* performance value.
|
||||
*
|
||||
* If booting the system with amd-pstate enabled but preferred cores disabled then
|
||||
* the correct boost numerator will be returned to match hardware capabilities
|
||||
* even if the preferred cores scheduling hints are not enabled.
|
||||
*
|
||||
* Return: 0 for success, negative error code otherwise.
|
||||
* Return: Effective highest performance value, or negative error code.
|
||||
*/
|
||||
int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
|
||||
int amd_get_effective_highest_perf(unsigned int cpu)
|
||||
{
|
||||
bool prefcore;
|
||||
int ret;
|
||||
u32 tmp;
|
||||
|
||||
ret = amd_detect_prefcore(&prefcore);
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* without preferred cores, return the highest perf register value */
|
||||
if (!prefcore) {
|
||||
*numerator = boost_numerator;
|
||||
return 0;
|
||||
}
|
||||
if (!prefcore)
|
||||
return boost_numerator;
|
||||
|
||||
/*
|
||||
* For AMD CPUs with Family ID 19H and Model ID range 0x70 to 0x7f,
|
||||
|
|
@ -263,8 +260,7 @@ int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
|
|||
if (cpu_feature_enabled(X86_FEATURE_ZEN4)) {
|
||||
switch (boot_cpu_data.x86_model) {
|
||||
case 0x70 ... 0x7f:
|
||||
*numerator = CPPC_HIGHEST_PERF_PERFORMANCE;
|
||||
return 0;
|
||||
return CPPC_HIGHEST_PERF_PERFORMANCE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -272,28 +268,100 @@ int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
|
|||
|
||||
/* detect if running on heterogeneous design */
|
||||
if (cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES)) {
|
||||
switch (cpu_data(cpu).topo.cpu_type) {
|
||||
case TOPO_CPU_TYPE_UNKNOWN:
|
||||
case TOPO_CPU_TYPE_ANY:
|
||||
pr_warn("Undefined core type found for cpu %d\n", cpu);
|
||||
break;
|
||||
case TOPO_CPU_TYPE_PERFORMANCE:
|
||||
/* use the max scale for performance cores */
|
||||
*numerator = CPPC_HIGHEST_PERF_PERFORMANCE;
|
||||
return 0;
|
||||
case TOPO_CPU_TYPE_LOW_POWER:
|
||||
case TOPO_CPU_TYPE_EFFICIENCY:
|
||||
/* use the highest perf value for efficiency and low-power cores */
|
||||
ret = amd_get_highest_perf(cpu, &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
*numerator = tmp;
|
||||
return 0;
|
||||
}
|
||||
if (cpu_feature_enabled(X86_FEATURE_ZEN5) &&
|
||||
cpu_data(cpu).topo.cpu_type == TOPO_CPU_TYPE_PERFORMANCE)
|
||||
return CPPC_HIGHEST_PERF_PERFORMANCE;
|
||||
|
||||
/* Zen 5 efficiency, and Zen 6+ */
|
||||
ret = amd_get_highest_perf(cpu, &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
*numerator = CPPC_HIGHEST_PERF_PREFCORE;
|
||||
return CPPC_HIGHEST_PERF_PREFCORE;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amd_get_effective_highest_perf);
|
||||
|
||||
struct amd_max_freq {
|
||||
u32 performance;
|
||||
u32 efficiency;
|
||||
u32 low_power;
|
||||
};
|
||||
|
||||
static const struct amd_max_freq max_freq_zen6_client = {
|
||||
.performance = 5025,
|
||||
.efficiency = 3524,
|
||||
.low_power = 2399,
|
||||
};
|
||||
|
||||
int amd_get_max_frequency(unsigned int cpu)
|
||||
{
|
||||
enum x86_topology_cpu_type type = cpu_data(cpu).topo.cpu_type;
|
||||
const struct amd_max_freq *max_freq = NULL;
|
||||
|
||||
if (cpu_feature_enabled(X86_FEATURE_ZEN6) &&
|
||||
cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
|
||||
max_freq = &max_freq_zen6_client;
|
||||
|
||||
if (!max_freq)
|
||||
return 0;
|
||||
|
||||
switch (type) {
|
||||
case TOPO_CPU_TYPE_PERFORMANCE:
|
||||
return max_freq->performance;
|
||||
case TOPO_CPU_TYPE_EFFICIENCY:
|
||||
return max_freq->efficiency;
|
||||
case TOPO_CPU_TYPE_LOW_POWER:
|
||||
return max_freq->low_power;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amd_get_boost_ratio_numerator);
|
||||
EXPORT_SYMBOL_GPL(amd_get_max_frequency);
|
||||
|
||||
/**
|
||||
* amd_get_boost_ratio: Get numerator and denominator for boost ratio
|
||||
* @cpu: CPU to get the boost ratio for.
|
||||
* @numerator: Output variable for numerator.
|
||||
* @denominator: Output variable for denominator.
|
||||
*
|
||||
* Get the numerator and denominator for calculating the boost ratio.
|
||||
*
|
||||
* Return: 0 for success, negative error code otherwise.
|
||||
*/
|
||||
int amd_get_boost_ratio(unsigned int cpu, u64 *numerator, u64 *denominator)
|
||||
{
|
||||
struct cppc_perf_caps perf_caps;
|
||||
int max_frequency;
|
||||
int ret;
|
||||
|
||||
ret = cppc_get_perf_caps(cpu, &perf_caps);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
max_frequency = amd_get_max_frequency(cpu);
|
||||
/* Use frequency values if available */
|
||||
if (max_frequency && perf_caps.nominal_freq) {
|
||||
*numerator = max_frequency;
|
||||
*denominator = perf_caps.nominal_freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fall back to performance values */
|
||||
ret = amd_get_effective_highest_perf(cpu);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*numerator = ret;
|
||||
|
||||
*denominator = perf_caps.nominal_perf;
|
||||
if (!*denominator)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amd_get_boost_ratio);
|
||||
|
|
|
|||
|
|
@ -63,6 +63,25 @@ struct restore_data_record {
|
|||
unsigned long e820_checksum;
|
||||
};
|
||||
|
||||
static bool trim_e820_page_zero_ram(struct e820_entry *entry)
|
||||
{
|
||||
u64 lowmem_size;
|
||||
|
||||
/*
|
||||
* Page zero is BIOS-owned and registered as nosave. Boot loaders may
|
||||
* therefore omit part of its conventional RAM entry without changing
|
||||
* any memory available to the image. Preserve all other E820 types.
|
||||
*/
|
||||
if (entry->type != E820_TYPE_RAM || entry->addr >= PAGE_SIZE)
|
||||
return true;
|
||||
|
||||
lowmem_size = min_t(u64, entry->size, PAGE_SIZE - entry->addr);
|
||||
entry->addr += lowmem_size;
|
||||
entry->size -= lowmem_size;
|
||||
|
||||
return entry->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* compute_e820_crc32 - calculate crc32 of a given e820 table
|
||||
*
|
||||
|
|
@ -70,18 +89,38 @@ struct restore_data_record {
|
|||
*
|
||||
* Return: the resulting checksum
|
||||
*/
|
||||
static inline u32 compute_e820_crc32(struct e820_table *table)
|
||||
static u32 compute_e820_crc32(struct e820_table *table)
|
||||
{
|
||||
int size = offsetof(struct e820_table, entries) +
|
||||
sizeof(struct e820_entry) * table->nr_entries;
|
||||
struct e820_entry entry;
|
||||
u32 crc = ~0;
|
||||
u32 nr_entries = 0;
|
||||
u32 i;
|
||||
|
||||
return ~crc32_le(~0, (unsigned char const *)table, size);
|
||||
for (i = 0; i < table->nr_entries; i++) {
|
||||
entry = table->entries[i];
|
||||
if (trim_e820_page_zero_ram(&entry))
|
||||
nr_entries++;
|
||||
}
|
||||
|
||||
crc = crc32_le(crc, (unsigned char const *)&nr_entries,
|
||||
sizeof(nr_entries));
|
||||
|
||||
for (i = 0; i < table->nr_entries; i++) {
|
||||
entry = table->entries[i];
|
||||
if (!trim_e820_page_zero_ram(&entry))
|
||||
continue;
|
||||
|
||||
crc = crc32_le(crc, (unsigned char const *)&entry,
|
||||
sizeof(entry));
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
#define RESTORE_MAGIC 0x23456789ABCDEF02UL
|
||||
#define RESTORE_MAGIC 0x23456789ABCDEF03UL
|
||||
#else
|
||||
#define RESTORE_MAGIC 0x12345679UL
|
||||
#define RESTORE_MAGIC 0x1234567AUL
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -97,6 +97,7 @@ bool cpc_ffh_supported(void)
|
|||
int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
|
||||
{
|
||||
struct sbi_cppc_data data;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON_ONCE(irqs_disabled()))
|
||||
return -EPERM;
|
||||
|
|
@ -107,19 +108,27 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
|
|||
|
||||
data.reg = FFH_CPPC_SBI_REG(reg->address);
|
||||
|
||||
smp_call_function_single(cpu, sbi_cppc_read, &data, 1);
|
||||
ret = smp_call_function_single(cpu, sbi_cppc_read, &data, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.ret.error)
|
||||
return sbi_err_map_linux_errno(data.ret.error);
|
||||
|
||||
*val = data.ret.value;
|
||||
|
||||
return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
|
||||
return 0;
|
||||
} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
|
||||
data.reg = FFH_CPPC_CSR_NUM(reg->address);
|
||||
|
||||
smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);
|
||||
ret = smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.ret.error)
|
||||
return data.ret.error;
|
||||
|
||||
*val = data.ret.value;
|
||||
|
||||
return data.ret.error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
|
@ -128,6 +137,7 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
|
|||
int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)
|
||||
{
|
||||
struct sbi_cppc_data data;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON_ONCE(irqs_disabled()))
|
||||
return -EPERM;
|
||||
|
|
@ -139,14 +149,18 @@ int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)
|
|||
data.reg = FFH_CPPC_SBI_REG(reg->address);
|
||||
data.val = val;
|
||||
|
||||
smp_call_function_single(cpu, sbi_cppc_write, &data, 1);
|
||||
ret = smp_call_function_single(cpu, sbi_cppc_write, &data, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
|
||||
} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
|
||||
data.reg = FFH_CPPC_CSR_NUM(reg->address);
|
||||
data.val = val;
|
||||
|
||||
smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);
|
||||
ret = smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return data.ret.error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1069,19 +1069,26 @@ EXPORT_SYMBOL(acpi_dev_is_video_device);
|
|||
* @plat: pointer to acpi_platform_list table terminated by a NULL entry
|
||||
*
|
||||
* Return the matched index if the system is found in the platform list.
|
||||
* Otherwise, return a negative error code.
|
||||
* Return -ENODEV for no match, or another negative error code if a table
|
||||
* header could not be read and no entry matched.
|
||||
*/
|
||||
int acpi_match_platform_list(const struct acpi_platform_list *plat)
|
||||
{
|
||||
struct acpi_table_header hdr;
|
||||
acpi_status status;
|
||||
int ret = -ENODEV;
|
||||
int idx = 0;
|
||||
|
||||
if (acpi_disabled)
|
||||
return -ENODEV;
|
||||
|
||||
for (; plat->oem_id[0]; plat++, idx++) {
|
||||
if (ACPI_FAILURE(acpi_get_table_header(plat->table, 0, &hdr)))
|
||||
status = acpi_get_table_header(plat->table, 0, &hdr);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
if (status != AE_NOT_FOUND)
|
||||
ret = status == AE_NO_MEMORY ? -ENOMEM : -EIO;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(plat->oem_id, hdr.oem_id, ACPI_OEM_ID_SIZE))
|
||||
continue;
|
||||
|
|
@ -1096,6 +1103,6 @@ int acpi_match_platform_list(const struct acpi_platform_list *plat)
|
|||
return idx;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_match_platform_list);
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ EXPORT_SYMBOL_GPL(pm_clk_add);
|
|||
*
|
||||
* Add the clock to the list of clocks used for the power management of @dev.
|
||||
* The power-management code will take control of the clock reference, so
|
||||
* callers should not call clk_put() on @clk after this function sucessfully
|
||||
* callers should not call clk_put() on @clk after this function successfully
|
||||
* returned.
|
||||
*/
|
||||
int pm_clk_add_clk(struct device *dev, struct clk *clk)
|
||||
|
|
|
|||
|
|
@ -802,6 +802,7 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy
|
|||
const char *info = NULL;
|
||||
bool skip_resume;
|
||||
int error = 0;
|
||||
DECLARE_DPM_WATCHDOG_ON_STACK(wd);
|
||||
|
||||
TRACE_DEVICE(dev);
|
||||
TRACE_RESUME(0);
|
||||
|
|
@ -827,6 +828,7 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy
|
|||
if (!dpm_wait_for_superior(dev, async))
|
||||
goto Out;
|
||||
|
||||
dpm_watchdog_set(&wd, dev);
|
||||
skip_resume = dev_pm_skip_resume(dev);
|
||||
/*
|
||||
* If the driver callback is skipped below or by the middle layer
|
||||
|
|
@ -871,6 +873,7 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy
|
|||
error = dpm_run_callback(callback, dev, state, info);
|
||||
|
||||
Skip:
|
||||
dpm_watchdog_clear(&wd);
|
||||
dev->power.is_noirq_suspended = false;
|
||||
|
||||
Out:
|
||||
|
|
@ -971,6 +974,7 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
|
|||
pm_callback_t callback = NULL;
|
||||
const char *info = NULL;
|
||||
int error = 0;
|
||||
DECLARE_DPM_WATCHDOG_ON_STACK(wd);
|
||||
|
||||
TRACE_DEVICE(dev);
|
||||
TRACE_RESUME(0);
|
||||
|
|
@ -987,6 +991,7 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
|
|||
if (!dpm_wait_for_superior(dev, async))
|
||||
goto Out;
|
||||
|
||||
dpm_watchdog_set(&wd, dev);
|
||||
if (dev->pm_domain) {
|
||||
info = "early power domain ";
|
||||
callback = pm_late_early_op(&dev->pm_domain->ops, state);
|
||||
|
|
@ -1004,7 +1009,7 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
|
|||
goto Run;
|
||||
|
||||
if (dev_pm_skip_resume(dev))
|
||||
goto Skip;
|
||||
goto End;
|
||||
|
||||
if (dev->driver && dev->driver->pm) {
|
||||
info = "early driver ";
|
||||
|
|
@ -1014,6 +1019,9 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
|
|||
Run:
|
||||
error = dpm_run_callback(callback, dev, state, info);
|
||||
|
||||
End:
|
||||
dpm_watchdog_clear(&wd);
|
||||
|
||||
Skip:
|
||||
dev->power.is_late_suspended = false;
|
||||
pm_runtime_enable(dev);
|
||||
|
|
@ -1281,10 +1289,12 @@ static void device_complete(struct device *dev, pm_message_t state)
|
|||
{
|
||||
void (*callback)(struct device *) = NULL;
|
||||
const char *info = NULL;
|
||||
DECLARE_DPM_WATCHDOG_ON_STACK(wd);
|
||||
|
||||
if (dev->power.syscore)
|
||||
goto out;
|
||||
|
||||
dpm_watchdog_set(&wd, dev);
|
||||
device_lock(dev);
|
||||
|
||||
if (dev->pm_domain) {
|
||||
|
|
@ -1312,6 +1322,7 @@ static void device_complete(struct device *dev, pm_message_t state)
|
|||
}
|
||||
|
||||
device_unlock(dev);
|
||||
dpm_watchdog_clear(&wd);
|
||||
|
||||
out:
|
||||
/* If enabling runtime PM for the device is blocked, unblock it. */
|
||||
|
|
@ -1471,6 +1482,7 @@ static pm_message_t resume_event(pm_message_t sleep_state)
|
|||
case PM_EVENT_QUIESCE:
|
||||
return PMSG_RECOVER;
|
||||
case PM_EVENT_HIBERNATE:
|
||||
case PM_EVENT_POWEROFF:
|
||||
return PMSG_RESTORE;
|
||||
}
|
||||
return PMSG_ON;
|
||||
|
|
@ -1508,6 +1520,7 @@ static void device_suspend_noirq(struct device *dev, pm_message_t state, bool as
|
|||
pm_callback_t callback = NULL;
|
||||
const char *info = NULL;
|
||||
int error = 0;
|
||||
DECLARE_DPM_WATCHDOG_ON_STACK(wd);
|
||||
|
||||
TRACE_DEVICE(dev);
|
||||
TRACE_SUSPEND(0);
|
||||
|
|
@ -1520,6 +1533,7 @@ static void device_suspend_noirq(struct device *dev, pm_message_t state, bool as
|
|||
if (dev->power.syscore || dev->power.direct_complete)
|
||||
goto Complete;
|
||||
|
||||
dpm_watchdog_set(&wd, dev);
|
||||
if (dev->pm_domain) {
|
||||
info = "noirq power domain ";
|
||||
callback = pm_noirq_op(&dev->pm_domain->ops, state);
|
||||
|
|
@ -1550,7 +1564,7 @@ static void device_suspend_noirq(struct device *dev, pm_message_t state, bool as
|
|||
WRITE_ONCE(async_error, error);
|
||||
dpm_save_failed_dev(dev_name(dev));
|
||||
pm_dev_err(dev, state, async ? " async noirq" : " noirq", error);
|
||||
goto Complete;
|
||||
goto End;
|
||||
}
|
||||
|
||||
Skip:
|
||||
|
|
@ -1569,6 +1583,9 @@ static void device_suspend_noirq(struct device *dev, pm_message_t state, bool as
|
|||
if (dev->power.must_resume)
|
||||
dpm_superior_set_must_resume(dev);
|
||||
|
||||
End:
|
||||
dpm_watchdog_clear(&wd);
|
||||
|
||||
Complete:
|
||||
complete_all(&dev->power.completion);
|
||||
TRACE_SUSPEND(error);
|
||||
|
|
@ -1703,6 +1720,7 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
|
|||
pm_callback_t callback = NULL;
|
||||
const char *info = NULL;
|
||||
int error = 0;
|
||||
DECLARE_DPM_WATCHDOG_ON_STACK(wd);
|
||||
|
||||
TRACE_DEVICE(dev);
|
||||
TRACE_SUSPEND(0);
|
||||
|
|
@ -1720,6 +1738,8 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
|
|||
if (dev->power.direct_complete)
|
||||
goto Complete;
|
||||
|
||||
dpm_watchdog_set(&wd, dev);
|
||||
|
||||
/*
|
||||
* After this point, any runtime PM operations targeting the device
|
||||
* will fail until the corresponding pm_runtime_enable() call in
|
||||
|
|
@ -1761,13 +1781,16 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
|
|||
dpm_save_failed_dev(dev_name(dev));
|
||||
pm_dev_err(dev, state, async ? " async late" : " late", error);
|
||||
pm_runtime_enable(dev);
|
||||
goto Complete;
|
||||
goto End;
|
||||
}
|
||||
dpm_propagate_wakeup_to_parent(dev);
|
||||
|
||||
Skip:
|
||||
dev->power.is_late_suspended = true;
|
||||
|
||||
End:
|
||||
dpm_watchdog_clear(&wd);
|
||||
|
||||
Complete:
|
||||
TRACE_SUSPEND(error);
|
||||
complete_all(&dev->power.completion);
|
||||
|
|
@ -2201,6 +2224,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
|
|||
int (*callback)(struct device *) = NULL;
|
||||
bool smart_suspend;
|
||||
int ret = 0;
|
||||
DECLARE_DPM_WATCHDOG_ON_STACK(wd);
|
||||
|
||||
/*
|
||||
* If a device's parent goes into runtime suspend at the wrong time,
|
||||
|
|
@ -2220,6 +2244,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
|
|||
if (dev->power.syscore)
|
||||
return 0;
|
||||
|
||||
dpm_watchdog_set(&wd, dev);
|
||||
device_lock(dev);
|
||||
|
||||
dev->power.wakeup_path = false;
|
||||
|
|
@ -2245,6 +2270,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
|
|||
|
||||
unlock:
|
||||
device_unlock(dev);
|
||||
dpm_watchdog_clear(&wd);
|
||||
|
||||
if (ret < 0) {
|
||||
suspend_report_result(dev, callback, ret);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/test.h>
|
||||
|
||||
|
|
@ -229,6 +230,61 @@ static void pm_runtime_probe_active_test(struct kunit *test)
|
|||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
|
||||
}
|
||||
|
||||
static void pm_runtime_supplier_suspend_test(struct kunit *test)
|
||||
{
|
||||
struct device *dev = kunit_device_register(test, DEVICE_NAME);
|
||||
struct device *supplier = kunit_device_register(test, DEVICE_NAME "_supplier");
|
||||
struct device *norpm_supplier = kunit_device_register(test, DEVICE_NAME "_norpm_supplier");
|
||||
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supplier);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, norpm_supplier);
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, device_link_add(dev, supplier, DL_FLAG_PM_RUNTIME));
|
||||
KUNIT_ASSERT_NOT_NULL(test, device_link_add(dev, norpm_supplier, 0));
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_enable(supplier);
|
||||
pm_runtime_enable(norpm_supplier);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(supplier));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(norpm_supplier));
|
||||
|
||||
/*
|
||||
* Resume the device and RPM-linked supplier; non-RPM-linked supplier
|
||||
* stays suspended.
|
||||
*/
|
||||
KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_active(supplier));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(norpm_supplier));
|
||||
|
||||
/* Also resume the non-RPM-linked supplier. */
|
||||
KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(norpm_supplier));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_active(norpm_supplier));
|
||||
|
||||
pm_runtime_put_noidle(norpm_supplier);
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_active(norpm_supplier));
|
||||
|
||||
/*
|
||||
* Suspend device. This should only suspend the device and its
|
||||
* RPM-linked supplier, not the non-RPM-linked supplier.
|
||||
*/
|
||||
KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
|
||||
/* Note: supplier suspend is async, so we have to flush the queue. */
|
||||
flush_workqueue(pm_wq);
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(supplier));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_active(norpm_supplier));
|
||||
|
||||
/* Now suspend non-RPM-linked supplier. */
|
||||
KUNIT_EXPECT_EQ(test, 0, pm_runtime_suspend(norpm_supplier));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(supplier));
|
||||
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(norpm_supplier));
|
||||
}
|
||||
|
||||
static struct kunit_case pm_runtime_test_cases[] = {
|
||||
KUNIT_CASE(pm_runtime_depth_test),
|
||||
KUNIT_CASE(pm_runtime_already_suspended_test),
|
||||
|
|
@ -236,6 +292,7 @@ static struct kunit_case pm_runtime_test_cases[] = {
|
|||
KUNIT_CASE(pm_runtime_disabled_test),
|
||||
KUNIT_CASE(pm_runtime_error_test),
|
||||
KUNIT_CASE(pm_runtime_probe_active_test),
|
||||
KUNIT_CASE(pm_runtime_supplier_suspend_test),
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -162,14 +162,14 @@ static void pm_runtime_cancel_pending(struct device *dev)
|
|||
dev->power.request = RPM_REQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* pm_runtime_autosuspend_expiration - Get a device's autosuspend-delay expiration time.
|
||||
* @dev: Device to handle.
|
||||
*
|
||||
* Compute the autosuspend-delay expiration time based on the device's
|
||||
* power.last_busy time. If the delay has already expired or is disabled
|
||||
* (negative) or the power.use_autosuspend flag isn't set, return 0.
|
||||
* Otherwise return the expiration time in nanoseconds (adjusted to be nonzero).
|
||||
* Otherwise return the expiration time in nanoseconds.
|
||||
*
|
||||
* This function may be called either with or without dev->power.lock held.
|
||||
* Either way it can be racy, since power.last_busy may be updated at any time.
|
||||
|
|
@ -200,7 +200,7 @@ static int dev_memalloc_noio(struct device *dev, void *data)
|
|||
return dev->power.memalloc_noio;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* pm_runtime_set_memalloc_noio - Set a device's memalloc_noio flag.
|
||||
* @dev: Device to handle.
|
||||
* @enable: True for setting the flag and False for clearing the flag.
|
||||
|
|
@ -361,7 +361,8 @@ static void rpm_suspend_suppliers(struct device *dev)
|
|||
|
||||
list_for_each_entry_rcu(link, &dev->links.suppliers, c_node,
|
||||
device_links_read_lock_held())
|
||||
pm_request_idle(link->supplier);
|
||||
if (device_link_test(link, DL_FLAG_PM_RUNTIME))
|
||||
pm_request_idle(link->supplier);
|
||||
|
||||
device_links_read_unlock(idx);
|
||||
}
|
||||
|
|
@ -1043,6 +1044,11 @@ static enum hrtimer_restart pm_suspend_timer_fn(struct hrtimer *timer)
|
|||
* pm_schedule_suspend - Set up a timer to submit a suspend request in future.
|
||||
* @dev: Device to suspend.
|
||||
* @delay: Time to wait before submitting a suspend request, in milliseconds.
|
||||
*
|
||||
* Return:
|
||||
* * %1: Success; @dev is already %RPM_SUSPENDED.
|
||||
* * %0: Success.
|
||||
* * Error code on failure.
|
||||
*/
|
||||
int pm_schedule_suspend(struct device *dev, unsigned int delay)
|
||||
{
|
||||
|
|
@ -1096,16 +1102,15 @@ static int rpm_drop_usage_count(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* __pm_runtime_idle - Entry point for runtime idle operations.
|
||||
* __pm_runtime_idle - Core entry point for runtime idle operations.
|
||||
* @dev: Device to send idle notification for.
|
||||
* @rpmflags: Flag bits.
|
||||
*
|
||||
* If the RPM_GET_PUT flag is set, decrement the device's usage count and
|
||||
* return immediately if it is larger than zero (if it becomes negative, log a
|
||||
* warning, increment it, and return an error). Then carry out an idle
|
||||
* notification, either synchronous or asynchronous.
|
||||
* Carry out an idle check for @dev, either synchronous or asynchronous.
|
||||
* If %RPM_GET_PUT is set in @rpmflags, decrement the device's usage count
|
||||
* first, proceeding with idle notification only if the counter drops to zero.
|
||||
*
|
||||
* This routine may be called in atomic context if the RPM_ASYNC flag is set,
|
||||
* This routine may be called in atomic context if the %RPM_ASYNC flag is set,
|
||||
* or if pm_runtime_irq_safe() has been called.
|
||||
*/
|
||||
int __pm_runtime_idle(struct device *dev, int rpmflags)
|
||||
|
|
@ -1134,16 +1139,15 @@ int __pm_runtime_idle(struct device *dev, int rpmflags)
|
|||
EXPORT_SYMBOL_GPL(__pm_runtime_idle);
|
||||
|
||||
/**
|
||||
* __pm_runtime_suspend - Entry point for runtime put/suspend operations.
|
||||
* __pm_runtime_suspend - Core entry point for runtime put/suspend operations.
|
||||
* @dev: Device to suspend.
|
||||
* @rpmflags: Flag bits.
|
||||
*
|
||||
* If the RPM_GET_PUT flag is set, decrement the device's usage count and
|
||||
* return immediately if it is larger than zero (if it becomes negative, log a
|
||||
* warning, increment it, and return an error). Then carry out a suspend,
|
||||
* either synchronous or asynchronous.
|
||||
* Carry out a suspend operation for @dev, either synchronous or asynchronous.
|
||||
* If %RPM_GET_PUT is set in @rpmflags, decrement the device's usage count
|
||||
* first, proceeding with suspend only if the counter drops to zero.
|
||||
*
|
||||
* This routine may be called in atomic context if the RPM_ASYNC flag is set,
|
||||
* This routine may be called in atomic context if the %RPM_ASYNC flag is set,
|
||||
* or if pm_runtime_irq_safe() has been called.
|
||||
*/
|
||||
int __pm_runtime_suspend(struct device *dev, int rpmflags)
|
||||
|
|
@ -1172,14 +1176,15 @@ int __pm_runtime_suspend(struct device *dev, int rpmflags)
|
|||
EXPORT_SYMBOL_GPL(__pm_runtime_suspend);
|
||||
|
||||
/**
|
||||
* __pm_runtime_resume - Entry point for runtime resume operations.
|
||||
* __pm_runtime_resume - Core entry point for runtime resume operations.
|
||||
* @dev: Device to resume.
|
||||
* @rpmflags: Flag bits.
|
||||
*
|
||||
* If the RPM_GET_PUT flag is set, increment the device's usage count. Then
|
||||
* carry out a resume, either synchronous or asynchronous.
|
||||
* Carry out a runtime resume operation for @dev, either synchronous or
|
||||
* asynchronous. If %RPM_GET_PUT is set in @rpmflags, increment the device's
|
||||
* usage count first, then bring the device to %RPM_ACTIVE state.
|
||||
*
|
||||
* This routine may be called in atomic context if the RPM_ASYNC flag is set,
|
||||
* This routine may be called in atomic context if the %RPM_ASYNC flag is set,
|
||||
* or if pm_runtime_irq_safe() has been called.
|
||||
*/
|
||||
int __pm_runtime_resume(struct device *dev, int rpmflags)
|
||||
|
|
@ -1249,14 +1254,16 @@ static int pm_runtime_get_conditional(struct device *dev, bool ign_usage_count)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_runtime_get_if_active - Bump up runtime PM usage counter if the device is
|
||||
* in active state
|
||||
* pm_runtime_get_if_active - Conditionally bump up runtime PM usage counter.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Increment the runtime PM usage counter of @dev if its runtime PM status is
|
||||
* %RPM_ACTIVE, in which case it returns 1. If the device is in a different
|
||||
* state, 0 is returned. -EINVAL is returned if runtime PM is disabled for the
|
||||
* device, in which case also the usage_count will remain unmodified.
|
||||
* already %RPM_ACTIVE.
|
||||
*
|
||||
* Return:
|
||||
* * %-EINVAL: Runtime PM is disabled for @dev. The usage counter is not incremented.
|
||||
* * %1: Success; usage counter is incremented.
|
||||
* * %0: @dev was not active.
|
||||
*/
|
||||
int pm_runtime_get_if_active(struct device *dev)
|
||||
{
|
||||
|
|
@ -1268,17 +1275,15 @@ EXPORT_SYMBOL_GPL(pm_runtime_get_if_active);
|
|||
* pm_runtime_get_if_in_use - Conditionally bump up runtime PM usage counter.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Increment the runtime PM usage counter of @dev if its runtime PM status is
|
||||
* %RPM_ACTIVE and its runtime PM usage counter is greater than 0 or it is not
|
||||
* ignoring children and its active child count is nonzero. 1 is returned in
|
||||
* this case.
|
||||
* Increment the runtime PM usage counter of @dev if it is "in use." A device
|
||||
* is considered in use if its runtime PM status is %RPM_ACTIVE and its runtime
|
||||
* PM usage counter is greater than 0, or if it is not ignoring children and
|
||||
* its active child count is nonzero.
|
||||
*
|
||||
* If @dev is in a different state or it is not in use (that is, its usage
|
||||
* counter is 0, or it is ignoring children, or its active child count is 0),
|
||||
* 0 is returned.
|
||||
*
|
||||
* -EINVAL is returned if runtime PM is disabled for the device, in which case
|
||||
* also the usage counter of @dev is not updated.
|
||||
* Return:
|
||||
* * %-EINVAL: Runtime PM is disabled for @dev. The usage counter is not incremented.
|
||||
* * %1: Success; usage counter is incremented.
|
||||
* * %0: @dev was not in use; usage counter is not incremented.
|
||||
*/
|
||||
int pm_runtime_get_if_in_use(struct device *dev)
|
||||
{
|
||||
|
|
@ -1287,7 +1292,7 @@ int pm_runtime_get_if_in_use(struct device *dev)
|
|||
EXPORT_SYMBOL_GPL(pm_runtime_get_if_in_use);
|
||||
|
||||
/**
|
||||
* __pm_runtime_set_status - Set runtime PM status of a device.
|
||||
* __pm_runtime_set_status - Set runtime PM status of a device and clear errors.
|
||||
* @dev: Device to handle.
|
||||
* @status: New runtime PM status of the device.
|
||||
*
|
||||
|
|
@ -1306,7 +1311,7 @@ EXPORT_SYMBOL_GPL(pm_runtime_get_if_in_use);
|
|||
* If @dev has any suppliers (as reflected by device links to them), and @status
|
||||
* is RPM_ACTIVE, they will be activated upfront and if the activation of one
|
||||
* of them fails, the status of @dev will be changed to RPM_SUSPENDED (instead
|
||||
* of the @status value) and the suppliers will be deacticated on exit. The
|
||||
* of the @status value) and the suppliers will be deactivated on exit. The
|
||||
* error returned by the failing supplier activation will be returned in that
|
||||
* case.
|
||||
*/
|
||||
|
|
@ -1462,11 +1467,13 @@ static void __pm_runtime_barrier(struct device *dev)
|
|||
* pm_runtime_barrier - Flush pending requests and wait for completions.
|
||||
* @dev: Device to handle.
|
||||
*
|
||||
* Prevent the device from being suspended by incrementing its usage counter and
|
||||
* if there's a pending resume request for the device, wake the device up.
|
||||
* Next, make sure that all pending requests for the device have been flushed
|
||||
* from pm_wq and wait for all runtime PM operations involving the device in
|
||||
* progress to complete.
|
||||
* If the device has a pending resume request, resume it synchronously. For all
|
||||
* other request types, cancel any queued request, and wait for running
|
||||
* operations to complete.
|
||||
*
|
||||
* Note that this is intentionally asymmetric, as it guarantees any queued
|
||||
* asynchronous resume request will complete, but it may cancel asynchronous
|
||||
* suspend requests.
|
||||
*/
|
||||
void pm_runtime_barrier(struct device *dev)
|
||||
{
|
||||
|
|
@ -1550,8 +1557,16 @@ void __pm_runtime_disable(struct device *dev, bool check_resume)
|
|||
EXPORT_SYMBOL_GPL(__pm_runtime_disable);
|
||||
|
||||
/**
|
||||
* pm_runtime_enable - Enable runtime PM of a device.
|
||||
* pm_runtime_enable - Enable runtime PM for a device.
|
||||
* @dev: Device to handle.
|
||||
*
|
||||
* Enable runtime PM transitions for @dev by decrementing its disable counter.
|
||||
* Once the counter reaches zero, the PM core is permitted to execute runtime
|
||||
* PM callbacks for @dev as power conditions change.
|
||||
*
|
||||
* Callers should ensure that the device's runtime PM status accurately reflects
|
||||
* its physical hardware state (via pm_runtime_set_active() or
|
||||
* pm_runtime_set_suspended()) before enabling runtime PM.
|
||||
*/
|
||||
void pm_runtime_enable(struct device *dev)
|
||||
{
|
||||
|
|
@ -1591,7 +1606,6 @@ static void pm_runtime_set_suspended_action(void *data)
|
|||
|
||||
/**
|
||||
* devm_pm_runtime_set_active_enabled - set_active version of devm_pm_runtime_enable.
|
||||
*
|
||||
* @dev: Device to handle.
|
||||
*/
|
||||
int devm_pm_runtime_set_active_enabled(struct device *dev)
|
||||
|
|
@ -1618,11 +1632,10 @@ static void pm_runtime_disable_action(void *data)
|
|||
|
||||
/**
|
||||
* devm_pm_runtime_enable - devres-enabled version of pm_runtime_enable.
|
||||
* @dev: Device to handle.
|
||||
*
|
||||
* NOTE: this will also handle calling pm_runtime_dont_use_autosuspend() for
|
||||
* you at driver exit time if needed.
|
||||
*
|
||||
* @dev: Device to handle.
|
||||
*/
|
||||
int devm_pm_runtime_enable(struct device *dev)
|
||||
{
|
||||
|
|
@ -1639,7 +1652,6 @@ static void pm_runtime_put_noidle_action(void *data)
|
|||
|
||||
/**
|
||||
* devm_pm_runtime_get_noresume - devres-enabled version of pm_runtime_get_noresume.
|
||||
*
|
||||
* @dev: Device to handle.
|
||||
*/
|
||||
int devm_pm_runtime_get_noresume(struct device *dev)
|
||||
|
|
@ -1874,6 +1886,9 @@ void pm_runtime_reinit(struct device *dev)
|
|||
if (dev->power.runtime_status == RPM_ACTIVE)
|
||||
pm_runtime_set_suspended(dev);
|
||||
|
||||
if (dev->power.use_autosuspend)
|
||||
pm_runtime_dont_use_autosuspend(dev);
|
||||
|
||||
if (dev->power.irq_safe) {
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->power.irq_safe = 0;
|
||||
|
|
|
|||
|
|
@ -1533,7 +1533,7 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
|
|||
*/
|
||||
static void omap_dm_timer_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dmtimer *timer;
|
||||
struct dmtimer *timer, *found = NULL;
|
||||
unsigned long flags;
|
||||
int ret = -EINVAL;
|
||||
|
||||
|
|
@ -1541,14 +1541,17 @@ static void omap_dm_timer_remove(struct platform_device *pdev)
|
|||
list_for_each_entry(timer, &omap_timer_list, node)
|
||||
if (!strcmp(dev_name(&timer->pdev->dev),
|
||||
dev_name(&pdev->dev))) {
|
||||
if (!(timer->capability & OMAP_TIMER_ALWON))
|
||||
cpu_pm_unregister_notifier(&timer->nb);
|
||||
list_del(&timer->node);
|
||||
found = timer;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&dm_timer_lock, flags);
|
||||
|
||||
/* Unregister outside the lock: cpu_pm_unregister_notifier() may sleep. */
|
||||
if (found && !(found->capability & OMAP_TIMER_ALWON))
|
||||
cpu_pm_unregister_notifier(&found->nb);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -288,6 +288,7 @@ endif
|
|||
if LOONGARCH
|
||||
config LOONGSON3_CPUFREQ
|
||||
tristate "Loongson3 CPUFreq Driver"
|
||||
depends on MACH_LOONGSON64
|
||||
help
|
||||
This option adds a CPUFreq driver for Loongson processors which
|
||||
support software configurable cpu frequency.
|
||||
|
|
|
|||
|
|
@ -626,7 +626,7 @@ static int acpi_cpufreq_blacklist(struct cpuinfo_x86 *c)
|
|||
static u64 get_max_boost_ratio(unsigned int cpu, u64 *nominal_freq)
|
||||
{
|
||||
struct cppc_perf_caps perf_caps;
|
||||
u64 highest_perf, nominal_perf;
|
||||
u64 numerator, denominator;
|
||||
int ret;
|
||||
|
||||
if (acpi_pstate_strict)
|
||||
|
|
@ -640,32 +640,31 @@ static u64 get_max_boost_ratio(unsigned int cpu, u64 *nominal_freq)
|
|||
}
|
||||
|
||||
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
|
||||
ret = amd_get_boost_ratio_numerator(cpu, &highest_perf);
|
||||
ret = amd_get_boost_ratio(cpu, &numerator, &denominator);
|
||||
if (ret) {
|
||||
pr_debug("CPU%d: Unable to get boost ratio numerator (%d)\n",
|
||||
pr_debug("CPU%d: Unable to get boost ratio (%d)\n",
|
||||
cpu, ret);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
highest_perf = perf_caps.highest_perf;
|
||||
numerator = perf_caps.highest_perf;
|
||||
denominator = perf_caps.nominal_perf;
|
||||
}
|
||||
|
||||
nominal_perf = perf_caps.nominal_perf;
|
||||
|
||||
if (nominal_freq)
|
||||
*nominal_freq = perf_caps.nominal_freq * 1000;
|
||||
|
||||
if (!highest_perf || !nominal_perf) {
|
||||
if (!numerator || !denominator) {
|
||||
pr_debug("CPU%d: highest or nominal performance missing\n", cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (highest_perf < nominal_perf) {
|
||||
if (numerator < denominator) {
|
||||
pr_debug("CPU%d: nominal performance above highest\n", cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return div_u64(highest_perf << SCHED_CAPACITY_SHIFT, nominal_perf);
|
||||
return div_u64(numerator << SCHED_CAPACITY_SHIFT, denominator);
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ static int airoha_cpufreq_probe(struct platform_device *pdev)
|
|||
/* Attach PM for OPP */
|
||||
ret = dev_pm_domain_attach_list(cpu_dev, &attach_data,
|
||||
&priv->pd_list);
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
goto clear_opp_config;
|
||||
|
||||
cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
|
||||
|
|
|
|||
|
|
@ -216,7 +216,14 @@ static int amd_pstate_ut_check_perf(u32 index)
|
|||
|
||||
/*
|
||||
* Check if frequency values are reasonable.
|
||||
* max_freq >= nominal_freq > lowest_nonlinear_freq > min_freq > 0
|
||||
* nominal_freq > lowest_nonlinear_freq >= min_freq > 0
|
||||
* max_freq >= lowest_nonlinear_freq
|
||||
*
|
||||
* On most parts the boost frequency is the highest, i.e.
|
||||
* max_freq >= nominal_freq. On heterogeneous designs (e.g. Zen6) a
|
||||
* low-power core can have a maximum frequency below the shared nominal
|
||||
* reference frequency, so only require the boost frequency to sit above
|
||||
* the lowest nonlinear frequency here.
|
||||
* check max freq when set support boost mode.
|
||||
*/
|
||||
static int amd_pstate_ut_check_freq(u32 index)
|
||||
|
|
@ -226,23 +233,44 @@ static int amd_pstate_ut_check_freq(u32 index)
|
|||
for_each_online_cpu(cpu) {
|
||||
struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
|
||||
struct amd_cpudata *cpudata;
|
||||
union perf_cached perf;
|
||||
|
||||
policy = cpufreq_cpu_get(cpu);
|
||||
if (!policy)
|
||||
continue;
|
||||
cpudata = policy->driver_data;
|
||||
|
||||
if (!((policy->cpuinfo.max_freq >= cpudata->nominal_freq) &&
|
||||
cpudata = policy->driver_data;
|
||||
perf = READ_ONCE(cpudata->perf);
|
||||
|
||||
if (!((policy->cpuinfo.max_freq >= cpudata->lowest_nonlinear_freq) &&
|
||||
(cpudata->nominal_freq > cpudata->lowest_nonlinear_freq) &&
|
||||
(cpudata->lowest_nonlinear_freq >= policy->cpuinfo.min_freq) &&
|
||||
(policy->cpuinfo.min_freq > 0))) {
|
||||
pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n",
|
||||
pr_err("%s cpu%d max=%d, nominal=%d, lowest_nonlinear=%d, min=%d, the formula is incorrect!\n",
|
||||
__func__, cpu, policy->cpuinfo.max_freq, cpudata->nominal_freq,
|
||||
cpudata->lowest_nonlinear_freq, policy->cpuinfo.min_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cpudata->lowest_nonlinear_freq != policy->min) {
|
||||
if (perf.bios_min_perf) {
|
||||
u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq,
|
||||
perf.bios_min_perf);
|
||||
|
||||
/*
|
||||
* User set bios_min_freq cannot be trusted to
|
||||
* be within the driver limits. Clamp it similar
|
||||
* to cpufreq_verify_within_cpu_limits().
|
||||
*/
|
||||
bios_min_freq = clamp_t(u32, bios_min_freq,
|
||||
policy->cpuinfo.min_freq,
|
||||
policy->cpuinfo.max_freq);
|
||||
|
||||
if (bios_min_freq != policy->min) {
|
||||
pr_err("%s cpu%d bios_min_freq=%d policy_min=%d, they should be equal!\n",
|
||||
__func__, cpu, bios_min_freq, policy->min);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (cpudata->lowest_nonlinear_freq != policy->min) {
|
||||
pr_err("%s cpu%d cpudata_lowest_nonlinear_freq=%d policy_min=%d, they should be equal!\n",
|
||||
__func__, cpu, cpudata->lowest_nonlinear_freq, policy->min);
|
||||
return -EINVAL;
|
||||
|
|
@ -276,12 +304,12 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode)
|
|||
|
||||
static int amd_pstate_ut_epp(u32 index)
|
||||
{
|
||||
static const char * const epp_strings[] = {
|
||||
"dynamic",
|
||||
"power",
|
||||
"balance_power",
|
||||
"balance_performance",
|
||||
"performance",
|
||||
enum energy_perf_value_index epp_modes[] = {
|
||||
EPP_INDEX_DYNAMIC,
|
||||
EPP_INDEX_POWERSAVE,
|
||||
EPP_INDEX_BALANCE_POWERSAVE,
|
||||
EPP_INDEX_BALANCE_PERFORMANCE,
|
||||
EPP_INDEX_PERFORMANCE,
|
||||
};
|
||||
char *buf __free(cleanup_page) = NULL;
|
||||
struct cpufreq_policy *policy = NULL;
|
||||
|
|
@ -350,9 +378,9 @@ static int amd_pstate_ut_epp(u32 index)
|
|||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(epp_strings); i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(epp_modes); i++) {
|
||||
memset(buf, 0, PAGE_SIZE);
|
||||
snprintf(buf, PAGE_SIZE, "%s", epp_strings[i]);
|
||||
snprintf(buf, PAGE_SIZE, "%s", energy_perf_strings[epp_modes[i]]);
|
||||
ret = store_energy_performance_preference(policy, buf, strlen(buf));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
|
@ -368,8 +396,24 @@ static int amd_pstate_ut_epp(u32 index)
|
|||
*/
|
||||
strreplace(buf, '(', '\0');
|
||||
|
||||
if (strcmp(buf, epp_strings[i])) {
|
||||
pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]);
|
||||
if (strcmp(buf, energy_perf_strings[epp_modes[i]])) {
|
||||
u8 epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
|
||||
|
||||
/*
|
||||
* The preference read back may legitimately differ from the one
|
||||
* written: some platforms (e.g. Zen6) program the same raw EPP
|
||||
* value for more than one named preference, so show() reports the
|
||||
* first name that matches that value.
|
||||
*
|
||||
* If strings mismatch, confirm the CPU is programmed with the correct
|
||||
* EPP value for the mode. This verifies hardware programming while
|
||||
* tolerating such aliasing in driver.
|
||||
*/
|
||||
if (epp == amd_pstate_cpu_epp_values(cpudata->cpu_type)[epp_modes[i]])
|
||||
continue;
|
||||
|
||||
pr_err("String EPP value mismatch: %s != %s\n",
|
||||
buf, energy_perf_strings[epp_modes[i]]);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -385,11 +429,12 @@ static int amd_pstate_ut_epp(u32 index)
|
|||
* restore it here before dropping policy reference.
|
||||
*/
|
||||
if (orig_dynamic_epp) {
|
||||
const char * const dynamic_string = energy_perf_strings[epp_modes[0]];
|
||||
int ret2;
|
||||
|
||||
ret2 = store_energy_performance_preference(policy,
|
||||
epp_strings[0],
|
||||
strlen(epp_strings[0]));
|
||||
dynamic_string,
|
||||
strlen(dynamic_string));
|
||||
if (!ret && (ret2 < 0))
|
||||
ret = ret2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@
|
|||
#include <acpi/cppc_acpi.h>
|
||||
|
||||
#include <asm/msr.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
|
|
@ -55,10 +54,10 @@
|
|||
#define AMD_PSTATE_TRANSITION_DELAY 1000
|
||||
#define AMD_PSTATE_FAST_CPPC_TRANSITION_DELAY 600
|
||||
|
||||
#define AMD_CPPC_EPP_PERFORMANCE 0x00
|
||||
#define AMD_CPPC_EPP_BALANCE_PERFORMANCE 0x80
|
||||
#define AMD_CPPC_EPP_BALANCE_POWERSAVE 0xBF
|
||||
#define AMD_CPPC_EPP_POWERSAVE 0xFF
|
||||
#define AMD_CPPC_EPP_LEGACY_PERFORMANCE 0x00
|
||||
#define AMD_CPPC_EPP_LEGACY_BALANCE_PERFORMANCE 0x80
|
||||
#define AMD_CPPC_EPP_LEGACY_BALANCE_POWERSAVE 0xBF
|
||||
#define AMD_CPPC_EPP_LEGACY_POWERSAVE 0xFF
|
||||
|
||||
static const char * const amd_pstate_mode_string[] = {
|
||||
[AMD_PSTATE_UNDEFINED] = "undefined",
|
||||
|
|
@ -90,53 +89,112 @@ static bool amd_pstate_prefcore = true;
|
|||
static struct quirk_entry *quirks;
|
||||
|
||||
/*
|
||||
* AMD Energy Preference Performance (EPP)
|
||||
* The EPP is used in the CCLK DPM controller to drive
|
||||
* the frequency that a core is going to operate during
|
||||
* short periods of activity. EPP values will be utilized for
|
||||
* different OS profiles (balanced, performance, power savings)
|
||||
* display strings corresponding to EPP index in the
|
||||
* energy_perf_strings[]
|
||||
* index String
|
||||
*-------------------------------------
|
||||
* 0 default
|
||||
* 1 performance
|
||||
* 2 balance_performance
|
||||
* 3 balance_power
|
||||
* 4 power
|
||||
* 5 custom (for raw EPP values)
|
||||
* 6 dynamic (platform profile driven selection)
|
||||
* The numeric EPP value programmed for each named preference. First dimension
|
||||
* is CPU type (TOPO_CPU_TYPE_ANY for non-hybrid, TOPO_CPU_TYPE_PERFORMANCE/
|
||||
* EFFICIENCY/LOW_POWER for hybrid). The initializer holds the legacy values
|
||||
* used as the fallback on any platform not listed in amd_pstate_epp_soc_ids[];
|
||||
* amd_pstate_init_epp_values() overwrites slots at boot when the running SoC
|
||||
* has a per-SoC (and potentially per-CPU-type) override.
|
||||
*/
|
||||
enum energy_perf_value_index {
|
||||
EPP_INDEX_DEFAULT = 0,
|
||||
EPP_INDEX_PERFORMANCE,
|
||||
EPP_INDEX_BALANCE_PERFORMANCE,
|
||||
EPP_INDEX_BALANCE_POWERSAVE,
|
||||
EPP_INDEX_POWERSAVE,
|
||||
EPP_INDEX_CUSTOM,
|
||||
EPP_INDEX_DYNAMIC,
|
||||
EPP_INDEX_MAX,
|
||||
static u8 epp_values[][EPP_INDEX_MAX] = {
|
||||
/*
|
||||
* Initialize all CPU types to legacy defaults.
|
||||
* amd_pstate_init_epp_values() will fix these up
|
||||
* based on the platform during boot.
|
||||
*/
|
||||
[TOPO_CPU_TYPE_ANY ... TOPO_CPU_TYPE_LOW_POWER] = {
|
||||
[EPP_INDEX_DEFAULT] = 0,
|
||||
[EPP_INDEX_PERFORMANCE] = AMD_CPPC_EPP_LEGACY_PERFORMANCE,
|
||||
[EPP_INDEX_BALANCE_PERFORMANCE] = AMD_CPPC_EPP_LEGACY_BALANCE_PERFORMANCE,
|
||||
[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_LEGACY_BALANCE_POWERSAVE,
|
||||
[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_LEGACY_POWERSAVE,
|
||||
},
|
||||
};
|
||||
static_assert(ARRAY_SIZE(epp_values) == TOPO_CPU_TYPE_LOW_POWER + 1,
|
||||
"epp_values must have entries for all CPU types up to TOPO_CPU_TYPE_LOW_POWER");
|
||||
|
||||
/*
|
||||
* Get the EPP value row for a given CPU, accounting for hybrid CPU types.
|
||||
* Non-hybrid systems use TOPO_CPU_TYPE_ANY; hybrid systems use the CPU's
|
||||
* actual type (PERFORMANCE/EFFICIENCY/LOW_POWER).
|
||||
*/
|
||||
u8 *amd_pstate_cpu_epp_values(enum x86_topology_cpu_type cpu_type)
|
||||
{
|
||||
switch (cpu_type) {
|
||||
case TOPO_CPU_TYPE_PERFORMANCE:
|
||||
case TOPO_CPU_TYPE_EFFICIENCY:
|
||||
case TOPO_CPU_TYPE_LOW_POWER:
|
||||
return epp_values[cpu_type];
|
||||
default:
|
||||
return epp_values[TOPO_CPU_TYPE_ANY];
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_cpu_epp_values);
|
||||
|
||||
/**
|
||||
* struct amd_pstate_epp_values - EPP values for the four named preferences
|
||||
* @performance: value for the "performance" preference
|
||||
* @balance_performance: value for the "balance_performance" preference
|
||||
* @balance_power: value for the "balance_power" preference
|
||||
* @power: value for the "power" preference
|
||||
*/
|
||||
struct amd_pstate_epp_values {
|
||||
u8 performance;
|
||||
u8 balance_performance;
|
||||
u8 balance_power;
|
||||
u8 power;
|
||||
};
|
||||
|
||||
static const char * const energy_perf_strings[] = {
|
||||
[EPP_INDEX_DEFAULT] = "default",
|
||||
[EPP_INDEX_PERFORMANCE] = "performance",
|
||||
[EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
|
||||
[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
|
||||
[EPP_INDEX_POWERSAVE] = "power",
|
||||
[EPP_INDEX_CUSTOM] = "custom",
|
||||
[EPP_INDEX_DYNAMIC] = "dynamic",
|
||||
/**
|
||||
* struct amd_pstate_epp_soc - per-CPU-type EPP overrides for hybrid systems
|
||||
* @performance_core: values for TOPO_CPU_TYPE_PERFORMANCE cores
|
||||
* @efficiency_core: values for TOPO_CPU_TYPE_EFFICIENCY cores
|
||||
* @low_power_core: values for TOPO_CPU_TYPE_LOW_POWER cores
|
||||
*
|
||||
* Referenced from amd_pstate_epp_soc_ids[] to give a hybrid platform its own
|
||||
* numeric EPP values for the four named preferences, with distinct values per
|
||||
* CPU type. Non-hybrid systems are not listed in the table and always use the
|
||||
* legacy defaults.
|
||||
*/
|
||||
struct amd_pstate_epp_soc {
|
||||
struct amd_pstate_epp_values performance_core;
|
||||
struct amd_pstate_epp_values efficiency_core;
|
||||
struct amd_pstate_epp_values low_power_core;
|
||||
};
|
||||
static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
|
||||
|
||||
static unsigned int epp_values[] = {
|
||||
[EPP_INDEX_DEFAULT] = 0,
|
||||
[EPP_INDEX_PERFORMANCE] = AMD_CPPC_EPP_PERFORMANCE,
|
||||
[EPP_INDEX_BALANCE_PERFORMANCE] = AMD_CPPC_EPP_BALANCE_PERFORMANCE,
|
||||
[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE,
|
||||
[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE,
|
||||
/*
|
||||
* Per-CPU-type EPP overrides for hybrid systems. Only hybrid SoCs should be
|
||||
* listed here; non-hybrid systems always use the legacy defaults.
|
||||
*/
|
||||
static const struct amd_pstate_epp_soc epp_soc_zen6_client __initconst = {
|
||||
.performance_core = {
|
||||
.performance = 25,
|
||||
.balance_performance = 51,
|
||||
.balance_power = 64,
|
||||
.power = 115,
|
||||
},
|
||||
.efficiency_core = {
|
||||
.performance = 25,
|
||||
.balance_performance = 51,
|
||||
.balance_power = 64,
|
||||
.power = 115,
|
||||
},
|
||||
.low_power_core = {
|
||||
.performance = 25,
|
||||
.balance_performance = 64,
|
||||
.balance_power = 64,
|
||||
.power = 64,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id amd_pstate_epp_soc_ids[] __initconst = {
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x80, &epp_soc_zen6_client),
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x81, &epp_soc_zen6_client),
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x84, &epp_soc_zen6_client),
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x85, &epp_soc_zen6_client),
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0xe0, &epp_soc_zen6_client),
|
||||
{}
|
||||
};
|
||||
static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2);
|
||||
|
||||
typedef int (*cppc_mode_transition_fn)(int);
|
||||
|
||||
|
|
@ -145,19 +203,6 @@ static struct quirk_entry quirk_amd_7k62 = {
|
|||
.lowest_freq = 550,
|
||||
};
|
||||
|
||||
static inline u8 freq_to_perf(union perf_cached perf, u32 nominal_freq, unsigned int freq_val)
|
||||
{
|
||||
u32 perf_val = DIV_ROUND_UP_ULL((u64)freq_val * perf.nominal_perf, nominal_freq);
|
||||
|
||||
return (u8)clamp(perf_val, perf.lowest_perf, perf.highest_perf);
|
||||
}
|
||||
|
||||
static inline u32 perf_to_freq(union perf_cached perf, u32 nominal_freq, u8 perf_val)
|
||||
{
|
||||
return DIV_ROUND_UP_ULL((u64)nominal_freq * perf_val,
|
||||
perf.nominal_perf);
|
||||
}
|
||||
|
||||
static int __init dmi_matched_7k62_bios_bug(const struct dmi_system_id *dmi)
|
||||
{
|
||||
/**
|
||||
|
|
@ -470,11 +515,13 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
|
||||
if (ret)
|
||||
ret = amd_get_effective_highest_perf(cpudata->cpu);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rdmsrq_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &cppc_req);
|
||||
numerator = ret;
|
||||
|
||||
ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &cppc_req);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -505,10 +552,12 @@ static int shmem_init_perf(struct amd_cpudata *cpudata)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
|
||||
if (ret)
|
||||
ret = amd_get_effective_highest_perf(cpudata->cpu);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
numerator = ret;
|
||||
|
||||
perf.highest_perf = numerator;
|
||||
perf.max_limit_perf = numerator;
|
||||
perf.min_limit_perf = cppc_perf.lowest_perf;
|
||||
|
|
@ -524,8 +573,10 @@ static int shmem_init_perf(struct amd_cpudata *cpudata)
|
|||
return 0;
|
||||
}
|
||||
|
||||
ret = cppc_set_auto_sel(cpudata->cpu,
|
||||
(cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
|
||||
if ((cppc_state != AMD_PSTATE_PASSIVE) == auto_sel)
|
||||
return 0;
|
||||
|
||||
ret = cppc_set_auto_sel(cpudata->cpu, !auto_sel);
|
||||
|
||||
if (ret)
|
||||
pr_warn("failed to set auto_sel, ret: %d\n", ret);
|
||||
|
|
@ -544,7 +595,11 @@ static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf,
|
|||
u8 des_perf, u8 max_perf, u8 epp, bool fast_switch)
|
||||
{
|
||||
struct amd_cpudata *cpudata = policy->driver_data;
|
||||
struct cppc_perf_ctrls perf_ctrls;
|
||||
struct cppc_perf_ctrls perf_ctrls = {
|
||||
.max_perf = max_perf,
|
||||
.min_perf = min_perf,
|
||||
.desired_perf = des_perf,
|
||||
};
|
||||
u64 value, prev;
|
||||
int ret;
|
||||
|
||||
|
|
@ -577,10 +632,6 @@ static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf,
|
|||
if (value == prev)
|
||||
return 0;
|
||||
|
||||
perf_ctrls.max_perf = max_perf;
|
||||
perf_ctrls.min_perf = min_perf;
|
||||
perf_ctrls.desired_perf = des_perf;
|
||||
|
||||
ret = cppc_set_perf(cpudata->cpu, &perf_ctrls);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -687,7 +738,10 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
|
|||
struct amd_cpudata *cpudata = policy->driver_data;
|
||||
union perf_cached perf = READ_ONCE(cpudata->perf);
|
||||
|
||||
perf.max_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->max);
|
||||
if (policy->max >= READ_ONCE(cpudata->max_freq))
|
||||
perf.max_limit_perf = perf.highest_perf;
|
||||
else
|
||||
perf.max_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->max);
|
||||
WRITE_ONCE(cpudata->max_limit_freq, policy->max);
|
||||
|
||||
if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
|
||||
|
|
@ -1013,8 +1067,10 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
|
|||
|
||||
WRITE_ONCE(cpudata->nominal_freq, nominal_freq);
|
||||
|
||||
/* max_freq is calculated according to (nominal_freq * highest_perf)/nominal_perf */
|
||||
max_freq = perf_to_freq(perf, nominal_freq, perf.highest_perf);
|
||||
/* try to look up from BIOS/quirk first, fall back to (nominal_freq * highest_perf)/nominal_perf */
|
||||
max_freq = amd_get_max_frequency(cpudata->cpu) * 1000;
|
||||
if (!max_freq)
|
||||
max_freq = perf_to_freq(perf, nominal_freq, perf.highest_perf);
|
||||
WRITE_ONCE(cpudata->max_freq, max_freq);
|
||||
|
||||
lowest_nonlinear_freq = perf_to_freq(perf, nominal_freq, perf.lowest_nonlinear_perf);
|
||||
|
|
@ -1068,6 +1124,7 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
|
|||
return -ENOMEM;
|
||||
|
||||
cpudata->cpu = policy->cpu;
|
||||
cpudata->cpu_type = cpu_data(policy->cpu).topo.cpu_type;
|
||||
|
||||
ret = amd_pstate_init_perf(cpudata);
|
||||
if (ret)
|
||||
|
|
@ -1195,13 +1252,16 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
|
|||
static int amd_pstate_get_epp_from_platform_profile(struct cpufreq_policy *policy,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
struct amd_cpudata *cpudata = policy->driver_data;
|
||||
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
|
||||
|
||||
switch (profile) {
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
return AMD_CPPC_EPP_PERFORMANCE;
|
||||
return values[EPP_INDEX_PERFORMANCE];
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
return amd_pstate_get_balanced_epp(policy);
|
||||
case PLATFORM_PROFILE_LOW_POWER:
|
||||
return AMD_CPPC_EPP_POWERSAVE;
|
||||
return values[EPP_INDEX_POWERSAVE];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1292,8 +1352,10 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
|
|||
return ret;
|
||||
|
||||
cpudata->profile_name = kasprintf(GFP_KERNEL, "amd-pstate-epp-cpu%d", cpudata->cpu);
|
||||
if (!cpudata->profile_name)
|
||||
return -ENOMEM;
|
||||
if (!cpudata->profile_name) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cpudata->ppdev = platform_profile_register(get_cpu_device(policy->cpu),
|
||||
cpudata->profile_name,
|
||||
|
|
@ -1411,6 +1473,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct amd_cpudata *cpudata = policy->driver_data;
|
||||
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
|
||||
ssize_t ret;
|
||||
bool raw_epp = false;
|
||||
u8 epp;
|
||||
|
|
@ -1445,12 +1508,13 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
|
|||
}
|
||||
|
||||
if (ret)
|
||||
epp = epp_values[ret];
|
||||
epp = values[ret];
|
||||
else
|
||||
epp = cpudata->epp_default_dc;
|
||||
}
|
||||
|
||||
if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
|
||||
if (epp > 0 && epp != values[EPP_INDEX_PERFORMANCE] &&
|
||||
cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
|
||||
pr_debug("EPP cannot be set under performance policy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
|
@ -1475,34 +1539,31 @@ EXPORT_SYMBOL_FOR_PSTATE_UT(store_energy_performance_preference);
|
|||
ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
|
||||
{
|
||||
struct amd_cpudata *cpudata = policy->driver_data;
|
||||
u8 preference, epp;
|
||||
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
|
||||
u8 epp;
|
||||
int i;
|
||||
|
||||
epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
|
||||
|
||||
if (!cpudata->dynamic_epp && cpudata->raw_epp)
|
||||
return sysfs_emit(buf, "%u\n", epp);
|
||||
|
||||
switch (epp) {
|
||||
case AMD_CPPC_EPP_PERFORMANCE:
|
||||
preference = EPP_INDEX_PERFORMANCE;
|
||||
break;
|
||||
case AMD_CPPC_EPP_BALANCE_PERFORMANCE:
|
||||
preference = EPP_INDEX_BALANCE_PERFORMANCE;
|
||||
break;
|
||||
case AMD_CPPC_EPP_BALANCE_POWERSAVE:
|
||||
preference = EPP_INDEX_BALANCE_POWERSAVE;
|
||||
break;
|
||||
case AMD_CPPC_EPP_POWERSAVE:
|
||||
preference = EPP_INDEX_POWERSAVE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Map the cached EPP value back to a named preference. Skip the
|
||||
* "default" slot (index 0) so an EPP of 0 reports as "performance".
|
||||
* Stop at POWERSAVE; CUSTOM and DYNAMIC are not initialized in epp_values.
|
||||
*/
|
||||
for (i = EPP_INDEX_PERFORMANCE; i <= EPP_INDEX_POWERSAVE; i++) {
|
||||
const char *name = energy_perf_strings[i];
|
||||
|
||||
if (epp == values[i]) {
|
||||
if (cpudata->dynamic_epp)
|
||||
return sysfs_emit(buf, "dynamic(profile:%s)\n", name);
|
||||
return sysfs_emit(buf, "%s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
if (cpudata->dynamic_epp)
|
||||
return sysfs_emit(buf, "dynamic(profile:%s)\n", energy_perf_strings[preference]);
|
||||
|
||||
return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
|
||||
return sysfs_emit(buf, "%u\n", epp);
|
||||
}
|
||||
EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference);
|
||||
|
||||
|
|
@ -1721,6 +1782,7 @@ static int amd_pstate_unregister_driver(int dummy)
|
|||
static int amd_pstate_change_mode_without_dvr_change(int mode)
|
||||
{
|
||||
int cpu = 0;
|
||||
int ret;
|
||||
|
||||
cppc_state = mode;
|
||||
|
||||
|
|
@ -1728,7 +1790,9 @@ static int amd_pstate_change_mode_without_dvr_change(int mode)
|
|||
return 0;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
cppc_set_auto_sel(cpu, (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
|
||||
ret = cppc_set_auto_sel(cpu, (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1736,6 +1800,7 @@ static int amd_pstate_change_mode_without_dvr_change(int mode)
|
|||
|
||||
static int amd_pstate_change_driver_mode(int mode)
|
||||
{
|
||||
int old_mode = cppc_state;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&amd_pstate_driver_lock);
|
||||
|
|
@ -1745,10 +1810,16 @@ static int amd_pstate_change_driver_mode(int mode)
|
|||
return ret;
|
||||
|
||||
ret = amd_pstate_register_driver(mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret) {
|
||||
pr_err("Failed to register %s mode, restoring %s mode\n",
|
||||
amd_pstate_get_mode_string(mode),
|
||||
amd_pstate_get_mode_string(old_mode));
|
||||
if (amd_pstate_register_driver(old_mode))
|
||||
pr_err("Failed to restore %s mode\n",
|
||||
amd_pstate_get_mode_string(old_mode));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cppc_mode_transition_fn mode_state_machine[AMD_PSTATE_MAX][AMD_PSTATE_MAX] = {
|
||||
|
|
@ -1794,7 +1865,8 @@ EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_status);
|
|||
|
||||
int amd_pstate_update_status(const char *buf, size_t size)
|
||||
{
|
||||
int mode_idx;
|
||||
cppc_mode_transition_fn fn;
|
||||
int cpu, mode_idx;
|
||||
|
||||
if (size > strlen("passive") || size < strlen("active"))
|
||||
return -EINVAL;
|
||||
|
|
@ -1803,12 +1875,25 @@ int amd_pstate_update_status(const char *buf, size_t size)
|
|||
if (mode_idx < 0)
|
||||
return mode_idx;
|
||||
|
||||
if (mode_state_machine[cppc_state][mode_idx]) {
|
||||
guard(mutex)(&amd_pstate_driver_lock);
|
||||
return mode_state_machine[cppc_state][mode_idx](mode_idx);
|
||||
/* Resolve the transition under the lock; cppc_state is only stable while held. */
|
||||
guard(mutex)(&amd_pstate_driver_lock);
|
||||
|
||||
if (!mode_state_machine[cppc_state][mode_idx])
|
||||
return 0;
|
||||
|
||||
if (mode_idx == AMD_PSTATE_PASSIVE &&
|
||||
!cpu_feature_enabled(X86_FEATURE_CPPC)) {
|
||||
guard(cpus_read_lock)();
|
||||
|
||||
/* Check offline CPUs too, before changing or removing the driver. */
|
||||
for_each_present_cpu(cpu) {
|
||||
if (cppc_auto_sel_is_immutable(cpu))
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
fn = mode_state_machine[cppc_state][mode_idx];
|
||||
return fn(mode_idx);
|
||||
}
|
||||
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_update_status);
|
||||
|
||||
|
|
@ -1894,6 +1979,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
|
|||
return -ENOMEM;
|
||||
|
||||
cpudata->cpu = policy->cpu;
|
||||
cpudata->cpu_type = cpu_data(policy->cpu).topo.cpu_type;
|
||||
|
||||
ret = amd_pstate_init_perf(cpudata);
|
||||
if (ret)
|
||||
|
|
@ -1944,9 +2030,11 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
|
|||
cpudata->epp_default_ac = cpudata->epp_default_dc = default_epp;
|
||||
cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE;
|
||||
} else {
|
||||
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
|
||||
|
||||
policy->policy = CPUFREQ_POLICY_POWERSAVE;
|
||||
cpudata->epp_default_ac = AMD_CPPC_EPP_PERFORMANCE;
|
||||
cpudata->epp_default_dc = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
|
||||
cpudata->epp_default_ac = values[EPP_INDEX_PERFORMANCE];
|
||||
cpudata->epp_default_dc = values[EPP_INDEX_BALANCE_PERFORMANCE];
|
||||
cpudata->current_profile = PLATFORM_PROFILE_BALANCED;
|
||||
}
|
||||
|
||||
|
|
@ -2245,6 +2333,38 @@ static bool amd_cppc_supported(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve the numeric EPP values for hybrid systems. Only hybrid SoCs are listed
|
||||
* in amd_pstate_epp_soc_ids[]; non-hybrid systems always use the legacy defaults.
|
||||
*/
|
||||
static inline void __init amd_pstate_set_epp_values(enum x86_topology_cpu_type type,
|
||||
const struct amd_pstate_epp_values *core)
|
||||
{
|
||||
epp_values[type][EPP_INDEX_PERFORMANCE] = core->performance;
|
||||
epp_values[type][EPP_INDEX_BALANCE_PERFORMANCE] = core->balance_performance;
|
||||
epp_values[type][EPP_INDEX_BALANCE_POWERSAVE] = core->balance_power;
|
||||
epp_values[type][EPP_INDEX_POWERSAVE] = core->power;
|
||||
}
|
||||
|
||||
static void __init amd_pstate_init_epp_values(void)
|
||||
{
|
||||
const struct x86_cpu_id *id = x86_match_cpu(amd_pstate_epp_soc_ids);
|
||||
const struct amd_pstate_epp_soc *soc;
|
||||
|
||||
if (!id || !id->driver_data) {
|
||||
if (cpu_feature_enabled(X86_FEATURE_ZEN6) &&
|
||||
cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
|
||||
pr_warn_once("No EPP tunings found for platform\n");
|
||||
return;
|
||||
}
|
||||
|
||||
soc = (const struct amd_pstate_epp_soc *)id->driver_data;
|
||||
|
||||
amd_pstate_set_epp_values(TOPO_CPU_TYPE_PERFORMANCE, &soc->performance_core);
|
||||
amd_pstate_set_epp_values(TOPO_CPU_TYPE_EFFICIENCY, &soc->efficiency_core);
|
||||
amd_pstate_set_epp_values(TOPO_CPU_TYPE_LOW_POWER, &soc->low_power_core);
|
||||
}
|
||||
|
||||
static int __init amd_pstate_init(void)
|
||||
{
|
||||
struct device *dev_root;
|
||||
|
|
@ -2272,6 +2392,9 @@ static int __init amd_pstate_init(void)
|
|||
/* check if this machine need CPPC quirks */
|
||||
dmi_check_system(amd_pstate_quirks_table);
|
||||
|
||||
/* resolve per-SoC EPP values for the named preferences */
|
||||
amd_pstate_init_epp_values();
|
||||
|
||||
/*
|
||||
* determine the driver mode from the command line or kernel config.
|
||||
* If no command line input is provided, cppc_state will be AMD_PSTATE_UNDEFINED.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
#include <linux/pm_qos.h>
|
||||
#include <linux/platform_profile.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
|
||||
#if IS_MODULE(CONFIG_X86_AMD_PSTATE_UT)
|
||||
#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) \
|
||||
EXPORT_SYMBOL_FOR_MODULES(symbol, "amd-pstate-ut")
|
||||
|
|
@ -146,6 +148,8 @@ struct amd_cpudata {
|
|||
enum platform_profile_option current_profile;
|
||||
struct device *ppdev;
|
||||
char *profile_name;
|
||||
|
||||
enum x86_topology_cpu_type cpu_type;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -159,6 +163,62 @@ enum amd_pstate_mode {
|
|||
AMD_PSTATE_GUIDED,
|
||||
AMD_PSTATE_MAX,
|
||||
};
|
||||
|
||||
/*
|
||||
* AMD Energy Preference Performance (EPP)
|
||||
* The EPP is used in the CCLK DPM controller to drive
|
||||
* the frequency that a core is going to operate during
|
||||
* short periods of activity. EPP values will be utilized for
|
||||
* different OS profiles (balanced, performance, power savings)
|
||||
* display strings corresponding to EPP index in the
|
||||
* energy_perf_strings[]
|
||||
* index String
|
||||
*-------------------------------------
|
||||
* 0 default
|
||||
* 1 performance
|
||||
* 2 balance_performance
|
||||
* 3 balance_power
|
||||
* 4 power
|
||||
* 5 custom (for raw EPP values)
|
||||
* 6 dynamic (platform profile driven selection)
|
||||
*/
|
||||
enum energy_perf_value_index {
|
||||
EPP_INDEX_DEFAULT = 0,
|
||||
EPP_INDEX_PERFORMANCE,
|
||||
EPP_INDEX_BALANCE_PERFORMANCE,
|
||||
EPP_INDEX_BALANCE_POWERSAVE,
|
||||
EPP_INDEX_POWERSAVE,
|
||||
EPP_INDEX_CUSTOM,
|
||||
EPP_INDEX_DYNAMIC,
|
||||
EPP_INDEX_MAX,
|
||||
};
|
||||
|
||||
static const char * const energy_perf_strings[] = {
|
||||
[EPP_INDEX_DEFAULT] = "default",
|
||||
[EPP_INDEX_PERFORMANCE] = "performance",
|
||||
[EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
|
||||
[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
|
||||
[EPP_INDEX_POWERSAVE] = "power",
|
||||
[EPP_INDEX_CUSTOM] = "custom",
|
||||
[EPP_INDEX_DYNAMIC] = "dynamic",
|
||||
};
|
||||
static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
|
||||
|
||||
u8 *amd_pstate_cpu_epp_values(enum x86_topology_cpu_type cpu_type);
|
||||
|
||||
static inline u8 freq_to_perf(union perf_cached perf, u32 nominal_freq, unsigned int freq_val)
|
||||
{
|
||||
u32 perf_val = DIV_ROUND_UP_ULL((u64)freq_val * perf.nominal_perf, nominal_freq);
|
||||
|
||||
return (u8)clamp(perf_val, perf.lowest_perf, perf.highest_perf);
|
||||
}
|
||||
|
||||
static inline u32 perf_to_freq(union perf_cached perf, u32 nominal_freq, u8 perf_val)
|
||||
{
|
||||
return DIV_ROUND_UP_ULL((u64)nominal_freq * perf_val,
|
||||
perf.nominal_perf);
|
||||
}
|
||||
|
||||
const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode);
|
||||
int amd_pstate_get_status(void);
|
||||
int amd_pstate_update_status(const char *buf, size_t size);
|
||||
|
|
|
|||
|
|
@ -132,8 +132,8 @@ static int bmips_cpufreq_init(struct cpufreq_policy *policy)
|
|||
|
||||
freq_table = bmips_cpufreq_get_freq_table(policy);
|
||||
if (IS_ERR(freq_table)) {
|
||||
pr_err("%s: couldn't determine frequency table (%ld).\n",
|
||||
BMIPS_CPUFREQ_NAME, PTR_ERR(freq_table));
|
||||
pr_err("%s: couldn't determine frequency table (%pe).\n",
|
||||
BMIPS_CPUFREQ_NAME, freq_table);
|
||||
return PTR_ERR(freq_table);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/cpufreq.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
|
|
@ -41,6 +42,7 @@ MODULE_PARM_DESC(fie_disabled, "Disable Frequency Invariance Engine (FIE)");
|
|||
/* Frequency invariance support */
|
||||
struct cppc_freq_invariance {
|
||||
int cpu;
|
||||
bool pcc_work_initialized;
|
||||
struct irq_work irq_work;
|
||||
struct kthread_work work;
|
||||
struct cppc_perf_fb_ctrs prev_perf_fb_ctrs;
|
||||
|
|
@ -49,10 +51,12 @@ struct cppc_freq_invariance {
|
|||
|
||||
static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv);
|
||||
static struct kthread_worker *kworker_fie;
|
||||
static DEFINE_MUTEX(cppc_fie_lock);
|
||||
|
||||
static int cppc_perf_from_fbctrs(u64 reference_perf,
|
||||
struct cppc_perf_fb_ctrs *fb_ctrs_t0,
|
||||
struct cppc_perf_fb_ctrs *fb_ctrs_t1);
|
||||
static int cppc_fie_kworker_init(void);
|
||||
|
||||
/**
|
||||
* __cppc_scale_freq_tick - CPPC arch_freq_scale updater for frequency invariance
|
||||
|
|
@ -149,7 +153,7 @@ static struct scale_freq_data cppc_sftd_pcc = {
|
|||
|
||||
static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct scale_freq_data *sftd = &cppc_sftd;
|
||||
struct scale_freq_data *sftd;
|
||||
struct cppc_freq_invariance *cppc_fi;
|
||||
int cpu, ret;
|
||||
|
||||
|
|
@ -161,9 +165,12 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
|
|||
cppc_fi->cpu = cpu;
|
||||
cppc_fi->cpu_data = policy->driver_data;
|
||||
if (cppc_perf_ctrs_in_pcc_cpu(cpu)) {
|
||||
if (cppc_fie_kworker_init())
|
||||
return;
|
||||
|
||||
kthread_init_work(&cppc_fi->work, cppc_scale_freq_workfn);
|
||||
init_irq_work(&cppc_fi->irq_work, cppc_irq_work);
|
||||
sftd = &cppc_sftd_pcc;
|
||||
cppc_fi->pcc_work_initialized = true;
|
||||
}
|
||||
|
||||
ret = cppc_get_perf_ctrs(cpu, &cppc_fi->prev_perf_fb_ctrs);
|
||||
|
|
@ -179,17 +186,20 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
|
|||
}
|
||||
}
|
||||
|
||||
/* Register for freq-invariance */
|
||||
topology_set_scale_freq_source(sftd, policy->cpus);
|
||||
/* A shared policy may contain both PCC and non-PCC counters. */
|
||||
for_each_cpu(cpu, policy->cpus) {
|
||||
cppc_fi = &per_cpu(cppc_freq_inv, cpu);
|
||||
if (cppc_fi->pcc_work_initialized)
|
||||
sftd = &cppc_sftd_pcc;
|
||||
else
|
||||
sftd = &cppc_sftd;
|
||||
topology_set_scale_freq_source(sftd, cpumask_of(cpu));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We free all the resources on policy's removal and not on CPU removal as the
|
||||
* irq-work are per-cpu and the hotplug core takes care of flushing the pending
|
||||
* irq-works (hint: smpcfd_dying_cpu()) on CPU hotplug. Even if the kthread-work
|
||||
* fires on another CPU after the concerned CPU is removed, it won't harm.
|
||||
*
|
||||
* We just need to make sure to remove them all on policy->exit().
|
||||
* Drain work initialized by this policy even if processor removal has
|
||||
* already unpublished the CPU's CPC descriptor.
|
||||
*/
|
||||
static void cppc_cpufreq_cpu_fie_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
|
|
@ -203,16 +213,18 @@ static void cppc_cpufreq_cpu_fie_exit(struct cpufreq_policy *policy)
|
|||
topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_CPPC, policy->related_cpus);
|
||||
|
||||
for_each_cpu(cpu, policy->related_cpus) {
|
||||
if (!cppc_perf_ctrs_in_pcc_cpu(cpu))
|
||||
continue;
|
||||
cppc_fi = &per_cpu(cppc_freq_inv, cpu);
|
||||
if (!cppc_fi->pcc_work_initialized)
|
||||
continue;
|
||||
irq_work_sync(&cppc_fi->irq_work);
|
||||
kthread_cancel_work_sync(&cppc_fi->work);
|
||||
cppc_fi->pcc_work_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void cppc_fie_kworker_init(void)
|
||||
static int cppc_fie_kworker_init(void)
|
||||
{
|
||||
struct kthread_worker *worker;
|
||||
struct sched_attr attr = {
|
||||
.size = sizeof(struct sched_attr),
|
||||
.sched_policy = SCHED_DEADLINE,
|
||||
|
|
@ -228,23 +240,27 @@ static void cppc_fie_kworker_init(void)
|
|||
};
|
||||
int ret;
|
||||
|
||||
kworker_fie = kthread_run_worker(0, "cppc_fie");
|
||||
if (IS_ERR(kworker_fie)) {
|
||||
pr_warn("%s: failed to create kworker_fie: %ld\n", __func__,
|
||||
PTR_ERR(kworker_fie));
|
||||
fie_disabled = FIE_DISABLED;
|
||||
kworker_fie = NULL;
|
||||
return;
|
||||
guard(mutex)(&cppc_fie_lock);
|
||||
|
||||
if (kworker_fie)
|
||||
return 0;
|
||||
|
||||
worker = kthread_run_worker(0, "cppc_fie");
|
||||
if (IS_ERR(worker)) {
|
||||
pr_warn("%s: failed to create kworker_fie: %pe\n", __func__, worker);
|
||||
return PTR_ERR(worker);
|
||||
}
|
||||
|
||||
ret = sched_setattr_nocheck(kworker_fie->task, &attr);
|
||||
ret = sched_setattr_nocheck(worker->task, &attr);
|
||||
if (ret) {
|
||||
pr_warn("%s: failed to set SCHED_DEADLINE: %d\n", __func__,
|
||||
ret);
|
||||
kthread_destroy_worker(kworker_fie);
|
||||
fie_disabled = FIE_DISABLED;
|
||||
kworker_fie = NULL;
|
||||
kthread_destroy_worker(worker);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kworker_fie = worker;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init cppc_freq_invariance_init(void)
|
||||
|
|
@ -259,11 +275,6 @@ static void __init cppc_freq_invariance_init(void)
|
|||
fie_disabled = FIE_ENABLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (fie_disabled || !perf_ctrs_in_pcc)
|
||||
return;
|
||||
|
||||
cppc_fie_kworker_init();
|
||||
}
|
||||
|
||||
static void cppc_freq_invariance_exit(void)
|
||||
|
|
@ -883,6 +894,7 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct cppc_cpudata *cpu_data = policy->driver_data;
|
||||
bool old_auto_sel = cpu_data->perf_ctrls.auto_sel;
|
||||
bool val;
|
||||
int ret;
|
||||
|
||||
|
|
@ -911,8 +923,8 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
|
|||
if (ret) {
|
||||
cpu_data->perf_ctrls.min_perf = old_min_perf;
|
||||
cpu_data->perf_ctrls.max_perf = old_max_perf;
|
||||
cppc_set_auto_sel(policy->cpu, false);
|
||||
cpu_data->perf_ctrls.auto_sel = false;
|
||||
cppc_set_auto_sel(policy->cpu, old_auto_sel);
|
||||
cpu_data->perf_ctrls.auto_sel = old_auto_sel;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ static const struct of_device_id blocklist[] __initconst = {
|
|||
{ .compatible = "nvidia,tegra234", },
|
||||
|
||||
{ .compatible = "qcom,apq8096", },
|
||||
{ .compatible = "qcom,milos", },
|
||||
{ .compatible = "qcom,msm8909", },
|
||||
{ .compatible = "qcom,msm8996", },
|
||||
{ .compatible = "qcom,msm8998", },
|
||||
|
|
@ -209,6 +210,7 @@ static const struct of_device_id blocklist[] __initconst = {
|
|||
{ .compatible = "qcom,ipq8064", },
|
||||
{ .compatible = "qcom,ipq8074", },
|
||||
{ .compatible = "qcom,ipq9574", },
|
||||
{ .compatible = "qcom,ipq9650", },
|
||||
{ .compatible = "qcom,apq8064", },
|
||||
{ .compatible = "qcom,msm8974", },
|
||||
{ .compatible = "qcom,msm8960", },
|
||||
|
|
|
|||
|
|
@ -85,10 +85,12 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy)
|
|||
freq_step = get_freq_step(cs_tuners, policy);
|
||||
|
||||
/*
|
||||
* Decrease requested_freq one freq_step for each idle period that
|
||||
* we didn't update the frequency.
|
||||
* Apply deferred down steps only when the policy sample load is
|
||||
* below down_threshold. Otherwise, multiple deferred down steps may
|
||||
* cause a net frequency decrease outside the downscaling region.
|
||||
*/
|
||||
if (policy_dbs->idle_periods < UINT_MAX) {
|
||||
if (policy_dbs->max_sample_load < cs_tuners->down_threshold &&
|
||||
policy_dbs->idle_periods < UINT_MAX) {
|
||||
unsigned int freq_steps = policy_dbs->idle_periods * freq_step;
|
||||
|
||||
if (requested_freq > policy->min + freq_steps)
|
||||
|
|
|
|||
|
|
@ -124,7 +124,8 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
|
|||
struct policy_dbs_info *policy_dbs = policy->governor_data;
|
||||
struct dbs_data *dbs_data = policy_dbs->dbs_data;
|
||||
unsigned int ignore_nice = dbs_data->ignore_nice_load;
|
||||
unsigned int max_load = 0, idle_periods = UINT_MAX;
|
||||
unsigned int max_load = 0, max_sample_load = 0;
|
||||
unsigned int idle_periods = UINT_MAX;
|
||||
unsigned int sampling_rate, io_busy, j;
|
||||
u64 cur_nice;
|
||||
|
||||
|
|
@ -147,7 +148,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
|
|||
struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j);
|
||||
u64 update_time, cur_idle_time;
|
||||
unsigned int idle_time, time_elapsed;
|
||||
unsigned int load;
|
||||
unsigned int load, sample_load;
|
||||
|
||||
cur_idle_time = get_cpu_idle_time(j, &update_time, io_busy);
|
||||
|
||||
|
|
@ -186,6 +187,20 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
|
|||
|
||||
j_cdbs->prev_cpu_nice = cur_nice;
|
||||
|
||||
/*
|
||||
* Compute the sample load separately from the prev_load value
|
||||
* that may be reused after a long idle interval. The conservative
|
||||
* governor uses it to decide whether to apply deferred down steps.
|
||||
* If no time has elapsed, retain the existing behavior and use
|
||||
* prev_load.
|
||||
*/
|
||||
if (unlikely(!time_elapsed))
|
||||
sample_load = j_cdbs->prev_load;
|
||||
else if (time_elapsed > idle_time)
|
||||
sample_load = 100 * (time_elapsed - idle_time) / time_elapsed;
|
||||
else
|
||||
sample_load = 0;
|
||||
|
||||
if (unlikely(!time_elapsed)) {
|
||||
/*
|
||||
* That can only happen when this function is called
|
||||
|
|
@ -220,11 +235,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
|
|||
load = j_cdbs->prev_load;
|
||||
j_cdbs->prev_load = 0;
|
||||
} else {
|
||||
if (time_elapsed > idle_time)
|
||||
load = 100 * (time_elapsed - idle_time) / time_elapsed;
|
||||
else
|
||||
load = 0;
|
||||
|
||||
load = sample_load;
|
||||
j_cdbs->prev_load = load;
|
||||
}
|
||||
|
||||
|
|
@ -237,9 +248,13 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
|
|||
|
||||
if (load > max_load)
|
||||
max_load = load;
|
||||
|
||||
if (sample_load > max_sample_load)
|
||||
max_sample_load = sample_load;
|
||||
}
|
||||
|
||||
policy_dbs->idle_periods = idle_periods;
|
||||
policy_dbs->max_sample_load = max_sample_load;
|
||||
|
||||
return max_load;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ struct policy_dbs_info {
|
|||
/* Multiplier for increasing sample delay temporarily. */
|
||||
unsigned int rate_mult;
|
||||
unsigned int idle_periods; /* For conservative */
|
||||
/* Maximum load from the current policy sample. */
|
||||
unsigned int max_sample_load;
|
||||
/* Status indicators */
|
||||
bool is_shared; /* This object is used by multiple CPUs */
|
||||
bool work_in_progress; /* Work is being queued up or in progress */
|
||||
|
|
|
|||
|
|
@ -16,14 +16,16 @@
|
|||
#include <asm/idle.h>
|
||||
#include <asm/loongarch.h>
|
||||
#include <asm/loongson.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/numa.h>
|
||||
|
||||
/* Message */
|
||||
union smc_message {
|
||||
u32 value;
|
||||
struct {
|
||||
u32 id : 4;
|
||||
u32 id : 8;
|
||||
u32 info : 4;
|
||||
u32 val : 16;
|
||||
u32 val : 12;
|
||||
u32 cmd : 6;
|
||||
u32 extra : 1;
|
||||
u32 complete : 1;
|
||||
|
|
@ -164,27 +166,47 @@ union smc_message {
|
|||
|
||||
#define FREQ_MAX_LEVEL 16
|
||||
|
||||
#define MMIO_SMCMBX(node) \
|
||||
((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_SMCMBX))
|
||||
|
||||
#define MMIO_MISC_FUNC(node) \
|
||||
((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_MISC_FUNC))
|
||||
|
||||
struct loongson3_freq_data {
|
||||
unsigned int def_freq_level;
|
||||
struct cpufreq_frequency_table table[];
|
||||
};
|
||||
|
||||
static struct mutex cpufreq_mutex[MAX_PACKAGES];
|
||||
static struct mutex cpufreq_mutex[MAX_NUMNODES];
|
||||
static struct cpufreq_driver loongson3_cpufreq_driver;
|
||||
static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data);
|
||||
|
||||
static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 extra)
|
||||
{
|
||||
int retries;
|
||||
unsigned int cpu = raw_smp_processor_id();
|
||||
unsigned int package = cpu_data[cpu].package;
|
||||
unsigned int cpu = 0, nid;
|
||||
union smc_message msg, last;
|
||||
|
||||
mutex_lock(&cpufreq_mutex[package]);
|
||||
#ifdef CONFIG_SMP
|
||||
switch (cmd) {
|
||||
case CMD_GET_FREQ_INFO:
|
||||
case CMD_SET_FREQ_INFO:
|
||||
case CMD_GET_FREQ_LEVEL_NUM:
|
||||
case CMD_GET_FREQ_LEVEL_INFO:
|
||||
case CMD_GET_FREQ_BOOST_LEVEL:
|
||||
cpu = cpu_number_map(id);
|
||||
break;
|
||||
default:
|
||||
cpu = raw_smp_processor_id();
|
||||
}
|
||||
#endif
|
||||
nid = cpu_to_node(cpu);
|
||||
|
||||
last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
|
||||
mutex_lock(&cpufreq_mutex[nid]);
|
||||
|
||||
last.value = readl(MMIO_SMCMBX(nid));
|
||||
if (!last.complete) {
|
||||
mutex_unlock(&cpufreq_mutex[package]);
|
||||
mutex_unlock(&cpufreq_mutex[nid]);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
|
|
@ -195,12 +217,11 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
|
|||
msg.extra = extra;
|
||||
msg.complete = 0;
|
||||
|
||||
iocsr_write32(msg.value, LOONGARCH_IOCSR_SMCMBX);
|
||||
iocsr_write32(iocsr_read32(LOONGARCH_IOCSR_MISC_FUNC) | IOCSR_MISC_FUNC_SOFT_INT,
|
||||
LOONGARCH_IOCSR_MISC_FUNC);
|
||||
writel(msg.value, MMIO_SMCMBX(nid));
|
||||
writel(readl(MMIO_MISC_FUNC(nid)) | IOCSR_MISC_FUNC_SOFT_INT, MMIO_MISC_FUNC(nid));
|
||||
|
||||
for (retries = 0; retries < 10000; retries++) {
|
||||
msg.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
|
||||
msg.value = readl(MMIO_SMCMBX(nid));
|
||||
if (msg.complete)
|
||||
break;
|
||||
|
||||
|
|
@ -208,49 +229,51 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
|
|||
}
|
||||
|
||||
if (!msg.complete || msg.cmd != CMD_OK) {
|
||||
mutex_unlock(&cpufreq_mutex[package]);
|
||||
mutex_unlock(&cpufreq_mutex[nid]);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
mutex_unlock(&cpufreq_mutex[package]);
|
||||
mutex_unlock(&cpufreq_mutex[nid]);
|
||||
|
||||
return msg.val;
|
||||
}
|
||||
|
||||
static unsigned int loongson3_cpufreq_get(unsigned int cpu)
|
||||
{
|
||||
int ret;
|
||||
int ret, core = cpu_logical_map(cpu);
|
||||
|
||||
ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);
|
||||
ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);
|
||||
|
||||
return ret * KILO;
|
||||
return (ret < 0) ? 0 : ret * KILO;
|
||||
}
|
||||
|
||||
static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
{
|
||||
int ret;
|
||||
int ret, core = cpu_logical_map(policy->cpu);
|
||||
|
||||
ret = do_service_request(cpu_data[policy->cpu].core,
|
||||
FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0);
|
||||
ret = do_service_request(core, FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO,
|
||||
index, 0);
|
||||
|
||||
return (ret >= 0) ? 0 : ret;
|
||||
}
|
||||
|
||||
static int configure_freq_table(int cpu)
|
||||
{
|
||||
int i, ret, boost_level, max_level, freq_level;
|
||||
int i, ret, core, boost_level, max_level, freq_level;
|
||||
struct platform_device *pdev = cpufreq_get_driver_data();
|
||||
struct loongson3_freq_data *data;
|
||||
|
||||
if (per_cpu(freq_data, cpu))
|
||||
return 0;
|
||||
|
||||
ret = do_service_request(cpu, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
|
||||
core = cpu_logical_map(cpu);
|
||||
|
||||
ret = do_service_request(core, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
max_level = ret;
|
||||
|
||||
ret = do_service_request(cpu, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
|
||||
ret = do_service_request(core, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
boost_level = ret;
|
||||
|
|
@ -263,7 +286,7 @@ static int configure_freq_table(int cpu)
|
|||
data->def_freq_level = boost_level - 1;
|
||||
|
||||
for (i = 0; i < freq_level; i++) {
|
||||
ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
|
||||
ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
|
||||
if (ret < 0) {
|
||||
devm_kfree(&pdev->dev, data);
|
||||
return ret;
|
||||
|
|
@ -337,7 +360,7 @@ static int loongson3_cpufreq_probe(struct platform_device *pdev)
|
|||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < MAX_PACKAGES; i++) {
|
||||
for (i = 0; i < MAX_NUMNODES; i++) {
|
||||
ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -603,6 +603,7 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
|
|||
policy->freq_table = freq_table;
|
||||
policy->driver_data = info;
|
||||
policy->clk = info->cpu_clk;
|
||||
policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(info->cpu_dev) / 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -624,6 +625,7 @@ static struct cpufreq_driver mtk_cpufreq_driver = {
|
|||
.init = mtk_cpufreq_init,
|
||||
.exit = mtk_cpufreq_exit,
|
||||
.register_em = cpufreq_register_em_with_opp,
|
||||
.suspend = cpufreq_generic_suspend,
|
||||
.name = "mtk-cpufreq",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -322,6 +322,8 @@ static void qcom_get_related_cpus(int index, struct cpumask *m)
|
|||
|
||||
if (index == args.args[0])
|
||||
cpumask_set_cpu(cpu, m);
|
||||
|
||||
of_node_put(args.np);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -554,6 +556,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
|||
return ret;
|
||||
|
||||
index = args.args[0];
|
||||
of_node_put(args.np);
|
||||
data = &qcom_cpufreq.data[index];
|
||||
|
||||
/* HW should be in enabled state to proceed */
|
||||
|
|
|
|||
|
|
@ -211,6 +211,14 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
|
|||
case QCOM_ID_IPQ5404:
|
||||
drv->versions = (*speedbin == 0x3b) ? BIT(1) : BIT(0);
|
||||
break;
|
||||
case QCOM_ID_IPQ9610:
|
||||
case QCOM_ID_IPQ9620:
|
||||
case QCOM_ID_IPQ9630:
|
||||
case QCOM_ID_IPQ9640:
|
||||
case QCOM_ID_IPQ9650:
|
||||
case QCOM_ID_IPQ9670:
|
||||
drv->versions = (*speedbin == 0x4b) ? BIT(3) : BIT(2);
|
||||
break;
|
||||
case QCOM_ID_MSM8996SG:
|
||||
case QCOM_ID_APQ8096SG:
|
||||
drv->versions = 1 << ((unsigned int)(*speedbin) + 4);
|
||||
|
|
@ -633,6 +641,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst __maybe_u
|
|||
{ .compatible = "qcom,ipq8074", .data = &match_data_ipq8074 },
|
||||
{ .compatible = "qcom,apq8064", .data = &match_data_krait },
|
||||
{ .compatible = "qcom,ipq9574", .data = &match_data_kryo },
|
||||
{ .compatible = "qcom,ipq9650", .data = &match_data_kryo },
|
||||
{ .compatible = "qcom,msm8974", .data = &match_data_krait },
|
||||
{ .compatible = "qcom,msm8960", .data = &match_data_krait },
|
||||
{},
|
||||
|
|
|
|||
|
|
@ -57,8 +57,7 @@ static u32 get_bus_freq(void)
|
|||
/* get platform freq by its clock name */
|
||||
pltclk = clk_get(NULL, "cg-pll0-div1");
|
||||
if (IS_ERR(pltclk)) {
|
||||
pr_err("%s: can't get bus frequency %ld\n",
|
||||
__func__, PTR_ERR(pltclk));
|
||||
pr_err("%s: can't get bus frequency %pe\n", __func__, pltclk);
|
||||
return PTR_ERR(pltclk);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -225,4 +225,5 @@ fn probe<'bound>(
|
|||
authors: ["Viresh Kumar <viresh.kumar@linaro.org>"],
|
||||
description: "Generic CPUFreq DT driver",
|
||||
license: "GPL v2",
|
||||
alias: ["platform:cpufreq-dt"],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,15 +152,14 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
|
|||
|
||||
policy->clk = clk_get(NULL, "armclk");
|
||||
if (IS_ERR(policy->clk)) {
|
||||
pr_err("Unable to obtain ARMCLK: %ld\n",
|
||||
PTR_ERR(policy->clk));
|
||||
pr_err("Unable to obtain ARMCLK: %pe\n", policy->clk);
|
||||
return PTR_ERR(policy->clk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
vddarm = regulator_get(NULL, "vddarm");
|
||||
if (IS_ERR(vddarm)) {
|
||||
pr_err("Failed to obtain VDDARM: %ld\n", PTR_ERR(vddarm));
|
||||
pr_err("Failed to obtain VDDARM: %pe\n", vddarm);
|
||||
pr_err("Only frequency scaling available\n");
|
||||
vddarm = NULL;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -282,12 +282,11 @@ static int us2e_freq_cpu_init(struct cpufreq_policy *policy)
|
|||
table[1].frequency = clock_tick / 2;
|
||||
table[2].driver_data = 2;
|
||||
table[2].frequency = clock_tick / 4;
|
||||
table[2].driver_data = 3;
|
||||
table[2].frequency = clock_tick / 6;
|
||||
table[2].driver_data = 4;
|
||||
table[2].frequency = clock_tick / 8;
|
||||
table[2].driver_data = 5;
|
||||
table[3].frequency = CPUFREQ_TABLE_END;
|
||||
table[3].driver_data = 3;
|
||||
table[3].frequency = clock_tick / 6;
|
||||
table[4].driver_data = 4;
|
||||
table[4].frequency = clock_tick / 8;
|
||||
table[5].frequency = CPUFREQ_TABLE_END;
|
||||
|
||||
policy->cpuinfo.transition_latency = 0;
|
||||
policy->cur = clock_tick;
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ static int __init sti_cpufreq_init(void)
|
|||
|
||||
ddata.cpu = get_cpu_device(0);
|
||||
if (!ddata.cpu) {
|
||||
dev_err(ddata.cpu, "Failed to get device for CPU0\n");
|
||||
pr_err("Failed to get device for CPU0\n");
|
||||
goto skip_voltage_scaling;
|
||||
}
|
||||
|
||||
|
|
@ -281,7 +281,7 @@ static int __init sti_cpufreq_init(void)
|
|||
goto register_cpufreq_dt;
|
||||
|
||||
skip_voltage_scaling:
|
||||
dev_err(ddata.cpu, "Not doing voltage scaling\n");
|
||||
pr_err("Not doing voltage scaling\n");
|
||||
|
||||
register_cpufreq_dt:
|
||||
platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ static void tegra194_get_cpu_ndiv_sysreg(void *ndiv)
|
|||
|
||||
static int tegra194_get_cpu_ndiv(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv)
|
||||
{
|
||||
return smp_call_function_single(cpu, tegra194_get_cpu_ndiv_sysreg, &ndiv, true);
|
||||
return smp_call_function_single(cpu, tegra194_get_cpu_ndiv_sysreg, ndiv, true);
|
||||
}
|
||||
|
||||
static void tegra194_set_cpu_ndiv_sysreg(void *data)
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ static int tegra114_enter_s2idle(struct cpuidle_device *dev,
|
|||
* LP2 | C7 (CPU core power gating)
|
||||
* LP2 | CC6 (CPU cluster power gating)
|
||||
*
|
||||
* Note that that the older CPUIDLE driver versions didn't explicitly
|
||||
* Note that the older CPUIDLE driver versions didn't explicitly
|
||||
* differentiate the LP2 states because these states either used the same
|
||||
* code path or because CC6 wasn't supported.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -284,10 +284,10 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
|
|||
data->bucket = BUCKETS - 1;
|
||||
}
|
||||
|
||||
if (latency_req == 0 ||
|
||||
((data->next_timer_ns < drv->states[1].target_residency_ns ||
|
||||
latency_req < drv->states[1].exit_latency_ns) &&
|
||||
!dev->states_usage[0].disable)) {
|
||||
if (!dev->states_usage[0].disable &&
|
||||
(latency_req == 0 ||
|
||||
data->next_timer_ns < drv->states[1].target_residency_ns ||
|
||||
latency_req < drv->states[1].exit_latency_ns)) {
|
||||
/*
|
||||
* In this case state[0] will be used no matter what, so return
|
||||
* it right away and keep the tick running if state[0] is a
|
||||
|
|
|
|||
|
|
@ -426,6 +426,17 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the latency constraint does not allow any of the enabled idle
|
||||
* states to be used, the candidate state index will be capped to 0
|
||||
* by the check below, but state 0 may be disabled. To prevent the
|
||||
* selection of a disabled state in that case, ensure that
|
||||
* constraint_idx is at least equal to the index of the first
|
||||
* enabled idle state.
|
||||
*/
|
||||
if (constraint_idx < idx0)
|
||||
constraint_idx = idx0;
|
||||
|
||||
/*
|
||||
* If there is a latency constraint, it may be necessary to select an
|
||||
* idle state shallower than the current candidate one.
|
||||
|
|
|
|||
|
|
@ -1792,9 +1792,7 @@ static bool __init intel_idle_cst_usable(void)
|
|||
{
|
||||
int cstate, limit;
|
||||
|
||||
limit = min_t(int, min_t(int, CPUIDLE_STATE_MAX, max_cstate + 1),
|
||||
acpi_state_table.count);
|
||||
|
||||
limit = min3(CPUIDLE_STATE_MAX, max_cstate + 1, acpi_state_table.count);
|
||||
for (cstate = 1; cstate < limit; cstate++) {
|
||||
struct acpi_processor_cx *cx = &acpi_state_table.states[cstate];
|
||||
|
||||
|
|
@ -1906,7 +1904,7 @@ static void __init intel_idle_init_cstates_acpi_lpi(struct cpuidle_driver *drv)
|
|||
state = &drv->states[drv->state_count++];
|
||||
|
||||
scnprintf(state->name, CPUIDLE_NAME_LEN, "C%d_LPI", index + 1);
|
||||
strscpy(state->desc, lpi_state->desc, CPUIDLE_DESC_LEN);
|
||||
strscpy(state->desc, lpi_state->desc);
|
||||
state->exit_latency = lpi_state->wake_latency;
|
||||
state->target_residency = lpi_state->min_residency;
|
||||
state->flags = MWAIT2flg(lpi_state->address);
|
||||
|
|
@ -1938,7 +1936,7 @@ static void __init intel_idle_init_cstates_acpi_lpi(struct cpuidle_driver *drv)
|
|||
|
||||
static void __init intel_idle_init_cstates_acpi_cst(struct cpuidle_driver *drv)
|
||||
{
|
||||
int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count);
|
||||
int cstate, limit = min(CPUIDLE_STATE_MAX, acpi_state_table.count);
|
||||
|
||||
/*
|
||||
* If limit > 0, intel_idle_cst_usable() has returned 'true', so all of
|
||||
|
|
@ -1956,7 +1954,7 @@ static void __init intel_idle_init_cstates_acpi_cst(struct cpuidle_driver *drv)
|
|||
state = &drv->states[drv->state_count++];
|
||||
|
||||
snprintf(state->name, CPUIDLE_NAME_LEN, "C%d_ACPI", cstate);
|
||||
strscpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
|
||||
strscpy(state->desc, cx->desc);
|
||||
state->exit_latency = cx->latency;
|
||||
/*
|
||||
* For C1-type C-states use the same number for both the exit
|
||||
|
|
@ -2018,7 +2016,7 @@ static bool __init intel_idle_off_by_default_lpi(unsigned int flags, u32 mwait_h
|
|||
|
||||
static bool __init intel_idle_off_by_default_cst(unsigned int flags, u32 mwait_hint)
|
||||
{
|
||||
int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count);
|
||||
int cstate, limit = min(CPUIDLE_STATE_MAX, acpi_state_table.count);
|
||||
|
||||
/*
|
||||
* If limit > 0, intel_idle_cst_usable() has returned 'true', so all of
|
||||
|
|
|
|||
|
|
@ -1409,7 +1409,7 @@ bool dev_pm_opp_of_has_required_opp(struct device *dev)
|
|||
* dev_pm_opp_get_of_node() - Gets the DT node corresponding to an opp
|
||||
* @opp: opp for which DT node has to be returned for
|
||||
*
|
||||
* Return: DT node corresponding to the opp, else 0 on success.
|
||||
* Return: DT node corresponding to the opp, else NULL.
|
||||
*
|
||||
* The caller needs to put the node with of_node_put() after using it.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -456,6 +456,7 @@ static const struct x86_cpu_id rapl_ids[] = {
|
|||
X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, &rapl_defaults_spr_server),
|
||||
X86_MATCH_VFM(INTEL_LUNARLAKE_M, &rapl_defaults_core),
|
||||
X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &rapl_defaults_core_pl4_pmu),
|
||||
X86_MATCH_VFM(INTEL_PANTHERLAKE_R, &rapl_defaults_core_pl4_pmu),
|
||||
X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &rapl_defaults_core_pl4_pmu),
|
||||
X86_MATCH_VFM(INTEL_NOVALAKE, &rapl_defaults_core_pl4),
|
||||
X86_MATCH_VFM(INTEL_NOVALAKE_L, &rapl_defaults_core_pl4),
|
||||
|
|
@ -582,8 +583,8 @@ static int intel_rapl_msr_init(void)
|
|||
rapl_msr_platdev = platform_device_register_data(NULL, "intel_rapl_msr", 0, def,
|
||||
sizeof(*def));
|
||||
if (IS_ERR(rapl_msr_platdev))
|
||||
pr_debug("intel_rapl_msr device register failed, ret:%ld\n",
|
||||
PTR_ERR(rapl_msr_platdev));
|
||||
pr_debug("intel_rapl_msr device register failed, ret:%pe\n",
|
||||
rapl_msr_platdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,11 +72,16 @@ struct cpc_register_resource {
|
|||
struct {
|
||||
struct cpc_reg reg;
|
||||
bool use_rmw_lock;
|
||||
bool read_unsupported;
|
||||
bool write_unsupported;
|
||||
};
|
||||
u64 int_value;
|
||||
} cpc_entry;
|
||||
};
|
||||
|
||||
struct cpc_sysmem_node;
|
||||
struct cpc_non_mmio_node;
|
||||
|
||||
/* Container to hold the CPC details for each CPU */
|
||||
struct cpc_desc {
|
||||
int num_entries;
|
||||
|
|
@ -84,10 +89,12 @@ struct cpc_desc {
|
|||
int cpu_id;
|
||||
int write_cmd_status;
|
||||
int write_cmd_id;
|
||||
/* Lock used for RMW operations in cpc_write() */
|
||||
/* Serialize partial SystemMemory writes within this descriptor. */
|
||||
raw_spinlock_t rmw_lock;
|
||||
struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT];
|
||||
struct acpi_psd_package domain_info;
|
||||
struct cpc_sysmem_node *sysmem_nodes;
|
||||
struct cpc_non_mmio_node *non_mmio_nodes;
|
||||
struct kobject kobj;
|
||||
};
|
||||
|
||||
|
|
@ -141,6 +148,8 @@ struct cppc_perf_ctrls {
|
|||
u32 desired_perf;
|
||||
u32 energy_perf;
|
||||
bool auto_sel;
|
||||
/* Allow an explicit zero minimum; otherwise zero omits the update. */
|
||||
bool min_perf_valid;
|
||||
};
|
||||
|
||||
struct cppc_perf_fb_ctrs {
|
||||
|
|
@ -188,11 +197,14 @@ extern int cppc_set_epp(int cpu, u64 epp_val);
|
|||
extern int cppc_get_auto_act_window(int cpu, u64 *auto_act_window);
|
||||
extern int cppc_set_auto_act_window(int cpu, u64 auto_act_window);
|
||||
extern int cppc_get_auto_sel(int cpu, bool *enable);
|
||||
bool cppc_auto_sel_is_immutable(int cpu);
|
||||
extern int cppc_set_auto_sel(int cpu, bool enable);
|
||||
extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
|
||||
extern int cppc_set_perf_limited(int cpu, u64 bits_to_clear);
|
||||
extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
|
||||
extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
|
||||
extern int amd_get_effective_highest_perf(unsigned int cpu);
|
||||
extern int amd_get_max_frequency(unsigned int cpu);
|
||||
extern int amd_get_boost_ratio(unsigned int cpu, u64 *numerator, u64 *denominator);
|
||||
extern int amd_detect_prefcore(bool *detected);
|
||||
#else /* !CONFIG_ACPI_CPPC_LIB */
|
||||
static inline int cppc_get_desired_perf(int cpunum, u64 *desired_perf)
|
||||
|
|
@ -289,6 +301,12 @@ static inline int cppc_get_auto_sel(int cpu, bool *enable)
|
|||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline bool cppc_auto_sel_is_immutable(int cpu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int cppc_set_auto_sel(int cpu, bool enable)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -305,7 +323,15 @@ static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
|
|||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
|
||||
static inline int amd_get_effective_highest_perf(unsigned int cpu)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int amd_get_max_frequency(unsigned int cpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int amd_get_boost_ratio(unsigned int cpu, u64 *numerator, u64 *denominator)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -663,6 +663,99 @@ struct pm_subsys_data {
|
|||
#define DPM_FLAG_SMART_SUSPEND BIT(2)
|
||||
#define DPM_FLAG_MAY_SKIP_RESUME BIT(3)
|
||||
|
||||
/**
|
||||
* struct dev_pm_info - Device power management information.
|
||||
*
|
||||
* @power_state: Legacy power state (mostly unused in modern kernels).
|
||||
* @can_wakeup: Device is capable of generating wakeup signals.
|
||||
* @async_suspend: Device can be suspended and resumed asynchronously.
|
||||
* @in_dpm_list: Device is on the dpm_list.
|
||||
* @is_prepared: Device's ->prepare() callback has run successfully.
|
||||
* @is_suspended: Device is suspended during a system sleep transition.
|
||||
* @is_noirq_suspended: Device's noirq suspend callback has run successfully.
|
||||
* @is_late_suspended: Device's late suspend callback has run successfully.
|
||||
* @no_pm: Device does not participate in power management transitions.
|
||||
* @early_init: Device was initialized before standard PM initialization.
|
||||
* @direct_complete: Device can skip suspend/resume callbacks and remain
|
||||
* runtime-suspended during system sleep.
|
||||
* @driver_flags: Driver flags (e.g. %DPM_FLAG_SMART_SUSPEND) set at probe time.
|
||||
* @lock: Spinlock used for synchronizing PM state transitions and runtime PM
|
||||
* operations.
|
||||
* @entry: List head for device power management lists.
|
||||
* @completion: Completion for synchronization during asynchronous system
|
||||
* suspend/resume.
|
||||
* @wakeup: Wakeup source object associated with the device.
|
||||
* @work_in_progress: Asynchronous PM operation in progress.
|
||||
* @wakeup_path: Device is in the wakeup path or can wake the system up.
|
||||
* @syscore: Device participates in syscore power management operations.
|
||||
* @no_pm_callbacks: Device has no PM callbacks; handled by parent or subsystem.
|
||||
* @smart_suspend: Driver requested smart-suspend behavior.
|
||||
* @must_resume: Device must be resumed during system resume.
|
||||
* @may_skip_resume: Set by subsystems to indicate driver resume callbacks may
|
||||
* be skipped.
|
||||
* @out_band_wakeup: Out-of-band wakeup is supported.
|
||||
* @strict_midlayer: Middle layer code does not want callbacks invoked via
|
||||
* pm_runtime_force_suspend() / pm_runtime_force_resume().
|
||||
* @should_wakeup: Wakeup flag when system sleep is not enabled.
|
||||
* @suspend_timer: High-resolution timer used for scheduling delayed runtime
|
||||
* suspend and autosuspend requests.
|
||||
* @timer_expires: Timer expiration time in nanoseconds monotonic time
|
||||
* (runtime PM).
|
||||
* @work: Work structure used for queuing up requests into pm_wq (runtime PM).
|
||||
* @wait_queue: Wait queue used if any helper functions need to wait for another
|
||||
* state change to complete (runtime PM).
|
||||
* @wakeirq: Dedicated wakeup interrupt for the device.
|
||||
* @usage_count: Device runtime PM usage counter.
|
||||
* @child_count: Count of active children of the device (runtime PM).
|
||||
* @disable_depth: Disable counter for runtime PM (runtime PM is enabled when
|
||||
* this is 0; initial value is 1).
|
||||
* @idle_notification: Set if ->runtime_idle() is being executed.
|
||||
* @request_pending: Set if a work item is queued into pm_wq (runtime PM).
|
||||
* @deferred_resume: Set if ->runtime_resume() should run as soon as
|
||||
* ->runtime_suspend() completes.
|
||||
* @needs_force_resume: Indicates the device was forced into suspend by
|
||||
* pm_runtime_force_suspend() and must be resumed by
|
||||
* pm_runtime_force_resume().
|
||||
* @runtime_auto: User space has allowed the driver to power manage the device
|
||||
* at runtime via sysfs control attribute; also can be set by
|
||||
* pm_runtime_allow() or pm_runtime_forbid().
|
||||
* @ignore_children: If set, the value of child_count is ignored for runtime
|
||||
* suspend and idle decisions.
|
||||
* @no_callbacks: Indicates the device does not use runtime PM callbacks.
|
||||
* @irq_safe: Indicates runtime PM callbacks will be invoked with the spinlock
|
||||
* held and interrupts disabled.
|
||||
* @use_autosuspend: Indicates the device driver supports delayed runtime
|
||||
* autosuspend.
|
||||
* @timer_autosuspends: Indicates the runtime PM core should attempt an
|
||||
* autosuspend rather than a normal suspend when the timer expires.
|
||||
* @memalloc_noio: Indicates memory allocation during runtime PM transitions
|
||||
* must avoid I/O (GFP_NOIO).
|
||||
* @links_count: Number of device links that require runtime PM coordination.
|
||||
* @request: Type of pending runtime PM request (valid if request_pending is
|
||||
* set).
|
||||
* @runtime_status: Runtime PM status of the device.
|
||||
* @last_status: Last status captured before disabling runtime PM, or
|
||||
* %RPM_BLOCKED / %RPM_INVALID.
|
||||
* @runtime_error: Fatal error code returned by a failing callback, blocking
|
||||
* helpers until cleared.
|
||||
* @autosuspend_delay: Delay time in milliseconds to be used for runtime
|
||||
* autosuspend.
|
||||
* @last_busy: Timestamp in nanoseconds when pm_runtime_mark_last_busy() was
|
||||
* last called. Used in calculating inactivity periods for autosuspend.
|
||||
* @active_time: Accumulated time in nanoseconds spent in %RPM_ACTIVE state.
|
||||
* @suspended_time: Accumulated time in nanoseconds spent in %RPM_SUSPENDED
|
||||
* state.
|
||||
* @accounting_timestamp: Timestamp in nanoseconds of the last runtime PM state
|
||||
* accounting update.
|
||||
* @subsys_data: Subsystem-specific power management data.
|
||||
* @set_latency_tolerance: Callback for setting latency tolerance.
|
||||
* @qos: Per-device PM Quality of Service (QoS) constraints.
|
||||
* @detach_power_off: Indicates device should be detached from PM domain on
|
||||
* power off.
|
||||
*
|
||||
* Device power management information stored in the "power" member of struct
|
||||
* device.
|
||||
*/
|
||||
struct dev_pm_info {
|
||||
pm_message_t power_state;
|
||||
bool can_wakeup:1;
|
||||
|
|
|
|||
|
|
@ -137,13 +137,14 @@ static inline void pm_runtime_put_noidle(struct device *dev)
|
|||
* pm_runtime_suspended - Check whether or not a device is runtime-suspended.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Return %true if runtime PM is enabled for @dev and its runtime PM status is
|
||||
* %RPM_SUSPENDED, or %false otherwise.
|
||||
*
|
||||
* Note that the return value of this function can only be trusted if it is
|
||||
* called under the runtime PM lock of @dev or under conditions in which
|
||||
* runtime PM cannot be either disabled or enabled for @dev and its runtime PM
|
||||
* status cannot change.
|
||||
*
|
||||
* Return:
|
||||
* * %true: @dev has runtime PM enabled and its status is %RPM_SUSPENDED.
|
||||
* * %false: Otherwise.
|
||||
*/
|
||||
static inline bool pm_runtime_suspended(struct device *dev)
|
||||
{
|
||||
|
|
@ -155,13 +156,14 @@ static inline bool pm_runtime_suspended(struct device *dev)
|
|||
* pm_runtime_active - Check whether or not a device is runtime-active.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Return %true if runtime PM is disabled for @dev or its runtime PM status is
|
||||
* %RPM_ACTIVE, or %false otherwise.
|
||||
*
|
||||
* Note that the return value of this function can only be trusted if it is
|
||||
* called under the runtime PM lock of @dev or under conditions in which
|
||||
* runtime PM cannot be either disabled or enabled for @dev and its runtime PM
|
||||
* status cannot change.
|
||||
*
|
||||
* Return:
|
||||
* * %true: Runtime PM is disabled for @dev or its status is %RPM_ACTIVE.
|
||||
* * %false: Otherwise.
|
||||
*/
|
||||
static inline bool pm_runtime_active(struct device *dev)
|
||||
{
|
||||
|
|
@ -173,12 +175,13 @@ static inline bool pm_runtime_active(struct device *dev)
|
|||
* pm_runtime_status_suspended - Check if runtime PM status is "suspended".
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Return %true if the runtime PM status of @dev is %RPM_SUSPENDED, or %false
|
||||
* otherwise, regardless of whether or not runtime PM has been enabled for @dev.
|
||||
*
|
||||
* Note that the return value of this function can only be trusted if it is
|
||||
* called under the runtime PM lock of @dev or under conditions in which the
|
||||
* runtime PM status of @dev cannot change.
|
||||
*
|
||||
* Return:
|
||||
* * %true: Runtime PM status of @dev is %RPM_SUSPENDED.
|
||||
* * %false: Otherwise.
|
||||
*/
|
||||
static inline bool pm_runtime_status_suspended(struct device *dev)
|
||||
{
|
||||
|
|
@ -189,11 +192,13 @@ static inline bool pm_runtime_status_suspended(struct device *dev)
|
|||
* pm_runtime_enabled - Check if runtime PM is enabled.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Return %true if runtime PM is enabled for @dev or %false otherwise.
|
||||
*
|
||||
* Note that the return value of this function can only be trusted if it is
|
||||
* called under the runtime PM lock of @dev or under conditions in which
|
||||
* runtime PM cannot be either disabled or enabled for @dev.
|
||||
*
|
||||
* Return:
|
||||
* * %true: Runtime PM is enabled for @dev.
|
||||
* * %false: Otherwise.
|
||||
*/
|
||||
static inline bool pm_runtime_enabled(struct device *dev)
|
||||
{
|
||||
|
|
@ -205,6 +210,10 @@ static inline bool pm_runtime_enabled(struct device *dev)
|
|||
* @dev: Target device.
|
||||
*
|
||||
* Do not call this function outside system suspend/resume code paths.
|
||||
*
|
||||
* Return:
|
||||
* * %true: Runtime PM enabling is blocked for @dev.
|
||||
* * %false: Otherwise.
|
||||
*/
|
||||
static inline bool pm_runtime_blocked(struct device *dev)
|
||||
{
|
||||
|
|
@ -215,8 +224,9 @@ static inline bool pm_runtime_blocked(struct device *dev)
|
|||
* pm_runtime_has_no_callbacks - Check if runtime PM callbacks may be present.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Return %true if @dev is a special device without runtime PM callbacks or
|
||||
* %false otherwise.
|
||||
* Return:
|
||||
* * %true: @dev is marked as having no runtime PM callbacks.
|
||||
* * %false: Otherwise.
|
||||
*/
|
||||
static inline bool pm_runtime_has_no_callbacks(struct device *dev)
|
||||
{
|
||||
|
|
@ -239,9 +249,11 @@ static inline void pm_runtime_mark_last_busy(struct device *dev)
|
|||
* pm_runtime_is_irq_safe - Check if runtime PM can work in interrupt context.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Return %true if @dev has been marked as an "IRQ-safe" device (with respect
|
||||
* to runtime PM), in which case its runtime PM callabcks can be expected to
|
||||
* work correctly when invoked from interrupt handlers.
|
||||
* Return:
|
||||
* * %true: @dev has been marked as an "IRQ-safe" device, in which case its
|
||||
* runtime PM callbacks can be expected to work correctly from interrupt
|
||||
* handlers.
|
||||
* * %false: Otherwise.
|
||||
*/
|
||||
static inline bool pm_runtime_is_irq_safe(struct device *dev)
|
||||
{
|
||||
|
|
@ -340,25 +352,25 @@ static inline int pm_runtime_force_resume(struct device *dev) { return -ENXIO; }
|
|||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
/**
|
||||
* pm_runtime_idle - Conditionally set up autosuspend of a device or suspend it.
|
||||
* pm_runtime_idle - Conditionally initiate autosuspend of a device or suspend it.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Invoke the "idle check" callback of @dev and, depending on its return value,
|
||||
* set up autosuspend of @dev or suspend it (depending on whether or not
|
||||
* initiate autosuspend of @dev or suspend it (depending on whether or not
|
||||
* autosuspend has been enabled for it).
|
||||
*
|
||||
* Return:
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter non-zero, Runtime PM status change
|
||||
* ongoing or device not in %RPM_ACTIVE state.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -EINPROGRESS: Suspend already in progress.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* Other values and conditions for the above values are possible as returned by
|
||||
* Runtime PM idle and suspend callbacks.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter non-zero, Runtime PM status change
|
||||
* ongoing or device not in %RPM_ACTIVE state.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-EINPROGRESS: Suspend already in progress.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
* * Other values and conditions for the above values are possible as returned
|
||||
* by Runtime PM idle and suspend callbacks.
|
||||
*/
|
||||
static inline int pm_runtime_idle(struct device *dev)
|
||||
{
|
||||
|
|
@ -370,17 +382,17 @@ static inline int pm_runtime_idle(struct device *dev)
|
|||
* @dev: Target device.
|
||||
*
|
||||
* Return:
|
||||
* * 1: Success; device was already suspended.
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter non-zero or Runtime PM status change
|
||||
* ongoing.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* Other values and conditions for the above values are possible as returned by
|
||||
* Runtime PM suspend callbacks.
|
||||
* * %1: Success; device was already suspended.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter non-zero or Runtime PM status change
|
||||
* ongoing.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
* * Other values and conditions for the above values are possible as returned
|
||||
* by Runtime PM suspend callbacks.
|
||||
*/
|
||||
static inline int pm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
|
|
@ -388,26 +400,26 @@ static inline int pm_runtime_suspend(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_runtime_autosuspend - Update the last access time and set up autosuspend
|
||||
* pm_runtime_autosuspend - Update the last access time and initiate autosuspend
|
||||
* of a device.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* First update the last access time, then set up autosuspend of @dev or suspend
|
||||
* it (depending on whether or not autosuspend is enabled for it) without
|
||||
* engaging its "idle check" callback.
|
||||
* First update the last access time, then initiate autosuspend of @dev or
|
||||
* suspend it (depending on whether or not autosuspend is enabled for it)
|
||||
* without engaging its "idle check" callback.
|
||||
*
|
||||
* Return:
|
||||
* * 1: Success; device was already suspended.
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter non-zero or Runtime PM status change
|
||||
* ongoing.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* Other values and conditions for the above values are possible as returned by
|
||||
* Runtime PM suspend callbacks.
|
||||
* * %1: Success; device was already suspended.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter non-zero or Runtime PM status change
|
||||
* ongoing.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
* * Other values and conditions for the above values are possible as returned
|
||||
* by Runtime PM suspend callbacks.
|
||||
*/
|
||||
static inline int pm_runtime_autosuspend(struct device *dev)
|
||||
{
|
||||
|
|
@ -418,6 +430,11 @@ static inline int pm_runtime_autosuspend(struct device *dev)
|
|||
/**
|
||||
* pm_runtime_resume - Resume a device synchronously.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Return:
|
||||
* * %1: Success; @dev is already %RPM_ACTIVE.
|
||||
* * %0: Success.
|
||||
* * Error code on failure.
|
||||
*/
|
||||
static inline int pm_runtime_resume(struct device *dev)
|
||||
{
|
||||
|
|
@ -425,22 +442,22 @@ static inline int pm_runtime_resume(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_request_idle - Queue up "idle check" execution for a device.
|
||||
* pm_request_idle - Request an asynchronous idle check for a device.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Queue up a work item to run an equivalent of pm_runtime_idle() for @dev
|
||||
* asynchronously.
|
||||
* Asynchronously request the PM core to evaluate whether @dev can be idled
|
||||
* or suspended, invoking its ->runtime_idle() callback if provided.
|
||||
*
|
||||
* Return:
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter non-zero, Runtime PM status change
|
||||
* ongoing or device not in %RPM_ACTIVE state.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -EINPROGRESS: Suspend already in progress.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter non-zero, Runtime PM status change
|
||||
* ongoing or device not in %RPM_ACTIVE state.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-EINPROGRESS: Suspend already in progress.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
*/
|
||||
static inline int pm_request_idle(struct device *dev)
|
||||
{
|
||||
|
|
@ -448,8 +465,16 @@ static inline int pm_request_idle(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_request_resume - Queue up runtime-resume of a device.
|
||||
* pm_request_resume - Request an asynchronous runtime resume for a device.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Asynchronously request the PM core to resume @dev to %RPM_ACTIVE state
|
||||
* without modifying its usage counter.
|
||||
*
|
||||
* Return:
|
||||
* * %1: Success; @dev is already %RPM_ACTIVE.
|
||||
* * %0: Success.
|
||||
* * Error code on failure.
|
||||
*/
|
||||
static inline int pm_request_resume(struct device *dev)
|
||||
{
|
||||
|
|
@ -457,24 +482,23 @@ static inline int pm_request_resume(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_request_autosuspend - Update the last access time and queue up autosuspend
|
||||
* of a device.
|
||||
* pm_request_autosuspend - Update access time and request delayed suspension.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Update the last access time of a device and queue up a work item to run an
|
||||
* equivalent pm_runtime_autosuspend() for @dev asynchronously.
|
||||
* Update the last access time of @dev and asynchronously request the PM core
|
||||
* to suspend it after the autosuspend delay has elapsed.
|
||||
*
|
||||
* Return:
|
||||
* * 1: Success; device was already suspended.
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter non-zero or Runtime PM status change
|
||||
* ongoing.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -EINPROGRESS: Suspend already in progress.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* * %1: Success; device was already suspended.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter non-zero or Runtime PM status change
|
||||
* ongoing.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-EINPROGRESS: Suspend already in progress.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
*/
|
||||
static inline int pm_request_autosuspend(struct device *dev)
|
||||
{
|
||||
|
|
@ -483,11 +507,16 @@ static inline int pm_request_autosuspend(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_runtime_get - Bump up usage counter and queue up resume of a device.
|
||||
* pm_runtime_get - Increment usage counter and request asynchronous resume.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Bump up the runtime PM usage counter of @dev and queue up a work item to
|
||||
* carry out runtime-resume of it.
|
||||
* Increment the runtime PM usage counter of @dev and, if the device is
|
||||
* currently suspended, asynchronously request the PM core to resume it.
|
||||
*
|
||||
* Return:
|
||||
* * %1: Success; @dev is already %RPM_ACTIVE.
|
||||
* * %0: Success; runtime-resume was queued.
|
||||
* * Error code on failure.
|
||||
*/
|
||||
static inline int pm_runtime_get(struct device *dev)
|
||||
{
|
||||
|
|
@ -501,12 +530,15 @@ static inline int pm_runtime_get(struct device *dev)
|
|||
* Bump up the runtime PM usage counter of @dev and carry out runtime-resume of
|
||||
* it synchronously.
|
||||
*
|
||||
* The possible return values of this function are the same as for
|
||||
* pm_runtime_resume() and the runtime PM usage counter of @dev remains
|
||||
* incremented in all cases, even if it returns an error code.
|
||||
* Consider using pm_runtime_resume_and_get() instead of it, especially
|
||||
* if its return value is checked by the caller, as this is likely to result
|
||||
* in cleaner code.
|
||||
* Note that the runtime PM usage counter of @dev remains incremented in all
|
||||
* cases, even if it returns an error code. Consider using
|
||||
* pm_runtime_resume_and_get() instead, especially if the return value is
|
||||
* checked by the caller, as this is likely to result in cleaner code.
|
||||
*
|
||||
* Return:
|
||||
* * %1: Success; @dev is already %RPM_ACTIVE.
|
||||
* * %0: Success.
|
||||
* * Error code on failure.
|
||||
*/
|
||||
static inline int pm_runtime_get_sync(struct device *dev)
|
||||
{
|
||||
|
|
@ -531,8 +563,11 @@ static inline int pm_runtime_get_active(struct device *dev, int rpmflags)
|
|||
* @dev: Target device.
|
||||
*
|
||||
* Resume @dev synchronously and if that is successful, increment its runtime
|
||||
* PM usage counter. Return 0 if the runtime PM usage counter of @dev has been
|
||||
* incremented or a negative error code otherwise.
|
||||
* PM usage counter.
|
||||
*
|
||||
* Return:
|
||||
* * %0: Success; @dev is active and its usage counter has been incremented.
|
||||
* * Negative error code on failure; usage counter is unchanged.
|
||||
*/
|
||||
static inline int pm_runtime_resume_and_get(struct device *dev)
|
||||
{
|
||||
|
|
@ -540,11 +575,12 @@ static inline int pm_runtime_resume_and_get(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_runtime_put - Drop device usage counter and queue up "idle check" if 0.
|
||||
* pm_runtime_put - Drop device usage counter and request asynchronous idle check.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Decrement the runtime PM usage counter of @dev and if it turns out to be
|
||||
* equal to 0, queue up a work item for @dev like in pm_request_idle().
|
||||
* Decrement the runtime PM usage counter of @dev. If the counter reaches zero
|
||||
* and the device has no active child dependencies, asynchronously request the
|
||||
* PM core to idle or suspend the device.
|
||||
*/
|
||||
static inline void pm_runtime_put(struct device *dev)
|
||||
{
|
||||
|
|
@ -559,16 +595,16 @@ static inline void pm_runtime_put(struct device *dev)
|
|||
* equal to 0, queue up a work item for @dev like in pm_request_autosuspend().
|
||||
*
|
||||
* Return:
|
||||
* * 1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -EINPROGRESS: Suspend already in progress.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* * %1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-EINPROGRESS: Suspend already in progress.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
*/
|
||||
static inline int __pm_runtime_put_autosuspend(struct device *dev)
|
||||
{
|
||||
|
|
@ -576,25 +612,25 @@ static inline int __pm_runtime_put_autosuspend(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_runtime_put_autosuspend - Update the last access time of a device, drop
|
||||
* its usage counter and queue autosuspend if the usage counter becomes 0.
|
||||
* pm_runtime_put_autosuspend - Update the last access time, drop usage counter
|
||||
* and request autosuspend.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Update the last access time of @dev, decrement runtime PM usage counter of
|
||||
* @dev and if it turns out to be equal to 0, queue up a work item for @dev like
|
||||
* in pm_request_autosuspend().
|
||||
* Update the last access time of @dev and decrement its runtime PM usage
|
||||
* counter. If the counter drops to zero, asynchronously request the PM core to
|
||||
* suspend the device once its autosuspend delay has elapsed.
|
||||
*
|
||||
* Return:
|
||||
* * 1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -EINPROGRESS: Suspend already in progress.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* * %1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-EINPROGRESS: Suspend already in progress.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
*/
|
||||
static inline int pm_runtime_put_autosuspend(struct device *dev)
|
||||
{
|
||||
|
|
@ -653,26 +689,28 @@ DEFINE_GUARD_COND(pm_runtime_active_auto, _try_enabled,
|
|||
* pm_runtime_put_sync - Drop device usage counter and run "idle check" if 0.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Decrement the runtime PM usage counter of @dev and if it turns out to be
|
||||
* equal to 0, invoke the "idle check" callback of @dev and, depending on its
|
||||
* return value, set up autosuspend of @dev or suspend it (depending on whether
|
||||
* or not autosuspend has been enabled for it).
|
||||
* Decrement the runtime PM usage counter of @dev. If the counter drops to zero,
|
||||
* synchronously evaluate and trigger idle/suspend handling.
|
||||
*
|
||||
* Note that this does not update the last access time, but it does respect
|
||||
* existing autosuspend timers. If @dev uses autosuspend, consider using
|
||||
* pm_runtime_put_sync_autosuspend() or pm_runtime_put_sync_suspend() instead.
|
||||
*
|
||||
* The runtime PM usage counter of @dev remains decremented in all cases, even
|
||||
* if it returns an error code.
|
||||
*
|
||||
* Return:
|
||||
* * 1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* Other values and conditions for the above values are possible as returned by
|
||||
* Runtime PM suspend callbacks.
|
||||
* * %1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
* * Other values and conditions for the above values are possible as returned
|
||||
* by Runtime PM suspend callbacks.
|
||||
*/
|
||||
static inline int pm_runtime_put_sync(struct device *dev)
|
||||
{
|
||||
|
|
@ -683,24 +721,28 @@ static inline int pm_runtime_put_sync(struct device *dev)
|
|||
* pm_runtime_put_sync_suspend - Drop device usage counter and suspend if 0.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Decrement the runtime PM usage counter of @dev and if it turns out to be
|
||||
* equal to 0, carry out runtime-suspend of @dev synchronously.
|
||||
* Decrement the runtime PM usage counter of @dev. If the counter drops to zero,
|
||||
* suspend the device synchronously.
|
||||
*
|
||||
* This API differs from pm_runtime_put_sync() and
|
||||
* pm_runtime_put_sync_autosuspend() in that it ignores any outstanding
|
||||
* autosuspend delays.
|
||||
*
|
||||
* The runtime PM usage counter of @dev remains decremented in all cases, even
|
||||
* if it returns an error code.
|
||||
*
|
||||
* Return:
|
||||
* * 1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* Other values and conditions for the above values are possible as returned by
|
||||
* Runtime PM suspend callbacks.
|
||||
* * %1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
* * Other values and conditions for the above values are possible as returned
|
||||
* by Runtime PM suspend callbacks.
|
||||
*/
|
||||
static inline int pm_runtime_put_sync_suspend(struct device *dev)
|
||||
{
|
||||
|
|
@ -712,27 +754,28 @@ static inline int pm_runtime_put_sync_suspend(struct device *dev)
|
|||
* drop device usage counter and autosuspend if 0.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Update the last access time of @dev, decrement the runtime PM usage counter
|
||||
* of @dev and if it turns out to be equal to 0, set up autosuspend of @dev or
|
||||
* suspend it synchronously (depending on whether or not autosuspend has been
|
||||
* enabled for it).
|
||||
* Update the last access time of @dev and decrement its runtime PM usage
|
||||
* counter. If the counter drops to zero, synchronously suspend the device (or
|
||||
* schedule autosuspend if the delay has not elapsed).
|
||||
*
|
||||
* Prefer this API over pm_runtime_put_sync() for devices that use autosuspend.
|
||||
*
|
||||
* The runtime PM usage counter of @dev remains decremented in all cases, even
|
||||
* if it returns an error code.
|
||||
*
|
||||
* Return:
|
||||
* * 1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * 0: Success.
|
||||
* * -EINVAL: Runtime PM error.
|
||||
* * -EACCES: Runtime PM disabled.
|
||||
* * -EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * -EBUSY: Runtime PM child_count non-zero.
|
||||
* * -EPERM: Device PM QoS resume latency 0.
|
||||
* * -EINPROGRESS: Suspend already in progress.
|
||||
* * -ENOSYS: CONFIG_PM not enabled.
|
||||
* Other values and conditions for the above values are possible as returned by
|
||||
* Runtime PM suspend callbacks.
|
||||
* * %1: Success. Usage counter dropped to zero, but device was already suspended.
|
||||
* * %0: Success.
|
||||
* * %-EINVAL: Runtime PM error.
|
||||
* * %-EACCES: Runtime PM disabled.
|
||||
* * %-EAGAIN: Runtime PM usage counter became non-zero or Runtime PM status
|
||||
* change ongoing.
|
||||
* * %-EBUSY: Runtime PM child_count non-zero.
|
||||
* * %-EPERM: Device PM QoS resume latency 0.
|
||||
* * %-EINPROGRESS: Suspend already in progress.
|
||||
* * %-ENOSYS: %CONFIG_PM not enabled.
|
||||
* * Other values and conditions for the above values are possible as returned
|
||||
* by Runtime PM suspend callbacks.
|
||||
*/
|
||||
static inline int pm_runtime_put_sync_autosuspend(struct device *dev)
|
||||
{
|
||||
|
|
@ -741,13 +784,22 @@ static inline int pm_runtime_put_sync_autosuspend(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_runtime_set_active - Set runtime PM status to "active".
|
||||
* pm_runtime_set_active - Set runtime PM status to "active" and clear errors.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Set the runtime PM status of @dev to %RPM_ACTIVE and ensure that dependencies
|
||||
* of it will be taken into account.
|
||||
* Set the runtime PM status of @dev to %RPM_ACTIVE and ensure that its
|
||||
* dependencies will be taken into account. Also clear the device's error
|
||||
* status (@dev->power.runtime_error).
|
||||
*
|
||||
* It is not valid to call this function for devices with runtime PM enabled.
|
||||
* It is only valid to call this function if runtime PM is disabled or if
|
||||
* @dev->power.runtime_error is set.
|
||||
*
|
||||
* This will fail if suppliers cannot be resumed, or if the parent is not in
|
||||
* the correct state.
|
||||
*
|
||||
* Return:
|
||||
* * %0: Success.
|
||||
* * Error code on failure.
|
||||
*/
|
||||
static inline int pm_runtime_set_active(struct device *dev)
|
||||
{
|
||||
|
|
@ -755,13 +807,19 @@ static inline int pm_runtime_set_active(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_runtime_set_suspended - Set runtime PM status to "suspended".
|
||||
* pm_runtime_set_suspended - Set runtime PM status to "suspended" and clear errors.
|
||||
* @dev: Target device.
|
||||
*
|
||||
* Set the runtime PM status of @dev to %RPM_SUSPENDED and ensure that
|
||||
* dependencies of it will be taken into account.
|
||||
* Set the runtime PM status of @dev to %RPM_SUSPENDED and ensure that its
|
||||
* dependencies will be taken into account. Also clear the device's error
|
||||
* status (@dev->power.runtime_error).
|
||||
*
|
||||
* It is not valid to call this function for devices with runtime PM enabled.
|
||||
* It is only valid to call this function if runtime PM is disabled or if
|
||||
* @dev->power.runtime_error is set.
|
||||
*
|
||||
* Return:
|
||||
* * %0: Success.
|
||||
* * Error code on failure.
|
||||
*/
|
||||
static inline int pm_runtime_set_suspended(struct device *dev)
|
||||
{
|
||||
|
|
@ -777,9 +835,8 @@ static inline int pm_runtime_set_suspended(struct device *dev)
|
|||
*
|
||||
* If the counter is zero when this function runs and there is a pending runtime
|
||||
* resume request for @dev, it will be resumed. If the counter is still zero at
|
||||
* that point, all of the pending runtime PM requests for @dev will be canceled
|
||||
* and all runtime PM operations in progress involving it will be waited for to
|
||||
* complete.
|
||||
* that point, this function cancels all pending runtime PM requests for @dev
|
||||
* and waits for its runtime PM operations to complete (if any).
|
||||
*
|
||||
* For each invocation of this function for @dev, there must be a matching
|
||||
* pm_runtime_enable() call, so that runtime PM is eventually enabled for it
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/cpu_pm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
|
|
@ -76,7 +77,8 @@ EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
|
|||
*
|
||||
* Remove a driver from the CPU PM notifier list.
|
||||
*
|
||||
* This function has the same return conditions as raw_notifier_chain_unregister.
|
||||
* This function may sleep, and has the same return conditions as
|
||||
* raw_notifier_chain_unregister.
|
||||
*/
|
||||
int cpu_pm_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
|
|
@ -86,6 +88,11 @@ int cpu_pm_unregister_notifier(struct notifier_block *nb)
|
|||
raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags);
|
||||
ret = raw_notifier_chain_unregister(&cpu_pm_notifier.chain, nb);
|
||||
raw_spin_unlock_irqrestore(&cpu_pm_notifier.lock, flags);
|
||||
|
||||
/* Wait for the rcu_read_lock() walkers in cpu_pm_notify(). */
|
||||
if (!ret)
|
||||
synchronize_rcu();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
|
||||
|
|
|
|||
|
|
@ -95,41 +95,62 @@ static int __em_nl_get_pd_for_dump(struct em_perf_domain *pd, void *data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int dev_energymodel_nl_get_perf_domains_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
int id, ret = -EMSGSIZE, msg_sz = 0;
|
||||
int cmd = info->genlhdr->cmd;
|
||||
struct em_perf_domain *pd;
|
||||
struct em_nl_doit_ctx {
|
||||
struct genl_info *info;
|
||||
int cmd;
|
||||
struct sk_buff *msg;
|
||||
};
|
||||
|
||||
static int __em_nl_get_pd_doit_fill(struct em_perf_domain *pd, void *data)
|
||||
{
|
||||
struct em_nl_doit_ctx *ctx = data;
|
||||
void *hdr;
|
||||
|
||||
hdr = genlmsg_put_reply(ctx->msg, ctx->info, &dev_energymodel_nl_family, 0,
|
||||
ctx->cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (__em_nl_get_pd(pd, ctx->msg)) {
|
||||
genlmsg_cancel(ctx->msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
genlmsg_end(ctx->msg, hdr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dev_energymodel_nl_get_perf_domains_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct em_nl_doit_ctx ctx = {
|
||||
.info = info,
|
||||
.cmd = info->genlhdr->cmd,
|
||||
};
|
||||
struct sk_buff *msg;
|
||||
int id, ret, msg_sz = 0;
|
||||
|
||||
if (!info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID])
|
||||
return -EINVAL;
|
||||
|
||||
id = nla_get_u32(info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID]);
|
||||
pd = em_perf_domain_get_by_id(id);
|
||||
if (!pd)
|
||||
return -EINVAL;
|
||||
|
||||
__em_nl_get_pd_size(pd, &msg_sz);
|
||||
/* Encode under em_pd_list_mutex, like the dumpit path. */
|
||||
ret = em_perf_domain_for_id(id, __em_nl_get_pd_size, &msg_sz);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msg = genlmsg_new(msg_sz, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
ret = __em_nl_get_pd(pd, msg);
|
||||
ctx.msg = msg;
|
||||
ret = em_perf_domain_for_id(id, __em_nl_get_pd_doit_fill, &ctx);
|
||||
if (ret)
|
||||
goto out_cancel_msg;
|
||||
genlmsg_end(msg, hdr);
|
||||
goto out_free_msg;
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
|
||||
out_cancel_msg:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
out_free_msg:
|
||||
nlmsg_free(msg);
|
||||
return ret;
|
||||
|
|
@ -148,19 +169,6 @@ int dev_energymodel_nl_get_perf_domains_dumpit(struct sk_buff *skb,
|
|||
return for_each_em_perf_domain(__em_nl_get_pd_for_dump, &ctx);
|
||||
}
|
||||
|
||||
static struct em_perf_domain *__em_nl_get_pd_table_id(struct nlattr **attrs)
|
||||
{
|
||||
struct em_perf_domain *pd;
|
||||
int id;
|
||||
|
||||
if (!attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID])
|
||||
return NULL;
|
||||
|
||||
id = nla_get_u32(attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID]);
|
||||
pd = em_perf_domain_get_by_id(id);
|
||||
return pd;
|
||||
}
|
||||
|
||||
static int __em_nl_get_pd_table_size(const struct em_perf_domain *pd)
|
||||
{
|
||||
int id_sz, ps_sz;
|
||||
|
|
@ -245,34 +253,58 @@ int __em_nl_get_pd_table(struct sk_buff *msg, const struct em_perf_domain *pd)
|
|||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
int dev_energymodel_nl_get_perf_table_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
static int __em_nl_get_pd_table_size_cb(struct em_perf_domain *pd, void *data)
|
||||
{
|
||||
int cmd = info->genlhdr->cmd;
|
||||
int msg_sz, ret = -EMSGSIZE;
|
||||
struct em_perf_domain *pd;
|
||||
struct sk_buff *msg;
|
||||
*(int *)data = __em_nl_get_pd_table_size(pd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __em_nl_get_pd_table_doit_fill(struct em_perf_domain *pd,
|
||||
void *data)
|
||||
{
|
||||
struct em_nl_doit_ctx *ctx = data;
|
||||
void *hdr;
|
||||
|
||||
pd = __em_nl_get_pd_table_id(info->attrs);
|
||||
if (!pd)
|
||||
hdr = genlmsg_put_reply(ctx->msg, ctx->info, &dev_energymodel_nl_family, 0,
|
||||
ctx->cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (__em_nl_get_pd_table(ctx->msg, pd))
|
||||
return -EMSGSIZE;
|
||||
|
||||
genlmsg_end(ctx->msg, hdr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dev_energymodel_nl_get_perf_table_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct em_nl_doit_ctx ctx = {
|
||||
.info = info,
|
||||
.cmd = info->genlhdr->cmd,
|
||||
};
|
||||
struct sk_buff *msg;
|
||||
int id, ret, msg_sz;
|
||||
|
||||
if (!info->attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID])
|
||||
return -EINVAL;
|
||||
|
||||
msg_sz = __em_nl_get_pd_table_size(pd);
|
||||
id = nla_get_u32(info->attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID]);
|
||||
|
||||
ret = em_perf_domain_for_id(id, __em_nl_get_pd_table_size_cb, &msg_sz);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msg = genlmsg_new(msg_sz, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
ret = __em_nl_get_pd_table(msg, pd);
|
||||
ctx.msg = msg;
|
||||
ret = em_perf_domain_for_id(id, __em_nl_get_pd_table_doit_fill, &ctx);
|
||||
if (ret)
|
||||
goto out_free_msg;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return genlmsg_reply(msg, info);
|
||||
|
||||
out_free_msg:
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@
|
|||
#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_NET)
|
||||
int for_each_em_perf_domain(int (*cb)(struct em_perf_domain*, void *),
|
||||
void *data);
|
||||
struct em_perf_domain *em_perf_domain_get_by_id(int id);
|
||||
int em_perf_domain_for_id(int id, int (*cb)(struct em_perf_domain *, void *),
|
||||
void *data);
|
||||
void em_notify_pd_created(const struct em_perf_domain *pd);
|
||||
void em_notify_pd_deleted(const struct em_perf_domain *pd);
|
||||
void em_notify_pd_updated(const struct em_perf_domain *pd);
|
||||
|
|
@ -24,9 +25,10 @@ int for_each_em_perf_domain(int (*cb)(struct em_perf_domain*, void *),
|
|||
return -EINVAL;
|
||||
}
|
||||
static inline
|
||||
struct em_perf_domain *em_perf_domain_get_by_id(int id)
|
||||
int em_perf_domain_for_id(int id, int (*cb)(struct em_perf_domain *, void *),
|
||||
void *data)
|
||||
{
|
||||
return NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void em_notify_pd_created(const struct em_perf_domain *pd) {}
|
||||
|
|
|
|||
|
|
@ -1031,7 +1031,9 @@ int for_each_em_perf_domain(int (*cb)(struct em_perf_domain*, void *),
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct em_perf_domain *em_perf_domain_get_by_id(int id)
|
||||
/* Run @cb on the matching domain with em_pd_list_mutex held. */
|
||||
int em_perf_domain_for_id(int id, int (*cb)(struct em_perf_domain *, void *),
|
||||
void *data)
|
||||
{
|
||||
struct em_perf_domain *pd;
|
||||
|
||||
|
|
@ -1040,9 +1042,9 @@ struct em_perf_domain *em_perf_domain_get_by_id(int id)
|
|||
|
||||
list_for_each_entry(pd, &em_pd_list, node) {
|
||||
if (pd->id == id)
|
||||
return pd;
|
||||
return cb(pd, data);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -73,6 +73,18 @@ static void sugov_update_rate_limit_us(struct sugov_policy *sg_policy)
|
|||
(s64)sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
|
||||
}
|
||||
|
||||
static void sugov_update_rate_limit_us_sync(struct sugov_policy *sg_policy)
|
||||
{
|
||||
/*
|
||||
* Serialize with rate_limit_us_store() to prevent this path from using
|
||||
* a stale rate limit tunable value to set the limit for the given
|
||||
* policy.
|
||||
*/
|
||||
guard(mutex)(&sg_policy->tunables->attr_set.update_lock);
|
||||
|
||||
sugov_update_rate_limit_us(sg_policy);
|
||||
}
|
||||
|
||||
static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
|
||||
{
|
||||
s64 delta_ns;
|
||||
|
|
@ -861,7 +873,7 @@ static int sugov_start(struct cpufreq_policy *policy)
|
|||
void (*uu)(struct update_util_data *data, u64 time, unsigned int flags);
|
||||
unsigned int cpu;
|
||||
|
||||
sugov_update_rate_limit_us(sg_policy);
|
||||
sugov_update_rate_limit_us_sync(sg_policy);
|
||||
sg_policy->last_freq_update_time = 0;
|
||||
sg_policy->next_freq = 0;
|
||||
sg_policy->work_in_progress = false;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,13 @@
|
|||
cpumask,
|
||||
device::{Bound, Device},
|
||||
devres,
|
||||
error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_DEFAULT_ERROR},
|
||||
error::{
|
||||
code::*,
|
||||
from_result,
|
||||
to_result,
|
||||
Result,
|
||||
VTABLE_DEFAULT_ERROR, //
|
||||
},
|
||||
ffi::{c_char, c_ulong},
|
||||
prelude::*,
|
||||
types::ForeignOwnable,
|
||||
|
|
@ -29,7 +35,10 @@
|
|||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
pin::Pin,
|
||||
ptr,
|
||||
ptr::{
|
||||
self,
|
||||
NonNull, //
|
||||
},
|
||||
};
|
||||
|
||||
use macros::vtable;
|
||||
|
|
@ -687,12 +696,13 @@ fn clear_data<T: ForeignOwnable>(&mut self) -> Option<T> {
|
|||
impl<'a> PolicyCpu<'a> {
|
||||
fn from_cpu(cpu: CpuId) -> Result<Self> {
|
||||
// SAFETY: It is safe to call `cpufreq_cpu_get` for any valid CPU.
|
||||
let ptr = from_err_ptr(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) })?;
|
||||
let ptr =
|
||||
NonNull::new(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) }).ok_or(ENODEV)?;
|
||||
|
||||
Ok(Self(
|
||||
// SAFETY: The `ptr` is guaranteed to be valid and remains valid for the lifetime of
|
||||
// the returned reference.
|
||||
unsafe { Policy::from_raw_mut(ptr) },
|
||||
// SAFETY: `ptr` is non-NULL and `cpufreq_cpu_get()` took a reference on it, so it is
|
||||
// valid for writing and remains valid for the lifetime of the returned reference.
|
||||
unsafe { Policy::from_raw_mut(ptr.as_ptr()) },
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1448,7 +1448,7 @@ class DevProps:
|
|||
|
||||
# Class: DeviceNode
|
||||
# Description:
|
||||
# A container used to create a device hierachy, with a single root node
|
||||
# A container used to create a device hierarchy, with a single root node
|
||||
# and a tree of child nodes. Used by Data.deviceTopology()
|
||||
class DeviceNode:
|
||||
def __init__(self, nodename, nodedepth):
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user