pmdomain core:

- Add support for naming idlestates through DT
 
 pmdomain providers:
  - arm: Explicitly request the current state at init for the SCMI PM domain
  - mediatek: Add Airoha CPU PM Domain support for CPU frequency scaling
  - ti: Add per-device latency constraint management to the ti_sci PM domain
 
 cpuidle-psci:
  - Enable system-wakeup through GENPD_FLAG_ACTIVE_WAKEUP
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmeSTf0XHHVsZi5oYW5z
 c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCmeThAAh7odiUJo0+nGiy4mdbMh2mik
 aF3hN419zZhVVNlmNBSVQhRIW2GFxMGX7FdaT+8yXmvhvE0cMbBiDtEyCySLmUOd
 43hXkboJ4T+7pFMsSJ7BSn3fzZNJb0135JZVQZY6UhrIFUsDOH4oSI350qllln61
 V294ABMNFHw6q4yD1m94Cqw7xraCMvZ3OxpK5p215uhDutHwA0BdOKUq/hcCojSi
 5hy1y3SIghhJS0Ug7NxoP+6I/tQg5T5LZV23YKRz5XZPSnvXoldjKEiKzNqBqmZu
 4E56M9MNDVhWz5CZt3aed/XvLW+X/uTgz63g81kDKnhV/HQXAp5iZrVsmnF2oaXl
 1Ls48fo25Vk6bVWZW3Z7tkgGfknjSYZhlSmFrbRxazsZfpvmwmXHJznnwQITyRia
 Ju8YnFA0kcbA18nNE9Jk8GI5pNt/GZyqZ5Z0t46Re6dYxmDxGLAlRL5/XoVbof+Y
 HIJzONTvJKap7qilYkmdqTWJF7CVMAWdsUwVb7fzHjoXegPNzZIPv8jEqbZNOWqh
 K4fYrx1OSDGLDwuQlTdK8FOgUoHwwiDOJb0G1UA2c6sUIMBCAOKyG2HpWIBB9CMK
 B5GNMwjV/9XQ7+xmA1hyysR2r6nipXaQPhREo/rLZa1GYL/jYy50jGJENo4M7gsM
 yBK5CwqVvWs1TEhYFog=
 =gGf+
 -----END PGP SIGNATURE-----

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

Pull pmdomain updates from Ulf Hansson:
 "pmdomain core:
   - Add support for naming idlestates through DT

  pmdomain providers:
   - arm: Explicitly request the current state at init for the SCMI PM
     domain
   - mediatek: Add Airoha CPU PM Domain support for CPU frequency
     scaling
   - ti: Add per-device latency constraint management to the ti_sci PM
     domain

  cpuidle-psci:
   - Enable system-wakeup through GENPD_FLAG_ACTIVE_WAKEUP"

* tag 'pmdomain-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm:
  pmdomain: airoha: Fix compilation error with Clang-20 and Thumb2 mode
  pmdomain: arm: scmi_pm_domain: Send an explicit request to set the current state
  pmdomain: airoha: Add Airoha CPU PM Domain support
  pmdomain: ti_sci: handle wake IRQs for IO daisy chain wakeups
  pmdomain: ti_sci: add wakeup constraint management
  pmdomain: ti_sci: add per-device latency constraint management
  pmdomain: imx-gpcv2: Suppress bind attrs
  pmdomain: imx8m[p]-blk-ctrl: Suppress bind attrs
  pmdomain: core: Support naming idle states
  dt-bindings: power: domain-idle-state: Allow idle-state-name
  cpuidle: psci: Activate GENPD_FLAG_ACTIVE_WAKEUP with OSI
This commit is contained in:
Linus Torvalds 2025-01-24 07:43:35 -08:00
commit 68732c0bf9
12 changed files with 276 additions and 3 deletions

View File

@ -54,6 +54,11 @@ patternProperties:
(i.e. idle states node with entry-method property is set to "psci")
must specify this property.
idle-state-name:
$ref: /schemas/types.yaml#/definitions/string
description:
A string used as a descriptive name for the idle state.
required:
- compatible
- entry-latency-us

