pmdomain core:

- Add a genpd config to support unknown initial status
  - Add support system wide resume latency constraints
  - Enable a non-CPU device in a CPU PM domain to do power on
  - Fall back to use device node name for domain idle states
 
 pmdomain providers:
  - cpuidle-psci: Initialize the PM domains in powered off state for OSI
  - cpuidle-psci: Move initialization a bit earlier in the boot sequence
  - qcom: Add NMXC power domain for Nord
  - qcom: Add support for SM7250, Kuno and MSM8952 power domains
  - renesas: Add support for r8a774a3 SYSC power domains
  - renesas: Fix lockdep false positive for r8a78000
  - riscv: Add RPMI device power service
  - st: Add the actual power domains on U8500
  - st: Convert Ux500 PM domains to schema
 
 pm qos:
  - Add a flag for system wide latency constraints
  - Add lockless read for flags
 -----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmrWFsAQHHVsZmhAa2Vy
 bmVsLm9yZwAKCRD+JoQlc1iMKRkXD/0bAYnBL9VJuBqxVFRpjGJkavjwF6+NVPSe
 2WXM04pdIQ3H+piy1+rbDWgx4F1qx9M+UpMGEq17wOq9SDw19m1qwTibnaBiNwzc
 jFsku056pNcIyeuviD1N0lhIzis3eY4jZ6PgxWp5EDd3Atd+th+MW/BDf/i+LgW3
 PhnegOb9DVJ5H9sX52tMpVVauXIJSkgMaM8bcnB484/n17KAscwrFntIrKqfLons
 M5VREjwqRh57iMv5YHWSr4qGQT5LVyL5npa1jhM8Gx/5UJJ17svLHLQ+LyzviGLe
 Qmr2MBysqss4HK6m5N/KXyfXTgKXSYd51A/+qivr4penHJEhEr/Q4q9AN0jedJXG
 8TJ0DJNwJBryOhoBG4+DtUv7XCg27itZQjMGp5n/uk+3ITxY0QvaUpo9NCSiRJHv
 dFf3eos5A9WVLwty0gmGW+owEN6kWuw3v1csUOAGtH/QleQ6DuExkKTDQnq4ueQP
 CHdj9M0ZXTtxfzhc9vUVbjPFT0U7MEsJ2bOJABxKGugDz6OiePhRlN8X7Mp6aRFJ
 2SLNCrNPOv7I9XoqvTTbR6HE3Oi1XxOkSxWLXWcztAN0DXsZgrkh7QK8cbpleSgF
 59dF4O1kTnO+ieCxFQ2H1s6S5Q+DPsdWf/oR86228tGYQChsekwahxn35e22VnAf
 GEzImsvLiQ==
 =bMow
 -----END PGP SIGNATURE-----

Merge tag 'pmdomain-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm

Pull pmdomain updates from Ulf Hansson:
 "pmdomain core:
   - Add a genpd config to support unknown initial status
   - Add support system wide resume latency constraints
   - Enable a non-CPU device in a CPU PM domain to do power on
   - Fall back to use device node name for domain idle states

  pmdomain providers:
   - cpuidle-psci: Initialize the PM domains in powered off state for OSI
   - cpuidle-psci: Move initialization a bit earlier in the boot sequence
   - qcom: Add NMXC power domain for Nord
   - qcom: Add support for SM7250, Kuno and MSM8952 power domains
   - renesas: Add support for r8a774a3 SYSC power domains
   - renesas: Fix lockdep false positive for r8a78000
   - riscv: Add RPMI device power service
   - st: Add the actual power domains on U8500
   - st: Convert Ux500 PM domains to schema

  pm qos:
   - Add a flag for system wide latency constraints
   - Add lockless read for flags"

* tag 'pmdomain-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm: (36 commits)
  dt-bindings: arm: Add the actual power domains on U8500
  dt-bindings: arm: ux500: Drop NR_DOMAINS
  dt-bindings: power: Convert Ux500 PM domains to schema
  dt-bindings: power: rpmpd: Add MSM8952 power domains
  pmdomain: rockchip: drop duplicated errno from supply failure message
  pmdomain: core: add support system-wide resume latency constraints
  pmdomain: core: add genpd_for_each_child() helper
  PM / QoS: add lockless read for flags
  PM / QoS: add flag to indicate latency applies system-wide
  pmdomain: renesas: rcar-sys: Add r8a774a3 support
  dt-bindings: power: Add r8a774a3 SYSC power domain definitions
  dt-bindings: power: renesas,rcar-sysc: Document vendor-prefixed header paths
  pmdomain: imx93-blk-ctrl: Use pm_runtime_resume_and_get()
  pmdomain: imx8m-blk-ctrl: Use pm_runtime_resume_and_get()
  pmdomain: imx: gpcv2: Use pm_runtime_resume_and_get()
  pmdomain: rockchip: Fix domain check in rockchip_pm_add_one_domain()
  pmdomain: core: Fall back to node name for idle states
  pmdomain: renesas: r8a78000: Fix lockdep false positive
  pmdomain: fix typo "releated" in comment
  pmdomain: imx: Propagate SCU provider registration failures
  ...
This commit is contained in:
Linus Torvalds 2026-10-22 07:31:02 -10:00
commit 0e557fc68a
42 changed files with 1113 additions and 106 deletions

View File

@ -1,35 +0,0 @@
* ST-Ericsson UX500 PM Domains
UX500 supports multiple PM domains which are used to gate power to one or
more peripherals on the SOC.
The implementation of PM domains for UX500 are based upon the generic PM domain
and use the corresponding DT bindings.
==PM domain providers==
Required properties:
- compatible: Must be "stericsson,ux500-pm-domains".
- #power-domain-cells : Number of cells in a power domain specifier, must be 1.
Example:
pm_domains: pm_domains0 {
compatible = "stericsson,ux500-pm-domains";
#power-domain-cells = <1>;
};
==PM domain consumers==
Required properties:
- power-domains: A phandle and PM domain specifier. Below are the list of
valid specifiers:
Index Specifier
----- ---------
0 DOMAIN_VAPE
Example:
sdi0_per1@80126000 {
compatible = "arm,pl18x", "arm,primecell";
power-domains = <&pm_domains DOMAIN_VAPE>
};

View File

@ -21,6 +21,7 @@ properties:
- qcom,glymur-rpmhpd
- qcom,hawi-rpmhpd
- qcom,kaanapali-rpmhpd
- qcom,kuno-rpmhpd
- qcom,mdm9607-rpmpd
- qcom,milos-rpmhpd
- qcom,msm8226-rpmpd
@ -63,6 +64,7 @@ properties:
- qcom,sm6350-rpmhpd
- qcom,sm6375-rpmpd
- qcom,sm7150-rpmhpd
- qcom,sm7250-rpmhpd
- qcom,sm8150-rpmhpd
- qcom,sm8250-rpmhpd
- qcom,sm8350-rpmhpd
@ -74,6 +76,7 @@ properties:
- items:
- enum:
- qcom,msm8937-rpmpd
- qcom,msm8952-rpmpd
- const: qcom,msm8917-rpmpd
- items:
- enum:

View File

@ -13,8 +13,11 @@ maintainers:
description: |
The R-Car (RZ/G) System Controller provides power management for the CPU
cores and various coprocessors.
The power domain IDs for consumers are defined in header files:
include/dt-bindings/power/r8*-sysc.h
The power domain IDs for consumers are defined in the below header files
(where the asterisk serves as a wildcard matching the specific SoC
identifier):
include/dt-bindings/power/r8*-sysc.h or
include/dt-bindings/power/renesas,r8*-sysc.h
properties:
compatible:

View File

@ -0,0 +1,65 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/riscv,rpmi-device-power.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RISC-V RPMI device power service group based power domain controller
maintainers:
- Joshua Yeong <joshua.yeong@starfivetech.com>
description: |
The RISC-V Platform Management Interface (RPMI) [1] defines a
messaging protocol which is modular and extensible. The supervisor
software can send/receive RPMI messages via SBI MPXY extension [2]
or some dedicated supervisor-mode RPMI transport.
The RPMI specification [1] defines device power service group for
accessing and controlling the power state of platform devices managed
by a platform microcontroller. The supervisor software can access RPMI
device power service group via SBI MPXY channel or some dedicated
supervisor-mode RPMI transport.
===========================================
References
===========================================
[1] RISC-V Platform Management Interface (RPMI) v1.0 (or higher)
https://github.com/riscv-non-isa/riscv-rpmi/releases
[2] RISC-V Supervisor Binary Interface (SBI) v3.0 (or higher)
https://github.com/riscv-non-isa/riscv-sbi-doc/releases
properties:
compatible:
description:
Intended for use by the supervisor software.
const: riscv,rpmi-device-power
mboxes:
maxItems: 1
description:
Mailbox channel of the underlying RPMI transport or SBI message proxy channel.
"#power-domain-cells":
const: 1
description:
Platform specific DOMAIN_ID as defined by the RISC-V Platform Management
Interface (RPMI) specification.
required:
- compatible
- mboxes
- "#power-domain-cells"
additionalProperties: false
examples:
- |
power-controller {
compatible = "riscv,rpmi-device-power";
mboxes = <&mpxy_mbox 0x1002 0x0>;
#power-domain-cells = <1>;
};
...

