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: compatible:
oneOf: oneOf:
- enum: - enum:
- qcom,glymur-rpmhpd
- qcom,mdm9607-rpmpd - qcom,mdm9607-rpmpd
- qcom,milos-rpmhpd - qcom,milos-rpmhpd
- qcom,msm8226-rpmpd - qcom,msm8226-rpmpd

View File

@ -1881,8 +1881,6 @@ static void fw_devlink_unblock_consumers(struct device *dev)
device_links_write_unlock(); device_links_write_unlock();
} }
#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev)
static bool fwnode_init_without_drv(struct fwnode_handle *fwnode) static bool fwnode_init_without_drv(struct fwnode_handle *fwnode)
{ {
struct device *dev; struct device *dev;
@ -5281,6 +5279,12 @@ void device_set_node(struct device *dev, struct fwnode_handle *fwnode)
} }
EXPORT_SYMBOL_GPL(device_set_node); 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) int device_match_name(struct device *dev, const void *name)
{ {
return sysfs_streq(dev_name(dev), 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 LIST_HEAD(psci_pd_providers);
static bool psci_pd_allow_domain_state;
static int psci_pd_power_off(struct generic_pm_domain *pd) 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) if (!state->data)
return 0; return 0;
if (!psci_pd_allow_domain_state)
return -EBUSY;
/* OSI mode is enabled, set the corresponding domain state. */ /* OSI mode is enabled, set the corresponding domain state. */
pd_state = state->data; pd_state = state->data;
psci_set_domain_state(pd, pd->state_idx, *pd_state); 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[] = { static const struct of_device_id psci_of_match[] = {
{ .compatible = "arm,psci-1.0" }, { .compatible = "arm,psci-1.0" },
{} {}
@ -195,7 +182,6 @@ static struct platform_driver psci_cpuidle_domain_driver = {
.driver = { .driver = {
.name = "psci-cpuidle-domain", .name = "psci-cpuidle-domain",
.of_match_table = psci_of_match, .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 DEFINE_PER_CPU(struct sbi_domain_state, domain_state);
static bool sbi_cpuidle_use_osi; static bool sbi_cpuidle_use_osi;
static bool sbi_cpuidle_use_cpuhp; static bool sbi_cpuidle_use_cpuhp;
static bool sbi_cpuidle_pd_allow_domain_state;
static inline void sbi_set_domain_state(u32 state) static inline void sbi_set_domain_state(u32 state)
{ {
@ -345,15 +344,6 @@ deinit:
return ret; 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 #ifdef CONFIG_DT_IDLE_GENPD
static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd) 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) if (!state->data)
return 0; return 0;
if (!sbi_cpuidle_pd_allow_domain_state)
return -EBUSY;
/* OSI mode is enabled, set the corresponding domain state. */ /* OSI mode is enabled, set the corresponding domain state. */
pd_state = state->data; pd_state = state->data;
sbi_set_domain_state(*pd_state); sbi_set_domain_state(*pd_state);
@ -564,7 +551,6 @@ static struct platform_driver sbi_cpuidle_driver = {
.probe = sbi_cpuidle_probe, .probe = sbi_cpuidle_probe,
.driver = { .driver = {
.name = "sbi-cpuidle", .name = "sbi-cpuidle",
.sync_state = sbi_cpuidle_domain_sync_state,
}, },
}; };

View File

@ -20,6 +20,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/hashtable.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 * This API function is to be used for notify the power management controller
* about the completed power management initialization. * 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); 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 * 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); 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[] = { static const struct of_device_id zynqmp_firmware_of_match[] = {
{.compatible = "xlnx,zynqmp-firmware"}, {.compatible = "xlnx,zynqmp-firmware"},
{.compatible = "xlnx,versal-firmware"}, {.compatible = "xlnx,versal-firmware"},
@ -2112,6 +2125,7 @@ static struct platform_driver zynqmp_firmware_driver = {
.name = "zynqmp_firmware", .name = "zynqmp_firmware",
.of_match_table = zynqmp_firmware_of_match, .of_match_table = zynqmp_firmware_of_match,
.dev_groups = zynqmp_firmware_groups, .dev_groups = zynqmp_firmware_groups,
.sync_state = zynqmp_firmware_sync_state,
}, },
.probe = zynqmp_firmware_probe, .probe = zynqmp_firmware_probe,
.remove = zynqmp_firmware_remove, .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); 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, .domains = a1_pwrc_domains,
.count = ARRAY_SIZE(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, .domains = a4_pwrc_domains,
.count = ARRAY_SIZE(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, .domains = a5_pwrc_domains,
.count = ARRAY_SIZE(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, .domains = c3_pwrc_domains,
.count = ARRAY_SIZE(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, .domains = s4_pwrc_domains,
.count = ARRAY_SIZE(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, .domains = t7_pwrc_domains,
.count = ARRAY_SIZE(t7_pwrc_domains), .count = ARRAY_SIZE(t7_pwrc_domains),
}; };

View File

@ -9,7 +9,6 @@ config APPLE_PMGR_PWRSTATE
select MFD_SYSCON select MFD_SYSCON
select PM_GENERIC_DOMAINS select PM_GENERIC_DOMAINS
select RESET_CONTROLLER select RESET_CONTROLLER
default ARCH_APPLE
help help
The PMGR block in Apple SoCs provides high-level power state The PMGR block in Apple SoCs provides high-level power state
controls for SoC devices. This driver manages them through the 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) #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); 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); return power_ops->state_set(pd->ph, pd->domain, state);
} }
static int scmi_pd_power_on(struct generic_pm_domain *domain) 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) 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) static int scmi_pm_domain_probe(struct scmi_device *sdev)

View File

@ -27,6 +27,16 @@
/* Provides a unique ID for each genpd device */ /* Provides a unique ID for each genpd device */
static DEFINE_IDA(genpd_ida); 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_RETRY_MAX_MS 250 /* Approximate */
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ #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_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_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_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, static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
const struct generic_pm_domain *genpd) 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); 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. * 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. * The domain is already in the "power off" state.
* System suspend is in progress. * System suspend is in progress.
* The domain is configured as always on. * 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. * The domain has a subdomain being powered on.
*/ */
if (!genpd_status_on(genpd) || genpd->prepared_count > 0 || if (!genpd_status_on(genpd) || genpd->prepared_count > 0 ||
genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd) || 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; return;
/* /*
@ -1312,6 +1357,7 @@ err_poweroff:
return ret; return ret;
} }
#ifndef CONFIG_PM_GENERIC_DOMAINS_OF
static bool pd_ignore_unused; static bool pd_ignore_unused;
static int __init pd_ignore_unused_setup(char *__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"); pr_info("genpd: Disabling unused power domains\n");
mutex_lock(&gpd_list_lock); 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); genpd_queue_power_off_work(genpd);
}
mutex_unlock(&gpd_list_lock); mutex_unlock(&gpd_list_lock);
return 0; return 0;
} }
late_initcall_sync(genpd_power_off_unused); late_initcall_sync(genpd_power_off_unused);
#endif
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
@ -2262,6 +2313,8 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd)
genpd->gd = gd; genpd->gd = gd;
device_initialize(&genpd->dev); device_initialize(&genpd->dev);
genpd->dev.release = genpd_provider_release; 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)) { if (!genpd_is_dev_name_fw(genpd)) {
dev_set_name(&genpd->dev, "%s", genpd->name); 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); INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
atomic_set(&genpd->sd_count, 0); atomic_set(&genpd->sd_count, 0);
genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON; 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->device_count = 0;
genpd->provider = NULL; genpd->provider = NULL;
genpd->device_id = -ENXIO; genpd->device_id = -ENXIO;
@ -2491,6 +2546,8 @@ struct of_genpd_provider {
static LIST_HEAD(of_genpd_providers); static LIST_HEAD(of_genpd_providers);
/* Mutex to protect the list above. */ /* Mutex to protect the list above. */
static DEFINE_MUTEX(of_genpd_mutex); 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 * 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->node = of_node_get(np);
cp->data = data; cp->data = data;
cp->xlate = xlate; cp->xlate = xlate;
fwnode_dev_initialized(&np->fwnode, true); fwnode_dev_initialized(of_fwnode_handle(np), true);
mutex_lock(&of_genpd_mutex); mutex_lock(&of_genpd_mutex);
list_add(&cp->link, &of_genpd_providers); list_add(&cp->link, &of_genpd_providers);
@ -2584,6 +2641,11 @@ static bool genpd_present(const struct generic_pm_domain *genpd)
return ret; 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 * of_genpd_add_provider_simple() - Register a simple PM domain provider
* @np: Device node pointer associated with the 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, int of_genpd_add_provider_simple(struct device_node *np,
struct generic_pm_domain *genpd) struct generic_pm_domain *genpd)
{ {
struct fwnode_handle *fwnode;
struct device *dev;
int ret; int ret;
if (!np || !genpd) if (!np || !genpd)
return -EINVAL; return -EINVAL;
if (!genpd_bus_registered)
return -ENODEV;
if (!genpd_present(genpd)) if (!genpd_present(genpd))
return -EINVAL; return -EINVAL;
genpd->dev.of_node = np; 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 */ /* Parse genpd OPP table */
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) { if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table(&genpd->dev); ret = dev_pm_opp_of_add_table(&genpd->dev);
if (ret) if (ret) {
return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n"); dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n");
goto err_del;
}
/* /*
* Save table for faster processing while setting performance * 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); ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
if (ret) { if (ret)
if (genpd->opp_table) { goto err_opp;
dev_pm_opp_put_opp_table(genpd->opp_table);
dev_pm_opp_of_remove_table(&genpd->dev);
}
return ret; genpd->provider = fwnode;
}
genpd->provider = &np->fwnode;
genpd->has_provider = true; genpd->has_provider = true;
return 0; 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;
} }
EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); 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 genpd_onecell_data *data)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
struct fwnode_handle *fwnode;
struct device *dev;
unsigned int i; unsigned int i;
int ret = -EINVAL; int ret = -EINVAL;
bool sync_state = false;
if (!np || !data) if (!np || !data)
return -EINVAL; return -EINVAL;
if (!genpd_bus_registered)
return -ENODEV;
if (!data->xlate) if (!data->xlate)
data->xlate = genpd_xlate_onecell; 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++) { for (i = 0; i < data->num_domains; i++) {
genpd = data->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; 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 */ /* Parse genpd OPP table */
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) { if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i); ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
if (ret) { if (ret) {
dev_err_probe(&genpd->dev, ret, dev_err_probe(&genpd->dev, ret,
"Failed to add OPP table for index %d\n", i); "Failed to add OPP table for index %d\n", i);
device_del(&genpd->dev);
goto error; goto error;
} }
@ -2678,7 +2791,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
WARN_ON(IS_ERR(genpd->opp_table)); WARN_ON(IS_ERR(genpd->opp_table));
} }
genpd->provider = &np->fwnode; genpd->provider = fwnode;
genpd->has_provider = true; genpd->has_provider = true;
} }
@ -2702,6 +2815,8 @@ error:
dev_pm_opp_put_opp_table(genpd->opp_table); dev_pm_opp_put_opp_table(genpd->opp_table);
dev_pm_opp_of_remove_table(&genpd->dev); dev_pm_opp_of_remove_table(&genpd->dev);
} }
device_del(&genpd->dev);
} }
return ret; return ret;
@ -2727,17 +2842,19 @@ void of_genpd_del_provider(struct device_node *np)
* so that the PM domain can be safely removed. * so that the PM domain can be safely removed.
*/ */
list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 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; gpd->has_provider = false;
if (gpd->opp_table) { if (gpd->opp_table) {
dev_pm_opp_put_opp_table(gpd->opp_table); dev_pm_opp_put_opp_table(gpd->opp_table);
dev_pm_opp_of_remove_table(&gpd->dev); 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); list_del(&cp->link);
of_node_put(cp->node); of_node_put(cp->node);
kfree(cp); kfree(cp);
@ -2916,7 +3033,7 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
mutex_lock(&gpd_list_lock); mutex_lock(&gpd_list_lock);
list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) { 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); ret = genpd_remove(gpd);
genpd = ret ? ERR_PTR(ret) : gpd; genpd = ret ? ERR_PTR(ret) : gpd;
break; break;
@ -3179,6 +3296,9 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev,
if (num_domains < 0 || index >= num_domains) if (num_domains < 0 || index >= num_domains)
return NULL; return NULL;
if (!genpd_bus_registered)
return ERR_PTR(-ENODEV);
/* Allocate and register device on the genpd bus. */ /* Allocate and register device on the genpd bus. */
virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL); virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
if (!virt_dev) 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_on_latency_ns = 1000LL * exit_latency;
genpd_state->power_off_latency_ns = 1000LL * entry_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; return 0;
} }
@ -3355,9 +3475,103 @@ int of_genpd_parse_idle_states(struct device_node *dn,
} }
EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); 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) 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); 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_1_RD_HURRY GENMASK(15, 13)
#define LCDIF_0_RD_HURRY GENMASK(12, 10) #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, static int imx8mp_media_power_notifier(struct notifier_block *nb,
unsigned long action, void *data) 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, regmap_set_bits(bc->regmap, LCDIF_ARCACHE_CTRL,
FIELD_PREP(LCDIF_1_RD_HURRY, 7) | FIELD_PREP(LCDIF_1_RD_HURRY, 7) |
FIELD_PREP(LCDIF_0_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; return NOTIFY_OK;

View File

@ -217,6 +217,24 @@ static struct rpmhpd gmxc = {
.res_name = "gmxc.lvl", .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 */ /* SA8540P RPMH powerdomains */
static struct rpmhpd *sa8540p_rpmhpds[] = { static struct rpmhpd *sa8540p_rpmhpds[] = {
[SC8280XP_CX] = &cx, [SC8280XP_CX] = &cx,
@ -666,6 +684,31 @@ static const struct rpmhpd_desc sc8280xp_desc = {
.num_pds = ARRAY_SIZE(sc8280xp_rpmhpds), .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 */ /* X1E80100 RPMH powerdomains */
static struct rpmhpd *x1e80100_rpmhpds[] = { static struct rpmhpd *x1e80100_rpmhpds[] = {
[RPMHPD_CX] = &cx, [RPMHPD_CX] = &cx,
@ -723,6 +766,8 @@ static const struct rpmhpd_desc qcs615_desc = {
}; };
static const struct of_device_id rpmhpd_match_table[] = { 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,qcs615-rpmhpd", .data = &qcs615_desc },
{ .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc }, { .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc },
{ .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc },
@ -1027,6 +1072,8 @@ static void rpmhpd_sync_state(struct device *dev)
unsigned int i; unsigned int i;
int ret; int ret;
of_genpd_sync_state(dev->of_node);
mutex_lock(&rpmhpd_lock); mutex_lock(&rpmhpd_lock);
for (i = 0; i < desc->num_pds; i++) { for (i = 0; i < desc->num_pds; i++) {
pd = rpmhpds[i]; pd = rpmhpds[i];

View File

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

View File

@ -1,113 +1,117 @@
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
if SOC_RENESAS if SOC_RENESAS
menu "Renesas PM Domains"
# SoC Family
config SYSC_RCAR config SYSC_RCAR
bool "System Controller support for R-Car" if COMPILE_TEST bool "System Controller support for R-Car" if COMPILE_TEST
config SYSC_RCAR_GEN4 config SYSC_RCAR_GEN4
bool "System Controller support for R-Car Gen4" if COMPILE_TEST 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 config SYSC_RMOBILE
bool "System Controller support for R-Mobile" if COMPILE_TEST bool "System Controller support for R-Mobile" if COMPILE_TEST
config SYSC_R8A77470 # SoC
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
config SYSC_R8A7742 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 select SYSC_RCAR
config SYSC_R8A7743 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 select SYSC_RCAR
config SYSC_R8A774C0 config SYSC_R8A7745
bool "System Controller support for RZ/G2E" if COMPILE_TEST bool "System Controller support for R8A7745 (RZ/G1E)" if COMPILE_TEST
select SYSC_RCAR select SYSC_RCAR
config SYSC_R8A774E1 config SYSC_R8A77470
bool "System Controller support for RZ/G2H" if COMPILE_TEST bool "System Controller support for R8A77470 (RZ/G1C)" if COMPILE_TEST
select SYSC_RCAR select SYSC_RCAR
config SYSC_R8A774A1 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 select SYSC_RCAR
config SYSC_R8A774B1 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 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 endif

View File

@ -374,4 +374,4 @@ out_put:
of_node_put(np); of_node_put(np);
return error; 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 genpd_onecell_data *rcar_sysc_onecell_data;
static struct device_node *rcar_sysc_onecell_np;
static int __init rcar_sysc_pd_init(void) 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: out_put:
of_node_put(np); of_node_put(np);
@ -436,6 +438,21 @@ out_put:
} }
early_initcall(rcar_sysc_pd_init); 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 #ifdef CONFIG_ARCH_R8A7779
static int rcar_sysc_power_cpu(unsigned int idx, bool on) 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; return ret;
} }
postcore_initcall(rmobile_init_pm_domains);
core_initcall(rmobile_init_pm_domains);

View File

@ -35,6 +35,7 @@
#include <dt-bindings/power/rk3366-power.h> #include <dt-bindings/power/rk3366-power.h>
#include <dt-bindings/power/rk3368-power.h> #include <dt-bindings/power/rk3368-power.h>
#include <dt-bindings/power/rk3399-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/rockchip,rk3562-power.h>
#include <dt-bindings/power/rk3568-power.h> #include <dt-bindings/power/rk3568-power.h>
#include <dt-bindings/power/rockchip,rk3576-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) \ #define DOMAIN_RK3399(name, pwr, status, req, wakeup) \
DOMAIN(name, pwr, status, req, req, 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) \ #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) 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), [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[] = { static const struct rockchip_domain_info rk3562_pm_domains[] = {
/* name pwr req g_mask mem wakeup */ /* name pwr req g_mask mem wakeup */
[RK3562_PD_GPU] = DOMAIN_RK3562("gpu", BIT(0), BIT(1), BIT(1), 0, false), [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, .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 = { static const struct rockchip_pmu_info rk3562_pmu = {
.pwr_offset = 0x210, .pwr_offset = 0x210,
.status_offset = 0x230, .status_offset = 0x230,
@ -1538,6 +1561,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = {
.compatible = "rockchip,rk3399-power-controller", .compatible = "rockchip,rk3399-power-controller",
.data = (void *)&rk3399_pmu, .data = (void *)&rk3399_pmu,
}, },
{
.compatible = "rockchip,rk3528-power-controller",
.data = (void *)&rk3528_pmu,
},
{ {
.compatible = "rockchip,rk3562-power-controller", .compatible = "rockchip,rk3562-power-controller",
.data = (void *)&rk3562_pmu, .data = (void *)&rk3562_pmu,

View File

@ -147,6 +147,15 @@ static int exynos_pd_probe(struct platform_device *pdev)
parent.np, child.np); 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); pm_runtime_enable(dev);
return ret; return ret;
} }

View File

@ -1,13 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
config SUN20I_PPU 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 ARCH_SUNXI || COMPILE_TEST
depends on PM depends on PM
default ARCH_SUNXI
select PM_GENERIC_DOMAINS select PM_GENERIC_DOMAINS
help help
Say y to enable the PPU power domain driver. This saves power Say y to enable the PPU power domain driver. This is required
when certain peripherals, such as the video engine, are idle. to enable power to certain peripherals, such as the display
engine.
config SUN50I_H6_PRCM_PPU config SUN50I_H6_PRCM_PPU
tristate "Allwinner H6 PRCM power domain driver" 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. 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 This is required to enable the Mali GPU in the H616 SoC, it is
optional for the H6. 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 # SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SUN20I_PPU) += sun20i-ppu.o obj-$(CONFIG_SUN20I_PPU) += sun20i-ppu.o
obj-$(CONFIG_SUN50I_H6_PRCM_PPU) += sun50i-h6-prcm-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), .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[] = { static const struct of_device_id sun20i_ppu_of_match[] = {
{ {
.compatible = "allwinner,sun20i-d1-ppu", .compatible = "allwinner,sun20i-d1-ppu",
@ -202,6 +215,10 @@ static const struct of_device_id sun20i_ppu_of_match[] = {
.compatible = "allwinner,sun8i-v853-ppu", .compatible = "allwinner,sun8i-v853-ppu",
.data = &sun8i_v853_ppu_desc, .data = &sun8i_v853_ppu_desc,
}, },
{
.compatible = "allwinner,sun55i-a523-ppu",
.data = &sun55i_a523_ppu_desc,
},
{ } { }
}; };
MODULE_DEVICE_TABLE(of, sun20i_ppu_of_match); 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" tristate "Support TH1520 Power Domains"
depends on TH1520_AON_PROTOCOL depends on TH1520_AON_PROTOCOL
select REGMAP_MMIO select REGMAP_MMIO
select AUXILIARY_BUS
help help
This driver enables power domain management for the T-HEAD This driver enables power domain management for the T-HEAD
TH-1520 SoC. On this SoC there are number of power domains, 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> * Author: Michal Wilczynski <m.wilczynski@samsung.com>
*/ */
#include <linux/auxiliary_bus.h>
#include <linux/firmware/thead/thead,th1520-aon.h> #include <linux/firmware/thead/thead,th1520-aon.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/platform_device.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) static int th1520_pd_probe(struct platform_device *pdev)
{ {
struct generic_pm_domain **domains; struct generic_pm_domain **domains;
@ -186,8 +231,14 @@ static int th1520_pd_probe(struct platform_device *pdev)
if (ret) if (ret)
goto err_clean_genpd; goto err_clean_genpd;
ret = th1520_pd_pwrseq_gpu_init(dev);
if (ret)
goto err_clean_provider;
return 0; return 0;
err_clean_provider:
of_genpd_del_provider(dev->of_node);
err_clean_genpd: err_clean_genpd:
for (i--; i >= 0; i--) for (i--; i >= 0; i--)
pm_genpd_remove(domains[i]); pm_genpd_remove(domains[i]);

View File

@ -10,7 +10,7 @@ if SOC_TI
config TI_SCI_PM_DOMAINS config TI_SCI_PM_DOMAINS
tristate "TI SCI PM Domains Driver" tristate "TI SCI PM Domains Driver"
depends on TI_SCI_PROTOCOL depends on TI_SCI_PROTOCOL
depends on PM_GENERIC_DOMAINS select PM_GENERIC_DOMAINS if PM
help help
Generic power domain implementation for TI device implementing Generic power domain implementation for TI device implementing
the TI SCI protocol. 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 device *dev)
{ {
struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain);
struct device_link *link;
int ret; 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 this is not the first device to attach there is nothing to do */
if (domain->device_count) if (domain->device_count)
return 0; return 0;
@ -298,19 +292,9 @@ static void zynqmp_gpd_remove(struct platform_device *pdev)
of_genpd_del_provider(pdev->dev.parent->of_node); 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 = { static struct platform_driver zynqmp_power_domain_driver = {
.driver = { .driver = {
.name = "zynqmp_power_controller", .name = "zynqmp_power_controller",
.sync_state = zynqmp_gpd_sync_state,
}, },
.probe = zynqmp_gpd_probe, .probe = zynqmp_gpd_probe,
.remove = zynqmp_gpd_remove, .remove = zynqmp_gpd_remove,

View File

@ -18,6 +18,7 @@
#include <linux/of_reserved_mem.h> #include <linux/of_reserved_mem.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/remoteproc.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) static int imx_rproc_attach_pd(struct imx_rproc *priv)
{ {
struct device *dev = priv->dev; struct device *dev = priv->dev;
int ret; int ret, i;
struct dev_pm_domain_attach_data pd_data = { bool detached = true;
.pd_flags = PD_FLAG_DEV_LINK_ON,
};
/* /*
* If there is only one power-domain entry, the platform driver framework * 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) if (dev->pm_domain)
return 0; return 0;
ret = dev_pm_domain_attach_list(dev, &pd_data, &priv->pd_list); ret = dev_pm_domain_attach_list(dev, NULL, &priv->pd_list);
return ret < 0 ? ret : 0; 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) 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; struct device *dev = priv->dev;
int ret; int ret;
/* Remote core is not under control of Linux */ /* Remote core is not under control of Linux or it is managed by SCU API */
if (dcfg->method == IMX_RPROC_NONE) if (dcfg->method == IMX_RPROC_NONE || dcfg->method == IMX_RPROC_SCU_API)
return 0; return 0;
priv->clk = devm_clk_get(dev, NULL); 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); ret = rproc_add(rproc);
if (ret) { if (ret) {
dev_err(dev, "rproc_add failed\n"); 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 rproc *rproc = platform_get_drvdata(pdev);
struct imx_rproc *priv = rproc->priv; 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); clk_disable_unprepare(priv->clk);
rproc_del(rproc); rproc_del(rproc);
imx_rproc_put_scu(rproc); imx_rproc_put_scu(rproc);

View File

@ -418,7 +418,6 @@ struct tegra_pmc_soc {
* @irq: chip implementation for the IRQ domain * @irq: chip implementation for the IRQ domain
* @clk_nb: pclk clock changes handler * @clk_nb: pclk clock changes handler
* @core_domain_state_synced: flag marking the core domain's state as synced * @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_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_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 * @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; struct notifier_block clk_nb;
bool core_domain_state_synced; bool core_domain_state_synced;
bool core_domain_registered;
unsigned long *wake_type_level_map; unsigned long *wake_type_level_map;
unsigned long *wake_type_dual_edge_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->id = id;
pg->genpd.name = np->name; pg->genpd.name = np->name;
pg->genpd.flags = GENPD_FLAG_NO_SYNC_STATE;
pg->genpd.power_off = tegra_genpd_power_off; pg->genpd.power_off = tegra_genpd_power_off;
pg->genpd.power_on = tegra_genpd_power_on; pg->genpd.power_on = tegra_genpd_power_on;
pg->pmc = pmc; pg->pmc = pmc;
@ -1406,6 +1405,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
return -ENOMEM; return -ENOMEM;
genpd->name = "core"; genpd->name = "core";
genpd->flags = GENPD_FLAG_NO_SYNC_STATE;
genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state; genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
err = devm_pm_opp_set_regulators(pmc->dev, rname); 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; goto remove_genpd;
} }
pmc->core_domain_registered = true;
return 0; return 0;
remove_genpd: remove_genpd:
@ -4383,8 +4381,25 @@ static const struct of_device_id tegra_pmc_match[] = {
static void tegra_pmc_sync_state(struct device *dev) static void tegra_pmc_sync_state(struct device *dev)
{ {
struct device_node *np, *child;
int err; 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 * Newer device-trees have power domains, but we need to prepare all
* device drivers with runtime PM and OPP support first, otherwise * 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 * no dependencies that will block the state syncing. We shouldn't
* mark the domain as synced in this case. * mark the domain as synced in this case.
*/ */
if (!pmc->core_domain_registered)
return;
pmc->core_domain_state_synced = true; pmc->core_domain_state_synced = true;
/* this is a no-op if core regulator isn't used */ /* 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; 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, static inline void dev_set_removable(struct device *dev,
enum device_removable removable) 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); int device_add_of_node(struct device *dev, struct device_node *of_node);
void device_remove_of_node(struct device *dev); void device_remove_of_node(struct device *dev);
void device_set_of_node_from_dev(struct device *dev, const struct device *dev2); 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) 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); int zynqmp_pm_reset_get_status(const u32 reset, u32 *status);
unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode); unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode);
int zynqmp_pm_bootmode_write(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_set_suspend_mode(u32 mode);
int zynqmp_pm_request_node(const u32 node, const u32 capabilities, int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
const u32 qos, const enum zynqmp_pm_request_ack ack); 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; return -ENODEV;
} }
static inline int zynqmp_pm_init_finalize(void)
{
return -ENODEV;
}
static inline int zynqmp_pm_set_suspend_mode(u32 mode) static inline int zynqmp_pm_set_suspend_mode(u32 mode)
{ {
return -ENODEV; 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 * GENPD_FLAG_DEV_NAME_FW: Instructs genpd to generate an unique device name
* using ida. It is used by genpd providers which * using ida. It is used by genpd providers which
* get their genpd-names directly from FW. * 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_PM_CLK (1U << 0)
#define GENPD_FLAG_IRQ_SAFE (1U << 1) #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_MIN_RESIDENCY (1U << 6)
#define GENPD_FLAG_OPP_TABLE_FW (1U << 7) #define GENPD_FLAG_OPP_TABLE_FW (1U << 7)
#define GENPD_FLAG_DEV_NAME_FW (1U << 8) #define GENPD_FLAG_DEV_NAME_FW (1U << 8)
#define GENPD_FLAG_NO_SYNC_STATE (1U << 9)
enum gpd_status { enum gpd_status {
GENPD_STATE_ON = 0, /* PM domain is on */ GENPD_STATE_ON = 0, /* PM domain is on */
@ -133,6 +139,12 @@ enum genpd_notication {
GENPD_NOTIFY_ON, GENPD_NOTIFY_ON,
}; };
enum genpd_sync_state {
GENPD_SYNC_STATE_OFF = 0,
GENPD_SYNC_STATE_SIMPLE,
GENPD_SYNC_STATE_ONECELL,
};
struct dev_power_governor { struct dev_power_governor {
bool (*power_down_ok)(struct dev_pm_domain *domain); bool (*power_down_ok)(struct dev_pm_domain *domain);
bool (*suspend_ok)(struct device *dev); bool (*suspend_ok)(struct device *dev);
@ -193,6 +205,8 @@ struct generic_pm_domain {
unsigned int performance_state; /* Aggregated max performance state */ unsigned int performance_state; /* Aggregated max performance state */
cpumask_var_t cpus; /* A cpumask of the attached CPUs */ cpumask_var_t cpus; /* A cpumask of the attached CPUs */
bool synced_poweroff; /* A consumer needs a synced poweroff */ 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_off)(struct generic_pm_domain *domain);
int (*power_on)(struct generic_pm_domain *domain); int (*power_on)(struct generic_pm_domain *domain);
struct raw_notifier_head power_notifiers; /* Power on/off notifiers */ 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); int dev_pm_genpd_set_hwmode(struct device *dev, bool enable);
bool dev_pm_genpd_get_hwmode(struct device *dev); bool dev_pm_genpd_get_hwmode(struct device *dev);
int dev_pm_genpd_rpm_always_on(struct device *dev, bool on); 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 simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov; 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; 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 simple_qos_governor (*(struct dev_power_governor *)(NULL))
#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL)) #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))
#endif #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); struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
int of_genpd_parse_idle_states(struct device_node *dn, int of_genpd_parse_idle_states(struct device_node *dn,
struct genpd_power_state **states, int *n); struct genpd_power_state **states, int *n);
void of_genpd_sync_state(struct device_node *np);
int genpd_dev_pm_attach(struct device *dev); int genpd_dev_pm_attach(struct device *dev);
struct device *genpd_dev_pm_attach_by_id(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; return -ENODEV;
} }
static inline void of_genpd_sync_state(struct device_node *np) {}
static inline int genpd_dev_pm_attach(struct device *dev) static inline int genpd_dev_pm_attach(struct device *dev)
{ {
return 0; return 0;