View File

@ -72,6 +72,7 @@ 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;
} else {

View File

@ -96,6 +96,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
continue;
}
/*
* Register the explicit power on request to the firmware so
* that it is tracked as used by OSPM agent and not
* accidentally turned off with OSPM's knowledge
*/
if (state == SCMI_POWER_STATE_GENERIC_ON)
power_ops->state_set(ph, i, state);
scmi_pd->domain = i;
scmi_pd->ph = ph;
scmi_pd->name = power_ops->name_get(ph, i);

View File

@ -3180,6 +3180,8 @@ 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);
genpd_state->power_on_latency_ns = 1000LL * exit_latency;
genpd_state->power_off_latency_ns = 1000LL * entry_latency;
genpd_state->fwnode = &state_node->fwnode;
@ -3458,7 +3460,10 @@ static int idle_states_show(struct seq_file *s, void *data)
seq_puts(s, "State Time Spent(ms) Usage Rejected\n");
for (i = 0; i < genpd->state_count; i++) {
idle_time += genpd->states[i].idle_time;
struct genpd_power_state *state = &genpd->states[i];
char state_name[15];
idle_time += state->idle_time;
if (genpd->status == GENPD_STATE_OFF && genpd->state_idx == i) {
now = ktime_get_mono_fast_ns();
@ -3468,9 +3473,13 @@ static int idle_states_show(struct seq_file *s, void *data)
}
}
if (!state->name)
snprintf(state_name, ARRAY_SIZE(state_name), "S%-13d", i);
do_div(idle_time, NSEC_PER_MSEC);
seq_printf(s, "S%-13i %-14llu %-14llu %llu\n", i, idle_time,
genpd->states[i].usage, genpd->states[i].rejected);
seq_printf(s, "%-14s %-14llu %-14llu %llu\n",
state->name ?: state_name, idle_time,
state->usage, state->rejected);
}
genpd_unlock(genpd);

View File

