pmdomain core:

- Leave powered-on genpds on until ->sync_state() or late_initcall_sync
  - Export a common ->sync_state() helper for genpd providers
  - Add generic ->sync_state() support
  - Add a bus/driver for genpd provider-devices
  - Introduce dev_pm_genpd_is_on() for consumers
 
 pmdomain providers:
  - cpuidle-psci: Drop redundant ->sync_state() support
  - cpuidle-riscv-sbi: Drop redundant ->sync_state() support
  - imx: Set ISI panic write for imx8m-blk-ctrl
  - qcom: Add support for Glymur and Milos RPMh power-domains
  - qcom: Use of_genpd_sync_state() for power-domains
  - rockchip: Add support for the RK3528 variant
  - samsung: Fix splash-screen handover by enforcing a ->sync_state()
  - sunxi: Add support for Allwinner A523's PCK600 power-controller
  - tegra: Opt-out from genpd's common ->sync_state() support for pmc
  - thead: Instantiate a GPU power sequencer via the auxiliary bus
  - renesas: Move init to postcore_initcalls
  - xilinx: Move ->sync_state() support to firmware driver
  - xilinx: Use of_genpd_sync_state() for power-domains
 
 pmdomain consumers:
  - remoteproc: imx_rproc: Fixup the detect/attach procedure for pre-booted cores
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmiIqskXHHVsZi5oYW5z
 c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjClOLBAAxsd5QP7QyeCePR4hADGePY4d
 Ab4fX01EVsu+g5cjg7L1V8gZMbNC/O+bxVhF5s1CiDbXbEcDliuW9yKCTFq4Yqx+
 7PtR+U538FADJGbbETzp8AFbcD8hmd/Wt/o7JyqC2wmmRwlgDEKPTR+M7FirrU3J
 fLsE+wyJSxn8ywJ1XwQOxBOZUtq11a8UueMXb8L1DhilyjT88zqCNiMvkcLtBlh+
 tRT5TWBEGr7qPCDAW6w3RL/8Y22g8xIjJxcDI8LycI/ed7qB5gtIs3ORiTl2w4qE
 k78wp2Ltv1Q2AmrYiLoaciFMTXckeBycVhobf0c4zVn2HGS15Fip0YNhPQmOz3aI
 JY+h5aUTMTUICVTG1D9uTW9gEbzqJOnqwU75R6zyV6xUk9pS4pGHaxaqKasNdDPy
 zIVgaURMZZxq37x7TNmnQZSZ3pAisFTm8Rv4oDTOJshblhuzG0TsXTlgMuy+EONB
 qh0obkHZFETrucGwaGBFVenzpnwZiEZ3EAKMGDcjBAO54waWdo+SmmJCc0KCQLih
 UheFi4v0IAUm8KqzwM4CKGg5NhGo2E1FcG8YbX6t2phycQLfVHDSw3rXHJRfXOzu
 s6njs7DhPKvUdlQxm1Fjq+eeMw2wsUFj8x1+usQYiD3UfuC26GU3cPsrU99+pcak
 eTcWfSnpDiXpVE3pEOk=
 =9SL+
 -----END PGP SIGNATURE-----

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

Pull pmdomain updates from Ulf Hansson:
 "pmdomain core:
   - Leave powered-on genpds on until ->sync_state() or late_initcall_sync
   - Export a common ->sync_state() helper for genpd providers
   - Add generic ->sync_state() support
   - Add a bus/driver for genpd provider-devices
   - Introduce dev_pm_genpd_is_on() for consumers

  pmdomain providers:
   - cpuidle-psci: Drop redundant ->sync_state() support
   - cpuidle-riscv-sbi: Drop redundant ->sync_state() support
   - imx: Set ISI panic write for imx8m-blk-ctrl
   - qcom: Add support for Glymur and Milos RPMh power-domains
   - qcom: Use of_genpd_sync_state() for power-domains
   - rockchip: Add support for the RK3528 variant
   - samsung: Fix splash-screen handover by enforcing a ->sync_state()
   - sunxi: Add support for Allwinner A523's PCK600 power-controller
   - tegra: Opt-out from genpd's common ->sync_state() support for pmc
   - thead: Instantiate a GPU power sequencer via the auxiliary bus
   - renesas: Move init to postcore_initcalls
   - xilinx: Move ->sync_state() support to firmware driver
   - xilinx: Use of_genpd_sync_state() for power-domains

  pmdomain consumers:
   - remoteproc: imx_rproc: Fixup the detect/attach procedure for
     pre-booted cores"

* tag 'pmdomain-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm: (44 commits)
  pmdomain: qcom: rpmhpd: Add Glymur RPMh Power Domains
  dt-bindings: power: rpmpd: Add Glymur power domains
  remoteproc: imx_rproc: detect and attach to pre-booted remote cores
  remoteproc: imx_rproc: skip clock enable when M-core is managed by the SCU
  pmdomain: core: introduce dev_pm_genpd_is_on()
  pmdomain: ti: Select PM_GENERIC_DOMAINS
  pmdomain: sunxi: sun20i-ppu: change to tristate and enable for ARCH_SUNXI
  pmdomain: sunxi: add driver for Allwinner A523's PCK-600 power controller
  pmdomain: sunxi: sun20i-ppu: add A523 support
  pmdomain: samsung: Fix splash-screen handover by enforcing a sync_state
  cpuidle: riscv-sbi: Drop redundant sync_state support
  cpuidle: psci: Drop redundant sync_state support
  pmdomain: core: Leave powered-on genpds on until sync_state
  pmdomain: core: Leave powered-on genpds on until late_initcall_sync
  pmdomain: core: Default to use of_genpd_sync_state() for genpd providers
  driver core: Add dev_set_drv_sync_state()
  pmdomain: core: Add common ->sync_state() support for genpd providers
  driver core: Export get_dev_from_fwnode()
  firmware: xilinx: Use of_genpd_sync_state()
  firmware: xilinx: Don't share zynqmp_pm_init_finalize()
  ...
pull/1309/head
Linus Torvalds 2025-07-29 11:42:31 -07:00
commit fc8f5028eb
31 changed files with 882 additions and 197 deletions

View File

@ -17,6 +17,7 @@ properties:
compatible:
oneOf:
- enum:
- qcom,glymur-rpmhpd
- qcom,mdm9607-rpmpd
- qcom,milos-rpmhpd
- qcom,msm8226-rpmpd

View File

