diff options
Diffstat (limited to 'drivers/pmdomain')
21 files changed, 743 insertions, 120 deletions
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, |