@ -1437,6 +1437,7 @@ static struct platform_driver imx_pgc_domain_driver = {
.driver = {
.name = "imx-pgc",
.pm = &imx_pgc_domain_pm_ops,
.suppress_bind_attrs = true,
},
.probe = imx_pgc_domain_probe,
.remove = imx_pgc_domain_remove,
@ -1549,6 +1550,7 @@ static struct platform_driver imx_gpc_driver = {
.driver = {
.name = "imx-gpcv2",
.of_match_table = imx_gpcv2_dt_ids,
.suppress_bind_attrs = true,
},
.probe = imx_gpcv2_probe,
};

View File

@ -894,6 +894,7 @@ static struct platform_driver imx8m_blk_ctrl_driver = {
.name = "imx8m-blk-ctrl",
.pm = &imx8m_blk_ctrl_pm_ops,
.of_match_table = imx8m_blk_ctrl_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(imx8m_blk_ctrl_driver);

View File

@ -862,6 +862,7 @@ static struct platform_driver imx8mp_blk_ctrl_driver = {
.name = "imx8mp-blk-ctrl",
.pm = &imx8mp_blk_ctrl_pm_ops,
.of_match_table = imx8mp_blk_ctrl_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(imx8mp_blk_ctrl_driver);

View File

@ -26,4 +26,16 @@ config MTK_SCPSYS_PM_DOMAINS
Control Processor System (SCPSYS) has several power management related
tasks in the system.
config AIROHA_CPU_PM_DOMAIN
tristate "Airoha CPU power domain"
default ARCH_AIROHA
depends on HAVE_ARM_SMCCC
depends on PM
select PM_GENERIC_DOMAINS
help
Say y here to enable CPU power domain support for Airoha SoC.
CPU frequency and power is controlled by ATF with SMC command to
set performance states.
endmenu

View File

@ -1,3 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o
obj-$(CONFIG_AIROHA_CPU_PM_DOMAIN) += airoha-cpu-pmdomain.o
ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy)
# The use of R7 in the SMCCC conflicts with the compiler's use of R7 as a frame
# pointer in Thumb2 mode, which is forcibly enabled by Clang when profiling
# hooks are inserted via the -pg switch.
CFLAGS_REMOVE_airoha-cpu-pmdomain.o += $(CC_FLAGS_FTRACE)
endif

View File

@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#define AIROHA_SIP_AVS_HANDLE 0x82000301
#define AIROHA_AVS_OP_BASE 0xddddddd0
#define AIROHA_AVS_OP_MASK GENMASK(1, 0)
#define AIROHA_AVS_OP_FREQ_DYN_ADJ (AIROHA_AVS_OP_BASE | \
FIELD_PREP(AIROHA_AVS_OP_MASK, 0x1))
#define AIROHA_AVS_OP_GET_FREQ (AIROHA_AVS_OP_BASE | \
FIELD_PREP(AIROHA_AVS_OP_MASK, 0x2))
struct airoha_cpu_pmdomain_priv {
struct clk_hw hw;
struct generic_pm_domain pd;
};
static long airoha_cpu_pmdomain_clk_round(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
return rate;
}
static unsigned long airoha_cpu_pmdomain_clk_get(struct clk_hw *hw,
unsigned long parent_rate)
{
struct arm_smccc_res res;
arm_smccc_1_1_invoke(AIROHA_SIP_AVS_HANDLE, AIROHA_AVS_OP_GET_FREQ,
0, 0, 0, 0, 0, 0, &res);
/* SMCCC returns freq in MHz */
return (int)(res.a0 * 1000 * 1000);
}
/* Airoha CPU clk SMCC is always enabled */
static int airoha_cpu_pmdomain_clk_is_enabled(struct clk_hw *hw)
{
return true;
}
static const struct clk_ops airoha_cpu_pmdomain_clk_ops = {
.recalc_rate = airoha_cpu_pmdomain_clk_get,
.is_enabled = airoha_cpu_pmdomain_clk_is_enabled,
.round_rate = airoha_cpu_pmdomain_clk_round,
};
static int airoha_cpu_pmdomain_set_performance_state(struct generic_pm_domain *domain,
unsigned int state)
{
struct arm_smccc_res res;
arm_smccc_1_1_invoke(AIROHA_SIP_AVS_HANDLE, AIROHA_AVS_OP_FREQ_DYN_ADJ,
0, state, 0, 0, 0, 0, &res);
/* SMC signal correct apply by unsetting BIT 0 */
return res.a0 & BIT(0) ? -EINVAL : 0;
}
static int airoha_cpu_pmdomain_probe(struct platform_device *pdev)
{
struct airoha_cpu_pmdomain_priv *priv;
struct device *dev = &pdev->dev;
const struct clk_init_data init = {
.name = "cpu",
.ops = &airoha_cpu_pmdomain_clk_ops,
/* Clock with no set_rate, can't cache */
.flags = CLK_GET_RATE_NOCACHE,
};
struct generic_pm_domain *pd;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Init and register a get-only clk for Cpufreq */
priv->hw.init = &init;
ret = devm_clk_hw_register(dev, &priv->hw);
if (ret)
return ret;
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
&priv->hw);
if (ret)
return ret;
/* Init and register a PD for CPU */
pd = &priv->pd;
pd->name = "cpu_pd";
pd->flags = GENPD_FLAG_ALWAYS_ON;
pd->set_performance_state = airoha_cpu_pmdomain_set_performance_state;
ret = pm_genpd_init(pd, NULL, false);
if (ret)
return ret;
ret = of_genpd_add_provider_simple(dev->of_node, pd);
if (ret)
goto err_add_provider;
platform_set_drvdata(pdev, priv);
return 0;
err_add_provider:
pm_genpd_remove(pd);
return ret;
}
static void airoha_cpu_pmdomain_remove(struct platform_device *pdev)
{
struct airoha_cpu_pmdomain_priv *priv = platform_get_drvdata(pdev);
of_genpd_del_provider(pdev->dev.of_node);
pm_genpd_remove(&priv->pd);
}
static const struct of_device_id airoha_cpu_pmdomain_of_match[] = {
{ .compatible = "airoha,en7581-cpufreq" },
{ },
};
MODULE_DEVICE_TABLE(of, airoha_cpu_pmdomain_of_match);
static struct platform_driver airoha_cpu_pmdomain_driver = {
.probe = airoha_cpu_pmdomain_probe,
.remove = airoha_cpu_pmdomain_remove,
.driver = {
.name = "airoha-cpu-pmdomain",
.of_match_table = airoha_cpu_pmdomain_of_match,
},
};
module_platform_driver(airoha_cpu_pmdomain_driver);
MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
MODULE_DESCRIPTION("CPU PM domain driver for Airoha SoCs");
MODULE_LICENSE("GPL");