@ -1881,8 +1881,6 @@ static void fw_devlink_unblock_consumers(struct device *dev)
device_links_write_unlock();
}
#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev)
static bool fwnode_init_without_drv(struct fwnode_handle *fwnode)
{
struct device *dev;
@ -5281,6 +5279,12 @@ void device_set_node(struct device *dev, struct fwnode_handle *fwnode)
}
EXPORT_SYMBOL_GPL(device_set_node);
struct device *get_dev_from_fwnode(struct fwnode_handle *fwnode)
{
return get_device((fwnode)->dev);
}
EXPORT_SYMBOL_GPL(get_dev_from_fwnode);
int device_match_name(struct device *dev, const void *name)
{
return sysfs_streq(dev_name(dev), name);

View File

@ -28,7 +28,6 @@ struct psci_pd_provider {
};
static LIST_HEAD(psci_pd_providers);
static bool psci_pd_allow_domain_state;
static int psci_pd_power_off(struct generic_pm_domain *pd)
{
@ -38,9 +37,6 @@ static int psci_pd_power_off(struct generic_pm_domain *pd)
if (!state->data)
return 0;
if (!psci_pd_allow_domain_state)
return -EBUSY;
/* OSI mode is enabled, set the corresponding domain state. */
pd_state = state->data;
psci_set_domain_state(pd, pd->state_idx, *pd_state);
@ -126,15 +122,6 @@ static void psci_pd_remove(void)
}
}
static void psci_cpuidle_domain_sync_state(struct device *dev)
{
/*
* All devices have now been attached/probed to the PM domain topology,
* hence it's fine to allow domain states to be picked.
*/
psci_pd_allow_domain_state = true;
}
static const struct of_device_id psci_of_match[] = {
{ .compatible = "arm,psci-1.0" },
{}
@ -195,7 +182,6 @@ static struct platform_driver psci_cpuidle_domain_driver = {
.driver = {
.name = "psci-cpuidle-domain",
.of_match_table = psci_of_match,
.sync_state = psci_cpuidle_domain_sync_state,
},
};

View File

@ -44,7 +44,6 @@ static DEFINE_PER_CPU_READ_MOSTLY(struct sbi_cpuidle_data, sbi_cpuidle_data);
static DEFINE_PER_CPU(struct sbi_domain_state, domain_state);
static bool sbi_cpuidle_use_osi;
static bool sbi_cpuidle_use_cpuhp;
static bool sbi_cpuidle_pd_allow_domain_state;
static inline void sbi_set_domain_state(u32 state)
{
@ -345,15 +344,6 @@ deinit:
return ret;
}
static void sbi_cpuidle_domain_sync_state(struct device *dev)
{
/*
* All devices have now been attached/probed to the PM domain
* topology, hence it's fine to allow domain states to be picked.
*/
sbi_cpuidle_pd_allow_domain_state = true;
}
#ifdef CONFIG_DT_IDLE_GENPD
static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd)
@ -364,9 +354,6 @@ static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd)
if (!state->data)
return 0;
if (!sbi_cpuidle_pd_allow_domain_state)
return -EBUSY;
/* OSI mode is enabled, set the corresponding domain state. */
pd_state = state->data;
sbi_set_domain_state(*pd_state);
@ -564,7 +551,6 @@ static struct platform_driver sbi_cpuidle_driver = {
.probe = sbi_cpuidle_probe,
.driver = {
.name = "sbi-cpuidle",
.sync_state = sbi_cpuidle_domain_sync_state,
},
};

View File

