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:
Linus Torvalds 2026-10-21 14:24:26 -10:00
commit df37e39242
57 changed files with 3474 additions and 1376 deletions

View File

@ -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.

View File

@ -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>

View File

@ -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
===========================================

View File

@ -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

View File

@ -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 ... */

View File

@ -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

View File

@ -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

View File

@ -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>;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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),
{}
};

View File

@ -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;

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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.

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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", },

View File

@ -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)

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;

View File

@ -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",
};

View File

@ -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 */

View File

@ -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 },
{},

View File

@ -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);
}

View File

@ -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"],
}

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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.
*/

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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:

View File

@ -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) {}

View File

@ -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

View File

@ -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;

View File

@ -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()) },
))
}
}

View File

@ -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):