View File

@ -0,0 +1,65 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/riscv,rpmi-mpxy-device-power.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RISC-V RPMI device power service group based message proxy
maintainers:
- Joshua Yeong <joshua.yeong@starfivetech.com>
description: |
The RISC-V Platform Management Interface (RPMI) [1] defines a
messaging protocol which is modular and extensible. The supervisor
software can send/receive RPMI messages via SBI MPXY extension [2]
or some dedicated supervisor-mode RPMI transport.
The RPMI specification [1] defines device power service group for
accessing and controlling the power state of platform devices managed
by a platform microcontroller. The SBI implementation (machine mode
firmware or hypervisor) can implement an SBI MPXY channel to allow RPMI
device power service group access to the supervisor software.
===========================================
References
===========================================
[1] RISC-V Platform Management Interface (RPMI) v1.0 (or higher)
https://github.com/riscv-non-isa/riscv-rpmi/releases
[2] RISC-V Supervisor Binary Interface (SBI) v3.0 (or higher)
https://github.com/riscv-non-isa/riscv-sbi-doc/releases
properties:
compatible:
description:
Intended for use by the SBI implementation.
const: riscv,rpmi-mpxy-device-power
mboxes:
maxItems: 1
description:
Mailbox channel of the underlying RPMI transport.
riscv,sbi-mpxy-channel-id:
$ref: /schemas/types.yaml#/definitions/uint32
description:
The SBI MPXY channel id to be used for providing RPMI access to
the supervisor software.
required:
- compatible
- mboxes
- riscv,sbi-mpxy-channel-id
additionalProperties: false
examples:
- |
device-power-service {
compatible = "riscv,rpmi-mpxy-device-power";
mboxes = <&rpmi_shmem_mbox 0x9>;
riscv,sbi-mpxy-channel-id = <0x1002>;
};
...

View File

@ -0,0 +1,41 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/stericsson,ux500-pm-domains.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ST-Ericsson UX500 power domains
maintainers:
- Linus Walleij <linusw@kernel.org>
- Ulf Hansson <ulfh@kernel.org>
description:
The UX500 power domain controller gates power to one or more peripherals on
the SoC. Domain specifiers use one cell containing one of the DOMAIN_*
indexes defined in include/dt-bindings/arm/ux500_pm_domains.h.
allOf:
- $ref: power-domain.yaml#
properties:
compatible:
const: stericsson,ux500-pm-domains
'#power-domain-cells':
const: 1
required:
- compatible
- '#power-domain-cells'
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/arm/ux500_pm_domains.h>
pm_domains: power-controller {
compatible = "stericsson,ux500-pm-domains";
#power-domain-cells = <1>;
};

View File

@ -3283,6 +3283,7 @@ F: Documentation/devicetree/bindings/arm/ux500.yaml
F: Documentation/devicetree/bindings/arm/ux500/
F: Documentation/devicetree/bindings/gpio/st,nomadik-gpio.yaml
F: Documentation/devicetree/bindings/i2c/st,nomadik-i2c.yaml
F: Documentation/devicetree/bindings/power/stericsson,ux500-pm-domains.yaml
F: arch/arm/boot/dts/st/ste-*
F: arch/arm/mach-nomadik/
F: arch/arm/mach-ux500/
@ -23680,6 +23681,14 @@ F: drivers/irqchip/irq-riscv-rpmi-sysmsi.c
F: drivers/mailbox/riscv-sbi-mpxy-mbox.c
F: include/linux/mailbox/riscv-rpmi-message.h
RISC-V RPMI DEVICE POWER DRIVER
M: Joshua Yeong <joshua.yeong@starfivetech.com>
L: linux-riscv@lists.infradead.org
S: Maintained
F: Documentation/devicetree/bindings/power/riscv,rpmi-device-power.yaml
F: Documentation/devicetree/bindings/power/riscv,rpmi-mpxy-device-power.yaml
F: drivers/pmdomain/riscv/
RISC-V SPACEMIT SoC Support
M: Yixun Lan <dlan@kernel.org>
L: linux-riscv@lists.infradead.org

View File

@ -68,9 +68,8 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
*/
if (use_osi) {
pd->power_off = psci_pd_power_off;
pd->flags |= GENPD_FLAG_ACTIVE_WAKEUP;
if (IS_ENABLED(CONFIG_PREEMPT_RT))
pd->flags |= GENPD_FLAG_RPM_ALWAYS_ON;
pd->flags |= GENPD_FLAG_ACTIVE_WAKEUP | GENPD_FLAG_POWER_UNKNOWN;
pd->state_idx = pd->state_count ? pd->state_count - 1 : 0;
} else {
pd->flags |= GENPD_FLAG_ALWAYS_ON;
}
@ -78,7 +77,7 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
/* Use governor for CPU PM domains if it has some states to manage. */
pd_gov = pd->states ? &pm_domain_cpu_gov : NULL;
ret = pm_genpd_init(pd, pd_gov, false);
ret = pm_genpd_init(pd, pd_gov, use_osi);
if (ret)
goto free_pd_prov;

View File

@ -479,4 +479,4 @@ static int __init psci_idle_init(void)
return 0;
}
device_initcall(psci_idle_init);
subsys_initcall(psci_idle_init);

View File

@ -11,6 +11,7 @@ source "drivers/pmdomain/marvell/Kconfig"
source "drivers/pmdomain/mediatek/Kconfig"
source "drivers/pmdomain/qcom/Kconfig"
source "drivers/pmdomain/renesas/Kconfig"
source "drivers/pmdomain/riscv/Kconfig"
source "drivers/pmdomain/rockchip/Kconfig"
source "drivers/pmdomain/samsung/Kconfig"
source "drivers/pmdomain/st/Kconfig"

View File

@ -9,6 +9,7 @@ obj-y += marvell/
obj-y += mediatek/
obj-y += qcom/
obj-y += renesas/
obj-y += riscv/
obj-y += rockchip/
obj-y += samsung/
obj-y += st/

View File