View File

@ -12,6 +12,8 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/soc/ti/ti_sci_protocol.h>
#include <dt-bindings/soc/ti,sci_pm_domain.h>
@ -51,6 +53,56 @@ struct ti_sci_pm_domain {
#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
static inline bool ti_sci_pd_is_valid_constraint(s32 val)
{
return val != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
}
#ifdef CONFIG_PM_SLEEP
static void ti_sci_pd_set_lat_constraint(struct device *dev, s32 val)
{
struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(genpd);
const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
u16 val_ms;
int ret;
/* PM QoS latency unit is usecs, TI SCI uses msecs */
val_ms = val / USEC_PER_MSEC;
ret = ti_sci->ops.pm_ops.set_latency_constraint(ti_sci, val_ms, TISCI_MSG_CONSTRAINT_SET);
if (ret)
dev_err(dev, "ti_sci_pd: set latency constraint failed: ret=%d\n",
ret);
else
dev_dbg(dev, "ti_sci_pd: ID:%d set latency constraint %d\n",
pd->idx, val);
}
#endif
static inline void ti_sci_pd_set_wkup_constraint(struct device *dev)
{
struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(genpd);
const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
int ret;
if (device_may_wakeup(dev)) {
/*
* If device can wakeup using IO daisy chain wakeups,
* we do not want to set a constraint.
*/
if (dev->power.wakeirq) {
dev_dbg(dev, "%s: has wake IRQ, not setting constraints\n", __func__);
return;
}
ret = ti_sci->ops.pm_ops.set_device_constraint(ti_sci, pd->idx,
TISCI_MSG_CONSTRAINT_SET);
if (!ret)
dev_dbg(dev, "ti_sci_pd: ID:%d set device constraint.\n", pd->idx);
}
}
/*
* ti_sci_pd_power_off(): genpd power down hook
* @domain: pointer to the powerdomain to power off
@ -79,6 +131,28 @@ static int ti_sci_pd_power_on(struct generic_pm_domain *domain)
return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx);
}
#ifdef CONFIG_PM_SLEEP
static int ti_sci_pd_suspend(struct device *dev)
{
int ret;
s32 val;
ret = pm_generic_suspend(dev);
if (ret)
return ret;
val = dev_pm_qos_read_value(dev, DEV_PM_QOS_RESUME_LATENCY);
if (ti_sci_pd_is_valid_constraint(val))
ti_sci_pd_set_lat_constraint(dev, val);
ti_sci_pd_set_wkup_constraint(dev);
return 0;
}
#else
#define ti_sci_pd_suspend NULL
#endif
/*
* ti_sci_pd_xlate(): translation service for TI SCI genpds
* @genpdspec: DT identification data for the genpd
@ -182,6 +256,13 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev)
pd->pd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
pd->idx = args.args[0];
pd->parent = pd_provider;
/*
* If SCI constraint functions are present, then firmware
* supports the constraints API.
*/
if (pd_provider->ti_sci->ops.pm_ops.set_device_constraint &&
pd_provider->ti_sci->ops.pm_ops.set_latency_constraint)
pd->pd.domain.ops.suspend = ti_sci_pd_suspend;
pm_genpd_init(&pd->pd, NULL, true);

View File

@ -147,6 +147,7 @@ struct genpd_governor_data {
};
struct genpd_power_state {
const char *name;
s64 power_off_latency_ns;
s64 power_on_latency_ns;
s64 residency_ns;