@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/hashtable.h>
@ -1299,11 +1300,10 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write);
* This API function is to be used for notify the power management controller
* about the completed power management initialization.
*/
int zynqmp_pm_init_finalize(void)
static int zynqmp_pm_init_finalize(void)
{
return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, NULL, 0);
}
EXPORT_SYMBOL_GPL(zynqmp_pm_init_finalize);
/**
* zynqmp_pm_set_suspend_mode() - Set system suspend mode
@ -2100,6 +2100,19 @@ static void zynqmp_firmware_remove(struct platform_device *pdev)
platform_device_unregister(em_dev);
}
static void zynqmp_firmware_sync_state(struct device *dev)
{
struct device_node *np = dev->of_node;
if (!of_device_is_compatible(np, "xlnx,zynqmp-firmware"))
return;
of_genpd_sync_state(np);
if (zynqmp_pm_init_finalize())
dev_warn(dev, "failed to release power management to firmware\n");
}
static const struct of_device_id zynqmp_firmware_of_match[] = {
{.compatible = "xlnx,zynqmp-firmware"},
{.compatible = "xlnx,versal-firmware"},
@ -2112,6 +2125,7 @@ static struct platform_driver zynqmp_firmware_driver = {
.name = "zynqmp_firmware",
.of_match_table = zynqmp_firmware_of_match,
.dev_groups = zynqmp_firmware_groups,
.sync_state = zynqmp_firmware_sync_state,
},
.probe = zynqmp_firmware_probe,
.remove = zynqmp_firmware_remove,

View File

@ -342,32 +342,32 @@ static int meson_secure_pwrc_probe(struct platform_device *pdev)
return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate);
}
static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = {
static const struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = {
.domains = a1_pwrc_domains,
.count = ARRAY_SIZE(a1_pwrc_domains),
};
static struct meson_secure_pwrc_domain_data amlogic_secure_a4_pwrc_data = {
static const struct meson_secure_pwrc_domain_data amlogic_secure_a4_pwrc_data = {
.domains = a4_pwrc_domains,
.count = ARRAY_SIZE(a4_pwrc_domains),
};
static struct meson_secure_pwrc_domain_data amlogic_secure_a5_pwrc_data = {
static const struct meson_secure_pwrc_domain_data amlogic_secure_a5_pwrc_data = {
.domains = a5_pwrc_domains,
.count = ARRAY_SIZE(a5_pwrc_domains),
};
static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = {
static const struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = {
.domains = c3_pwrc_domains,
.count = ARRAY_SIZE(c3_pwrc_domains),
};
static struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = {
static const struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = {
.domains = s4_pwrc_domains,
.count = ARRAY_SIZE(s4_pwrc_domains),
};
static struct meson_secure_pwrc_domain_data amlogic_secure_t7_pwrc_data = {
static const struct meson_secure_pwrc_domain_data amlogic_secure_t7_pwrc_data = {
.domains = t7_pwrc_domains,
.count = ARRAY_SIZE(t7_pwrc_domains),
};

View File

@ -9,7 +9,6 @@ config APPLE_PMGR_PWRSTATE
select MFD_SYSCON
select PM_GENERIC_DOMAINS
select RESET_CONTROLLER
default ARCH_APPLE
help
The PMGR block in Apple SoCs provides high-level power state
controls for SoC devices. This driver manages them through the

View File

@ -22,27 +22,21 @@ struct scmi_pm_domain {
#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
static int scmi_pd_power(struct generic_pm_domain *domain, u32 state)
{
u32 state;
struct scmi_pm_domain *pd = to_scmi_pd(domain);
if (power_on)
state = SCMI_POWER_STATE_GENERIC_ON;
else
state = SCMI_POWER_STATE_GENERIC_OFF;
return power_ops->state_set(pd->ph, pd->domain, state);
}
static int scmi_pd_power_on(struct generic_pm_domain *domain)
{
return scmi_pd_power(domain, true);
return scmi_pd_power(domain, SCMI_POWER_STATE_GENERIC_ON);
}
static int scmi_pd_power_off(struct generic_pm_domain *domain)
{
return scmi_pd_power(domain, false);
return scmi_pd_power(domain, SCMI_POWER_STATE_GENERIC_OFF);
}
static int scmi_pm_domain_probe(struct scmi_device *sdev)

View File

@ -27,6 +27,16 @@
/* Provides a unique ID for each genpd device */
static DEFINE_IDA(genpd_ida);
/* The bus for genpd_providers. */
static const struct bus_type genpd_provider_bus_type = {
.name = "genpd_provider",
};
/* The parent for genpd_provider devices. */
static struct device genpd_provider_bus = {
.init_name = "genpd_provider",
};
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
@ -176,6 +186,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
#define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON)
#define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW)
#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)
static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
const struct generic_pm_domain *genpd)
@ -758,6 +769,39 @@ int dev_pm_genpd_rpm_always_on(struct device *dev, bool on)
}
EXPORT_SYMBOL_GPL(dev_pm_genpd_rpm_always_on);
/**
* dev_pm_genpd_is_on() - Get device's current power domain status
*
* @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.
*
* 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
* sharing the same genpd. Therefore, this information should not be relied for
* long-term decisions about the device power state.
*
* Return: 'true' if the device's power domain is on, 'false' otherwise.
*/
bool dev_pm_genpd_is_on(struct device *dev)
{
struct generic_pm_domain *genpd;
bool is_on;
genpd = dev_to_genpd_safe(dev);
if (!genpd)
return false;
genpd_lock(genpd);
is_on = genpd_status_on(genpd);
genpd_unlock(genpd);
return is_on;
}
EXPORT_SYMBOL_GPL(dev_pm_genpd_is_on);
/**
* pm_genpd_inc_rejected() - Adjust the rejected/usage counts for an idle-state.
*
@ -920,11 +964,12 @@ static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
* The domain is already in the "power off" state.
* System suspend is in progress.
* The domain is configured as always 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 ||
genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd) ||
atomic_read(&genpd->sd_count) > 0)
genpd->stay_on || atomic_read(&genpd->sd_count) > 0)
return;
/*
@ -1312,6 +1357,7 @@ err_poweroff:
return ret;
}
#ifndef CONFIG_PM_GENERIC_DOMAINS_OF
static bool pd_ignore_unused;
static int __init pd_ignore_unused_setup(char *__unused)
{
@ -1335,14 +1381,19 @@ static int __init genpd_power_off_unused(void)
pr_info("genpd: Disabling unused power domains\n");
mutex_lock(&gpd_list_lock);
list_for_each_entry(genpd, &gpd_list, gpd_list_node)
list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
genpd_lock(genpd);
genpd->stay_on = false;
genpd_unlock(genpd);
genpd_queue_power_off_work(genpd);
}
mutex_unlock(&gpd_list_lock);
return 0;
}
late_initcall_sync(genpd_power_off_unused);
#endif
#ifdef CONFIG_PM_SLEEP
@ -2262,6 +2313,8 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd)
genpd->gd = gd;
device_initialize(&genpd->dev);
genpd->dev.release = genpd_provider_release;
genpd->dev.bus = &genpd_provider_bus_type;
genpd->dev.parent = &genpd_provider_bus;
if (!genpd_is_dev_name_fw(genpd)) {
dev_set_name(&genpd->dev, "%s", genpd->name);
@ -2339,6 +2392,8 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
atomic_set(&genpd->sd_count, 0);
genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
genpd->stay_on = !is_off;
genpd->sync_state = GENPD_SYNC_STATE_OFF;
genpd->device_count = 0;
genpd->provider = NULL;
genpd->device_id = -ENXIO;
@ -2491,6 +2546,8 @@ struct of_genpd_provider {
static LIST_HEAD(of_genpd_providers);
/* Mutex to protect the list above. */
static DEFINE_MUTEX(of_genpd_mutex);
/* Used to prevent registering devices before the bus. */
static bool genpd_bus_registered;
/**
* genpd_xlate_simple() - Xlate function for direct node-domain mapping
@ -2557,7 +2614,7 @@ static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
cp->node = of_node_get(np);
cp->data = data;
cp->xlate = xlate;
fwnode_dev_initialized(&np->fwnode, true);
fwnode_dev_initialized(of_fwnode_handle(np), true);
mutex_lock(&of_genpd_mutex);
list_add(&cp->link, &of_genpd_providers);
@ -2584,6 +2641,11 @@ static bool genpd_present(const struct generic_pm_domain *genpd)
return ret;
}
static void genpd_sync_state(struct device *dev)
{
return of_genpd_sync_state(dev->of_node);
}
/**
* of_genpd_add_provider_simple() - Register a simple PM domain provider
* @np: Device node pointer associated with the PM domain provider.
@ -2592,21 +2654,43 @@ static bool genpd_present(const struct generic_pm_domain *genpd)
int of_genpd_add_provider_simple(struct device_node *np,
struct generic_pm_domain *genpd)
{
struct fwnode_handle *fwnode;
struct device *dev;
int ret;
if (!np || !genpd)
return -EINVAL;
if (!genpd_bus_registered)
return -ENODEV;
if (!genpd_present(genpd))
return -EINVAL;
genpd->dev.of_node = np;
fwnode = of_fwnode_handle(np);
dev = get_dev_from_fwnode(fwnode);
if (!dev && !genpd_is_no_sync_state(genpd)) {
genpd->sync_state = GENPD_SYNC_STATE_SIMPLE;
device_set_node(&genpd->dev, fwnode);
} else {
dev_set_drv_sync_state(dev, genpd_sync_state);
}
put_device(dev);
ret = device_add(&genpd->dev);
if (ret)
return ret;
/* Parse genpd OPP table */
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table(&genpd->dev);
if (ret)
return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n");
if (ret) {
dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n");
goto err_del;
}
/*
* Save table for faster processing while setting performance
@ -2617,19 +2701,22 @@ int of_genpd_add_provider_simple(struct device_node *np,
}
ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
if (ret) {
if (ret)
goto err_opp;
genpd->provider = fwnode;
genpd->has_provider = true;
return 0;
err_opp:
if (genpd->opp_table) {
dev_pm_opp_put_opp_table(genpd->opp_table);
dev_pm_opp_of_remove_table(&genpd->dev);
}
err_del:
device_del(&genpd->dev);
return ret;
}
genpd->provider = &np->fwnode;
genpd->has_provider = true;
return 0;
}
EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple);
@ -2642,15 +2729,30 @@ int of_genpd_add_provider_onecell(struct device_node *np,
struct genpd_onecell_data *data)
{
struct generic_pm_domain *genpd;
struct fwnode_handle *fwnode;
struct device *dev;
unsigned int i;
int ret = -EINVAL;
bool sync_state = false;
if (!np || !data)
return -EINVAL;
if (!genpd_bus_registered)
return -ENODEV;
if (!data->xlate)
data->xlate = genpd_xlate_onecell;
fwnode = of_fwnode_handle(np);
dev = get_dev_from_fwnode(fwnode);
if (!dev)
sync_state = true;
else
dev_set_drv_sync_state(dev, genpd_sync_state);
put_device(dev);
for (i = 0; i < data->num_domains; i++) {
genpd = data->domains[i];
@ -2661,12 +2763,23 @@ int of_genpd_add_provider_onecell(struct device_node *np,
genpd->dev.of_node = np;
if (sync_state && !genpd_is_no_sync_state(genpd)) {
genpd->sync_state = GENPD_SYNC_STATE_ONECELL;
device_set_node(&genpd->dev, fwnode);
sync_state = false;
}
ret = device_add(&genpd->dev);
if (ret)
goto error;
/* Parse genpd OPP table */
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
if (ret) {
dev_err_probe(&genpd->dev, ret,
"Failed to add OPP table for index %d\n", i);
device_del(&genpd->dev);
goto error;
}
@ -2678,7 +2791,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
WARN_ON(IS_ERR(genpd->opp_table));
}
genpd->provider = &np->fwnode;
genpd->provider = fwnode;
genpd->has_provider = true;
}
@ -2702,6 +2815,8 @@ error:
dev_pm_opp_put_opp_table(genpd->opp_table);
dev_pm_opp_of_remove_table(&genpd->dev);
}
device_del(&genpd->dev);
}
return ret;
@ -2727,17 +2842,19 @@ void of_genpd_del_provider(struct device_node *np)
* so that the PM domain can be safely removed.
*/
list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
if (gpd->provider == &np->fwnode) {
if (gpd->provider == of_fwnode_handle(np)) {
gpd->has_provider = false;
if (gpd->opp_table) {
dev_pm_opp_put_opp_table(gpd->opp_table);
dev_pm_opp_of_remove_table(&gpd->dev);
}
device_del(&gpd->dev);
}
}
fwnode_dev_initialized(&cp->node->fwnode, false);
fwnode_dev_initialized(of_fwnode_handle(cp->node), false);
list_del(&cp->link);
of_node_put(cp->node);
kfree(cp);
@ -2916,7 +3033,7 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
mutex_lock(&gpd_list_lock);
list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) {
if (gpd->provider == &np->fwnode) {
if (gpd->provider == of_fwnode_handle(np)) {
ret = genpd_remove(gpd);
genpd = ret ? ERR_PTR(ret) : gpd;
break;
@ -3179,6 +3296,9 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev,
if (num_domains < 0 || index >= num_domains)
return NULL;
if (!genpd_bus_registered)
return ERR_PTR(-ENODEV);
/* Allocate and register device on the genpd bus. */
virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
if (!virt_dev)
@ -3269,7 +3389,7 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state,
genpd_state->power_on_latency_ns = 1000LL * exit_latency;
genpd_state->power_off_latency_ns = 1000LL * entry_latency;
genpd_state->fwnode = &state_node->fwnode;
genpd_state->fwnode = of_fwnode_handle(state_node);
return 0;
}
@ -3355,9 +3475,103 @@ int of_genpd_parse_idle_states(struct device_node *dn,
}
EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states);
/**
* of_genpd_sync_state() - A common sync_state function for genpd providers
* @np: The device node the genpd provider is associated with.
*
* The @np that corresponds to a genpd provider may provide one or multiple
* genpds. This function makes use @np to find the genpds that belongs to the
* provider. For each genpd we try a power-off.
*/
void of_genpd_sync_state(struct device_node *np)
{
struct generic_pm_domain *genpd;
if (!np)
return;
mutex_lock(&gpd_list_lock);
list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
if (genpd->provider == of_fwnode_handle(np)) {
genpd_lock(genpd);
genpd->stay_on = false;
genpd_power_off(genpd, false, 0);
genpd_unlock(genpd);
}
}
mutex_unlock(&gpd_list_lock);
}
EXPORT_SYMBOL_GPL(of_genpd_sync_state);
static int genpd_provider_probe(struct device *dev)
{
return 0;
}
static void genpd_provider_sync_state(struct device *dev)
{
struct generic_pm_domain *genpd = container_of(dev, struct generic_pm_domain, dev);
switch (genpd->sync_state) {
case GENPD_SYNC_STATE_OFF:
break;
case GENPD_SYNC_STATE_ONECELL:
of_genpd_sync_state(dev->of_node);
break;
case GENPD_SYNC_STATE_SIMPLE:
genpd_lock(genpd);
genpd->stay_on = false;
genpd_power_off(genpd, false, 0);
genpd_unlock(genpd);
break;
default:
break;
}
}
static struct device_driver genpd_provider_drv = {
.name = "genpd_provider",
.bus = &genpd_provider_bus_type,
.probe = genpd_provider_probe,
.sync_state = genpd_provider_sync_state,
.suppress_bind_attrs = true,
};
static int __init genpd_bus_init(void)
{
return bus_register(&genpd_bus_type);
int ret;
ret = device_register(&genpd_provider_bus);
if (ret) {
put_device(&genpd_provider_bus);
return ret;
}
ret = bus_register(&genpd_provider_bus_type);
if (ret)
goto err_dev;
ret = bus_register(&genpd_bus_type);
if (ret)
goto err_prov_bus;
ret = driver_register(&genpd_provider_drv);
if (ret)
goto err_bus;
genpd_bus_registered = true;
return 0;
err_bus:
bus_unregister(&genpd_bus_type);
err_prov_bus:
bus_unregister(&genpd_provider_bus_type);
err_dev:
device_unregister(&genpd_provider_bus);
return ret;
}
core_initcall(genpd_bus_init);

