mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
0e557fc68a
|
|
@ -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>
|
||||
};
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
...
|
||||
|
|
@ -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>;
|
||||
};
|
||||
...
|
||||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -479,4 +479,4 @@ static int __init psci_idle_init(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(psci_idle_init);
|
||||
subsys_initcall(psci_idle_init);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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
18
drivers/pmdomain/core.h
Normal 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__ */
|
||||
|
|
@ -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 +
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
45
drivers/pmdomain/renesas/r8a774a3-sysc.c
Normal file
45
drivers/pmdomain/renesas/r8a774a3-sysc.c
Normal 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),
|
||||
};
|
||||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
15
drivers/pmdomain/riscv/Kconfig
Normal file
15
drivers/pmdomain/riscv/Kconfig
Normal 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.
|
||||
3
drivers/pmdomain/riscv/Makefile
Normal file
3
drivers/pmdomain/riscv/Makefile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_RISCV_RPMI_DEVICE_POWER) += riscv-rpmi-device-power.o
|
||||
485
drivers/pmdomain/riscv/riscv-rpmi-device-power.c
Normal file
485
drivers/pmdomain/riscv/riscv-rpmi-device-power.c
Normal 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");
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
30
include/dt-bindings/power/renesas,r8a774a3-sysc.h
Normal file
30
include/dt-bindings/power/renesas,r8a774a3-sysc.h
Normal 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__ */
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user