diff options
31 files changed, 858 insertions, 173 deletions
diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 7d4394a3ccbc..af5fef872529 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -17,6 +17,7 @@ properties: compatible: oneOf: - enum: + - qcom,glymur-rpmhpd - qcom,mdm9607-rpmpd - qcom,milos-rpmhpd - qcom,msm8226-rpmpd diff --git a/drivers/base/core.c b/drivers/base/core.c index cbc0099d8ef2..6f91ece7c06a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -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); diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c index 2041f59116ce..37c41209eaf9 100644 --- a/drivers/cpuidle/cpuidle-psci-domain.c +++ b/drivers/cpuidle/cpuidle-psci-domain.c @@ -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, }, }; diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c index 0fe1ece9fbdc..a360bc4d20b7 100644 --- a/drivers/cpuidle/cpuidle-riscv-sbi.c +++ b/drivers/cpuidle/cpuidle-riscv-sbi.c @@ -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, }, }; diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 7356e860e65c..02da3e48bc8f 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -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, diff --git a/drivers/pmdomain/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c index ff76ea36835e..e8bda60078c4 100644 --- a/drivers/pmdomain/amlogic/meson-secure-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-secure-pwrc.c @@ -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), }; diff --git a/drivers/pmdomain/apple/Kconfig b/drivers/pmdomain/apple/Kconfig index 12237cbcfaa9..a8973f8057fb 100644 --- a/drivers/pmdomain/apple/Kconfig +++ b/drivers/pmdomain/apple/Kconfig @@ -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 diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c index 2a213c218126..8fe1c0a501c9 100644 --- a/drivers/pmdomain/arm/scmi_pm_domain.c +++ b/drivers/pmdomain/arm/scmi_pm_domain.c @@ -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) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index ff5c7f2b69ce..0006ab3d0789 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -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) @@ -759,6 +770,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. * * @genpd: The PM domain the idle-state belongs to. @@ -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 (genpd->opp_table) { - dev_pm_opp_put_opp_table(genpd->opp_table); - dev_pm_opp_of_remove_table(&genpd->dev); - } - - return ret; - } + if (ret) + goto err_opp; - genpd->provider = &np->fwnode; + 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; } 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); diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index 912802b5215b..5c83e5599f1e 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -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; diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 078323b85b56..4faa8a256186 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -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]; diff --git a/drivers/pmdomain/qcom/rpmpd.c b/drivers/pmdomain/qcom/rpmpd.c index 0be6b3026e3a..833c46944600 100644 --- a/drivers/pmdomain/qcom/rpmpd.c +++ b/drivers/pmdomain/qcom/rpmpd.c @@ -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]; diff --git a/drivers/pmdomain/renesas/Kconfig b/drivers/pmdomain/renesas/Kconfig index 54acb4b1ec7c..b507c3e0d723 100644 --- a/drivers/pmdomain/renesas/Kconfig +++ b/drivers/pmdomain/renesas/Kconfig @@ -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 +config SYSC_RMOBILE + bool "System Controller support for R-Mobile" if COMPILE_TEST + +# SoC +config SYSC_R8A7742 + bool "System Controller support for R8A7742 (RZ/G1H)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A7794 - bool "System Controller support for R-Car E2" if COMPILE_TEST +config SYSC_R8A7743 + bool "System Controller support for R8A7743 (RZ/G1M)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A77990 - bool "System Controller support for R-Car E3" if COMPILE_TEST +config SYSC_R8A7745 + bool "System Controller support for R8A7745 (RZ/G1E)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A7779 - bool "System Controller support for R-Car H1" if COMPILE_TEST +config SYSC_R8A77470 + bool "System Controller support for R8A77470 (RZ/G1C)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A7790 - bool "System Controller support for R-Car H2" if COMPILE_TEST +config SYSC_R8A774A1 + bool "System Controller support for R8A774A1 (RZ/G2M)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A7795 - bool "System Controller support for R-Car H3" if COMPILE_TEST +config SYSC_R8A774B1 + bool "System Controller support for R8A774B1 (RZ/G2N)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A7791 - bool "System Controller support for R-Car M2-W/N" if COMPILE_TEST +config SYSC_R8A774C0 + bool "System Controller support for R8A774C0 (RZ/G2E)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A77965 - bool "System Controller support for R-Car M3-N" if COMPILE_TEST +config SYSC_R8A774E1 + bool "System Controller support for R8A774E1 (RZ/G2H)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A77960 - bool "System Controller support for R-Car M3-W" if COMPILE_TEST +config SYSC_R8A7779 + bool "System Controller support for R8A7779 (R-Car H1)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A77961 - bool "System Controller support for R-Car M3-W+" if COMPILE_TEST +config SYSC_R8A7790 + bool "System Controller support for R8A7790 (R-Car H2)" 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_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 R-Car V2H" if COMPILE_TEST + bool "System Controller support for R8A7792 (R-Car V2H)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A77980 - bool "System Controller support for R-Car V3H" if COMPILE_TEST +config SYSC_R8A7794 + bool "System Controller support for R8A7794 (R-Car E2)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A77970 - bool "System Controller support for R-Car V3M" if COMPILE_TEST +config SYSC_R8A7795 + bool "System Controller support for R8A7795 (R-Car H3)" 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 +config SYSC_R8A77960 + bool "System Controller support for R8A77960 (R-Car M3-W)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A7745 - bool "System Controller support for RZ/G1E" if COMPILE_TEST +config SYSC_R8A77961 + bool "System Controller support for R8A77961 (R-Car M3-W+)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A7742 - bool "System Controller support for RZ/G1H" if COMPILE_TEST +config SYSC_R8A77965 + bool "System Controller support for R8A77965 (R-Car M3-N)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A7743 - bool "System Controller support for RZ/G1M" if COMPILE_TEST +config SYSC_R8A77970 + bool "System Controller support for R8A77970 (R-Car V3M)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A774C0 - bool "System Controller support for RZ/G2E" if COMPILE_TEST +config SYSC_R8A77980 + bool "System Controller support for R8A77980 (R-Car V3H)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A774E1 - bool "System Controller support for RZ/G2H" if COMPILE_TEST +config SYSC_R8A77990 + bool "System Controller support for R8A77990 (R-Car E3)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A774A1 - bool "System Controller support for RZ/G2M" if COMPILE_TEST +config SYSC_R8A77995 + bool "System Controller support for R8A77995 (R-Car D3)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A774B1 - bool "System Controller support for RZ/G2N" 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 diff --git a/drivers/pmdomain/renesas/rcar-gen4-sysc.c b/drivers/pmdomain/renesas/rcar-gen4-sysc.c index e001b5c25bed..5aa7fa1df8fe 100644 --- a/drivers/pmdomain/renesas/rcar-gen4-sysc.c +++ b/drivers/pmdomain/renesas/rcar-gen4-sysc.c @@ -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); diff --git a/drivers/pmdomain/renesas/rcar-sysc.c b/drivers/pmdomain/renesas/rcar-sysc.c index 047495f54e8a..4b310c1d35fa 100644 --- a/drivers/pmdomain/renesas/rcar-sysc.c +++ b/drivers/pmdomain/renesas/rcar-sysc.c @@ -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) { diff --git a/drivers/pmdomain/renesas/rmobile-sysc.c b/drivers/pmdomain/renesas/rmobile-sysc.c index 5848e79aa438..8eedc9a1d825 100644 --- a/drivers/pmdomain/renesas/rmobile-sysc.c +++ b/drivers/pmdomain/renesas/rmobile-sysc.c @@ -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); diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 4cce407bb1eb..242570c505fb 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -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, @@ -1539,6 +1562,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = { .data = (void *)&rk3399_pmu, }, { + .compatible = "rockchip,rk3528-power-controller", + .data = (void *)&rk3528_pmu, + }, + { .compatible = "rockchip,rk3562-power-controller", .data = (void *)&rk3562_pmu, }, diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index 9b502e8751d1..5d478bb37ad6 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -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; } diff --git a/drivers/pmdomain/sunxi/Kconfig b/drivers/pmdomain/sunxi/Kconfig index 43eecb3ea981..858446594c88 100644 --- a/drivers/pmdomain/sunxi/Kconfig +++ b/drivers/pmdomain/sunxi/Kconfig @@ -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. diff --git a/drivers/pmdomain/sunxi/Makefile b/drivers/pmdomain/sunxi/Makefile index c1343e123759..e344b232fc9f 100644 --- a/drivers/pmdomain/sunxi/Makefile +++ b/drivers/pmdomain/sunxi/Makefile @@ -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 diff --git a/drivers/pmdomain/sunxi/sun20i-ppu.c b/drivers/pmdomain/sunxi/sun20i-ppu.c index 9f002748d224..b65876a68cc1 100644 --- a/drivers/pmdomain/sunxi/sun20i-ppu.c +++ b/drivers/pmdomain/sunxi/sun20i-ppu.c @@ -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); diff --git a/drivers/pmdomain/sunxi/sun55i-pck600.c b/drivers/pmdomain/sunxi/sun55i-pck600.c new file mode 100644 index 000000000000..c7ab51514531 --- /dev/null +++ b/drivers/pmdomain/sunxi/sun55i-pck600.c @@ -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, ®, 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"); diff --git a/drivers/pmdomain/thead/Kconfig b/drivers/pmdomain/thead/Kconfig index 7d52f8374b07..208828e0fa0d 100644 --- a/drivers/pmdomain/thead/Kconfig +++ b/drivers/pmdomain/thead/Kconfig @@ -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, diff --git a/drivers/pmdomain/thead/th1520-pm-domains.c b/drivers/pmdomain/thead/th1520-pm-domains.c index f702e20306f4..9040b698e7f7 100644 --- a/drivers/pmdomain/thead/th1520-pm-domains.c +++ b/drivers/pmdomain/thead/th1520-pm-domains.c @@ -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]); diff --git a/drivers/pmdomain/ti/Kconfig b/drivers/pmdomain/ti/Kconfig index 67c608bf7ed0..5386b362a7ab 100644 --- a/drivers/pmdomain/ti/Kconfig +++ b/drivers/pmdomain/ti/Kconfig @@ -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. diff --git a/drivers/pmdomain/xilinx/zynqmp-pm-domains.c b/drivers/pmdomain/xilinx/zynqmp-pm-domains.c index d579220a4500..b5aedd6e33ad 100644 --- a/drivers/pmdomain/xilinx/zynqmp-pm-domains.c +++ b/drivers/pmdomain/xilinx/zynqmp-pm-domains.c @@ -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, diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 74299af1d7f1..a6eef0080ca9 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -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); diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 4d6a46bc8c21..2a5f24ee858c 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -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 */ diff --git a/include/linux/device.h b/include/linux/device.h index 5137f9d213ec..60dabfa2e1f8 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -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) { diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 6d4dbc196b93..ae48d619c4e0 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -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; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 62a35a78ce9b..c84edf217819 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -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; |