@ -10,6 +10,7 @@
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
@ -19,11 +20,15 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/suspend.h>
#include <linux/export.h>
#include <linux/cpu.h>
#include <linux/debugfs.h>
#include <trace/events/ipi.h>
#include "core.h"
/* Provides a unique ID for each genpd device */
static DEFINE_IDA(genpd_ida);
@ -32,7 +37,9 @@ static const struct bus_type genpd_provider_bus_type = {
.name = "genpd_provider",
};
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
#define GENPD_CPU_ON_POLL_PERIOD_US 100 /* 100us */
#define GENPD_CPU_ON_TIMEOUT_US 300000 /* 300ms */
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
({ \
@ -173,7 +180,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
#define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p)
#define genpd_unlock(p) p->lock_ops->unlock(p)
#define genpd_status_on(genpd) (genpd->status == GENPD_STATE_ON)
#define genpd_status_on_unlocked(genpd) (genpd->status == GENPD_STATE_ON)
#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE)
#define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON)
#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
@ -183,6 +190,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
#define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW)
#define genpd_is_no_sync_state(genpd) (genpd->flags & GENPD_FLAG_NO_SYNC_STATE)
#define genpd_is_no_stay_on(genpd) (genpd->flags & GENPD_FLAG_NO_STAY_ON)
#define genpd_is_power_unknown(genpd) (genpd->flags & GENPD_FLAG_POWER_UNKNOWN)
static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
const struct generic_pm_domain *genpd)
@ -276,6 +284,49 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
smp_mb__after_atomic();
}
/**
* genpd_for_each_child - Recursively iterate over all devices
* in a PM domain and its subdomains.
* @genpd: PM domain to iterate over.
* @fn: Callback function to invoke for each device.
* @data: Data to pass to the callback function.
*
* This function recursively walks through all devices in the given PM domain
* and all devices in its child PM domains (subdomains). For each device found,
* the callback function @fn is invoked with the device and @data as arguments.
*
* Note: this function is inteded for use by the core and governors,
* not for pmdomain providers.
*
* Returns: 0 on success, or the first non-zero value returned by @fn.
*/
int genpd_for_each_child(struct generic_pm_domain *genpd,
int (*fn)(struct device *dev, void *data),
void *data)
{
struct pm_domain_data *pdd;
struct gpd_link *link;
int ret;
/* First, iterate over all devices in this domain */
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
ret = fn(pdd->dev, data);
if (ret)
return ret;
}
/* Then, recursively iterate over all child domains (subdomains) */
list_for_each_entry(link, &genpd->parent_links, parent_node) {
struct generic_pm_domain *child_pd = link->child;
ret = genpd_for_each_child(child_pd, fn, data);
if (ret)
return ret;
}
return 0;
}
#ifdef CONFIG_DEBUG_FS
static struct dentry *genpd_debugfs_dir;
@ -771,8 +822,7 @@ EXPORT_SYMBOL_GPL(dev_pm_genpd_rpm_always_on);
* @dev: Device to get the current power status
*
* This function checks whether the generic power domain associated with the
* given device is on or not by verifying if genpd_status_on equals
* GENPD_STATE_ON.
* given device is on or not by checking if genpd->status equals GENPD_STATE_ON.
*
* Note: this function returns the power status of the genpd at the time of the
* call. The power status may change after due to activity from other devices
@ -791,7 +841,7 @@ bool dev_pm_genpd_is_on(struct device *dev)
return false;
genpd_lock(genpd);
is_on = genpd_status_on(genpd);
is_on = genpd_status_on_unlocked(genpd);
genpd_unlock(genpd);
return is_on;
@ -940,7 +990,7 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
* genpd_power_off - Remove power from a given PM domain.
* @genpd: PM domain to power down.
* @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the
* RPM status of the releated device is in an intermediate state, not yet turned
* RPM status of the related device is in an intermediate state, not yet turned
* into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not
* be RPM_SUSPENDED, while it tries to power off the PM domain.
* @depth: nesting count for lockdep.
@ -963,7 +1013,7 @@ static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
* The domain was on at boot and still need to stay on.
* The domain has a subdomain being powered on.
*/
if (!genpd_status_on(genpd) || genpd->prepared_count > 0 ||
if (!genpd_status_on_unlocked(genpd) || genpd->prepared_count > 0 ||
genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd) ||
genpd->stay_on || atomic_read(&genpd->sd_count) > 0)
return;
@ -1027,22 +1077,86 @@ static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
}
}
static bool genpd_status_on(struct generic_pm_domain *genpd)
{
bool is_on;
genpd_lock(genpd);
is_on = genpd_status_on_unlocked(genpd);
genpd_unlock(genpd);
return is_on;
}
static int genpd_wakeup_cpu(struct generic_pm_domain *genpd)
{
unsigned int cpu;
bool is_on;
int ret;
/* Find the first online CPU in the genpd's cpumask. */
cpu = cpumask_first_and(genpd->cpus, cpu_online_mask);
if (cpu >= nr_cpu_ids)
return -EAGAIN;
genpd_unlock(genpd);
/* Send a IPI to wakeup the selected CPU. */
smp_send_reschedule(cpu);
/* Poll to wait for it to complete the power on sequence. */
ret = readx_poll_timeout_atomic(genpd_status_on, genpd, is_on, is_on,
GENPD_CPU_ON_POLL_PERIOD_US,
GENPD_CPU_ON_TIMEOUT_US);
genpd_lock(genpd);
/* Re-check the status as we have released the lock in between. */
if (ret || !genpd_status_on_unlocked(genpd))
return -EAGAIN;
return 0;
}
static bool genpd_need_alive_cpu(struct generic_pm_domain *genpd,
struct device *dev)
{
if (!genpd_is_cpu_domain(genpd))
return false;
/* This is not for CPU devices as those are managed differently. */
if (to_gpd_data(dev->power.subsys_data->domain_data)->cpu >= 0)
return false;
/*
* If the current CPU doesn't belong to the genpd's cpumask, we need to
* wake up one of those idle CPUs to power on the CPU domain correctly.
*/
return !cpumask_test_cpu(smp_processor_id(), genpd->cpus);
}
/**
* genpd_power_on - Restore power to a given PM domain and its parents.
* @genpd: PM domain to power up.
* @dev: The device that needs the PM domain to power on.
* @depth: nesting count for lockdep.
*
* Restore power to @genpd and all of its parents so that it is possible to
* resume a device belonging to it.
*/
static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
static int genpd_power_on(struct generic_pm_domain *genpd, struct device *dev,
unsigned int depth)
{
struct gpd_link *link;
int ret = 0;
if (genpd_status_on(genpd))
if (genpd_status_on_unlocked(genpd))
return 0;
/* Special case for a device attached to a CPU domain. */
if (genpd_need_alive_cpu(genpd, dev))
return genpd_wakeup_cpu(genpd);
/* Reflect over the entered idle-states residency for debugfs. */
genpd_reflect_residency(genpd);
@ -1057,7 +1171,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
genpd_sd_counter_inc(parent);
genpd_lock_nested(parent, depth + 1);
ret = genpd_power_on(parent, depth + 1);
ret = genpd_power_on(parent, dev, depth + 1);
genpd_unlock(parent);
if (ret) {
@ -1307,7 +1421,7 @@ static int genpd_runtime_resume(struct device *dev)
genpd_lock(genpd);
genpd_restore_performance_state(dev, gpd_data->rpm_pstate);
ret = genpd_power_on(genpd, 0);
ret = genpd_power_on(genpd, dev, 0);
genpd_unlock(genpd);
if (ret)
@ -1406,7 +1520,7 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
{
struct gpd_link *link;
if (!genpd_status_on(genpd) || genpd_is_always_on(genpd))
if (!genpd_status_on_unlocked(genpd) || genpd_is_always_on(genpd))
return;
if (genpd->suspended_count != genpd->device_count
@ -1472,7 +1586,7 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,
{
struct gpd_link *link;
if (genpd_status_on(genpd))
if (genpd_status_on_unlocked(genpd))
return;
list_for_each_entry(link, &genpd->child_links, child_node) {
@ -2170,7 +2284,8 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd,
genpd_lock(subdomain);
genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
if (!genpd_status_on(genpd) && genpd_status_on(subdomain)) {
if (!genpd_status_on_unlocked(genpd) &&
genpd_status_on_unlocked(subdomain)) {
ret = -EINVAL;
goto out;
}
@ -2186,7 +2301,7 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd,
list_add_tail(&link->parent_node, &genpd->parent_links);
link->child = subdomain;
list_add_tail(&link->child_node, &subdomain->child_links);
if (genpd_status_on(subdomain))
if (genpd_status_on_unlocked(subdomain))
genpd_sd_counter_inc(genpd);
out:
@ -2246,7 +2361,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
list_del(&link->parent_node);
list_del(&link->child_node);
kfree(link);
if (genpd_status_on(subdomain))
if (genpd_status_on_unlocked(subdomain))
genpd_sd_counter_dec(genpd);
ret = 0;
@ -2375,7 +2490,10 @@ static void genpd_lock_init(struct generic_pm_domain *genpd)
#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
{
genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
if (genpd_is_power_unknown(genpd))
genpd->stay_on = is_off;
else
genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
}
#else
static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
@ -2441,7 +2559,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
/* Always-on domains must be powered on at initialization. */
if ((genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) &&
!genpd_status_on(genpd)) {
!genpd_status_on_unlocked(genpd)) {
pr_err("always-on PM domain %s is not on\n", genpd->name);
return -EINVAL;
}
@ -3410,7 +3528,7 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
if (power_on) {
genpd_lock(pd);
ret = genpd_power_on(pd, 0);
ret = genpd_power_on(pd, dev, 0);
genpd_unlock(pd);
}
@ -3583,7 +3701,10 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state,
if (!err)
genpd_state->residency_ns = 1000LL * residency;
of_property_read_string(state_node, "idle-state-name", &genpd_state->name);
err = of_property_read_string(state_node, "idle-state-name",
&genpd_state->name);
if (err)
genpd_state->name = state_node->name;
genpd_state->power_on_latency_ns = 1000LL * exit_latency;
genpd_state->power_off_latency_ns = 1000LL * entry_latency;
@ -3839,7 +3960,7 @@ static int genpd_summary_one(struct seq_file *s,
if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup)))
goto exit;
if (!genpd_status_on(genpd))
if (!genpd_status_on_unlocked(genpd))
snprintf(state, sizeof(state), "%s-%u",
status_lookup[genpd->status], genpd->state_idx);
else

18
drivers/pmdomain/core.h Normal file
View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Internal header for PM domain core
*
* Copyright (C) 2026 Kevin Hilman <khilman@baylibre.com>, Texas Instruments
*/
#ifndef __PM_DOMAIN_CORE_H__
#define __PM_DOMAIN_CORE_H__
struct device;
struct generic_pm_domain;
int genpd_for_each_child(struct generic_pm_domain *genpd,
int (*fn)(struct device *dev, void *data),
void *data);
#endif /* __PM_DOMAIN_CORE_H__ */

View File

@ -13,6 +13,8 @@
#include <linux/cpumask.h>
#include <linux/ktime.h>
#include "core.h"
static int dev_update_qos_constraint(struct device *dev, void *data)
{
s64 *constraint_ns_p = data;
@ -425,17 +427,60 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd)
return true;
}
/**
* check_device_qos_latency - Callback to check device QoS latency constraints
* @dev: Device to check
* @data: Pointer to s32 variable holding minimum latency found so far
*
* This callback checks if the device has a system-wide resume latency QoS
* constraint and updates the minimum latency if this device has a stricter
* constraint.
*
* The system-wide flag is checked first so that devices that have not opted
* in only incur a single lockless read.
*
* Returns: 0 to continue iteration.
*/
static int check_device_qos_latency(struct device *dev, void *data)
{
s32 *min_dev_latency = data;
s32 dev_latency;
if (!(dev_pm_qos_raw_flags(dev) & PM_QOS_FLAG_LATENCY_SYS))
return 0;
dev_latency = dev_pm_qos_raw_resume_latency(dev);
if (dev_latency != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) {
if (dev_latency < *min_dev_latency)
*min_dev_latency = dev_latency;
}
return 0;
}
static bool cpu_system_power_down_ok(struct dev_pm_domain *pd)
{
s64 constraint_ns = cpu_wakeup_latency_qos_limit() * NSEC_PER_USEC;
struct generic_pm_domain *genpd = pd_to_genpd(pd);
int state_idx = genpd->state_count - 1;
s32 min_dev_latency = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
s64 min_dev_latency_ns;
if (!(genpd->flags & GENPD_FLAG_CPU_DOMAIN)) {
genpd->state_idx = state_idx;
return true;
}
genpd_for_each_child(genpd, check_device_qos_latency,
&min_dev_latency);
/* If device latency < CPU wakeup latency, use it instead */
if (min_dev_latency != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) {
min_dev_latency_ns = (s64)min_dev_latency * NSEC_PER_USEC;
if (min_dev_latency_ns < constraint_ns)
constraint_ns = min_dev_latency_ns;
}
/* Find the deepest state for the latency constraint. */
while (state_idx >= 0) {
s64 latency_ns = genpd->states[state_idx].power_off_latency_ns +

View File

@ -317,11 +317,9 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
u32 reg_val, pgc;
int ret;
ret = pm_runtime_get_sync(domain->dev);
if (ret < 0) {
pm_runtime_put_noidle(domain->dev);
ret = pm_runtime_resume_and_get(domain->dev);
if (ret < 0)
return ret;
}
if (!IS_ERR(domain->regulator)) {
ret = regulator_enable(domain->regulator);
@ -1397,21 +1395,13 @@ static void imx_pgc_domain_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int imx_pgc_domain_suspend(struct device *dev)
{
int ret;
/*
* This may look strange, but is done so the generic PM_SLEEP code
* can power down our domain and more importantly power it up again
* after resume, without tripping over our usage of runtime PM to
* power up/down the nested domains.
*/
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev);
return ret;
}
return 0;
return pm_runtime_resume_and_get(dev);
}
static int imx_pgc_domain_resume(struct device *dev)

View File

@ -108,9 +108,8 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
guard(mutex)(&bc->power_lock);
/* make sure bus domain is awake */
ret = pm_runtime_get_sync(bc->bus_power_dev);
ret = pm_runtime_resume_and_get(bc->bus_power_dev);
if (ret < 0) {
pm_runtime_put_noidle(bc->bus_power_dev);
dev_err(bc->dev, "failed to power up bus domain\n");
return ret;
}
@ -397,20 +396,16 @@ static int imx8m_blk_ctrl_suspend(struct device *dev)
* in the system suspend/resume paths due to the device parent/child
* hierarchy.
*/
ret = pm_runtime_get_sync(bc->bus_power_dev);
if (ret < 0) {
pm_runtime_put_noidle(bc->bus_power_dev);
ret = pm_runtime_resume_and_get(bc->bus_power_dev);
if (ret < 0)
return ret;
}
for (i = 0; i < bc->onecell_data.num_domains; i++) {
struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
ret = pm_runtime_get_sync(domain->power_dev);
if (ret < 0) {
pm_runtime_put_noidle(domain->power_dev);
ret = pm_runtime_resume_and_get(domain->power_dev);
if (ret < 0)
goto out_fail;
}
}
return 0;

View File

@ -152,9 +152,8 @@ static int imx93_blk_ctrl_power_on(struct generic_pm_domain *genpd)
return ret;
}
ret = pm_runtime_get_sync(bc->dev);
ret = pm_runtime_resume_and_get(bc->dev);
if (ret < 0) {
pm_runtime_put_noidle(bc->dev);
dev_err(bc->dev, "failed to power up domain\n");
goto disable_clk;
}

View File

@ -474,7 +474,7 @@ static int imx_scu_init_pm_domains(struct device *dev,
struct genpd_onecell_data *pd_data;
struct imx_sc_pm_domain *sc_pd;
u32 count = 0;
int i, j;
int i, j, ret;
for (i = 0; i < pd_soc->num_ranges; i++)
count += pd_ranges[i].num;
@ -503,7 +503,13 @@ static int imx_scu_init_pm_domains(struct device *dev,
pd_data->num_domains = count;
pd_data->xlate = imx_scu_pd_xlate;
of_genpd_add_provider_onecell(dev->of_node, pd_data);
ret = of_genpd_add_provider_onecell(dev->of_node, pd_data);
if (ret) {
while (count)
pm_genpd_remove(domains[--count]);
return ret;
}
return 0;
}

View File

@ -198,6 +198,11 @@ static struct rpmhpd mxc_ao = {
.res_name = "mxc.lvl",
};
static struct rpmhpd nmxc = {
.pd = { .name = "nmxc", },
.res_name = "nmxc.lvl",
};
static struct rpmhpd nsp = {
.pd = { .name = "nsp", },
.res_name = "nsp.lvl",
@ -327,6 +332,7 @@ static struct rpmhpd *nord_rpmhpds[] = {
[RPMHPD_MX_AO] = &mx_ao,
[RPMHPD_MXC] = &mxc,
[RPMHPD_MXC_AO] = &mxc_ao,
[RPMHPD_NMXC] = &nmxc,
[RPMHPD_NSP0] = &nsp0,
[RPMHPD_NSP1] = &nsp1,
[RPMHPD_NSP2] = &nsp2,
@ -486,6 +492,24 @@ static const struct rpmhpd_desc sm7150_desc = {
.num_pds = ARRAY_SIZE(sm7150_rpmhpds),
};
/* SM7250 RPMH powerdomains */
static struct rpmhpd *sm7250_rpmhpds[] = {
[RPMHPD_CX] = &cx_w_mx_parent,
[RPMHPD_CX_AO] = &cx_ao_w_mx_parent,
[RPMHPD_EBI] = &ebi,
[RPMHPD_GFX] = &gfx,
[RPMHPD_LCX] = &lcx,
[RPMHPD_LMX] = &lmx,
[RPMHPD_MSS] = &mss,
[RPMHPD_MX] = &mx,
[RPMHPD_MX_AO] = &mx_ao,
};
static const struct rpmhpd_desc sm7250_desc = {
.rpmhpds = sm7250_rpmhpds,
.num_pds = ARRAY_SIZE(sm7250_rpmhpds),
};
/* SM8150 RPMH powerdomains */
static struct rpmhpd *sm8150_rpmhpds[] = {
[SM8150_CX] = &cx_w_mx_parent,
@ -681,6 +705,21 @@ static const struct rpmhpd_desc kaanapali_desc = {
.num_pds = ARRAY_SIZE(kaanapali_rpmhpds),
};
/* Kuno RPMH power domains */
static struct rpmhpd *kuno_rpmhpds[] = {
[RPMHPD_CX] = &cx,
[RPMHPD_CX_AO] = &cx_ao,
[RPMHPD_MX] = &mx,
[RPMHPD_MX_AO] = &mx_ao,
[RPMHPD_MXC] = &mxc,
[RPMHPD_MXC_AO] = &mxc_ao,
};
static const struct rpmhpd_desc kuno_desc = {
.rpmhpds = kuno_rpmhpds,
.num_pds = ARRAY_SIZE(kuno_rpmhpds),
};
/* Hawi RPMH powerdomains */
static struct rpmhpd *hawi_rpmhpds[] = {
[RPMHPD_CX] = &cx,
@ -885,6 +924,7 @@ static const struct of_device_id rpmhpd_match_table[] = {
{ .compatible = "qcom,glymur-rpmhpd", .data = &glymur_desc },
{ .compatible = "qcom,hawi-rpmhpd", .data = &hawi_desc },
{ .compatible = "qcom,kaanapali-rpmhpd", .data = &kaanapali_desc },
{ .compatible = "qcom,kuno-rpmhpd", .data = &kuno_desc },
{ .compatible = "qcom,milos-rpmhpd", .data = &milos_desc },
{ .compatible = "qcom,nord-rpmhpd", .data = &nord_desc },
{ .compatible = "qcom,qcs615-rpmhpd", .data = &qcs615_desc },
@ -906,6 +946,7 @@ static const struct of_device_id rpmhpd_match_table[] = {
{ .compatible = "qcom,sm4450-rpmhpd", .data = &sm4450_desc },
{ .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc },
{ .compatible = "qcom,sm7150-rpmhpd", .data = &sm7150_desc },
{ .compatible = "qcom,sm7250-rpmhpd", .data = &sm7250_desc },
{ .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc },
{ .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc },
{ .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc },

View File

@ -37,6 +37,10 @@ config SYSC_R8A774A1
bool "System Controller support for R8A774A1 (RZ/G2M)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774A3
bool "System Controller support for R8A774A3 (RZ/G2M v3.0)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774B1
bool "System Controller support for R8A774B1 (RZ/G2N)" if COMPILE_TEST
select SYSC_RCAR

View File

@ -6,6 +6,7 @@ obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o
obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o
obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o
obj-$(CONFIG_SYSC_R8A774A1) += r8a774a1-sysc.o
obj-$(CONFIG_SYSC_R8A774A3) += r8a774a3-sysc.o
obj-$(CONFIG_SYSC_R8A774B1) += r8a774b1-sysc.o
obj-$(CONFIG_SYSC_R8A774C0) += r8a774c0-sysc.o
obj-$(CONFIG_SYSC_R8A774E1) += r8a774e1-sysc.o

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G2M v3.0 System Controller
* Copyright (C) 2026 Renesas Electronics Corp.
*
* Based on Renesas R-Car M3-W System Controller
* Copyright (C) 2016 Glider bvba
*/
#include <linux/kernel.h>
#include <dt-bindings/power/renesas,r8a774a3-sysc.h>
#include "rcar-sysc.h"
static const struct rcar_sysc_area r8a774a3_areas[] __initconst = {
{ "always-on", 0, 0, R8A774A3_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "ca57-scu", 0x1c0, 0, R8A774A3_PD_CA57_SCU, R8A774A3_PD_ALWAYS_ON,
PD_SCU },
{ "ca57-cpu0", 0x80, 0, R8A774A3_PD_CA57_CPU0, R8A774A3_PD_CA57_SCU,
PD_CPU_NOCR },
{ "ca57-cpu1", 0x80, 1, R8A774A3_PD_CA57_CPU1, R8A774A3_PD_CA57_SCU,
PD_CPU_NOCR },
{ "ca53-scu", 0x140, 0, R8A774A3_PD_CA53_SCU, R8A774A3_PD_ALWAYS_ON,
PD_SCU },
{ "ca53-cpu0", 0x200, 0, R8A774A3_PD_CA53_CPU0, R8A774A3_PD_CA53_SCU,
PD_CPU_NOCR },
{ "ca53-cpu1", 0x200, 1, R8A774A3_PD_CA53_CPU1, R8A774A3_PD_CA53_SCU,
PD_CPU_NOCR },
{ "ca53-cpu2", 0x200, 2, R8A774A3_PD_CA53_CPU2, R8A774A3_PD_CA53_SCU,
PD_CPU_NOCR },
{ "ca53-cpu3", 0x200, 3, R8A774A3_PD_CA53_CPU3, R8A774A3_PD_CA53_SCU,
PD_CPU_NOCR },
{ "a3vc", 0x380, 0, R8A774A3_PD_A3VC, R8A774A3_PD_ALWAYS_ON },
{ "a2vc1", 0x3c0, 1, R8A774A3_PD_A2VC1, R8A774A3_PD_A3VC },
{ "3dg-a", 0x100, 0, R8A774A3_PD_3DG_A, R8A774A3_PD_ALWAYS_ON },
{ "3dg-b", 0x100, 1, R8A774A3_PD_3DG_B, R8A774A3_PD_3DG_A },
};
const struct rcar_sysc_info r8a774a3_sysc_info __initconst = {
.areas = r8a774a3_areas,
.num_areas = ARRAY_SIZE(r8a774a3_areas),
.extmask_offs = 0x2f8,
.extmask_val = BIT(0),
};

View File

@ -13,7 +13,7 @@
#include "rcar-sysc.h"
static struct rcar_sysc_area r8a7795_areas[] __initdata = {
static const struct rcar_sysc_area r8a7795_areas[] __initconst = {
{ "always-on", 0, 0, R8A7795_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "ca57-scu", 0x1c0, 0, R8A7795_PD_CA57_SCU, R8A7795_PD_ALWAYS_ON,
PD_SCU },

View File

@ -11,7 +11,7 @@
#include "rcar-gen4-sysc.h"
static struct rcar_gen4_sysc_area r8a779a0_areas[] __initdata = {
static const struct rcar_gen4_sysc_area r8a779a0_areas[] __initconst = {
{ "always-on", R8A779A0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "a3e0", R8A779A0_PD_A3E0, R8A779A0_PD_ALWAYS_ON, PD_SCU },
{ "a3e1", R8A779A0_PD_A3E1, R8A779A0_PD_ALWAYS_ON, PD_SCU },

View File

@ -11,7 +11,7 @@
#include "rcar-gen4-sysc.h"
static struct rcar_gen4_sysc_area r8a779f0_areas[] __initdata = {
static const struct rcar_gen4_sysc_area r8a779f0_areas[] __initconst = {
{ "always-on", R8A779F0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "a3e0", R8A779F0_PD_A3E0, R8A779F0_PD_ALWAYS_ON, PD_SCU },
{ "a3e1", R8A779F0_PD_A3E1, R8A779F0_PD_ALWAYS_ON, PD_SCU },

View File

@ -11,7 +11,7 @@
#include "rcar-gen4-sysc.h"
static struct rcar_gen4_sysc_area r8a779g0_areas[] __initdata = {
static const struct rcar_gen4_sysc_area r8a779g0_areas[] __initconst = {
{ "always-on", R8A779G0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "a3e0", R8A779G0_PD_A3E0, R8A779G0_PD_ALWAYS_ON, PD_SCU },
{ "a2e0d0", R8A779G0_PD_A2E0D0, R8A779G0_PD_A3E0, PD_SCU },

View File

@ -11,7 +11,7 @@
#include "rcar-gen4-sysc.h"
static struct rcar_gen4_sysc_area r8a779h0_areas[] __initdata = {
static const struct rcar_gen4_sysc_area r8a779h0_areas[] __initconst = {
{ "always-on", R8A779H0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "c4", R8A779H0_PD_C4, R8A779H0_PD_ALWAYS_ON },
{ "a2e0d0", R8A779H0_PD_A2E0D0, R8A779H0_PD_C4, PD_SCU },

View File

@ -45,8 +45,9 @@ struct r8a78000_mdlc_priv {
};
static struct generic_pm_domain *r8a78000_genpd_always_on;
static HLIST_HEAD(r8a78000_mdlc_list);
static DEFINE_MUTEX(r8a78000_mdlc_lock); /* protects the two above */
static DEFINE_MUTEX(r8a78000_mdlc_lock); /* protects the list above */
static struct generic_pm_domain *r8a78000_genpd_xlate(
const struct of_phandle_args *spec, void *data)
@ -191,10 +192,11 @@ static void r8a78000_genpd_del_provider(void *data)
static int r8a78000_genpd_always_on_singleton(struct device *dev)
{
static DEFINE_MUTEX(singleton_lock);
struct generic_pm_domain *genpd;
int ret;
guard(mutex)(&r8a78000_mdlc_lock);
guard(mutex)(&singleton_lock);
if (r8a78000_genpd_always_on)
return 0;

View File

@ -284,6 +284,9 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = {
#ifdef CONFIG_SYSC_R8A774A1
{ .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A774A3
{ .compatible = "renesas,r8a774a3-sysc", .data = &r8a774a3_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A774B1
{ .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info },
#endif

View File

@ -29,9 +29,15 @@
struct rcar_sysc_area {
const char *name;
u16 chan_offs; /* Offset of PWRSR register for this area */
u8 chan_bit; /* Bit in PWR* (except for PWRUP in PWRSR) */
u8 isr_bit; /* Bit in SYSCI*R */
u16 chan_offs; /*
* PWRSR register offset; or 0 if area is
* always on
*/
u8 chan_bit; /*
* Bit in PWR* (except for PWRUP in PWRSR);
* 0 if area is always on
*/
u8 isr_bit; /* Bit in SYSCI*R; 0 if area is always on */
s8 parent; /* -1 if none */
u8 flags; /* See PD_* */
};
@ -55,6 +61,7 @@ extern const struct rcar_sysc_info r8a7743_sysc_info;
extern const struct rcar_sysc_info r8a7745_sysc_info;
extern const struct rcar_sysc_info r8a77470_sysc_info;
extern const struct rcar_sysc_info r8a774a1_sysc_info;
extern const struct rcar_sysc_info r8a774a3_sysc_info;
extern const struct rcar_sysc_info r8a774b1_sysc_info;
extern const struct rcar_sysc_info r8a774c0_sysc_info;
extern const struct rcar_sysc_info r8a774e1_sysc_info;

View File

@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
config RISCV_RPMI_DEVICE_POWER
bool "RISC-V RPMI Based Device Power driver"
depends on RISCV || COMPILE_TEST
depends on MAILBOX
depends on PM
default RISCV
select PM_GENERIC_DOMAINS
help
Support for device power domains based on the device power service
group defined by the RISC-V platform management interface (RPMI)
specification. The power domains advertised by the platform
microcontroller are registered as generic power domains, so that
devices can reference them through the "power-domains" property.

View File

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_RISCV_RPMI_DEVICE_POWER) += riscv-rpmi-device-power.o

View File

@ -0,0 +1,485 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RISC-V RPMI Based Device Power Driver through SBI MPXY
*
* Copyright (C) 2026 Shanghai StarFive Technology Co., Ltd.
*
* Implements a Device Power driver on top of SBI RPMI Message Proxy Extension (MPXY)
*
* Each SBI MPXY Device Power instance is associated, through the means of a proper DT
* entry description, to a specific Transport ID.
*/
#include <linux/bitfield.h>
#include <linux/mailbox/riscv-rpmi-message.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#define RPMI_PM_DOMAIN_NAME_LEN 16
/* power state for device power domains */
#define RPMI_POWER_STATE_CONTEXT_MASK GENMASK(16, 16)
#define RPMI_POWER_STATE_CONTEXT_PRESERVED 0
#define RPMI_POWER_STATE_CONTEXT_LOST 1
#define RPMI_POWER_STATE_VALUE_MASK GENMASK(15, 0)
#define RPMI_POWER_STATE_VALUE_ON 0
#define RPMI_POWER_STATE_VALUE_OFF 3
#define RPMI_POWER_STATE_PARAM(context_type, state_value) \
((FIELD_PREP(RPMI_POWER_STATE_CONTEXT_MASK, context_type)) | \
(FIELD_PREP(RPMI_POWER_STATE_VALUE_MASK, state_value)))
#define RPMI_POWER_STATE_GENERIC_ON \
RPMI_POWER_STATE_PARAM(RPMI_POWER_STATE_CONTEXT_PRESERVED, \
RPMI_POWER_STATE_VALUE_ON)
#define RPMI_POWER_STATE_GENERIC_OFF \
RPMI_POWER_STATE_PARAM(RPMI_POWER_STATE_CONTEXT_PRESERVED, \
RPMI_POWER_STATE_VALUE_OFF)
/**
* struct rpmi_ctx - RPMI transport context shared by all power domains
*
* @chan: mailbox channel of the underlying SBI MPXY transport
* @client: mailbox client bound to @chan
* @lock: serialises RPMI transactions on @chan
*/
struct rpmi_ctx {
struct mbox_chan *chan;
struct mbox_client client;
/* serialises RPMI transactions on @chan */
struct mutex lock;
};
/**
* struct rpmi_device_power_domain - describe one available MPXY Device Power Domain
*
* @id: the power domain ID as advertised by PuC
* @transition_latency: worst case transition latency of power domain from one state
* to another
* @dev: device associated with this power domain
* @mpxy_ctx: RPMI transport context used to reach the PuC
* @genpd: generic power domain registered with the genpd core
* @name: device power domain name assigned by PuC
*/
struct rpmi_device_power_domain {
u32 id;
u32 transition_latency;
struct device *dev;
struct rpmi_ctx *mpxy_ctx;
struct generic_pm_domain genpd;
char name[RPMI_PM_DOMAIN_NAME_LEN];
};
#define to_rpmi_pd(gpd) container_of(gpd, struct rpmi_device_power_domain, genpd)
/* Service: GET_POWER_DOMAINS */
struct rpmi_pm_get_num_domain_rx {
__le32 status;
__le32 num_domains;
};
/* Service: GET_POWER_DOMAIN_ATTRS */
struct rpmi_pm_get_domain_attrs_tx {
__le32 domain_id;
};
/* pm domain attributes response data */
struct rpmi_pm_get_domain_attrs_rx {
__le32 status;
__le32 flags;
__le32 transition_latency;
char name[RPMI_PM_DOMAIN_NAME_LEN];
};
/* Service: SET_POWER_DOMAIN_STATE */
struct rpmi_pm_set_power_state_tx {
__le32 domain_id;
__le32 power_state;
};
struct rpmi_pm_set_power_state_rx {
__le32 status;
};
/* Service: GET_POWER_DOMAIN_STATE */
struct rpmi_pm_get_power_state_tx {
__le32 domain_id;
};
struct rpmi_pm_get_power_state_rx {
__le32 status;
__le32 power_state;
};
static int rpmi_pm_send_message(struct rpmi_ctx *mpxy_ctx,
struct rpmi_mbox_message *msg)
{
guard(mutex)(&mpxy_ctx->lock);
return rpmi_mbox_send_message(mpxy_ctx->chan, msg);
}
static int rpmi_power_state_get(struct rpmi_device_power_domain *mpxy_pm_domain,
u32 domain_id, u32 *state)
{
struct rpmi_pm_get_power_state_rx rx = { };
struct rpmi_pm_get_power_state_tx tx;
struct rpmi_mbox_message msg;
int ret;
tx.domain_id = cpu_to_le32(domain_id);
rpmi_mbox_init_send_with_response(&msg, RPMI_DP_SRV_GET_STATE,
&tx, sizeof(tx), &rx, sizeof(rx));
ret = rpmi_pm_send_message(mpxy_pm_domain->mpxy_ctx, &msg);
if (ret)
return ret;
if (rx.status)
return rpmi_to_linux_error(le32_to_cpu(rx.status));
if (msg.data.out_response_len < sizeof(rx))
return -EPROTO;
*state = le32_to_cpu(rx.power_state);
return 0;
}
static int rpmi_power_state_set(struct rpmi_device_power_domain *mpxy_pm_domain,
u32 domain_id, u32 state)
{
struct rpmi_pm_set_power_state_rx rx = { };
struct rpmi_pm_set_power_state_tx tx;
struct rpmi_mbox_message msg;
int ret;
tx.domain_id = cpu_to_le32(domain_id);
tx.power_state = cpu_to_le32(state);
rpmi_mbox_init_send_with_response(&msg, RPMI_DP_SRV_SET_STATE,
&tx, sizeof(tx), &rx, sizeof(rx));
ret = rpmi_pm_send_message(mpxy_pm_domain->mpxy_ctx, &msg);
if (ret)
return ret;
if (rx.status)
return rpmi_to_linux_error(le32_to_cpu(rx.status));
if (msg.data.out_response_len < sizeof(rx))
return -EPROTO;
return 0;
}
static int rpmi_pd_power(struct generic_pm_domain *domain, bool power_on)
{
struct rpmi_device_power_domain *mpxy_pm_domain;
u32 state, ret_state, domain_id;
int ret;
if (power_on)
state = RPMI_POWER_STATE_GENERIC_ON;
else
state = RPMI_POWER_STATE_GENERIC_OFF;
mpxy_pm_domain = to_rpmi_pd(domain);
domain_id = mpxy_pm_domain->id;
ret = rpmi_power_state_set(mpxy_pm_domain, domain_id, state);
if (ret)
return ret;
ret = rpmi_power_state_get(mpxy_pm_domain, domain_id, &ret_state);
if (ret)
return ret;
if (FIELD_GET(RPMI_POWER_STATE_VALUE_MASK, ret_state) !=
FIELD_GET(RPMI_POWER_STATE_VALUE_MASK, state))
return -EIO;
return ret;
}
static int rpmi_pd_power_on(struct generic_pm_domain *domain)
{
return rpmi_pd_power(domain, true);
}
static int rpmi_pd_power_off(struct generic_pm_domain *domain)
{
return rpmi_pd_power(domain, false);
}
static int rpmi_pm_get_num_domains(struct rpmi_ctx *mpxy_ctx, u32 *domain)
{
struct rpmi_pm_get_num_domain_rx rx = { };
struct rpmi_mbox_message msg;
int ret;
rpmi_mbox_init_send_with_response(&msg, RPMI_DP_SRV_GET_NUM_DOMAINS,
NULL, 0, &rx, sizeof(rx));
ret = rpmi_pm_send_message(mpxy_ctx, &msg);
if (ret)
return ret;
if (rx.status)
return rpmi_to_linux_error(le32_to_cpu(rx.status));
if (msg.data.out_response_len < sizeof(rx))
return -EPROTO;
*domain = le32_to_cpu(rx.num_domains);
return 0;
}
/* obtain the MPXY device power domain attributes */
static int rpmi_device_power_get_attrs(u32 domain_id,
struct rpmi_device_power_domain *mpxy_pm_domain)
{
struct rpmi_pm_get_domain_attrs_rx rx = { };
struct rpmi_pm_get_domain_attrs_tx tx;
struct rpmi_mbox_message msg;
int ret;
tx.domain_id = cpu_to_le32(domain_id);
rpmi_mbox_init_send_with_response(&msg, RPMI_DP_SRV_GET_ATTRS,
&tx, sizeof(tx), &rx, sizeof(rx));
ret = rpmi_pm_send_message(mpxy_pm_domain->mpxy_ctx, &msg);
if (ret)
return ret;
if (rx.status)
return rpmi_to_linux_error(le32_to_cpu(rx.status));
if (msg.data.out_response_len < sizeof(rx))
return -EPROTO;
mpxy_pm_domain->transition_latency = le32_to_cpu(rx.transition_latency);
strscpy(mpxy_pm_domain->name, rx.name, sizeof(mpxy_pm_domain->name));
return 0;
}
static int rpmi_pm_attr_setup(struct device *dev, struct rpmi_ctx *mpxy_ctx)
{
struct rpmi_mbox_message msg;
int ret;
/* Validate RPMI specification version */
rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SPEC_VERSION);
ret = rpmi_pm_send_message(mpxy_ctx, &msg);
if (ret) {
dev_dbg(dev, "Failed to get spec version\n");
return ret;
}
if (msg.attr.value < RPMI_MKVER(1, 0)) {
dev_dbg(dev,
"msg protocol version mismatch, expected 0x%x, found 0x%x\n",
RPMI_MKVER(1, 0), msg.attr.value);
return -EINVAL;
}
/* Validate device power service group ID */
rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SERVICEGROUP_ID);
ret = rpmi_pm_send_message(mpxy_ctx, &msg);
if (ret) {
dev_dbg(dev, "Failed to get service group ID\n");
return ret;
}
if (msg.attr.value != RPMI_SRVGRP_DEVICE_POWER) {
dev_dbg(dev,
"service group match failed, expected 0x%x, found 0x%x\n",
RPMI_SRVGRP_DEVICE_POWER, msg.attr.value);
return -EINVAL;
}
/* Validate device power service group version */
rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SERVICEGROUP_VERSION);
ret = rpmi_pm_send_message(mpxy_ctx, &msg);
if (ret) {
dev_dbg(dev, "Failed to get service group version\n");
return ret;
}
if (msg.attr.value < RPMI_MKVER(1, 0)) {
dev_dbg(dev,
"service group version failed, expected 0x%x, found 0x%x\n",
RPMI_MKVER(1, 0), msg.attr.value);
return -EINVAL;
}
return 0;
}
static void rpmi_pm_domain_mbox_chan_release(void *data)
{
mbox_free_channel((struct mbox_chan *)data);
}
static int rpmi_pm_domain_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct rpmi_device_power_domain *mpxy_pd;
struct genpd_onecell_data *mpxy_pd_data;
struct generic_pm_domain **domains;
struct device *dev = &pdev->dev;
struct rpmi_ctx *mpxy_ctx;
u32 num_domains = 0;
int ret;
u32 i;
mpxy_ctx = devm_kzalloc(&pdev->dev, sizeof(*mpxy_ctx), GFP_KERNEL);
if (!mpxy_ctx)
return -ENOMEM;
ret = devm_mutex_init(dev, &mpxy_ctx->lock);
if (ret)
return ret;
/* Setup mailbox client */
mpxy_ctx->client.dev = dev;
mpxy_ctx->client.rx_callback = NULL;
mpxy_ctx->client.tx_block = false;
mpxy_ctx->client.knows_txdone = true;
mpxy_ctx->client.tx_tout = 0;
/* Request mailbox channel */
mpxy_ctx->chan = mbox_request_channel(&mpxy_ctx->client, 0);
if (IS_ERR(mpxy_ctx->chan))
return PTR_ERR(mpxy_ctx->chan);
ret = devm_add_action_or_reset(dev, rpmi_pm_domain_mbox_chan_release,
mpxy_ctx->chan);
if (ret)
return dev_err_probe(dev, ret,
"failed to add rpmi mbox channel cleanup\n");
ret = rpmi_pm_attr_setup(dev, mpxy_ctx);
if (ret)
return dev_err_probe(dev, ret,
"failed to verify RPMI attribute\n");
/* Get number of device power domain */
ret = rpmi_pm_get_num_domains(mpxy_ctx, &num_domains);
if (ret)
return dev_err_probe(dev, ret,
"failed to get number of pm domains\n");
if (!num_domains)
return dev_err_probe(dev, -EINVAL, "No PM domains found!\n");
dev_info(&pdev->dev, "%u MPXY PM domains are found\n", num_domains);
mpxy_pd = devm_kcalloc(&pdev->dev, num_domains, sizeof(*mpxy_pd), GFP_KERNEL);
if (!mpxy_pd)
return -ENOMEM;
domains = devm_kcalloc(&pdev->dev, num_domains, sizeof(*domains), GFP_KERNEL);
if (!domains)
return -ENOMEM;
mpxy_pd_data = devm_kzalloc(&pdev->dev, sizeof(*mpxy_pd_data), GFP_KERNEL);
if (!mpxy_pd_data)
return -ENOMEM;
for (i = 0; i < num_domains; i++, mpxy_pd++) {
u32 state;
mpxy_pd->dev = &pdev->dev;
mpxy_pd->mpxy_ctx = mpxy_ctx;
mpxy_pd->id = i;
ret = rpmi_device_power_get_attrs(i, mpxy_pd);
if (ret) {
dev_warn(mpxy_pd->dev,
"power domain %u initialization failed\n",
mpxy_pd->id);
domains[i] = NULL;
continue;
}
ret = rpmi_power_state_get(mpxy_pd, i, &state);
if (ret) {
dev_warn(mpxy_pd->dev,
"failed to get state for power domain %u\n",
mpxy_pd->id);
domains[i] = NULL;
continue;
}
state = FIELD_GET(RPMI_POWER_STATE_VALUE_MASK, state);
if (state != RPMI_POWER_STATE_VALUE_OFF &&
state != RPMI_POWER_STATE_VALUE_ON) {
dev_warn(mpxy_pd->dev,
"unsupported state 0x%x for power domain %u\n",
state, mpxy_pd->id);
domains[i] = NULL;
continue;
}
mpxy_pd->genpd.name = mpxy_pd->name;
mpxy_pd->genpd.power_off = rpmi_pd_power_off;
mpxy_pd->genpd.power_on = rpmi_pd_power_on;
/*
* Domain names come from the PuC and are not guaranteed to be
* unique or non-empty, so let genpd derive unique device names
* from them rather than using them verbatim.
*/
mpxy_pd->genpd.flags = GENPD_FLAG_DEV_NAME_FW;
ret = pm_genpd_init(&mpxy_pd->genpd, NULL,
state == RPMI_POWER_STATE_VALUE_OFF);
if (ret) {
dev_warn(mpxy_pd->dev,
"failed to init power domain %u\n",
mpxy_pd->id);
domains[i] = NULL;
continue;
}
domains[i] = &mpxy_pd->genpd;
}
mpxy_pd_data->domains = domains;
mpxy_pd_data->num_domains = num_domains;
ret = of_genpd_add_provider_onecell(np, mpxy_pd_data);
if (ret) {
while (i--) {
if (domains[i])
pm_genpd_remove(domains[i]);
}
return dev_err_probe(dev, ret, "failed to add genpd provider\n");
}
return 0;
}
static const struct of_device_id rpmi_pm_domain_of_match[] = {
{ .compatible = "riscv,rpmi-device-power" },
{},
};
static struct platform_driver rpmi_pm_domain_platdrv = {
.driver = {
.name = "riscv-rpmi-device-power",
.of_match_table = rpmi_pm_domain_of_match,
.suppress_bind_attrs = true,
},
.probe = rpmi_pm_domain_probe,
};
builtin_platform_driver(rpmi_pm_domain_platdrv);
MODULE_AUTHOR("Joshua Yeong <joshua.yeong@starfivetech.com>");
MODULE_DESCRIPTION("Device Power Driver based on RPMI message protocol");
MODULE_LICENSE("GPL");

View File

@ -721,7 +721,7 @@ static int rockchip_pd_power_on(struct generic_pm_domain *domain)
ret = rockchip_pd_regulator_enable(pd);
if (ret)
return dev_err_probe(pd->pmu->dev, ret,
"Failed to enable supply: %d\n", ret);
"Failed to enable supply\n");
ret = rockchip_pd_power(pd, true);
if (ret)
@ -821,7 +821,7 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
return 0;
pd_info = &pmu->info->domain_info[id];
if (!pd_info) {
if (!pd_info->pwr_mask && !pd_info->req_mask) {
dev_err(pmu->dev, "%pOFn: undefined domain id %d\n",
node, id);
return -EINVAL;

View File

@ -17,6 +17,8 @@
#include <dt-bindings/arm/ux500_pm_domains.h>
#define NR_DOMAINS (DOMAIN_VAPE + 1)
static int pd_power_off(struct generic_pm_domain *domain)
{
/*

View File

@ -8,8 +8,21 @@
#define _DT_BINDINGS_ARM_UX500_PM_DOMAINS_H
#define DOMAIN_VAPE 0
#define DOMAIN_VARM 1
#define DOMAIN_VMODEM 2
#define DOMAIN_VPLL 3
#define DOMAIN_VSMPS1 4
#define DOMAIN_VSMPS2 5
#define DOMAIN_VSMPS3 6
#define DOMAIN_VRF1 7
#define DOMAIN_SVA_MMDSP 8
#define DOMAIN_SVA_PIPE 9
#define DOMAIN_SIA_MMDSP 10
#define DOMAIN_SIA_PIPE 11
#define DOMAIN_SGA 12
#define DOMAIN_B2R2_MCDE 13
#define DOMAIN_ESRAM_12 14
#define DOMAIN_ESRAM_34 15
/* Number of PM domains. */
#define NR_DOMAINS (DOMAIN_VAPE + 1)
#endif

View File

@ -32,6 +32,7 @@
#define RPMHPD_GBX 22
#define RPMHPD_NSP3 23
#define RPMHPD_GFX1 24
#define RPMHPD_NMXC 25
/* RPMh Power Domain performance levels */
#define RPMH_REGULATOR_LEVEL_RETENTION 16

View File

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/*
* Copyright (C) 2026 Renesas Electronics Corp.
*/
#ifndef __DT_BINDINGS_POWER_RENESAS_R8A774A3_SYSC_H__
#define __DT_BINDINGS_POWER_RENESAS_R8A774A3_SYSC_H__
/*
* These power domain indices match the numbers of the interrupt bits
* representing the power areas in the various Interrupt Registers
* (e.g. SYSCISR, Interrupt Status Register)
*/
#define R8A774A3_PD_CA57_CPU0 0
#define R8A774A3_PD_CA57_CPU1 1
#define R8A774A3_PD_CA53_CPU0 5
#define R8A774A3_PD_CA53_CPU1 6
#define R8A774A3_PD_CA53_CPU2 7
#define R8A774A3_PD_CA53_CPU3 8
#define R8A774A3_PD_CA57_SCU 12
#define R8A774A3_PD_A3VC 14
#define R8A774A3_PD_3DG_A 17
#define R8A774A3_PD_3DG_B 18
#define R8A774A3_PD_CA53_SCU 21
#define R8A774A3_PD_A2VC1 26
/* Always-on power area */
#define R8A774A3_PD_ALWAYS_ON 32
#endif /* __DT_BINDINGS_POWER_RENESAS_R8A774A3_SYSC_H__ */

View File

@ -93,6 +93,7 @@ static inline int rpmi_to_linux_error(int rpmi_error)
/* RPMI service group IDs */
#define RPMI_SRVGRP_SYSTEM_MSI 0x00002
#define RPMI_SRVGRP_CLOCK 0x00008
#define RPMI_SRVGRP_DEVICE_POWER 0x00009
/* RPMI clock service IDs */
enum rpmi_clock_service_id {
@ -119,6 +120,16 @@ enum rpmi_sysmsi_service_id {
RPMI_SYSMSI_SRV_ID_MAX_COUNT
};
/* RPMI device power service IDs */
enum rpmi_device_power_service_id {
RPMI_DP_SRV_ENABLE_NOTIFICATION = 0x01,
RPMI_DP_SRV_GET_NUM_DOMAINS = 0x02,
RPMI_DP_SRV_GET_ATTRS = 0x03,
RPMI_DP_SRV_SET_STATE = 0x04,
RPMI_DP_SRV_GET_STATE = 0x05,
RPMI_DP_SRV_ID_MAX_COUNT,
};
/* RPMI Linux mailbox attribute IDs */
enum rpmi_mbox_attribute_id {
RPMI_MBOX_ATTR_SPEC_VERSION,

View File

@ -121,6 +121,14 @@ struct dev_pm_domain_list {
* powered-off until the ->sync_state() callback is
* invoked. This flag informs genpd to allow a
* power-off without waiting for ->sync_state().
*
* GENPD_FLAG_POWER_UNKNOWN: Use this flag to inform genpd that its initial
* status for the PM domain is set to powered off,
* which may not correctly reflect the state of the
* HW, as it's unknown. If the PM domain becomes
* powered on during boot, genpd will prevent it
* from being powered off until the ->sync_state
* callback is invoked for it.
*/
#define GENPD_FLAG_PM_CLK (1U << 0)
#define GENPD_FLAG_IRQ_SAFE (1U << 1)
@ -133,6 +141,7 @@ struct dev_pm_domain_list {
#define GENPD_FLAG_DEV_NAME_FW (1U << 8)
#define GENPD_FLAG_NO_SYNC_STATE (1U << 9)
#define GENPD_FLAG_NO_STAY_ON (1U << 10)
#define GENPD_FLAG_POWER_UNKNOWN (1U << 11)
enum gpd_status {
GENPD_STATE_ON = 0, /* PM domain is on */

View File

@ -37,6 +37,8 @@ enum pm_qos_flags_status {
#define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1)
#define PM_QOS_FLAG_NO_POWER_OFF (1 << 0)
/* latency value applies to system-wide suspend/s2idle */
#define PM_QOS_FLAG_LATENCY_SYS (2 << 0)
enum pm_qos_type {
PM_QOS_UNITIALIZED,
@ -217,6 +219,12 @@ static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev)
PM_QOS_RESUME_LATENCY_NO_CONSTRAINT :
pm_qos_read_value(&dev->power.qos->resume_latency);
}
static inline s32 dev_pm_qos_raw_flags(struct device *dev)
{
return IS_ERR_OR_NULL(dev->power.qos) ?
0 : READ_ONCE(dev->power.qos->flags.effective_flags);
}
#else
static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev,
s32 mask)
@ -298,6 +306,7 @@ static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev)
{
return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
}
static inline s32 dev_pm_qos_raw_flags(struct device *dev) { return 0; }
#endif
static inline int freq_qos_request_active(struct freq_qos_request *req)

View File

@ -159,7 +159,7 @@ static void pm_qos_flags_remove_req(struct pm_qos_flags *pqf,
list_for_each_entry(req, &pqf->list, node)
val |= req->flags;
pqf->effective_flags = val;
WRITE_ONCE(pqf->effective_flags, val);
}
/**
@ -193,7 +193,7 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf,
req->flags = val;
INIT_LIST_HEAD(&req->node);
list_add_tail(&req->node, &pqf->list);
pqf->effective_flags |= val;
WRITE_ONCE(pqf->effective_flags, pqf->effective_flags | val);
break;
default:
/* no action */