View File

@ -665,6 +665,11 @@ static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = {
#define LCDIF_1_RD_HURRY GENMASK(15, 13)
#define LCDIF_0_RD_HURRY GENMASK(12, 10)
#define ISI_CACHE_CTRL 0x50
#define ISI_V_WR_HURRY GENMASK(28, 26)
#define ISI_U_WR_HURRY GENMASK(25, 23)
#define ISI_Y_WR_HURRY GENMASK(22, 20)
static int imx8mp_media_power_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
@ -694,6 +699,11 @@ static int imx8mp_media_power_notifier(struct notifier_block *nb,
regmap_set_bits(bc->regmap, LCDIF_ARCACHE_CTRL,
FIELD_PREP(LCDIF_1_RD_HURRY, 7) |
FIELD_PREP(LCDIF_0_RD_HURRY, 7));
/* Same here for ISI */
regmap_set_bits(bc->regmap, ISI_CACHE_CTRL,
FIELD_PREP(ISI_V_WR_HURRY, 7) |
FIELD_PREP(ISI_U_WR_HURRY, 7) |
FIELD_PREP(ISI_Y_WR_HURRY, 7));
}
return NOTIFY_OK;

View File

@ -217,6 +217,24 @@ static struct rpmhpd gmxc = {
.res_name = "gmxc.lvl",
};
/* Milos RPMH powerdomains */
static struct rpmhpd *milos_rpmhpds[] = {
[RPMHPD_CX] = &cx,
[RPMHPD_CX_AO] = &cx_ao,
[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 milos_desc = {
.rpmhpds = milos_rpmhpds,
.num_pds = ARRAY_SIZE(milos_rpmhpds),
};
/* SA8540P RPMH powerdomains */
static struct rpmhpd *sa8540p_rpmhpds[] = {
[SC8280XP_CX] = &cx,
@ -666,6 +684,31 @@ static const struct rpmhpd_desc sc8280xp_desc = {
.num_pds = ARRAY_SIZE(sc8280xp_rpmhpds),
};
/* Glymur RPMH powerdomains */
static struct rpmhpd *glymur_rpmhpds[] = {
[RPMHPD_CX] = &cx,
[RPMHPD_CX_AO] = &cx_ao,
[RPMHPD_EBI] = &ebi,
[RPMHPD_GFX] = &gfx,
[RPMHPD_LCX] = &lcx,
[RPMHPD_LMX] = &lmx,
[RPMHPD_MMCX] = &mmcx,
[RPMHPD_MMCX_AO] = &mmcx_ao,
[RPMHPD_MX] = &mx,
[RPMHPD_MX_AO] = &mx_ao,
[RPMHPD_MXC] = &mxc,
[RPMHPD_MXC_AO] = &mxc_ao,
[RPMHPD_MSS] = &mss,
[RPMHPD_NSP] = &nsp,
[RPMHPD_NSP2] = &nsp2,
[RPMHPD_GMXC] = &gmxc,
};
static const struct rpmhpd_desc glymur_desc = {
.rpmhpds = glymur_rpmhpds,
.num_pds = ARRAY_SIZE(glymur_rpmhpds),
};
/* X1E80100 RPMH powerdomains */
static struct rpmhpd *x1e80100_rpmhpds[] = {
[RPMHPD_CX] = &cx,
@ -723,6 +766,8 @@ static const struct rpmhpd_desc qcs615_desc = {
};
static const struct of_device_id rpmhpd_match_table[] = {
{ .compatible = "qcom,glymur-rpmhpd", .data = &glymur_desc },
{ .compatible = "qcom,milos-rpmhpd", .data = &milos_desc },
{ .compatible = "qcom,qcs615-rpmhpd", .data = &qcs615_desc },
{ .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc },
{ .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc },
@ -1027,6 +1072,8 @@ static void rpmhpd_sync_state(struct device *dev)
unsigned int i;
int ret;
of_genpd_sync_state(dev->of_node);
mutex_lock(&rpmhpd_lock);
for (i = 0; i < desc->num_pds; i++) {
pd = rpmhpds[i];

View File

@ -1144,6 +1144,8 @@ static void rpmpd_sync_state(struct device *dev)
unsigned int i;
int ret;
of_genpd_sync_state(dev->of_node);
mutex_lock(&rpmpd_lock);
for (i = 0; i < desc->num_pds; i++) {
pd = rpmpds[i];

View File

@ -1,113 +1,117 @@
# SPDX-License-Identifier: GPL-2.0
if SOC_RENESAS
menu "Renesas PM Domains"
# SoC Family
config SYSC_RCAR
bool "System Controller support for R-Car" if COMPILE_TEST
config SYSC_RCAR_GEN4
bool "System Controller support for R-Car Gen4" if COMPILE_TEST
config SYSC_R8A77995
bool "System Controller support for R-Car D3" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7794
bool "System Controller support for R-Car E2" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77990
bool "System Controller support for R-Car E3" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7779
bool "System Controller support for R-Car H1" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7790
bool "System Controller support for R-Car H2" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7795
bool "System Controller support for R-Car H3" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7791
bool "System Controller support for R-Car M2-W/N" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77965
bool "System Controller support for R-Car M3-N" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77960
bool "System Controller support for R-Car M3-W" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77961
bool "System Controller support for R-Car M3-W+" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A779F0
bool "System Controller support for R-Car S4-8" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_R8A7792
bool "System Controller support for R-Car V2H" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77980
bool "System Controller support for R-Car V3H" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77970
bool "System Controller support for R-Car V3M" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A779A0
bool "System Controller support for R-Car V3U" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_R8A779G0
bool "System Controller support for R-Car V4H" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_R8A779H0
bool "System Controller support for R-Car V4M" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_RMOBILE
bool "System Controller support for R-Mobile" if COMPILE_TEST
config SYSC_R8A77470
bool "System Controller support for RZ/G1C" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7745
bool "System Controller support for RZ/G1E" if COMPILE_TEST
select SYSC_RCAR
# SoC
config SYSC_R8A7742
bool "System Controller support for RZ/G1H" if COMPILE_TEST
bool "System Controller support for R8A7742 (RZ/G1H)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7743
bool "System Controller support for RZ/G1M" if COMPILE_TEST
bool "System Controller support for R8A7743 (RZ/G1M)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774C0
bool "System Controller support for RZ/G2E" if COMPILE_TEST
config SYSC_R8A7745
bool "System Controller support for R8A7745 (RZ/G1E)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774E1
bool "System Controller support for RZ/G2H" if COMPILE_TEST
config SYSC_R8A77470
bool "System Controller support for R8A77470 (RZ/G1C)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774A1
bool "System Controller support for RZ/G2M" if COMPILE_TEST
bool "System Controller support for R8A774A1 (RZ/G2M)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774B1
bool "System Controller support for RZ/G2N" if COMPILE_TEST
bool "System Controller support for R8A774B1 (RZ/G2N)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774C0
bool "System Controller support for R8A774C0 (RZ/G2E)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774E1
bool "System Controller support for R8A774E1 (RZ/G2H)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7779
bool "System Controller support for R8A7779 (R-Car H1)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7790
bool "System Controller support for R8A7790 (R-Car H2)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7791
bool "System Controller support for R8A7791/R8A7793 (R-Car M2-W/N)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7792
bool "System Controller support for R8A7792 (R-Car V2H)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7794
bool "System Controller support for R8A7794 (R-Car E2)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7795
bool "System Controller support for R8A7795 (R-Car H3)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77960
bool "System Controller support for R8A77960 (R-Car M3-W)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77961
bool "System Controller support for R8A77961 (R-Car M3-W+)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77965
bool "System Controller support for R8A77965 (R-Car M3-N)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77970
bool "System Controller support for R8A77970 (R-Car V3M)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77980
bool "System Controller support for R8A77980 (R-Car V3H)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77990
bool "System Controller support for R8A77990 (R-Car E3)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77995
bool "System Controller support for R8A77995 (R-Car D3)" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A779A0
bool "System Controller support for R8A779A0 (R-Car V3U)" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_R8A779F0
bool "System Controller support for R8A779F0 (R-Car S4-8)" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_R8A779G0
bool "System Controller support for R8A779G0 (R-Car V4H)" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_R8A779H0
bool "System Controller support for R8A779H0 (R-Car V4M)" if COMPILE_TEST
select SYSC_RCAR_GEN4
endmenu
endif

View File

@ -374,4 +374,4 @@ out_put:
of_node_put(np);
return error;
}
early_initcall(rcar_gen4_sysc_pd_init);
postcore_initcall(rcar_gen4_sysc_pd_init);

View File

@ -342,6 +342,7 @@ struct rcar_pm_domains {
};
static struct genpd_onecell_data *rcar_sysc_onecell_data;
static struct device_node *rcar_sysc_onecell_np;
static int __init rcar_sysc_pd_init(void)
{
@ -428,7 +429,8 @@ static int __init rcar_sysc_pd_init(void)
}
}
error = of_genpd_add_provider_onecell(np, &domains->onecell_data);
rcar_sysc_onecell_np = np;
return 0;
out_put:
of_node_put(np);
@ -436,6 +438,21 @@ out_put:
}
early_initcall(rcar_sysc_pd_init);
static int __init rcar_sysc_pd_init_provider(void)
{
int error;
if (!rcar_sysc_onecell_np)
return -ENODEV;
error = of_genpd_add_provider_onecell(rcar_sysc_onecell_np,
rcar_sysc_onecell_data);
of_node_put(rcar_sysc_onecell_np);
return error;
}
postcore_initcall(rcar_sysc_pd_init_provider);
#ifdef CONFIG_ARCH_R8A7779
static int rcar_sysc_power_cpu(unsigned int idx, bool on)
{

View File

@ -335,5 +335,4 @@ static int __init rmobile_init_pm_domains(void)
return ret;
}
core_initcall(rmobile_init_pm_domains);
postcore_initcall(rmobile_init_pm_domains);

View File

@ -35,6 +35,7 @@
#include <dt-bindings/power/rk3366-power.h>
#include <dt-bindings/power/rk3368-power.h>
#include <dt-bindings/power/rk3399-power.h>
#include <dt-bindings/power/rockchip,rk3528-power.h>
#include <dt-bindings/power/rockchip,rk3562-power.h>
#include <dt-bindings/power/rk3568-power.h>
#include <dt-bindings/power/rockchip,rk3576-power.h>
@ -216,6 +217,9 @@ struct rockchip_pmu {
#define DOMAIN_RK3399(name, pwr, status, req, wakeup) \
DOMAIN(name, pwr, status, req, req, req, wakeup)
#define DOMAIN_RK3528(name, pwr, req) \
DOMAIN_M(name, pwr, pwr, req, req, req, false)
#define DOMAIN_RK3562(name, pwr, req, g_mask, mem, wakeup) \
DOMAIN_M_G_SD(name, pwr, pwr, req, req, req, g_mask, mem, wakeup, false)
@ -1215,6 +1219,14 @@ static const struct rockchip_domain_info rk3399_pm_domains[] = {
[RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399("sdioaudio", BIT(31), BIT(31), BIT(29), true),
};
static const struct rockchip_domain_info rk3528_pm_domains[] = {
[RK3528_PD_GPU] = DOMAIN_RK3528("gpu", BIT(0), BIT(4)),
[RK3528_PD_RKVDEC] = DOMAIN_RK3528("vdec", 0, BIT(5)),
[RK3528_PD_RKVENC] = DOMAIN_RK3528("venc", 0, BIT(6)),
[RK3528_PD_VO] = DOMAIN_RK3528("vo", 0, BIT(7)),
[RK3528_PD_VPU] = DOMAIN_RK3528("vpu", 0, BIT(8)),
};
static const struct rockchip_domain_info rk3562_pm_domains[] = {
/* name pwr req g_mask mem wakeup */
[RK3562_PD_GPU] = DOMAIN_RK3562("gpu", BIT(0), BIT(1), BIT(1), 0, false),
@ -1428,6 +1440,17 @@ static const struct rockchip_pmu_info rk3399_pmu = {
.domain_info = rk3399_pm_domains,
};
static const struct rockchip_pmu_info rk3528_pmu = {
.pwr_offset = 0x1210,
.status_offset = 0x1230,
.req_offset = 0x1110,
.idle_offset = 0x1128,
.ack_offset = 0x1120,
.num_domains = ARRAY_SIZE(rk3528_pm_domains),
.domain_info = rk3528_pm_domains,
};
static const struct rockchip_pmu_info rk3562_pmu = {
.pwr_offset = 0x210,
.status_offset = 0x230,
@ -1538,6 +1561,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = {
.compatible = "rockchip,rk3399-power-controller",
.data = (void *)&rk3399_pmu,
},
{
.compatible = "rockchip,rk3528-power-controller",
.data = (void *)&rk3528_pmu,
},
{
.compatible = "rockchip,rk3562-power-controller",
.data = (void *)&rk3562_pmu,

View File

@ -147,6 +147,15 @@ static int exynos_pd_probe(struct platform_device *pdev)
parent.np, child.np);
}
/*
* Some Samsung platforms with bootloaders turning on the splash-screen
* and handing it over to the kernel, requires the power-domains to be
* reset during boot. As a temporary hack to manage this, let's enforce
* a sync_state.
*/
if (!ret)
of_genpd_sync_state(np);
pm_runtime_enable(dev);
return ret;
}

View File

@ -1,13 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
config SUN20I_PPU
bool "Allwinner D1 PPU power domain driver"
tristate "Allwinner D1 PPU power domain driver"
depends on ARCH_SUNXI || COMPILE_TEST
depends on PM
default ARCH_SUNXI
select PM_GENERIC_DOMAINS
help
Say y to enable the PPU power domain driver. This saves power
when certain peripherals, such as the video engine, are idle.
Say y to enable the PPU power domain driver. This is required
to enable power to certain peripherals, such as the display
engine.
config SUN50I_H6_PRCM_PPU
tristate "Allwinner H6 PRCM power domain driver"
@ -18,3 +20,14 @@ config SUN50I_H6_PRCM_PPU
Say y to enable the Allwinner H6/H616 PRCM power domain driver.
This is required to enable the Mali GPU in the H616 SoC, it is
optional for the H6.
config SUN55I_PCK600
tristate "Allwinner A523 PCK-600 power domain driver"
depends on ARCH_SUNXI || COMPILE_TEST
depends on PM
default ARCH_SUNXI
select PM_GENERIC_DOMAINS
help
Say y to enable the PCK-600 power domain driver. This is required
to enable power to certain peripherals, such as the display and
video engines.

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SUN20I_PPU) += sun20i-ppu.o
obj-$(CONFIG_SUN50I_H6_PRCM_PPU) += sun50i-h6-prcm-ppu.o
obj-$(CONFIG_SUN55I_PCK600) += sun55i-pck600.o

View File

@ -193,6 +193,19 @@ static const struct sun20i_ppu_desc sun8i_v853_ppu_desc = {
.num_domains = ARRAY_SIZE(sun8i_v853_ppu_pd_names),
};
static const char *const sun55i_a523_ppu_pd_names[] = {
"DSP",
"NPU",
"AUDIO",
"SRAM",
"RISCV",
};
static const struct sun20i_ppu_desc sun55i_a523_ppu_desc = {
.names = sun55i_a523_ppu_pd_names,
.num_domains = ARRAY_SIZE(sun55i_a523_ppu_pd_names),
};
static const struct of_device_id sun20i_ppu_of_match[] = {
{
.compatible = "allwinner,sun20i-d1-ppu",
@ -202,6 +215,10 @@ static const struct of_device_id sun20i_ppu_of_match[] = {
.compatible = "allwinner,sun8i-v853-ppu",
.data = &sun8i_v853_ppu_desc,
},
{
.compatible = "allwinner,sun55i-a523-ppu",
.data = &sun55i_a523_ppu_desc,
},
{ }
};
MODULE_DEVICE_TABLE(of, sun20i_ppu_of_match);

View File

@ -0,0 +1,234 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Allwinner PCK-600 power domain support
*
* Copyright (c) 2025 Chen-Yu Tsai <wens@csie.org>
*
* The hardware is likely based on the Arm PCK-600 IP, since some of
* the registers match Arm's documents, with additional delay controls
* that are in registers listed as reserved.
*
* Documents include:
* - "Arm CoreLink PCK-600 Power Control Kit" TRM
* - "Arm Power Policy Unit" architecture specification (DEN0051E)
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#define PPU_PWPR 0x0
#define PPU_PWSR 0x8
#define PPU_DCDR0 0x170
#define PPU_DCDR1 0x174
/* shared definition for PPU_PWPR and PPU_PWSR */
#define PPU_PWR_STATUS GENMASK(3, 0)
#define PPU_POWER_MODE_ON 0x8
#define PPU_POWER_MODE_OFF 0x0
#define PPU_REG_SIZE 0x1000
struct sunxi_pck600_desc {
const char * const *pd_names;
unsigned int num_domains;
u32 logic_power_switch0_delay_offset;
u32 logic_power_switch1_delay_offset;
u32 off2on_delay_offset;
u32 device_ctrl0_delay;
u32 device_ctrl1_delay;
u32 logic_power_switch0_delay;
u32 logic_power_switch1_delay;
u32 off2on_delay;
};
struct sunxi_pck600_pd {
struct generic_pm_domain genpd;
struct sunxi_pck600 *pck;
void __iomem *base;
};
struct sunxi_pck600 {
struct device *dev;
struct genpd_onecell_data genpd_data;
struct sunxi_pck600_pd pds[];
};
#define to_sunxi_pd(gpd) container_of(gpd, struct sunxi_pck600_pd, genpd)
static int sunxi_pck600_pd_set_power(struct sunxi_pck600_pd *pd, bool on)
{
struct sunxi_pck600 *pck = pd->pck;
struct generic_pm_domain *genpd = &pd->genpd;
int ret;
u32 val, reg;
val = on ? PPU_POWER_MODE_ON : PPU_POWER_MODE_OFF;
reg = readl(pd->base + PPU_PWPR);
FIELD_MODIFY(PPU_PWR_STATUS, &reg, val);
writel(reg, pd->base + PPU_PWPR);
/* push write out to hardware */
reg = readl(pd->base + PPU_PWPR);
ret = readl_poll_timeout_atomic(pd->base + PPU_PWSR, reg,
FIELD_GET(PPU_PWR_STATUS, reg) == val,
0, 10000);
if (ret)
dev_err(pck->dev, "failed to turn domain \"%s\" %s: %d\n",
genpd->name, str_on_off(on), ret);
return ret;
}
static int sunxi_pck600_power_on(struct generic_pm_domain *domain)
{
struct sunxi_pck600_pd *pd = to_sunxi_pd(domain);
return sunxi_pck600_pd_set_power(pd, true);
}
static int sunxi_pck600_power_off(struct generic_pm_domain *domain)
{
struct sunxi_pck600_pd *pd = to_sunxi_pd(domain);
return sunxi_pck600_pd_set_power(pd, false);
}
static void sunxi_pck600_pd_setup(struct sunxi_pck600_pd *pd,
const struct sunxi_pck600_desc *desc)
{
writel(desc->device_ctrl0_delay, pd->base + PPU_DCDR0);
writel(desc->device_ctrl1_delay, pd->base + PPU_DCDR1);
writel(desc->logic_power_switch0_delay,
pd->base + desc->logic_power_switch0_delay_offset);
writel(desc->logic_power_switch1_delay,
pd->base + desc->logic_power_switch1_delay_offset);
writel(desc->off2on_delay, pd->base + desc->off2on_delay_offset);
}
static int sunxi_pck600_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct sunxi_pck600_desc *desc;
struct genpd_onecell_data *genpds;
struct sunxi_pck600 *pck;
struct reset_control *rst;
struct clk *clk;
void __iomem *base;
int i, ret;
desc = of_device_get_match_data(dev);
pck = devm_kzalloc(dev, struct_size(pck, pds, desc->num_domains), GFP_KERNEL);
if (!pck)
return -ENOMEM;
pck->dev = &pdev->dev;
platform_set_drvdata(pdev, pck);
genpds = &pck->genpd_data;
genpds->num_domains = desc->num_domains;
genpds->domains = devm_kcalloc(dev, desc->num_domains,
sizeof(*genpds->domains), GFP_KERNEL);
if (!genpds->domains)
return -ENOMEM;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
rst = devm_reset_control_get_exclusive_released(dev, NULL);
if (IS_ERR(rst))
return dev_err_probe(dev, PTR_ERR(rst), "failed to get reset control\n");
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");
for (i = 0; i < desc->num_domains; i++) {
struct sunxi_pck600_pd *pd = &pck->pds[i];
pd->genpd.name = desc->pd_names[i];
pd->genpd.power_off = sunxi_pck600_power_off;
pd->genpd.power_on = sunxi_pck600_power_on;
pd->base = base + PPU_REG_SIZE * i;
sunxi_pck600_pd_setup(pd, desc);
ret = pm_genpd_init(&pd->genpd, NULL, false);
if (ret) {
dev_err_probe(dev, ret, "failed to initialize power domain\n");
goto err_remove_pds;
}
genpds->domains[i] = &pd->genpd;
}
ret = of_genpd_add_provider_onecell(dev_of_node(dev), genpds);
if (ret) {
dev_err_probe(dev, ret, "failed to add PD provider\n");
goto err_remove_pds;
}
return 0;
err_remove_pds:
for (i--; i >= 0; i--)
pm_genpd_remove(genpds->domains[i]);
return ret;
}
static const char * const sun55i_a523_pck600_pd_names[] = {
"VE", "GPU", "VI", "VO0", "VO1", "DE", "NAND", "PCIE"
};
static const struct sunxi_pck600_desc sun55i_a523_pck600_desc = {
.pd_names = sun55i_a523_pck600_pd_names,
.num_domains = ARRAY_SIZE(sun55i_a523_pck600_pd_names),
.logic_power_switch0_delay_offset = 0xc00,
.logic_power_switch1_delay_offset = 0xc04,
.off2on_delay_offset = 0xc10,
.device_ctrl0_delay = 0xffffff,
.device_ctrl1_delay = 0xffff,
.logic_power_switch0_delay = 0x8080808,
.logic_power_switch1_delay = 0x808,
.off2on_delay = 0x8
};
static const struct of_device_id sunxi_pck600_of_match[] = {
{
.compatible = "allwinner,sun55i-a523-pck-600",
.data = &sun55i_a523_pck600_desc,
},
{}
};
MODULE_DEVICE_TABLE(of, sunxi_pck600_of_match);
static struct platform_driver sunxi_pck600_driver = {
.probe = sunxi_pck600_probe,
.driver = {
.name = "sunxi-pck-600",
.of_match_table = sunxi_pck600_of_match,
/* Power domains cannot be removed if in use. */
.suppress_bind_attrs = true,
},
};
module_platform_driver(sunxi_pck600_driver);
MODULE_DESCRIPTION("Allwinner PCK-600 power domain driver");
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
MODULE_LICENSE("GPL");

View File

@ -4,6 +4,7 @@ config TH1520_PM_DOMAINS
tristate "Support TH1520 Power Domains"
depends on TH1520_AON_PROTOCOL
select REGMAP_MMIO
select AUXILIARY_BUS
help
This driver enables power domain management for the T-HEAD
TH-1520 SoC. On this SoC there are number of power domains,

View File

@ -5,6 +5,7 @@
* Author: Michal Wilczynski <m.wilczynski@samsung.com>
*/
#include <linux/auxiliary_bus.h>
#include <linux/firmware/thead/thead,th1520-aon.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
@ -128,6 +129,50 @@ static void th1520_pd_init_all_off(struct generic_pm_domain **domains,
}
}
static void th1520_pd_pwrseq_unregister_adev(void *adev)
{
auxiliary_device_delete(adev);
auxiliary_device_uninit(adev);
}
static int th1520_pd_pwrseq_gpu_init(struct device *dev)
{
struct auxiliary_device *adev;
int ret;
/*
* Correctly check only for the property's existence in the DT node.
* We don't need to get/claim the reset here; that is the job of
* the auxiliary driver that we are about to spawn.
*/
if (device_property_match_string(dev, "reset-names", "gpu-clkgen") < 0)
/*
* This is not an error. It simply means the optional sequencer
* is not described in the device tree.
*/
return 0;
adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
if (!adev)
return -ENOMEM;
adev->name = "pwrseq-gpu";
adev->dev.parent = dev;
ret = auxiliary_device_init(adev);
if (ret)
return ret;
ret = auxiliary_device_add(adev);
if (ret) {
auxiliary_device_uninit(adev);
return ret;
}
return devm_add_action_or_reset(dev, th1520_pd_pwrseq_unregister_adev,
adev);
}
static int th1520_pd_probe(struct platform_device *pdev)
{
struct generic_pm_domain **domains;
@ -186,8 +231,14 @@ static int th1520_pd_probe(struct platform_device *pdev)
if (ret)
goto err_clean_genpd;
ret = th1520_pd_pwrseq_gpu_init(dev);
if (ret)
goto err_clean_provider;
return 0;
err_clean_provider:
of_genpd_del_provider(dev->of_node);
err_clean_genpd:
for (i--; i >= 0; i--)
pm_genpd_remove(domains[i]);

View File

@ -10,7 +10,7 @@ if SOC_TI
config TI_SCI_PM_DOMAINS
tristate "TI SCI PM Domains Driver"
depends on TI_SCI_PROTOCOL
depends on PM_GENERIC_DOMAINS
select PM_GENERIC_DOMAINS if PM
help
Generic power domain implementation for TI device implementing
the TI SCI protocol.

View File

@ -153,14 +153,8 @@ static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain,
struct device *dev)
{
struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain);
struct device_link *link;
int ret;
link = device_link_add(dev, &domain->dev, DL_FLAG_SYNC_STATE_ONLY);
if (!link)
dev_dbg(&domain->dev, "failed to create device link for %s\n",
dev_name(dev));
/* If this is not the first device to attach there is nothing to do */
if (domain->device_count)
return 0;
@ -298,19 +292,9 @@ static void zynqmp_gpd_remove(struct platform_device *pdev)
of_genpd_del_provider(pdev->dev.parent->of_node);
}
static void zynqmp_gpd_sync_state(struct device *dev)
{
int ret;
ret = zynqmp_pm_init_finalize();
if (ret)
dev_warn(dev, "failed to release power management to firmware\n");
}
static struct platform_driver zynqmp_power_domain_driver = {
.driver = {
.name = "zynqmp_power_controller",
.sync_state = zynqmp_gpd_sync_state,
},
.probe = zynqmp_gpd_probe,
.remove = zynqmp_gpd_remove,

View File

@ -18,6 +18,7 @@
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/remoteproc.h>
@ -890,10 +891,8 @@ static int imx_rproc_partition_notify(struct notifier_block *nb,
static int imx_rproc_attach_pd(struct imx_rproc *priv)
{
struct device *dev = priv->dev;
int ret;
struct dev_pm_domain_attach_data pd_data = {
.pd_flags = PD_FLAG_DEV_LINK_ON,
};
int ret, i;
bool detached = true;
/*
* If there is only one power-domain entry, the platform driver framework
@ -902,8 +901,25 @@ static int imx_rproc_attach_pd(struct imx_rproc *priv)
if (dev->pm_domain)
return 0;
ret = dev_pm_domain_attach_list(dev, &pd_data, &priv->pd_list);
return ret < 0 ? ret : 0;
ret = dev_pm_domain_attach_list(dev, NULL, &priv->pd_list);
if (ret < 0)
return ret;
/*
* If all the power domain devices are already turned on, the remote
* core is already powered up and running when the kernel booted (e.g.,
* started by U-Boot's bootaux command). In this case attach to it.
*/
for (i = 0; i < ret; i++) {
if (!dev_pm_genpd_is_on(priv->pd_list->pd_devs[i])) {
detached = false;
break;
}
}
if (detached)
priv->rproc->state = RPROC_DETACHED;
return 0;
}
static int imx_rproc_detect_mode(struct imx_rproc *priv)
@ -1029,8 +1045,8 @@ static int imx_rproc_clk_enable(struct imx_rproc *priv)
struct device *dev = priv->dev;
int ret;
/* Remote core is not under control of Linux */
if (dcfg->method == IMX_RPROC_NONE)
/* Remote core is not under control of Linux or it is managed by SCU API */
if (dcfg->method == IMX_RPROC_NONE || dcfg->method == IMX_RPROC_SCU_API)
return 0;
priv->clk = devm_clk_get(dev, NULL);
@ -1146,6 +1162,15 @@ static int imx_rproc_probe(struct platform_device *pdev)
}
}
if (dcfg->method == IMX_RPROC_SCU_API) {
pm_runtime_enable(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret) {
dev_err(dev, "pm_runtime get failed: %d\n", ret);
goto err_put_clk;
}
}
ret = rproc_add(rproc);
if (ret) {
dev_err(dev, "rproc_add failed\n");
@ -1171,6 +1196,10 @@ static void imx_rproc_remove(struct platform_device *pdev)
struct rproc *rproc = platform_get_drvdata(pdev);
struct imx_rproc *priv = rproc->priv;
if (priv->dcfg->method == IMX_RPROC_SCU_API) {
pm_runtime_disable(priv->dev);
pm_runtime_put(priv->dev);
}
clk_disable_unprepare(priv->clk);
rproc_del(rproc);
imx_rproc_put_scu(rproc);

View File

@ -418,7 +418,6 @@ struct tegra_pmc_soc {
* @irq: chip implementation for the IRQ domain
* @clk_nb: pclk clock changes handler
* @core_domain_state_synced: flag marking the core domain's state as synced
* @core_domain_registered: flag marking the core domain as registered
* @wake_type_level_map: Bitmap indicating level type for non-dual edge wakes
* @wake_type_dual_edge_map: Bitmap indicating if a wake is dual-edge or not
* @wake_sw_status_map: Bitmap to hold raw status of wakes without mask
@ -462,7 +461,6 @@ struct tegra_pmc {
struct notifier_block clk_nb;
bool core_domain_state_synced;
bool core_domain_registered;
unsigned long *wake_type_level_map;
unsigned long *wake_type_dual_edge_map;
@ -1297,6 +1295,7 @@ static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
pg->id = id;
pg->genpd.name = np->name;
pg->genpd.flags = GENPD_FLAG_NO_SYNC_STATE;
pg->genpd.power_off = tegra_genpd_power_off;
pg->genpd.power_on = tegra_genpd_power_on;
pg->pmc = pmc;
@ -1406,6 +1405,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
return -ENOMEM;
genpd->name = "core";
genpd->flags = GENPD_FLAG_NO_SYNC_STATE;
genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
err = devm_pm_opp_set_regulators(pmc->dev, rname);
@ -1425,8 +1425,6 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
goto remove_genpd;
}
pmc->core_domain_registered = true;
return 0;
remove_genpd:
@ -4383,8 +4381,25 @@ static const struct of_device_id tegra_pmc_match[] = {
static void tegra_pmc_sync_state(struct device *dev)
{
struct device_node *np, *child;
int err;
np = of_get_child_by_name(dev->of_node, "powergates");
if (!np)
return;
for_each_child_of_node(np, child)
of_genpd_sync_state(child);
of_node_put(np);
np = of_get_child_by_name(dev->of_node, "core-domain");
if (!np)
return;
of_genpd_sync_state(np);
of_node_put(np);
/*
* Newer device-trees have power domains, but we need to prepare all
* device drivers with runtime PM and OPP support first, otherwise
@ -4398,9 +4413,6 @@ static void tegra_pmc_sync_state(struct device *dev)
* no dependencies that will block the state syncing. We shouldn't
* mark the domain as synced in this case.
*/
if (!pmc->core_domain_registered)
return;
pmc->core_domain_state_synced = true;
/* this is a no-op if core regulator isn't used */

View File

@ -944,6 +944,18 @@ static inline bool dev_has_sync_state(struct device *dev)
return false;
}
static inline int dev_set_drv_sync_state(struct device *dev,
void (*fn)(struct device *dev))
{
if (!dev || !dev->driver)
return 0;
if (dev->driver->sync_state && dev->driver->sync_state != fn)
return -EBUSY;
if (!dev->driver->sync_state)
dev->driver->sync_state = fn;
return 0;
}
static inline void dev_set_removable(struct device *dev,
enum device_removable removable)
{
@ -1075,6 +1087,7 @@ void device_set_node(struct device *dev, struct fwnode_handle *fwnode);
int device_add_of_node(struct device *dev, struct device_node *of_node);
void device_remove_of_node(struct device *dev);
void device_set_of_node_from_dev(struct device *dev, const struct device *dev2);
struct device *get_dev_from_fwnode(struct fwnode_handle *fwnode);
static inline struct device_node *dev_of_node(struct device *dev)
{

View File

@ -585,7 +585,6 @@ int zynqmp_pm_reset_assert(const u32 reset,
int zynqmp_pm_reset_get_status(const u32 reset, u32 *status);
unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode);
int zynqmp_pm_bootmode_write(u32 ps_mode);
int zynqmp_pm_init_finalize(void);
int zynqmp_pm_set_suspend_mode(u32 mode);
int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
const u32 qos, const enum zynqmp_pm_request_ack ack);
@ -746,11 +745,6 @@ static inline int zynqmp_pm_bootmode_write(u32 ps_mode)
return -ENODEV;
}
static inline int zynqmp_pm_init_finalize(void)
{
return -ENODEV;
}
static inline int zynqmp_pm_set_suspend_mode(u32 mode)
{
return -ENODEV;

View File

@ -110,6 +110,11 @@ struct dev_pm_domain_list {
* GENPD_FLAG_DEV_NAME_FW: Instructs genpd to generate an unique device name
* using ida. It is used by genpd providers which
* get their genpd-names directly from FW.
*
* GENPD_FLAG_NO_SYNC_STATE: The ->sync_state() support is implemented in a
* genpd provider specific way, likely through a
* parent device node. This flag makes genpd to
* skip its internal support for this.
*/
#define GENPD_FLAG_PM_CLK (1U << 0)
#define GENPD_FLAG_IRQ_SAFE (1U << 1)
@ -120,6 +125,7 @@ struct dev_pm_domain_list {
#define GENPD_FLAG_MIN_RESIDENCY (1U << 6)
#define GENPD_FLAG_OPP_TABLE_FW (1U << 7)
#define GENPD_FLAG_DEV_NAME_FW (1U << 8)
#define GENPD_FLAG_NO_SYNC_STATE (1U << 9)
enum gpd_status {
GENPD_STATE_ON = 0, /* PM domain is on */
@ -133,6 +139,12 @@ enum genpd_notication {
GENPD_NOTIFY_ON,
};
enum genpd_sync_state {
GENPD_SYNC_STATE_OFF = 0,
GENPD_SYNC_STATE_SIMPLE,
GENPD_SYNC_STATE_ONECELL,
};
struct dev_power_governor {
bool (*power_down_ok)(struct dev_pm_domain *domain);
bool (*suspend_ok)(struct device *dev);
@ -193,6 +205,8 @@ struct generic_pm_domain {
unsigned int performance_state; /* Aggregated max performance state */
cpumask_var_t cpus; /* A cpumask of the attached CPUs */
bool synced_poweroff; /* A consumer needs a synced poweroff */
bool stay_on; /* Stay powered-on during boot. */
enum genpd_sync_state sync_state; /* How sync_state is managed. */
int (*power_off)(struct generic_pm_domain *domain);
int (*power_on)(struct generic_pm_domain *domain);
struct raw_notifier_head power_notifiers; /* Power on/off notifiers */
@ -307,6 +321,7 @@ void dev_pm_genpd_synced_poweroff(struct device *dev);
int dev_pm_genpd_set_hwmode(struct device *dev, bool enable);
bool dev_pm_genpd_get_hwmode(struct device *dev);
int dev_pm_genpd_rpm_always_on(struct device *dev, bool on);
bool dev_pm_genpd_is_on(struct device *dev);
extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov;
@ -399,6 +414,11 @@ static inline int dev_pm_genpd_rpm_always_on(struct device *dev, bool on)
return -EOPNOTSUPP;
}
static inline bool dev_pm_genpd_is_on(struct device *dev)
{
return false;
}
#define simple_qos_governor (*(struct dev_power_governor *)(NULL))
#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))
#endif
@ -437,6 +457,7 @@ int of_genpd_remove_subdomain(const struct of_phandle_args *parent_spec,
struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
int of_genpd_parse_idle_states(struct device_node *dn,
struct genpd_power_state **states, int *n);
void of_genpd_sync_state(struct device_node *np);
int genpd_dev_pm_attach(struct device *dev);
struct device *genpd_dev_pm_attach_by_id(struct device *dev,
@ -482,6 +503,8 @@ static inline int of_genpd_parse_idle_states(struct device_node *dn,
return -ENODEV;
}
static inline void of_genpd_sync_state(struct device_node *np) {}
static inline int genpd_dev_pm_attach(struct device *dev)
{
return 0;